Docker 进阶 | Docker Compose

1,085 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

Docker 进阶 | Docker Compose

背景

以前通过DockerFile 使用 Docker Build 命令生成镜像 通过 Docker Run 去执行, 都是手动去操作 并且是单个容器 。如果有100 个微服务, 并且都存在依赖关系, 那我们该去怎么维护呢?使用 Docker Compose 就可以轻松高效的管理容器。[定义和运行多个容器]

官方介绍

  • Compose 是一个 定义运行 多个容器的程序。

  • 需要 Yaml file 配置文件

  • single command 命令

Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see the list of features.

所有的环境都可以使用 Docker compose

Compose works in all environments: production, staging, development, testing, as well as CI workflows. You can learn more about each case in Common Use Cases.

使用步骤

Using Compose is basically a three-step process:

  1. Define your app’s environment with a Dockerfile so it can be reproduced anywhere.
    • Dockerfile 保证我们的项目在任何地方可以运行
  2. Define the services that make up your app in docker-compose.yml so they can be run together in an isolated environment.
    • services 什么是服务?

    • Docker-compose.yml 这个文件怎么写?

  3. Run docker compose up and the Docker compose command starts and runs your entire app. You can alternatively run docker-compose up using the docker-compose binary.
    • 启动项目

作用: 批量容器编排。

Compose 是 Docker 官方的开源项目, 独立项目, 需要安装

Dockerfile 让程序在任何地方运行。Web服务, redis,mysql,nginx,多个容器, 我们可以写一个 Compose 文件, 将这些服务批量打包进来

docker-compose.yml

version: "3.9"  # optional since v1.27.0
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
      - logvolume01:/var/log
    links:
      - redis
  redis:
    image: redis
volumes:
  logvolume01: {}

docker-compose up 一键上线所有服务

Compose: 重要概念

  • 服务 services, 应用(web, redis,mysql) 使用 compose 进行编排
  • 项目 project。 一组关联的容器

安装

  1. 下载

    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 curl -L "https://get.daocloud.io/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100   423  100   423    0     0    177      0  0:00:02  0:00:02 --:--:--   177
    100 12.1M  100 12.1M    0     0  3832k      0  0:00:03  0:00:03 --:--:-- 31.4M
    
    # 查看下载结果
    $ cd /usr/local/bin
    
    [root@VM-8-10-centos ~]# cd /usr/local/bin
    [root@VM-8-10-centos bin]# ll
    总用量 12476
    -rwxr-xr-x 1 root root      383 12月 10 2019 chardetect
    -rwxr-xr-x 1 root root      389 12月 10 2019 cloud-init
    -rwxr-xr-x 1 root root     1781 12月 10 2019 cloud-init-per
    -rw-r--r-- 1 root root 12737304 10月  1 14:26 docker-compose # 下载成功
    
  2. 授权

    $ sudo chmod +x /usr/local/bin/docker-compose # 授权
    $ docker-compose version # 测试安装结果
    
      docker-compose version 1.29.2, build 5becea4c
      docker-py version: 5.0.0
      CPython version: 3.7.10
      OpenSSL version: OpenSSL 1.1.0l  10 Sep 2019
    # 安装成功
    

体验 Get started

Python 应用: 计数器, 使用 Redis 计数

1.Setup : Define the application dependencies

创建项目需要的依赖文件~

# 创建项目存储目录
$ cd ~
$ mkdir app  
$ cd app
# 创建项目文件阿基
$ mkdir composetest
$ cd composetest
# 创建 app.py 文件
$ vim app.py  # 源码在下面
# 创建 requirements.txt 写上项目需要的依赖~
$ vim  requirements.txt 

app.py

import time

import redis
from flask import Flask

app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)

def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)

@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.\n'.format(count)

requirements.txt

flask
redis

2.Create a Dockerfile

创建 Dockerfile

$ vim Dockerfile

Dockerfile

# syntax=docker/dockerfile:1
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]

3.Define services in a Compose file

在 Compose 里面定义服务

vim docker-compose.yml

docker-compose.yml

