Docker 教程

Docker 生命周期管理

在使用 docker run 命令成功创建并运行容器之后,管理容器化应用的下一个关键技能就是掌握如何控制它们的状态。容器就像任何其他进程或应用程序一样,拥有自己的生命周期。它们需要被启动、停止、有时需要重启,偶尔还需要被强制终止。

掌握这些命令能够确保你的应用程序可靠运行、高效利用资源,并且能够在没有意外停机或数据丢失的情况下进行平滑的维护或更新。本章将深入探讨这些允许你管理容器完整运行生命周期的基础 Docker 命令,让你对容器的行为和可用性拥有细粒度的控制权。

1. 理解容器状态与 docker ps

在管理容器之前,我们需要能够查看它们的当前状态。docker ps 命令是你列出容器并了解其状态的首选工具。

1.1 使用 docker ps 列出运行中的容器

docker ps(“process status”的缩写)命令默认只显示当前正在运行的容器。

docker ps

示例: 如果你有一个正在运行的 Nginx 容器,输出可能如下所示:

CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS      NAMES
a1b2c3d4e5f6   nginx     "/docker-entrypoint.…"   2 minutes ago    Up 2 minutes    80/tcp     my-nginx-container

列含义解析:

  • CONTAINER ID (容器 ID): 容器的唯一标识符。你可以使用完整的 ID 或前几个字符(通常 3-4 个字符就足以保证唯一性)。
  • IMAGE (镜像): 创建该容器所使用的 Docker 镜像(例如:nginx)。
  • COMMAND (命令): 容器启动时执行的命令。
  • CREATED (创建时间): 容器是多久之前创建的。
  • STATUS (状态): 容器的当前状态(例如:Up X minutes 表示已运行 X 分钟,Exited (0) X minutes ago 表示已退出)。
  • PORTS (端口): 为容器配置的任何端口映射(例如:80/tcp)。
  • NAMES (名称): 分配给容器的人类可读名称。如果你在 docker run 时没有用 --name 指定,Docker 会自动生成一个随机名称。

1.2 使用 docker ps -a 列出所有容器

有时你需要查看当前未运行的容器,比如那些已经退出的容器。docker ps -a(或 docker ps --all)命令会显示所有容器,无论它们当前处于什么状态。

docker ps -a

示例: 如果你有一个运行中的 Nginx 容器和一个之前停止的 Ubuntu 容器,输出可能是:

CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS                     PORTS      NAMES
a1b2c3d4e5f6   nginx     "/docker-entrypoint.…"   2 minutes ago    Up 2 minutes               80/tcp     my-nginx-container
g7h8i9j0k1l2   ubuntu    "bash"                   5 minutes ago    Exited (0) 3 minutes ago              my-ubuntu-box

注意 my-ubuntu-box 容器的 STATUSExited(已退出)。这对于识别那些不再处理请求但仍然存在于你系统上的容器至关重要。

2. 启动与停止容器

管理容器的运行时间是基础操作。docker startdocker stop 提供了让容器上线和优雅下线的主要手段。

2.1 docker start:唤醒停止的容器

一旦容器被创建(例如通过 docker run)且不再运行(可能是退出了或被停止了),你可以使用 docker start 让它重新上线。这个命令会重新执行容器的主进程,有效地从它之前的状态恢复运行,并保留其可写层中的任何数据(除非涉及到数据卷,我们将在未来的章节中讨论)。

语法:

docker start [选项] 容器名称或ID [容器名称或ID...]

你可以通过 CONTAINER IDNAME 来指定容器。

真实场景示例: 假设你有一个名为 my-api 的后端 API 服务运行在容器中。如果这个服务因为维护被停止或崩溃了,你可以重新启动它:

首先,列出所有容器找到它的 ID 或名称:

docker ps -a
# (假设显示 my-api 的状态为 Exited)

启动该容器:

docker start my-api

或者使用 ID:

docker start a1b2c3d4e5f6 # 替换为实际的 ID

验证它是否再次运行:

docker ps

my-api 容器现在应该显示为 Up X seconds/minutes

假设场景: 考虑一个名为 data-cruncher 的数据处理容器,它执行夜间批处理任务。任务完成后,它就退出了。第二天晚上,你不需要创建一个新容器(那需要再次运行 docker run),你只需简单地执行 docker start data-cruncher 即可启动下一轮处理周期(假设它被设计为在启动时运行其任务)。

2.2 docker stop:优雅停机

docker stop 命令用于优雅地关闭一个正在运行的容器。当你发出 docker stop 时,Docker 会向容器的主进程发送一个 SIGTERM 信号。这个信号告诉容器内运行的应用程序执行优雅关机,允许它:

  • 保存任何打开的文件或内存中的数据。
  • 关闭网络连接。
  • 清理资源。
  • 干净地退出。

Docker 默认等待 10 秒 让容器优雅停止。如果应用程序在这个超时时间内没有退出,Docker 会接着发送一个 SIGKILL 信号,这会立即终止进程(即“强制杀死”)。

语法:

docker stop [选项] 容器名称或ID [容器名称或ID...]

选项:

  • -t, --time int: 在杀死容器之前等待停止的秒数(默认是 10 秒)。

真实场景示例: 你有一个 Web 应用容器 web-app-production,需要暂时下线进行快速更新或故障排查。

停止容器:

docker stop web-app-production

Docker 会发送 SIGTERM。如果 web-app-production 内部的应用程序表现良好,它会捕获这个信号,执行清理工作,然后退出。

验证它已停止:

docker ps

web-app-production 容器应该不再出现在运行中的容器列表中。

带超时的进阶示例: 假设你的应用程序需要超过默认的 10 秒才能优雅关闭,可能是因为它正在处理大量消息队列。你可以增加超时时间:

docker stop -t 30 my-message-processor

这条命令给了 my-message-processor 最多 30 秒的时间来优雅关闭,之后 Docker 才会用 SIGKILL 介入干预。

3. 重启与强制终止容器

除了简单的启动/停止操作外,docker restartdocker kill 提供了对容器执行管理的更具体控制。

3.1 docker restart:先停后启

docker restart 命令是一种便捷方式,用于停止一个运行中的容器然后立即再次启动它。它实际上将 docker stopdocker start 组合成了一个单一操作。这在应用需要重启才能生效的配置更改,或者仅仅是为了恢复一个可能处于不稳定状态的容器时特别有用。

语法:

docker restart [选项] 容器名称或ID [容器名称或ID...]

选项:

  • -t, --time int: 在杀死容器之前等待停止的秒数(默认是 10 秒)。与 docker stop 类似,这个超时适用于重启操作中的“停止”阶段。

真实场景示例: 你的 my-nginx-container 正在运行,但你刚刚在宿主机上更新了它的配置文件(该文件可能是作为数据卷挂载到容器中的——我们将在第 4 模块探索这个概念)。为了让 Nginx 加载新配置,它通常需要重启。

重启 Nginx 容器:

docker restart my-nginx-container

这条命令会优雅地停止 Nginx(发送 SIGTERM),等待它退出,然后再次启动它,从而加载新的配置。

验证其状态:

docker ps

my-nginx-container 应该显示为 Up X seconds/minutes

3.2 docker kill:强制终止

虽然 docker stop 会尝试优雅关闭,但 docker kill 会对容器执行立即的、强制的终止。它会向容器的主进程发送一个 SIGKILL 信号,应用程序无法捕获或忽略该信号。这类似于拔掉物理机器的电源插头——任何未保存的数据都将丢失,进程也没有机会进行清理。

你只应该在 docker stop 失败,或者绝对需要立即终止时(例如,一个失控的容器消耗了过多资源且对 SIGTERM 没有响应)才使用 docker kill

语法:

docker kill [选项] 容器名称或ID [容器名称或ID...]

选项:

  • --signal string: 发送到容器的信号(默认是 KILL)。你可以发送其他信号,但 KILL (SIGKILL) 是这个命令最常见的用例。

