Featured image of post Docker 日常运维速查:日志、磁盘、端口、容器自省

Docker 日常运维速查:日志、磁盘、端口、容器自省

容器日志查询与时间窗口过滤、磁盘空间分析、容器 IP / 启动命令 / 进程查找、网络断开与重启的完整命令手册

跑 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(如 30m2h)和 RFC 3339 时间戳。跨时区排查时必须用 RFC 3339 + Z 后缀,否则会以 daemon 本地时区解析。

1.3 日志文件位置

Docker 默认把日志写到本地 JSON 文件:

1
/var/lib/docker/containers/<容器id>/<容器id>-json.log

问题:JSON 日志没有自动轮转,长期运行的容器日志能把磁盘撑爆。两种解法:

  1. 配置全局日志轮转(推荐,daemon.json 级别):
1
2
3
4
5
6
7
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "50m",
    "max-file": "3"
  }
}
  1. 手动清空(应急用):
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/runlikejoinsunsoft/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 镜像 + 所有停止的容器 + 所有未用卷**

十二、要点回顾

  1. 日志查询--since / --until + ISO 8601 时间戳最稳
  2. 磁盘分析docker system df -vUNIQUE SIZE,那是真实可释放空间
  3. 容器 IPinspect + range .NetworkSettings.Networks,比单字段 IPAddress 兼容多网络
  4. 还原启动命令cucker/get_command_4_run_containerrunlike
  5. 批量清理 Exiteddocker ps -a | grep Exited | awk | xargs
  6. docker system prune -af --volumes 是核弹——生产环境谨慎

十三、小结

日常运维最花时间的不是"启动容器",而是"出问题后查根因"。这份命令清单的核心理念是:

  • 查日志 → ISO 8601 + grep 上下文
  • 查磁盘system df -v 看 UNIQUE SIZE
  • 查 IP / 命令 / 进程inspect + Go template + cgroup
  • 批量操作awk + xargs 串起来
  • 清理dangling=trueprune -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 守好关键服务,把运维压力从"救火"变成"看仪表盘"。

参考资料

使用 Hugo 构建
主题 StackJimmy 设计