3. Docker容器管理:从生到死的全流程掌控

容器管理,说白了就是管好你的应用进程。我见过不少新手,docker run一把梭,出了问题就抓瞎。今天咱们把容器的生命周期、资源限制、日志监控这些核心操作捋一遍。嗯,都是实战中每天要用的东西。

3.1 容器生命周期:创建、启动、停止、删除

容器的生命周期,就像人的一生:创建(出生)、启动(开始工作)、停止(休息)、删除(离开)。每个阶段都有对应的命令,咱们一个一个看。

3.1.1 创建容器(docker create)

docker create 只是创建一个容器,但不启动它。我个人习惯用这个命令做预配置,比如先挂载卷、设置网络,确认无误后再启动。

# 创建一个nginx容器,但不启动
docker create --name my-nginx -p 8080:80 nginx:latest

# 查看容器状态(Created)
docker ps -a | grep my-nginx
小技巧:docker create 可以提前分配好资源,避免启动时手忙脚乱。我在项目中经常用它来批量创建测试环境。

3.1.2 启动容器(docker start)

容器创建后,用 docker start 启动它。注意,这和 docker run 不同——docker run 是创建+启动一步到位。

# 启动刚才创建的容器
docker start my-nginx

# 查看运行状态
docker ps

为什么会有人用 docker create + docker start 而不是直接 docker run?因为有时候你需要精细控制启动时机。比如在CI/CD流水线中,先拉取镜像、创建容器,等所有依赖就绪后再统一启动。

3.1.3 停止容器(docker stop)

停止容器时,Docker会先发送SIGTERM信号,给进程10秒的优雅退出时间。如果进程没反应,再发SIGKILL强制杀死。

# 优雅停止
docker stop my-nginx

# 强制停止(不推荐)
docker kill my-nginx
避坑指南: 我曾经在生产环境直接用 docker kill 停了一个数据库容器,结果导致数据损坏。后来我养成了习惯:优先用 docker stop,给应用足够的清理时间。如果10秒不够,可以用 -t 参数延长等待时间,比如 docker stop -t 30 my-nginx

3.1.4 删除容器(docker rm)

容器停止后,它的文件系统还在。要彻底清理,用 docker rm

# 删除已停止的容器
docker rm my-nginx

# 强制删除运行中的容器
docker rm -f my-nginx

# 清理所有已停止的容器
docker container prune

你想想看,如果容器一直不删,磁盘空间迟早会爆。我在CI流水线中,每次构建完都会自动清理临时容器,不然跑几次磁盘就满了。

3.2 容器资源限制:别让一个容器吃掉所有资源

默认情况下,容器可以使用宿主机的全部CPU和内存。这很危险——一个出问题的容器可能拖垮整个机器。所以,资源限制是必须的。

3.2.1 CPU限制

Docker通过cgroup来控制CPU使用。常用参数有 --cpus--cpu-shares

参数 说明 示例
--cpus 限制容器使用的CPU核心数(精确到小数点) --cpus=1.5 表示最多用1.5个核心
--cpu-shares 设置CPU权重(相对值,默认1024) --cpu-shares=512 表示只有默认一半的优先级
--cpuset-cpus 绑定到特定CPU核心 --cpuset-cpus=0,2 只使用第0和第2个核心
# 限制容器最多使用1.5个CPU核心
docker run -d --name web --cpus=1.5 nginx

# 设置CPU权重,让重要容器优先获得CPU
docker run -d --name critical-app --cpu-shares=2048 myapp
实战经验: 我在一个微服务项目中,把核心服务(如网关)的 --cpu-shares 设成2048,非核心服务设成512。这样即使CPU争抢,核心服务也不会被饿死。

3.2.2 内存限制

内存限制比CPU更严格——超过限制会触发OOM(Out Of Memory)杀死容器。

参数 说明 示例
--memory-m 限制最大内存使用量 -m=512m 最多使用512MB
--memory-swap 限制内存+交换分区总量 --memory-swap=1g 内存+swap最多1GB
--memory-reservation 软限制,当内存充足时允许超过 --memory-reservation=256m 建议保留256MB
# 限制内存为512MB,swap为1GB
docker run -d --name app -m=512m --memory-swap=1g myapp

# 设置软限制,内存紧张时才会限制
docker run -d --name relaxed-app --memory-reservation=256m myapp
注意: 如果不设置 --memory-swap,默认情况下容器可以使用双倍内存(比如 -m=512m 时,swap也是512m)。我曾经有个Java应用因为没设swap限制,内存飙升后把宿主机swap打满了,导致其他容器响应变慢。所以,建议明确设置 --memory-swap

3.3 容器日志与监控:出了问题别慌

容器跑起来后,日志和监控就是你的眼睛。没有它们,你就像在黑暗中开车。

3.3.1 查看日志(docker logs)

docker logs 是最常用的调试命令。它默认输出容器的标准输出和标准错误流。

# 查看最近100行日志
docker logs --tail 100 my-nginx

# 实时跟踪日志(类似tail -f)
docker logs -f my-nginx

# 显示时间戳
docker logs -t my-nginx

