# Docker 基础

# 参考文档

# CI/CD

  • continuous integration,持续集成
  • continuous delivery,持续交付
  • continuous deployment,持续部署

# 起步

# 安装

本处使用的是 Ubuntu 20.04 进行安装。 因为 Ubuntu 20.04 官方 apt 源中就有 Docker,所以可以直接通过 apt 命令来安装:

WARNING

切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker.

sudo apt update
sudo apt install docker.io
docker --version
1
2
3

安装成功以后,执行 docker container run hello-world 来检查是否已经成功。

如果本地没有该镜像,这个命令将会下载测试镜像,在容器中运行它,打印出 "Hello from Docker",并且退出。

# 配置

# docker 用户组

安装成功后,执行 docker 命令(比如 docker ps -a),会显示 connect: permission denied

这是因为在默认情况下,普通用户没有权限执行 docker 相关操作,虽然可以每次加 sudo 执行,但是过于麻烦。可以将普通用户加入到 docker 用户组,并退出 shell 重新登录(以便用户组会员信息刷新),就可以不使用 sudo 来运行 docker 相关操作了。

操作如下:

# $USER 是一个环境变量,代表当前用户名。
# 添加当前用户到 group docker 中。
sudo usermod -aG docker $USER
docker info
1
2
3
4

usermod 可用来修改用户帐号的各项设定:

  • -a 在 -G 存在的情况下,增加次要用户组的支持,不是修改当前用户组。
    • -G 修改当前次要组
    • -aG 是增加新的次要组
  • -G <群组> 修改用户所属的附加群组。
  • -g <群组> 修改用户所属的群组。
  • -c <备注> 修改用户帐号的备注文字。
  • -d <登入目录> 修改用户登入时的目录。
  • -e <有效期限> 修改帐号的有效期限。
  • -f <缓冲天数> 修改在密码过期后多少天即关闭该帐号。
  • -l <帐号名称> 修改用户帐号名称。
  • -L 锁定用户密码,使密码无效。
  • -s <shell> 修改用户登入后所使用的shell。
  • -u <uid> 修改用户ID。
  • -U 解除密码锁定。

docker info: 显示 Docker 系统信息,包括镜像和容器数。

# 配置国内源

由于 docker 默认镜像仓库在海外,所以需要配置国内源来加快镜像拉取速度。

修改 /etc/docker/daemon.json (如果该文件不存在,手动创建即可)。文件内容如下:

{
    "registry-mirrors": [
        "https://hub-mirror.c.163.com"
    ]
}
1
2
3
4
5

# 卸载

在卸载 Docker 之前,最好移除所有的容器,镜像,卷和网络。

运行下面的命令停止所有正在运行的容器,并且移除所有的 docker 对象:

docker container stop $(docker container ls -aq)
docker system prune -a --volumes
1
2

现在你可以使用 apt 像卸载其他软件包一样来卸载 Docker:

sudo apt purge docker-ce
sudo apt autoremove
1
2

# Docker 简介

Docker 是一个开源的应用容器引擎,基于 Go 语言,并遵从 Apache2.0 协议开源。

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

理论介绍先忽略,这里在网上曾看到一个十分通俗易懂的说法:

电脑装系统的时候,需要一张盘,我们称其为镜像。镜像是一个固定的文件,这次读盘和下次读盘内容是一样的。 但有些开发者会把这个镜像安装到电脑上,再在这个电脑上删删改改,再重新打包一个镜像刻盘,固化出一个镜像来,这就是镜像打包。 比如说国内以前泛滥的盗版XP系统,从微软官方镜像出发,添加小工具,系统设置修改优化,加主题,造出番茄花园,雨林木风,深度之类的盗版安装碟。

镜像 => 修改打包 => 镜像 => 修改打包 => 镜像

镜像装到电脑后,这个电脑就是个容器,里面包含使用者的数据和设置。从玩盗版系统到玩 Docker,区别只是电脑从实体变成虚拟机罢了。