version: "3.9"
services:
  web:
    build: .
    ports:
      - "5000:5000"
  redis:
    image: "redis:alpine"

4.Build and run your app with Compose

Build & Run 我们的应用

$ docker-compose up 
# 或
$ docker-compose up -d  # 后台启动

Building web
Sending build context to Docker daemon  5.632kB
Step 1/10 : FROM python:3.7-alpine
 ---> a436fb2c575c
Step 2/10 : WORKDIR /code
 ---> Using cache
 ---> 02545a7b7f7c
Step 3/10 : ENV FLASK_APP=app.py
 ---> Using cache
 ---> ed7fe3f6c9c6
Step 4/10 : ENV FLASK_RUN_HOST=0.0.0.0
 ---> Using cache
 ---> 80ea3d59f3f4
Step 5/10 : RUN apk add --no-cache gcc musl-dev linux-headers
 ---> Running in d2e239100b80
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz
^CGracefully stopping... (press Ctrl+C again to force)
[root@VM-8-10-centos composetest]# ^C
[root@VM-8-10-centos composetest]# clear

[root@VM-8-10-centos composetest]# sudo docker-compose up 
sudo: docker-compose:找不到命令
[root@VM-8-10-centos composetest]# su docker-compose up 
su: 用户 docker-compose 不存在
[root@VM-8-10-centos composetest]# docker-compose up 
Building web
Sending build context to Docker daemon  5.632kB
Step 1/10 : FROM python:3.7-alpine
 ---> a436fb2c575c
Step 2/10 : WORKDIR /code
 ---> Using cache
 ---> 02545a7b7f7c
Step 3/10 : ENV FLASK_APP=app.py
 ---> Using cache
 ---> ed7fe3f6c9c6
Step 4/10 : ENV FLASK_RUN_HOST=0.0.0.0
 ---> Using cache
 ---> 80ea3d59f3f4
Step 5/10 : RUN apk add --no-cache gcc musl-dev linux-headers
 ---> Running in 7d9a832e54e3
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz
(1/13) Installing libgcc (10.3.1_git20210424-r2)
(2/13) Installing libstdc++ (10.3.1_git20210424-r2)
(3/13) Installing binutils (2.35.2-r2)
(4/13) Installing libgomp (10.3.1_git20210424-r2)
(5/13) Installing libatomic (10.3.1_git20210424-r2)
(6/13) Installing libgphobos (10.3.1_git20210424-r2)
(7/13) Installing gmp (6.2.1-r0)
(8/13) Installing isl22 (0.22-r0)
(9/13) Installing mpfr4 (4.1.0-r0)
(10/13) Installing mpc1 (1.2.1-r0)
(11/13) Installing gcc (10.3.1_git20210424-r2)
(12/13) Installing linux-headers (5.10.41-r0)
(13/13) Installing musl-dev (1.2.2-r3)
Executing busybox-1.33.1-r3.trigger
OK: 140 MiB in 48 packages
Removing intermediate container 7d9a832e54e3
 ---> c6b8ad3388a1
Step 6/10 : COPY requirements.txt requirements.txt
 ---> b25eb321aea3
Step 7/10 : RUN pip install -r requirements.txt
 ---> Running in c9b03beaca70
Collecting flask
  Downloading Flask-2.0.1-py3-none-any.whl (94 kB)
Collecting redis
  Downloading redis-3.5.3-py2.py3-none-any.whl (72 kB)
Collecting Jinja2>=3.0
  Downloading Jinja2-3.0.1-py3-none-any.whl (133 kB)
Collecting click>=7.1.2
  Downloading click-8.0.1-py3-none-any.whl (97 kB)
Collecting Werkzeug>=2.0
  Downloading Werkzeug-2.0.1-py3-none-any.whl (288 kB)
Collecting itsdangerous>=2.0
  Downloading itsdangerous-2.0.1-py3-none-any.whl (18 kB)
Collecting importlib-metadata
  Downloading importlib_metadata-4.8.1-py3-none-any.whl (17 kB)
Collecting MarkupSafe>=2.0
  Downloading MarkupSafe-2.0.1.tar.gz (18 kB)