为什么有时候 docker logs 看不到日志?因为应用可能把日志写到了文件里,而不是标准输出。我建议在Dockerfile里把日志重定向到stdout,比如 RUN ln -sf /dev/stdout /var/log/nginx/access.log

3.3.2 日志驱动配置

Docker支持多种日志驱动,默认是 json-file。生产环境建议用 journaldsyslog,方便集中管理。

日志驱动 说明 适用场景
json-file 默认,日志以JSON格式存文件 开发环境、单机测试
journald 写入systemd日志 与systemd集成的系统
syslog 发送到syslog服务器 集中式日志收集
gelf Graylog Extended Log Format 与Graylog等工具集成
# 使用journald驱动启动容器
docker run -d --log-driver journald --name app myapp

# 限制日志文件大小和数量(json-file驱动)
docker run -d --log-opt max-size=10m --log-opt max-file=3 myapp
避坑指南: 我曾经没设置日志轮转,结果一个容器跑了三天,日志文件撑爆了磁盘。后来我养成了习惯:每个容器都加上 --log-opt max-size=10m --log-opt max-file=3,确保日志不会无限增长。

3.3.3 实时监控(docker stats)

docker stats 可以实时查看容器的CPU、内存、网络、磁盘IO等指标。它就像容器的任务管理器。

# 查看所有运行中容器的资源使用
docker stats

# 查看特定容器
docker stats my-nginx my-app

# 只显示一次(不持续刷新)
docker stats --no-stream

输出示例:

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O
abc123def456   my-nginx  0.05%     12.3MiB / 512MiB      2.40%     1.2kB / 1.5kB    0B / 0B

你想想看,如果某个容器CPU一直100%,或者内存持续增长,那肯定有问题。我在生产环境会用 docker stats 配合脚本做告警,比如内存超过80%就发通知。

3.4 进入容器与执行命令:调试的终极武器

有时候容器出了问题,日志看不出来,你需要直接进到容器里面看看。这时候 docker exec 就派上用场了。

3.4.1 进入容器(docker exec -it)

# 进入容器的bash shell
docker exec -it my-nginx /bin/bash

# 如果容器没有bash,用sh
docker exec -it my-nginx /bin/sh

-it 参数很关键:-i 保持标准输入打开,-t 分配一个伪终端。少了任何一个,你可能无法正常交互。

实战经验: 我遇到过一种情况:容器里没有bash也没有sh,只有busybox。这时候可以用 docker exec -it my-container /bin/busybox sh。如果连busybox都没有,那就只能挂载卷进去看了。

3.4.2 在容器中执行单条命令

有时候你不需要交互式shell,只想执行一条命令看看结果。

# 查看容器内的环境变量
docker exec my-nginx env

# 检查进程
docker exec my-nginx ps aux

# 测试网络连通性
docker exec my-nginx ping google.com

为什么不用 docker attachattach 是连接到容器的主进程,如果你退出,容器也会停止。而 exec 是启动一个新进程,退出不影响容器运行。我个人几乎不用 attach,除非要调试容器的初始化过程。

3.4.3 从宿主机复制文件到容器

有时候你需要把配置文件或调试工具拷进容器。

# 从宿主机复制到容器
docker cp /path/to/local/file my-nginx:/path/in/container/

# 从容器复制到宿主机
docker cp my-nginx:/var/log/nginx/access.log ./
注意: docker cp 在容器停止时也能工作,但只能复制文件,不能复制目录的权限信息。我曾经用 docker cp 覆盖了容器里的配置文件,结果因为权限不对导致应用启动失败。所以,复制后最好检查一下文件权限。

3.5 综合实战:一个完整的容器管理流程

咱们把今天学的串起来,模拟一个典型的CI/CD场景:

# 1. 拉取镜像
docker pull myapp:latest

# 2. 创建容器(预配置资源限制)
docker create \
  --name myapp-prod \
  --cpus=2 \
  -m=1g \
  --memory-swap=2g \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  -p 8080:8080 \
  myapp:latest

# 3. 启动容器
docker start myapp-prod

# 4. 检查运行状态
docker ps | grep myapp-prod

# 5. 查看资源使用
docker stats --no-stream myapp-prod

# 6. 查看日志
docker logs --tail 50 myapp-prod

# 7. 如果需要调试,进入容器
docker exec -it myapp-prod /bin/bash

# 8. 停止容器(优雅退出)
docker stop -t 30 myapp-prod

# 9. 删除容器
docker rm myapp-prod

这个流程我在自动化部署脚本里用了无数次。你想想看,每一步都有对应的命令,出了问题也能快速定位。嗯,这就是Docker容器管理的精髓——把复杂的事情拆成一个个可控的步骤。

最后说一句: 容器管理不是一蹴而就的。我刚开始用Docker时也经常忘记加资源限制,或者日志爆满。但只要你养成习惯,把每个命令都当成生产环境来对待,慢慢就会形成肌肉记忆。好了,这一章就到这里,下一章咱们聊聊Docker网络——容器之间怎么通信,怎么暴露服务到外部。