1. docker compose介绍
1.1 什么是docker compose
- 在部署容器的时候会经过许多步骤,比如创建容器、配置网络、启动容器等等,此时可以把这些命令写成一个脚本。
- 为了实现这个需求,就创建了
docker compose
用于简化这些步骤。这个工具通过yml
格式的文件去定义需要的操作信息,并且使用docker compose
执行该文件:
1.2 docker compose的安装
- Windows和Mac主机安装了docker desktop之后,docker compose也会自动安装
- Linux主机可以使用以下命令安装:
$ 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 $ sudo chmod +x /usr/local/bin/docker-compose $ docker-compose --version # 查看是否安装成功
- 使用
pip install docker-compose
(docker compose
是使用python语言写的工具,所以可以使用pip安装)
1.3 compose文件的结构和版本
- 基本语法结构:
version: "3.8" # 表示docker compose语法的版本 services: # 容器 servicename: # 服务(容器)名字,这个名字也是内部bridge网络可以使用的DNS name(即可以被其他使用相同bridge网络的容 器通过该名字ping通) image: # 镜像的名字 command: # 可选,如果设置,则会覆盖默认镜像里的 CMD命令 environment: # 可选,相当于 docker run里的 --env volumes: # 可选,相当于docker run里的 -v networks: # 可选,相当于 docker run里的 --network ports: # 可选,相当于 docker run里的 -p servicename2: volumes: # 可选,相当于 docker volume create,会被上面的service使用到 networks: # 可选,相当于 docker network create,会被上面的service使用到
以pytnon+Flask+redis练习使用的命令为例,将其改造为
docker compose
文件:练习中的命令如下:
docker image pull redis docker image build -t flask-demo . # create network docker network create -d bridge demo-network # create container docker container run -d --name redis-server --network demo-network redis docker container run -d --network demo-network --name flask-demo --env REDIS_HOST=redis-server -p 8080:5000 flask-demo
docker-compose.yml
文件内容如下:version: "3.8" services: flask-demo: image: flask-demo:latest environment: - REDIS_HOST=redis-server networks: - demo-network ports: - 8080:5000 redis-server: image: redis:latest networks: - demo-network networks: demo-network: # 这里没有写别的参数就使用默认的bridge
tips:
- 当
docker engine
的版本为较新版本时也是可以使用老版本的docker compose
版本
1.4 docker compose命令行的基本使用
假设已经按照pytnon+Flask+redis练习创建了相应的镜像:
- 使用
docker-compose up
去启动docker-compose.yml
定义的容器以及网络等,加上-d
参数可以让容器在后台运行:
- 使用
docker-compose ps
可以查看当前后台运行的容器情况:
- 使用
dockcer-compose stop
停止容器- 使用
docker-compose rm
后会删除退出状态的容器,但是创建的网络并不会删除tips:
- 在使用
docker-compose
命令时需要在docker-compose.yml
所在目录运行- 在查看容器运行情况时,会看到Name中出现
root_flaks-demo_1
,前缀默认为当前文件夹。前缀名可以使用docker-compose -p xxx up
在启动时指定前缀名,但是在后续使用别的命令时都需要加上-p xxx
。如果在docker-compose.yml
文件中对具体容器使用container_name
指定名称后,root_flaks-demo_1
就会变为指定名称
2. docker compose镜像构建和拉取
假设当前没有构建
flask-demo
这个镜像,此时去执行docker-compose up
会报错:
修改后的
docker-compose.yml
:version: "3.8" services: flask-demo: build: context: ./ # 需要指定构建使用的dockerfile所在目录 dockerfile: Dockerfile_train # 指定使用的dockrfile image: flask-demo:2.0 # 指定构建镜像的名称,不写的话镜像名称默认为root_flaks-demo这样的名称 environment: - REDIS_HOST=redis-server networks: - demo-network ports: - 8080:5000 redis-server: image: redis:latest networks: - demo-network networks: demo-network: # 这里没有写别的参数就使用默认的bridge
在使用
docker-compose up
时,假设docker image
中没有是需要的镜像,会自动去拉取该镜像。同样也可以使用docker-compose pull/build
先去拉取/构建所需镜像,这样可以加快执行docker-compose up
的速度
3. docker compose服务更新
假如dockerfile文件中做了修改,此时需要去重新构建镜像:
docker-compose build
:重新构建docker-compose.yml
需要的镜像
docker-compose up -d --build
:构建作了变更的镜像然后启动服务
4. docker compose网络
- docker-compose.yml:
version: "3.8" services: box1: image: xiaopeng163/net-box:latest # 多种网络工具的镜像 command: /bin/sh -c "while true; do sleep 3600; done" box2: image: xiaopeng163/net-box:latest command: /bin/sh -c "while true; do sleep 3600; done"
- 使用
docker-compose up -d
创建服务,再使用docker container exec -it root_box1_1 sh
进入容器内部(两个容器的名字为root_box1_1和root_box2_1,服务的名字为box1和box2)去ping root_box2_1
是可以ping通的,同时ping box2
也是可以ping通的- 在容器内部使用查询
dig root_box2_1
(dig是在镜像中安装好的)DNS服务器IP地址为127.0.0.11:53
,这个DNS服务器是后台docker engine维护的:
- 在宿主中使用
ping 127.0.0.11
是可以ping通的,但是ping root_box1_1
是无法ping通的使用。使用more /etc/resolv.conf
可以查看到宿主机的DNS服务器IP地址并不是127.0.0.11
- 在容器的shell中ping宿主机或百度等是可以ping通的,因为在
127.0.0.11
这个DNS服务器中找不到域名后,会使用宿主机的DNS服务器
5. docker compose水平扩展和负载均衡
水平扩展即快速增加
docker-compose.yml
中services的数量
- docker-compose.yml:
version: "3.8" services: flask: build: context: ./ dockerfile: Dockerfile_train image: flask-demo:latest environment: - REDIS_HOST=redis-server redis-server: image: redis:latest client: image: xiaopeng163/net-box:latest command: sh -c "while true; do sleep 3600; done;"
- 使用
docker-compose up -d --scale flask=3
可以将flask的容器增加到3个,同时也可以令flask=1
将3个容器减少到1个:
- 使用
docker container exec -it root_client_1 sh
进入容器,然后去ping flask
(flask是在yml文件中定义的service名称),会发现每次ping通的flask容器IP地址是不一样的:
上面情况就是docker compose实现的负载均衡,使得每次请求可以转发给扩展后不同的容器服务
6. docker compose环境变量
- docker-compose.yml:
services: flask: build: context: ./flask dockerfile: Dockerfile_train image: flask:latest environment: - REDIS_HOST=redis-server - REDIS_PASS=${REDIS_PASSWORD} networks: - backend - frontend redis-server: image: redis:latest command: redis-server --requirepass ${REDIS_PASSWORD} networks: - backend
在文件中可以看到
environment
设置了多个环境变量,因为这里变量是密码,将这个yml文件提供给他人时肯定不能直接写入,所以需要将这个环境变量设置为从外部读取的环境变量:
- 在docker-compose.yml同一个目录下创建
.env
文件,其中写入REDIS_PASSWORD=123456
,在进行代码提交的时候会将该文件加入到.gitignore
中。再次进行docker-compose up -d
时会自动读取该文件,使用docker-compose config
可以查看yml文件中被替换的环境变量:
- 如果不想设置该文件名为
.env
,则需要使用docker-compose --env-file .\自定义的文件名 up -d
以及docker-compose --env-file .\自定义的文件名 config
才能使得配置文件生效
7. 服务依赖和健康检查
7.1 服务依赖
在
docker-compose.yml
文件中可以定义多个service,它们之间可能存在依赖关系,所以它们的启动顺序也是很重要的。实现服务依赖主要是通过depends_on
实现:
- docker-compose.yml:
services: flask: build: context: ./flask dockerfile: Dockerfile_train image: flask:latest environment: - REDIS_HOST=redis-server - REDIS_PASS=${REDIS_PASSWORD} depends_on: - redis-server networks: - backend - frontend
上面表示flak依赖于
redis-server
,需要先启动redis才能启动flask
7.2 健康检查
有些服务已经启动了,但是无法对外正常提供服务,所以需要检查服务是否正常。在
Dockerfile
和docker-compose.yml
都是可以定义健康检查的命令的:
7.2.1 在Dockerfile中定义健康检查
- flask的Dockerfile中增加
HEALTHCHECK
命令,这里表示定期30s检查5000端口是否正常启动,失败的话就exit 1
:RUN pip install flask redis && \ apt-get update && \ apt-get install -y curl && \ groupadd -r flask && useradd -r -g flask flask && \ mkdir /src && \ chown -R flask:flask /src USER flask COPY app.py /src/app.py WORKDIR /src ENV FLASK_APP=app.py REDIS_HOST=redis EXPOSE 5000 HEALTHCHECK --interval=30s --timeout=3s \ CMD curl -f http://localhost:5000/ || exit 1 CMD ["flask", "run", "-h", "0.0.0.0"]
- 重新构建flask的镜像并且启动一个容器,此时会发现
STATUS
中出现unhealthy
,因为app.py需要使用到redis,而redis服务还未启动:
- 启动redis容器后,
STATUS
变为healthy,同样可以是docker inspect
去查看记录健康检查的日志:
7.2.2 在docker-compose.yml中定义健康检查
- 对flask进行健康检查,将
docker-compose.yml
文件修改如下(上节在flask的Dockerfile文件中添加的代码可以删除):version: "3.8" services: flask: build: context: ./flask dockerfile: Dockerfile_train image: flask:latest environment: - REDIS_HOST=redis-server - REDIS_PASS=${REDIS_PASSWORD} healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5000"] interval: 30s timeout: 3s retries: 3 start_period: 40s depends_on: - redis-server networks: - backend - frontend
- 使用
docker-compose up -d
创建服务,再使用docker-compose ps查看信息时会显示健康状态(这里将docker-compose.yml
文件中redis密码修改同.env
文件不一致所以显示unhealthy):
- 此时发现依赖于flask的nginx还是启动了,要对nginx进行健康检查需要修改
docker-compose.yml
中nginx部分为:nginx: image: nginx:stable-alpine ports: - 8000:80 depends_on: flask: condition: service_healthy volumes: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro - ./var/log/nginx:/var/log/nginx networks: - frontend
- 再次使用
docker-compose up -d
创建服务,会显示无法创建nginx服务: