运行容器
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 或更高版本镜像格式的镜像具有一个称为摘要的内容可寻址标识符。只要用于生成镜像的输入保持不变,摘要值就是可预测的。
以下示例使用 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 run --detach: 在后台运行容器docker run --attach: 附加到stdin、stdout和stderrdocker run --tty: 分配一个伪终端docker run --interactive: 保持stdin打开,即使未附加
有关重新连接到后台容器的更多信息,请参阅
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 支持两种主要类型的挂载:
- 卷挂载
- 挂载绑定
卷挂载非常适合为容器持久存储数据,以及在容器之间共享数据。另一方面,绑定挂载用于在容器和主机之间共享数据。
您可以使用 --mount 标志为容器添加文件系统挂载,该标志用于 docker run 命令。
以下部分展示了如何创建卷和绑定挂载的基本示例。有关更深入的示例和说明,请参阅文档中的 存储部分。
卷挂载
要创建卷挂载:
$ docker run --mount source=<VOLUME_NAME>,target=[PATH] [IMAGE] [COMMAND...]
在这种情况下,--mount 标志需要两个参数:source 和 target。
source 参数的值是卷的名称。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
其他退出码
任何除 125、126 和 127 以外的退出代码都表示所提供容器命令的退出代码。
$ docker run busybox /bin/sh -c 'exit 3'
$ echo $?
3
资源运行时约束
操作员还可以调整容器的性能参数:
| 选项 | 描述 |
|---|---|
-m, --memory="" | 内存限制(格式:<number>[<unit>])。数字为正整数。单位可以是 b、k、m 或 g 之一。最小值为 6M。 |
--memory-swap="" | 总内存限制(内存 + 交换空间,格式:<number>[<unit>])。数字为正整数。单位可以是 b、k、m 或 g 之一。 |
--memory-reservation="" | 内存软限制(格式:<number>[<unit>])。数字为正整数。单位可以是 b、k、m 或 g 之一。 |
--kernel-memory="" | 内核内存限制(格式:<number>[<unit>])。数字为正整数。单位可以是 b、k、m 或 g 之一。最小值为 4M。 |
-c, --cpu-shares=0 | CPU 份额(相对权重) |
--cpus=0.000 | CPU数量。该数字为小数。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,且不能高于父 cgroups。同时检查 rtprio ulimits。 |
--blkio-weight=0 | 块 IO 权重(相对权重)接受介于 10 和 1000 之间的权重值。 |
--blkio-weight-device="" | 块 IO 权重(相对设备权重,格式:DEVICE_NAME:WEIGHT) |
--device-read-bps="" | 限制设备的读取速率(格式:<device-path>:<number>[<unit>])。数字为正整数。单位可以是 kb、mb 或 gb 之一。 |
--device-write-bps="" | 限制设备的写入速率(格式:<device-path>:<number>[<unit>])。数字为正整数。单位可以是 kb、mb 或 gb 之一。 |
--device-read-iops="" | 限制从设备的读取速率(每秒IO次数)(格式:<device-path>:<number>)。数字为正整数。 |
--device-write-iops="" | 限制设备的写入速率(每秒 IO 次数)(格式:<device-path>:<number>)。数字为正整数。 |
--oom-kill-disable=false | 是否禁用容器的 OOM Killer。 |
--oom-score-adj=0 | 调整容器的 OOM 偏好设置(-1000 到 1000) |
--memory-swappiness="" | 调整容器的内存交换行为。接受一个 0 到 100 之间的整数。 |
--shm-size="" | /dev/shm 的大小。格式为 <number><unit>。number 必须大于 0。单位是可选的,可以是 b(字节)、k(千字节)、m(兆字节)或 g(吉字节)。如果省略单位,系统将使用字节。如果完全省略大小,系统将使用 64m。 |
用户内存限制
我们有四种方法来设置用户内存使用:
| 选项 | 结果 |
|---|---|
| memory=inf, memory-swap=inf (默认) | 容器没有内存限制。容器可以使用所需的任意内存量。 |
| memory=L<inf, memory-swap=inf | (指定内存并将 memory-swap 设置为 -1)容器不允许使用超过 L 字节的内存,但可以根据需要使用任意数量的交换空间(如果主机支持交换内存)。 |
| 内存=L<inf, 内存交换=2*L | (在不指定内存交换的情况下)容器不允许使用超过 L 字节的内存,交换 加上 内存使用量是两倍。 |
| memory=L<inf, memory-swap=S<inf, L<=S | (同时指定 memory 和 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 终止程序。如果未设置 -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 页面
- 套接字内存压力
- tcp 内存压力
您可以设置内核内存限制来约束这些类型的内存。例如, 每个进程都会消耗一些栈页。通过限制内核内存,您可以在内核内存使用过高时 防止创建新进程。
内核内存绝不是完全独立于用户内存的。相反,你在用户内存限制的上下文中限制内核内存。假设“U”是用户内存限制,“K”是内核限制。有三种可能的方式来设置限制:
| 选项 | 结果 |
|---|---|
| U != 0, K = inf (默认值) | 这是在使用内核内存之前已经存在的标准内存限制机制。内核内存被完全忽略。 |
| U != 0, K < U | 内核内存是用户内存的一个子集。这种设置在部署中非常有用,尤其是在每个cgroup的内存总量被超额分配的情况下。 不建议超额分配内核内存限制,因为系统仍然可能耗尽不可回收的内存。 在这种情况下,您可以配置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,内存交换性值将从父级继承。
例如,您可以设置:
$ 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 时,第一个容器将获得 50% 的总 CPU 时间。如果添加第四个 cpu-share 为 1024 的容器,第一个容器只能获得 33% 的 CPU。其余容器分别获得 16.5%、16.5% 和 33% 的 CPU。
在多核系统中,CPU时间的份额分布在所有CPU核心上。即使容器的CPU时间限制在不到100%,它也可以使用每个单独CPU核心的100%。
例如,考虑一个拥有超过三个核心的系统。如果你启动一个容器 {C0} 并运行一个进程,再启动另一个容器 {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
如果有 1 个 CPU,这意味着容器每 50 毫秒可以获得 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 可将容器的 CPU 资源限制为 50%。对于多个 CPU,请根据需要调整 --cpu-quota。有关更多信息,请参阅
关于带宽限制的 CFS 文档。
块IO带宽(Blkio)限制
默认情况下,所有容器获得相同比例的块IO带宽
(blkio)。此比例为500。要修改此比例,请更改
容器的blkio权重,相对于所有其他运行中
容器的权重,使用--blkio-weight标志。
注意
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 标志将写入速率(每秒字节数)限制到设备。
例如,此命令创建一个容器,并将写入速率限制为每秒 1mb,持续 /dev/sda 秒:
$ 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 操作次数)。
例如,此命令创建一个容器并将写入速率限制为
1000 每秒 IO 操作次数到 /dev/sda:
$ 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 能力 |
--cap-drop | 移除 Linux 能力 |
--privileged | 授予此容器扩展权限 |
--device=[] | 允许您在容器内运行设备而不需要 --privileged 标志。 |
默认情况下,Docker 容器是“非特权”的,无法在 Docker 容器内运行 Docker 守护进程。这是因为默认情况下容器不允许访问任何设备,但“特权”容器被授予访问所有设备的权限(请参阅有关 cgroups 设备的文档)。
--privileged 标志赋予容器所有能力。当操作员
执行 docker run --privileged 时,Docker 会启用对主机上所有设备的访问,
并重新配置 AppArmor 或 SELinux 以允许容器
几乎与主机上容器外运行的进程具有相同的访问权限。
请谨慎使用此标志。
有关 --privileged 标志的更多信息,请参阅
docker run 参考。
如果您想限制对特定设备或多个设备的访问,您可以使用
--device 标志。它允许您指定一个或多个设备,这些设备将在
容器内可访问。
$ docker run --device=/dev/snd:/dev/snd ...
默认情况下,容器将能够 read、write 和 mknod 这些设备。
这可以通过为每个 --device 标志设置第三组 :rwm 选项来覆盖:
$ 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 | 修改文件时,不要清除设置用户ID和设置组ID权限位。 |
| 终止 | 绕过发送信号的权限检查。 |
| MKNOD | 使用 mknod(2) 创建特殊文件。 |
| NET_BIND_SERVICE | 将套接字绑定到互联网域特权端口(端口号小于1024)。 |
| NET_RAW | 使用 RAW 和 PACKET 套接字。 |
| SETFCAP | 设置文件能力。 |
| SETGID | 对进程 GID 和补充 GID 列表进行任意操作。 |
| SETPCAP | 修改进程能力。 |
| SETUID | 对进程UID进行任意操作。 |
| SYS_CHROOT | 使用 chroot(2),更改根目录。 |
下表显示了默认未授予但可能添加的功能。
| 功能键 | 功能描述 |
|---|---|
| AUDIT_CONTROL | 启用和禁用内核审计;更改审计过滤规则;检索审计状态和过滤规则。 |
| AUDIT_READ | 允许通过多播 netlink 套接字读取审计日志。 |
| BLOCK_SUSPEND | 允许防止系统挂起。 |
| BPF | 允许创建 BPF 映射,加载 BPF 类型格式(BTF)数据,检索 BPF 程序的 JIT 代码,以及更多功能。 |
| CHECKPOINT_RESTORE | 允许检查点/恢复相关操作。在内核 5.9 中引入。 |
| DAC_READ_SEARCH | 绕过文件读取权限检查以及目录读取和执行权限检查。 |
| IPC_LOCK | 锁定内存 (mlock(2), mlockall(2), mmap(2), shmctl(2))。 |
| IPC_OWNER | 绕过对 System V IPC 对象执行操作的权限检查。 |
| 租用 | 在任意文件上建立租约(参见 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 | 进行套接字广播,并侦听多播。 |
| 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(2) 操作。 |
| WAKE_ALARM | 触发一些将唤醒系统的操作。 |
更多参考信息可在 capabilities(7) - Linux 手册页 和 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 的命令语法支持可选地指定容器入口点的命令和参数,在以下摘要示例中表示为 [COMMAND] 和 [ARG...]:
$ docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]
此命令是可选的,因为创建 IMAGE 的人可能已经通过 Dockerfile CMD 指令提供了默认的 COMMAND。当您运行容器时,只需指定一个新的 COMMAND,即可覆盖该 CMD 指令。
如果镜像还指定了一个 ENTRYPOINT,那么 CMD 或 COMMAND 会作为参数附加到 ENTRYPOINT 上。
默认入口点
--entrypoint="": Overwrite the default entrypoint set by the image入口点是指运行容器时调用的默认可执行文件。容器的入口点是通过 Dockerfile 中的 ENTRYPOINT 指令定义的。它类似于指定默认命令,因为它指定了默认行为,但区别在于你需要传递一个显式标志来覆盖入口点,而你可以使用位置参数覆盖默认命令。入口点定义了容器的默认行为,其理念是当你设置入口点时,你可以运行容器,就像它是那个Binaries一样,包括默认选项,并且你可以作为命令传递更多选项。但在某些情况下,你可能希望在容器内运行其他内容。这时在运行时覆盖默认入口点就派上用场了,可以使用 docker run 命令的 --entrypoint 标志。
--entrypoint 标志期望一个字符串值,表示容器启动时要调用的Binaries的名称或路径。以下示例展示了如何在设置为自动运行其他Binaries(如 /usr/bin/redis-server)的容器中运行 Bash shell:
$ docker run -it --entrypoint /bin/bash example/redis
以下示例展示了如何使用位置命令参数将额外参数传递给自定义入口点:
$ docker run -it --entrypoint /bin/bash example/redis -c ls -l
$ docker run -it --entrypoint /usr/bin/redis-cli example/redis --help
您可以通过传递空字符串来重置容器的入口点,例如:
$ docker run -it --entrypoint="" mysql bash
注意
传递
--entrypoint会清除镜像上设置的任何默认命令。也就是说,用于构建该镜像的 Dockerfile 中的任何CMD指令。
暴露端口
默认情况下,当您运行容器时,容器的端口不会暴露给主机。这意味着您无法访问容器可能正在监听的任何端口。要使容器的端口可以从主机访问,您需要发布这些端口。
您可以使用 -P 或 -p 标志来启动容器以暴露其端口:
-P(或--publish-all)标志会将所有暴露的端口发布到 主机。Docker 会将每个暴露的端口绑定到主机上的一个随机端口。-P标志仅发布被明确标记为暴露的端口号,这些端口号可以通过 Dockerfile 的EXPOSE指令或docker run命令的--expose标志来指定。The
-p(或--publish) 标志允许您显式地将容器中的单个端口或端口范围映射到主机。
容器内部的端口号(服务监听的端口)不需要与容器外部发布的端口号(客户端连接的端口)匹配。例如,在容器内部,HTTP服务可能监听在端口80上。在运行时,该端口可能会绑定到主机上的42800端口。要查找主机端口与暴露端口之间的映射关系,请使用docker port命令。
环境变量
Docker 在创建 Linux 容器时会自动设置一些环境变量。在创建 Windows 容器时,Docker 不会设置任何环境变量。
为 Linux 容器设置了以下环境变量:
| 变量 | 值 |
|---|---|
HOME | 基于值 USER 进行设置 |
HOSTNAME | 与容器关联的主机名 |
PATH | 包括流行目录,例如 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |
TERM | 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 范围内。如果您传递的是用户名,则该用户必须存在于容器中。
工作目录
在容器内运行Binaries的默认工作目录是
根目录 (/)。镜像的默认工作目录是通过
Dockerfile 的 WORKDIR 命令设置的。你可以使用
docker run 命令的 -w(或 --workdir)标志来覆盖镜像的默认工作目录:
$ docker run --rm -w /my/workdir alpine pwd
/my/workdir如果容器中尚不存在该目录,则会创建它。