Collecting typing-extensions>=3.6.4
  Downloading typing_extensions-3.10.0.2-py3-none-any.whl (26 kB)
Collecting zipp>=0.5
  Downloading zipp-3.6.0-py3-none-any.whl (5.3 kB)
Building wheels for collected packages: MarkupSafe
  Building wheel for MarkupSafe (setup.py): started
  Building wheel for MarkupSafe (setup.py): finished with status 'done'
  Created wheel for MarkupSafe: filename=MarkupSafe-2.0.1-cp37-cp37m-linux_x86_64.whl size=14616 sha256=a2be1498a2fa606910a87342770f0f777eb9ce4a73a209969fbded2bfb5bd0b5
  Stored in directory: /root/.cache/pip/wheels/1a/18/04/e3b5bd888f000c2716bccc94a565239f9defc47ef93d9e7bea
Successfully built MarkupSafe
Installing collected packages: zipp, typing-extensions, MarkupSafe, importlib-metadata, Werkzeug, Jinja2, itsdangerous, click, redis, flask
Successfully installed Jinja2-3.0.1 MarkupSafe-2.0.1 Werkzeug-2.0.1 click-8.0.1 flask-2.0.1 importlib-metadata-4.8.1 itsdangerous-2.0.1 redis-3.5.3 typing-extensions-3.10.0.2 zipp-3.6.0
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
Removing intermediate container c9b03beaca70
 ---> 6ca85fbb3685
Step 8/10 : EXPOSE 5000
 ---> Running in 58a8698ea25a
Removing intermediate container 58a8698ea25a
 ---> 313ecb8fbfbd
Step 9/10 : COPY . .
 ---> 11705881d5c4
Step 10/10 : CMD ["flask", "run"]
 ---> Running in e2c0d681ab04
Removing intermediate container e2c0d681ab04
 ---> e1a776378e7e
Successfully built e1a776378e7e
Successfully tagged composetest_web:latest
WARNING: Image for service web was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Pulling redis (redis:alpine)...
alpine: Pulling from library/redis
a0d0a0d46f8b: Already exists
a04b0375051e: Pull complete
cdc2bb0f9590: Pull complete
8f19735ec10c: Pull complete
ac5156a4c6ca: Pull complete
7b7e1b3fdb00: Pull complete
Digest: sha256:fa785f9bd167b94a6b30210ae32422469f4b0f805f4df12733c2f177f500d1ba
Status: Downloaded newer image for redis:alpine
Creating composetest_web_1   ... done
Creating composetest_redis_1 ... done
Attaching to composetest_redis_1, composetest_web_1
redis_1  | 1:C 01 Oct 2021 06:59:57.739 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1  | 1:C 01 Oct 2021 06:59:57.739 # Redis version=6.2.5, bits=64, commit=00000000, modified=0, pid=1, just started
redis_1  | 1:C 01 Oct 2021 06:59:57.739 # 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 01 Oct 2021 06:59:57.740 * monotonic clock: POSIX clock_gettime
redis_1  | 1:M 01 Oct 2021 06:59:57.741 * Running mode=standalone, port=6379.
redis_1  | 1:M 01 Oct 2021 06:59:57.741 # 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 01 Oct 2021 06:59:57.741 # Server initialized
redis_1  | 1:M 01 Oct 2021 06:59:57.741 # 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 01 Oct 2021 06:59:57.741 * Ready to accept connections
web_1    |  * Serving Flask app 'app.py' (lazy loading)
web_1    |  * Environment: production
web_1    |    WARNING: This is a development server. Do not use it in a production deployment.
web_1    |    Use a production WSGI server instead.
web_1    |  * Debug mode: off
web_1    |  * Running on all addresses.
web_1    |    WARNING: This is a development server. Do not use it in a production deployment.
web_1    |  * Running on http://172.19.0.3:5000/ (Press CTRL+C to quit)

# 启动成功

image.png

