文章

2. Docker 使用

主要讲解 docker-compose.yaml 如何书写, Docker Swarm 集群管理方式 (未写完), Dockerfile 的书写规范和注意点

2. Docker 使用

1. Docker 测试镜像 (Hello World)

1.1 Hello World 镜像

1
2
runoob@runoob:~$ docker run ubuntu:15.10 /bin/echo "Hello world"
Hello world

各个参数解析:

  • docker: Docker 的二进制执行文件
  • run: 与前面的 docker 组合
  • ubuntu:15.10 指定要运行的镜像, 首先从本地主机上查找镜像是否存在; 若不存在, 则从镜像仓库 Docker Hub 下载公共镜像
  • /bin/echo “Hello world”: 在启动的容器里执行的命令

以上命令可解释为: Docker 以 ubuntu15.10 镜像创建一个新容器, 在容器里执行 bin/echo “Hello world”, 输出结果

1.2. 运行交互式容器

通过 docker 的参数 -i -t, 让容器实现“对话”的能力:

1
2
runoob@runoob:~$ docker run -i -t ubuntu:15.10 /bin/bash
root@0123ce188bd8:/#

各个参数解析:

  • -t: 在新容器内指定一个伪终端 or 终端
  • -i: 允许对容器内的标准输入 (STDIN) 进行交互

在容器中运行命令 cat /proc/versionls 分别查看当前系统的版本信息和当前目录下的文件列表

1
2
3
4
5
root@0123ce188bd8:/#  cat /proc/version
Linux version 4.4.0-151-generic (buildd@lgw01-amd64-043) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10) ) #178-Ubuntu SMP Tue Jun 11 08:30:22 UTC 2019
root@0123ce188bd8:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@0123ce188bd8:/# 

通过运行 exit 命令 or 使用 CTRL+D 退出容器

1
2
3
root@0123ce188bd8:/#  exit
exit
root@runoob:~# 

1.3. 启动容器 (后台模式)

使用以下命令创建以 进程方式 运行的容器:

1
2
runoob@runoob:~$ docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
2b1b7a428627c51ab8810d541d759f072b4fc75487eed05812646b8534a2fe63

在输出中, 我们没有看到期望的 “hello world”, 而是一串长字符

2b1b7a428627c51ab8810d541d759f072b4fc75487eed05812646b8534a2fe63: 容器 ID, 对每个容器来说唯一; 可通过容器 ID 来查看对应的容器发送了什么

可以通过 docker ps 来查看正在运行的容器:

1
2
3
runoob@runoob:~$ docker ps
CONTAINER ID        IMAGE                  COMMAND              ...  
5917eac21c36        ubuntu:15.10           "/bin/sh -c 'while t…"    ...

输出详情介绍:

  • COMMAND: 启动容器时运行的命令

  • CREATED: 容器的创建时间

  • STATUS: 容器状态

    • created (已创建)

    • restarting (重启中)

    • running 或 Up (运行中)

    • removing (迁移中) (如何促使迁移)

    • paused (暂停) (如何促使暂停)

    • exited (停止)

    • dead (死亡) (如何促使死亡)

  • PORTS: 容器的端口信息, 使用的连接类型 (TCP\UDP)

  • NAMES: 自动分配的容器名称

在宿主主机内使用 docker logs 命令, 可查看容器内的标准输出.

1.4. 停止容器

1
docker stop <Container_name>


2. Docker 容器的使用

2.1. Docker 客户端

  • 是与 Docker 守护进程 (Docker Daemon) 交互的命令行工具
  • 非常简单, 可以直接输入 docker 命令来查看到 Docker 客户端所有命令选项

  • 可通过命令 docker <command>--help 更深入了解指定的 Docker 命令使用方法

常用 Docker 客户端命令:

命令 功能 示例
docker run 启动一个新的容器并运行命令 docker run -d ubuntu
docker ps 列出当前正在运行的容器 docker ps
docker ps -a 列出所有容器 (包括已停止的容器) docker ps -a
docker build 使用 Dockerfile 构建镜像 docker build -t my-image .
docker images 列出本地存储的所有镜像 docker images
docker pull 从 Docker 仓库拉取镜像 docker pull ubuntu
docker push 将镜像推送到 Docker 仓库 docker push my-image
docker exec 在运行的容器中执行命令 docker exec -it container_name bash
docker stop 停止一个或多个容器 docker stop container_name
docker start 启动已停止的容器 docker start container_name
docker restart 重启一个容器 docker restart container_name
docker rm 删除一个或多个容器 docker rm container_name
docker rmi 删除一个或多个镜像 docker rmi my-image
docker logs 查看容器的日志 docker logs container_name
docker inspect 获取容器或镜像的详细信息 docker inspect container_name
docker exec -it 进入容器的交互式终端 docker exec -it container_name /bin/bash
docker network ls 列出所有 Docker 网络 docker network ls
docker volume ls 列出所有 Docker 卷 docker volume ls
docker compose up 启动多容器应用 (从 docker-compose.yaml 文件) docker-compose up
docker compose down 停止并删除docker-compose 启动的容器、网络等 docker-compose down
docker info 显示 Docker 系统的详细信息 docker info
docker version 显示 Docker 客户端和守护进程的版本信息 docker version
docker stats 显示容器的实时资源使用情况 docker stats
docker login 登录 Docker 仓库 docker login
docker logout 登出 Docker 仓库 docker logout

常用选项说明:

  • -d: 后台运行容器, 例如 docker run -d ubuntu
  • -it: 以交互式终端运行容器, 例如 docker exec -it container_name bash
  • -t: 为镜像指定标签, 例如 docker build -t my-image

2.2. 容器使用

1. 获取镜像

1
docker pull <image_name>

2. 启动容器

1
docker run -it ubuntu /bin/bash

3. 启动已停止运行的容器

4. 后台运行

1
docker run -itd --name ubuntu-test ubuntu /bin/bash

5. 停止容器运行

[空]

6. 重启容器

[空]

7. 进入后台运行的容器

在使用 -d 参数时启动容器时, 容器运行在后台, 通过以下命令进入:

  • docker attach: 允许你与容器的标准输如 (stdin), 输出 (stdout), 标准错误 (stderr) 进行交互; 退出会导致容器停止运行.
  • docker exec: 推荐使用 docker exec 命令 - 只退出容器终端

docker exec 实例:

1
docker exec -it 243c32535da7 /bin/bash

8. 导入, 导出容器

导出容器 - docker export

实例: docker export 1e560fca3906 > ubuntu.tar

[!note]

  • 此时导出的 ubuntu.tar 只是容器镜像, 其中没有镜像元数据, 运行环境;
  • 可使用 tar -xvf ubuntu.tar -C ubuntu_container 来手动解压 (镜像 tar 不行)

导入容器 - docker import

实例:

  • 将快照文件 ubuntu.tar 导入镜像 test/ubuntu:v1: $ cat docker/ubuntu.tar | docker import - test/ubuntu:v1
  • 指定 URL or 某个目录导入: docker import http://example.com/exampleimage.tgz example/imagerepo

9. 删除容器

[空]

10. 运行一个 Web 应用

