Docker Container(容器)使用指南

Docker Container(容器)使用指南

八月 31, 2025 次阅读

什么是容器

通俗地讲,容器是镜像的运行实体。镜像是静态的只读文件,而容器带有运行时需要的可写文件层,并且容器中的进程属于运行状态。即容器运行着真正的应用进程。容器有初建、运行、停止、暂停和删除五种状态。
虽然容器的本质是主机上运行的一个进程,但是容器有自己独立的命名空间隔离和资源限制。也就是说,在容器内部,无法看到主机上的进程、环境变量、网络等信息,这是容器与直接运行在主机上进程的本质区别。
容器是基于镜像创建的可运行实例,并且单独存在,一个镜像可以创建出多个容器。运行容器化环境时,实际上是在容器内部创建该文件系统的读写副本。这将添加一个容器层,该层允许修改镜像的整个副本。

base

为什么需要容器

镜像是静态文件,是只读的,它并不能提供服务,就好比我有一个 Linux 的光盘,它里面有操作系统的所有文件,但我无法直接从光盘上运行这些文件。为了让这些文件发挥作用,我需要将它们加载到内存中并运行。这就是容器的作用,它为镜像提供了一个可运行的环境。

容器的生命周期

容器的生命周期指的是容器可能处于的不同状态,通常分为以下几类:

1、created:容器已创建,但尚未启动。
2、running:容器正在运行中。
3、stopped:容器已停止运行。
4、paused:容器已暂停。
5、deleted:容器已被删除。

各生命周期之间的转换关系如图所示:

status

名词解释如下:

  • docker create:创建容器后,不立即启动运行,容器进入初建状态;
  • docker run:创建容器,并立即启动运行,进入运行状态;
  • docker start:容器转为运行状态;
  • docker stop:容器将转入停止状态;
  • docker kill:容器在故障(死机)时,执行 kill(断电),容器转入停止状态,这种操作容易丢失数据,除非必要,否则不建议使用;
  • docker restart:重启容器,容器转入运行状态;
  • docker pause:容器进入暂停状态;
  • docker unpause:取消暂停状态,容器进入运行状态;
  • docker rm:删除容器,容器转入删除状态。

异常终止状态说明:

  • killed by out-of-memory(因内存不足被终止)

宿主机内存被耗尽,也被称为 OOM。属于非计划终止,这时需要杀死最吃内存的容器。

  • container process exited(异常终止)

出现容器被终止后,将进入 Should restart? 选择操作:

  • yes:需要重启,容器执行 start 命令,转为运行状态。
  • no:不需要重启,容器转为停止状态。

针对容器的 OOM、异常退出以及暂停,我们来作进一步的深入分析。

容器 OOM

Docker 在处理 OOM 事件时分为三种情况:

(1)容器内存超限触发 OOM
如果容器中的应用耗尽了主机系统分配给容器的内存限额,就会触发 OOM 事件。例如,在容器当中部署了一个 web 服务。假设主机分配给此容器的内存上限为 1G,当脚本申请的内存大于 1G 时,此容器就会触发 OOM 事件。而在这种情况下,此容器将会被强制关闭。

但需要注意的是,此时关闭容器的并非是 Docker Daemon,而是宿主机操作系统。因为一个容器其实就是一组运行在宿主机操作系统中的进程,宿主机操作系统通过 cgroups 对这组进程设定资源上限,当这些进程申请的资源到达上限时,触发的是宿主机操作系统的内核 OOM 事件,因此最终是由宿主机内核来关闭这些进程。

(2)禁用 OOM-Killer 的情况
如果用户不想关闭这个容器,那么可以选择 --oom-kill-disable 来禁用 OOM-Killer。使用此参数时,仍需要注意:如果使用 -m 设置了此容器内存上限,那么当容器到达内存资源上限时,主机不会关闭容器,但也不会继续向此容器继续分配资源,此时容器将处于 hung 状态。只需要将最坏的情况封闭在一定范围之内,而不至于蔓延出去。

(3)禁用 OOM-Killer 但未设置内存上限
如果用户使用了 --oom-kill-disable,但也没有使用 -m 来设定上限,因而此时此容器将会尽可能地使用主机内存资源。换言之,主机内存有多大,它就将用多大。

容器异常退出

每个容器内部都存在一个 Init 进程,容器中其他所有进程都是此进程的子进程。运行的容器是因为 Init 进程在运行,如果一个子进程因为某种原因造成了退出,那么其父进程也会同步退出,直至 Init 进程也退出。当 Init 进程退出时,也就代表着此容器被关闭。

Docker 目前没有办法知道此时的进程退出属于正常退出还是异常退出。当出现容器关闭情况时,Docker Daemon 会尝试再次重新将此容器由 Stopped 状态转为 Running 状态。只有设置了 --restart 参数的容器,Docker Daemon 才会去尝试启动,否则容器会保持停止状态。

容器暂停

Docker”剥夺”了此容器的 CPU 资源。而其他资源,如 Memory 资源、Network 资源等还保留未动。如此一来,失去了 CPU 资源的进程,是不会被主机内核系统所调度的,所以此容器就处于”冰封”状态,底层实现上,实际上就是不给该容器的进程分配时间片。

Docker Container 常用命令

docker create

docker create 命令用于创建一个新的容器,但并不会立即启动它。

语法格式:

docker create [OPTIONS] IMAGE [COMMAND] [ARG...]

你可以把它想象成买了一辆新车(从镜像创建容器),把它停在了车库裡,但还没有插入钥匙启动引擎。此时,车辆的所有配置(颜色、型号、内饰)都已就绪,只差最后一步——点火。

docker create 与 docker run 的区别

我们可以用一个简单的类比来区分:

  • docker run = docker create + docker start

    • docker run nginx:latest:这条命令会直接拉取镜像(如果本地没有)、创建容器、并立刻启动它,让你马上看到一个运行中的 Nginx 服务。
  • docker create:它只完成 docker run 的前半部分工作。

    • docker create --name my-nginx nginx:latest:这条命令会基于 nginx:latest 镜像创建好一个容器,配置好一切,然后将其置于已创建(Created) 状态。此时,容器存在,但内部的进程(如 Nginx 主进程)还没有启动。

核心区别在于:docker create 让容器在运行前有一个“停滞”的状态,这为我们提供了检查和修改配置的机会。

docker create 的核心工作流程

  1. 查找镜像:首先,Docker 会在本地查找指定的镜像(如 nginx:latest)。如果找不到,它会尝试从配置的仓库(如 Docker Hub)拉取。
  2. 创建容器层:在镜像的只读层(Read-only Layers)之上,添加一个可写的容器层(Container Layer),为未来的文件系统更改做准备。
  3. 配置网络:根据命令参数或默认配置,为容器设置网络模式(如桥接网络 bridge)。
  4. 初始化配置:处理所有通过 -e(环境变量)、-v(数据卷)、-p(端口映射)等参数传递的配置。
  5. 准备执行:设置好默认要运行的命令(即 Dockerfile 中的 CMD 或 ENTRYPOINT),但不执行它。
  6. 输出容器 ID:最终,命令会返回一个唯一的长 ID(如 f39a6aadc79f...)和容器名称,表示创建成功。

常用参数和示例

docker create 的参数与 docker run 几乎完全一致,因为它需要为容器设定所有运行时的配置。

基本示例 1:

# 创建一个名为 `my_web` 的 Nginx 容器,但不启动它
docker create --name my_web nginx:latest

基本示例 2(包含常用参数):

# 创建一个复杂的容器
docker create \
  --name my_app \                  # 为容器指定一个名称
  -p 8080:80 \                     # 将主机的 8080 端口映射到容器的 80 端口
  -v /host/path:/container/path \  # 挂载主机目录到容器中
  -e ENV_VAR="my_value" \          # 设置环境变量
  --restart=unless-stopped \       # 设置重启策略
  nginx:latest                     # 使用的镜像

执行上述命令后,你会得到一个容器 ID,表示容器已创建成功。可以使用 docker ps -a 查看所有容器(包括已停止的),你会发现 my_app 的状态是 Created。

docker create 的典型应用场景

  1. 预先配置:在复杂部署前,先创建容器以确保所有配置(如卷挂载、环境变量)正确无误,之后再统一启动。
  2. CI/CD 流水线:在持续集成/部署中,可以先创建容器镜像,然后在一个部署阶段统一启动所有容器,保证服务同时上线。
  3. 调试和检查:创建后,可以使用 docker inspect <container_id> 命令详细检查容器的所有配置,确保其符合预期,然后再启动。
  4. 作为模板:我们可以先创建一个配置好的容器,如果需要多个相同配置的实例,可以基于这个已创建的容器来提交新的镜像,或者作为参考模板。

docker start

创建容器后,通常会需要启动它:

语法格式:

docker start [OPTIONS] CONTAINER [CONTAINER...]

示例:

# 启动一个已创建的容器
docker start my_web

这条命令会将容器从 Created 状态转为 Running 状态,启动容器内的主进程(如 Nginx 服务)。

docker run

如果说 Docker 镜像是一个静态的、可执行的软件包(包含代码、运行时、库、环境变量和配置文件),那么 docker run 就是让这个软件包活起来的魔法咒语。它是 Docker 中最核心、最常用的命令,是创建和启动容器的唯一入口。理解 docker run,就掌握了开启容器化之旅的钥匙。


docker run 命令用于从指定的镜像创建并启动一个新的容器。

它的工作流程可以拆解为两个核心步骤:

  1. 创建:基于指定的镜像,创建一个新的可写容器层(Container Layer)。
  2. 启动:执行镜像中定义的默认命令(如 CMD 或 ENTRYPOINT),让容器内的应用进程运行起来。

基本语法:

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
  • IMAGE:用于创建容器的镜像名称(如 nginx:latest、ubuntu:20.04)。
  • [COMMAND] [ARG...]:(可选)覆盖镜像中定义的默认启动命令。
  • [OPTIONS]:这是命令的灵魂,用于配置容器的几乎所有方面,包括网络、存储、资源限制等。

为什么需要它?核心价值

  1. 应用标准化部署:只需一个包含所有依赖的镜像和一条 docker run 命令,就能在任何安装了 Docker 的环境中一致地运行应用,彻底解决“在我这儿是好的”的环境问题。
  2. 资源隔离与安全:通过选项轻松为应用分配和限制 CPU、内存等资源,并通过隔离机制保证安全性。
  3. 极致的可配置性:通过丰富的选项,可以灵活配置容器的网络、存储、日志、重启策略等,满足各种复杂场景的需求。

核心选项(OPTIONS)分类详解

docker run 的威力体现在其数十个选项上,以下是其中最常用和关键的几类:

1. 容器运行模式与交互:

  • -d, --detach:在后台运行容器(守护进程模式)。这是运行后台服务(如 Nginx、MySQL)的标准方式。

    docker run -d nginx
  • -it:组合使用,用于启动一个交互式容器。-i(保持标准输入打开)和 -t(分配一个伪终端) together 让你可以像使用普通 Shell 一样与容器交互。这是运行临时工具容器(如 Ubuntu、Alpine)的标准方式。

    docker run -it ubuntu /bin/bash

2. 名称与标识:

  • --name:为容器指定一个自定义名称,而不是使用 Docker 随机生成的名字。这对于后续的管理操作(如 docker stop、docker logs)至关重要。

    docker run --name my_web -d nginx

3. 网络配置:

  • -p, --publish:映射端口,将容器的端口发布到宿主机的端口上。这是从外部访问容器内服务的基础。

    # 将容器的80端口映射到宿主机的8080端口
    docker run -p 8080:80 nginx
  • --network:指定容器的网络模式(如 bridge、host、none 或自定义网络),实现容器间的隔离或通信。

4. 存储与数据持久化:

  • -v, --volume:挂载数据卷或绑定宿主机目录。这是实现数据持久化的核心方法,避免数据随着容器的删除而丢失。

    # 绑定挂载:将宿主机的 /host/data 挂载到容器的 /container/data
    docker run -v /host/data:/container/data nginx
    
    # 使用命名的数据卷
    docker run -v my_volume:/container/data nginx

5. 环境变量配置:

  • -e, --env:设置容器内的环境变量。常用于传递配置参数(如密码、运行模式)给容器内的应用。

    docker run -e MODE=production -e DB_HOST=db.example.com my_app

6. 资源管理:

  • --cpus:限制容器能使用的 CPU 核心数。

  • -m, --memory:限制容器能使用的最大内存。

    docker run --cpus="1.5" -m "512m" my_app

7. 重启策略:

  • --restart:配置容器的自动重启策略,如 no(不重启)、on-failure(失败时重启)、always(总是重启)。这是保证服务高可用的关键配置。

    docker run --restart unless-stopped my_app

实战示例:组合使用选项

一个典型的后台 Web 服务启动命令可能结合了上述所有选项:

docker run -d \                  # 后台运行
  --name my-app \                # 指定容器名
  -p 80:80 \                     # 端口映射
  -v /app/config:/etc/config \   # 挂载配置文件目录
  -e DATABASE_URL=postgresql://... \ # 设置环境变量
  --cpus=2 \                     # 资源限制
  -m=1g \
  --restart on-failure \         # 重启策略
  my-app-image:latest           # 镜像名

最佳实践与注意事项

  1. 总是使用 --name:为容器命名是良好的习惯,这让管理变得更加简单和清晰。
  2. 理解前台与后台运行:交互式工具用 -it,后台服务用 -d。
  3. 数据持久化是必须的:任何需要保留的数据都必须通过 -v 挂载到宿主机或数据卷中,切勿依赖容器的可写层。
  4. 使用特定版本标签:尽量不要使用 latest 标签,而应使用明确的版本(如 nginx:1.25-alpine),以保证部署的一致性。
  5. 优先使用 docker compose:当选项变得复杂时(需要启动多个关联容器),使用 docker compose 来管理这些配置是更佳实践。

