一、了解docker持久化
1. docker持久化的方案
- 基于本地文件系统的volum。 可以在执行docker create 或者 docker run时,通过-v参数将主机的目录作为容器的数据卷。这部分功能便是基于本地的文件系统的volume管理
- 基于plugin的volume, 支持第三方的存储方案,比如NAS,aws
- Volume的类型:
- 收管理的data volume,有docker后台自动创建
- 绑定挂载的volume,具体挂载位置有用户指定
2. 数据持久化之 data volume
- 参考地址:https://github.com/docker-library/mysql/tree/master/5.7# ‘官方提供的MySQL Dockerfile’
- data volume 持久化的数据不会随着 容器的删除 而 删除,解决了数据安全的问题
- 默认产生的data volume的名称偶读非常长,不便于使用,使用-v参数可以指定名称
示例:-v <volume名称>:<挂载路径>
# 运行一个允许空密码登入的mysql
[root@localhost ~]# docker run -d --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD mysql
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
36a16497b015 mysql "docker-entrypoint.s…" 14 seconds ago Exited (1) 12 seconds ago mysql1
# 没有启动成功,查看mysql1日志,发现还有密码问题导致无法启动
[root@localhost ~]# docker logs mysql1
2021-05-29 16:55:46+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.25-1debian10 started.
2021-05-29 16:55:46+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2021-05-29 16:55:46+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.25-1debian10 started.
2021-05-29 16:55:46+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified
You need to specify one of the following:
- MYSQL_ROOT_PASSWORD
- MYSQL_ALLOW_EMPTY_PASSWORD
- MYSQL_RANDOM_ROOT_PASSWORD
# 删掉之前的mysql
[root@localhost ~]# docker rm mysql1
# 虽然容器被删除了,但是volume依旧在,可以执行 docker volume rm 删除
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local dc21763599f6430ea2a8decee10495c51510fc562f108ec22496cc07c4f91023
[root@localhost ~]# docker volume rm dc21763599f6430ea2a8decee10495c51510fc562f108ec22496cc07c4f91023
dc21763599f6430ea2a8decee10495c51510fc562f108ec22496cc07c4f91023
# 设置允许空密码
[root@localhost ~]# docker run -d --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
# 容器up了
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
604a6a4136f0 mysql "docker-entrypoint.s…" 4 seconds ago Up 3 seconds 3306/tcp, 33060/tcp mysql1
# 查看本地volume
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local cffb1dc331784981ef76baa7929a38b50a4407c6c6bc160a1b60e48f6263e4a8
# 查看这个volume的详细信息
[root@localhost ~]# docker volume inspect cffb1dc331784981ef76baa7929a38b50a4407c6c6bc160a1b60e48f6263e4a8
[
{
"CreatedAt": "2021-05-30T01:03:53+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/cffb1dc331784981ef76baa7929a38b50a4407c6c6bc160a1b60e48f6263e4a8/_data",
"Name": "cffb1dc331784981ef76baa7929a38b50a4407c6c6bc160a1b60e48f6263e4a8",
"Options": null,
"Scope": "local"
}
]
# 利用-v 指定volume的名称和路径
[root@localhost ~]# docker run -d --name mysql2 -v mysql-data:/var/lib/mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
# 生成了一个mysql-data的volume
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local cffb1dc331784981ef76baa7929a38b50a4407c6c6bc160a1b60e48f6263e4a8
local mysql-data
# 查看mysql-data信息,可以看到挂载路径
[root@localhost ~]# docker volume inspect mysql-data
[
{
"CreatedAt": "2021-05-30T01:13:50+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/mysql-data/_data",
"Name": "mysql-data",
"Options": null,
"Scope": "local"
}
]
验证volume是否有效
# 进入mysql2查看数据库
[root@localhost ~]# docker exec -it mysql2 bash -c mysql
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
# 创建docker库
mysql> create database docker;
Query OK, 1 row affected (0.01 sec)
# 强制删除所有的mysql容器
[root@localhost ~]# docker rm -f $(docker ps -aq)
# 查看容器,已经被删除了
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 查看volume,mysql-data的数据卷还在
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local mysql-data
# 使用-v指定volume名称和路径(注意,一定要和上面的一致,否则会重新创建一个volume),重新创建一个mysql容器,
[root@localhost ~]# docker run -d --name mysql1 -v mysql-data:/var/lib/mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
# 进入数据库查看,之前创建docker库还在
[root@localhost ~]# docker exec -it mysql1 bash -c mysql
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| docker |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.01 sec)
3. 数据持久化之 bind mounting
- bind mount和data volume的区别:
- data volume:需要在Dockerfile 中定义创建的volume
- bind mount:只需要在运行容器时-v指定本地路径和容器内路径
-v <指定本地路径>:<容器内路径>
制作一个nginx访问百度的容器
# 用Dockerfile生成镜像
[root@localhost nginx]# cat Dockerfile
FROM nginx:latest
WORKDIR /usr/share/nginx/html
COPY index.html index.html
# 利用百度首页生成index.html文件
[root@localhost nginx]# curl www.baidu.com > index.html
# 制作镜像
[root@localhost nginx]# docker build -t test/baidu-nginx .
# 运行容器
[root@localhost nginx]# docker run -d -p 80:80 --name nginx-web test/baidu-nginx
[root@localhost nginx]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7f70c92f40c3 test/baidu-nginx "/docker-entrypoint.…" 9 seconds ago Up 8 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx-web
访问测试
使用bind volume
[root@localhost nginx]# ls
Dockerfile index.html
# 将当前目录映射到容器的/usr/share/nginx/html目录下
[root@localhost nginx]# docker run -d -p 80:80 -v $(pwd):/usr/share/nginx/html --name nginx-web test/baidu-nginx
# 在当前目录下生成一个test.html文件
[root@localhost nginx]# vim test.html
<head>
<meta charset="utf-8">
<title>YY</title>
<h1>精美壁纸</h1><br>
<img src="http://pic1.cxtuku.com/00/03/34/b3846967c5ac.jpg" />
</head>
访问测试,可以访问新增的test.html文件
4. bind mounting 实用演示
# 下载代码
[root@localhost ~]# git clone https://github.com/LTP7534/skeleton.git
[root@localhost ~]# cd skeleton
[root@localhost skeleton]# docker build -t test/flask-skeleton .
[root@localhost skeleton]# [root@localhost skeleton]# docker run -d -p 80:5000 -v $(pwd):/skeleton --name flask-skeleton test/flask-skeleton:latest
[root@localhost skeleton]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
30b1f87f4cbc test/skeleton:latest "scripts/dev.sh" 6 seconds ago Up 5 seconds 0.0.0.0:80->5000/tcp, :::80->5000/tcp flask-skeleton
二、了解docker compose
1. 部署 wordprocess(练习)
- 参考地址:http://hub.docker.com/_/wordpress
- 通过多个容器部署wordpress应用
1.1 创建mysql数据库容器
[root@localhost skeleton]# docker run -d --name mysql -v mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=wordpress mysql
[root@localhost skeleton]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7fc6b75c1989 mysql "docker-entrypoint.s…" 4 seconds ago Up 3 seconds 3306/tcp, 33060/tcp mysql
[root@localhost skeleton]#
1.2 部署wordpress容器
[root@localhost skeleton]# docker run -d --name wordpress -e WORDPRESS_DB_HOST=mysql:3306 -e WORDPRESS_DB_USER=root -e WORDPRESS_DB_PASSWORD=123456 --link mysql -p 8080:80 wordpress
[root@localhost skeleton]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
275a8f58ff01 wordpress "docker-entrypoint.s…" 9 seconds ago Up 7 seconds 0.0.0.0:8080->80/tcp, :::8080->80/tcp distracted_zhukovsky
7fc6b75c1989 mysql "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 3306/tcp, 33060/tcp mysql
1.3 浏览器访问
2. docker compose 部署应用
参考地址:https://docs.docker.com/compose/compose-file/compose-file-v3/
- docker compose 解决了多容器部署管理的问题
- docker compose是一个工具
- 这个工具通过一个yml文件定义多容器的docker应用
- 通过一条命令就可以根据yml文件的定义去创建或者管理多个容器
docker compose 的三大概念-
Services :
-
一个Services代表一个container,这个container可以通过Dockerfile创建的image 或者dockerhub上拉取的image创建而来
-
Service的启动类似docker run,我们可以service指定network 和 volume
-
例如:
# 示例1: services: db: image: postgres:9.4 # 从dockerhub上拉取的镜像 volumes: - "db-data":/var/lib/postgres/data networks: - back-tier
相当于:
docker run -d --networ kback-tier -v db-data:/var/lib/postgres/data postgres:9.4
# 示例2: services: worker: build: ./worker # 利用Dockerfile创建的镜像 link: - db - redis networks: - back-tier
-
-
Networks :
- 例如
- 例如
-
Volumes:
- 例如:
- 例如:
-
2.1 安装 docker compose
[root@localhost ~]# sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
[root@localhost ~]# chmod +x /usr/local/bin/docker-compose
[root@localhost ~]# docker-compose --version
docker-compose version 1.29.2, build 5becea4c
2.2 利用docker compose部署wordpress
docker-compose -f <yml文件> up
---- 开始编排容器,并且直接运行容器
[root@localhost nginx]# vim docker-compose.yml
version: '3'
services:
wordpress:
image: wordpress
ports:
- 8080:80
depends_on:
- mysql
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_PASSWORD: 123456
WORDPRESS_DB_USER: root
networks:
- my-bridge
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: wordpress
volumes:
- mysql-data:/var/lib/mysql
networks:
- my-bridge
volumes:
mysql-data:
networks:
my-bridge:
driver: bridge
# 查看yml文件
[root@localhost nginx]# ls
docker-compose.yml
# 开始编排,-f 默认的文件名就是docker-compose.yml,因此这里可以省略指定文件名
# -d参数:后台运行(加上这个参数后,debug日志就不会打印出)
# 一般用于调试时会直接up,不加-d,可以实时查看log
[root@localhost nginx]# docker-compose up&
# 打印出 docker-compose 启动的容器
[root@localhost nginx]# docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------------
nginx_mysql_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp
nginx_wordpress_1 docker-entrypoint.sh apach ... Up 0.0.0.0:8080->80/tcp,:::8080->80/tcp
访问测试
# docker-compose stop 可以停止docker-compose启动的容器(不会删除)
[root@localhost nginx]# docker-compose stop
# 查看容器状态
[root@localhost nginx]# docker-compose ps
Name Command State Ports
-------------------------------------------------------------------
nginx_mysql_1 docker-entrypoint.sh mysqld Exit 0
nginx_wordpress_1 docker-entrypoint.sh apach ... Exit 0
# docker-compose start 会将停止的容器再开启
[root@localhost nginx]# docker-compose start
Starting mysql ... done
Starting wordpress ... done
# 又会处于运行状态
[root@localhost nginx]# docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------------
nginx_mysql_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp
nginx_wordpress_1 docker-entrypoint.sh apach ... Up 0.0.0.0:8080->80/tcp,:::8080->80/tcp
# docker-compose down 会将相关的容器,镜像,数据卷等删除
[root@localhost nginx]# docker-compose down
Stopping nginx_wordpress_1 ... done
Stopping nginx_mysql_1 ... done
Removing nginx_wordpress_1 ... done
Removing nginx_mysql_1 ... done
Removing network nginx_my-bridge
# 编排的容器以及被删除
[root@localhost nginx]# docker-compose ps
Name Command State Ports
------------------------------
# -d参数:后台运行(加上这个参数后,debug日志就不会打印出)
[root@localhost nginx]# docker-compose up -d
Creating network "nginx_my-bridge" with driver "bridge"
Creating nginx_mysql_1 ... done
Creating nginx_wordpress_1 ... done
# 查看docker-compose生成的镜像
[root@localhost nginx]# docker-compose images
Container Repository Tag Image Id Size
-----------------------------------------------------------------
nginx_mysql_1 mysql 5.7 2c9028880e58 447 MB
nginx_wordpress_1 wordpress latest 0adda6ed742f 550.5 MB
# 进入容器docker-compose exec <yml中定义的Services> bash
[root@localhost nginx]# docker-compose exec mysql bash
root@0539d45f62c3:/# mysql -u root -p123456
mysql>
# 生成的网络名称格式 : "当前目录名称_yml中定义的networks名称" 如这个nginx_my-bridge
[root@localhost nginx]# docker network ls
NETWORK ID NAME DRIVER SCOPE
5748917a989f bridge bridge local
ef2dd6544dee host host local
6b5d1976ff28 nginx_my-bridge bridge local
0051745c4534 none null local
[root@localhost nginx]# docker-compose down
Stopping nginx_wordpress_1 ... done
Stopping nginx_mysql_1 ... done
Removing nginx_wordpress_1 ... done
Removing nginx_mysql_1 ... done
Removing network nginx_my-bridge
3. docker-compose水平扩展和负载均衡
- 比方说我们有一个web服务,总redis中获取数据,当只有一个web容器时,直接就可以连接访问,但是如果存在多个相同的web,就不能直接访问了,端口会冲突,这时候就需要负载均衡
3.1 实战示例
首先看下web应用程序脚本,会从redis中获取数据,然后web展示
3.1.1 先进行单个web容器的编排部署
[root@localhost flask-redis]# vim app.py
from flask import Flask
from redis import Redis
import os
import socket
app = Flask(__name__)
redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)
@app.route('/')
def hello():
redis.incr('hits')
return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'),socket.gethostname())
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)
查看Dockerfile
[root@localhost flask-redis]# vim Dockerfile
FROM python:2.7
LABEL maintaner="Peng Xiao xiaoquwl@gmail.com"
COPY . /app
WORKDIR /app
RUN pip install flask redis
EXPOSE 5000
CMD [ "python", "app.py" ]
查看docker-compose
[root@localhost flask-redis]# vim docker-compose.yml
version: "3"
services:
redis:
image: redis
web:
build:
context: .
dockerfile: Dockerfile
ports:
- 8080:5000
environment:
REDIS_HOST: redis
进行编排,将容器内5000端口映射到8080
[root@localhost flask-redis]# docker-compose up -d
[root@localhost flask-redis]# docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------------------
flask-redis_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
flask-redis_web_1 python app.py Up 0.0.0.0:8080->5000/tcp,:::8080->5000/tcp
访问测试
3.1.2 多web容器部署
- 但是在多web容器时,就不能使用上面的yml文件了,会端口冲突,需要借助haproxy
- 扩容容器命令
docker-compose up --scale <service名称>=<扩容数目> -d
- 这种方式扩展性较强,更加灵活
# 先移除之前的
[root@localhost flask-redis]# docker-compose down
Removing flask-redis_redis_1 ... done
Removing flask-redis_web_1 ... done
Removing network flask-redis_default
查看相关文件
[root@localhost flask-redis]# cat app.py
from flask import Flask
from redis import Redis
import os
import socket
app = Flask(__name__)
redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)
@app.route('/')
def hello():
redis.incr('hits')
return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'),socket.gethostname())
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80, debug=True)
[root@localhost flask-redis]# cat Dockerfile
FROM python:2.7
LABEL maintaner="Peng Xiao xiaoquwl@gmail.com"
COPY . /app
WORKDIR /app
RUN pip install flask redis
EXPOSE 80
CMD [ "python", "app.py" ]
[root@localhost flask-redis]# cat docker-compose.yml
version: "3"
services:
redis:
image: redis
web:
build:
context: .
dockerfile: Dockerfile
environment:
REDIS_HOST: redis
lb:
image: dockercloud/haproxy
links:
- web
ports:
- 8080:80
volumes:
- /var/run/docker.sock:/var/run/docker.sock
单容器运行
# 运行容器
[root@localhost flask-redis]# docker-compose up -d
# 访问web
[root@localhost flask-redis]# curl 192.168.10.70:8080
Hello Container World! I have been seen 1 times and my hostname is b04337110d23.
# 查看运行容器
[root@localhost flask-redis]# docker-compose ps -a
Name Command State Ports
----------------------------------------------------------------------------------------------------------------------
flask-redis_lb_1 /sbin/tini -- dockercloud- ... Up 1936/tcp, 443/tcp, 0.0.0.0:8080->80/tcp,:::8080->80/tcp
flask-redis_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
flask-redis_web_1 python app.py Up 5000/tcp
多web容器运行(可以在原基础上直接扩容)
# 这是web容器数量为3
[root@localhost flask-redis]# docker-compose up --scale web=3 -d
flask-redis_redis_1 is up-to-date
Creating flask-redis_web_2 ... done
Creating flask-redis_web_3 ... done
flask-redis_lb_1 is up-to-date
# 查看容器,多出了web_2 和 web_3
[root@localhost flask-redis]# docker-compose ps -a
Name Command State Ports
----------------------------------------------------------------------------------------------------------------------
flask-redis_lb_1 /sbin/tini -- dockercloud- ... Up 1936/tcp, 443/tcp, 0.0.0.0:8080->80/tcp,:::8080->80/tcp
flask-redis_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
flask-redis_web_1 python app.py Up 5000/tcp
flask-redis_web_2 python app.py Up 5000/tcp
flask-redis_web_3 python app.py Up 5000/tcp
# 访问测试,正常
[root@localhost flask-redis]# curl 192.168.10.70:8080
Hello Container World! I have been seen 2 times and my hostname is b04337110d23.
4. 利用docker-compose部署比较复杂的应用
- voting app:用于投票
- results app:用于查看投票结果
# 下载代码
[root@localhost voting]# git clone https://github.com/LTP7534/voting.git
# 查看docker-compose.yml
[root@localhost voting]# cat docker-compose.yml
version: "3"
services:
voting-app:
build: ./voting-app/.
volumes:
- ./voting-app:/app
ports:
# 容器的80端口映射到本地的5000端口
- "5000:80"
links:
- redis
networks:
- front-tier
- back-tier
result-app:
build: ./result-app/.
volumes:
- ./result-app:/app
ports:
- "5001:80"
links:
- db
networks:
- front-tier
- back-tier
worker:
build: ./worker
links:
- db
- redis
networks:
- back-tier
redis:
image: redis
ports: ["6379"]
networks:
- back-tier
db:
image: postgres:9.4
volumes:
- "db-data:/var/lib/postgresql/data"
networks:
- back-tier
volumes:
db-data:
networks:
# front-tier 和 back-tier 都没有指明driver,默认时bridge
front-tier:
back-tier: