Docker 与容器 列表全部文章
Docker 与容器
Docker 与容器第 1 / 5 篇

Docker 笔记(五)速查篇:常用命令清单与 12 个常见场景案例

前四篇是按知识点讲的,这篇是反过来用的:命令忘了、线上出事了,回来翻。上半部分按镜像、容器、网络、存储、Compose、系统清理分组整理常用命令,能直接复制;下半部分是 12 个真实场景案例,每个给出现象、怎么查、结论三步。

更多

前四篇是按知识点讲的,这篇是反过来用的:命令忘了、线上出事了,回来翻。上半部分按场景分组整理常用命令,能直接复制;下半部分是 12 个真实场景案例,每个给出现象、怎么查、结论三步。

一、镜像

docker build -t myapp:1.0 .                       # 构建
docker build --no-cache -t myapp:1.0 .            # 跳过缓存重新构建
docker build --build-arg VERSION=1.0 .            # 传构建参数
docker images                                     # 本地镜像列表
docker image ls --digests                         # 带摘要
docker history myapp:1.0                          # 看分层与每层大小
docker inspect myapp:1.0                          # 完整元数据
docker tag myapp:1.0 registry/team/myapp:1.0      # 打标签
docker push registry/team/myapp:1.0               # 推送
docker pull registry/team/myapp:1.0               # 拉取
docker save myapp:1.0 | gzip > myapp.tar.gz       # 导出(离线交付)
docker load < myapp.tar.gz                        # 导入
docker rmi myapp:1.0                              # 删除镜像
docker image prune -a                             # 删掉没被引用的镜像
想干什么命令
看镜像为什么这么大docker history --human myapp:1.0,或用 dive myapp:1.0
只保留最近几个版本docker image prune -a --filter "until=168h"(保留 7 天内)
扫漏洞trivy image myapp:1.0

二、容器

docker run -d --name app -p 127.0.0.1:8080:8080 --restart unless-stopped myapp:1.0
docker run -it --rm --network app-net busybox sh  # 一次性调试容器
docker ps                                         # 在跑的
docker ps -a                                      # 所有(含已退出)
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
docker start/stop/restart app
docker stop --time 30 app                         # 给 30 秒优雅退出
docker kill -s SIGTERM app                        # 只发信号不硬杀
docker rm -f app                                  # 强制删除
docker logs -f --tail 200 app                     # 实时跟日志
docker logs --since 30m app                       # 最近 30 分钟
docker logs --until 2025-10-01T12:00:00 app       # 时间区间
docker exec -it app sh                            # 进容器(alpine 用 sh)
docker exec app env                               # 看环境变量
docker cp ./conf.yml app:/etc/app/                # 拷文件进容器
docker cp app:/var/log/app.log ./                 # 拷出来
docker stats --no-stream                          # 资源占用快照
docker top app                                    # 容器内进程
docker inspect app --format '{{.State.ExitCode}} {{.State.OOMKilled}}'
docker update --memory 2g --cpus 2 app            # 运行中调整限制
docker wait app                                   # 阻塞到容器退出,脚本里等结果用
docker diff app                                   # 容器层改了哪些文件
想干什么命令
看容器为什么退了docker inspect app --format '{{.State.Error}}' + docker logs app
看健康检查状态docker inspect app --format '{{.State.Health.Status}}'
看重启了几次docker inspect app --format '{{.RestartCount}}'
容器里没 shelldocker run --rm --network container:app --pid container:app nicolaka/netshoot ...

三、网络

docker network ls
docker network create app-net                     # 自定义 bridge,支持容器名互访
docker network inspect app-net                    # 看谁在里面、子网分配
docker network connect app-net redis              # 运行中加进来
docker network disconnect app-net redis
docker network prune                              # 清理没用的网络
docker exec app getent hosts mysql                # 测容器内名字解析
docker exec app sh -c 'nc -zv mysql 3306'         # 测端口通不通
iptables -t nat -L DOCKER -n                      # 宿主机上看端口映射规则

关键差异:默认 bridge 不支持容器名解析,自定义网络才支持。所以多容器协作一律先 docker network create。

四、存储

docker volume create mysqldata
docker volume ls
docker volume inspect mysqldata                   # 看真实路径
docker volume rm mysqldata
docker volume prune                               # 清理没被引用的卷(先看清楚)
docker system df -v                               # 容器/镜像/卷各占多少
docker run -v mysqldata:/var/lib/mysql mysql:8.0                     # 命名卷
docker run -v /opt/conf/my.cnf:/etc/my.cnf:ro mysql:8.0              # 只读挂载配置
docker run --mount type=bind,src=/opt/conf,dst=/etc/app,ro myapp:1.0 # 等价写法

五、Compose

docker compose up -d                              # 后台启动
docker compose up -d --build                      # 重新构建镜像再启
docker compose ps                                 # 各服务状态与健康检查
docker compose logs -f --tail 100 app
docker compose restart app
docker compose exec app sh
docker compose config                             # 展开最终配置,排查变量没生效
docker compose pull && docker compose up -d       # 更新
docker compose down                               # 停并删容器(保留卷)
docker compose down -v                            # 连卷一起删(危险)

六、系统与清理

docker system df                                  # 总览
docker system df -v                               # 明细
docker system prune                               # 清理停止的容器、无用网络、悬空镜像
docker system prune -a --volumes                  # 连未使用镜像和卷一起清(危险)
docker events --since 1h                          # 这一小时发生了什么
docker events --since 30m --filter container=app  # 只看某个容器
docker info                                       # 存储驱动、cgroup 版本等环境信息
docker version

清理前先跑一次 docker system df -v,确认要删的是什么。-a --volumes 会把没被任何容器引用的数据卷一起删掉——数据库卷如果容器停了,就属于"未引用"。

七、排障组合拳

# 1. 是不是被内核 OOM 干掉的(exit 137 第一件事)
dmesg | grep -i -E 'killed|oom' | tail -20
docker inspect app --format '{{.State.OOMKilled}}'

# 2. 资源现状
docker stats --no-stream
free -m && df -h

# 3. 容器里啥命令都没有时,借网络命名空间排查
docker run --rm --network container:app nicolaka/netshoot ss -lntp
docker run --rm --network container:app nicolaka/netshoot curl -v localhost:8080

# 4. 直接在宿主机上看容器进程(不用进容器)
docker top app
ps -ef --forest | grep -A5 $(docker inspect app --format '{{.State.Pid}}')

# 5. 看容器到底改了哪些文件
docker diff app

八、常见案例说明

下面 12 个案例都是真实会遇到的,每个按「现象 → 怎么查 → 结论」三步给。

案例 1:容器一启动就退出,状态是 Exited (0)

docker ps -a                       # STATUS: Exited (0) 2 seconds ago
docker logs app                    # 往往什么都没有

结论:容器里没有前台进程。PID 1 跑完就退出,容器就结束。常见于启动命令是 bash、或用了后台方式启动(service nginx start)。改成前台:nginx -g 'daemon off;',交互调试加 -it。

案例 2:exit 137,容器莫名其妙没了

docker inspect app --format '{{.State.ExitCode}} {{.State.OOMKilled}}'
dmesg | grep -i -E 'killed|oom' | tail

结论:被 SIGKILL,八成是内存超限。要么 --memory 给小了,要么应用有堆外内存泄漏,要么没配 JVM 容器感知(堆按宿主机内存算)。完整排查见Java 搬进 Docker 就水土不服与堆明明没满,容器却被 OOMKilled。

案例 3:端口映射了,外部访问不到

docker ps                          # PORTS 列显示 0.0.0.0:8080->8080/tcp 才算真映射
ss -lntp | grep 8080               # 宿主机上有没有在监听
curl 127.0.0.1:8080                # 先在宿主机本机试
iptables -t nat -L DOCKER -n       # 看 DNAT 规则

结论:三种原因——①应用只监听 127.0.0.1(容器里也是 localhost,外部进不来,要监听 0.0.0.0);②EXPOSE 写了但 docker run 没加 -p;③防火墙/安全组没放。

案例 4:容器里连不上宿主机的 MySQL

ip addr show docker0               # 通常是 172.17.0.1
docker exec app sh -c 'nc -zv 172.17.0.1 3306'

结论:容器里的 localhost 是容器自己,不是宿主机。Linux 用 docker0 地址(172.17.0.1),Docker Desktop 用 host.docker.internal,或者干脆 --network host。同时要确认 MySQL 的 bind-address 不是 127.0.0.1。五个不通场景见容器里 curl localhost 永远拒绝连接。

案例 5:挂载目录 Permission denied

ls -ln /opt/data                   # 宿主机上看数字 uid
docker exec app id                 # 容器里看当前 uid

