跑 Docker 容器不难,跑久了之后的"找日志、看磁盘、查端口、还原启动命令"才是真正花时间的地方。这一篇把日常 80% 的运维命令收成一份"速查表",按主题组织。
阅读对象:日常需要管理 Docker 容器、想要一份"忘了就能查"的命令清单
覆盖范围:日志查询(时间窗口 + grep)、磁盘分析、容器 IP / 启动命令 / 进程 PID 反查、网络断开、批量清理
一、容器日志查询
1.1 基础查询
1
2
3
4
5
6
7
8
9
10
11
| # 持续查看(类似 tail -f)
docker logs -f <container>
# 看最后 50 条
docker logs -f --tail=50 <container>
# 过滤关键词
docker logs -f --tail=50 <container> | grep "Oss"
# 上下文过滤:-C 前后多少行,-B 前多少行,-A 后多少行
docker logs -f --tail=50 <container> | grep "Oss" -C 10
|
1.2 时间窗口查询
1
2
3
4
5
6
7
8
9
10
11
12
| # 查从 2016-07-01 起的最后 10 条
docker logs --since="2016-07-01" --tail=10 mynginx
# 查最近 30 分钟
docker logs --since 30m <container>
# 查指定时间区间(ISO 8601)
docker logs --since='2024-04-23T08:00:00Z' --until='2024-04-23T08:34:00Z' base
# 1 小时前 ~ 8.5 小时前
docker logs --since "$(date -d '1 hour ago' +'%Y-%m-%dT%H:%M:%SZ')" \
--until "$(date -d '8 hour 30 minute ago' +'%Y-%m-%dT%H:%M:%SZ')" base
|
Why 用 ISO 8601:--since / --until 接受 Go duration(如 30m、2h)和 RFC 3339 时间戳。跨时区排查时必须用 RFC 3339 + Z 后缀,否则会以 daemon 本地时区解析。
1.3 日志文件位置
Docker 默认把日志写到本地 JSON 文件:
1
| /var/lib/docker/containers/<容器id>/<容器id>-json.log
|
问题:JSON 日志没有自动轮转,长期运行的容器日志能把磁盘撑爆。两种解法:
- 配置全局日志轮转(推荐,daemon.json 级别):
1
2
3
4
5
6
7
| {
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}
|
- 手动清空(应急用):
1
2
3
4
5
| # 容器运行时
truncate -s 0 /var/lib/docker/containers/<container-id>/<container-id>-json.log
# 或更直接
cat /dev/null > /var/lib/docker/containers/<container-id>/<container-id>-json.log
|
二、磁盘占用分析
1
2
3
4
5
| # 一句话看整体
docker system df
# 详细看每个镜像 / 容器 / 卷
docker system df -v
|
典型输出:
1
2
3
4
5
6
| REPOSITORY TAG IMAGE ID CREATED SIZE SHARED SIZE UNIQUE SIZE
go-scratch 1.0.0 45579d645abb 31 minutes ago 0B 0B 0B
easysoft/zentao 16.4 a07d6a23404c 2 weeks ago 699.7MB 0B 699.7MB
postgres 14.1-alpine 2302d5724f71 3 months ago 209MB 0B 209MB
sonarqube 9.2.1-community cb35e2de836c 3 months ago 549.1MB 0B 549.1MB
gitlab/gitlab-ee 13.12.9-ee.0 4ccbcfe81d60 7 months ago 2.435GB 0B 2.435GB
|
关键字段:
SHARED SIZE:被多个容器共享的只读层(多个镜像公用)UNIQUE SIZE:当前镜像独占的可写层(这是清理时的真实可释放空间)- 容器层"容器一停就消失",但镜像层不会自动清——长期累积的
<none>:<none> 是元凶
三、容器网络:查所有 IP
1
2
3
4
5
6
7
| # 所有运行中容器
docker inspect --format='{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
$(docker ps -aq)
# docker-compose 启动的(多网络时 .IPAddress 拿到的是第一个)
docker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
$(docker ps -aq)
|
四、通过 PID 反查容器
1
2
3
4
5
6
7
8
| # 知道宿主机上某个 PID 是哪个容器的
docker ps -q | xargs docker inspect --format '{{.State.Pid}}, {{.Name}}' | grep "<PID>"
# 反向:根据启动命令里的特征字符串找容器
pgrep -f "/app/start-scms.sh" | xargs -I {} sudo cat /proc/{}/cgroup
# 输出长这样(取 docker-xxx.scope 那行):
# 0::/system.slice/docker-a89a19896cf24e4a60745870ac9fc7a1691c3fa6ff539d6acfc5094154bb44db.scope
|
典型场景:服务 A 调用服务 B 出问题,但服务 B 在 K8s 集群里名字很难找——从宿主机 cgroup 文件反查最快。
五、还原容器的启动命令
1
2
3
4
5
6
7
8
9
10
11
| # 标准工具:cucker/get_command_4_run_container
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
cucker/get_command_4_run_container:1.3 <container-name>
# 输出示例:
# docker run -d \
# --name mysql01 \
# --env MYSQL_ROOT_PASSWORD=py123456 \
# -p 13306:3306/tcp \
# --restart=always \
# mysql
|
Why 这个工具:容器跑久了之后,谁、用什么参数起的经常说不清。docker inspect 能看到参数但格式反人类;这个工具一键还原成 docker run 命令。
类似的工具还有 assaflavie/runlike 和 joinsunsoft/runcommand,用法类似。
六、批量操作 Exited 容器
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 启动所有 Exited 的容器
docker ps -a | grep Exited | awk '{print $1}' | xargs docker start
# 删所有 Exited 的
docker ps -a | grep Exited | awk '{print $1}' | xargs docker rm
# 按 status 过滤
docker ps -f "status=exited"
docker rm -f $(docker ps -f "status=exited" -q)
# 强制删除 + 清理网络占用
docker rm -f <container-id>
docker network disconnect --force bridge <container-id>
|
七、查端口占用
1
2
3
4
5
6
7
8
9
10
11
12
| # 看所有 docker-proxy 监听的端口
netstat -nlp | grep docker-proxy | awk '{print $4}' | sort
# host 模式下,docker-proxy 不会监听,得用 pid 找
docker top <container>
netstat -anp | grep <pid>
lsof -i | grep <pid>
# 一行命令找容器进程的端口
for i in $(docker top <container> | awk '{print $2}'); do
netstat -tunlp | grep $i
done
|
八、清理无用的卷 / 镜像
1
2
3
4
5
6
7
8
9
| # 删 dangling 镜像(仓库名 / tag 为 <none>)
docker rmi $(docker images -f "dangling=true" -q)
# 删所有未挂载的卷(**危险**,生产前必须确认)
docker volume prune
docker volume ls -f "dangling=true" -q | xargs --no-run-if-empty docker volume rm
# 一键清空(**非常危险**,会把所有停止的容器、镜像、网络、未用卷全删)
docker system prune -af --volumes
|
生产警示:docker system prune -af --volumes 是"一键核弹"——会删所有 dangling 镜像、所有停止的容器、所有未挂载卷、所有未用网络。CI 节点上偶尔用一下尚可,生产数据库节点永远不要跑。
九、查看磁盘与目录映射
1
2
3
4
5
6
7
| # 容器对应的 overlay2 目录
docker inspect <container> -f {{.GraphDriver.Data.MergedDir}}
# 输出:/var/lib/docker/overlay2/<id>/merged
# 容器元数据目录
ls /var/lib/docker/containers/<container-id>
# 里面有 config.v2.json / hostconfig.json / *-json.log
|
十、根据状态查询
1
2
3
4
5
| # 只看退出的
docker ps -f "status=exited"
# 只看健康的(需要 healthcheck)
docker ps -f "health=healthy"
|
十一、一键全清(应急用)
1
2
3
4
5
6
7
| # 删所有容器(不管状态)
docker rm -f $(docker ps -a -q)
# 清镜像 / 网络 / 卷
docker system prune -af --volumes
# 注意:上面会**删 dangling 镜像 + 所有停止的容器 + 所有未用卷**
|
十二、要点回顾
- 日志查询用
--since / --until + ISO 8601 时间戳最稳 - 磁盘分析用
docker system df -v 看 UNIQUE SIZE,那是真实可释放空间 - 容器 IP用
inspect + range .NetworkSettings.Networks,比单字段 IPAddress 兼容多网络 - 还原启动命令用
cucker/get_command_4_run_container 或 runlike - 批量清理 Exited用
docker ps -a | grep Exited | awk | xargs docker system prune -af --volumes 是核弹——生产环境谨慎
十三、小结
日常运维最花时间的不是"启动容器",而是"出问题后查根因"。这份命令清单的核心理念是:
- 查日志 → ISO 8601 + grep 上下文
- 查磁盘 →
system df -v 看 UNIQUE SIZE - 查 IP / 命令 / 进程 →
inspect + Go template + cgroup - 批量操作 →
awk + xargs 串起来 - 清理 →
dangling=true 比 prune -af 安全
把这些命令攒成 shell 函数或 alias,比每次翻笔记快:
1
2
3
4
5
6
| # ~/.bashrc
alias dl='docker logs -f --tail=100'
alias dlf='docker logs -f --tail=100 | grep'
alias ddf='docker system df -v'
alias drmexit='docker rm -f $(docker ps -f "status=exited" -q)'
alias drmi='docker rmi $(docker images -f "dangling=true" -q)'
|
下一步:日志和磁盘都是"被动响应"问题。下一步是"主动预防"——daemon.json 里配好 log-opts 限制日志大小、给所有容器加 healthcheck、用 restart: always 守好关键服务,把运维压力从"救火"变成"看仪表盘"。
参考资料