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