结论:两边 uid 对不上。用 --user $(id -u):$(id -g) 启动,或在 Dockerfile 里建同 uid 用户。SELinux 环境(CentOS/RHEL)还要加 :z:-v /opt/data:/data:z。详见挂载目录 Permission denied 及两个难兄难弟。

案例 6:日志时间差 8 小时,定时任务对不上点

docker exec app date
docker exec app cat /etc/timezone

结论:容器默认 UTC。挂 -v /etc/localtime:/etc/localtime:ro 或设 -e TZ=Asia/Shanghai。Java 应用还有三层要对齐:容器 TZ、JVM -Duser.timezone、JDBC 连接串的 serverTimezone。

案例 7:磁盘被容器日志吃满

docker system df -v                                   # 看谁占得多
du -sh /var/lib/docker/containers/*/*-json.log | sort -h | tail

结论:json-file 驱动默认不轮转。给容器配 max-size / max-file,并在 /etc/docker/daemon.json 里设全局兜底(只对新建容器生效,老容器要重建)。复盘见根分区 100%:一个没配 max-size 的 json.log。

案例 8:构建镜像特别慢,改一行代码都要重装依赖

docker build --no-cache -t myapp:1.0 .     # 先确认是不是缓存问题
docker history myapp:1.0

结论:COPY 顺序错了。应该是先拷 pom.xml/package.json 装依赖,再拷源码。另外检查 .dockerignore——构建上下文太大也会拖慢。

案例 9:改了代码/配置,容器里没生效

docker diff app                            # 看容器层有没有变化
docker inspect app --format '{{.Mounts}}'  # 确认挂载对不对

结论:分三种——①文件在镜像里,改宿主机没用,必须重新 build;②bind mount 的文件改了立刻生效,但 Java 没热部署就得重启容器;③Dockerfile 里对该目录声明过 VOLUME,匿名卷会屏蔽镜像内容。

案例 10:两个容器互相 ping 不通 / 连不上

docker network inspect app-net              # 确认两个容器在同一个网络
docker exec app getent hosts mysql          # 名字能不能解析

结论:默认 bridge(docker0)不做容器名解析。建自定义网络 docker network create app-net,两个容器都加进去,就能用容器名互访。--link 已废弃。

案例 11:容器每 40 秒重启一次,重启风暴

docker inspect app --format '{{.RestartCount}} {{.State.ExitCode}}'
docker compose logs --since 10m app | tail -50

结论:启动失败 + restart: always 的死循环。典型是内存给小了,应用启动到一半被 OOM Kill。改 on-failure:5 或 unless-stopped,把资源限制调够,配健康检查让编排器知道它没起来。见容器每 40 秒重生一次。

案例 12:磁盘满了,怎么安全清理

docker system df -v                                  # 第一步永远是看清楚
docker container prune                               # 只清停止的容器
docker image prune                                   # 只清悬空镜像(<none>)
docker image prune -a --filter "until=168h"          # 清 7 天前的镜像
docker volume prune                                  # 最后再考虑卷(先看 ls!)
docker builder prune                                 # 清构建缓存,往往能回收几 G

结论:按上面顺序从安全到危险逐个来。docker volume prune 之前务必 docker volume ls 确认——停掉的容器引用的卷也算"未使用",误删就是数据丢失。真正的兜底是生产篇里的日志轮转,而不是事后清理。

备忘

  • 排障顺序:看 exit code → 看 OOMKilled → dmesg → logs → inspect → stats。
  • exit 137 = 被杀(多因内存);143 = 正常 SIGTERM;127 = 命令不存在;126 = 没执行权限。
  • 清理磁盘按 容器 → 悬空镜像 → 老镜像 → 构建缓存 → 卷 的顺序来,df -v 先行。
  • 容器里没 shell 时,用 netshoot 借网络命名空间,比重新构建镜像快得多。
  • 更多真实事故的排查过程在生产问题排查手记总目录。
— 感谢读到这里 —发现错误,或想补充?告诉我 →
TURN KNOWLEDGE INTO SOMETHING REAL

有想法,也想把它做出来?

从技术方案到上线交付,聊聊你的下一个项目。

聊聊需求

一起聊聊

搜索文章

输入关键词,按标题和摘要即时筛选。

浏览分类文章归档

阅读设置

字号
行宽
专注模式

专注模式会隐藏页头与侧栏,只留正文。设置保存在当前浏览器。

稍后阅读

保存在当前浏览器,留给下一次安静阅读。

总浏览 -- 次 · 总访客 -- 人 · 今日浏览 -- 次 · 今日访客 -- 人