IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。
在前几篇文章中,我们学习了如何构建镜像、优化 Dockerfile,并且已经能把 Flask + Redis 计数器应用跑起来了。但是------容器跑起来只是第一步。容器挂了怎么办?怎么查看它为什么挂?如何在不停止服务的情况下进入容器排查问题?如何在压力测试时实时观察 CPU 和内存变化?
这些,就是本篇要回答的问题。
在前两篇中,我们掌握了 Dockerfile 的核心指令和最佳实践,学会了构建一个"能用"的镜像。但从"能用"到"可控",中间差的就是这一篇:真正掌控容器的运行状态。掌握了容器生命周期管理的完整图景,当你进入 Kubernetes 的世界时,会发现 Pod 的状态转换、探针机制、弹性伸缩------这些概念你早已在 Docker 层面亲手操练过。
一、容器不是"启动就完事":理解生命周期
很多人对容器的理解停留在 docker run 和 docker stop,但实际上,容器是一个有始有终的生命周期过程。理解它的状态流转,是高效使用 Docker 的关键。
1.1 容器的五种状态
Docker 容器在其生命周期中会经历以下状态:
bash
┌─────────────┐
│ created │ ← 容器已创建(docker create),但未启动
└──────┬──────┘
│ docker start
┌──────▼──────┐
┌─────────│ running │──────────┐
│ └──────┬──────┘ │
│ │ │
docker docker stop docker kill
pause (SIGTERM) (SIGKILL)
│ │ │
│ ┌──────▼──────┐ │
└────────►│ paused │ │
└─────────────┘ │
│
┌──────▼──────┐ │
│ exited │◄─────────┘
└──────┬──────┘
│ docker rm
┌──────▼──────┐
│ deleted │ ← 容器彻底删除,可写层被清除
└─────────────┘
-
created :容器已创建但未启动(如使用
docker create命令) -
running:容器正在运行(主进程未退出)
-
paused:容器被暂停(进程冻结,但资源保留,适用于调试场景)
-
exited:容器主进程已退出(正常退出码 0 或其他)
-
deleted:容器被彻底删除,元数据和可写层清除
docker run 本质上是 docker create + docker start + docker attach(前台模式时)的组合操作。先创建容器的可写层和网络配置,再启动容器内进程。
1.2 前台 vs 后台运行的区别
容器的运行模式分为两种:
前台模式(默认):
bash
docker run --name front-nginx nginx
此时终端会被容器日志"霸占",Ctrl+C 会向容器发送 SIGINT 信号导致容器停止。前台模式适合调试和短期任务。
后台模式(detached,使用 -d 参数):
bash
docker run -d --name back-nginx nginx
容器在后台运行,终端立即返回控制权,适合长期运行的服务。这也是生产环境的默认选择。
二、容器生命周期核心命令
2.1 创建与启动:docker run / create / start
docker run 是容器管理的核心命令,也是参数最丰富的命令。以下是日常使用中最高频的几个参数:
以下是一些实操示例:
bash
# 示例 1:后台运行 Nginx,映射 8080 端口
docker run -d --name my-web -p 8080:80 nginx
# 示例 2:交互式运行 Ubuntu 用于调试
docker run -it --name debug-box ubuntu:22.04 bash
# 示例 3:运行一个临时容器,执行完自动删除
docker run --rm alpine echo "I'll be gone after this"
补充 :对于不需要长期保留的临时容器,加上 --rm 是一个好习惯。--rm 确保容器退出后自动清理文件系统和元数据,避免日积月累的"垃圾容器"占用磁盘空间。
2.2 状态查看:docker ps
bash
# 查看正在运行的容器
docker ps
# 查看所有容器(包括已停止的)
docker ps -a
# 只显示容器 ID
docker ps -q
# 格式化输出(自定义列)
docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}"
# 过滤容器(按名称、状态、标签等)
docker ps --filter "status=exited"
docker ps --filter "name=my-web"
docker ps -a 是最高频的命令之一------它能让你一眼看到所有容器的当前状态,是排查问题的第一入口。
2.3 停止与启动:docker stop / start / restart
Docker 提供了三种停止容器的命令,它们的行为有本质区别:
bash
# 优雅停止(发送 SIGTERM,默认等待 10 秒后强制 SIGKILL)
docker stop my-web
# 强制终止(直接发送 SIGKILL,不给应用清理资源的机会)
docker kill my-web
# 暂停容器(冻结进程,资源保留,适用于调试)
docker pause my-web
docker unpause my-web
docker stop vs docker kill 的核心区别:
docker stop 的执行流程是:首先向容器内主进程(PID 1)发送 SIGTERM 信号,给应用 10 秒(默认)的"优雅关闭"时间;10 秒后如果进程仍未退出,Docker 自动发送 SIGKILL 强制终止。
生产环境中推荐使用带超时参数的 stop 命令:
bash
# 给应用 30 秒的优雅终止时间
docker stop -t 30 my-web
这样给应用留出足够时间完成正在处理的请求、关闭数据库连接、刷新缓冲区。
重启已停止的容器:
bash
# 启动已停止的容器(保留之前的配置)
docker start my-web
# 重启正在运行的容器(= stop + start)
docker restart my-web
2.4 删除容器:docker rm
bash
# 删除已停止的容器
docker rm my-web
# 强制删除运行中的容器(先 stop 再 rm)
docker rm -f my-web
# 批量删除所有已停止的容器
docker container prune
# 删除所有容器(⚠️ 危险操作,请谨慎执行)
docker rm -f $(docker ps -aq)
很多初学者会积累大量已停止的容器。定期 docker container prune 是一个好习惯------就像定期清理回收站一样,可以避免磁盘被无用数据占满。
2.5 重启策略(Restart Policy):让容器自动恢复
生产环境中,容器可能因为各种原因退出------应用崩溃、OOM、宿主机重启。重启策略决定了 Docker 引擎在容器退出后是否自动重新启动它。
Docker 提供了四种重启策略:
实战示例:
bash
# 启动时设置重启策略
docker run -d --restart=unless-stopped --name my-flask -p 5000:5000 flask-redis-counter:2.0
# 查看容器的重启策略
docker inspect my-flask --format='{{.HostConfig.RestartPolicy.Name}}'
# 输出:unless-stopped
# 统计容器的重启次数
docker inspect my-flask --format='{{.RestartCount}}'
# 输出:0(如果容器从未异常退出过)
# 修改已运行容器的重启策略
docker update --restart=always my-flask
关键补充------重启循环与安全阀:
需要特别注意的是,--restart=always 存在一个隐藏风险:如果一个容器因为配置错误(如错误的启动命令)根本无法启动,always 策略会让 Docker 不断尝试重启,形成"重启循环"。为此 Docker 内置了一个安全阀:容器必须成功运行至少 10 秒,Docker 才开始监控并应用重启策略。这意味着一个启动即退出的容器不会被反复重启,从而保护宿主机资源。此外,手动执行 docker stop 后,重启策略会被暂时挂起------直到 Docker 守护进程重启或手动执行 docker start 才会恢复。unless-stopped 之所以是生产环境最推荐的选择,正是因为它结合了"自动恢复"和"手动控制"的双重能力。
三、调试三板斧:日志、终端、详情
当容器出问题时,以下三个命令组成了一套高效的调试方法,能解决 80% 的运行时问题。
3.1 第一板斧:docker logs ------ 查看日志
日志是容器调试的首要工具,也是最直接的排查入口。
bash
# 查看全部日志
docker logs my-flask
# 实时跟踪日志(类似 tail -f)
docker logs -f my-flask
# 只看最后 50 行
docker logs --tail 50 my-flask
# 只看最近 10 分钟的日志
docker logs --since 10m my-flask
# 只看某个时间段的日志
docker logs --since 2025-01-15T10:00:00 --until 2025-01-15T11:00:00 my-flask
# 显示时间戳(排查顺序问题必备)
docker logs -t my-flask
高级技巧:多容器日志聚合
当排查涉及多个服务时,可以同时查看多个容器的日志:
bash
# 同时追踪 Flask 和 Redis 容器的日志(最后 10 行 + 实时追踪)
docker logs -f --tail 10 my-flask &
docker logs -f --tail 10 my-redis &
# 按 Ctrl+C 退出
重要提示:日志驱动与轮转
默认情况下,Docker 使用 json-file 日志驱动,将容器日志以 JSON 格式存储在宿主机磁盘上。如果不做任何配置,日志文件会无限制增长直到撑爆磁盘。生产环境中,建议在启动容器时配置日志轮转:
bash
docker run -d --name my-flask \
--log-opt max-size=10m \
--log-opt max-file=3 \
flask-redis-counter:2.0
-
max-size=10m:单个日志文件达到 10MB 后自动轮转 -
max-file=3:最多保留 3 个轮转文件(超过后自动删除最旧的)
这样日志总量上限为 30MB,既能满足日常排查需求,又不会挤占应用所需的磁盘空间。
3.2 第二板斧:docker exec ------ 进入容器内部
当日志不足以定位问题时,需要"进入现场"直接调查。
bash
# 进入容器启动交互式 shell
docker exec -it my-flask /bin/bash
# 如果容器用的是精简镜像(如 Alpine),可能没有 bash,用 sh
docker exec -it my-flask /bin/sh
# 以指定用户身份进入
docker exec -it -u root my-flask /bin/bash
# 不需要进入交互模式,直接执行单条命令
docker exec my-flask ls -la /app
docker exec my-flask env # 查看环境变量
docker exec my-flask cat /etc/hosts # 查看 hosts 文件
# 查看容器内运行的进程
docker exec my-flask ps aux
进入容器后可以做的排查操作:
-
检查应用配置文件是否正确
-
使用
curl localhost:5000/health测试内部接口 -
使用
ping redis测试网络连通性 -
查看磁盘使用情况
df -h
常见问题:某些精简镜像(如 Distroless)连 shell 都没有怎么办?
Distroless 等安全镜像移除了 shell 和包管理器,无法 docker exec 进入。排查这类容器的替代方案包括:借助 Kubernetes 的 Debug 容器(Ephemeral Container,第 48 篇会讲到);或在同一 Pod 中部署一个带 shell 的 Sidecar 容器共享进程命名空间进行调试。
3.3 第三板斧:docker inspect ------ 查看元数据
docker inspect 返回容器全部的元数据,是一个巨大的 JSON 对象,包含网络配置、挂载信息、环境变量、健康检查状态等一切信息。
bash
# 查看完整信息(输出很长)
docker inspect my-flask
# 提取特定字段(推荐)
docker inspect my-flask --format='{{.NetworkSettings.IPAddress}}'
# 输出:172.17.0.2
docker inspect my-flask --format='{{.State.Status}}'
# 输出:running
docker inspect my-flask --format='{{.Mounts}}'
# 输出:[{volume ... /app/data ...}]
docker inspect my-flask --format='{{range .Config.Env}}{{println .}}{{end}}'
# 输出所有环境变量
docker inspect my-flask --format='{{.HostConfig.RestartPolicy.Name}}'
# 输出:unless-stopped
3.4 补充调试手段
除了三板斧,还有两个在特定场景下非常实用的工具。
文件传输:docker cp
在宿主机和容器之间传输文件,常用于提取容器内的日志文件或向容器注入调试脚本:
bash
# 宿主机 → 容器
docker cp test.txt my-flask:/app/
# 容器 → 宿主机
docker cp my-flask:/app/logs.txt ./
实时事件流:docker events
监听 Docker 引擎的实时事件,适用于追踪容器状态变化的排查场景:
bash
# 实时监听容器事件(启动/停止/销毁等)
docker events
# 过滤特定容器的事件
docker events --filter container=my-flask
# 过滤特定事件类型
docker events --filter event=start --filter event=die
四、容器状态监控与资源实时观测
当怀疑容器性能瓶颈或异常资源占用时,需要实时监控工具辅助定位。
4.1 docker stats ------ 内置资源监控
Docker 内置的 docker stats 命令可以实时显示所有运行中容器的 CPU、内存、网络和磁盘 IO 使用情况:
bash
# 实时监控所有运行中的容器
docker stats
# 只监控指定容器
docker stats my-flask my-redis
# 只输出一次当前快照(不持续刷新)
docker stats --no-stream
输出示例:
bash
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
a1b2c3d4e5f6 my-flask 2.15% 45.2MiB / 3.84GiB 1.15% 1.2kB / 890B 0B / 0B
f6a7b8c9d0e1 my-redis 0.50% 8.5MiB / 3.84GiB 0.22% 890B / 1.2kB 0B / 0B
各列含义:
-
CPU %:容器使用的 CPU 百分比
-
MEM USAGE / LIMIT:当前内存使用 / 可用内存上限
-
MEM %:内存使用百分比
-
NET I/O:网络收发字节数
-
BLOCK I/O:磁盘读写字节数
docker stats 适用于快速排查"容器是不是太吃资源"的初步诊断。但如果需要长期趋势分析或告警,需要配合 Prometheus + cAdvisor 等工具(本系列第 41 篇会详细展开)。
4.2 ctop ------ 增强版容器监控
ctop 是专为容器设计的类 top 交互式监控工具,提供比 docker stats 更直观的交互界面和排序/过滤功能。
安装 ctop:
bash
# Linux(通用)
sudo wget https://github.com/bcicen/ctop/releases/download/v0.7.7/ctop-0.7.7-linux-amd64 \
-O /usr/local/bin/ctop
sudo chmod +x /usr/local/bin/ctop
# macOS
brew install ctop
# 或直接用 Docker 运行 ctop
docker run --rm -ti \
--name ctop \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
quay.io/vektorlab/ctop:latest
ctop 提供了以下实用交互功能:按 s 键选择 CPU/内存/网络等不同指标排序,按 f 键按容器名称过滤,按 o 进入单个容器详细视图,按 l 直接查看容器日志。
4.3 容器进程级调试
对于"容器内进程 CPU 飙升但不知道是哪个线程"这类问题,可以直接用 Linux 原生工具进入容器的进程命名空间进行细粒度分析:
bash
# 1. 先找到容器内进程在宿主机的 PID
PID=$(docker inspect -f '{{.State.Pid}}' my-flask)
echo $PID
# 2. 用 nsenter 进入该容器的命名空间运行命令
sudo nsenter -t $PID -n -- curl -s http://localhost:5000/health
# 3. 进入容器的完整命名空间调试(共享 PID/网络/挂载等)
sudo nsenter -t $PID -m -u -i -n -p -- /bin/bash
五、健康检查:让容器自己"报平安"
在之前的调试中,我们是从外部主动排查。但生产环境中,运维人员不能 7×24 盯着 docker ps。更可靠的做法是让容器自己报告自己是否健康------这就是 Docker 的 Healthcheck(健康检查)机制。
HEALTHCHECK 允许你指定一条命令,Docker 会周期性执行这条命令来判断容器内的应用是否真正"活着"。注意:容器进程存在(Status = Up)不等于应用健康------应用可能死锁、端口不通、数据库断连。HEALTHCHECK 正是为了区分"进程活着"和"服务可用"。
5.1 配置健康检查
有两种配置方式,Dockerfile 内置和运行时指定。
方式一:Dockerfile 中内置(推荐,与镜像绑定)
我们前面编写 Flask 应用时已经在 Dockerfile 中配置了健康检查:
bash
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
方式二:docker run 时指定(灵活,适合测试和临时容器)
bash
docker run -d --name nginx-test \
--health-cmd="curl -f http://localhost:80 || exit 1" \
--health-interval=30s \
--health-timeout=5s \
--health-retries=3 \
--health-start-period=60s \
nginx
核心参数详解:
5.2 查看健康状态
bash
# 直接查看(STATUS 列显示健康状态)
docker ps --format "table {{.Names}}\t{{.Status}}"
# 输出示例:
# NAMES STATUS
# my-flask Up 10 minutes (healthy)
# broken-app Up 5 minutes (unhealthy)
# starting-app Up 30 seconds (health: starting)
有三种健康状态:starting (启动中,还在 start-period 内或尚未完成首次检查)、healthy (检查通过)、unhealthy(连续失败 retries 次)。
bash
# 查看健康检查详细日志(排查假阳性/假阴性必备)
docker inspect --format='{{json .State.Health}}' my-flask | python3 -m json.tool
输出示例:
bash
{
"Status": "healthy",
"FailingStreak": 0,
"Log": [
{
"Start": "2025-01-15T10:00:00+08:00",
"End": "2025-01-15T10:00:02+08:00",
"ExitCode": 0,
"Output": "{\"status\":\"ok\"}"
}
]
}
-
Status:当前健康状态 -
FailingStreak:连续失败次数(0 表示健康,≥3 时触发 unhealthy) -
Log:最近 5 次检查的详细日志,ExitCode=0表示健康检查通过
5.3 健康检查的注意事项
-
命令要轻量:健康检查命令每隔 interval 秒就执行一次,不要在里面跑重 CPU/IO 的操作(比如大文件哈希计算),否则健康检查本身就是性能瓶颈。
-
注意 start-period :给应用留够启动时间。对于启动慢的应用(比如 Java 应用可能需要 30-60 秒),要把
--start-period设得足够长,否则应用还没启动完就被标记为 unhealthy。 -
Docker Swarm/Kubernetes 集成:健康检查的状态可被编排工具用于自动重启不健康的容器或 Pod。
-
Alpine 容器注意依赖 :如果容器基于 Alpine 镜像且需要
curl做健康检查,记得先在 Dockerfile 中安装它:RUN apk add --no-cache curl。
六、系统清理:避免磁盘被"垃圾"占满
开发一段时间后,磁盘空间可能被大量无用镜像、已停止容器和悬空数据卷占满。
bash
# 查看 Docker 磁盘使用情况
docker system df
# 一键清理(删除停止的容器、未使用的网络、悬空镜像)
docker system prune
# 更彻底的清理(额外删除所有未使用的镜像和数据卷)
docker system prune -a --volumes
# 单独清理构建缓存(Docker 17.06+ 可用)
docker builder prune
重要提醒 :在生产服务器上执行 docker system prune -a 前务必三思-------a 参数会删除所有未被任何容器引用的镜像,包括你可能之后需要回滚的旧版本。一个更安全的清理习惯是定期执行 docker container prune(只清理已停止容器)和 docker image prune(清理悬空镜像 <none>:<none>),把彻底清理留到有计划的维护窗口。
七、实战:Flask + Redis 计数器应用运维演示
现在让我们把学到的命令应用到贯穿本系列的 Flask + Redis 计数器应用中,走一遍完整的运维流程。
7.1 启动应用并配置重启策略
bash
# 创建网络
docker network create counter-net
# 启动 Redis(配置 unless-stopped 重启策略)
docker run -d --name my-redis --network counter-net \
--restart=unless-stopped redis:alpine
# 启动 Flask 应用(配置健康检查和日志轮转)
docker run -d --name my-flask --network counter-net \
-p 5000:5000 \
--restart=unless-stopped \
--log-opt max-size=10m --log-opt max-file=3 \
flask-redis-counter:2.0
7.2 验证运行状态
bash
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
输出:
bash
NAMES STATUS PORTS
my-flask Up 30 seconds (healthy) 0.0.0.0:5000->5000/tcp
my-redis Up 30 seconds 6379/tcp
注意 my-flask 的 STATUS 列显示了 (healthy),这是 HEALTHCHECK 在起作用。
7.3 实时监控与压力测试
打开第二个终端窗口,启动实时监控:
bash
docker stats my-flask my-redis
在第一个终端中执行压力测试:
bash
# 安装压测工具(如尚未安装)
# macOS: brew install wrk
# Ubuntu: sudo apt-get install -y wrk
# 对 Flask 应用发起 30 秒的并发请求
wrk -t4 -c20 -d30s http://localhost:5000
在 docker stats 窗口中观察 CPU 和内存的实时飙升------这就是生产环境中触发 HPA(水平自动伸缩)的底层信号。同时可以追踪日志验证请求处理:
bash
docker logs -f --tail 10 my-flask
7.4 测试健康检查和自愈
bash
# 1. 查看健康状态
docker inspect --format='{{json .State.Health}}' my-flask | python3 -m json.tool | grep Status
# 输出:"Status": "healthy"
# 2. 模拟故障:手动停止 Redis(让 /health 端点的 Redis 检查失败)
docker pause my-redis
# 3. 等待约 90 秒(30s interval × 3 retries),查看 Flask 健康状态
docker ps --format "table {{.Names}}\t{{.Status}}"
# my-flask 从 (healthy) 变为 (unhealthy)
# 4. 恢复 Redis
docker unpause my-redis
# 等待下一轮健康检查通过,状态会自动恢复为 healthy
7.5 查看重启策略的实际表现
bash
# 查看重启策略配置
docker inspect my-flask --format='{{.HostConfig.RestartPolicy.Name}}'
# 输出:unless-stopped
# 查看容器重启次数(如果为 0,表示容器一直稳定运行)
docker inspect my-flask --format='{{.RestartCount}}'
# 输出:0
# 停止 Redis 和 Flask
docker stop my-flask my-redis
# ⚠️ 危险操作演示:模拟容器 crash 的自动恢复
docker kill -s SIGKILL my-flask # 模拟进程崩溃
sleep 3
docker ps --filter name=my-flask
# 容器自动重新启动!(因为 restart policy = unless-stopped)
7.6 用 ctop 可视化查看(可选)
如果你已经安装了 ctop,可以运行:
在交互界面中按 s 选择按 CPU 排序,按 f 过滤 flask 只查看 Flask 容器,按 o 进入单容器详细视图查看 CPU/内存/网络的实时曲线。
7.7 清理测试环境
bash
# 停止并删除容器
docker rm -f my-flask my-redis
# 删除网络
docker network rm counter-net
八、命令速查表
将本篇最高频的命令整理为一张速查表,方便你随时查阅:
九、本篇总结
这一篇我们系统地掌握了容器生命周期的完整管理:
-
容器生命周期五种状态 :
created → running → paused → exited → deleted,以及docker run = create + start的本质 -
核心管理命令 :
docker run(参数详解)、docker ps(状态查看)、docker stop/kill(区别与使用场景)、docker rm(清理) -
重启策略 :
no / on-failure / always / unless-stopped四种策略的选择原则与 10 秒安全阀 -
调试三板斧 :
docker logs(日志追踪 + 轮转配置)、docker exec(进入容器)、docker inspect(元数据查询)+docker cp和docker events两个补充工具 -
资源监控 :
docker stats(内置)+ctop(第三方增强)+nsenter(进程级调试) -
健康检查 :
HEALTHCHECK的配置、状态查看(healthy / unhealthy / starting)和 troubleshooting -
系统清理 :
docker system df+docker system prune
从下一篇文章------第 7 篇:Docker 数据管理:Volume 与 Bind Mount,我们将深入容器的数据持久化方案,学会如何在容器删除后保留数据、如何在多个容器之间共享文件、如何在开发环境中实现代码热更新。
想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维!