Docker 上虚拟机被设计得很廉价,一台物理机上可以开很多虚拟机。廉价到可以为每一个软件专开一个虚拟机,像银行的 ATM 机器,虽是 Windows 系统的电脑,但终其一生只跑一个程序只显示一个界面。虚拟机的廉价,加上镜像里又打包好了所需的软件,导致了大家想装某个软件的时候,可以直接开个新虚拟机,然后找这个软件的 Docker 镜像,啪一下装上去跑起来。比起把软件往一个现成系统上装,要省事千倍,尤其是装那些需要编译,系统内核兼容,各种库依赖,缺少帮助文档的开源软件。

所以 Docker 流行了起来。

# 三个基本概念

  • 镜像(Image):Docker 镜像,
    • 相当于是一个 root 文件系统。
    • 比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
  • 容器(container):
    • 镜像和容器的关系,就像是类和实例的关系一样。
    • 镜像是静态的定义,容器就像是镜像运行时的实体。
    • 容器可以创建、启动、停止、删除、暂停等。
  • 仓库(Repository):
    • 仓库可以看成是一个代码控制中心,用来保存镜像。

# STATUS 容器状态

状态有7种:

  • created(已创建)
  • restarting(重启中)
  • runningUp(运行中)
  • removing(迁移中)
  • paused(暂停)
  • exited(停止)
  • dead(死亡)

# 常用命令

  • 直接输入 docker 命令来查看到 Docker 客户端的所有命令选项。
  • 通过命令 docker command --help 更深入的了解指定的 Docker 命令使用方法。
  • docker info
  • docker stats,查看资源占用情况。

# 运行一个 WEB 应用

  • docker pull training/webapp # 载入镜像
  • docker run -d -P training/webapp python app.py
    • -d:让容器在后台运行。
    • -P:将容器内部使用的网络端口随机映射到我们使用的主机上。
  • 也可以通过 -p 参数来设置不一样的端口:
    • docker run -d -p 5000:5000 training/webapp python app.py

# 查看网络端口

  1. docker ps
  2. docker <容器 ID 或者 容器 name>

# 查看 WEB 应用程序日志

docker logs [ID 或者名字],可以查看容器内部的标准输出。

例如 docker logs -f bf08b7f2cd89

  • -f
    • docker logs 像使用 tail -f 一样来输出容器内部的标准输出。
  • -t,显示时间戳

# 查看容器内的进程

  • docker top [name / id]

# 检查 WEB 应用程序

  • docker inspect [name / id],检查 Docker 的底层信息。
  • 它会返回一个 JSON 文件记录着 Docker 容器的配置和状态信息。

# 停止 / 重启 WEB 应用容器

  • docker stop [name / id]
  • docker restart [name / id]

# 移除 WEB 应用容器

  • docker rm [name / id]
  • 删除容器时,容器的状态必须是停止状态。

# 容器的使用

# 获取镜像

因为容器是镜像的示例,想要使用容器,那么首先你要先有一个镜像。如果我们本地没有 ubuntu 镜像,我们可以使用 docker pull 命令来载入 ubuntu 镜像:

  • docker pull ubuntu

# 启动/停止容器

# 启动一个容器

以下命令使用 ubuntu 镜像启动一个容器,参数为以命令行模式进入该容器:

  • docker run -it ubuntu /bin/bash
  • 参数说明:
    • -i: 交互式操作。
    • -t: 终端。
    • ubuntu: ubuntu 镜像。
    • /bin/bash:放在镜像名后的是命令,该命令会在容器启动后立即执行。
      • 这里我们希望有个交互式 Shell,因此用的是 /bin/bash

要退出终端,直接输入 exit

# 启动已停止运行的容器

  • docker ps
    • docker ps -a,查看所有容器。
  • 启动一个已停止的容器,docker start

# 后台运行

在大部分的场景下,我们希望 docker 的服务是在后台运行的,我们可以过 -d 指定容器的运行模式。

  • docker run -itd --name ubuntu-test ubuntu /bin/bash
    • -d 参数默认不会进入容器。

