运行容器

Docker 在隔离容器中运行进程。容器是在主机上运行的进程。主机可以是本地的,也可以是远程的。当您执行 docker run 时,运行的容器进程是隔离的,它拥有自己的文件系统、自己的网络以及与主机分开的独立进程树。

本页详细介绍了如何使用 docker run 命令运行容器。

通用格式

一个 docker run 命令具有以下形式:

$ docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]

docker run 命令必须指定一个镜像引用来创建容器。

镜像引用

镜像引用是镜像的名称和版本。您可以使用镜像引用基于镜像创建或运行容器。

  • docker run IMAGE[:TAG][@DIGEST]
  • docker create IMAGE[:TAG][@DIGEST]

镜像标签是镜像版本,省略时默认为 latest。使用标签可以从特定版本的镜像运行容器。例如,要运行 ubuntu 镜像的 24.04 版本:docker run ubuntu:24.04

镜像摘要

使用 v2 或更高版本镜像格式的镜像具有一个内容可寻址的标识符,称为摘要 (digest)。只要用于生成镜像的输入保持不变,摘要值就是可预测的。

以下示例使用 alpine 镜像和 sha256:9cacb71397b640eca97488cf08582ae4e4068513101088e9f96c9814bfda95e0 摘要运行容器:

$ docker run alpine@sha256:9cacb71397b640eca97488cf08582ae4e4068513101088e9f96c9814bfda95e0 date

选项

[OPTIONS] 允许您配置容器的选项。例如,您可以给容器指定名称 (--name),或将其作为后台进程运行 (-d)。您还可以设置选项来控制资源限制和网络等方面。

命令和参数

您可以使用 [COMMAND][ARG...] 位置参数来指定容器启动时要运行的命令和参数。例如,您可以指定 sh 作为 [COMMAND],结合 -i-t 标志,在容器中启动一个交互式 shell(如果您选择的镜像在 PATH 上有 sh 可执行文件)。

$ docker run -it IMAGE sh

注意

根据您的 Docker 系统配置,您可能需要在 docker run 命令前加上 sudo。为了避免在使用 docker 命令时需要使用 sudo,您的系统管理员可以创建一个名为 docker 的 Unix 组并将用户添加到其中。有关此配置的更多信息,请参阅您的操作系统的 Docker 安装文档。

前台和后台

启动容器时,容器默认在前台运行。如果您想让容器在后台运行,可以使用 --detach(或 -d)标志。这将启动容器而不占用您的终端窗口。

$ docker run -d <IMAGE>

当容器在后台运行时,您可以使用其他 CLI 命令与容器交互。例如,docker logs 允许您查看容器日志,而 docker attach 则将其带到前台。

$ docker run -d nginx
0246aa4d1448a401cabd2ce8f242192b6e7af721527e48a810463366c7ff54f1
$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS        PORTS     NAMES
0246aa4d1448   nginx     "/docker-entrypoint.…"   2 seconds ago   Up 1 second   80/tcp    pedantic_liskov
$ docker logs -n 5 0246aa4d1448
2023/11/06 15:58:23 [notice] 1#1: start worker process 33
2023/11/06 15:58:23 [notice] 1#1: start worker process 34
2023/11/06 15:58:23 [notice] 1#1: start worker process 35
2023/11/06 15:58:23 [notice] 1#1: start worker process 36
2023/11/06 15:58:23 [notice] 1#1: start worker process 37
$ docker attach 0246aa4d1448
^C
2023/11/06 15:58:40 [notice] 1#1: signal 2 (SIGINT) received, exiting
...

有关与前台和后台模式相关的 docker run 标志的更多信息,请参阅:

有关重新连接到后台容器的更多信息,请参阅 docker attach

容器标识

您可以通过三种方式标识容器:

标识符类型示例值
UUID 长标识符f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778
UUID 短标识符f78375b1c487
名称evil_ptolemy

UUID 标识符是由守护进程分配给容器的随机 ID。

守护进程会自动为容器生成一个随机字符串名称。您也可以使用 --name 标志...定义自定义名称。定义一个 name 可以方便地为容器添加意义。如果您指定了 name,则可以在用户定义网络中引用该容器时使用它。这适用于后台和前台 Docker 容器。

容器标识符与镜像引用不同。镜像引用指定了运行容器时使用的镜像是哪个。您不能运行 docker exec nginx:alpine sh 在基于 nginx:alpine 镜像的容器中打开 shell,因为 docker exec 需要容器标识符(名称或 ID),而不是镜像。

虽然容器使用的镜像不是容器的标识符,但您可以通过使用 `--filter` 标志找到使用特定镜像的容器 ID。例如,以下 docker ps 命令获取基于 nginx:alpine 镜像的所有正在运行的容器的 ID:

$ docker ps -q --filter ancestor=nginx:alpine

有关使用过滤器的更多信息,请参阅过滤...

容器网络

容器默认启用网络,并且可以进行出站连接。如果您运行多个需要相互通信的容器,您可以创建一个自定义网络并将这些容器连接到该网络。

当多个容器连接到同一个自定义网络时,它们可以使用容器名称作为 DNS 主机名相互通信。以下示例创建了一个名为 my-net 的自定义网络,并运行两个连接到该网络的容器。

