Docker:容器编排 Docker Compose
- docker-compose
- docker-compose.yml
- services
- image
- command
- environment
- networks
- volumes
- ports
- healthcheck
- depends_on
- 命令
- docker compose up
- docker compose down
- 其它
docker-compose
多数情况下,一个服务需要依赖多个服务,而这多个服务需要部署在不同的容器内部,这就涉及到多个容器的启动问题。比如容器之间的依赖关系,镜像版本,启动命令等等问题。
这导致多容器启动会比较麻烦,为此Docker
官方推出了开源项目docker-compose
,其使用python
编写,实现多个容器的启动问题。
docker-compose
的使用,基于docker-compose.yml
文件,只要会编写这个文件,就可以命令一次性快速启动多个容器。
docker-compose.yml
后续称docker-compose.yml
为.yml
文件。
services
在.yml
文件中,用services
表示服务,一个services
下可以有多个容器。
services:
service_1:
...
service_2:
...
service_3:
...
# 一行注释
其中service_1
、service_2
、service_3
是三个服务,其实就是三个容器。
在docker
中,推荐使用两个空格换行缩进,使用#
注释。
image
- 功能:指定容器运行的镜像
在每个service
中,必须带有image
选项,容器必须基于镜像创建。
语法:
services:
服务名:
image: 镜像名:tag
示例:
services:
service_1:
image: redis
service_2:
image: mysql:5.7
service_3:
image: nginx
指定镜像时,可以通过:tag
指定版本。以上代码,可以同时启动三个容器。
在.yml
文件所处目录下,使用docker compose up -d
可以快速启动容器,这个命令在稍后讲解。
通过输出,可以看到三个容器创建成功了,但是docker ps
只能看到两个容器。这是因为mysql
容器在创建时必须指定一个环境变量,配置root
的密码。
容器名称为compose-service_1-1
,compose-service_2-1
,前面的compose
的当前的目录名称,中间是服务名称,也就是指定的services
,最后是一个编号。
启动的容器命名规则如下:
所处目录名-服务名-编号
command
- 功能:指定容器启动时执行的命令
语法:
services:
服务名:
command: 命令 选项1 选项2
services:
服务名:
command: ["命令", "选项1", "选项2"]
指定指令时,可以直接将完整的指令写在command
后面,也可以按照数组形式[]
,一个一个输入选项。
示例:
services:
service_1:
image: redis
command: ls -l -a
service_2:
image: redis
command: ["ls", "-l", "-a"]
environment
- 功能:指定容器的环境变量
语法:
services:
服务名:
environment:
env1: value1
env2: value2
env3: value3
services:
服务名:
environment:
- env1=value1
- env2=value2
- env3=value3
指定环境变量有两种语法,第一种是直接在environment
后面添加key:value
形式的环境变量,另一种是- key=value
的形式。
比如mysql
启动时,就需要指定环境变量,来设置root
用户的密码:
services:
service_1:
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=123456
启动mysql
服务:
这一次mysql
就启动成功了,因为环境变量配置成功。
networks
- 功能:指定容器的网络
语法:
services:
服务名:
networks:
- 网络
networks:
网络:
driver: 网络类型
网络:
driver: 网络类型
在服务中,可以通过networks
指定要使用的网络,以- 网络名
的形式。
- 创建网络:
如果网络不存在,可以在services
同级的区域,创建一个networks
指定要创建的网络。通过driver
指定网络的类型,如果不指定,那么默认为bridge
网络。
示例:
services:
service_1:
image: nginx
networks:
- test_net
networks:
test_net:
以上代码,创建了一个网络test_net
,这个网络没用进行任何属性配置,默认为bridge
网络。虽然没用进行属性配置,test_net:
的冒号依然不能省略。
输出结果:
服务启动后,多出一个compose_test_net
网络,这就是刚才创建的网络。
这个网络名称是自动生成的,如果想要自己指定网络名,可以在创建网络时通过name
属性指定:
networks:
网络:
name:网络名
- 加入网络:
如果想要加入一个现有的网络,不能写为如下格式:
services:
服务名:
networks:
- 现有网络名
就算是现有网络,也要在services
同级的networks
下指明:
services:
service_1:
networks:
- 网络A
networks:
网络A:
external: true
name: 现有网络名
在services
同级的networks
下,不仅可以创建一个新的网络,还可以通过external: true
来指定一个已有网络,表示当前网络复用了已有网络,在name
中指定已有网络名。
- 默认网络:
如果指定的网络类型为host
、none
或者默认桥接网络bridge
,不能写为如下格式:
services:
服务名:
networks:
- host
服务名:
networks:
- none
服务名:
networks:
- bridge
使用host
和none
网络需要指定network_mode
:
services:
服务名:
network_mode: host
服务名:
network_mode: none
服务名:
network_mode: bridge
volumes
- 功能:指定容器的存储卷
语法:
volume:
存储卷名:
services:
服务名:
volumes:
- type: volume
source: 存储卷名
target: 容器内绑定地址
- type: bind
source: 宿主机绑定地址
target: 容器内绑定地址
通过volumes
绑定存储卷,在下一级缩进中,指定要绑定的存储卷。
可以一次性绑定多个存储卷,使用-
区分不同存储卷的属性。
- type: volume
source: 存储卷名
target: 容器内绑定地址
type
指定存储卷的类型,如果类型为volume
卷,那么source
填入存储卷的名称,target
填入容器中存储卷要绑定的地址。
这个存储卷名可以是一个已有的存储卷,如果存储卷不存在,可以在services
同级缩进之前,创建一个存储卷:
volume:
存储卷名:
这和网络类似,不指定任何参数默认创建一个volume
类型存储卷。
- type: bind
source: 宿主机绑定地址
target: 容器内绑定地址
如果使用绑定卷,那么source
变为宿主机地址,target
依然是容器内部的地址。
示例:
volumes:
test-vm:
services:
service_1:
image: nginx
volumes:
- type: volume
source: test-vm
target: /var/lib/html
以上.yml
创建了一个存储卷test-vm
,并在service_1
使用了这个存储卷。
输出结果:
服务启动后,多出了一个compose_test-vm
存储卷,也就是目录名-存储卷名
的格式。
- 短语法
除此之外,存储卷还有短语法:
services:
服务名:
volumes:
- "存储卷名称:容器路径" # 数据卷
- "宿主机路径:容器路径" # 绑定卷
ports
- 功能:指定容器的端口映射
语法:
services:
服务名:
ports:
- "A" # 容器的端口A 映射到 宿主机的端口A
- "A-B" # 容器A-B的端口 映射到 宿主机A-B的端口
- "A:B" # 容器的端口B 映射到 宿主机的端口A
- "A-B:C-D" # 容器C-D的端口 映射到 宿主机A-B的端口
- "A:B/udp" # 容器的端口B 映射到 宿主机的端口A 使用UDP协议
- "A:B/tcp" # 容器的端口B 映射到 宿主机的端口A 使用TCP协议
其中ABCD
等符号都代指端口号。
healthcheck
- 功能:检查容器健康状态
语法:
services:
服务名:
healthcheck:
test: 测试命令
interval: 探测间隔
timeout: 超时时间
retries: 检测次数
所谓的健康检查healthcheck
,其实就是执行一个测试命令,如果命令执行成功,那么这个容器就算健康。
test
指定要测试的命令,每次检测最多等timeout
时间,如果超出时间视为检测失败,最多重复检测retries
次,每次间隔interval
。
示例:
services:
service_1:
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=123456
healthcheck:
test: mysql --user=root --password='123456' -e "SELECT 1;"
interval: 3s
timeout: 5s
retries: 5
这是一个mysql
容器的健康检测,执行一个查询语句SELECT 1
,这个语句没有任何意义,只要执行成功说明用户名密码,以及mysql
都是正常的。
最多检测五次,每次五秒,多次检测间隔三秒。
启动之后,通过docker ps
查看容器,发现STATUS
栏多出一个小括号(health: starting)
,表示这个容器正在检测健康。
一段时间后,健康状态变为healthy
,表示健康检测完成,这个容器是健康的。
depends_on
- 功能:设置容器间的依赖关系
语法:
services:
服务名:
depends_on:
- 服务名
- 服务名
在depends_on
后面,以列表的形式写出依赖的服务,只有依赖的服务都启动了,才会启动该服务。
除此之外,还可以指定健康检查:
services:
服务名:
depends_on:
服务名:
condition: service_healthy
服务名:
condition: service_started
如果设置condition: service_started
,表示只要容器启动了那么当前服务就可以开始。如果设置condition: service_healthy
,表示容器启动后还要通过健康检查,只有检查结果为healthy
才能启动当前容器。
示例:
services:
redis:
image: redis
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=123456
healthcheck:
test: mysql --user=root --password=123456 -e "SELECT 1;"
interval: 3s
timeout: 5s
retries: 5
以上.yml
,启动了一个redis
和mysql
容器,其中redis
依赖mysql
,并且需要mysql
进行健康检查。
启动结果:
首先mysql
进入了waiting
状态,这是正在进行健康检查,redis
容器已经创建完毕,但是还没有启动。
当检测完mysql
状态为healthy
,redis
才进入start
状态,说明确实先启动了mysql
后启动redis
。
命令
docker compose
的命令使用一致的格式:
docker compose [option] command [args...]
选项:
-f
:指定文件,如果不指定,默认为当前目录下的docker-compose.yml
文件-p
:指定项目名称,如果不指定,默认以当前目录名作为项目名称
docker compose up
- 功能:拉取镜像,创建服务,启动服务,完成创建容器的所有步骤
docker compose up [option]
选项:
-d
:在后台运行容器,常用选项
先测试一下-f
选项:
在当前目录下,有一个other
文件,可以通过-f
来指定通过这个文件启动容器。默认情况下会默认去找名为docker-compose.yml
的文件来启动容器,通过-f
可以改变文件。
在这个过程中,创建的两个容器分别叫做compose-mysql-1
和compose-redis-1
,这是因为当前的目录名称为compose
,容器名默认为项目名-服务名-编号
,如果不指定项目名称,以目录名称为项目名称。
可以通过-p
选项指定项目名:
此处指定项目名称为new_name
,那么两个容器名的前缀也就变为了new_name-xxx
。
docker compose down
- 功能:删除由
docker compose
创建的服务
docker compose down [option]
选项:
-v
:删除容器的同时删除存储卷
其它
除去以上两个最重要的命令,还有很多其它的命令:
命令 | 功能 |
---|---|
docker compose build | 构建服务 |
docker compose config | 规范的格式来显示服务配置 |
docker compose cp | 在本地系统和服务容器直接拷贝文件 |
docker compose create | 创建服务的容器 |
docker compose down | 停止所有容器,并删除容器 |
docker compose events | 从服务器获取实时事件 |
docker compose exec | 在容器中执行命令 |
docker compose images | 列出所有容器使用的镜像 |
docker compose kill | 强制停止服务的容器 |
docker compose logs | 显示日志 |
docker compose ls | 显示所有项目 |
docker compose pause | 暂停服务 |
docker compose port | 列出所有的端口映射 |
docker compose ps | 该命令可以列出项目中目前的所有容器 |
docker compose pull | 拉取服务镜像 |
docker compose push | 推送服务镜像 |
docker compose restart | 重启或者重启某个服务 |
docker compose rm | 删除服务停止的容器 |
docker compose run | 在指定服务容器上执行相关的命令 |
docker compose start | 启动当前停止的某个容器 |
docker compose stop | 停止当前运行的某个容器 |
docker compose top | 显示运行的进程 |
docker compose unpause | 恢复服务 |
这些命令看似很多,但是其实和docker container
的命令是高度重合的。使用docker container
执行命令时,只作用于指定容器,但是docker compose
的命令,作用于服务内的所有容器。
例如top
:
执行时会把所有服务内部的进程都列举出来。
logs
命令:
其会输出所有服务的日志,包括mysql
和redis
。