# 停止/重启容器

  • docker stop <容器 ID>,停止
  • docker restart <容器 ID>,重启

# 进入容器

在使用 -d 参数时,容器启动后会进入后台。此时想要进入容器,可以通过以下指令进入:

  • docker attach
    • docker attach 1e560fca3906
  • docker exec
    • docker exec -it 243c32535da7 /bin/bash
  • 推荐使用 docker exec 命令,因为这个退出容器终端,不会导致容器的停止。

# 删除容器

  • docker rm ID
  • docker container prune,清理掉所有处于终止状态的容器。

# 导出和导入容器

# 导出

  • docker export 命令,导出本地某个容器。
    • docker export 1e560fca3906 > ubuntu.tar
    • 导出容器 1e560fca3906 快照到本地文件 ubuntu.tar。

# 导入

  • docker import 从容器快照文件中再导入为镜像。
    • cat docker/ubuntu.tar | docker import - test/ubuntu:v1
    • 将快照文件 ubuntu.tar 导入到镜像 test/ubuntu:v1

# 指定 url 进行导入

  • docker import http://example.com/exampleimage.tgz example/imagerepo

# 镜像的使用

当运行容器时,使用的镜像如果在本地中不存在,docker 就会自动从 docker 镜像仓库中下载,默认是从 Docker Hub 公共镜像源下载。

Docker 的默认存储位置 var/lib/docker

# 列出镜像列表

  • docker images,列出本地主机的镜像。
  • 各个选项说明:
    • REPOSITORY:表示镜像的仓库源
    • TAG:镜像的标签
    • IMAGE ID:镜像ID
    • CREATED:镜像创建时间
    • SIZE:镜像大小
  • 使用 REPOSITORY:TAG 来定义不同的镜像。

# 获取一个新的镜像

  • docker pull ubuntu:13.10

# 查找镜像

  • docker search httpd
  • 参数说明:
    • NAME: 镜像仓库源的名称
    • DESCRIPTION: 镜像的描述
    • OFFICIAL: 是否 docker 官方发布
    • stars: 类似 Github 里面的 star,表示点赞、喜欢的意思。
    • AUTOMATED: 自动构建。

# 删除镜像

  • docker rmi hello-world
  • 注意,这里是 rmi,rm image,删除容器是 rm。

# 创建镜像

当我们从 docker 镜像仓库中下载的镜像不能满足我们的需求时,我们可以通过以下两种方式对镜像进行更改。

  1. 从已经创建的容器中更新镜像,并且提交这个镜像。
  2. 使用 Dockerfile 指令来创建一个新的镜像。

# 更新镜像

更新镜像之前,需要使用镜像来创建一个容器。

  1. 在运行的镜像内使用 apt-get update 来更新镜像。
  2. 完成操作,输入 exit 退出这个容器。
  3. docker commit,提交容器副本。
    • 例如,docker commit -m="has update" -a="runoob" e218edb10161 runoob/ubuntu:v2
    • 参数说明:
      • -m: 提交的描述信息
      • -a: 指定镜像作者
      • e218edb10161:容器 ID
      • runoob/ubuntu:v2: 指定要创建的目标镜像名
  4. 查看新镜像,docker images
  5. 使用新镜像来启动一个容器。
    • 例如:docker run -t -i runoob/ubuntu:v2 /bin/bash

# 构建镜像

使用命令 docker build, 从零开始来创建一个新的镜像。为此,我们需要创建一个 Dockerfile 文件,其中包含一组指令来告诉 Docker 如何构建我们的镜像。

  1. 创建 Dockerfile 文件。

    FROM    centos:6.7
    MAINTAINER      Fisher "fisher@sudops.com"
    
    RUN     /bin/echo 'root:123456' |chpasswd
    RUN     useradd runoob
    RUN     /bin/echo 'runoob:123456' |chpasswd
    RUN     /bin/echo -e "LANG=\"en_US.UTF-8\"" >/etc/default/local
    EXPOSE  22
    EXPOSE  80
    CMD     /usr/sbin/sshd -D
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    • 每一个指令都会在镜像上创建一个新的层,每一个指令的前缀都必须是大写的。
    • 第一条 FROM,指定使用哪个镜像源
    • RUN 指令告诉 docker 在镜像内执行命令,安装了什么。
  2. 使用 docker build 命令进行构建。

    • docker build -t runoob/centos:6.7 .
    • -t:指定要创建的目标镜像名
    • .:Dockerfile 文件所在目录,可以指定 Dockerfile 的绝对路径

# 设置镜像标签

  • docker tag 860c279d2fec runoob/centos:dev
  • docker tag [容器 ID] [用户名]/[镜像源名]:[新标签名]

# 仓库管理

目前 Docker 官方维护了一个公共仓库 Docker Hub。

大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。

https://hub.docker.com 免费注册一个 Docker 账号。 ` 登录需要输入用户名和密码,登录成功后,我们就可以从 docker hub 上拉取自己账号下的全部镜像。

  • 登录,docker login
  • 退出,docker logout

# 拉取镜像

  • 使用 docker search 查找镜像,然后使用 docker pull 将它下载到本地。

# 推送镜像

  1. docker push username/ubuntu:18.04
    • docker push [用户名]/[镜像名]:[标签名]
  2. docker search username/ubuntu

# 数据管理

可以 绑定数据卷 或者 挂载主机目录 两种方式。

# 数据卷

数据卷是一个可以供一个或多个容器使用的特殊目录,它绕过了 UFS,提供了很多有用的特性:

  • 数据卷可以在容器间共享和重用。
  • 对数据卷的修改会立马生效。
  • 对数据卷的更新,不会影响镜像。
  • 数据卷默认会一直存在,即使容器被删除。

注意:数据卷的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会复制到数据卷中(仅数据卷为空时会复制)。

# 创建一个数据卷

创建:docker volume create my-vol

查看所有数据卷:docker volume ls

查看数据卷详细信息:docker volume inspect my-vol

# 启动一个挂载数据卷的容器

使用 docker run 运行一个容器的时候,可以使用 --mount 参数指定将一个数据卷挂载到容器上。在一次 docker run 中可以挂载多个数据卷。

示例:创建一个名为 web 的容器,并加载一个数据卷到容器的 /usr/share/nginx/html 目录。

docker run -d -P \
    --name web \
    # -v my-vol:/usr/share/nginx/html \
    --mount source=my-vol,target=/usr/share/nginx/html \
    nginx:alpine
1
2
3
4
5

# 查看数据卷的具体信息

docker inspect web 可以查看容器的详细信息,其中数据卷的信息在 Mounts 下:

"Mounts": [
    {
        "Type": "volume",
        "Name": "my-vol",
        "Source": "/var/lib/docker/volumes/my-vol/_data",
        "Destination": "/usr/share/nginx/html",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
],
1
2
3
4
5
6
7
8
9
10
11
12

# 删除数据卷

docker volume rm my-vol

数据卷是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除数据卷,并且也不会存在垃圾回收这样的机制来处理没有任何容器引用的数据卷。如果要在删除容器的同时删除数据卷,可以在删除容器的时候使用 docker rm -v 命令。

无主的数据卷可能会占用很多空间,要清理可以使用 docker volume prune

# 挂载主机目录

# 挂载一个主机目录作为数据卷

使用 --mount 命令可以指定挂载一个本地主机的目录到容器中去:

docker run -d -P \
    --name web \
    # -v /src/webapp:/usr/share/nginx/html \
    --mount type=bind,source=/src/webapp,target=/usr/share/nginx/html \
    nginx:alpine
1
2
3
4
5

上面的命令加载主机的 /src/webapp 目录到容器的 /usr/share/nginx/html,这个功能在测试的时候很方便,比如用户可以放置一些程序到本地目录中,来查看容器是正常工作。本地目录的路径必须是绝对路径,以前使用 -v 参数时,如果本地目录不存在 Docker 会自动为你创建一个文件夹,现在使用 --mount 参数,如果本地目录不存在的话,Docker 会报错。

Docker 挂载主机目录的默认权限是读写,用户也可以通过增加 readonly 指定为只读:

docker -run -d -P \
    --name web \
    # -v /src/webapp:/usr/share/nginx/html:ro \
    --mount type=bind,source=/src/webapp,target=/usr/share/nginx/html,readonly \
    nginx:alpine
1
2
3
4
5

加了 readonly 之后,就挂载为只读了。如果你在容器内 /usr/share/nginx/html 目录新建文件,会显示如下错误:

/usr/share/nginx/html # touch new.text
touch: new.text: Read-only file system
1
2

# 挂载一个本地主机文件作为数据卷

--mount 参数也可以从主机挂载单个文件到容器中:

docker run --rm -it \
    # -v $HOME/.bash_history:/root/.bash_history
    --mount type:bind,source=$HOME/.bash_history,target=/root/.bash_history \
    ubuntu:18.04 \
    bash    
1
2
3
4
5

# Docker 网络

# 外部访问容器

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

当使用 -P 标记时,Docker 会随机映射一个端口到内部容器开放的网络端口。

使用 docker container ls 可以看到,本地主机的 32768 被映射到了容器的 80 端口。此时访问本机的 32768 端口即可访问容器内 NGINX 默认页面:

docker run -d -P nginx:alpine

docker container ls -l
# CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
# fae320d08268        nginx:alpine        "/docker-entrypoint.…"   24 seconds ago      Up 20 seconds       0.0.0.0:32768->80/tcp   bold_mcnulty
1
2
3
4
5

同样的,可以通过 docker logs 命令来查看访问记录。

# 端口映射

格式:<主机端口>:<容器端口>

  • -P随机 映射一个主机端口 -> 容器内部开放的网络端口。
  • -p指定 一个主机端口 -> 容器内部开放的网络端口。

# 示例

  • 映射所有接口地址

使用 ip:hostPort:containerPort 格式将本地 80 端口映射到容器的 80 端口:

docker run -d -p 80:80 nginx:alpine
1
  • 映射到指定地址的指定端口

可以使用 ip:hostPort:containerPort 格式指定映射使用一个特定地址,比如 localhost 地址 127.0.0.1:

docker run -d -p 127.0.0.1:80:80
1
  • 映射到指定地址的任意端口

使用 ip:containerPort 绑定 localhost 的任意端口到容器的 80 端口,本地主机会自动分配一个端口:

docker run -d -p 127.0.0.1::80 nginx:alpine
1

还可以使用 udp 标记来指定 udp 端口:

docker run -d -p 127.0.0.1:80:80/udp nginx:alpine
1

# 查看映射端口配置

使用 docker port <containerId / containerName> 来查看当前映射的端口配置,也可以查看到绑定的地址。

注意:

  • 容器有自己的内部网络和 IP 地址(使用 docker inspect 查看,Docker 还可以有一个可变的网络配置。)

  • -p 标记可以多次使用来绑定多个端口,例如:

        docker run -d \
            -p 80:80 \
            -p 443:443 \
            nginx:alpine
    
    1
    2
    3
    4

# 容器互联

如果你之前有 Docker 的使用经验,你可能习惯了使用 --link 参数来使容器互联。

随着 Docker 网络的完善,强烈建议大家将容器加入自定义的 Docker 网络来连接多个容器,而不是使用 --link

# 新建网络

docker network create -d bridge my-net

-d 参数指定 Docker 网络类型,有 bridgeoverlay

其中 overlay 网络类型用于 Swarm mode。

# 连接容器

  1. 运行一个容器并连接到新建的 my-net 网络: docker run -it --rm --name busybox1 --network my-net busybox sh
  2. 再运行一个新的容器加入到 my-net 网络: docker run -it --rm --name busybox2 --network my-net busybox sh

然后在一个容器里 ping 另外一个容器。

# 配置 DNS

如何自定义配置容器的主机名和 DNS 呢?答案就是 Docker 利用虚拟文件来挂载容器的 3 个相关配置文件。

在容器中使用 mount 命令可以看到挂载信息。这种机制可以让宿主主机 DNS 信息发生更新后,所有 Docker 容器的 DNS 配置通过 /etc/resolv.config 文件得到立即更新。

配置全部容器的 DNS,也可以在 /etc/docker/daemon.json 文件设置:

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

// TODO

# Dockerfile

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

# 定制镜像

例如定制一个 nginx 镜像(构建好的镜像内会有一个 /usr/share/nginx/html/index.html 文件):

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

        FROM nginx
        RUN echo '这是一个本地镜像' > /usr/share/nginx/html/index.html
    
    1
    2
    • FROM:定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像。后续的操作都是基于 nginx。
    • RUN:用于执行后面跟着的命令行命令。有以下俩种格式:
      • shell 格式:
        • RUN <命令行命令> 参数1 参数2
        • <命令行命令> 等同于,在终端操作的 shell 命令。
      • exec 格式:
        • RUN ["可执行文件", "参数1", "参数2"]
        • 例如:
          • RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline
  2. 在 Dockerfile 文件的存放目录下,执行构建动作。

    • 以下示例,通过目录下的 Dockerfile 构建一个 nginx:v3(镜像名称:镜像标签)。
    • 注:最后的 . 代表本次执行的上下文路径,下一节会介绍。

# 上下文路径

docker build -t nginx:v3 .,指令最后一个 . 是上下文路径。

  • 上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。

  • 解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。

  • 如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。

注意: 上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。

# 指令集

# COPY

复制指令,从上下文目录中复制文件或者目录到容器里指定路径。

  • 格式:

        COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
        COPY [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]
        [--chown=<user>:<group>]:可选参数,用户改变复制到容器内文件的拥有者和属组。
    
    1
    2
    3
    • <源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:

          COPY hom* /mydir/
          COPY hom?.txt /mydir/
      
      1
      2
    • <目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

# ADD

ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:

ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。 ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

# CMD

类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

  • CMD 在docker run 时运行。
  • RUN 是在 docker build。

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

注意: 如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

  • 格式:

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

推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。

# ENTRYPOINT

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 CMD 指令指定的程序。

优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

格式:ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

可以搭配 CMD 命令使用:一般是变参才会使用 CMD,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。

示例:

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

FROM nginx

ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参

# 1、不传参运行
docker run  nginx:test
# 容器内会默认运行以下命令,启动主进程。
# nginx -c /etc/nginx/nginx.conf

# 2、传参运行
docker run  nginx:test -c /etc/nginx/new.conf
# 容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)
# nginx -c /etc/nginx/new.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# ENV

设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

格式:

ENV <key> <value> or ENV <key1>=<value1> <key2>=<value2>...

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

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"
1
2

# ARG

构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。

格式:

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

# VOLUME

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

作用:避免重要的数据,因容器重启而丢失,这是非常致命的。避免容器不断变大。

格式:

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

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

# EXPOSE

仅仅只是声明端口。

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

格式:

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

# WORKDIR

指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。

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

格式:

WORKDIR <工作目录路径>

# USER

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

格式:

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

# HEALTHCHECK

用于指定某个程序或者指令来监控 docker 容器服务的运行状态。

格式:

HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令

HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

HEALTHCHECK [选项] CMD <命令>: 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。

# ONBUILD

用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。

格式:

ONBUILD <其它指令>

# LABEL

LABEL 指令用来给镜像添加一些元数据(metadata),以键值对的形式,语法格式如下:

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

比如我们可以添加镜像的作者:

LABEL org.opencontainers.image.authors="runoob"

# 讨论区

由于评论过多会影响页面最下方的导航,故将评论区做默认折叠处理。

点击查看评论区内容,渴望您的宝贵建议~
Last Updated: 9/11/2024, 2:55:45 PM