尝试使用 docker 构建一个 web 应用程序; 在docker容器中运行一个 Python Flask 应用来运行一个web应用

1
2
runoob@runoob:~# docker pull training/webapp
runoob@runoob:~# docker run -d -P training/webapp python app.py

img

参数说明:

  • -d: 让容器在后台运行
  • -P: 将容器内部使用的网络端口随机映射到我们使用的主机上

11. 查看 WEB 应用容器

使用 docker ps 来查看正在运行的容器:

1
2
3
runoob@runoob:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             ...        PORTS                 
d3d5e39ed9d3        training/webapp     "python app.py"     ...        0.0.0.0:32769->5000/tcp

Docker 开放了 5000 端口 (默认 Python Flask 端口) 映射到主机端口 32769

这时我们可以通过浏览器访问WEB应用

img

可通过 -p 参数来设置不一样的端口:

1
2
3
4
5
runoob@runoob:~$ docker run -d -p 5000:5000 training/webapp python app.py
runoob@runoob:~$ docker ps
CONTAINER ID        IMAGE                             PORTS                     NAMES
bf08b7f2cd89        training/webapp     ...        0.0.0.0:5000->5000/tcp    wizardly_chandrasekhar
d3d5e39ed9d3        training/webapp     ...        0.0.0.0:32769->5000/tcp   xenodochial_hoov

容器内部的 5000 端口映射到宿主机的 5000 端口上

12. 网络端口的快捷方式

docker 还提供了另一个快捷方式 docker port, 使用 docker port 可以查看指定 (ID 或者名字) 容器的某个确定端口映射到宿主机的端口号

上面创建的 web 应用容器 ID 为 bf08b7f2cd89 名字为 wizardly_chandrasekhar.

1
2
3
4
runoob@runoob:~$ docker port bf08b7f2cd89
5000/tcp -> 0.0.0.0:5000
runoob@runoob:~$ docker port wizardly_chandrasekhar
5000/tcp -> 0.0.0.0:5000

13. 查看 WEB 应用程序日志

docker logs <container_ID or container_name> 可查看容器内部的标准输出

1
2
3
4
runoob@runoob:~$ docker logs -f bf08b7f2cd89
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
192.168.239.1 - - [09/May/2016 16:30:37] "GET / HTTP/1.1" 200 -
192.168.239.1 - - [09/May/2016 16:30:37] "GET /favicon.ico HTTP/1.1" 404 -

-f: 让 docker logs 像使用 tail -f 一样来输出容器内部的标准输出

14. 查看 WEB 应用程序容器的进程

命令: docker top

1
2
3
runoob@runoob:~$ docker top wizardly_chandrasekhar
UID     PID         PPID          ...       TIME                CMD
root    23245       23228         ...       00:00:00            python app.py

15. 检查 WEB 应用程序

命令: docker inspect <container_name>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
runoob@runoob:~$ docker inspect wizardly_chandrasekhar
[
    {
        "Id": "bf08b7f2cd897b5964943134aa6d373e355c286db9b9885b1f60b6e8f82b2b85",
        "Created": "2018-09-17T01:41:26.174228707Z",
        "Path": "python",
        "Args": [
            "app.py"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 23245,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2018-09-17T01:41:26.494185806Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
......

16. 移除 WEB 应用容器

删除容器时, 容器必须是停止状态, 否则报错如下:

1
2
runoob@runoob:~$ docker rm wizardly_chandrasekhar
Error response from daemon: You cannot remove a running container bf08b7f2cd897b5964943134aa6d373e355c286db9b9885b1f60b6e8f82b2b85. Stop the container before attempting removal or force remove

2.3. Docker 镜像使用

任务:

  • 管理 & 使用本地镜像
  • 创建镜像

1. 列出镜像列表

命令: docker images or docker image ls

输出如下:

1
2
3
4
5
6
7
runoob@runoob:~$ docker images           
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              14.04               90d5884b1ee0        5 days ago          188 MB
php                 5.6                 f40e9e0f10c8        9 days ago          444.8 MB
ubuntu              15.10               4e3b13c8a266        4 weeks ago         136.3 MB
hello-world         latest              690ed74de00f        6 months ago        960 B
training/webapp     latest              6fae60ef3446        11 months ago       348.8 MB

字段说明:

  • REPOSITORY: 表示镜像的仓库源
  • TAG: 镜像的标签
  • IMAGE ID: 镜像ID
  • CREATED: 镜像创建时间
  • SIZE: 镜像大小

同一仓库源可有多个 TAG, 代表这个仓库源的不同个版本; 如 ubuntu 仓库源里: 有 15.10, 14.04 等多个不同的版本; 使用 REPOSITORY:TAG 来定义不同的镜像. 若要使用版本为 14.04 的 ubuntu 系统镜像来运行容器时, 命令如下:

1
2
runoob@runoob:~$ docker run -t -i ubuntu:14.04 /bin/bash 
root@39e968165990:/# 

2. 获取 or 拖取一个新镜像

命令: docker pull <image_name>

3. 查找镜像

命令: docker search httpd img

  • NAME: 镜像仓库源的名称
  • DESCRIPTION: 镜像的描述
  • OFFICIAL: 是否 docker 官方发布
  • stars: 类似 Github 中 star
  • AUTOMATED: 自动构建

4. 删除镜像

命令: docker rmi <image_name>

5. 创建镜像

通过以下两种方式对镜像进行更改:

  • 更新镜像: 从已经创建的容器中更新镜像, 并且提交这个镜像

    命令: docker commit -m="<描述信息>" -a="<author>" <container_name> <new_image_name>:<version>

  • 构建镜像: 使用 Dockerfile 指令来创建一个新的镜像

    • 前提: 可用的 Dockerfile
    • 命令: docker build -t <image_name>:<version> . (在 Dockerfile 所在目录)

6. 设置镜像标签

命令: docker tag <标签内容> <image_name>:<version>

3. Docker 容器连接

容器中运行一些网络应用, 要让外部也可以访问这些应用, 可以通过 -P-p 参数来指定端口映射

3.1. 网络端口映射

[!Caution]

只有网络模式为 bridge 时需要网络端口映射

[!Note]

  • 网络模式 bridge: 容器会有自己的虚拟网络接口 $\to$ 需要使用网络端口映射
  • 网络模式 none: 容器没有网络连接, 完全隔离与宿主机网络
  • 网络模式 host: 容器的网络栈与宿主机共享, 容器直接能使用宿主机 IP 地址, 端口 $\to$ 不需要使用网络端口映射

创建一个 python 应用的容器:

1
2
runoob@runoob:~$ docker run -d -P training/webapp python app.py
fce072cc88cee71b1cdceb57c2821d054a4a59f67da6b416fceb5593f059fc6d

可指定容器绑定的网络地址: 如绑定 127.0.0.1

我们使用 -P 绑定端口号, 使用 docker ps 可看到容器端口 5000 绑定主机端口 32768

1
2
3
runoob@runoob:~$ docker ps
CONTAINER ID    IMAGE               COMMAND            ...           PORTS                     NAMES
fce072cc88ce    training/webapp     "python app.py"    ...     0.0.0.0:32768->5000/tcp   grave_hopper

可使用 -p 标识来指定容器端口绑定到主机端口

两种方式的区别:

  • -P: 容器内部端口随机映射到主机的端口
  • -p: 容器内部端口绑定到指定的主机端口
1
2
3
4
5
6
runoob@runoob:~$ docker run -d -p 5000:5000 training/webapp python app.py
33e4523d30aaf0258915c368e66e03b49535de0ef20317d3f639d40222ba6bc0
runoob@runoob:~$ docker ps
CONTAINER ID        IMAGE               COMMAND           ...           PORTS                     NAMES
33e4523d30aa        training/webapp     "python app.py"   ...   0.0.0.0:5000->5000/tcp    berserk_bartik
fce072cc88ce        training/webapp     "python app.py"   ...   0.0.0.0:32768->5000/tcp   grave_hopper

可指定容器绑定的网络地址: 如绑定 127.0.0.1

1
2
3
4
5
6
7
runoob@runoob:~$ docker run -d -p 127.0.0.1:5001:5000 training/webapp python app.py
95c6ceef88ca3e71eaf303c2833fd6701d8d1b2572b5613b5a932dfdfe8a857c
runoob@runoob:~$ docker ps
CONTAINER ID        IMAGE               COMMAND           ...     PORTS                                NAMES
95c6ceef88ca        training/webapp     "python app.py"   ...  5000/tcp, 127.0.0.1:5001->5000/tcp   adoring_stonebraker
33e4523d30aa        training/webapp     "python app.py"   ...  0.0.0.0:5000->5000/tcp               berserk_bartik
fce072cc88ce        training/webapp     "python app.py"   ...    0.0.0.0:32768->5000/tcp              grave_hopper

这样就可通过访问 127.0.0.1:5001 来访问容器的 5000 端口

若要绑定 UDP 端口, 可以在端口后面加上 /udp

1
2
3
4
5
6
7
8
runoob@runoob:~$ docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py
6779686f06f6204579c1d655dd8b2b31e8e809b245a97b2d3a8e35abe9dcd22a
runoob@runoob:~$ docker ps
CONTAINER ID        IMAGE               COMMAND           ...   PORTS                                NAMES
6779686f06f6        training/webapp     "python app.py"   ...   5000/tcp, 127.0.0.1:5000->5000/udp   drunk_visvesvaraya
95c6ceef88ca        training/webapp     "python app.py"   ...    5000/tcp, 127.0.0.1:5001->5000/tcp   adoring_stonebraker
33e4523d30aa        training/webapp     "python app.py"   ...     0.0.0.0:5000->5000/tcp               berserk_bartik
fce072cc88ce        training/webapp     "python app.py"   ...    0.0.0.0:32768->5000/tcp              grave_hopper

docker port 命令可让我们快捷查看端口的绑定情况

1
2
runoob@runoob:~$ docker port adoring_stonebraker 5000
127.0.0.1:5001

3.2. Docker 容器互联

互联方法:

  • 网络端口映射
  • 连接系统 允许将多个容器连接在一起, 共享连接信息; docker 连接会创建父子关系: 父容器可看到子容器的信息

3.2.1. 容器命名

创建容器的时候, docker 会自动对它进行命名. 但可以使用 --name 标识来命名容器, 如:

1
2
runoob@runoob:~$  docker run -d -P --name runoob training/webapp python app.py
43780a6eabaaf14e590b6e849235c75f3012995403f97749775e38436db9a441

可使用 docker ps 命令来查看容器名称:

1
2
3
runoob@runoob:~$ docker ps -l
CONTAINER ID     IMAGE            COMMAND           ...    PORTS                     NAMES
43780a6eabaa     training/webapp   "python app.py"  ...     0.0.0.0:32769->5000/tcp   runoob

3.2.2. 新建网络

创建一个新的 Docker 网络

1
docker network create -d bridge test-net

img

参数说明:

-d: 参数指定 Docker 网络类型, 有 bridge、overlay

  • overlay 网络类型用于 Swarm mode; (什么是 overly 网络类型?)

3.2.3 连接容器

运行一个容器并连接到 test-net 网络:

1
docker run -itd --name test1 --network test-net ubuntu /bin/bash

打开新的终端, 再运行一个容器并加入到 test-net 网络:

1
docker run -itd --name test2 --network test-net ubuntu /bin/bash

img

通过 ping 来证明 test1 容器和 test2 容器建立了互联关系: 若 test1, test2 容器内中无 ping 命令, 则在容器内执行以下命令安装 ping

1
2
apt-get update
apt install iputils-ping

在 test1 容器输入以下命令: img

在 test2 容器也会成功连接到: img

[!Note]

多容器之前相互连接, 使用 Docker Compose

3.2.4. 配置 DNS

可在宿主机的 /etc/docker/daemon.json 文件中增加以下内容来设置容器 DNS:

1
2
3
4
5
6
{
  "dns" : [
    "114.114.114.114",
    "8.8.8.8"
  ]
}

设置后, 启动容器的 DNS 会自动配置为 114.114.114.1148.8.8.8; 配置完, 需要重启 docker 才能生效

查看容器 DNS 是否生效可以使用以下命令:

1
docker run -it --rm  ubuntu  cat etc/resolv.conf

img

若只想在指定的容器设置 DNS, 运行以下命令:

1
docker run -it --rm -h host_ubuntu  --dns=114.114.114.114 --dns-search=test.com ubuntu

参数说明:

  • --rm: 容器退出时自动清理容器内部的文件系统

  • -h HOSTNAME or --hostname=HOSTNAME: 设定容器主机名, 会被写到容器内的 /etc/hostname & /etc/hosts 中

  • --dns=IP_ADDRESS: 添加 DNS 服务器到容器的 /etc/resolv.conf 中, 让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名

  • --dns-search=DOMAIN: 设定容器的搜索域, 当设定搜索域为 .example.com 时, 在搜索一个名为 host 的主机时, DNS 不仅搜索 host, 还会搜索 host.example.com (这儿没理解) img

[!Caution]

若容器启动时没指定 --dns--dns-search, Docker 默认使用宿主机的 /etc/resolv.conf 配置容器 DNS

4. Docker 仓库管理

仓库 (Repository) 是集中存放镜像的地方. 以下介绍 Docker Hub. 当然不止 docker hub, 只是远程的服务商不一样, 操作一样.

4.1. Docker Hub

目前 Docker 官方维护了一个公共仓库 Docker Hub. 大部分需求都可通过在 Docker Hub 中直接下载镜像来实现

4.2. 登录和退出

1. 登录

1
docker login

img

2. 退出

1
docker logout

3. 拉取镜像

通过 docker search命令来查找官方仓库中的镜像

以 ubuntu 为关键词进行搜索:

1
docker search ubuntu

img

使用 docker pull 将官方 ubuntu 镜像下载到本地:

1
docker pull ubuntu 

img

4.3. 推送镜像

用户登录后, 可通过 docker push 命令将自己的镜像推送到 Docker Hub

以下命令中的 username 请替换为你的 Docker 账号用户名

1
2
3
4
5
6
7
8
9
10
11
$ docker tag ubuntu:18.04 username/ubuntu:18.04
$ docker image ls

REPOSITORY      TAG        IMAGE ID            CREATED           ...  
ubuntu          18.04      275d79972a86        6 days ago        ...  
username/ubuntu 18.04      275d79972a86        6 days ago        ...  
$ docker push username/ubuntu:18.04
$ docker search username/ubuntu

NAME             DESCRIPTION       STARS         OFFICIAL    AUTOMATED
username/ubuntu

5. Docker Dockerfile

5.1. 使用 Dockerfile 定制镜像

仅讲解如何运行 Dockerfile 文件来定制一个镜像; Dockerfile 文件内指令详解将在下一节中介绍

1. 下面以定制一个 nginx 镜像 (构建好的镜像内会有一个 /usr/share/nginx/html/index.html 文件)

空目录下, 新建一个名为 Dockerfile 文件, 并在文件内添加以下内容:

1
2
FROM nginx
RUN echo '这是一个本地构建的nginx镜像' > /usr/share/nginx/html/index.html

img

2. FROMRUN 指令的作用

FROM: 定制的镜像是基于 FROM 的镜像, 这里的 nginx 就是定制需要的基础镜像

RUN: 用于执行后面跟着的命令行命令. 有以下俩种格式:

shell 格式:

1
RUN <命令行命令>

exec 格式:

1
2
3
RUN ["可执行文件", "参数1", "参数2"]
# 例如: 
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline

[!Caution]

注意!!! Dockerfile 的指令每执行一次都会在 docker 上新建一层. 所以过多无意义的层, 会造成镜像膨胀过大. 例如:

1
2
3
4
FROM centos
RUN **yum** -y **install** **wget**
RUN **wget** -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN **tar** -xvf redis.tar.gz

以上执行会创建 3 层镜像. 可简化格式:

1
2
3
4
FROM centos
RUN **yum** -y **install** **wget** \
  **&&** **wget** -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
  **&&** **tar** -xvf redis.tar.gz

如上, 以 && 符号连接命令, 这样执行后, 只会创建 1 层镜像

5.2. 开始构建镜像

在 Dockerfile 文件的目录下, 执行构建动作. 以下示例, 通过 Dockerfile 构建 nginx:v3

1
docker build -t nginx:v3 .

img


5.3. 指令详解

Dockerfile 指令 说明
FROM 指定基础镜像, 用于后续的指令构建
MAINTAINER 指定Dockerfile的作者/维护者 (已弃用, 推荐使用LABEL指令)
LABEL 添加镜像的元数据, 使用键值对的形式
RUN 在构建过程中在镜像中执行命令
CMD 指定容器创建时的默认命令 (可以被覆盖)
ENTRYPOINT 设置容器创建时的主要命令 (不可被覆盖)
EXPOSE 声明容器运行时监听的特定网络端口.
ENV 在容器内部设置环境变量
ADD 将文件、目录或远程URL复制到镜像中
COPY 将文件或目录复制到镜像中
VOLUME 为容器创建挂载点或声明卷
WORKDIR 设置后续指令的工作目录
USER 指定后续指令的用户上下文
ARG 定义在构建过程中传递给构建器的变量, 可使用 “docker build” 命令设置
ONBUILD 当该镜像被用作另一个构建过程的基础时, 添加触发器
STOPSIGNAL 设置发送给容器以退出的系统调用信号
HEALTHCHECK 定义周期性检查容器健康状态的命令
SHELL 覆盖Docker中默认的shell, 用于RUN、CMD和ENTRYPOINT指令

COPY

功能: 复制, 从指定目录中复制 文件 or 目录 到容器里指定路径

格式:

1
2
COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]

[--chown=<user>:<group>]: 可选参数, 可改变复制到容器内文件的拥有者和属组

<源路径>: 源文件 or 源目录; 可是通配符表达式, 其通配符规则要满足 Go 的 filepath.Match 规则. 如: (了解 Go 的 filepath.Match 规则)

1
2
COPY hom* /mydir/
COPY hom?.txt /mydir/

<目标路径>: 容器内的指定路径, 可自动创建

ADD

ADD 指令和 COPY 格式类型, 功能类型; 推荐用 `COPY`; 不同之处如下:

  • ADD 的优点: 源文件为 tar 格式 (压缩格式: gzip, bzip2, xz), 自动复制 & 解压到 <目标路径>
  • ADD 的缺点: 不解压时, 无法复制 tar 压缩文件. 会令镜像构建缓存失效, 可能会导致镜像构建比较缓慢.

CMD

类似于 RUN 指令, 用于运行程序, 不同点:

  • CMD: 在 docker run 时运行.

  • RUN: 在 docker build 时运行

作用: 为启动的容器指定默认运行程序; 程序运行结束, 容器也就结束. CMD 指令指定的程序可被 docker run 命令行参数中指定运行的程序覆盖

[!Caution]

若 Dockerfile 中存在多个 CMD 指令, 仅最后一条生效

格式:

1
2
3
CMD <shell 命令> 
CMD ["<可执行文件或命令>","<param1>","<param2>",...] 
CMD ["<param1>","<param2>",...]  # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

推荐使用第二种格式; 第一种格式在运行的过程中会转换成第二种格式; 且默认可执行文件是 sh.

ENTRYPOINT (entrypoint, 入口点)

类似 CMD 指令, 但不会被 docker run 的命令行参数指定的指令所覆盖; 且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序.

能被 `docker run` 覆盖的情况: 使用了 ``--entrypoint` 选项, 将覆盖 ENTRYPOINT 指令指定程序

优点: 执行 docker run 时可指定 ENTRYPOINT 运行所需参数

[!Caution]

若 Dockerfile 中存在多个 ENTRYPOINT 指令, 仅最后一个生效

格式:

1
ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

可搭配 CMD 命令使用: 一般变参才用 CMD; 这里的 CMD 是在给 ENTRYPOINT 传参

示例:

假设已通过 Dockerfile 构建了 nginx:test 镜像:

1
2
3
4
FROM nginx

ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参 
  1. 不传参运行
1
$ docker run  nginx:test

​ 容器内默认运行以下命令, 启动主进程

1
nginx -c /etc/nginx/nginx.conf
  1. 传参运行
1
$ docker run  nginx:test -c /etc/nginx/new.conf

​ 容器内默认运行以下命令, 启动主进程

1
nginx -c /etc/nginx/new.conf

ENV

设置 Dockerfile 环境变量; 类似于 C 语言中 #define:

格式:

1
2
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

以下示例设置 NODE_VERSION = 7.2.0, 后续指令中可通过 $NODE_VERSION 引用:

1
2
3
4
ENV NODE_VERSION 7.2.0

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
  && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"

ARG (Argument, 参数)

构建参数, 与 ENV 作用一致.

ARG 与 ENV 不同点:

特性 ARG ENV
作用范围 仅在 构建阶段 可用 构建阶段运行时阶段 均可用
生命周期 构建时有效, 容器运行时不可访问 构建时有效, 容器运行时有效
使用场景 构建时的参数传递; 如版本号, 构建配置等 容器运行时的环境变量; 如路径, 配置等
是否可覆盖 可以在 docker build 时使用 --build-arg 覆盖 可以在容器运行时使用 -e 覆盖; 如: docker run -e MY_VAR=new_value my_image
是否传递给容器 不传递给容器, 容器无法访问 会传递给容器, 容器可以访问

格式:

1
ARG <参数名>[=<默认值>]

VOLUME (volumes, 数据卷)

定义匿名数据卷. 在启动容器时忘记挂载数据卷, 会自动挂载到匿名卷.

作用:

  • 避免重要的数据因容器重启丢失
  • 避免容器不断变大

格式:

1
2
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>

在启动容器 docker run 的时候, 我们可以通过 -v 参数修改挂载点

EXPOSE (expose; 暴露, 端口)

仅仅声明端口

作用:

  • 帮助镜像使用者理解镜像服务守护端口, 以方便配置映射
  • 在运行时使用随机端口映射时, 也就是 docker run -P 时, 会自动随机映射 EXPOSE 的端口

格式:

1
EXPOSE <端口1> [<端口2>...]

WORKDIR (workdir, 工作目录)

指定工作目录; 指定的工作目录在构建镜像的每一层中都存在. 以后各层的当前目录就被改为指定的目录. 如该目录不存在, WORKDIR 会帮你建立目录

docker build 构建镜像过程中的, 每一个 RUN 命令都是新建的一层. 只有通过 WORKDIR 创建的目录才会一直存在. (不太理解)

格式:

1
WORKDIR <工作目录路径>

USER (user; 用户 or 用户组)

指定执行后续命令的用户和用户组; 这边只是切换后续命令执行的用户 (用户和用户组必须提前已经存在)

格式:

1
USER <用户名>[:<用户组>]

HEALTHCHECK (healthcheck; 容器健康检查)

作用

  • 监控容器内运行的应用是否正常, 自动化处理不可用的容器状态
  • 若容器未通过健康检查, Docker 会标记该容器为“不健康”, 并根据 --restart 策略重启容器 (若设置了重启策略)

使用场景

  • 健康检查通常用于需要长期运行的服务, 如 Web 服务器、数据库等

HEALTHCHECK 语法

1
2
HEALTHCHECK [OPTIONS] CMD <command>
HEALTHCHECK NONE: 若基础镜像有健康检查指令, 使用这行可以屏蔽掉其健康检查指令
  • CMD: 指定健康检查命令, Docker 会在容器中运行该命令来检查健康状态
  • OPTIONS: 健康检查的可选参数; 用来配置健康检查的超时时间, 重试次数;

常见选项

  • --interval: 指定健康检查的间隔时间 (默认是 30s)
  • --timeout: 指定健康检查的超时时间 (默认是 30s)
  • --retries: 指定健康检查失败时的最大重试次数 (默认是 3)
  • --start-period: 在容器启动后的指定时间内, 健康检查失败不会被视为失败 (默认是 0s)

健康检查的返回值

  • 0: 表示健康检查通过, 容器正常
  • 1: 表示健康检查失败, 容器不健康
  • 2: 表示健康检查不可用, 容器无法执行健康检查命令 (如: 命令未找到)

例: 检查 HTTP 服务是否正常 若容器内运行一个 Web 服务, 可使用 curl 来检查端口 80 上的 HTTP 服务是否正常:

1
2
HEALTHCHECK --interval=5s --timeout=3s \
  CMD curl --silent --fail http://localhost:80 || exit 1
  • 每隔 5 秒钟检查一次
  • 若健康检查超时 3 秒 or HTTP 请求失败, 返回状态码 1

ONBUILD (onbuild, 其他镜像继承时执行)

父镜像 被用作基础镜像构建子镜像时设置预定义的行为.

典型使用场景:

  • 模板镜像: 当你创建一个基础镜像时, 希望某些操作基于该镜像的构建中自动发生
  • 通用构建逻辑: 在某些构建过程中, 通用的操作(如设置工作目录、复制源代码、安装依赖等)可通过 ONBUILD 指令自动化

语法

1
ONBUILD <instruction>
  • <instruction> 是推迟执行的指令: 如 RUN, COPY, ADD

LABEL (label, 添加元数据)

给镜像添加一些元数据 (metadata), 键值对形式, 格式如下:

1
LABEL <key>=<value> <key>=<value> <key>=<value> ...

如:

1
2
3
4
5
LABEL version="1.0" \
      description="This image contains a simple web app for demo purposes." \
      author="John Doe" \
      license="MIT" \
      os="ubuntu:20.04"

为什么使用 LABEL

  1. 管理和追踪
  2. 自动化工具集成: 自动化工具和 CI/CD 系统会从镜像标签中提取信息
  3. 镜像描述: 可帮助其他开发者 or 系统管理员快速了解镜像的用途, 版本, 作者等信息
  4. 符合规范: 公共镜像仓库 (如 Docker Hub) 要求在镜像中使用 LABEL 来提供元数据, 符合这些要求有助于镜像易于发现和管理

查看镜像标签:

1
2
3
docker inspect --format '' <image_name>
# or 
docker inspect <image_name> # 之后寻找 Config -> Labels

6. Docker Compose

6.1. Compose 简介

用于定义, 运行多容器 Docker 应用程序的工具. 可使用 yaml 文件配置应用程序需要的服务. 使用命令, 可以 yaml 文件配置中创建, 启动所有服务

Compose 使用步骤:

  • Dockerfile 定义应用程序环境
  • docker-compose.yaml 定义构成应用程序的服务, 这样它们可在隔离环境中一起运行
  • 执行 docker-compose up 命令启动, 运行整个应用程序

docker-compose.yaml 的配置案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# yaml 配置实例
version: '3'
services:
  web:
    build: .
    ports:
    - "5000:5000"
    volumes:
    - .:/code
    - logvolume01:/var/log
    links:
    - redis
  redis:
    image: redis
volumes:
  logvolume01: {}

6.2. Compose 安装

Linux: 从 Github 上下载二进制包 (此方法为旧版), 新版的 docker compose, docker engine 默认一起提供. 

下载 Docker Compose 的当前稳定版本:

1
sudo curl -L "https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

赋予执行权限:

1
sudo chmod +x /usr/local/bin/docker-compose

创建软链:

1
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

测试是否安装成功:

1
2
3
docker compose version
# 输出如下: 
Docker Compose version v2.40.3

6.3. Compose 使用

6.3.1. 准备

创建一个测试目录:

1
2
mkdir composetest
cd composetest

创建 app.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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)

redis: 应用程序网络上的 redis 容器主机名, 端口为 6379

创建 requirements.txt:

flask
redis

6.3.2. 创建 Dockerfile 文件

创建 Dockerfile:

1
2
3
4
5
6
7
8
9
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
COPY . .
CMD ["flask", "run"]

内容解释:

  • FROM python:3.7-alpine: 从 Python 3.7 映像构建镜像
  • WORKDIR /code: 工作目录设置 /code
  • ENV FLASK_APP app.py & ENV FLASK_RUN_HOST 0.0.0.0: 设置 flask 命令使用的环境变量
  • RUN apk add --no-cache gcc musl-dev linux-headers: 安装 gcc; 以便如 MarkupSafe, SQLAlchemy 类 Python 包编译加速
  • COPY requirements.txt requirements.txt & RUN pip install -r requirements.txt: 复制 requirements.txt; 安装 Python 依赖项
  • COPY . .: 将 . 项目中的当前目录复制到 . 镜像中的工作目录
  • CMD ["flask", "run"]: 容器提供默认执行命令: flask run

6.3.3. 创建 docker-compose.yaml

1
2
3
4
5
6
7
8
9
# yaml 配置
version: '3'
services:
  web:
    build: .
    ports:
      - "5000:5000"
  redis:
    image: "redis:alpine"

定义两个服务: web, redis

  • web: 使用从 Dockerfile 当前目录中构建的镜像; 将容器和主机绑定到端口 5000; 此示例服务使用 Flask Web 服务器的默认端口 5000 .
  • redis: 使用 Docker Hub 的公共 Redis 映像

6.3.4. 使用 Compose 命令构建

启动应用程序:

1
docker compose up

在后台执行该服务可加上 -d:

1
docker compose up -d

6.4. docker-compose.yaml 配置指令

version 指定本 yaml 依从的 compose 哪个版本制定的

build (这一段没看懂) 指定为构建镜像上下文路径:

  • webapp 服务: 指定从上下文路径 ./dir/Dockerfile 所构建的镜像

    1
    2
    3
    4
    
      version: "3.7"
      services:
        webapp:
          build: ./dir
    
  • 作为具有在上下文指定的路径的对象, 以及可选的 Dockerfile 和 args:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
      version: "3.7"
      services:
        webapp:
          build:
            context: ./dir
            dockerfile: Dockerfile-alternate
            args:
              buildno: 1
            labels:
              - "com.example.description=Accounting webapp"
              - "com.example.department=Finance"
              - "com.example.label-with-empty-value"
            target: prod
    
    • context: 上下文路径
    • dockerfile: 指定构建镜像的 Dockerfile 文件名
    • args: 添加构建参数, 这是只能在构建过程中访问的环境变量.
    • labels: 设置构建镜像的标签.
    • target: 多层构建, 可以指定构建哪一层.

cap_add, cap_drop (这段没看懂) 添加或删除容器拥有的宿主机的内核功能:

1
2
3
4
5
cap_add:
  - ALL # 开启全部权限

cap_drop:
  - SYS_PTRACE # 关闭 ptrace权限

cgroup_parent (cgroup 组是干什么的) 为容器指定父 cgroup 组, 意味着将继承该组的资源限制

1
cgroup_parent: m-executor-abcd

command 覆盖容器启动的默认命令.

1
command: ["bundle", "exec", "thin", "-p", "3000"]

container_name 指定自定义容器名称, 而不是生成的默认名称.

1
container_name: my-web-container

depends_on 设置依赖关系.

  • docker-compose up : 以依赖性顺序启动服务. 以下示例中, 先启动 db & redis, 再启动 web
  • docker-compose up SERVICE : 自动包含 SERVICE 的依赖项. 以下示例中, docker-compose up web 还将创建并启动 db 和 redis. (SERVICE 是什么? 为什么会要创建 db & redis, 能不创建就启动吗?)
  • docker-compose stop : 按依赖关系顺序停止服务. 以下示例中, 先停止 web, 再停止 db & redis

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
      version: "3.7"
      services:
        web:
          build: .
          depends_on:
            - db
            - redis
        redis:
          image: redis
        db:
          image: postgres
    

    [!Caution]

    Web 服务不会等待 redis & db 完全启动之后再启动

deploy 指定与服务的部署和运行有关的配置. 只在 swarm 模式下才会有用. (deploy 的具体作用, swarm 模式详细资料)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
version: "3.7"
services:
  redis:
    image: redis:alpine
    deploy:
      mode: replicated
      replicas: 6
      endpoint_mode: dnsrr
      labels: 
        description: "This redis service label"
      resources:
        limits:
          cpus: '0.50'
          memory: 50M
        reservations:
          cpus: '0.25'
          memory: 20M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s

可以选参数:

  • endpoint_mode: 访问集群服务的方式

    1
    2
    3
    4
    
      endpoint_mode: vip 
      # Docker 集群服务一个对外的虚拟 ip. 所有的请求都会通过这个虚拟 ip 到达集群服务内部机器.
      endpoint_mode: dnsrr
      # DNS 轮询 (DNSRR). 所有请求会自动轮询获取到集群 ip 列表中的一个 ip 地址
    
  • labels: 在服务上设置标签. 可用容器的 labels (跟 deploy 同级的配置) 覆盖 deploy 下的 labels

  • mode: 指定服务提供的模式

    • replicated: 复制服务, 复制指定服务到集群机器上.

    • global: 全局服务, 服务将部署至集群每个节点

    • 图解: 下图中黄色方块是 replicated 模式运行情况; 灰色方块是 global 模式运行情况

      img

  • replicas: mode 为 replicated 时, 需用此参数配置具体运行节点数量

  • resources: 配置服务器资源使用限制; 例如上例子, 配置 redis 集群运行需要的 cpu 的百分比 和 内存占用. 避免占用资源过高出现异常.

  • restart_policy: 配置退出容器时如何重新启动容器

    • condition: 可选 none, on-failure, any (默认值: any). (这三个值代表的意义)

    • delay: 设置重启延时 (默认值: 0).

    • max_attempts: 尝试重新启动容器的次数; (默认值: 一直重试).

    • window: 设置容器重启超时时间 (默认值: 0).

  • rollback_config: 配置在更新失败时如何回滚服务

    • parallelism: 一次要回滚的容器数. 若=0 $\to$ 所有容器同时回滚.

    • delay: 每个容器组回滚间等待时间 (默认 0s)

    • failure_action: 若回滚失败执行的操作. 其中有 continue 或者 pause (默认pause). (continue, pause 分别代表什么? 还能设置什么操作)

    • monitor: 每个容器更新后, 持续观察是否失败的时间 (ns or us or ms or s or m or h) (默认 0s)

    • max_failure_ratio: 回滚时可容忍的故障率 (默认为0).

    • order: 回滚时的操作顺序. 其中有 stop-first (串行回滚) or start-first (并行回滚) (默认 stop-first ).

  • update_config: 配置如何更新服务 (对配置滚动更新有用)

    • parallelism: 一次更新的容器数.

    • delay: 在更新一组容器间的等待时间.

    • failure_action: 若更新失败, 如何操作. 其中有 continue, rollback, pause (默认: pause). (continue, rollback, pause 分别代表什么? 还能设置什么操作)

    • monitor: 每个容器更新后, 持续观察是否失败的时间 (ns or us or ms or s or m or h) (默认 0s).

    • max_failure_ratio: 在更新过程中可容忍的故障率.

    • order: 更新时的操作顺序. 其中有 stop-first (串行回滚) or start-first (并行回滚) (默认stop-first).

[!Caution]

仅支持 V3.4 及更高版本

devices 指定设备映射列表.

1
2
devices:
  - "/dev/ttyUSB0:/dev/ttyUSB0"

dns 自定义 DNS 服务器, 可列单值 or 列多个值.

1
2
3
4
5
dns: 8.8.8.8

dns:
  - 8.8.8.8
  - 9.9.9.9

dns_search 自定义 DNS 搜索域. 可列单值 or 列多个值.

1
2
3
4
5
dns_search: example.com

dns_search:
  - dc1.example.com
  - dc2.example.com

entrypoint
覆盖容器默认 entrypoint. (entrypoint 是干嘛的)

1
2
3
4
5
6
7
8
9
10
entrypoint: /code/entrypoint.sh

# or 
entrypoint:
    - php
    - -d
    - zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20100525/xdebug.so
    - -d
    - memory_limit=-1
    - vendor/bin/phpunit

env_file 从文件添加环境变量. 可列单值 or 列多个值.

1
2
3
4
5
6
7
env_file: .env

# or
env_file:
  - ./common.env
  - ./apps/web.env
  - /opt/secrets.env

environment 环境变量. 可数组或字典, 布尔值(需引号)

1
2
3
environment:
  RACK_ENV: development
  SHOW: 'true'

expose 暴露端口 (仅可指定内部端口为参数)

1
2
3
expose:
 - "3000"
 - "8000"

extra_hosts 添加主机名映射. 类似 docker client --add-host

1
2
3
extra_hosts:
 - "somehost:162.242.195.82"
 - "otherhost:50.31.209.229"

会在此服务内部容器中 /etc/hosts 创建具有 ip 地址&主机名的映射关系:

162.242.195.82  somehost
50.31.209.229   otherhost

healthcheck 检测 docker 服务是否健康运行

1
2
3
4
5
6
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost"] # 设置检测程序
  interval: 1m30s # 设置检测间隔
  timeout: 10s # 设置检测超时时间
  retries: 3 # 设置重试次数
  start_period: 40s # 启动后, 多少秒开始启动检测程序

image 指定容器运行镜像. 以下格式均可:

1
2
3
4
5
image: redis
image: ubuntu:14.04
image: tutum/influxdb
image: example-registry.com:4000/postgresql
image: a4bc65fd # 镜像id

logging 服务日志记录配置 driver: 指定服务容器日志记录驱动程序; 默认值为json-file. 有以下三个选项 (这三个分别代表什么意思)

1
2
3
driver: "json-file"
driver: "syslog"
driver: "none"
  • json-file 驱动程序下, 可使用以下参数, 限制日志得数量和大小

    1
    2
    3
    4
    5
    
      logging:
        driver: json-file
        options:
          max-size: "200k" # 单个文件大小为200k
          max-file: "10" # 最多10个文件
    

    [!Note] 达到文件限制上限, 会自动删除旧文件

  • syslog 驱动程序下, 可使用 syslog-addres 指定日志接收地址.

    1
    2
    3
    4
    
      logging:
        driver: syslog
        options:
          syslog-address: "tcp://192.168.0.42:123"
    

network_mode 设置网络模式.

1
2
3
4
5
network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"
  • networks: 配置容器连接的网络, 引用顶级 networks 下的条目.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
      services:
        some-service:
          networks:
            some-network:
              aliases:
               - alias1
            other-network:
              aliases:
               - alias2
      networks:
        some-network:
          # Use a custom driver
          driver: custom-driver-1
        other-network:
          # Use a custom driver which takes special options
          driver: custom-driver-2
    
  • aliases: 同一网络上的其他容器可以使用服务名称或此别名来连接到对应容器的服务.

restart

1
2
3
4
restart: "no"
restart: always
restart: on-failure
restart: unless-stopped
  • no: 默认重启策略 $\to$ 不会重启容器
  • always: 总是重新启动.
  • on-failure: 非正常退出时 (退出状态非0) 重启容器
  • unless-stopped: 退出时总是重启容器, 不考虑 Docker 守护进程启动时就已停止的容器

[!Caution]

swarm 集群模式改用 restart_policy

secrets 存储敏感数据, 如密码:

1
2
3
4
5
6
7
8
9
10
11
12
13
version: "3.1"
services:

mysql:
  image: mysql
  environment:
    MYSQL_ROOT_PASSWORD_FILE: /run/secrets/my_secret
  secrets:
    - my_secret

secrets:
  my_secret:
    file: ./my_secret.txt

security_opt 修改容器默认的 schema 标签. (解释太少, 需要拓展)

1
2
3
4
5
security-opt: 
  - label:user:USER		# 设置容器的用户标签
  - label:role:ROLE		# 设置容器的角色标签
  - label:type:TYPE		# 设置容器的安全策略标签
  - label:level:LEVEL	# 设置容器的安全等级标签

stop_grace_period 指定在容器无法处理 SIGTERM (or 任何 stop_signal 信号), 等待多久后发送 SIGKILL 信号关闭容器. 默认 10 s (SIGKILL 信号是干什么用的, SIGTERM 信号是干嘛用的)

1
2
stop_grace_period: 1s		# 等待 1 秒
stop_grace_period: 1m30s	# 等待 1 分 30 秒 

stop_signal 设置停止容器的替代信号. 默认情况用 SIGTERM (stop_signal 有哪些信号类型, 有什么区别; 分别用于哪些场景)

1
stop_signal: SIGUSR1	# 使用 SIGUSR1 替代信号 SIGTERM 来停止容器. 

sysctls 设置容器中内核参数, 可使用 数组or字典 (详细说说两个参数代指的意思)

1
2
3
4
5
6
7
sysctls:
  net.core.somaxconn: 1024
  net.ipv4.tcp_syncookies: 0

sysctls:
  - net.core.somaxconn=1024
  - net.ipv4.tcp_syncookies=0

tmpfs 容器内安装一个临时文件系统. 可列单值 or 列多个值. (临时文件系统的作用, 常用于哪些场景)

1
2
3
4
5
tmpfs: /run

tmpfs:
  - /run
  - /tmp

ulimits 覆盖容器默认的 ulimit. (ulimit 是干嘛用的, 为什么要覆盖默认的)

1
2
3
4
5
ulimits:
  nproc: 65535
  nofile:
    soft: 20000
    hard: 40000

volumes 主机数据卷 or 文件挂载到容器

1
2
3
4
5
6
7
version: "3.7"
services:
  db:
    image: postgres:latest
    volumes:
      - "/localhost/postgres.sock:/var/run/postgres/postgres.sock"
      - "/localhost/data:/var/lib/postgresql/data"

7. Docker Machine

简介

可在虚拟主机上安装 Docker 的工具, 并可使用 docker-machine 命令来管理主机; 可集中管理所有 docker 主机(比如快速给 100 台服务器安装上 docker)

[!Caution]

  1. docker-machine 先处于归档状态; 已停止维护
  2. Docker Desktop 已经普及, 已自带完善虚拟机管理; 不需要通过 docker-machine 创建底层环境
  3. 核心技术替代: docker context

现代的替代方案:

场景 推荐方案
本地开发 (Win/Mac) Docker Desktop or OrbStack
管理远程服务器 docker context (最推荐,原生支持 SSH)
自动化运维/云端 Terraform, Ansible or 云厂商提供的容器服务 (ACK, EKS等)
本地多节点集群测试 Kind (Kubernetes in Docker) or Minikube

[!Caution]

此方面应该将精力放在: docker context & Infrastructure as Code (IaC) 工具上
此处应该补充 `docker context` & `Infrastructure as Code (IaC)` 两方面.

8. Swarm 集群管理

[!note]

维度 Docker Swarm Kubernetes (K8s) 备注
设计理念 简单至上; 追求与 Docker 生态无缝集成 功能至上. 追求极致可扩展性, 自动化&复杂资源调度 -
上手难度 极低, 懂 Docker Compose 没有学习成本 , 概念繁多 (Pod, Deployment, Ingress, CRD…) -
基本单位 Container (容器), 直接操作容器实例 Pod, 逻辑封装,一个 Pod 可包含多个紧密耦合容器 K8s 最小调度单位: Pod
安装部署 docker swarm init 通常需借助 kubeadm, k3s or 云厂商提供的托管服务 -
自动扩缩容 不支持原生自动缩放 (需手动 or 编写脚本) 原生支持(HPA/VPA),根据CPU/内存压力自动增减实例 K8s 强项
自愈能力 基础级别,容器挂了会重启,调度策略简单 高级级别,具备健康检查, 自动替换, 状态同步机制 -
网络模型 简单Overlay网络, 内置Ingress负载均衡 复杂且灵活, 支持 CNI 插件 (Calico, Flannel, Istio等) K8s 支持服务网格 (Service Mesh)
生态系统 有限, 依赖 Docker 社区 庞大且统治级, 大多云厂商 or 中间件优先支持 K8s 职业发展首选 K8s
资源消耗 极轻量, Manager 节点不占额外资源 较重, Control Plane(控制面)需可观内存&CPU 边缘计算多选 Swarm

K8s 学习网站:

  • 官方教程: [Kubernetes 文档 Kubernetes](https://kubernetes.io/zh-cn/docs/home/)
  • Killercoda 交互式场景: [Kubernetes Killercoda](https://killercoda.com/kubernetes)
  • Kuboard 中文教程: [Kubernetes教程 Kuboard](https://kuboard.cn/learning/)
  • Kubernetes Handbook (深度专业): [Kubernetes 架构与生态:从云原生到 AI 原生基础设施的构建指南 Jimmy Song](https://jimmysong.io/zh/book/kubernetes-handbook/)
  • K8s 2026 最新标准补充: [Kubernetes Tutorial For Beginners 2026 Learn Kubernetes Kubernetes Tutorial Simplilearn](https://www.youtube.com/watch?v=KFpxOO7PXFg)

8.1. 简介

Docker 集群管理工具; 将 Docker 主机池转变为单个虚拟 Docker 主机. 提供标准 Docker API; 所有已与 Docker 守护程序通信的工具都可用 Swarm 扩展到多个主机

支持的工具包括但不限于:

  • Dokku: 非常轻量级的脚本集合,底层基于 Docker,为简化部署而生
  • Docker Compose
  • Jenkins: 通用的自动化任务执行器

[!Note]

Dokku: 一款 “迷你版 Heroku” 的轻量级 PaaS 平台; 通过简单的 Git 工作流实现应用的自动化部署. 基于 Docker, 极其适合个人开发者 or 小微团队, 只需通过 git push 指令, Dokku 就能自动完成代码构建、容器运行、端口映射及 SSL 证书配置, 是追求极简主义和低成本服务器管理的专业首选

Jenkins: DevOps 领域的工业级自动化枢纽, 主要负责持续集成与持续部署 (CI/CD); 像是拥有上千种插件的“万能流水线”, 能够串联起代码测试、安全扫描、镜像构建及多环境分发等复杂任务; 虽配置相对繁琐, 但在处理大规模、严谨的企业级开发流程时, 它是确保交付质量和流程自动化的核心支柱

8.2. 原理

如图, swarm 集群由管理节点 (manager), 工作节点 (work node) 构成

  • swarm mananger:负责整个集群管理工作; 包括集群配置, 服务管理等 所有跟集群有关工作
  • work node:即图中 available node; 主要负责运行相应的服务执行任务 (task)

img

8.3. 使用

8.3.1. 创建 swarm 集群管理节点(manager)

执行命令:

1
$ docker swarm init --advertise-addr <MANAGER-IP>

在 WSL 中输入: ` docker swarm init –advertise-addr 172.31.226.130` 之后, 输出如下

1
2
3
4
5
6
7
Swarm initialized: current node (u8mvcbr6iyk2ih2jtgkgnqt58) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-4q4z3r9pyt266jb74xibmpi6ssxhb0mk6xpah0eovdnia255wb-d6fy5v722al21mz3k8pe2rx2z 172.31.226.130:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

执行命令:

1
2
3
$ docker node ls
ID                            HOSTNAME          STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
u8mvcbr6iyk2ih2jtgkgnqt58 *   DESKTOP-02TPVR7   Ready     Active         Leader           29.1.3
  • AVAILABILITY:
    • Active: 调度程序 (Scheduler) 可将任务分配到该节点;
    • Pause(静默观察): 停止接收新任务; 现有任务继续运行; 用于临时维护, 不希望增加负载
    • Drain(排空): 停止接收新任务; 现有任务立即停止并转移到其他节点; 适用于硬件下线, 系统升级 or 彻底移除节点
  • MANAGER STATUS:
    • Leader(领袖): 负责所有集群管理请求, 下达调度指令; 一个集群只有一个 Leader
    • Reachable(可达): 处于健康状态, 参与 Raft 选举; 若 Leader 挂了, 通过竞选产生新 Leader
    • Unavailable(不可用): 无法与其他 Manager 通信; 意味着节点宕机, Docker 服务停止, 网络隔离;
    • 空值 (Blank): 该节点是普通 Worker 节点, 不参与集群

大纲:

第一步:创建管理节点 (Manager)

你已经完成了这一步:

Bash

1
docker swarm init --advertise-addr 172.31.226.130

第二步:创建工作节点 (Worker)

现在的变化: 在 WSL 中,如果你想模拟多个节点,你不需要再去安装 VirtualBox。你可以启动另一个 WSL 发行版(比如 Debian),或者更简单地,使用 Docker-in-Docker (DinD) 技术。

第三步:部署服务 (Service)

老教材用的是 alpine ping,我们来个更实际的 Nginx 服务,并体现 Swarm 的声明式编程思想:

Bash

1
docker service create --name my-web --replicas 2 -p 8080:80 nginx

3. 核心实验:滚动更新 (Rolling Update)

这是老教材第 8 点提到的精华。在 Swarm 中,更新服务不会导致停机(Downtime),这非常符合心理学中的“平滑过渡”预期。

让我们动手试试: 假设我们要把刚才部署的 Nginx 从旧版本升级到新版本:

Bash

1
docker service update --image nginx:latest --update-delay 10s my-web
  • --update-delay 10s:这就是老教材提到的延迟,确保一个容器升级成功后再升级下一个。

附录

1. 碰到的其他技术

Docker-in-Docker (DinD)技术

本文由作者按照 CC BY 4.0 进行授权