docker run 远不止是一个启动命令,它是容器生命的起点,是应用环境的编织者。

  • 它是 Docker 功能的集大成者,通过数十个选项将镜像、网络、存储、资源等模块串联起来。
  • 它的灵活性使得同一个镜像可以通过不同的参数,轻松适应开发、测试、生产等各种环境。
  • 掌握其常用选项是熟练使用 Docker 的必经之路。

从一条简单的 docker run hello-world 到复杂的企业级应用部署,docker run 贯穿始终,是每个 Docker 用户必须深刻理解和掌握的核心命令。

docker ps

该命令在之前的文章中已经介绍过,用于查看当前运行的容器,若需要了解请展开:

docker logs

当我们启动一个容器后,一个很自然的问题是:我的应用程序在里面运行得怎么样?它输出了什么?有没有报错? 容器是一个隔离的环境,我们无法直接像操作普通进程一样看到它的终端输出。这时,docker logs 命令就成了我们窥探容器内部运行时状态的 “万能窗口”。

docker logs 命令用于获取容器的日志。它会抓取并输出目标容器在其生命周期内,其内部主进程(PID 1)写入到标准输出(STDOUT) 和标准错误(STDERR) 的所有数据。

简单来说,你在容器内应用程序中用 console.log()、print()、cout、System.out.println() 等语句输出的内容,以及产生的错误信息,都可以通过这个命令查看到。

Docker 的日志处理机制

要理解 docker logs,首先要知道 Docker 是如何处理容器日志的。

  1. 默认的日志驱动(JSON File):Docker 默认使用 json-file 日志驱动。这意味着容器内主进程的 STDOUT 和 STDERR 流会被 Docker 守护进程(Docker Daemon)捕获。
  2. 持久化存储:Docker Daemon 会将捕获到的每一条日志,以 JSON 格式追加写入到主机磁盘上的一个文件中(通常位于 /var/lib/docker/containers/<container_id>/<container_id>-json.log)。
  3. 命令读取:当你执行 docker logs 时,Docker 客户端会与 Daemon 通信,Daemon 则去读取对应的 JSON 日志文件,并将格式化后的内容返回给你。

这种机制的好处在于,即使容器已经停止(Exited),只要日志文件没有被手动清理(例如通过 docker rm 删除容器),你依然可以查看其历史日志。

基本语法和常用参数

docker logs 的功能非常强大,通过添加不同的参数,可以满足各种查看需求。

基本语法:

docker logs [OPTIONS] CONTAINER

其中 CONTAINER 可以是容器名称 (--name 指定的) 或容器 ID(通常取前几位即可)。

最常用的参数(Flags):

参数 全称 作用
-f --follow 持续跟踪日志输出。类似于 tail -f,会实时显示新产生的日志,是监控容器行为的利器。
-t --timestamps 显示时间戳。每条日志前都会加上其产生的时间,对于调试和排序非常有用。
-n --tail 仅显示最后 N 条日志。例如 --tail 100 显示最后 100 行。
--since 显示从某个时间点开始的日志。例如 --since 2024-08-31 或 --since 1h(过去 1 小时)。
--until 显示在某个时间点之前的日志。
--details 显示更多的细节(额外的属性)。

实战示例

假设我们有一个正在运行的名为 my_web_app 的容器。

  1. 查看最新日志:

    # 查看最后10条日志(默认)
    docker logs my_web_app
    
    # 查看最后100条日志
    docker logs --tail 100 my_web_app
    # 或
    docker logs -n 100 my_web_app
  2. 实时跟踪日志(最常用):

    # 实时查看最新日志,常用于调试和监控
    docker logs -f my_web_app
    
    # 实时查看并带上时间戳
    docker logs -ft my_web_app

    按下 Ctrl+C 可以退出跟踪模式。

  3. 查看某个时间段内的日志:

    # 查看过去30分钟内的日志
    docker logs --since 30m my_web_app
    
    # 查看2024年1月1日之后的日志,并显示时间戳
    docker logs -t --since 2024-01-01 my_web_app
    
    # 查看今天早上8点到9点之间的日志
    docker logs --since 2024-08-31T08:00:00 --until 2024-08-31T09:00:00 my_web_app
  4. 查看已停止容器的日志:

    # 即使容器已停止,只要没被删除,日志依然可查
    docker logs my_stopped_container

高级用法与最佳实践

  1. 日志驱动(Logging Driver):
    Docker 支持多种日志驱动(如 json-file, syslog, journald, fluentd, awslogs 等)。docker logs 命令仅适用于 json-file、journald 和 local 等少数几种驱动。如果你将日志驱动设置为 syslog 或第三方工具(如 Fluentd),则 docker logs 将无法工作,你需要使用相应日志系统的工具(如 journalctl)来查看日志。

  2. 避免在容器内写日志文件:
    一个常见的反模式是在容器内将日志写入文件(如 /app/logs/app.log)。这会导致:

    • 日志失去 Docker 的自动管理功能。
    • 使用 docker logs 看不到这些日志。
    • 容器文件层变大,且日志清理困难。
      最佳实践是始终将日志输出到 STDOUT/STDERR,让 Docker 来统一处理。
  3. 日志大小轮替(Log Rotation):
    使用默认的 json-file 驱动时,需要注意日志文件会不断增长。可以通过 docker run 时设置 --log-opt 参数来控制日志大小和轮替,防止撑爆磁盘。

    docker run --log-opt max-size=10m --log-opt max-file=3 my_image
    # 含义:最多保存3个日志文件,每个文件最大10MB

docker attach

在 Docker 的交互命令工具箱里,docker attach 是一个特殊的存在。如果说 docker exec 像是通过 SSH 远程登录到一台服务器,那么 docker attach 就更像是直接坐在那台服务器的显示器前,接管了它的键盘和屏幕。先说明一个个人的观点:docker attach 并不是一个常用命令,除非你非常清楚它的行为和风险,否则不建议随意使用。


docker attach 命令用于将你的终端附加到一个正在运行的容器的主进程(PID 1)上。

它的核心作用是:

  • 将你终端的标准输入(STDIN)、标准输出(STDOUT) 和标准错误(STDERR) 连接到容器的主进程。
  • 让你能够看到主进程产生的输出,并向主进程发送输入。

基本语法:

docker attach [OPTIONS] CONTAINER

docker attach 的核心特性与工作机制

要理解 attach,必须理解它的几个关键特性:

  1. 附加到主进程,而非新进程:

    • docker exec:在容器内启动一个新进程(如 /bin/bash)。
    • docker attach:连接到一个已存在的进程(主进程)。你看到的就是主进程自己的输出,你输入的内容也会直接发送给主进程。
  2. 输入输出流的重定向:
    假设你这样启动一个容器:

    docker run --name my_container -it alpine /bin/sh

    此时,/bin/sh 就是主进程。docker attach my_container 会直接将你的终端连接到这个 /bin/sh 进程的输入输出流。

  3. 退出行为的危险性(最需要注意!):

    • 在 attach 的模式下,如果你按下 Ctrl+C(发送 SIGINT 信号),这个信号会直接传递给容器的主进程。
    • 如果主进程因此终止,容器也会随之停止(Exited)。
    • 同理,输入 exit 或 Ctrl+D(EOF)也会导致主进程退出,从而停止容器。

实战示例与场景

场景 1:查看交互式容器的输出:

假设你有一个 Python 应用在容器中运行,它会每秒打印一次日志。

# 假设容器已通过 `docker run -d --name my_app my_python_image` 运行
docker attach my_app

输出:

Current time: 19:25:01
Current time: 19:25:02
Current time: 19:25:03
...

此时,你就像在 tail -f 日志一样。但要小心! 如果你不小心按了 Ctrl+C,Python 程序会被中断,容器也就停止了。

场景 2:与交互式程序通信:

有些容器运行的是交互式程序,比如一个简单的 CLI 工具。

# 启动一个容器,运行一个要求输入名字的程序
docker run -it --name asker my_image

# (在另一个终端)附加到它
docker attach asker

你现在就可以直接向那个程序输入内容了。

如何安全地退出 docker attach?

由于直接退出会停止容器,Docker 提供了一个序列来脱离(detach)终端,而不停止容器:

按顺序按下:Ctrl+P, 然后 Ctrl+Q,需要注意的是,通过这种方式脱离终端需要你再启动该容器的时候给该容器指定了 -it 参数。

这个按键序列会将你的终端从容器主进程上分离出来,但让容器继续在后台运行。这是使用 attach 时必须掌握的“安全逃生”技巧。

当然,docker attach 也提供了一种安全脱离的方式,避免意外停止容器:

docker attach --sig-proxy=false my_container

docker attach 与 docker exec 的对比

特性 docker attach docker exec
目标进程 附加到现有的主进程(PID 1) 启动一个新的子进程(如 bash)
作用 连接主进程的输入输出流 在容器内执行额外命令
退出后果 Ctrl+C 会停止主进程和容器 exit 只退出 Shell,容器照常运行
安全退出 Ctrl+P + Ctrl+Q(分离而不停止) exit 或 Ctrl+D
典型用途 查看主进程原始输出流、与交互式主进程通信 调试、排查、管理容器(主要手段)

最佳实践和适用场景

什么时候使用 docker attach?

  1. 查看实时输出:当你需要查看一个前台运行的容器的原始、未经过滤的实时输出流,并且这个容器没有将日志重定向到其他地方时。
  2. 与交互式应用程序通信:容器的主进程本身就是一个等待用户输入的程序(例如一个自定义的 CLI 工具、一个 REPL 环境)。

什么时候避免使用 docker attach?

  1. 绝大多数调试和管理任务:这是 docker exec 的主场。你需要的是一个稳定的、不会意外停止容器的 Shell 环境。
  2. 生产环境调试:在生产环境中使用 attach 非常危险,一个误操作就可能导致服务中断。
  3. 容器主进程是非交互式的:例如,附加到一个 Nginx 或 MySQL 容器,你只会看到它们的访问日志或错误日志疯狂滚动,而你无法输入任何有意义的命令。按 Ctrl+C 会立刻终止服务。

docker attach 是一个强大但具有破坏性的工具。它提供了与容器主进程最直接的连接方式,但这份力量也带来了风险。

  • 它的本质是“连接”而非“执行”。
  • 牢记 Ctrl+C 的破坏性,并熟练掌握安全脱离序列 Ctrl+P + Ctrl+Q。
  • 对于 99% 的日常操作,docker exec -it 是更安全、更灵活的选择。我们应该优先使用它来进入容器 Shell。

再次强调,该指令在实践中请务必谨慎使用。

docker exec

在 Docker 的日常运维中,我们经常会遇到这样的场景:一个容器正在运行,但我们需要检查其配置文件、查看日志文件、安装调试工具,或者简单地看看容器内部的环境。由于容器是隔离的环境,我们无法像登录物理服务器一样直接进入。这时,docker exec 命令就如同一位万能锁匠,为我们打开了进入运行中容器的大门,是 Docker 运维中使用最频繁、最重要的诊断和调试工具。


docker exec(execute 的缩写)命令用于在正在运行的容器内部启动一个新的进程。

它与容器启动时运行的主进程(PID 1)并行运行,互不干扰。这意味着你可以安全地在容器内执行任何命令,而无需担心影响主应用程序的正常运行。这就像是在一台正在提供服务的服务器上,新开了一个 SSH 会话进行操作,完全不会影响正在运行的网站或数据库服务。

基本语法:

docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
  • CONTAINER: 目标容器的名称或 ID(通常取前几位即可)。
  • COMMAND: 要在容器内执行的命令及其参数,例如 /bin/bash, ls, ps, cat 等。

为什么需要它?核心价值

  1. 实时诊断与调试:当容器行为异常时(如服务不可用、报错),exec 是排查问题的第一选择。你可以直接进入容器查看日志、进程状态、网络连接等。
  2. 交互式探索:对于不熟悉的镜像,可以启动一个 Shell 来探索其文件系统结构、预装软件和环境变量,从而更好地理解它的行为。
  3. 执行管理任务:在不重新构建镜像的情况下,临时安装软件(如 curl, vim, net-tools)来进行测试或调试。
  4. 安全且非侵入:由于是在新进程中操作,你的调试行为不会影响主进程,极大地提升了操作的安全性。

核心选项(OPTIONS)详解

docker exec 的强大功能通过其选项来实现,其中最关键是 -i 和 -t。

选项 全称 作用 示例
-i --interactive 保持标准输入(STDIN)打开。允许你向容器内的进程发送命令。 docker exec -i ...
-t --tty 分配一个伪终端(pseudo-TTY)。它会格式化输出,提供完整的终端体验,支持命令补全、颜色显示等。 docker exec -t ...
-it 几乎总是组合使用。提供交互式的终端体验,是进入容器 Shell 的标准方式。 docker exec -it ...
-e --env 设置环境变量。这对于临时改变程序行为非常有用。 docker exec -e MY_VAR=value ...
-u --user 指定以哪个用户身份执行命令。可以是用户名或 UID。 docker exec -u root ...
docker exec -u 1000 ...
-w --workdir 设置命令在容器内的工作目录。 docker exec -w /app ...

实战示例:从入门到精通

假设我们有一个名为 my_web 的 Nginx 容器在运行。

1. 进入容器的交互式 Shell(最常用)
这是 docker exec 最经典的用法,让你获得一个容器内的终端。

# 使用 bash(如果镜像基于 Ubuntu、CentOS 等)
docker exec -it my_web /bin/bash

# 使用 sh(更通用,Alpine 等精简镜像也支持)
docker exec -it my_web /bin/sh

成功执行后,命令行提示符会变化,表示你已“进入”容器:

root@a1b2c3d4e5f6:/#

现在,你可以像操作普通 Linux 系统一样执行任何命令:

# 探索文件系统
ls -la /etc/nginx/
cat /etc/nginx/nginx.conf

# 查看进程
ps aux

# 安装软件(但不推荐持久化这样做)
apt update && apt install -y vim net-tools

# 检查网络
netstat -tulpn
curl localhost

退出时,只需输入 exit 或按 Ctrl+D。容器会继续正常运行。

2. 执行单条命令并获取结果
你不需要总是启动一个完整的 Shell 会话,可以直接执行命令并返回结果。

# 查看容器内的当前工作目录
docker exec my_web pwd

# 查看环境变量
docker exec my_web env

# 查看 Nginx 访问日志的最后10行
docker exec my_web tail -10 /var/log/nginx/access.log

# 测试配置文件语法是否正确(非常实用的调试命令)
docker exec my_web nginx -t

3. 以特定用户身份执行命令
为了安全,最佳实践是不以 root 身份运行应用。exec 允许你指定用户。

# 假设你的 Nginx 以 'www-data' 用户运行
# 以该用户身份查看它有权访问的文件
docker exec -it -u www-data my_web /bin/sh
whoami # 会显示 'www-data'

# 但如果需要调试,可以临时切换回 root
docker exec -it -u root my_web /bin/bash

4. 在特定目录下执行命令

# 直接列出容器内 /app 目录下的文件
docker exec -w /app my_web ls -la

最佳实践与注意事项

  1. 容器必须处于运行状态:docker exec 只能用于状态为 Up 的容器。如果容器已停止(Exited),你需要先使用 docker start 启动它。

  2. 选择正确的 Shell:基于 Alpine 的镜像非常流行,但它们不包含 bash,只包含 sh(通常是 ash)。因此,使用 /bin/sh 是更通用、更安全的选择。

  3. 修改是临时的:通过 exec 在容器内进行的任何修改(如安装软件、创建文件)都发生在容器的可写层中。一旦容器被删除并重新创建(这是常事),这些修改会全部丢失。持久化配置应通过 Docker 数据卷(Volumes) 或绑定挂载(Bind Mounts) 来实现。

  4. 区分 exec 与 attach:

    • docker exec:启动新进程,用于调试和管理,退出不会影响容器。
    • docker attach:连接主进程,直接与主进程交互,误操作可能导致容器停止。绝大多数情况下,应优先使用 exec。

docker exec -it <container> /bin/sh 是 Docker 运维的瑞士军刀,是每个开发者和运维人员必须熟练掌握的核心命令。

  • 它是你的诊断控制台,让你可以实时洞察容器内部状态。
  • 它是你的调试工具,帮助快速定位和解决应用问题。
  • 它是你的探索工具,用于学习和理解第三方镜像的构建方式。

docker restart

在容器化应用的日常运维中,重启是一个高频操作。无论是应用配置更新、依赖项变更,还是简单地应对一些难以定位的偶发性问题,我们常常需要重启容器。Docker 提供了 docker restart 命令来满足这一需求,但它背后的行为远比表面上的“重启”二字要复杂。理解它,能帮助你更安全、更优雅地管理你的容器。


docker restart 命令用于重启一个或多个正在运行或已停止的容器。

它的核心工作流程可以简单概括为:
docker stop + docker start

也就是说,restart 并不是让容器内的进程原地“热重启”,而是先执行一个停止流程,然后再重新启动容器。这使得它的行为非常明确和可预测。

基本语法:

docker restart [OPTIONS] CONTAINER [CONTAINER...]

你可以同时重启多个容器,只需将它们的名称或 ID 用空格隔开。

docker restart 的工作流程与生命周期

要深入理解 restart,我们需要跟踪一个容器在重启过程中的状态变化:

  1. 发起重启请求:

    docker restart my_container
  2. 停止阶段(docker stop):

    • Docker Daemon 会向容器内的主进程(PID 1) 发送 SIGTERM 信号。
    • 这是一个“优雅终止”的信号,通知应用程序:“你即将被关闭,请做好收尾工作”(如保存数据、关闭网络连接、释放资源等)。
    • 系统会等待一个“宽限期”(默认为 10 秒)。如果进程在此时限内自行退出,则停止流程完成。
  3. 强制终止(如果必要):

    • 如果 10 秒后容器进程仍然没有停止,Docker Daemon 会发送 SIGKILL 信号。
    • 这个信号无法被捕获或忽略,会立即强制终止进程。这是一种强制手段,可能会造成数据丢失或状态不一致。
  4. 启动阶段(docker start):

    • 停止完成后,Docker 会立即基于容器最初的配置(镜像、命令、网络、卷等)重新启动它。
    • 容器重新进入运行(Up)状态,其内部的文件系统也会回退到镜像的原始状态(除非有数据卷或绑定挂载)。

核心选项(OPTIONS)

docker restart 的选项相对简单,但非常实用:

选项 全称 作用 示例
-t --time 改变停止超时时间。这是最重要的选项。你可以指定一个不同于默认 10 秒的等待时间(单位:秒)。 docker restart -t 30 my_container
(给予应用 30 秒的时间进行优雅关闭)

实战示例与场景

1. 重启单个容器:

# 重启名为 'nginx' 的容器
docker restart nginx

# 重启指定ID的容器(取前几位即可)
docker restart a1b2c3d4

2. 重启多个容器:

# 同时重启整个应用栈的所有容器
docker restart nginx web_app database cache

3. 延长优雅停止时间:
对于数据库、有状态服务等需要较长时间进行收尾工作的应用,默认的 10 秒可能不够。

# 给予数据库容器60秒的时间来完成事务、刷新缓存和关闭连接
docker restart -t 60 mysql_db

最佳实践与注意事项

  1. 理解“无状态”与“有状态”:

    • 无状态服务(如 Web 服务器、API):docker restart 通常非常安全,是更新配置和应对问题的标准操作。
    • 有状态服务(如数据库、消息队列):需要格外小心。虽然 -t 选项可以增加优雅停止的时间,但重启仍然可能导致正在进行的客户端连接中断。对于生产环境的有状态服务,应有更完善的高可用和故障转移方案,而不是简单地重启。
  2. 数据持久化是前提:
    务必确保容器内需要保留的任何数据(如数据库文件、上传的内容、日志)都通过 Docker 数据卷(Volumes) 或绑定挂载(Bind Mounts) 存储在容器之外。否则,重启后容器内的所有更改都会丢失(因为文件系统会回退到镜像初始状态)。

  3. 与 docker stop + docker start 的区别:
    从结果上看,restart 等价于先后执行 stop 和 start。但使用单一命令更简洁,并且在一些编排工具中更容易管理。restart 的优势在于其原子性和便利性。

  4. 监控重启后的状态:
    重启后,务必检查容器是否真正成功启动并健康运行。

    # 查看容器状态
    docker ps -a | grep my_container
    
    # 查看容器启动日志,排查任何启动错误
    docker logs my_container
  5. 不要滥用重启:
    重启是解决“症状”的快速方法,但不应替代对“根因”的排查。如果某个容器需要频繁重启,你应该深入调查其背后的根本原因,如内存泄漏、配置错误或资源不足。

restart vs stop/start vs run --restart

  • docker restart:手动重启特定容器。
  • docker stop && docker start:手动将停止和启动操作分开执行,提供更精细的控制。
  • docker run --restart <policy>:自动重启策略。这是在容器退出后由 Docker Daemon 自动触发的行为(如总是重启、失败时重启等),与手动的 restart 命令有本质区别。

docker restart 是一个简单却强大的命令,是容器生命周期管理中的“重启按钮”。

  • 它的本质是“优雅停止”后“重新启动”,遵循标准的生命周期。
  • -t 选项允许你为重要应用定制优雅停止的时长,是安全重启的关键。
  • 它最适合用于无状态服务,对于有状态服务需谨慎评估影响。
  • 确保数据持久化是安全使用任何重启操作的前提。

掌握 docker restart 的正确使用场景和方法,能让你在保证服务可用性的同时,更加从容地进行应用管理和故障排除。

docker stop

在 Docker 的运维世界里,我们不仅需要知道如何让容器跑起来,更需要懂得如何让它们优雅地停下来。docker stop 便是完成这一使命的关键命令。它远非简单的“杀死”进程,而是一个遵循标准流程、旨在保护数据完整性的优雅终止(Graceful Shutdown) 操作。理解 docker stop,就是理解容器生命末期如何实现“善终”。


docker stop 命令用于停止一个或多个正在运行的容器。

其核心目标是在尽可能保证应用程序数据一致性的前提下,安全地终止容器。它通过向容器内进程发送特定的系统信号(Signal)来实现这一目标。

基本语法:

docker stop [OPTIONS] CONTAINER [CONTAINER...]

docker stop 的工作流程:一个两阶段的优雅过程

docker stop 的执行并非一蹴而就,而是一个包含等待和协商的过程:

  1. 阶段一:友好协商(SIGTERM)

    • 当您执行 docker stop my_container 时,Docker Daemon 会首先向容器内的主进程(PID 1) 发送一个 SIGTERM 信号。
    • SIGTERM 是一个“礼貌”的终止信号,它通知应用程序:“你即将被关闭,请做好收尾工作”。收到此信号后,一个设计良好的应用程序应当执行一系列清理操作,例如:
      • 停止接受新的连接请求。
      • 完成正在进行的任务或事务。
      • 将内存中的数据刷新到磁盘(如数据库提交事务、缓存持久化)。
      • 释放占用的资源(关闭文件描述符、网络连接等)。
      • 最终自行退出。
  2. 阶段二:强制终止(SIGKILL)

    • 系统不会无限期地等待。默认情况下,Docker 会给予进程 10 秒的“宽限期”来完成上述清理工作。
    • 如果 10 秒后容器进程仍然没有自行终止,Docker Daemon 便会失去耐心,发送 SIGKILL 信号。
    • SIGKILL 信号非常强大且粗暴,它无法被应用程序捕获或忽略,会立即从内核层面强制终止进程。这是一种保底手段,但可能会导致数据丢失或状态损坏。

核心选项:-t, –time

此选项是 docker stop 命令的灵魂,它允许您自定义第一阶段“友好协商”的超时时间。

  • 作用:改变默认的 10 秒等待时间。

  • 使用场景:对于数据库、消息队列等需要较长时间进行收尾工作的有状态服务,默认的 10 秒可能远远不够。使用 -t 可以给予它们充足的时间完成优雅关闭。

  • 示例:

    # 给予容器30秒的时间进行优雅关闭,30秒后才会强制终止
    docker stop -t 30 mysql_container

最佳实践与精要注意事项

  1. 区分“有状态”与“无状态”服务:

    • 无状态服务(Stateless):如 Web 服务器、API 微服务。它们通常可以快速关闭,对 docker stop 不敏感,使用默认超时即可。
    • 有状态服务(Stateful):如 MySQL、Redis、Kafka。它们是 docker stop 操作的重点关注对象。务必使用 -t 选项设置一个足够长的超时时间,以确保它们能完成数据持久化等关键操作。
  2. 应用程序的信号处理是前提:
    docker stop 的优雅与否,最终取决于容器内主进程是否正确地处理了 SIGTERM 信号。如果您的自定义应用程序会运行为主进程,请确保它实现了 SIGTERM 信号处理逻辑。一个对 SIGTERM 毫无反应的进程,最终都难逃被 SIGKILL 强制终结的命运。

  3. 数据持久化是安全停靠的港湾:
    再次强调,任何有价值的数据都不应只存在于容器的可写层中。必须通过 Docker 数据卷(Volumes) 或绑定挂载(Bind Mounts) 将数据存储在容器之外。这样,无论容器是优雅停止还是被强制杀死,您的数据都是安全的。

  4. 停止 vs 杀死:
    与 docker stop 的优雅形成鲜明对比的是 docker kill 命令。docker kill 默认发送 SIGKILL 信号(也可指定其他信号),实现的是强制立即终止,相当于直接拔掉电源。除非容器已完全无响应,否则应优先使用 docker stop。


docker stop 远不止是一个停止容器的指令,它体现了 Docker 设计中对应用生命周期的尊重。

  • 它不是杀手,而是信使:它先礼(SIGTERM)后兵(SIGKILL),致力于协商而非破坏。
  • -t 选项是其灵魂:通过自定义超时时间,您可以将优雅终止的控制权掌握在自己手中,尤其对于有状态服务至关重要。
  • 优雅是双向的:Docker 提供了优雅停止的机制,但最终效果需要容器内的应用程序协同配合。

docker kill

在 Docker 的管理工具箱中,如果 docker stop 是遵循流程、彬彬有礼的“交涉官”,那么 docker kill 就是果断坚决、毫无余地的“行刑队”。它不进行协商,不给予宽限期,它的任务只有一个:立即终止容器。理解何时以及如何使用这个“最终手段”,是应对紧急情况的关键。


docker kill 命令用于向一个或多个容器的主进程发送一个特定的系统信号(Signal),默认情况下,这个信号是 SIGKILL。

它的核心特点是强制性和即时性。它绕过了 docker stop 的优雅终止流程,旨在以最快的方式让容器停止运行。

基本语法:

docker kill [OPTIONS] CONTAINER [CONTAINER...]

docker kill 的核心机制:信号的力量

docker kill 的威力源于 Linux 的系统信号机制。与 docker stop 固定先 SIGTERM 再 SIGKILL 的流程不同,docker kill 允许你直接指定发送任何信号,赋予了操作者极大的灵活性和控制力。

最关键的选项:-s (–signal)
这是 docker kill 的灵魂所在,它允许你指定要发送的信号。

信号 值 作用 使用示例
SIGKILL 9 强制终止。无法被捕获、阻塞或忽略,进程会立即被操作系统内核终止。这是默认信号。 docker kill my_container
SIGTERM 15 优雅终止。通知进程终止,允许其进行清理工作。 docker kill -s SIGTERM my_container
SIGHUP 1 挂起。通常用于通知守护进程重新加载其配置。 docker kill -s SIGHUP nginx
其他信号 如 SIGINT (2), SIGUSR1 (10) 等,可用于与进程进行特定交互。 docker kill -s SIGUSR1 my_app

工作流程:

  1. 你执行 docker kill [OPTIONS] CONTAINER。
  2. Docker Daemon 直接向指定容器的主进程发送你通过 -s 指定的信号(默认为 SIGKILL)。
  3. 进程根据收到的信号做出反应(对于 SIGKILL,就是立即被终结)。
  4. 容器状态变为 Exited。

为什么需要它?应用场景

docker kill 的存在不是为了替代 docker stop,而是为了处理 docker stop 无法有效解决的极端情况。它的应用场景非常明确:

  1. 容器完全无响应(死机):这是最经典的场景。当容器内的主进程陷入死循环、死锁或因为其他原因完全卡住,不再响应任何请求(包括 SIGTERM 信号)时,docker stop 会在等待超时后失败。此时,docker kill -s SIGKILL 是唯一能强制结束它的方法。

  2. 立即释放关键资源:当某个失控的容器正在疯狂消耗主机资源(如 CPU、内存、磁盘 I/O),导致系统即将崩溃时,你没有时间等待 10 秒的优雅退出。必须使用 docker kill 立即“拔掉电源”,以保护主机和其他容器。

  3. 与进程进行特定通信:通过 -s 选项,它可以成为一个高级管理工具。例如,通知 Nginx 重新加载配置而不重启进程:

    # 向nginx容器发送SIGHUP信号,使其重新加载配置文件
    docker kill -s SIGHUP nginx

    在这种情况下,它非但不会终止容器,反而是一种“管理”指令。

最佳实践与严厉警告

⚠️ 警告:核武器选项

默认的 SIGKILL 是容器世界里的“核武器”。使用时必须清楚其后果:

  • 数据丢失风险极高:进程没有机会执行任何清理操作。正在进行的写操作会中断,内存中的数据会丢失。绝对不要将其作为停止数据库或有状态服务的常规手段。
  • 可能导致状态不一致:强制终止可能会使应用程序留下残缺的临时文件、孤立的锁文件或处于中间状态的数据,为下次启动埋下隐患。

最佳实践:

  1. 始终优先使用 docker stop:将其作为停止容器的默认和首选命令。只有在 docker stop 失效或情况万分紧急时,才诉诸于 docker kill。

  2. 明确的升级流程:建立自己的操作流程:docker stop -> (等待) -> 如果超时或无响应) -> docker kill。

  3. 尝试“软”杀死:在发送最终的 SIGKILL 之前,可以尝试先手动发送 SIGTERM,给它最后一次机会:

    docker kill -s SIGTERM my_stuck_container
    # 等待几秒...
    docker kill my_stuck_container # 默认发送SIGKILL
  4. 记录与复盘:每次使用 docker kill 后,都应记录原因并复盘。一个需要频繁被强制杀死的容器,其本身一定存在需要修复的缺陷(如内存泄漏、死锁 bug)。


docker kill 是一个强大但危险的命令,是 Docker 管理员武器库中的“最后手段”。

  • 它的核心是直接发送信号,默认是立即终止的 SIGKILL。
  • 它的设计目的是处理故障和紧急情况,而非日常操作。
  • -s 选项赋予了它灵活性,使其不仅能强制终止,还能用于特定的进程管理。
  • 最大的风险是数据丢失,使用时必须心怀敬畏。

docker top

当我们运行一个 Docker 容器时,它就像是一个黑盒——我们知道它在运行,但很难直观地看到里面究竟发生了什么。docker top 命令正是为了打破这种信息壁垒而生的。它就像一台X 光机,能够让我们无需进入容器内部,就能清晰地洞察其内部运行的进程信息,是容器监控和故障排查中一个轻量级却极其实用的工具。


docker top 命令用于显示一个运行中容器内部的进程信息。

它的功能类似于在宿主机上执行 ps(process status)命令,但它的查看对象是容器内部的进程树。这个命令执行速度极快,因为它直接从 Docker 守护进程获取信息,而无需通过容器的 Shell 环境。

基本语法:

docker top CONTAINER [ps OPTIONS]
  • CONTAINER: 目标容器的名称或 ID。
  • [ps OPTIONS]: (可选)任何标准的 ps 命令选项,用于格式化输出内容。

为什么需要它?核心价值

  1. 快速诊断与排查:当容器行为异常(如 CPU/内存占用过高)时,docker top 是第一步的排查工具。你可以立即看到是容器内的哪个(或哪些)进程在消耗资源,而无需先 exec 进入容器。
  2. 验证容器内容:确认容器内运行的主进程是否符合预期。例如,你运行了一个 Nginx 镜像,可以用 docker top 来验证 Nginx 主进程及其 worker 进程是否都已启动。
  3. 获取进程 PID:容器内进程的 PID 在宿主机上有其对应的 PID。docker top 可以显示这些映射关系,这对于一些高级调试和监控场景非常有用。
  4. 轻量级且非侵入:它不需要在容器内安装任何额外的工具(如 htop, ps 本身),几乎所有容器都可以直接使用,对容器本身零影响。

实战示例:从基础到高级

假设我们有一个名为 my_web 的 Nginx 容器在运行。

1. 基础用法:查看容器内进程
最基本的命令,显示容器内所有进程的基本信息,默认输出格式与 ps 相同。

docker top my_web

输出示例:

UID    PID    PPID   C   STIME   TTY   TIME        CMD
root   12345  12316  0   10:00   ?     00:00:00   nginx: master process nginx -g daemon off;
systemd+ 12367 12345  0   10:00   ?     00:00:00   nginx: worker process
systemd+ 12368 12345  0   10:00   ?     00:00:00   nginx: worker process

从输出可以清晰地看到:

  • 主进程 (PID 12345):nginx: master process。
  • 子进程 (PPID 12345):两个 nginx: worker process,它们的父进程 ID (PPID) 指向主进程的 PID。

2. 高级用法:自定义输出格式
通过传递标准的 ps 选项,可以获取更详细或更具体的信息。这是 docker top 最强大的地方。

  • 显示完整的命令及其参数:

    docker top my_web -e
    # 或者
    docker top my_web -args
  • 查看进程的 UID 和用户名:

    docker top my_web -e -o uid,user,pid,cmd
  • 以森林模式显示进程层级关系:

    docker top my_web -e -o pid,ppid,user,cmd --forest

    输出示例:

    PID    PPID   USER    COMMAND
    12345  12316  root    nginx: master process nginx -g daemon off;
    12367  12345  systemd+  \_ nginx: worker process
    12368  12345  systemd+  \_ nginx: worker process

    这种方式可以非常直观地看到进程的父子关系。

  • 查看进程资源占用(CPU、内存):

    docker top my_web -o pid,user,%cpu,%mem,cmd --sort -%cpu

    此命令按 CPU 使用率降序排列,快速定位最耗资源的进程。

最佳实践与注意事项

  1. 容器必须处于运行状态:docker top 只能用于状态为 Up 的容器。对于已停止的容器,该命令无法工作。
  2. 理解 PID 映射:docker top 显示的第一个 PID 列是进程在宿主机命名空间中的真实 PID,而不是在容器内部的 PID。这对于在宿主机上使用 strace, gdb 等工具调试容器进程至关重要。
  3. 功能的局限性:docker top 是一个诊断工具,而非管理工具。你可以用它来查看进程,但不能用它来直接管理(如杀死、改变优先级)进程。要管理进程,你需要使用 docker exec 在容器内执行命令,或使用宿主机的 kill 命令配合从 docker top 获取的宿主 PID。
  4. 结合其他命令使用:docker top 通常与 docker stats(查看整体资源使用)和 docker logs(查看进程输出)结合使用,形成完整的排查链条:
    • docker stats 发现某个容器 CPU 高 ->
    • docker top 该容器找到具体的异常进程 ->
    • docker exec 进入容器或 docker logs 查看该进程的日志进行深度排查。

docker top 是一个简单却极其强大的原生调试命令,是每一位 Docker 使用者都应该掌握的“显微镜”。

  • 它是洞察容器内部进程状态的窗口,提供了无需侵入即可观察的能力。
  • 它通过支持原生 ps 选项,提供了强大的信息过滤和格式化功能。
  • 它是性能排查和故障诊断的起点,能快速将问题定位到具体进程。
  • 它揭示了容器与宿主机之间的进程映射关系,为高级调试铺平了道路。

将其加入你的日常运维工具箱,你会发现排查容器问题的效率得到了显著的提升。

docker stat

当我们在一台主机上运行多个 Docker 容器时,一个核心的运维问题是:我的系统资源(CPU、内存、网络…)都被谁吃掉了?哪个容器是“资源大户”? 靠手动登录每个容器查看显然不现实。这时,docker stats 命令就如同一个统一的实时资源仪表盘,让你能够一目了然地监控所有容器的性能指标,是保障容器化环境健康运行的必备工具。


docker stats 命令用于实时显示一个或多个容器的资源使用情况统计信息。

它会动态刷新一个表格,其中包含了每个容器在 CPU、内存、网络 I/O 和磁盘 I/O 等方面的关键性能指标。这些数据直接来源于 Linux 内核的控制组(cgroups),因此非常准确和高效。

基本语法:

docker stats [OPTIONS] [CONTAINER...]

如果不指定任何容器,默认会显示所有运行中(Up)容器的统计信息。

为什么需要它?核心价值

  1. 实时性能监控:提供容器级别的实时资源消耗视图,帮助快速发现异常(如内存泄漏、CPU 爆满)。
  2. 资源瓶颈定位:当主机出现高负载时,可以快速定位是哪个(或哪些)容器导致的,从而进行针对性处理。
  3. 容量规划与优化:通过观察常态下的资源使用情况,可以为容器设置更合理的资源限制(-m, --cpus),避免资源浪费或竞争。
  4. 零成本、零侵入:该命令是 Docker CLI 自带的,无需在容器内安装任何代理或监控软件,对容器本身毫无影响。

解读监控面板:每一列的含义

执行 docker stats 后,你会看到一个类似这样的动态更新的表格:

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O        PIDS
a1b2c3d4e5f6   redis     0.12%     45.21MiB / 1.5GiB     2.94%     1.45kB / 0B      0B / 0B          4
f6e5d4c3b2a1   nginx     0.05%     12.5MiB /  unlimited    -        25.6kB / 120kB   0B / 0B          3
c3b2a1f6e5d4   web-app   125.7%    512.4MiB / 512MiB     100.0%    450kB / 1.2MB    12.3MB / 0B      27

每一列都代表一个关键指标:

  • CONTAINER ID & NAME: 容器的标识符和名称。
  • CPU %: CPU 使用率百分比。显示容器正在使用的宿主 CPU 总时间的百分比。如果有多核心 CPU,这个值可以超过 100%。例如,125% 表示容器使用了 1.25 个 CPU 核心的计算能力。
  • MEM USAGE / LIMIT: 内存使用量 / 内存限制。这是两个最关键的数字之一,直观显示了“用量”和“上限”。
  • MEM %: 内存使用率百分比。即 MEM USAGE / LIMIT * 100%。如果未设置内存限制(unlimited),此列会显示为 -。
  • NET I/O: 网络输入/输出流量。显示容器自启动以来累计接收和发送的数据量。这是排查网络流量异常的重要依据。
  • BLOCK I/O: 块设备输入/输出量。显示容器读写磁盘的数据量。对于数据库等磁盘密集型应用,这个指标非常重要。
  • PIDS: 进程数量。显示容器内当前存在的进程和线程数。一个异常的激增可能预示着某些问题。

实战示例:从概览到聚焦

1. 监控所有容器(全局概览)
最基本的命令,给你一个主机上所有运行容器的资源大盘。

docker stats

按 Ctrl+C 退出监控。

2. 监控特定容器(精准聚焦)
如果你只关心某几个容器,可以在命令后指定它们的名称或 ID。

# 监控指定的容器
docker stats nginx redis

# 使用容器ID(取前几位即可)
docker stats a1b2c3 f6e5d4

3. 使用选项格式化输出
docker stats 提供了一些有用的选项来定制输出。

  • --no-stream:只输出一次当前状态,然后退出。适用于编写脚本或快速获取某个时间点的快照,而不是持续监控。

    docker stats --no-stream
  • --format:按照 Go 模板自定义输出格式。这是高级用法,可以让你只显示关心的列,或者用于自动化脚本。

    # 只显示容器名、CPU百分比和内存使用量
    docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
    
    # 以纯JSON格式输出一次(非常适合由其他程序解析)
    docker stats --no-stream --format "{{json .}}"

最佳实践与注意事项

  1. 它只是一个实时工具:docker stats 显示的是实时数据流,而非历史数据。它不会保存任何历史记录。对于长期趋势分析、告警和数据持久化,你需要更专业的监控方案,如 Prometheus + Grafana(通常通过 cAdvisor 来收集 Docker 指标)。

  2. 结合 docker top 进行深度排查:docker stats 告诉你哪个容器出了问题(如 CPU 100%),而 docker top 可以进一步告诉你该容器内是哪个进程导致了问题。这两个命令是黄金搭档。

  3. 理解“ unlimited”:如果运行容器时未使用 -m 或 --memory-swap 设置内存限制,MEM LIMIT 会显示为 unlimited,MEM % 会显示为 -。在生产环境中,强烈建议为所有容器设置内存限制,以防止某个容器耗尽整个主机内存导致系统崩溃(OOM)。

  4. 性能影响极小:由于数据直接来自内核的 cgroups,运行 docker stats 的开销非常小,可以放心使用。


docker stats 是 Docker 原生提供的、最简单高效的实时监控工具。

  • 它是容器资源的实时仪表盘,让你对系统状态了如指掌。
  • 它是故障排查的起点,能快速将性能问题定位到具体容器。
  • 它简单易用且零成本,是每个 Docker 用户都应该掌握的第一个监控命令。
  • 对于更高级的需求,它是指引你走向更强大监控系统(如 Prometheus)的敲门砖。

无论是日常运维还是应急排查,熟练使用 docker stats 都能让你更加从容地管理和维护你的容器化应用。

docker container inspect

在 Docker 的生态中,我们经常需要了解一个容器的详细信息:它的配置是什么?它的网络是如何设置的?它挂载了哪些数据卷?虽然我们可以通过 docker ps 看到容器的概览,但要获取其全部底层细节,就需要一个更强大的工具——docker container inspect。这个命令就像是为容器生成的一份全方位的“体检报告”,揭示了从创建到运行的每一个技术细节。


docker container inspect 命令用于获取 Docker 容器底层配置和运行时状态的详细信息(元数据)。

它会返回一个庞大的 JSON 对象,这个对象包含了容器生命周期的几乎所有信息,从最初的创建配置(docker create/docker run 时指定的所有参数)到当前的运行时状态(如 IP 地址、端口映射、进程 ID 等)。

基本语法:

docker container inspect [OPTIONS] CONTAINER [CONTAINER...]

你也可以使用它的传统缩写形式:

docker inspect CONTAINER

两者功能完全一致。

为什么需要它?核心价值

  1. 故障排查与调试:当容器行为异常时,inspect 是查明根本原因的第一站。你可以检查网络配置、卷挂载、环境变量等是否正确。
  2. 发现连接信息:快速查找容器的 IP 地址、网关以及端口映射关系,这对于容器间的网络通信或从外部访问服务至关重要。
  3. 审计与验证:验证容器的运行配置是否与预期一致,例如资源限制、重启策略、安全选项等。
  4. 信息提取用于自动化:其结构化(JSON)的输出格式非常适合被脚本或其他工具(如 jq)解析,以实现自动化运维。
  5. 学习与理解:通过查看容器元数据,可以更深入地理解 Docker 是如何构建和管理容器环境的。

解读“体检报告”:JSON 输出中的关键信息

docker container inspect 的输出是一个深度嵌套的 JSON 对象,主要包含两大块:容器配置(Config) 和容器状态(State)。

执行以下命令查看原始格式:

docker container inspect my_container

输出内容非常丰富,以下是一些最常用和关键的字段:

1. 网络设置(NetworkSettings):

"NetworkSettings": {
  "Networks": {
    "bridge": { // 网络模式,可能是 bridge, host, 或自定义网络名
      "IPAddress": "172.17.0.2", // **容器的IP地址**
      "Gateway": "172.17.0.1",
      "MacAddress": "02:42:ac:11:00:02"
    }
  },
  "Ports": { // **端口映射**
    "80/tcp": [
      {
        "HostIp": "0.0.0.0",
        "HostPort": "8080" // **宿主机的映射端口**
      }
    ]
  }
}
  • 用途:查找容器 IP 以进行连接测试,确认端口映射是否正确。

2. 挂载信息(Mounts):

"Mounts": [
  {
    "Type": "volume", // 类型可以是 volume(数据卷), bind(绑定挂载), tmpfs
    "Name": "my_volume", // 卷名
    "Source": "/var/lib/docker/volumes/my_volume/_data", // **在宿主机上的源路径**
    "Destination": "/app/data", // **在容器内的目标路径**
    "Mode": "z",
    "RW": true // 读写权限
  }
]
  • 用途:确认数据卷和绑定挂载是否正确配置,并找到数据在宿主机上的实际存储位置。

3. 配置信息(Config):

"Config": {
  "Image": "nginx:latest", // 使用的镜像
  "Env": [ // **环境变量**
    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
    "NGINX_VERSION=1.25.3"
  ],
  "Cmd": ["nginx", "-g", "daemon off;"], // 启动命令
  "Labels": { // 标签
    "maintainer": "NGINX Docker Maintainers"
  }
}
  • 用途:检查容器运行时的核心配置,如启动命令、环境变量等。

4. 状态信息(State):

"State": {
  "Status": "running", // 状态:running, paused, exited, restarting
  "Running": true,
  "Paused": false,
  "Restarting": false,
  "Pid": 1234, // **容器主进程在宿主机上的PID**
  "StartedAt": "2024-08-31T10:00:00.123456789Z",
  "FinishedAt": "0001-01-01T00:00:00Z"
}
  • 用途:确认容器的精确状态和运行时长,获取 PID 用于高级调试。

实战示例:使用格式化和过滤

原始 JSON 输出信息量巨大,我们通常需要借助选项来提取特定信息。

1. 使用 --format 或 -f 提取特定字段(Go 模板)
这是最强大的功能,可以精准获取你需要的值。

# 获取容器的IP地址
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my_container

# 获取容器状态
docker inspect --format='{{.State.Status}}' my_container

# 获取容器使用的镜像
docker inspect --format='{{.Config.Image}}' my_container

# 获取宿主机映射的端口(例如映射到容器80端口的主机端口)
docker inspect --format='{{(index (index .NetworkSettings.Ports "80/tcp") 0).HostPort}}' my_container

2. 使用 jq 工具解析输出(更灵活)
jq 是一个强大的命令行 JSON 处理器,与 docker inspect 是绝配。

# 首先确保系统安装了 jq: apt-get install jq

# 获取所有挂载点的源和目标
docker inspect my_container | jq '.[].Mounts[] | .Source, .Destination'

# 以更漂亮的格式输出整个JSON,便于阅读
docker inspect my_container | jq .

3. 一次检查多个容器

docker container inspect nginx redis

最佳实践与注意事项

  1. 适用于所有状态的容器:与许多命令不同,docker container inspect 不仅可以用于运行中的容器,也可以用于已停止(exited)的容器。这对于排查为什么容器启动失败非常有用。
  2. 信息是只读的:该命令仅用于查看信息,无法用于修改任何容器配置。要修改配置,需要重新创建容器。
  3. 掌握 --format 和 jq:学习基础的 Go 模板语法和 jq 的使用,能让你从信息海洋中高效地捞出所需的数据,这是进阶玩家的必备技能。
  4. 理解输出结构:花些时间浏览一次完整的输出,熟悉其主要结构(Config, State, NetworkSettings, Mounts),以后排查问题时就能快速定位。

docker container inspect 是 Docker 工具箱中最强大的诊断命令之一,是探索容器内部机制的“瑞士军刀”。

  • 它是容器元数据的终极来源,提供了无与伦比的细节深度。
  • 它是连接抽象概念和具体实现的桥梁,让你真正“看透”容器。
  • 结合 --format 和 jq,它从一個诊断工具转变为自动化脚本的核心组件。
  • 无论是开发、调试还是运维,它都是不可或缺的得力助手。

docker port

在 Docker 网络模型中,容器拥有自己独立的网络命名空间,这意味着容器内部服务的监听端口(如 Nginx 的 80 端口)默认在外部是无法直接访问的。为了解决这个问题,我们使用 -p 或 -P 参数来创建端口映射。但容器一旦运行起来,我们如何快速确认容器内的端口到底映射到了宿主机的哪个端口上?docker port 命令就是为了回答这个简单而关键的问题而生的,它是一个轻巧、快速的专用查询工具。


docker port 命令用于快速列出容器端口到宿主机端口的映射关系。

它直接查询 Docker 守护进程中记录的端口绑定信息,并以最清晰的方式呈现出来,让你无需从冗长的 docker inspect 输出中费力地寻找答案。

基本语法:

docker port CONTAINER [PRIVATE_PORT[/PROTO]]
  • CONTAINER: 目标容器的名称或 ID。
  • [PRIVATE_PORT[/PROTO]]: (可选)指定要查询的容器内部端口和协议(如 80/tcp),用于过滤结果。

为什么需要它?核心价值

  1. 快速验证与连接:当你想从宿主机或其他机器连接到一个容器内的服务时,需要知道具体映射到了哪个端口。docker port 提供了最直接的查询方式。
  2. 简化运维操作:相比使用 docker inspect 并手动解析庞大的 JSON 输出,docker port 的命令和输出都极其简单,非常适合在脚本或快速运维时使用。
  3. 避免记忆和猜测在复杂环境中,可能同时运行着多个容器,每个容器都有多个端口映射。此命令可以准确告诉你当前的映射状态,避免因记错端口而导致的连接失败。

实战示例:从查全部到精准查询

假设我们运行了一个 Nginx 容器,并将容器的 80 端口映射到了宿主机的 8080 端口,同时将容器的 443 端口映射到了宿主机的 8443 端口。

docker run -d --name my_nginx -p 8080:80 -p 8443:443 nginx

1. 查询容器的所有端口映射
这是最常用的方式,列出该容器配置的所有端口映射规则。

docker port my_nginx

输出示例:

80/tcp -> 0.0.0.0:8080
443/tcp -> 0.0.0.0:8443

解读输出:

  • 80/tcp:容器内部监听的端口和协议(TCP)。
  • ->:表示映射关系。
  • 0.0.0.0:8080:宿主机上绑定的 IP 地址和端口。0.0.0.0 表示绑定在宿主机的所有网络接口上,可通过宿主机的任何一个 IP 地址加 8080 端口来访问。

2. 查询容器的特定端口映射
如果你只关心某个特定端口(例如只想知道 80 端口映射到了哪里),可以使用可选参数进行过滤。

# 查询容器内部80端口的映射情况
docker port my_nginx 80
# 或更精确地指定协议(虽然TCP是默认值)
docker port my_nginx 80/tcp

输出示例:

0.0.0.0:8080

这次输出更加简洁,只返回了宿主机端的绑定信息。

工作原理与局限性

工作原理:
docker port 命令本质上是查询 Docker 守护进程维护的 iptables 规则或内部状态表,这些规则是在容器启动时通过 -p 或 -P 参数设置的。它返回的是配置的映射关系,而不是实时检测端口是否真正处于监听状态。

局限性:

  1. 仅显示显式映射的端口:它只显示通过 -p 或 -P 参数设置的端口映射。对于使用 --network=host 模式(主机网络模式)的容器,由于容器直接使用宿主机的网络命名空间,没有端口映射的概念,因此此命令不会返回任何信息。

  2. 不检测端口状态:它只告诉你“根据配置,端口应该映射到哪里”,但并不会检查宿主机的 8080 端口是否真的处于监听状态。要验证端口是否可访问,还需要配合 netstat、ss 或 telnet 等命令。

    # 使用 docker port 找到映射端口,再用 telnet 测试连通性
    docker port my_nginx 80
    telnet localhost 8080

最佳实践与应用场景

  1. 快速诊断连接问题:当无法通过宿主机 IP 和端口访问容器服务时,第一步就应用 docker port 确认映射关系是否正确建立。

  2. 自动化脚本:在 CI/CD 流水线或运维脚本中,如果需要获取容器的映射端口以便进行健康检查或集成测试,docker port 的简洁输出非常适合被其他命令直接使用。

    # 在脚本中获取映射端口并赋值给变量
    MAPPED_PORT=$(docker port my_nginx 80 | cut -d: -f2)
    echo "The application is accessible on port $MAPPED_PORT."
  3. 与 docker ps 互补:docker ps 也会显示端口映射,但格式是浓缩的(如 0.0.0.0:8080->80/tcp)。当 docker ps 的输出因信息过多而不易阅读时,使用 docker port 查看特定容器的映射会更加清晰。


docker port 是一个“小而美”的典范,它专注于解决一个非常具体的问题:

  • 它是端口映射的专用查询工具,功能单一但极其高效。
  • 它的输出简洁明了,无需像解析 docker inspect 那样需要处理复杂的 JSON。
  • 它是网络调试的第一步,帮助快速验证基础的网络配置是否正确。
  • 它完美体现了 Unix 哲学——“做好一件事”,并与其他命令(如 inspect, ps, netstat)组合使用,形成完整的故障排查链条。

docker cp

Docker 容器以其隔离性而闻名,但这种隔离有时也会带来不便:我们如何将宿主机的配置文件传入容器?又如何将容器内应用程序生成的日志或数据文件提取出来进行分析?docker cp(copy 的缩写)命令正是为了打破这种隔离而设计的。它就像一座安全的文件桥梁,允许在宿主机和容器之间双向复制文件和目录,是开发、调试和运维中不可或缺的实用工具。


docker cp 命令用于在宿主机文件系统和一个运行的或已停止的容器文件系统之间复制文件或目录。

它实现了容器内外环境的文件交换,支持双向操作,并且对容器内的进程毫无影响。

基本语法:

docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH
  • CONTAINER:目标容器的名称或 ID。
  • SRC_PATH:源文件或目录的路径。
  • DEST_PATH:目标文件或目录的路径。
  • [OPTIONS]:可选参数,目前主要支持 -a(归档模式,保留文件属性)和 -L(跟随符号链接)。

为什么需要它?核心价值

  1. 快速注入配置:将宿主机上修改好的配置文件(如 Nginx 的 nginx.conf)快速复制到容器中生效,无需重新构建镜像。
  2. 提取日志和数据:将容器内应用生成的日志文件、临时数据或崩溃报告复制到宿主机上进行深入分析。
  3. 动态调试与支持:在调试时,向容器内注入调试脚本或工具,或者从容器中提取状态信息。
  4. 备份与恢复:快速备份容器内用户生成的重要数据,或者将备份数据恢复到另一个容器中。

实战示例:从宿主机到容器,以及反向操作

1. 将文件从宿主机复制到容器内(Host -> Container)
这是最常见的操作,例如传入一个配置文件。

# 将宿主机当前目录下的 app.conf 文件复制到容器内 /etc/ 目录下
docker cp ./app.conf my_container:/etc/

# 将宿主机目录 /host/logs 整个复制到容器内的 /tmp 目录下
docker cp -a /host/logs my_container:/tmp/
  • 注意:如果容器内的目标路径是一个目录,文件会被复制到该目录下。如果目标路径不存在,docker cp 会尝试创建它。

2. 将文件从容器内复制到宿主机(Container -> Host)
同样重要,常用于提取数据。

# 将容器内 /var/log/nginx/error.log 文件复制到宿主机当前目录
docker cp my_container:/var/log/nginx/error.log ./

# 将容器内 /app/data 整个目录复制到宿主机的 /backup 目录下
docker cp -a my_container:/app/data /backup/
  • 注意:从容器内复制时,源路径必须写全路径,并以 容器名或ID: 开头。

3. 复制已停止的容器中的文件
docker cp 的一个巨大优势是它不仅适用于运行中的容器,也适用于已停止(exited)的容器。只要容器没有被删除,你仍然可以访问其文件系统。

# 一个已停止的容器,其ID为 stopped_container
docker cp stopped_container:/app/config.json ./recovered_config.json

核心选项(OPTIONS)详解

  • -a (–archive):归档模式。这是最常用的选项,它在复制时会保留文件的所有元信息,包括:

    • 用户和组所有权(UID/GID)
    • 时间戳(修改时间、访问时间)
    • 权限模式(读、写、执行)
    • 特殊标志(如 setuid)
    • 使用 -a 选项可以确保复制的文件与原始文件属性完全一致。
  • -L (–follow-link):跟随符号链接。默认情况下,docker cp 会复制符号链接本身(一个指向其他文件的快捷方式)。使用此选项后,它会复制符号链接所指向的实际文件内容。

最佳实践与重要注意事项

  1. 它不是持久化的替代品:docker cp 是用于临时文件传输的。通过它复制到容器内的文件仅存在于该容器的可写层中。如果容器被删除并重新创建(基于原镜像),这些文件会丢失。持久化数据必须通过 Docker 数据卷(Volumes)或绑定挂载(Bind Mounts)来实现。

  2. 对运行中应用的影响:向一个运行中的容器复制文件(尤其是覆盖正在被进程打开的文件)可能会导致不可预知的行为。对于配置文件,更安全的做法是复制进去后,再在容器内发送信号让应用重载配置(如 nginx -s reload),或者重启容器。

  3. 路径中的空格和特殊字符:如果文件或目录的路径中包含空格或特殊字符,务必使用引号将其括起来。

    docker cp "my_container:/path/with spaces/file.txt" ./
  4. 与 docker exec 和重定向的结合使用:有时,直接生成文件内容到容器内更方便。

    # 不推荐:先在本机创建文件,再复制
    echo "config_value=123" > temp.conf
    docker cp temp.conf my_container:/app/config
    
    # 推荐:使用 docker exec 和重定向直接写入(更高效)
    docker exec my_container sh -c 'echo "config_value=123" > /app/config'
  5. 性能考虑:复制大量小文件时,打包成一个归档文件(如 .tar)再进行复制,效率会远高于直接复制目录。

    # 在宿主机上打包
    tar -czf logs.tar.gz /path/to/many/files/
    docker cp logs.tar.gz my_container:/tmp/
    # 在容器内解压
    docker exec my_container tar -xzf /tmp/logs.tar.gz -C /target/dir

docker cp 是一个简单而强大的“救火队员”和“调试助手”。

  • 它是容器与宿主机间临时的文件交换通道,打破了隔离的壁垒。
  • 它支持双向操作,既可用于注入,也可用于提取。
  • 它适用于运行中和已停止的容器,提供了极大的灵活性。
  • -a 选项是保障文件元信息正确的关键。
  • 切记它并非持久化方案,对于重要数据,应始终使用数据卷。

docker diff

Docker 镜像的核心优势之一是其不可变性(Immutable)。我们基于一个纯净的镜像运行容器,但容器在运行过程中,应用程序会产生日志、用户会上传文件、临时数据会被创建。一个关键问题随之而来:自从容器启动后,它的文件系统相对于最初的镜像,发生了哪些变化? docker diff 命令正是为了回答这个问题而设计的。它就像一台文件系统的时光机,可以清晰地展示出容器读写层(Container Layer)的所有变更,是审计、调试和理解容器行为的强大工具。


docker diff 命令用于检查一个容器相对于其基础镜像,在文件系统层面所做的更改。

这些更改包括添加、修改和删除的文件和目录,它们都被记录在容器的可写层(Container Layer)中。该命令会扫描这些变更,并以列表形式呈现出来。

基本语法:

docker diff CONTAINER
  • CONTAINER:目标容器的名称或 ID。

为什么需要它?核心价值

  1. 调试与故障排查:当容器行为异常时,快速检查是否有关键配置文件被意外修改或删除,或者是否有异常文件被创建(如病毒、入侵痕迹)。
  2. 审计与安全分析:验证容器内的文件变更是否符合预期。例如,一个只读的应用容器不应该在系统目录创建文件。
  3. 逆向工程与学习:对于不熟悉的第三方镜像,运行后通过 docker diff 可以了解它启动了哪些服务、修改了哪些配置、在哪些路径写入了数据,从而更好地理解其行为。
  4. 为提交镜像做准备:在手动调试容器并准备使用 docker commit 将其保存为新镜像之前,先用 docker diff 审查一下变更内容,避免将临时文件、日志或敏感信息意外提交到新镜像中。

解读输出:理解变更类型

docker diff 的命令输出非常简洁,每一行代表一个文件的变更,由一个表示变更类型的字母开头,后接文件或目录的路径。

变更类型标识符:

标识符 含义 说明
A Add 新添加的文件或目录。
C Change 已存在文件的内容被修改。
D Delete 已存在文件被删除。

实战示例输出:
假设我们运行一个 Ubuntu 容器,并做一些操作:

  1. 安装 nginx 软件包(会添加大量文件)
  2. 修改 /etc/hosts 文件
  3. 删除 /etc/issue 文件

执行 docker diff <container_id> 后,你可能会看到如下输出:

C /etc
C /etc/hosts
D /etc/issue.net
A /var
A /var/lib
A /var/lib/nginx
A /var/lib/nginx/body
A /var/lib/nginx/fastcgi
A /var/lib/nginx/proxy
A /var/lib/nginx/uwsgi
A /var/lib/nginx/scgi
A /run
A /run/nginx.pid
C /var/lib/dpkg/status
A /usr
A /usr/sbin
A /usr/sbin/nginx
... (很长的一系列添加项)

解读:

  • C /etc/hosts:我们修改了 hosts 文件。
  • D /etc/issue.net:我们删除了 issue.net 文件。
  • 所有以 A 开头的行(如 /usr/sbin/nginx)都是安装 Nginx 时添加的新文件。

实战示例与应用场景

场景 1:快速排查“容器为什么不起作用?”
一个原本运行良好的容器突然无法启动。你可以运行它的最新版本,并使用 docker diff 检查它与之前正常版本的文件差异,也许会发现某个关键的配置文件(C)被覆盖或数据库文件被意外删除(D)。

场景 2:检查数据持久化位置
你想知道一个第三方应用(如 WordPress)默认会把上传的文件和插件放在哪里。你可以:

  1. 启动一个纯净的 WordPress 容器。
  2. 在浏览器中完成安装,上传一张图片,安装一个插件。
  3. 使用 docker diff 命令,查看新增了哪些文件(A)。
docker diff wordpress_container

输出可能会显示:

A /var/www/html/wp-content/uploads
A /var/www/html/wp-content/uploads/2024/08
A /var/www/html/wp-content/uploads/2024/08/my-image.jpg
A /var/www/html/wp-content/plugins
A /var/www/html/wp-content/plugins/my-plugin

这清晰地告诉你,必须将 /var/www/html/wp-content 目录通过卷挂载出来才能持久化数据。

场景 3:审计容器安全性
检查一个正在运行的容器是否被入侵,可以定期执行 docker diff,查看是否有异常的可执行文件(A)被添加到 /tmp、/dev/shm 等目录,或者系统的关键命令(如 /bin/bash)是否被修改(C)。

最佳实践与注意事项

  1. 适用于所有状态的容器:与许多命令不同,docker diff 既可以用于运行中的容器,也可以用于已停止的容器。这对于排查已经停止的容器为何出问题非常有用。

  2. 它显示的是“差异”,不是“内容”:docker diff 只告诉你哪些文件变了(以及变化的类型),但不会显示文件变化的具体内容。要查看具体内容,你需要使用 docker cp 将文件复制出来,或者使用 docker exec 在容器内用 cat 查看。

  3. 理解容器层的本质:这些变更都存储在容器的可写层中。一旦容器被删除,所有这些变更都会永久丢失(除非通过 docker commit 提交为镜像,或通过 docker cp 备份出来)。这再次强调了使用数据卷(Volumes) 进行持久化的重要性。

  4. 输出可能很长:对于安装了大量软件的容器(如通过 apt install),输出列表会非常长。可以结合 grep 等工具进行过滤。

    # 只查看 /etc 目录下的变更
    docker diff my_container | grep /etc
    
    # 只查看被删除的文件
    docker diff my_container | grep ^D

docker diff 是一个低调但极其强大的诊断和审计工具。

  • 它是容器文件系统变化的“审计日志”,提供了从镜像基础层到当前状态的完整变更跟踪。
  • 它是理解容器行为的“显微镜”,通过文件变更反向推导出容器内发生的操作。
  • 它是安全和调试的“第一响应者”,帮助快速定位异常文件变更。
  • 它的输出简单却信息丰富,三个字母(A, C, D)清晰地概括了所有变更类型。

虽然它不像 docker logs 或 docker exec 那样常用,但当你需要深入理解容器内部的文件系统发生了什么时,docker diff 是无可替代的首选工具。掌握它,会让你对容器的运作机制有更深刻的理解。

docker commit

在 Docker 的世界里,我们通常通过编写 Dockerfile 来构建一个可重复、可声明的基础镜像。但有时我们会遇到一种情况:需要在容器内进行一系列复杂的交互式调试和配置,这个过程难以用 Dockerfile 的指令完全描述。此时,docker commit 命令就成了一把“快照刀”,它能够将容器当前的可写层(变化)冻结下来,并打包成一个全新的镜像。这是一个强大却需要慎用的功能。


docker commit 命令用于基于一个容器的当前状态创建一个新的镜像。

它会将容器相对于其基础镜像所做的所有更改(包括文件系统的添加、修改和删除,但不包括挂载的卷中的数据)保存下来,形成一个新镜像的只读层。这个新镜像可以像其他任何镜像一样,被用于创建新的容器。

基本语法:

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
  • CONTAINER:源容器的名称或 ID,其当前状态将被保存。
  • [REPOSITORY[:TAG]]:(可选)为新镜像指定仓库名和标签。如果省略,新镜像会成为一个没有名字的悬虚镜像(dangling image)。

为什么需要它?核心价值与争议

docker commit 是一个带有“争议”的命令,因为它违背了“不可变基础设施”和“声明式配置”的最佳实践。但在特定场景下,它无可替代:

  1. 交互式调试与原型设计:当你需要反复试验才能确定正确的配置、依赖项或设置时,可以在容器内手动操作,成功后一次性提交为镜像。这在快速原型阶段非常有用。
  2. 保存调试现场:当一个正在运行的容器出现复杂问题,你通过 docker exec 进入容器排查。排查结束后,可以将这个包含现场信息(如日志、临时测试文件)的容器状态保存为镜像,供后续分析或分享给他人复现问题。
  3. 从他人手中“抢救”镜像:如果有人给了你一个正在运行的容器(但无法提供其 Dockerfile),你可以使用 commit 将其保存为镜像,从而得以继续使用和分发。

尽管有上述用途,但通常认为:
docker commit 应仅作为最后的手段或临时工具,而不应作为创建镜像的常规方法。 优先使用 Dockerfile。

核心选项(OPTIONS)详解

选项 全称 作用 示例
-a --author 指定新镜像的作者信息。 -a "John Doe <john@example.com>"
-m --message 为本次提交添加一条注释信息,类似于 Git commit message。这对于记录这次快照的原因至关重要。 -m "Added curl and configured proxy"
-c --change 非常强大的选项。允许你在提交时应用一条 Dockerfile 指令。可以多次使用。 -c 'CMD ["nginx", "-g", "daemon off;"]'
-c 'ENV MODE=production'
-p --pause 在提交过程中暂停容器。这能确保数据一致性,是默认行为。

实战示例

1. 基础提交:创建一个无名镜像

# 假设我们在一个ubuntu容器中安装了nginx
docker exec my_container apt update && apt install -y nginx

# 将安装了nginx的容器状态提交为一个新镜像
docker commit my_container

输出会返回一个新镜像的 ID,如 sha256:a1b2c3d4...。这个镜像没有名字(REPOSITORY: TAG 为 <none>:<none>),但可以通过 ID 使用。

2. 提交并命名镜像(推荐)

# 提交并指定仓库名和标签
docker commit my_container my-nginx:version1

3. 提交并添加元信息

# 提交并添加作者信息、提交说明,同时修改启动命令
docker commit \
  -a "Your Name <email@example.com>" \
  -m "Installed Nginx and configured custom homepage" \
  -c 'CMD ["nginx", "-g", "daemon off;"]' \
  my_container \
  my-custom-app:latest

最佳实践与严重警告

  1. Dockerfile 优先原则:永远不要用 docker commit 来代替 Dockerfile。通过 commit 生成的镜像被称为“黑盒镜像”,它缺乏透明性、可重复性和可维护性。你无法确切知道镜像中包含了哪些更改,也无法轻松地重建它。

  2. 数据卷不会被提交:docker commit 不会保存通过 -v 挂载的数据卷(Volumes) 中的内容。卷的目的是持久化数据,与镜像的生命周期分离。

  3. 小心提交敏感信息:如果你在容器内操作时写入了密码、API 密钥等敏感信息,它们会被完整地提交到新镜像中。使用前务必检查,或使用 --change 选项在提交后覆盖环境变量。

  4. 作为临时工具,而非流程核心:将 docker commit 视为一个“快速存档”工具。一旦通过交互式方法确定了正确的配置,就应该将这些步骤翻译成 Dockerfile 指令,从而构建一个真正可管理的镜像。

  5. 先审查再提交:提交前,使用 docker diff <container> 命令检查一下容器内到底发生了哪些变化,避免将临时文件、缓存或日志提交进去。

docker commit 的工作流程

graph LR
    A[基础镜像: ubuntu:latest] --> B[运行容器: docker run -it --name my_container ubuntu]
    B --> C[在容器内操作: apt install nginx]
    C --> D[提交容器: docker commit my_container my-nginx:custom]
    D --> E[新镜像: my-nginx:custom]
    E --> F[基于新镜像运行容器: docker run my-nginx:custom]

docker commit 是一把强大的“双刃剑”。

  • 它是什么:一个将容器现场保存为镜像的“快照”工具。
  • 它的价值:在交互式调试、原型设计和拯救现场等场景中提供了无与伦比的便捷性。
  • 它的危险:极易创建出臃肿、不透明、不可重复的“黑盒镜像”,违背基础设施即代码(IaC)的最佳实践。
  • 最佳实践:谨慎使用,仅作为权宜之计。一旦实验成功,应立即将过程转化为 Dockerfile。使用时务必通过 -m 和 -a 添加注释,并通过 --change 规范化配置。

docker pause 和 docker unpause

在 Docker 的日常运维中,我们熟悉了 start、stop 和 restart 这种“生杀予夺”式的生命周期管理。但有时我们需要一种更细腻的控制——能否在不终止进程的情况下,暂时让一个容器“静默”?Docker 提供的 docker pause 和 docker unpause 命令正是这样一对精巧的“魔法开关”,它们允许我们冻结和恢复容器内所有进程的执行,为容器管理提供了前所未有的灵活性。

什么是 docker pause 和 docker unpause?

  • docker pause:用于暂停(冻结) 一个运行中容器内的所有进程。
  • docker unpause:用于恢复(解冻) 一个被暂停的容器,使其进程继续正常运行。

这对命令的核心价值在于其操作的即时性和无损性。它利用 Linux 内核的 cgroup freezer 功能来实现,整个过程不涉及进程终止和启动,因此对容器内应用程序来说是完全无感知的。

为什么需要它们?核心价值与应用场景

虽然不像 stop 和 start 那样常用,但 pause/unpause 在特定场景下是不可或缺的利器:

  1. 故障排查与“抓现场”:当某个容器占用了极高的 CPU 或磁盘 I/O,导致系统不稳定时,直接 stop 它会丢失现场。使用 pause 可以立即“定格”所有进程的状态(包括内存中的数据),方便您用 docker inspect 或其他工具分析问题根源,然后再 unpause 恢复。

    • 这就像是给正在播放的电影按下了暂停键,画面定格,但播放器并未关闭。
  2. 资源争用管理:在开发或测试环境中,当主机资源紧张时,可以临时 pause 一些非关键容器(如后台任务处理器、开发环境容器),将 CPU 和内存资源让给更重要的服务(如数据库、主应用)。处理完后,再 unpause 它们,无需重新启动。

  3. 维护与升级:在对集群进行维护或滚动升级时,可以先 pause 一个容器,确保它不会处理新的请求,同时保留其完整状态。待维护完成后,再 unpause 它无缝接回服务中。

  4. 一致性快照的辅助工具:虽然容器本身不支持热备份,但 pause 容器可以为底层文件系统(如果结合存储驱动)或外部备份工具创建一个短暂的、相对一致的窗口期,因为此时没有进程在写入文件。

工作原理:cgroup freezer 的魔法

这对命令的实现依赖于 Linux 内核的一个强大功能:cgroup freezer。

  1. docker pause CONTAINER:

    • Docker Daemon 会向该容器对应的 cgroup 发送一个“冻结”指令。
    • 内核立即将属于这个 cgroup 的所有进程(即容器内所有进程)标记为 TASK_STOPPED 或 TASK_TRACED 状态。
    • 此时,这些进程不再被 CPU 调度执行,它们“在原地被冻住”,但依然完整地保留在内存中,包括它们的堆栈、内存数据和所有打开的文件描述符。
  2. docker unpause CONTAINER:

    • Docker Daemon 向 cgroup 发送“解冻”指令。
    • 内核将所有被冻结的进程状态恢复为 TASK_RUNNING。
    • 进程立即重新进入调度队列,从刚才被暂停的指令处继续执行,就像什么都没有发生过一样。

整个过程不涉及 SIGTERM 或 SIGKILL 信号,因此应用程序完全不知道曾经被暂停过。

实战示例

假设我们有一个名为 my_app 的容器正在运行一个 Web 服务。

  1. 暂停容器:

    docker pause my_app

    执行后,容器的状态(在 docker ps 中)会从 Up 变为 Up (Paused)。

  2. 尝试访问服务:
    此时,任何尝试连接到该容器服务的请求(如 HTTP 请求)都会挂起或超时,因为处理请求的进程已经被冻结,无法响应。

  3. 检查状态:

    docker ps

    输出示例:

    CONTAINER ID   IMAGE          COMMAND                  STATUS
    a1b2c3d4e5f6   nginx:latest   "/docker-entrypoint.…"   Up 5 minutes (Paused)
  4. 恢复容器:

    docker unpause my_app

    容器状态立刻恢复为 Up,被挂起的请求会得到处理,服务恢复正常。

最佳实践与重要注意事项

  1. 对网络的影响:被暂停的容器网络栈同样被冻结。它不会响应 ARP 请求、TCP 握手等。对于服务发现和负载均衡器来说,这个容器会表现得像突然宕机一样,可能导致请求失败和连接错误。解冻后,恢复过程取决于应用程序和网络设备的超时设置。

  2. 无损性并非绝对:虽然进程状态无损,但应用程序的业务逻辑可能会受影响。例如,一个被暂停的数据库容器可能导致依赖它的应用报错;一个被暂停的客户端可能导致心跳超时而被服务器踢下线。因此,要在合适的时机(如没有活跃事务时)使用。

  3. 不能暂停已停止的容器:pause 和 unpause 只能作用于状态为 Up(运行中)的容器。无法暂停一个已 Exited 的容器。

  4. 资源并未释放:被暂停的容器虽然不再使用 CPU,但它占用的内存会被完整保留。这一点与 stop 不同,stop 会释放所有资源。


docker pause 和 docker unpause 是 Docker 命令集中一对非常独特且强大的工具。

  • 它们提供了“暂停”而非“停止”的精细控制,实现了对容器进程的瞬时冻结与恢复。
  • 其底层依赖于 Linux cgroup freezer,保证了操作的无损性和即时性。
  • 核心应用场景是故障排查和资源调度,允许你在不丢失现场的情况下解决问题。
  • 使用时需谨慎评估对业务连续性的影响,特别是对于有状态和有连接的服务。

docker rm

在 Docker 的日常使用中,我们会创建大量的容器用于测试、开发和学习。这些容器在完成任务后,如果一直保留,会逐渐占用大量的磁盘空间。docker rm(remove 的缩写)就是 Docker 世界的“回收站清理”工具,它的作用非常简单直接:删除一个或多个已停止的容器。

什么是 docker rm?

docker rm 命令用于删除一个或多个已停止的容器。

删除容器会一并移除与其关联的可写层(容器层),从而释放磁盘空间。这是一个清理环境、保持系统整洁的核心命令。

基本语法:

docker rm [OPTIONS] CONTAINER [CONTAINER...]

你可以同时删除多个容器,只需将它们的名称或 ID 用空格隔开。

如何使用?简单示例

1. 删除一个已停止的容器:
这是最常用的场景。

# 首先停止容器
docker stop my_container
# 然后删除它
docker rm my_container

# 或者使用容器ID(取前几位即可)
docker rm a1b2c3d4

2. 删除多个已停止的容器:

docker rm container_1 container_2 container_3

3. 强制删除一个运行中的容器(不推荐常规使用):
如果你想强制删除一个还在运行的容器,可以使用 -f 或 --force 选项。但这相当于先执行 docker kill,再执行 docker rm,是一种粗暴的操作。

docker rm -f my_running_container

4. 清理所有已停止的容器(实用技巧):
使用命令替换一次性删除所有已停止的容器,这是非常高效的清理方式。

docker rm $(docker ps -aq)
  • docker ps -aq:列出所有容器(包括已停止的)的 ID。
  • docker rm:删除这些 ID 对应的容器。

核心注意事项

虽然命令简单,但以下几点必须牢记,否则可能导致数据丢失:

  1. 数据会永久丢失!:这是最重要的一点。删除容器会销毁存储在其容器层中的所有数据。这包括任何创建或修改的文件、安装的软件等。如果这些数据没有通过 Docker 数据卷(Volumes) 或绑定挂载(Bind Mounts) 持久化到宿主机上,那么删除后数据将无法恢复。

  2. 无法删除运行中的容器:默认情况下,Docker 会保护运行中的容器不被意外删除。如果你尝试删除一个 Up 状态的容器,会收到一个错误提示。你必须先 docker stop 它,或者使用 -f 选项强制删除。

  3. 谨慎使用 -f (force) 选项:docker rm -f 虽然方便,但应避免成为习惯。它不会给容器内进程 gracefully shutdown 的机会,可能会中断正在进行的操作(如数据库写入),导致数据损坏。最佳实践永远是先停止,再删除。

  4. 卷不会被自动删除:docker rm 不会删除与容器关联的匿名卷。这是 Docker 的一种保护机制,防止你意外删除重要的持久化数据。如果你确认某个匿名卷也不再需要,必须使用 docker volume rm 手动删除它。


docker rm 是一个简单但至关重要的容器生命周期管理命令。

  • 它的核心职责是清理已停止的容器,释放磁盘空间。
  • 操作前务必确认容器内的数据已通过卷正确持久化,否则会造成永久性数据丢失。
  • 养成先停后删的好习惯,谨慎使用 -f 选项。
  • 结合 docker ps -aq 可以高效地进行批量清理,保持开发环境的整洁。

把它当作一个需要谨慎使用的回收站——在按下回车键前,永远记得确认里面的东西是否真的不再需要了。

docker export 和 docker import

在 Docker 的镜像和容器管理中,我们通常使用 docker commit 来创建新镜像,使用仓库来分发镜像。但有时我们需要一种更底层、更直接的方式来迁移或备份一个容器的纯粹的文件系统,而不关心其历史、层状结构或元数据。这时,docker export 和 docker import 这一对命令就派上了用场。它们像是为容器文件系统制作了一个“快照”并将其“还原”为一个扁平化的镜像。


这是一对用于将容器的文件系统导出为一个压缩包,再将该压缩包导入成为一个新镜像的命令。

  • docker export:将一个容器的文件系统导出为一个 tar 归档文件(压缩包)。这个包只包含容器当时的文件系统快照,不包含任何镜像历史、层信息或元数据(如 CMD, ENV 等)。
  • docker import:将一个由 docker export 或其他方式生成的 tar 归档文件,导入并创建一个新的 Docker 镜像。这个新镜像只有一层(扁平化),并且会丢失所有原始的历史和大部分元数据。

简单比喻:

  • docker export:就像把一台电脑的整个硬盘克隆并打包成一个巨大的 .gho 文件。
  • docker import:就像是把那个 .gho 文件恢复到一块新硬盘上。你得到了一个完全一样的系统,但丢失了原来的分区信息、安装日志等。

为什么需要它们?核心价值与应用场景

这对命令的使用场景相对特殊,但在以下情况下非常有用:

  1. 跨环境迁移容器状态:将一个在本地开发环境中配置好的复杂容器(安装了大量依赖和软件),完整地迁移到另一台无法直接连接 Docker 仓库的生产机器上。你可以导出 tar 包, physically(物理地)复制过去,再导入。
  2. 制作极简的基础镜像:从一个大而全的发行版(如 Ubuntu)容器开始,精心配置和瘦身后,导出其文件系统,再导入为一个新的镜像。这个新镜像不包含任何中间层,可能比通过 Dockerfile 构建的镜像体积更小。
  3. 备份与恢复:作为一种容器级别的备份手段。虽然不推荐作为数据库等有状态服务的主要备份方式(因为无法保证导出时数据一致性),但可以用于备份静态文件或整个环境。
  4. 与非 Docker 系统交换文件系统:导出的 tar 包是一个标准的文件系统归档,可以被 chroot、虚拟机器或其他容器运行时使用,实现了某种程度的“互通”。

实战示例

第 1 步:导出(Export)一个容器
假设我们有一个名为 my_configured_container 的容器,里面装好了我们需要的所有软件。

# 将容器的文件系统导出为 tar.gz 压缩包
docker export my_configured_container > my_container_backup.tar.gz

# 或者使用 -o (--output) 选项
docker export -o my_container_backup.tar my_configured_container

现在,你得到了一个 my_container_backup.tar.gz 文件,它包含了容器某时刻的完整文件系统快照。

第 2 步:导入(Import)为一个新镜像
将这个 tar 包拿到另一台机器上,或者在本机将其导入为一个全新的镜像。

# 从 tar 包导入,并为新镜像指定仓库名和标签
cat my_container_backup.tar.gz | docker import - my_custom_image:latest

# 或者从文件直接导入
docker import my_container_backup.tar.gz my_custom_image:latest

# 也可以在导入时添加提交信息
docker import --message "Imported from backup on $(date)" my_container_backup.tar.gz my_custom_image:latest

导入成功后,你就可以像使用其他镜像一样,使用 my_custom_image:latest 来运行新的容器了。

核心注意事项与局限性(非常重要!)

  1. 只导出文件系统,不导出元数据:这是最大的限制。export 不会包含:

    • 镜像的构建历史( layers)。
    • 原始的 CMD、ENTRYPOINT、ENV、EXPOSE、WORKDIR 等元数据配置。
    • 容器的运行状态、内存中的数据。
    • 使用 -v 挂载的数据卷中的内容。
  2. 数据一致性问题:export 命令不会暂停容器。如果在你导出的瞬间,容器内的进程正在写入文件,可能会导致导出的归档文件出现数据不一致或文件损坏。对于数据库等活跃容器,这是一个高风险操作。

  3. 扁平化与历史丢失:通过 import 创建的镜像只有一层。你丢失了所有层缓存的优势,也无法查看镜像的构建历史。这使得新镜像的构建和存储效率可能更低。

  4. 需要手动指定运行时配置:由于元数据丢失,基于新镜像运行容器时,你很可能需要手动指定所有配置,例如:

    docker run -it --rm \
      -p 8080:80 \
      -e "MY_VAR=value" \
      my_custom_image:latest \
      /bin/bash -c "/usr/sbin/nginx" # 必须手动指定启动命令!

与 docker save / docker load 的区别

这是一个非常常见的困惑点:

特性 export / import save / load
操作对象 容器 -> tar -> 镜像 镜像 -> tar -> 镜像
内容 仅容器的文件系统快照 完整的镜像(包括所有层、历史、元数据)
元数据 不保留(CMD, ENV 等丢失) 完整保留
层级 合并为单层(扁平化) 保留所有多层结构
主要用途 迁移或备份容器的当前状态 迁移或备份完整的镜像

