Docker Compose是一种用于定义和共享多容器应用程序的工具。本文主要介绍 Compose 项目的具体情况,以及如何进行安装和使用。
Compose 项目是 Docker 官方的开源项目,负责实现对基于 Docker器的多应用服务的快速编排。从功能上看,跟 OpenStack 中的 Heat 十分类似。其代码目前在 https://github.com/docker/compose上开源。Compose 定位是“定义和运行多个 Docker 容器的应用”,其前身是开源项目 Fig, 目前仍然兼容 Fig 格式的模板文件。
通过前面的学习我们已经知道使用一个 Dockerfile 模板文件,可以让用户很方便地定义一个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括前端的负载均衡容器等。
Compose 恰好满足了这样的需求。它允许用户通过一个单独的 docker-compose.yml模板文件 (YAML 格式)来定义一组相关联的应用容器为一个服务栈 (stack)。
Compose 中有两个重要的概念:
服务 (service) :一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
项目 (project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。
Compose 的默认管理对象是项⽬,通过子命令对项⽬中的多个服务进行便捷的生命周期管理。
Compose 项目由 Python 编写,实现上调用了 Docker 服务提供的 API 来对容器进行管理。因此,只要所操作的平台支持 Docker API, 就可以在其上利用 Compose 来进行编排管理。
以下示例通过一个具体的示例应用程序说明了 Compose 规范概念。
将应用程序拆分为前端 Web 应用程序和后端服务。
前端在运行时使用由基础设施管理的 HTTP 配置文件进行配置,提供外部域名和由平台的安全机密存储注入的 HTTPS 服务器证书。
后端将数据存储在持久卷中。
两个服务在隔离的后端网络上相互通信,而前端也连接到前端网络并公开端口 443 以供外部使用。
(External user) --> 443 [frontend network]
|
+--------------------+
| frontend service |...ro...
| "webapp" |...ro... #secured
+--------------------+
|
[backend network]
|
+--------------------+
| backend service | r+w ___________________
| "database" |=======( persistent volume )
+--------------------+ \_________________/
2 个服务,由 Docker 镜像支持:webapp和database
1 个密钥(HTTPS 证书),注入前端
1个配置(HTTP),注入前端
1 个持久卷,附加到后端
2 个网络
services:
frontend:
image: awesome/webapp
ports:
- "443:8043"
networks:
- front-tier
- back-tier
configs:
- httpd-config
secrets:
- server-certificate
backend:
image: awesome/database
volumes:
- db-data:/etc/data
networks:
- back-tier
volumes:
db-data:
driver: flocker
driver_opts:
size: "10GiB"
configs:
httpd-config:
external: true
secrets:
server-certificate:
external: true
networks:
# The presence of these objects is sufficient to define them
front-tier: {}
back-tier: {}
Compose ⽀持 Linux、macOS、Windows 10 三⼤平台。Compose 可以通过 Python 的包管理⼯ 具 pip 进⾏安装,也可以直接下载编译好的⼆进制⽂件使⽤,甚⾄能够直接在 Docker 容器中运⾏。 前两种⽅式是传统⽅式,适合本地环境下安装使⽤;最后⼀种⽅式则不破坏系统环境,更适合云计算场景。
这种⽅式是将 Compose 当作⼀个 Python 应⽤来从 pip 源中安装。执⾏安装命令:
sudo pip install -U docker-compose
[root@iZhp33j6fklnmhbf0lz2obZ ~]# sudo pip3 install -U docker-compose
WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
Looking in indexes: http://mirrors.cloud.aliyuncs.com/pypi/simple/
Collecting docker-compose
Downloading http://mirrors.cloud.aliyuncs.com/pypi/packages/f3/3e/ca05e486d44e38eb495ca60b8ca526b192071717387346ed1031ecf78966/docker_compose-1.29.2-py2.py3-none-any.whl (114 kB)
|████████████████████████████████| 114 kB 51.5 MB/s
Collecting distro<2,>=1.5.0
Downloading http://mirrors.cloud.aliyuncs.com/pypi/packages/e1/54/d08d1ad53788515392bec14d2d6e8c410bffdc127780a9a4aa8e6854d502/distro-1.7.0-py3-none-any.whl (20 kB)
Collecting python-dotenv<1,>=0.13.0
...
在 Linux 上的也安装⼗分简单,从 官⽅ GitHub Release 处直接下载编译好的⼆进制⽂件即可。例 如,在 Linux 64 位系统上直接下载对应的⼆进制包。
sudo curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-` uname -s`-`uname -m` > /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose -v
[root@iZhp33j6fklnmhbf0lz2obZ ~]# docker-compose -v
/usr/local/lib/python3.6/site-packages/paramiko/transport.py:33: CryptographyDeprecationWarning: Python 3.6 is no longer supported by the Python core team. Therefore, support for it is deprecated in cryptography and will be removed in a future release.
from cryptography.hazmat.backends import default_backend
docker-compose version 1.29.2, build unknown
可以添加 bash 补全命令:
curl -L https://raw.githubusercontent.com/docker/compose/1.29.2/contrib/completion/bash/d ocker-compose > /etc/bash_completion.d/docker-compose
[root@iZhp33j6fklnmhbf0lz2obZ ~]# curl -L https://raw.githubusercontent.com/docker/compose/1.29.2/contrib/completion/bash/d ocker-compose > /etc/bash_completion.d/docker-compose
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:34 --:--:-- 0
Compose 既然是一个 Python 应用,自然也可以直接用容器来执行它:
curl -L https://github.com/docker/compose/releases/download/1.29.2/run.sh > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
[root@iZhp33j6fklnmhbf0lz2obZ ~]# curl -L https://github.com/docker/compose/releases/download/1.29.2/run.sh > /usr/local/bin/docker-compose
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 2585 100 2585 0 0 1729 0 0:00:01 0:00:01 --:--:-- 3801
[root@iZhp33j6fklnmhbf0lz2obZ ~]# chmod +x /usr/local/bin/docker-compose
实际上,查看下载的 run. sh 脚本内容,如下:
set -e
VERSION="1.29.2"
IMAGE="docker/compose:$VERSION"
# Setup options for connecting to docker host
if [ -z "$DOCKER_HOST" ]; then
DOCKER_HOST='unix:///var/run/docker.sock'
fi
if [ -S "${DOCKER_HOST#unix://}" ]; then
DOCKER_ADDR="-v ${DOCKER_HOST#unix://}:${DOCKER_HOST#unix://} -e DOCKER_HOST"
else
DOCKER_ADDR="-e DOCKER_HOST -e DOCKER_TLS_VERIFY -e DOCKER_CERT_PATH"
fi
# Setup volume mounts for compose config and context
if [ "$(pwd)" != '/' ]; then
VOLUMES="-v $(pwd):$(pwd)"
fi
if [ -n "$COMPOSE_FILE" ]; then
COMPOSE_OPTIONS="$COMPOSE_OPTIONS -e COMPOSE_FILE=$COMPOSE_FILE"
compose_dir="$(dirname "$COMPOSE_FILE")"
# canonicalize dir, do not use realpath or readlink -f
# since they are not available in some systems (e.g. macOS).
compose_dir="$(cd "$compose_dir" && pwd)"
fi
if [ -n "$COMPOSE_PROJECT_NAME" ]; then
COMPOSE_OPTIONS="-e COMPOSE_PROJECT_NAME $COMPOSE_OPTIONS"
fi
if [ -n "$compose_dir" ]; then
VOLUMES="$VOLUMES -v $compose_dir:$compose_dir"
fi
if [ -n "$HOME" ]; then
VOLUMES="$VOLUMES -v $HOME:$HOME -e HOME" # Pass in HOME to share docker.config and allow ~/-relative paths to work.
fi
i=$#
while [ $i -gt 0 ]; do
arg=$1
i=$((i - 1))
shift
case "$arg" in
-f|--file)
value=$1
i=$((i - 1))
shift
set -- "$@" "$arg" "$value"
file_dir=$(realpath "$(dirname "$value")")
VOLUMES="$VOLUMES -v $file_dir:$file_dir"
;;
*) set -- "$@" "$arg" ;;
esac
done
# Setup environment variables for compose config and context
ENV_OPTIONS=$(printenv | sed -E "/^PATH=.*/d; s/^/-e /g; s/=.*//g; s/\n/ /g")
# Only allocate tty if we detect one
if [ -t 0 ] && [ -t 1 ]; then
DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS -t"
fi
# Always set -i to support piped and terminal input in run/exec
DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS -i"
# Handle userns security
if docker info --format '{{json .SecurityOptions}}' 2>/dev/null | grep -q 'name=userns'; then
DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS --userns=host"
fi
# shellcheck disable=SC2086
exec docker run --rm $DOCKER_RUN_OPTIONS $DOCKER_ADDR $COMPOSE_OPTIONS $ENV_OPTIONS $VOLUMES -w "$(pwd)" $IMAGE "$@"
可以看到,它其实是下载了 docker/compose 镜像并运行。
如果是二进制包方式安装的,删除二进制文件即可:
sudo rm /usr/local/bin/docker-compose
如果是通过 Python pip 工具安装的,则可以执行如下命令删除:
sudo pip uninstall docker-compose
from flask import Flask
from redis import Redis
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
count = redis.incr('hits')
return 'Hello World! 该页面已被访问 {} 次。\n'.format(count)
if __name__ == "__main__":
# 设置运行信息
app.run(host="127.0.0.1", debug=True)
FROM python:3.6-alpine
ADD . /code
WORKDIR /code
RUN pip install redis flask
CMD ["python", "app.py"]
version: '3'
services:
web:
build: .
ports:
- "8000:5000"
volumes:
- .:/code
redis:
image: "redis:alpine"
docker-compose up
[root@iZhp33j6fklnmhbf0lz2obZ admin]# docker-compose up
Starting admin_web_1 ... done
Starting admin_redis_1 ... done
Attaching to admin_web_1, admin_redis_1
redis_1 | 1:C 17 Aug 2022 06:49:50.124 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1 | 1:C 17 Aug 2022 06:49:50.124 # Redis version=7.0.4, bits=64, commit=00000000, modified=0, pid=1, just started
redis_1 | 1:C 17 Aug 2022 06:49:50.124 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
redis_1 | 1:M 17 Aug 2022 06:49:50.125 * monotonic clock: POSIX clock_gettime
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * Running mode=standalone, port=6379.
redis_1 | 1:M 17 Aug 2022 06:49:50.126 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
redis_1 | 1:M 17 Aug 2022 06:49:50.126 # Server initialized
redis_1 | 1:M 17 Aug 2022 06:49:50.126 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1'
for this to take effect.redis_1 | 1:M 17 Aug 2022 06:49:50.126 * Loading RDB produced by version 7.0.4
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * RDB age 1576 seconds
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * RDB memory usage when created 0.82 Mb
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * Done loading RDB, keys loaded: 0, keys expired: 0.
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * DB loaded from disk: 0.000 seconds
redis_1 | 1:M 17 Aug 2022 06:49:50.126 * Ready to accept connections
此时访问本地 8000 端⼝,每访问一次,计数加 1。
[root@iZhp33j6fklnmhbf0lz2obZ admin]# curl -v localhost:8000
* Rebuilt URL to: localhost:8000/
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.61.1
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 44
< Server: Werkzeug/2.0.3 Python/3.6.15
< Date: Wed, 17 Aug 2022 06:56:07 GMT
<
Hello World! 该页面已被访问 8 次。
* Closing connection 0