我们知道使用一个Dockerfile模板文件,可以让用户很方便的定义⼀个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。
例如要实现一个Web项目,除了Web服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等,我们只能一个一个写dockerfile文件,然后bulid构建和run,手动操作单个容器。
Docker Compose恰好满足了这样的需求。它允许⽤户通过⼀个单独的docker-compose.yml
模板⽂件(YAML格式)来定义⼀组相关联的应⽤容器为⼀个项⽬(project)。
docker-compose基础概念
docker-compose项目是docker官方的开源项目,负责实现对docker容器集群的快速编排。
docker-compose将所管理的容器分为三层,分别是工程(project),服务(service)以及容器(containner)
- docker-compose运行目录下的所有文件(docker-compose.yml文件、extends文件或环境变量等)组成一个工程,如无特殊指定,工程名即为当前目录名。
- 一个工程当中,可以包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖。
- 一个服务中可以包括多个容器实例,docker-compose并没有解决负载均衡的问题。因此需要借助其他工具实现服务发现及负载均衡,比如nginx。
docker-compose的安装
Docker安装完成后,docker-compose也随之自动安装:
$ docker-compose --version
docker-compose version 1.25.0, build unknown
最新版本号可以在这里查询:https://github.com/docker/compose/releases
没有安装的可以使用下面的命令进行安装:
$ 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
docker-compose version 1.29.2, build 5becea4c
熟悉python的朋友,可以使用pip去安装docker-Compose:
$ pip install docker-compose
docker-compose文件的语法结构
docker compose文件的语法说明 https://docs.docker.com/compose/compose-file/
version: "3.8" # 可选,docker compose语法的版本
services: # 容器
servicename: # 服务名字,这个名字也是内部 bridge网络可以使用的 DNS name
image: # 镜像的名字
command: # 可选,如果设置,则会覆盖默认镜像里的 CMD命令
environment: # 可选,相当于 docker run里的 --env
volumes: # 可选,相当于docker run里的 -v
networks: # 可选,相当于 docker run里的 --network
ports: # 可选,相当于 docker run里的 -p
servicename2:
volumes: # 可选,相当于 docker volume create
networks: # 可选,相当于 docker network create
程序准备
准备一个Python文件,名字为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 f"Hello Container World! I have been seen {redis.get('hits').decode('utf-8')} times and my hostname is {socket.gethostname()}.\n"
准备Dockerfile
准备一个Dockerfile
FROM python:3.9.5-slim
RUN pip install flask redis && \
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
CMD ["flask", "run", "-h", "0.0.0.0"]
镜像准备
构建flask镜像,准备一个redis镜像。
$ docker image pull redis
$ docker image build -t flask-demo .
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
flask-demo latest e878470f65a4 13 minutes ago 128MB
redis latest 7614ae9453d1 21 months ago 113MB
python 3.9.5-slim c71955050276 2 years ago 115MB
创建一个docker bridge
$ docker network create -d bridge demo-network
adc45a365e7294f56b65464eb88a212624855b52dc568c9f70c4e39c31d43e4a
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
28e45b9b8f4b bridge bridge local
adc45a365e72 demo-network bridge local
29c08e905dab host host local
d466db2f54a1 none null local
创建redis container
创建一个名为redis-server的container,连到demo-network上:
$ docker container run --rm -d --name redis-server --network demo-network redis
ff46dca96a56f8ce739732a87e2407fffcbc4a16651638aed15224ea9e355a6a
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ff46dca96a56 redis "docker-entrypoint.s…" 24 seconds ago Up 23 seconds 6379/tcp redis-server
创建flask container
创建一个名为flask-demo的container,连到demo-network上:
$ docker container run --rm -d --network demo-network --name flask-demo --env REDIS_HOST=redis-server -p 5000:5000 flask-demo
a843952b19fc98f8dfd8a46eac18abb8885834a1f718de5ca265f09795db0f2a
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a843952b19fc flask-demo "flask run -h 0.0.0.0" About a minute ago Up 59 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp flask-demo
ff46dca96a56 redis "docker-entrypoint.s…" 9 minutes ago Up 9 minutes 6379/tcp redis-server
访问http://127.0.0.1:5000应该能看到类似下面的内容,每访问一次,计数加1:
$ curl http://127.0.0.1:5000
Hello Container World! I have been seen 1 times and my hostname is a843952b19fc.
可以把上面的步骤合并到一起,成为一个部署脚本:
# prepare image
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 5000:5000 flask-demo
改造为Docker Compose
以上面的Python Flask + Redis练习为例子,改造成一个docker-compose文件。
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:
docker-compose常用命令
运行这些命令需要结合docker-compose一起使用,且必须要在含有docker-compose.yml
文件的目录中或者使用-f
参数指定docker-compose.yml文件才可以使用,不然报错。
docker-compse up
docker-compse up
创建和启动容器。
$ docker-compose up -d
Starting app_redis-server_1 ... done
Starting app_flask-demo_1 ... done
如果想要服务后台运行,需要带上-d
参数。
docker-compose ps
docker-compose ps
列出容器。
$ docker-compose ps
Name Command State Ports
------------------------------------------------------------------------------------------------------
app_flask-demo_1 flask run -h 0.0.0.0 Up 0.0.0.0:8080->5000/tcp,:::8080->5000/tcp
app_redis-server_1 docker-entrypoint.sh redis ... Up 6379/tcp
down 删除容器、网络、数据卷和镜像
stop/start/restart 停止/启动/重启服务
docker-compose stop
docker-compose stop
停止服务。
$ docker-compose stop
Stopping app_redis-server_1 ... done
Stopping app_flask-demo_1 ... done
docker-compose start
docker-compose start
启动服务。
$ docker-compose start
Starting flask-demo ... done
Starting redis-server ... done
docker-compsoe restart
docker-compose restart
重启服务。
$ docker-compose restart
Restarting app_redis-server_1 ... done
Restarting app_flask-demo_1 ... done
docker-compse down
docker-compse down
删除容器、网络、数据卷和镜像。
$ docker-compose down
Stopping app_redis-server_1 ... done
Stopping app_flask-demo_1 ... done
Removing app_redis-server_1 ... done
Removing app_flask-demo_1 ... done
docker-compse logs
docker-compse logs
查看服务容器的输出。
$ docker-compose logs -f
Attaching to app_flask-demo_1, app_redis-server_1
flask-demo_1 | * Serving Flask app 'app.py'
flask-demo_1 | * Debug mode: off
flask-demo_1 | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
flask-demo_1 | * Running on all addresses (0.0.0.0)
flask-demo_1 | * Running on http://127.0.0.1:5000
flask-demo_1 | * Running on http://172.21.0.3:5000
flask-demo_1 | Press CTRL+C to quit
。。。 。。。
docker-compose top
docker-compose top
显示正在运行的容器进程。
$ docker-compose top
app_flask-demo_1
UID PID PPID C STIME TTY TIME CMD
-----------------------------------------------------------------------------------------------------------------
systemd+ 11265 11244 2 16:31 ? 00:00:00 /usr/local/bin/python /usr/local/bin/flask run -h 0.0.0.0
app_redis-server_1
UID PID PPID C STIME TTY TIME CMD
---------------------------------------------------------------------------
systemd+ 11078 11059 0 16:30 ? 00:00:00 redis-server *:6379
docker-compose scale
docker-compose scale
指定一个服务容器启动数量。
现在推荐使用docker-compose up -d --scale
。
$ docker-compose scale redis-server=2
WARNING: The scale command is deprecated. Use the up command with the --scale flag instead.
Starting app_redis-server_1 ... done
Creating app_redis-server_2 ... done
$ docker-compose ps
Name Command State Ports
---------------------------------------------------------------------------------------------------------
app_flask-demo_1 flask run -h 0.0.0.0 Up 0.0.0.0:8080->5000/tcp,:::8080->5000/tcp
app_redis-server_1 docker-entrypoint.sh redis ... Up 6379/tcp
app_redis-server_2 docker-entrypoint.sh redis ... Up 6379/tcp
$ docker-compose up -d --scale redis-server=1
app_flask-demo_1 is up-to-date
Stopping and removing app_redis-server_2 ... done
Starting app_redis-server_1 ... done
docker-compose exec
docker-compose exec
在容器里面执行命令。
$ docker-compose exec --index=2 redis-server bash
root@7b36aecb80d1:/data#
docker-compse build
docker-compse build
重新构建服务。
将上面的docker-compose.yml修改如下:
version: "3.3"
services:
flask-demo:
build:
context: .
dockerfile: Dockerfile
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:
主要是将flask-demo的镜像改为从Dockerfile构建而不是使用已有镜像。
$ docker-compose build
redis-server uses an image, skipping
Building flask-demo
Step 1/8 : FROM python:3.9.5-slim
---> c71955050276
Step 2/8 : RUN pip install flask redis && groupadd -r flask && useradd -r -g flask flask && mkdir /src && chown -R flask:flask /src
---> Using cache
---> 41091d3d13c5