$ docker network create my-net
$ docker run -d --name web --network my-net nginx:alpine
$ docker run --rm -it --network my-net busybox
/ # ping web
PING web (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.326 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.257 ms
64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.281 ms
^C
--- web ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.257/0.288/0.326 ms

有关容器网络的更多信息,请参阅网络概述...

文件系统挂载

默认情况下,容器中的数据存储在一个临时的、可写的容器层中。移除容器也会移除其数据。如果您想将持久化数据与容器一起使用,可以使用文件系统挂载将数据持久存储在主机系统上。文件系统挂载还可以让您在容器和主机之间共享数据。

Docker 支持两类主要的挂载:

  • 卷挂载
  • 绑定挂载

卷挂载非常适合为容器持久存储数据以及在容器之间共享数据。另一方面,绑定挂载用于在容器和主机之间共享数据。

您可以使用 docker run 命令的 --mount 标志为容器添加文件系统挂载。

以下部分展示了如何创建卷和绑定挂载的基本示例。有关更深入的示例和描述,请参阅文档中的存储部分...

卷挂载

创建卷挂载:

$ docker run --mount source=<VOLUME_NAME>,target=[PATH] [IMAGE] [COMMAND...]

在这种情况下,--mount 标志接受两个参数:sourcetargetsource 参数的值是卷的名称。target 的值是卷在容器内的挂载位置。创建卷后,您写入卷的任何数据都会持久化,即使您停止或移除容器也是如此。

$ docker run --rm --mount source=my_volume,target=/foo busybox \
  echo "hello, volume!" > /foo/hello.txt
$ docker run --mount source=my_volume,target=/bar busybox
  cat /bar/hello.txt
hello, volume!

target 必须始终是绝对路径,例如 /src/docs。绝对路径以 /(正斜杠)开头。卷名称必须以字母数字字符开头,后跟 a-z0-9_(下划线)、.(点)或 -(连字符)。

绑定挂载

创建绑定挂载:

$ docker run -it --mount type=bind,source=[PATH],target=[PATH] busybox

在这种情况下,--mount 标志接受三个参数。一个类型(bind)和两个路径。source 路径是您想要绑定挂载到容器中的主机位置。target 路径是容器内的挂载目标。

绑定挂载默认是读写的,这意味着您可以从容器中读取和写入挂载位置的文件。您所做的更改,例如添加或编辑文件,会反映在主机文件系统上。

$ docker run -it --mount type=bind,source=.,target=/foo busybox
/ # echo "hello from container" > /foo/hello.txt
/ # exit
$ cat hello.txt
hello from container

退出状态

docker run 的退出代码提供了关于容器为何运行失败或为何退出的信息。以下部分描述了不同容器退出代码值的含义。

125

退出代码 125 表示错误出在 Docker 守护进程本身。

$ docker run --foo busybox; echo $?

flag provided but not defined: --foo
See 'docker run --help'.
125

126

退出代码 126 表示无法调用指定的容器命令。以下示例中的容器命令是:/etc

$ docker run busybox /etc; echo $?

docker: Error response from daemon: Container command '/etc' could not be invoked.
126

127

退出码 127 表示包含的命令未找到。

$ docker run busybox foo; echo $?

docker: Error response from daemon: Container command 'foo' not found or does not exist.
127

其他退出代码

125126127 之外的任何退出码表示所提供的容器命令的退出码。

$ docker run busybox /bin/sh -c 'exit 3'
$ echo $?
3

资源运行时限制

运维人员也可以调整容器的性能参数

选项描述
-m, --memory=""内存限制(格式:<number>[<unit>])。Number 是一个正整数。Unit 可以是 bkmg 中的一个。最小值为 6M。
--memory-swap=""总内存限制(内存 + 交换空间,格式:<number>[<unit>])。Number 是一个正整数。Unit 可以是 bkmg 中的一个。
--memory-reservation=""内存软限制(格式:<number>[<unit>])。Number 是一个正整数。Unit 可以是 bkmg 中的一个。
--kernel-memory=""内核内存限制(格式:<number>[<unit>])。Number 是一个正整数。Unit 可以是 bkmg 中的一个。最小值为 4M。
-c, --cpu-shares=0CPU 份额(相对权重)
--cpus=0.000CPU 数量。Number 是一个小数。0.000 表示没有限制。
--cpu-period=0限制 CPU CFS (完全公平调度器) 周期
--cpuset-cpus=""允许执行的 CPU (0-3, 0,1)
--cpuset-mems=""允许执行的内存节点 (MEMs) (0-3, 0,1)。仅在 NUMA 系统上有效。
--cpu-quota=0限制 CPU CFS (完全公平调度器) 配额
--cpu-rt-period=0限制 CPU 实时周期。以微秒为单位。需要设置父 cgroups 且不能高于父级。同时检查 rtprio ulimits。
--cpu-rt-runtime=0限制 CPU 实时运行时长。以微秒为单位。需要设置父 cgroups 且不能高于父级。同时检查 rtprio ulimits。
--blkio-weight=0块 IO 权重(相对权重)接受一个介于 10 到 1000 之间的权重值。
--blkio-weight-device=""块 IO 权重(相对设备权重,格式:DEVICE_NAME:WEIGHT
--device-read-bps=""限制从设备读取速率(格式:<device-path>:<number>[<unit>])。Number 是一个正整数。Unit 可以是 kbmbgb 中的一个。
--device-write-bps=""限制写入设备的速率(格式:<device-path>:<number>[<unit>])。Number 是一个正整数。Unit 可以是 kbmbgb 中的一个。
--device-read-iops=""限制从设备读取速率(每秒 IO 数)(格式:<device-path>:<number>)。Number 是一个正整数。
--device-write-iops=""限制写入设备的速率(每秒 IO 数)(格式:<device-path>:<number>)。Number 是一个正整数。
--oom-kill-disable=false是否禁用容器的 OOM Killer。
--oom-score-adj=0调整容器的 OOM 优先级 (-1000 到 1000)
--memory-swappiness=""调整容器的内存 swappiness 行为。接受一个介于 0 到 100 之间的整数。
--shm-size=""/dev/shm 的大小。格式为 <number><unit>number 必须大于 0unit 是可选的,可以是 b (字节)、k (千字节)、m (兆字节) 或 g (千兆字节)。如果省略 unit,系统将使用字节。如果完全省略 size,系统将使用 64m

用户内存限制

我们有四种方式设置用户内存使用量

选项结果
memory=inf, memory-swap=inf (default)容器没有内存限制。容器可以根据需要使用任意多的内存。
memory=L<inf, memory-swap=inf(指定内存并将 memory-swap 设置为 -1)容器不允许使用超过 L 字节的内存,但可以使用任意多的交换空间(如果主机支持交换内存)。
memory=L<inf, memory-swap=2*L(只指定内存而不指定 memory-swap)容器不允许使用超过 L 字节的内存,交换空间 加上 内存使用量是该值的两倍。
memory=L<inf, memory-swap=S<inf, L<=S(同时指定内存和 memory-swap)容器不允许使用超过 L 字节的内存,交换空间 加上 内存使用量受 S 限制。

示例

$ docker run -it ubuntu:24.04 /bin/bash

我们没有设置关于内存的任何限制,这意味着容器中的进程可以根据需要使用任意多的内存和交换空间。

$ docker run -it -m 300M --memory-swap -1 ubuntu:24.04 /bin/bash

我们设置了内存限制并禁用了交换空间限制,这意味着容器中的进程可以使用 300M 内存,并根据需要使用任意多的交换空间(如果主机支持交换内存)。

$ docker run -it -m 300M ubuntu:24.04 /bin/bash

我们只设置了内存限制,这意味着容器中的进程可以使用 300M 内存和 300M 交换空间;默认情况下,总虚拟内存大小(--memory-swap)将设置为内存的两倍,在本例中,内存 + 交换空间将是 2*300M,因此进程也可以使用 300M 交换空间。

$ docker run -it -m 300M --memory-swap 1G ubuntu:24.04 /bin/bash

我们同时设置了内存和交换空间,因此容器中的进程可以使用 300M 内存和 700M 交换空间。

内存预留是一种内存软限制,它允许更好地共享内存。在正常情况下,容器可以根据需要使用任意多的内存,并且仅受通过 -m/--memory 选项设置的硬限制约束。设置内存预留后,Docker 会检测内存争用或低内存情况,并强制容器将其消耗限制在预留限制内。

务必将内存预留值设置在硬限制之下,否则硬限制将优先。预留值为 0 等同于未设置预留。默认情况下(未设置预留时),内存预留等于硬内存限制。

内存预留是软限制功能,不能保证限制不会被超出。相反,该功能试图确保在内存严重争用时,根据预留提示/设置分配内存。

以下示例将内存 (-m) 限制为 500M,并将内存预留设置为 200M。

$ docker run -it -m 500M --memory-reservation 200M ubuntu:24.04 /bin/bash

在此配置下,当容器消耗的内存超过 200M 但小于 500M 时,下一次系统内存回收会尝试将容器内存缩小到 200M 以下。

以下示例将内存预留设置为 1G,未设置硬内存限制。

$ docker run -it --memory-reservation 1G ubuntu:24.04 /bin/bash

容器可以根据需要使用任意多的内存。内存预留设置确保容器长时间内不会消耗过多内存,因为每次内存回收都会将容器的消耗量缩小到预留值。

默认情况下,如果发生内存不足 (OOM) 错误,内核会杀死容器中的进程。要更改此行为,请使用 --oom-kill-disable 选项。仅在同时设置了 -m/--memory 选项的容器上禁用 OOM killer。如果未设置 -m 标志,可能会导致主机内存不足,需要杀死主机系统进程以释放内存。

以下示例将内存限制为 100M 并禁用此容器的 OOM killer

$ docker run -it -m 100M --oom-kill-disable ubuntu:24.04 /bin/bash

以下示例说明了使用此标志的一种危险方式

$ docker run -it --oom-kill-disable ubuntu:24.04 /bin/bash

容器具有无限内存,这可能导致主机内存不足,需要杀死系统进程以释放内存。可以更改 --oom-score-adj 参数来选择系统内存不足时要杀死的容器的优先级,负分使其不太可能被杀死,正分则更可能被杀死。

内核内存限制

内核内存与用户内存有着根本区别,因为内核内存无法换出。无法换出使得容器有可能通过消耗过多的内核内存来阻塞系统服务。内核内存包括:

  • 堆栈页
  • slab 页
  • socket 内存压力
  • tcp 内存压力

您可以设置内核内存限制来约束这些类型的内存。例如,每个进程都会消耗一些堆栈页。通过限制内核内存,您可以在内核内存使用过高时阻止创建新进程。

内核内存永远不会完全独立于用户内存。相反,您是在用户内存限制的上下文中限制内核内存。假设 "U" 是用户内存限制,“K” 是内核限制。有三种可能的限制设置方式

选项结果
U != 0, K = inf (default)这是在使用内核内存之前已经存在的标准内存限制机制。内核内存完全被忽略。
U != 0, K < U内核内存是用户内存的一个子集。这种设置在每个 cgroup 的总内存过量分配(overcommitted)的部署中很有用。过量分配内核内存限制绝对不推荐,因为主机仍然可能耗尽不可回收的内存。在这种情况下,您可以配置 K,使所有组的总和永远不大于总内存。然后,可以自由设置 U,但会牺牲系统的服务质量。
U != 0, K > U由于内核内存消耗也会计入用户计数器,并且会针对容器触发两种内存的回收。此配置为管理员提供了统一的内存视图。对于只想跟踪内核内存使用量的人来说也很有用。

示例

$ docker run -it -m 500M --kernel-memory 50M ubuntu:24.04 /bin/bash

我们设置了内存和内核内存,因此容器中的进程总共可以使用 500M 内存,在这 500M 内存中,最多可以使用 50M 内核内存。

$ docker run -it --kernel-memory 50M ubuntu:24.04 /bin/bash

我们在未设置 -m 的情况下设置了内核内存,因此容器中的进程可以使用任意多的内存,但只能使用 50M 内核内存。

Swappiness 限制

默认情况下,容器的内核可以换出一定比例的匿名页。要为容器设置此百分比,请指定一个介于 0 到 100 之间的 --memory-swappiness 值。值为 0 表示关闭匿名页换出。值为 100 表示将所有匿名页设置为可换出。默认情况下,如果您未使用 --memory-swappiness,内存 swappiness 值将从父级继承。

例如,您可以设置

$ docker run -it --memory-swappiness=0 ubuntu:24.04 /bin/bash

设置 --memory-swappiness 选项有助于保留容器的工作集并避免换出带来的性能损失。

CPU 份额限制

默认情况下,所有容器获得相同比例的 CPU 周期。可以通过更改容器相对于所有其他运行容器的 CPU 份额权重来修改此比例。

要修改默认的 1024 比例,请使用 -c--cpu-shares 标志将权重设置为 2 或更高。如果设置为 0,系统将忽略该值并使用默认值 1024。

该比例仅在运行 CPU 密集型进程时适用。当一个容器中的任务处于空闲状态时,其他容器可以使用剩余的 CPU 时间。实际获得的 CPU 时间量将根据系统上运行的容器数量而有所不同。

例如,考虑三个容器,一个的 cpu-share 为 1024,另外两个的 cpu-share 设置为 512。当所有三个容器中的进程都尝试使用 100% 的 CPU 时,第一个容器将获得总 CPU 时间的 50%。如果您添加第四个 cpu-share 为 1024 的容器,则第一个容器仅获得 33% 的 CPU。其余容器分别获得 16.5%、16.5% 和 33% 的 CPU。

在多核系统上,CPU 时间的份额分布在所有 CPU 核上。即使容器的 CPU 时间被限制在低于 100%,它仍然可以使用每个独立 CPU 核的 100%。

例如,考虑一个拥有三个以上核的系统。如果您启动一个 -c=512 运行一个进程的容器 {C0},以及另一个 -c=1024 运行两个进程的容器 {C1},则可能导致以下 CPU 份额划分

PID    container	CPU	CPU share
100    {C0}		0	100% of CPU0
101    {C1}		1	100% of CPU1
102    {C1}		2	100% of CPU2

CPU 周期限制

默认的 CPU CFS (完全公平调度器) 周期是 100ms。我们可以使用 --cpu-period 来设置 CPU 周期以限制容器的 CPU 使用率。通常 --cpu-period 应该与 --cpu-quota 一起使用。

示例

$ docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu:24.04 /bin/bash

如果有一个 CPU,这意味着容器每 50ms 可以获得相当于 50% CPU 的运行时间。

除了使用 --cpu-period--cpu-quota 设置 CPU 周期限制外,也可以使用浮点数指定 --cpus 来达到相同的目的。例如,如果有 1 个 CPU,那么 --cpus=0.5 将实现与设置 --cpu-period=50000--cpu-quota=25000 (50% CPU) 相同的结果。

--cpus 的默认值是 0.000,这意味着没有限制。

更多信息请参阅 CFS 带宽限制文档

Cpuset 限制

我们可以设置允许容器在哪些 CPU 上执行。

示例

$ docker run -it --cpuset-cpus="1,3" ubuntu:24.04 /bin/bash

这意味着容器中的进程可以在 cpu 1 和 cpu 3 上执行。

$ docker run -it --cpuset-cpus="0-2" ubuntu:24.04 /bin/bash

这意味着容器中的进程可以在 cpu 0、cpu 1 和 cpu 2 上执行。

我们可以设置允许容器在哪些内存节点上执行。仅在 NUMA 系统上有效。

示例

$ docker run -it --cpuset-mems="1,3" ubuntu:24.04 /bin/bash

此示例将容器中的进程限制为仅使用内存节点 1 和 3 的内存。

$ docker run -it --cpuset-mems="0-2" ubuntu:24.04 /bin/bash

此示例将容器中的进程限制为仅使用内存节点 0、1 和 2 的内存。

CPU 配额限制

--cpu-quota 标志限制容器的 CPU 使用率。默认值 0 允许容器占用 100% 的 CPU 资源(1 个 CPU)。CFS (完全公平调度器) 处理执行进程的资源分配,并且是内核使用的默认 Linux 调度器。将此值设置为 50000 可将容器限制为 50% 的 CPU 资源。对于多个 CPU,请根据需要调整 --cpu-quota。更多信息请参阅 CFS 带宽限制文档

块 IO 带宽 (Blkio) 限制

默认情况下,所有容器获得相同比例的块 IO 带宽 (blkio)。此比例为 500。要修改此比例,请使用 --blkio-weight 标志更改容器相对于所有其他运行容器的 blkio 权重。

注意

blkio 权重设置仅适用于直接 IO。当前不支持缓冲 IO。

--blkio-weight 标志可以将权重设置为介于 10 到 1000 之间的值。例如,以下命令创建两个具有不同 blkio 权重的容器

$ docker run -it --name c1 --blkio-weight 300 ubuntu:24.04 /bin/bash
$ docker run -it --name c2 --blkio-weight 600 ubuntu:24.04 /bin/bash

如果您同时在两个容器中进行块 IO 操作,例如

$ time dd if=/mnt/zerofile of=test.out bs=1M count=1024 oflag=direct

您会发现花费的时间比例与两个容器的 blkio 权重比例相同。

--blkio-weight-device="DEVICE_NAME:WEIGHT" 标志设置特定设备的权重。DEVICE_NAME:WEIGHT 是一个包含冒号分隔的设备名称和权重的字符串。例如,要将 /dev/sda 设备的权重设置为 200

$ docker run -it \
    --blkio-weight-device "/dev/sda:200" \
    ubuntu

如果同时指定 --blkio-weight--blkio-weight-device,Docker 将使用 --blkio-weight 作为默认权重,并使用 --blkio-weight-device 在特定设备上用新值覆盖此默认值。以下示例使用默认权重 300,并在 /dev/sda 上覆盖此默认值,将其权重设置为 200

$ docker run -it \
    --blkio-weight 300 \
    --blkio-weight-device "/dev/sda:200" \
    ubuntu

--device-read-bps 标志限制从设备的读取速率(字节/秒)。例如,此命令创建了一个容器并限制从 /dev/sda 的读取速率为每秒 1mb

$ docker run -it --device-read-bps /dev/sda:1mb ubuntu

--device-write-bps 标志限制写入设备的速率(字节/秒)。例如,此命令创建了一个容器并限制写入 /dev/sda 的速率为每秒 1mb

$ docker run -it --device-write-bps /dev/sda:1mb ubuntu

这两个标志都接受 <device-path>:<limit>[unit] 格式的限制。读取和写入速率都必须是正整数。您可以以 kb (千字节)、mb (兆字节) 或 gb (千兆字节) 为单位指定速率。

--device-read-iops 标志限制从设备的读取速率(每秒 IO 数)。例如,此命令创建了一个容器并限制从 /dev/sda 的读取速率为每秒 1000 IO

$ docker run -it --device-read-iops /dev/sda:1000 ubuntu

--device-write-iops 标志限制写入设备的速率(每秒 IO 数)。例如,此命令创建了一个容器并限制写入 /dev/sda 的速率为每秒 1000 IO

$ docker run -it --device-write-iops /dev/sda:1000 ubuntu

这两个标志都接受 <device-path>:<limit> 格式的限制。读取和写入速率都必须是正整数。

附加组

--group-add: Add additional groups to run as

默认情况下,docker 容器进程以查找指定用户的补充组运行。如果要向该组列表中添加更多组,则可以使用此标志

$ docker run --rm --group-add audio --group-add nogroup --group-add 777 busybox id

uid=0(root) gid=0(root) groups=10(wheel),29(audio),99(nogroup),777

运行时特权和 Linux 能力

选项描述
--cap-add添加 Linux 能力(capabilities)
--cap-drop删除 Linux 能力(capabilities)
--privileged为此容器提供扩展权限
--device=[]允许您在不使用 --privileged 标志的情况下在容器内运行设备。

默认情况下,Docker 容器是“非特权”的,例如,不能在 Docker 容器内部运行 Docker daemon。这是因为默认情况下不允许容器访问任何设备,但“特权”容器被授予访问所有设备的权限(参见关于 cgroups 设备的文档)。

--privileged 标志为容器提供所有能力。当运维人员执行 docker run --privileged 时,Docker 会启用对主机上所有设备的访问,并重新配置 AppArmor 或 SELinux,以允许容器获得与主机上容器外部运行的进程几乎相同的访问权限。请谨慎使用此标志。有关 --privileged 标志的更多信息,请参阅 docker run 参考文档

如果您想限制对特定设备或多个设备的访问,可以使用 --device 标志。它允许您指定一个或多个在容器内可访问的设备。

$ docker run --device=/dev/snd:/dev/snd ...

默认情况下,容器将能够 readwritemknod 这些设备。可以使用第三个 :rwm 选项集覆盖每个 --device 标志的此默认设置

$ docker run --device=/dev/sda:/dev/xvdc --rm -it ubuntu fdisk  /dev/xvdc

Command (m for help): q
$ docker run --device=/dev/sda:/dev/xvdc:r --rm -it ubuntu fdisk  /dev/xvdc
You will not be able to write the partition table.

Command (m for help): q

$ docker run --device=/dev/sda:/dev/xvdc:w --rm -it ubuntu fdisk  /dev/xvdc
    crash....

$ docker run --device=/dev/sda:/dev/xvdc:m --rm -it ubuntu fdisk  /dev/xvdc
fdisk: unable to open /dev/xvdc: Operation not permitted

除了 --privileged 之外,运维人员可以使用 --cap-add--cap-drop 对能力进行细粒度控制。默认情况下,Docker 保留了一组默认的能力列表。下表列出了默认允许且可以删除的 Linux 能力选项。

能力键能力描述
AUDIT_WRITE将记录写入内核审计日志。
CHOWN对文件 UID 和 GID 进行任意更改(参见 chown(2))。
DAC_OVERRIDE绕过文件读取、写入和执行权限检查。
FOWNER绕过通常需要进程的文件系统 UID 与文件的 UID 匹配的操作的权限检查。
FSETID修改文件时不清空 set-user-ID 和 set-group-ID 权限位。
KILL绕过发送信号的权限检查。
MKNOD使用 mknod(2) 创建特殊文件。
NET_BIND_SERVICE将 socket 绑定到互联网域特权端口(端口号小于 1024)。
NET_RAW使用 RAW 和 PACKET socket。
SETFCAP设置文件能力。
SETGID对进程 GID 和补充 GID 列表进行任意操作。
SETPCAP修改进程能力。
SETUID对进程 UID 进行任意操作。
SYS_CHROOT使用 chroot(2),更改根目录。

下表显示了默认未授予但可以添加的能力。

能力键能力描述
AUDIT_CONTROL启用和禁用内核审计;更改审计过滤规则;检索审计状态和过滤规则。
AUDIT_READ允许通过多播 netlink socket 读取审计日志。
BLOCK_SUSPEND允许阻止系统挂起。
BPF允许创建 BPF map,加载 BPF 类型格式 (BTF) 数据,检索 BPF 程序的 JIT 编译代码等。
CHECKPOINT_RESTORE允许与检查点/恢复相关的操作。在 kernel 5.9 中引入。
DAC_READ_SEARCH绕过文件读取权限检查以及目录读取和执行权限检查。
IPC_LOCK锁定内存 (mlock(2), mlockall(2), mmap(2), shmctl(2))。
IPC_OWNER绕过对 System V IPC 对象的权限检查。
LEASE在任意文件上建立租约(参见 fcntl(2))。
LINUX_IMMUTABLE设置 FS_APPEND_FL 和 FS_IMMUTABLE_FL i-node 标志。
MAC_ADMIN允许 MAC 配置或状态更改。针对 Smack LSM 实现。
MAC_OVERRIDE覆盖强制访问控制 (MAC)。针对 Smack Linux 安全模块 (LSM) 实现。
NET_ADMIN执行各种与网络相关的操作。
NET_BROADCAST进行 socket 广播,并监听多播。
PERFMON允许使用 perf_events、i915_perf 和其他内核子系统执行系统性能和可观测性特权操作
SYS_ADMIN执行一系列系统管理操作。
SYS_BOOT使用 reboot(2) 和 kexec_load(2),重启并加载新内核以供后续执行。
SYS_MODULE加载和卸载内核模块。
SYS_NICE提高进程的 nice 值 (nice(2), setpriority(2)) 并更改任意进程的 nice 值。
SYS_PACCT使用 acct(2),打开或关闭进程记账。
SYS_PTRACE使用 ptrace(2) 跟踪任意进程。
SYS_RAWIO执行 I/O 端口操作 (iopl(2) 和 ioperm(2))。
SYS_RESOURCE覆盖资源限制。
SYS_TIME设置系统时钟 (settimeofday(2), stime(2), adjtimex(2));设置实时(硬件)时钟。
SYS_TTY_CONFIG使用 vhangup(2);在虚拟终端上使用各种特权 ioctl(2) 操作。
SYSLOG执行特权 syslog(2) 操作。
WAKE_ALARM触发唤醒系统的操作。

更多参考信息可在 capabilities(7) - Linux man 手册页Linux 内核源代码中。

这两个标志都支持值 ALL,因此要允许容器使用除 MKNOD 之外的所有能力

$ docker run --cap-add=ALL --cap-drop=MKNOD ...

--cap-add--cap-drop 标志接受带 CAP_ 前缀指定的能力。因此,以下示例是等效的

$ docker run --cap-add=SYS_ADMIN ...
$ docker run --cap-add=CAP_SYS_ADMIN ...

对于与网络堆栈交互,他们不应使用 --privileged,而应使用 --cap-add=NET_ADMIN 来修改网络接口。

$ docker run -it --rm  ubuntu:24.04 ip link add dummy0 type dummy

RTNETLINK answers: Operation not permitted

$ docker run -it --rm --cap-add=NET_ADMIN ubuntu:24.04 ip link add dummy0 type dummy

要挂载基于 FUSE 的文件系统,您需要结合使用 --cap-add--device

$ docker run --rm -it --cap-add SYS_ADMIN sshfs sshfs sven@10.10.10.20:/home/sven /mnt

fuse: failed to open /dev/fuse: Operation not permitted

$ docker run --rm -it --device /dev/fuse sshfs sshfs sven@10.10.10.20:/home/sven /mnt

fusermount: mount failed: Operation not permitted

$ docker run --rm -it --cap-add SYS_ADMIN --device /dev/fuse sshfs

# sshfs sven@10.10.10.20:/home/sven /mnt
The authenticity of host '10.10.10.20 (10.10.10.20)' can't be established.
ECDSA key fingerprint is 25:34:85:75:25:b0:17:46:05:19:04:93:b5:dd:5f:c6.
Are you sure you want to continue connecting (yes/no)? yes
sven@10.10.10.20's password:

root@30aa0cfaf1b5:/# ls -la /mnt/src/docker

total 1516
drwxrwxr-x 1 1000 1000   4096 Dec  4 06:08 .
drwxrwxr-x 1 1000 1000   4096 Dec  4 11:46 ..
-rw-rw-r-- 1 1000 1000     16 Oct  8 00:09 .dockerignore
-rwxrwxr-x 1 1000 1000    464 Oct  8 00:09 .drone.yml
drwxrwxr-x 1 1000 1000   4096 Dec  4 06:11 .git
-rw-rw-r-- 1 1000 1000    461 Dec  4 06:08 .gitignore
....

默认的 seccomp 配置文件会根据所选的能力进行调整,以允许使用能力所允许的功能,因此您应该不需要调整此项。

覆盖镜像默认设置

当您从 Dockerfile 构建镜像或提交镜像时,您可以设置一些默认参数,这些参数在镜像作为容器启动时生效。运行镜像时,您可以使用 docker run 命令的标志覆盖这些默认值。

默认命令和选项

docker run 的命令语法支持可选地为容器的 entrypoint 指定命令和参数,在以下概要示例中表示为 [COMMAND][ARG...]

$ docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]

此命令是可选的,因为创建 IMAGE 的人可能已经使用 Dockerfile CMD 指令提供了一个默认 COMMAND。运行容器时,您只需指定新的 COMMAND 即可覆盖该 CMD 指令。

如果镜像还指定了 ENTRYPOINT,则 CMDCOMMAND 将作为参数追加到 ENTRYPOINT 后面。

默认入口点

--entrypoint="": Overwrite the default entrypoint set by the image

entrypoint 指的是运行容器时调用的默认可执行文件。容器的 entrypoint 使用 Dockerfile ENTRYPOINT 指令定义。它类似于指定默认命令,因为它指定了,但区别在于您需要传递一个显式标志来覆盖 entrypoint,而可以使用位置参数覆盖默认命令。Entrypoint 定义了容器的默认行为,其理念是当您设置 entrypoint 时,可以 像运行该二进制文件一样 运行容器,附带默认选项,并且可以传递更多选项作为命令。但在某些情况下,您可能想在容器内运行其他东西。这时在运行时使用 docker run 命令的 --entrypoint 标志覆盖默认 entrypoint 就派上用场了。

--entrypoint 标志需要一个字符串值,表示容器启动时要调用的二进制文件的名称或路径。以下示例展示了如何在已设置为自动运行其他二进制文件(如 /usr/bin/redis-server)的容器中运行 Bash shell

$ docker run -it --entrypoint /bin/bash example/redis

以下示例展示了如何使用位置命令参数将附加参数传递给自定义 entrypoint

$ docker run -it --entrypoint /bin/bash example/redis -c ls -l
$ docker run -it --entrypoint /usr/bin/redis-cli example/redis --help

您可以通过传递空字符串来重置容器的 entrypoint,例如

$ docker run -it --entrypoint="" mysql bash

注意

传递 --entrypoint 会清除镜像上设置的所有默认命令。也就是说,用于构建镜像的 Dockerfile 中的任何 CMD 指令。

暴露端口

默认情况下,运行容器时,容器的任何端口都不会暴露给主机。这意味着您将无法访问容器可能监听的任何端口。要使容器的端口可以从主机访问,您需要发布端口。

您可以使用 -P-p 标志启动容器以暴露其端口

  • -P(或 --publish-all)标志将所有暴露的端口发布到主机。Docker 将每个暴露的端口绑定到主机上的一个随机端口。

    -P 标志仅发布那些被明确标记为暴露的端口号,这可以通过 Dockerfile 的 EXPOSE 指令或 docker run 命令的 --expose 标志实现。

  • -p(或 --publish)标志允许您将容器中的单个端口或端口范围显式映射到宿主机。

容器内部的端口号(服务监听的端口)不需要与容器外部发布的端口号(客户端连接的端口)匹配。例如,在容器内部,一个 HTTP 服务可能正在监听端口 80。运行时,该端口在宿主机上可能被绑定到 42800。要查找宿主机端口和暴露端口之间的映射关系,请使用 docker port 命令。

环境变量

创建 Linux 容器时,Docker 会自动设置一些环境变量。创建 Windows 容器时,Docker 不会设置任何环境变量。

为 Linux 容器设置了以下环境变量

变量
HOME基于 USER 的值设置
HOSTNAME与容器关联的主机名
PATH包含常用目录,例如 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
TERM如果为容器分配了伪 TTY,则为 xterm

此外,您可以使用一个或多个 -e 标志在容器中设置任何环境变量。您甚至可以覆盖上面提到的变量,或在使用 Dockerfile ENV 指令构建镜像时定义的变量。

如果您指定环境变量名称但不指定值,则宿主机上该命名变量的当前值会传播到容器的环境中

$ export today=Wednesday
$ docker run -e "deep=purple" -e today --rm alpine env

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=d2219b854598
deep=purple
today=Wednesday
HOME=/root
PS C:\> docker run --rm -e "foo=bar" microsoft/nanoserver cmd /s /c set
ALLUSERSPROFILE=C:\ProgramData
APPDATA=C:\Users\ContainerAdministrator\AppData\Roaming
CommonProgramFiles=C:\Program Files\Common Files
CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
CommonProgramW6432=C:\Program Files\Common Files
COMPUTERNAME=C2FAEFCC8253
ComSpec=C:\Windows\system32\cmd.exe
foo=bar
LOCALAPPDATA=C:\Users\ContainerAdministrator\AppData\Local
NUMBER_OF_PROCESSORS=8
OS=Windows_NT
Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Users\ContainerAdministrator\AppData\Local\Microsoft\WindowsApps
PATHEXT=.COM;.EXE;.BAT;.CMD
PROCESSOR_ARCHITECTURE=AMD64
PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 62 Stepping 4, GenuineIntel
PROCESSOR_LEVEL=6
PROCESSOR_REVISION=3e04
ProgramData=C:\ProgramData
ProgramFiles=C:\Program Files
ProgramFiles(x86)=C:\Program Files (x86)
ProgramW6432=C:\Program Files
PROMPT=$P$G
PUBLIC=C:\Users\Public
SystemDrive=C:
SystemRoot=C:\Windows
TEMP=C:\Users\ContainerAdministrator\AppData\Local\Temp
TMP=C:\Users\ContainerAdministrator\AppData\Local\Temp
USERDOMAIN=User Manager
USERNAME=ContainerAdministrator
USERPROFILE=C:\Users\ContainerAdministrator
windir=C:\Windows

健康检查

docker run 命令的以下标志允许您控制容器健康检查的参数

选项描述
--health-cmd用于检查健康的命令
--health-interval运行检查之间的时间间隔
--health-retries报告不健康所需的连续失败次数
--health-timeout允许一次检查运行的最大时间
--health-start-period容器在开始健康检查重试倒计时之前的启动周期
--health-start-interval启动周期内运行检查之间的时间间隔
--no-healthcheck禁用容器指定的任何 HEALTHCHECK

示例

$ docker run --name=test -d \
    --health-cmd='stat /etc/passwd || exit 1' \
    --health-interval=2s \
    busybox sleep 1d
$ sleep 2; docker inspect --format='{{.State.Health.Status}}' test
healthy
$ docker exec test rm /etc/passwd
$ sleep 2; docker inspect --format='{{json .State.Health}}' test
{
  "Status": "unhealthy",
  "FailingStreak": 3,
  "Log": [
    {
      "Start": "2016-05-25T17:22:04.635478668Z",
      "End": "2016-05-25T17:22:04.7272552Z",
      "ExitCode": 0,
      "Output": "  File: /etc/passwd\n  Size: 334       \tBlocks: 8          IO Block: 4096   regular file\nDevice: 32h/50d\tInode: 12          Links: 1\nAccess: (0664/-rw-rw-r--)  Uid: (    0/    root)   Gid: (    0/    root)\nAccess: 2015-12-05 22:05:32.000000000\nModify: 2015..."
    },
    {
      "Start": "2016-05-25T17:22:06.732900633Z",
      "End": "2016-05-25T17:22:06.822168935Z",
      "ExitCode": 0,
      "Output": "  File: /etc/passwd\n  Size: 334       \tBlocks: 8          IO Block: 4096   regular file\nDevice: 32h/50d\tInode: 12          Links: 1\nAccess: (0664/-rw-rw-r--)  Uid: (    0/    root)   Gid: (    0/    root)\nAccess: 2015-12-05 22:05:32.000000000\nModify: 2015..."
    },
    {
      "Start": "2016-05-25T17:22:08.823956535Z",
      "End": "2016-05-25T17:22:08.897359124Z",
      "ExitCode": 1,
      "Output": "stat: can't stat '/etc/passwd': No such file or directory\n"
    },
    {
      "Start": "2016-05-25T17:22:10.898802931Z",
      "End": "2016-05-25T17:22:10.969631866Z",
      "ExitCode": 1,
      "Output": "stat: can't stat '/etc/passwd': No such file or directory\n"
    },
    {
      "Start": "2016-05-25T17:22:12.971033523Z",
      "End": "2016-05-25T17:22:13.082015516Z",
      "ExitCode": 1,
      "Output": "stat: can't stat '/etc/passwd': No such file or directory\n"
    }
  ]
}

健康状态也会显示在 docker ps 输出中。

用户

容器内的默认用户是 root (uid = 0)。您可以使用 Dockerfile 的 USER 指令设置默认用户来运行第一个进程。启动容器时,您可以通过传递 -u 选项来覆盖 USER 指令。

-u="", --user="": Sets the username or UID used and optionally the groupname or GID for the specified command.

以下所有示例均有效

--user=[ user | user:group | uid | uid:gid | user:gid | uid:group ]

注意

如果您传递数字用户 ID,它必须在 0-2147483647 的范围内。如果您传递用户名,该用户必须存在于容器中。

工作目录

在容器内运行二进制文件的默认工作目录是根目录(/)。镜像的默认工作目录是使用 Dockerfile 的 WORKDIR 命令设置的。您可以使用 docker run 命令的 -w(或 --workdir)标志覆盖镜像的默认工作目录

$ docker run --rm -w /my/workdir alpine pwd
/my/workdir

如果该目录在容器中尚不存在,则会创建它。

页面选项