真实场景示例: 你有一个开发容器 dev-env-container 冻结了并且没有响应。即使加上很长超时的 docker stop 也不起作用。为了快速回收资源:

强制杀死容器:

docker kill dev-env-container

该容器将立即停止运行。

验证其状态:

docker ps -a

dev-env-container 现在应该显示 Exited (137) X seconds ago。退出码 137 通常表示进程是被 SIGKILL 信号杀死的(128 + 9 代表 SIGKILL)。

3.3 docker stop 与 docker kill 的区别

特性docker stopdocker kill
发送的信号SIGTERM (默认),超时后发送 SIGKILLSIGKILL (默认)
优雅关闭是,允许应用程序干净地关闭否,立即终止
数据丢失风险低,应用程序可以保存状态高,未保存的数据可能会丢失
使用场景常规关机、更新、计划内维护无响应的容器、紧急终止
超时机制杀死前有可配置的超时时间无超时,立即行动
提示: 始终优先使用 docker stop 而不是 docker kill,以确保数据完整性和正确的资源管理。

4. 综合实战演练与演示

让我们使用一个简单的 Nginx Web 服务器容器来走一遍完整的生命周期演示。

第 1 步:运行一个 Nginx 容器
首先,我们将在后台模式 (-d) 下运行一个 Nginx 容器,并给它命名 (my-webserver)。我们还将把宿主机上的 8080 端口映射到容器内的 80 端口。

docker run -d --name my-webserver -p 8080:80 nginx
  • -d: 后台模式运行容器。
  • --name my-webserver: 给容器命名为 my-webserver
  • -p 8080:80: 将宿主机的 8080 端口映射到容器的 80 端口。
  • nginx: 指定使用的镜像。

你应该会看到打印出一个容器 ID,确认它正在运行。

第 2 步:验证容器正在运行
使用 docker ps 查看运行中的 my-webserver 容器。

docker ps

你也可以在浏览器中导航到 http://localhost:8080 来检查它。你应该能看到 Nginx 的欢迎页面。

第 3 步:停止容器
现在,让我们优雅地停止 my-webserver 容器。

docker stop my-webserver

你应该会看到打印出 my-webserver,表明停止命令已执行。

第 4 步:验证容器已停止
检查 docker psmy-webserver 应该不再列出。

docker ps

为了确认它仍然存在只是停止了,使用 docker ps -a

docker ps -a

my-webserver 容器现在应该显示 Exited (0) X seconds ago。如果你尝试在浏览器访问 http://localhost:8080,它应该无法访问了。

第 5 步:启动容器
让我们把 my-webserver 重新拉上线。

docker start my-webserver

你应该会看到打印出 my-webserver

第 6 步:验证容器再次运行
检查 docker ps 并在浏览器中刷新 http://localhost:8080。它应该正在运行并且再次可访问。

docker ps

第 7 步:重启容器
现在,让我们使用 docker restart。这会短暂地关闭 Web 服务器并将其重新启动。

docker restart my-webserver

第 8 步:验证重启后的容器运行状态
使用 docker ps 并在浏览器刷新确认。

docker ps

第 9 步:体验 docker kill
为此,让我们使用一个简单的 Ubuntu 容器运行一个长任务,比如 sleep 300(睡眠 300 秒)。这将允许我们演示突然的终止。

首先,运行 Ubuntu 容器:

docker run -d --name my-sleeper ubuntu sleep 300

验证其运行:

docker ps

现在,强制杀死它:

docker kill my-sleeper

验证它已停止,并检查其退出状态:

docker ps -a

my-sleeper 容器应该显示 Exited (137) X seconds ago。这确认了它是被杀死的。

第 10 步:清理
删除不再需要的容器是个好习惯(未来的章节会详解 docker rm):

docker rm my-webserver
docker rm my-sleeper

这些命令将删除已停止的容器。如果不加 -f (强制) 标志,你不能删除一个运行中的容器,因为这通常不推荐(它会隐式地先杀死容器)。