[root@VM-8-10-centos composetest]# curl localhost:5000
Hello World! I have been seen 2 times.
[root@VM-8-10-centos composetest]# curl localhost:5000
Hello World! I have been seen 3 times.
[root@VM-8-10-centos composetest]# curl localhost:5000
Hello World! I have been seen 4 times.
[root@VM-8-10-centos composetest]# curl localhost:5000
Hello World! I have been seen 5 times.
[root@VM-8-10-centos composetest]# curl localhost:5000
Hello World! I have been seen 6 times.
[root@VM-8-10-centos composetest]# curl localhost:5000
Hello World! I have been seen 7 times.

总结

  • 准备应用 app.py
  • Dockerfile 应用打包为镜像
  • Docker-compose yaml 定义整个服务, 需要的环境
  • 启动 compose 项目 docker-compose up

流程

  • 创建网络
  • 执行 Docker-compose-yml
  • 启动所有的服务
$ ls
# 准备的文件
app.py  docker-compose.yml  Dockerfile  requirements.txt

规则

  1. Docker images 会自动把依赖的镜像全部下载下来
[root@VM-8-10-centos composetest]# docker images
REPOSITORY        TAG                IMAGE ID       CREATED          SIZE
composetest_web   latest             e1a776378e7e   11 minutes ago   184MB
python            3.7-alpine         a436fb2c575c   3 weeks ago      41.9MB
redis             5.0.9-alpine3.11   3661c84ee9d0   17 months ago    29.8MB
  1. docker service 现在没有在服务中
[root@VM-8-10-centos composetest]# docker ps
CONTAINER ID   IMAGE             COMMAND                  CREATED          STATUS         PORTS                                       NAMES
52a3bb4546dd   redis:alpine      "docker-entrypoint.s…"   14 minutes ago   Up 3 minutes   6379/tcp                                    composetest_redis_1
480ad107851f   composetest_web   "flask run"              14 minutes ago   Up 3 minutes   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   composetest_web_1
[root@VM-8-10-centos composetest]# docker service ls # 查看所有的服务
Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.

默认服务名 文件名_服务名_ _num, 例如, composetest_web_1

_num 代表的是副本数量, 集群状态下使用

  1. 网络规则

    $ docker network ls
    [root@VM-8-10-centos composetest]# docker network ls
    NETWORK ID     NAME                  DRIVER    SCOPE
    acaeca217cc5   bridge                bridge    local
    152181e8650e   composetest_default   bridge    local
    3edbb9cd9ab0   host                  host      local
    dad0ff6720e0   none                  null      local
    

    自动帮我们创建了一个网络 composetest_default

    项目中的内容都在同个网络下面, 他们之间都可以通过域名访问

    mysql:3360

    查看网络细节

    $ docker network inspect composetest_default
    
    "Containers": {
                "480ad107851fc859c79497f95999b917fbfd75e28ec8994f019a740f74798580": {
                    "Name": "composetest_web_1",
                    "EndpointID": "48252e19ea813a665fdbac7ff456bf893350e5f48b2452de7d3c2b4fe8f867ae",
                    "MacAddress": "02:42:ac:13:00:02",
                    "IPv4Address": "172.19.0.2/16",
                    "IPv6Address": ""
                },
                "52a3bb4546dd6526ff7e0fe11e2c0b33bd1efa8516da353845f84520d45e355f": {
                    "Name": "composetest_redis_1",
                    "EndpointID": "15ddf67cbbdbbad1fc922142a66ef9dfb302ed8a4ba65ad2d8b068228d512015",
                    "MacAddress": "02:42:ac:13:00:03",
                    "IPv4Address": "172.19.0.3/16",
                    "IPv6Address": ""
                }
    },
    

    如果在同一个网络下, 我们可以直接通过域名访问。保证高可用。

停止

# 进入项目文件
$ docker-compose down # 默认启动 不加-d 的那种 使用 Ctrl+c

以前都是单个 docker run 启动容器, 现在可以通过 docker-compose 编写 yaml 配置文件, 通过 docker-compose up 一键启动、停止所有服务

总结

  1. Docker 镜像。 Run => 容器
  2. DockerFile 构建镜像 => 服务打包
  3. docker-compose 启动项目 => 编排多个微服务/环境
  4. Docker 网络