简单总结:用 save/load 来完整地“复制-粘贴”镜像;用 export/import 来“克隆”一个容器的硬盘。


docker export 和 docker import 是一对强大但略显“粗暴”的工具。

  • 它们提供了容器文件系统级别的快速快照和迁移能力,绕过了镜像层的概念。
  • 其核心代价是元数据的丢失,导致新镜像需要手动配置才能正常运行。
  • 主要适用于特殊场景,如环境迁移、制作特定基础镜像或与外部系统交互。
  • 对于大多数日常需要备份和迁移镜像的场景,应优先使用 docker save 和 docker load。

docker wait

在 Docker 的自动化脚本和 CI/CD 流水线中,我们经常需要启动一个容器执行任务,并等待这个任务完成后再进行下一步操作。容器任务完成的标准就是容器退出。那么,如何让脚本自动等待容器退出并获取其结果呢?这就是 docker wait 命令的用武之地。它是一个简单、专注且对自动化极其有用的工具。


docker wait 命令用于阻塞(等待)一个或多个容器退出,然后打印它们的退出代码。

它的行为非常纯粹:调用这个命令后,Shell 会在此处等待,直到指定的容器停止运行。一旦容器退出,docker wait 会立即返回该容器的退出状态码(Exit Code),然后脚本才可以继续执行。

基本语法:

docker wait CONTAINER [CONTAINER...]

你可以同时等待多个容器退出,命令会阻塞直到所有指定容器都退出,并分别返回它们的退出码。

为什么需要它?核心价值

docker wait 的核心价值在于自动化脚本和任务编排。

  1. 同步任务执行:在 Shell 脚本中,你需要确保一个容器(可能是一个数据库迁移脚本、一个批处理任务)完全执行成功后,再启动下一个依赖它的容器。docker wait 提供了这种同步能力。
  2. 获取执行结果:容器内进程的退出状态码(0 通常表示成功,非 0 表示失败)是判断任务是否成功的标准依据。docker wait 是获取这个状态码最直接的方式之一。
  3. 非交互式操作:它不需要像 docker logs 那样持续输出信息,也不会像 docker attach 那样占用标准输入。它只是安静地等待,非常适合在后台脚本中使用。

实战示例

场景: 你有一个名为 batch_job 的容器,它运行着一个数据处理任务。你需要在脚本中启动它,并等待任务完成,根据成功与否决定后续步骤。

示例脚本:

#!/bin/bash

# 1. 启动一个执行后台任务的容器,并将其放入后台运行
docker run --name batch_job my_data_processor:latest &

# 2. 使用 docker wait 等待这个容器执行完毕。
#    脚本会在此处阻塞,直到 batch_job 容器退出。
EXIT_CODE=$(docker wait batch_job)

# 3. 根据退出代码判断任务是否成功
if [ $EXIT_CODE -eq 0 ]; then
    echo "✅ 任务执行成功!"
    # 执行成功后的后续操作,例如通知、启动下一个服务等
else
    echo "❌ 任务执行失败,退出代码:$EXIT_CODE"
    # 执行失败后的处理,例如错误处理、日志收集、报警等
    docker logs batch_job # 可以立刻获取日志来排查错误
    exit 1
fi

# 4. 清理容器
docker rm batch_job

echo "脚本继续执行..."

等待多个容器:

# 同时启动多个任务容器
docker run --name worker_1 my_worker &
docker run --name worker_2 my_worker &

# 等待所有worker容器退出
EXIT_CODE_1=$(docker wait worker_1)
EXIT_CODE_2=$(docker wait worker_2)

echo "Worker 1 退出代码: $EXIT_CODE_1"
echo "Worker 2 退出代码: $EXIT_CODE_2"

核心注意事项

  1. 它只等待已存在的容器:docker wait 只能作用于已经创建的容器(无论状态是运行中还是已停止)。如果指定的容器不存在,命令会报错。
  2. 阻塞特性:命令会阻塞当前 Shell 或脚本的进程。这意味着在等待期间,你的脚本什么都做不了。请确保这是在预期内的行为。
  3. 与 docker run 的分离模式(-d)是绝配:最经典的用法就是使用 docker run -d 在后台启动容器,然后立即使用 docker wait 等待它的结果。这样既能实现后台启动,又能同步等待。
  4. 退出代码是关键:docker wait 的核心输出就是退出代码。容器内的主进程必须正确地返回退出代码(例如,在脚本中使用 exit 0 或 exit 1),这个机制才有意义。
  5. 它不输出日志:docker wait 非常“安静”,它不会像 docker logs -f 那样向你实时展示容器的输出。你通常需要在其之后配合 docker logs 来获取详细日志。

docker wait 是一个“人狠话不多”的实用命令。

  • 它的功能极其专注:安静地等待容器退出并返回结果码。
  • 它是自动化脚本的“粘合剂”:实现了容器任务的同步执行,是 CI/CD 流水线和运维脚本中的无名英雄。
  • 使用时需注意其阻塞特性,并确保与 docker run -d 配合使用以达到最佳效果。
  • 它的价值体现在退出代码上,因此要求容器内的应用程序有良好的退出状态管理。

docker rename

docker rename 命令用于修改一个容器的名称。


容器创建时,我们会通过 --name 为其指定一个名称。但之后可能发现名称不合适、有拼写错误,或者想遵循新的命名规范。docker rename 就是为了解决这个问题,它允许你在容器创建后重新命名,而无需删除和重新创建容器。

基本语法

docker rename OLD_NAME NEW_NAME

实战示例

  1. 纠正拼写错误:

    # 不小心把名字打错了
    docker run -d --name ngixn nginx:latest
    
    # 用 rename 修正它
    docker rename ngixn nginx
  2. 根据新的命名规范重命名:

    # 旧名称:web
    # 新名称:希望加上项目前缀 prod-frontend-web
    docker rename web prod-frontend-web
  3. 为匿名容器命名:
    如果最初运行容器时忘了使用 --name 参数,Docker 会随机分配一个名字(如 dreamy_curie)。你可以给它一个更有意义的名字。

    docker rename dreamy_curie my-app

核心注意事项

  • 新旧名称不能重复:新的容器名称必须在当前 Docker 环境中是唯一的,不能与其他任何容器或镜像重名。
  • 容器状态不限:此命令对容器的状态没有要求,无论是运行中(Up)、已停止(Exited) 还是已暂停(Paused),都可以成功重命名。
  • 即时生效:重命名操作是立即生效的。之后所有 Docker 命令(docker start, docker stop, docker logs等)都需要使用新的名称来操作这个容器。

docker rename 是一个简单到极致的命令,它就做一件事:给容器改个名。它是一个非常方便的管理工具,让你能轻松地维护容器的标识符,保持环境的整洁和规范。

docker container prune

docker container prune 是一个高效的清理命令,它的功能非常简单粗暴:一键删除所有已停止的容器。


执行这个命令后,Docker 会找出所有处于 exited 状态的容器,并将其全部删除,释放磁盘空间。

基本用法:

# 直接执行会要求确认
docker container prune

# 跳过确认提示,直接删除
docker container prune -f

为什么需要它?

在开发和测试过程中,我们会频繁地创建和停止大量临时容器。手动一个个删除 (docker rm) 非常繁琐。这个命令极大地简化了清理工作,让环境保持整洁。

⚠️ 核心注意事项(重中之重)

这个命令的便利性背后藏着巨大的数据风险,请务必牢记以下几点:

  1. 数据会永久丢失!
    这是最重要的一点。删除容器会同时删除其产生的所有数据。如果这些数据没有通过 Docker 数据卷(Volumes) 或绑定挂载(Bind Mounts) 持久化到宿主机上,那么删除后数据将无法恢复。

  2. 它不问青红皂白
    命令会删除所有已停止的容器。它不会区分哪个容器是重要的,哪个是临时的。你可能一不小心就删除了一个包含重要配置或日志的已停止容器。

  3. 谨慎使用 -f (force) 选项
    -f 选项让你跳过最后的确认提示。虽然方便,但这也移除了最后一道安全屏障。强烈建议不要将 docker container prune -f 放入自动化脚本或设为定时任务,除非你完全确信所有已停止容器都毫无价值。

  4. 它不删除关联的匿名卷
    这是唯一的安全措施。prune 不会自动删除与这些容器关联的匿名卷。如果你确认这些卷也不再需要,必须使用 docker volume prune 进行二次清理。

安全使用准则

  • 清理前,请务必使用 docker ps -a 清单所有已停止的容器,确认其中没有包含重要数据的容器。
  • 对于重要容器,养成使用具名卷或绑定挂载的习惯,这是避免数据丢失的根本方法。
  • 可以先使用 docker rm $(docker ps -aq) 手动选择要删除的容器,控制力更强。

docker container prune 是一把锋利的“双刃剑”。

  • 它是什么:一个一键清理所有已停止容器的便捷工具。
  • 它的价值:快速释放资源,保持环境整洁。
  • 最大的风险:永久性数据丢失。
  • 最佳实践:谨慎使用。每次执行前,请务必确认即将被删除的容器列表,确保没有你需要保留的任何数据。

docker update

在容器化应用的日常运维中,我们经常会遇到这样的场景:一个正在运行的容器突然需要更多内存来处理高峰流量,或者某个容器占用了过多 CPU 资源需要被限制。如果不想停止和重启服务,该怎么办?Docker 提供的 docker update 命令正是为了解决这个问题,它允许我们动态地修改一个或多个运行中容器的配置,是实现弹性伸缩和资源优化的关键工具。


docker update 命令用于动态更新一个或多个运行中容器的配置。

其核心功能是调整容器的资源限制(如 CPU、内存)和部分运行时参数,而无需停止和重新启动容器。这实现了对应用服务的“在线热调整”,对于保证服务的连续性和可用性至关重要。

基本语法:

docker update [OPTIONS] CONTAINER [CONTAINER...]

为什么需要它?核心价值

  1. 应用弹性伸缩:应对流量波动,在业务高峰时临时为容器增加 CPU 或内存配额,高峰过后再降低,从而高效利用资源。
  2. 故障排查与资源限制:当某个容器因 bug 导致资源耗尽(如内存泄漏),可以立即降低其资源上限,防止它拖垮整个宿主系统,为排查问题争取时间。
  3. 避免服务中断:对于有状态服务(如数据库)或难以重启的服务,docker update 提供了无需停服即可调整资源配置的能力,最大程度保证业务连续性。
  4. 成本优化:根据监控数据,将过度分配的容器资源下调到合理水平,节省云计算成本。

核心选项(OPTIONS)详解

docker update 的强大之处在于其丰富的选项,主要用于调整资源限制:

1. CPU 资源调整:

  • -c, --cpu-shares int:调整 CPU 份额(相对权重)。默认是 1024。提高或降低这个值可以增加或减少容器在 CPU 竞争时的优先级。
  • --cpus decimal:最常用。限制容器可以使用的 CPU 核心数。例如,--cpus 1.5 表示限制容器最多使用 1.5 个 CPU 核心的计算能力。
  • --cpuset-cpus string:限制容器只能运行在哪些特定的 CPU 核心上。例如,--cpuset-cpus "0-2" 表示只使用前三个核心。

2. 内存资源调整:

  • -m, --memory bytes:最常用。限制容器能使用的最大内存。例如,-m 512m 或 -m 2g。
  • --memory-swap bytes:限制容器能使用的内存+交换分区(Swap)的总大小。必须与 -m 参数一起使用。

3. 重启策略调整:

  • --restart string:修改容器的重启策略(如 no, on-failure, always, unless-stopped)。这在容器持续崩溃需要修改恢复策略时非常有用。

实战示例

场景 1:应对流量高峰,临时增加资源
一个 Web 容器在促销期间需要更多计算能力。

# 将容器 my_web 的 CPU 限制提升到 2 个核心,内存限制提升到 2GB
docker update --cpus 2 -m 2g my_web

场景 2:限制失控的容器
监控发现一个容器内存泄漏,占用了过多资源。

# 立即将容器的内存上限硬限制为 512MB,防止它耗尽主机内存
docker update -m 512m my_buggy_container

场景 3:批量调整多个容器
为整个应用栈的所有容器降低资源配额。

# 同时更新 nginx, web_app, cache 三个容器的 CPU 份额
docker update --cpus 0.5 nginx web_app cache

最佳实践与重要注意事项

  1. 主要针对运行中的容器:虽然也可用于已停止的容器,但其主要价值体现在不中断运行中服务的动态调整上。

  2. 资源限制的生效方式:

    • 调低限制(如减少内存):立即生效。如果容器当前使用量已超过新限制,操作系统内核会强制其释放内存,可能会触发容器内的 OOM(内存不足)机制。
    • 调高限制(如增加 CPU):立即生效。容器可以立即开始使用新分配的资源。
  3. 理解“硬限制”与“软限制”:-m 设置的是硬性上限,容器进程绝对不能超过此值。而 --cpu-shares 是软性权重,只在 CPU 资源发生竞争时起作用。

  4. 无法更新所有配置:docker update 不能修改与容器架构相关的核心配置,例如:

    • 网络模式(--network)
    • 端口映射(-p)
    • 卷挂载(-v)
    • 环境变量(-e)
    • 这些修改必须通过docker commit创建新镜像或重建容器来实现。
  5. 监控调整结果:更新后,使用 docker stats 命令观察容器的实际资源使用情况,确认调整是否达到预期效果。


docker update 是 Docker 运维中实现精细化资源管理和快速故障响应的利器。

  • 它是什么:一个可以动态调整运行中容器资源限制的命令。
  • 它的核心价值:无需重启服务即可实现资源的弹性伸缩和故障隔离,极大保障了业务的可用性。
  • 它的主要能力:调整 CPU、内存限制和重启策略。
  • 它的局限性:无法修改网络、存储等核心配置。
  • 最佳实践:与监控系统结合,根据 metrics 指标动态调整资源配置,是实现容器化环境高效、稳定运行的高级技巧