docker —— 容器日常运维

运维工具手册 06-容器与编排 8.4 KB 原文

docker —— 容器日常运维

分类:06 容器与编排 适用:Docker Engine 20.10 / 23 / 24 / 25 / 26 / 27 一句话:起停、进容器、看日志、查资源、清空间,容器运维的高频操作全在这里。

1. 它解决什么问题

  • 容器起不来,docker ps -a 看到 Exited (1),为什么?
  • 进容器里看配置、抓包、跑调试命令。
  • 磁盘满了,是镜像、容器还是日志占的?
  • 容器资源占用异常,谁在吃 CPU/内存。

2. 状态与信息

docker ps                       # 运行中的容器
docker ps -a                    # 含已退出(排查必用)
docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'
docker ps -q | wc -l            # 容器数量
docker inspect <name>           # 完整 JSON 配置
docker inspect -f '{{.State.Status}} {{.State.ExitCode}}' <name>
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <name>
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' <name>
docker stats                    # 实时资源(CPU/内存/网络/IO)
docker stats --no-stream
docker stats --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}'
docker top <name>               # 容器内进程
docker port <name>              # 端口映射
docker diff <name>              # 容器文件系统改动
docker events --since 1h        # 事件流(排查"谁把容器停了")

3. 生命周期

docker run -d --name app -p 8080:80 -v /data:/data --restart unless-stopped nginx:1.25
docker start app
docker stop app                 # SIGTERM → 10s → SIGKILL
docker stop -t 30 app           # 给 30 秒优雅退出
docker restart app
docker kill app                 # 直接 SIGKILL(慎用)
docker rm app                   # 删除已停止容器
docker rm -f app                # 强制删除运行中
docker pause / unpause app

docker run 常用参数

参数 作用
-d 后台运行
-it 交互 + TTY(进容器调试)
--name 容器名
-p 8080:80 端口映射(宿主:容器)
-P 随机映射所有 EXPOSE 端口
-v /host:/container 绑定挂载
-v vol:/data 命名卷
--mount type=bind,src=...,dst=... 更明确的挂载写法
-e KEY=VALUE 环境变量
--env-file=.env 从文件读环境变量
--network 指定网络
--restart no / on-failure / always / unless-stopped
--memory / --cpus 资源限制
--log-driver / --log-opt 日志驱动与大小限制
--rm 退出后自动删除
--privileged 特权模式(⚠️ 近乎宿主 root)
--cap-add / --cap-drop 细粒度能力
--user 指定 UID
--health-cmd 健康检查
-w 工作目录

4. 看日志

docker logs app                          # 全部(可能巨大)
docker logs --tail 100 app
docker logs -f --tail 50 app             # 实时
docker logs --since 30m app
docker logs --since 2026-11-05T14:00:00 app
docker logs -t app                       # 带时间戳
docker logs app 2>&1 | grep ERROR

docker logs 只显示容器的 stdout/stderr。容器内写到文件的日志它看不到,需要 docker exec 或挂载卷。

5. 进容器与拷贝

docker exec -it app /bin/bash            # 进容器(推荐)
docker exec -it app sh                   # 精简镜像没 bash
docker exec app cat /etc/nginx/nginx.conf
docker exec -u root app bash             # 以 root 进
docker exec -it app env                  # 看环境变量
docker exec -it app ps aux               # 看进程
docker exec -it app ss -lntp             # 看端口

docker cp app:/etc/nginx/nginx.conf ./nginx.conf    # 拷出
docker cp ./nginx.conf app:/etc/nginx/nginx.conf    # 拷入
docker cp app:/var/log/nginx ./logs

exec 与 attach 的区别:exec 新开进程,attach 接上主进程的 stdin/stdout(退出可能带走容器),日常用 exec。

6. 镜像

docker images
docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}'
docker pull nginx:1.25
docker tag app:latest registry.example.com/ns/app:1.2.3
docker push registry.example.com/ns/app:1.2.3
docker rmi <image>
docker rmi -f <image>                    # 强制(有容器引用时危险)
docker history nginx:1.25                # 看层
docker image prune -a                    # 清理未使用镜像(⚠️ 会删掉所有无容器引用的)
docker save app:1.2.3 -o app.tar
docker load -i app.tar
docker export app -o app-rootfs.tar      # 导出容器文件系统
docker import app-rootfs.tar newimage:snap

离线环境迁移镜像:

docker save nginx:1.25 | gzip > nginx.tar.gz
scp nginx.tar.gz host:/tmp/
ssh host 'gunzip -c /tmp/nginx.tar.gz | docker load'

7. 网络

docker network ls
docker network inspect bridge
docker network create --subnet 172.20.0.0/16 appnet
docker network connect appnet app
docker network disconnect appnet app

# 容器名互通(自定义网络内自动 DNS)
docker run -d --name db --network appnet mysql:8
docker run -d --name api --network appnet myapi
docker exec api ping db

模式:

模式 特点
bridge(默认) 独立网络栈,端口需映射
host 与宿主共享网络,性能最好,端口冲突风险
none 无网络
container:<name> 共享另一容器网络栈
自定义 bridge 内置 DNS,容器名互访

查看容器 IP:

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' app
docker exec app hostname -i

8. 存储与磁盘

docker volume ls
docker volume inspect <vol>
docker volume create data
docker volume rm <vol>
docker system df                 # 总览:镜像/容器/卷/构建缓存
docker system df -v              # 明细

清理(按危险程度递增):

docker container prune                    # 删除已停止容器
docker image prune                        # 删除悬空镜像(<none>)
docker image prune -a                     # 删除所有无容器使用的镜像 ⚠️
docker volume prune                       # 删除未使用卷 ⚠️ 数据可能丢
docker builder prune                      # 构建缓存
docker network prune
docker system prune                       # 上面合集(不含卷)
docker system prune -a --volumes          # ⚠️⚠️ 大扫除,生产禁用

磁盘被日志撑满(最常见的生产问题):

du -sh /var/lib/docker/* 2>/dev/null | sort -h
du -sh /var/lib/docker/containers/*/*-json.log | sort -h | tail
# 临时清空某容器日志(不要 rm 文件,用 truncate)
truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.log

全局限制日志大小,编辑 /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}
systemctl reload docker      # 或 restart

该配置只对新创建的容器生效,已有容器需要重建。

9. 实战场景

9.1 容器反复退出,四步定位

docker ps -a | grep app                       # 1. 看退出码
docker inspect -f '{{.State.ExitCode}} {{.State.Error}} {{.State.OOMKilled}}' app
docker logs --tail 100 app                    # 2. 看日志
docker inspect app | jq '.[0].Config'         # 3. 看启动命令与环境
docker run -it --entrypoint sh app:tag        # 4. 绕过入口进容器手动试

常见退出码:

码 含义
0 正常退出(前台进程结束)
1 应用报错
126 命令不可执行
127 命令不存在
137 SIGKILL(OOM 或 docker kill)
139 段错误
143 SIGTERM(正常停止)

OOMKilled=true → 内存超限,调 --memory 或查泄漏。

9.2 磁盘满

df -h /
docker system df -v
# 依次清理:停止的容器 → 悬空镜像 → 构建缓存 → 旧卷
docker container prune -f
docker image prune -f
docker builder prune -f

9.3 容器内没有调试工具

# 方案一:用带工具的镜像共享网络与 PID
docker run -it --rm --network container:app --pid container:app nicolaka/netshoot

# 方案二:拷贝静态编译的工具进去
docker cp /usr/local/bin/busybox app:/tmp/
docker exec app /tmp/busybox netstat -tlnp

# 方案三:在宿主机用 nsenter 进容器命名空间
PID=$(docker inspect -f '{{.State.Pid}}' app)
nsenter -t $PID -n -p -m --preserve-credentials

9.4 验证容器资源限制

docker run -d --name t --memory 256m --cpus 0.5 nginx
docker stats t --no-stream
docker inspect -f '{{.HostConfig.Memory}} {{.HostConfig.NanoCpus}}' t

9.5 导出容器当前状态做镜像

docker commit app app:snapshot-$(date +%F)

只适合临时取证;正式构建请用 Dockerfile,commit 产物不可复现。

10. 常见坑

  1. docker ps 看不到已退出的容器:排查必用 -a。
  2. 容器退出是因为主进程结束:容器生命周期 = PID 1 的生命周期,前台进程一退出容器就停。加 tail -f /dev/null 只是权宜。
  3. -v 挂载覆盖镜像内容:宿主空目录挂到 /etc/nginx 会把配置全覆盖,容器起不来。
  4. --restart always 与 docker stop:手动 stop 后不会自动起,但 Docker 重启会。unless-stopped 更符合直觉。
  5. 日志不轮转把磁盘写满:见第 8 节,默认 json-file 无上限。
  6. docker system prune -a 删掉正在用的镜像:只删"无容器引用"的,但会拖慢下次启动。
  7. -p 127.0.0.1:8080:80 只绑本地:不加 IP 默认绑 0.0.0.0,公网暴露风险。
  8. 容器时区不对:加 -e TZ=Asia/Shanghai 或挂载 /etc/localtime。
  9. docker exec 进不去:容器用的是 scratch/distroless 镜像,没有 shell,改用 --target 调试镜像或 nsenter。
  10. --privileged 滥用:等于给容器宿主 root 权限,安全审计必查。
  11. SELinux 导致挂载权限拒绝:加 :z 或 :Z 标签(-v /data:/data:z)。
  12. docker cp 不跟随软链接:拷出的是链接本身。
  13. 镜像 latest 不可追溯:生产必须用具体 tag 或 digest。
  14. overlay2 空间不释放:删除容器/镜像后仍需 docker system prune,被删除文件若被进程持有也不会释放(用 lsof +L1 查)。

11. 速查表

docker ps -a                                   # 所有容器
docker ps -a --format 'table {{.Names}}\t{{.Status}}'
docker logs -f --tail 100 <c>
docker exec -it <c> sh
docker inspect -f '{{.State.ExitCode}} {{.State.OOMKilled}}' <c>
docker stats --no-stream
docker cp <c>:/path ./ 
docker run -d --name x -p 8080:80 -e TZ=Asia/Shanghai --restart unless-stopped img:tag
docker network ls / inspect / create
docker volume ls
docker system df -v
docker container prune -f
docker image prune -f
docker builder prune -f
docker save img:tag | gzip > img.tar.gz
docker load -i img.tar.gz
PID=$(docker inspect -f '{{.State.Pid}}' <c>); nsenter -t $PID -n -p -m
docker —— 容器日常运维 - 知识库 - 起航社区