Docker容器运维故障排查命令和操作方法

描述

问题背景

容器化已经成了现代运维的标准配置。不管你是用 Docker 还是 Kubernetes,容器技术的普及让"环境一致性问题"大大减少,但同时也带来了一类新的运维挑战:容器自身的问题排查、镜像管理、网络配置、存储卷管理、资源限制、日志采集,以及容器重启后数据丢失、镜像拉取失败、容器间通信异常等典型故障。

很多从虚拟机时代过来的运维工程师,对容器的熟悉程度还不够。虚拟机出问题了,SSH 登录、top、df、journalctl 一套组合拳基本能定位问题。容器出问题了,该用什么命令?docker ps 只能看到容器的表面状态,docker logs 只能看到主进程的日志,docker exec 能进容器但进了也不知道该看什么。

本文面向有一定 Linux 基础的运维工程师,系统讲解容器化运维中最常用的故障排查命令和操作方法,涵盖 Docker 容器管理、镜像管理、日志采集、资源监控、网络排查、存储卷管理,以及 Kubernetes 环境下 Pod 的常用操作。内容偏实战,拿来就能用。

Docker 容器基础操作

容器状态与进程分析

容器启动后,最常用的第一步是确认容器状态。查看所有容器(包括已停止的):

 

docker ps -a

 

-a 参数显示所有容器,不带 -a 只显示正在运行的容器。输出包含容器 ID、镜像名、命令、状态、端口映射、容器名称。

查看单个容器的详细状态:

 

docker inspect <容器名或ID>

 

docker inspect 输出是 JSON 格式,包含容器的完整配置:网络设置、存储卷、环境变量、启动命令、状态信息、资源限制等。常用 docker inspect 配合 jq 提取特定字段:

 

# 查看容器的重启次数
docker inspect -f '{{.RestartCount}}' <容器名>

# 查看容器的退出代码(容器退出后)
docker inspect -f '{{.State.ExitCode}}' <容器名>

# 查看容器的 IP 地址
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <容器名>

# 查看容器的日志驱动配置
docker inspect -f '{{.HostConfig.LogConfig}}' <容器名>

 

查看容器内运行的所有进程(包括非主进程):

 

docker top <容器名>

 

这相当于在容器内执行 ps aux,可以看容器内所有进程。如果发现容器内只有一个 PID 为 1 的进程在运行,说明这个容器没有启动任何子服务(比如 nginx 容器里只跑了 nginx 主进程,这是正常的;如果是一个 Java 应用,通常会有多个线程在进程内,不会有多个进程)。

进入容器内部

有时候需要进入容器内部进行排查。需要根据容器内的 shell 环境选择合适的工具。

 

# 使用 docker exec(推荐,会自动分配伪终端)
docker exec -it <容器名> /bin/bash

# 如果容器没有 bash(例如精简的 alpine 镜像),用 sh
docker exec -it <容器名> /bin/sh

# 如果容器没有 shell 或者已经卡死,使用 nsenter 进入容器的 namespace
# 先找到容器的 PID
PID=$(docker inspect --format '{{.State.Pid}}' <容器名>)
# 进入容器的 namespace
nsenter -t $PID -m -u -i -n

 

nsenter 的优势是不依赖容器内的 shell 环境,可以直接进入容器的主进程 namespace。如果容器已经卡死(docker exec 都无法进入),nsenter 是最后的选择。

进入容器后,常用的排查命令和物理机一样:top、ps、ls、df、netstat、ss、cat /proc/*/status。如果容器内工具不全,可以先安装(apt-get install procps net-tools 等)。

容器日志

容器日志是排查问题的第一手资料。

 

# 查看容器日志(持续跟踪)
docker logs -f <容器名>

# 查看最近 200 行日志
docker logs --tail 200 <容器名>

# 查看某个时间点之后的日志
docker logs --since "2024-06-01T0300" <容器名>

# 查看最近 30 分钟的日志
docker logs --since 30m <容器名>

# 带时间戳查看
docker logs -t <容器名>

# 搜索日志中的错误关键词
docker logs <容器名> | grep -i error

 

如果容器启动时没有指定日志驱动,日志默认存在宿主机的 /var/lib/docker/containers/<容器ID>/<容器ID>-json.log。如果磁盘空间紧张,可以清理这个目录下的日志文件(注意不要删正在写入的文件)。

如果容器日志量极大,可以配置日志轮转。在 /etc/docker/daemon.json 中配置:

 

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "10"
  }
}

 

这会将单个日志文件限制在 100MB,最多保留 10 个日志文件,超过后自动删除旧文件。修改后需要 systemctl reload docker 生效。

容器资源监控

查看运行中容器的资源使用情况:

 

docker stats

 

docker stats 默认实时显示所有运行容器的 CPU、内存、网络 I/O、磁盘 I/O 情况。按 Ctrl+C 退出。

只查看特定容器:

 

docker stats <容器名1> <容器名2>

 

不自动滚动,持续输出:

 

docker stats --no-stream <容器名>

 

查看容器详细的资源限制配置:

 

docker inspect <容器名> | jq '.[0].HostConfig'

 

关注 Memory、NanoCpus、CpuPeriod、CpuQuota、CpuShares 等字段,这些是容器的资源硬限制。

容器网络排查

容器无法对外通信,或者容器之间无法互通,是常见问题。

查看容器的网络模式和网络连接:

 

# 查看容器的网络信息
docker inspect -f '{{range .NetworkSettings.Networks}}{{.NetworkID}} {{.IPAddress}}{{end}}' <容器名>

# 查看容器的端口映射
docker port <容器名>

# 查看所有网络
docker network ls

# 查看特定网络的详细信息
docker network inspect <网络名>

 

排查容器网络连通性:

 

# 进入容器 ping 其他容器或外网地址
docker exec -it <容器名> ping <目标IP>

# 进入容器查看网络路由
docker exec -it <容器名> ip route

# 查看容器内的网络连接状态
docker exec -it <容器名> netstat -tunapl

# 抓包分析(如果容器内有 tcpdump)
docker exec -it <容器名> tcpdump -i eth0 -w /tmp/capture.pcap

 

如果发现容器 IP 变了但业务不通,可能是容器重启后 IP 变化导致的。在生产环境中,推荐使用自定义 bridge 网络或 overlay 网络(Docker Swarm)或 host 网络(Kubernetes),不要依赖容器 IP。

Docker 镜像管理

查看和清理镜像

镜像管理不当是磁盘空间耗尽的常见原因。

 

# 列出所有镜像
docker images

# 列出镜像 ID(适合脚本处理)
docker images -q

# 查看镜像的构建历史
docker history <镜像名>

# 查看镜像的详细信息
docker inspect <镜像名>

 

清理未使用的镜像:

 

# 删除 dangling 镜像(无 tag 的镜像层)
docker image prune

# 删除所有未使用的镜像
docker image prune -a

# 删除指定镜像
docker rmi <镜像名或ID>

 

清理未使用的容器、网络、构建缓存(一次性清理):

 

docker system prune -a

 

镜像拉取和推送

拉取镜像时的问题排查:

 

# 拉取镜像
docker pull <镜像名>:<标签>

# 拉取镜像时显示详细进度
docker pull -i <镜像名>

# 重新拉取镜像(强制覆盖本地)
docker pull --force <镜像名>

 

如果拉取失败,常见原因及排查方法:

网络问题:检查宿主机的 DNS 解析和网络连通性。

 

# 测试 Docker Hub 连通性
curl -I https://registry-1.docker.io/v2/

# 测试私有镜像仓库连通性
curl -I https://your-private-registry.com/v2/

# 检查 DNS 解析
docker run --rm busybox nslookup registry-1.docker.io

 

认证问题:如果拉取私有镜像仓库需要认证,确保已经执行过 docker login。

 

# 登录镜像仓库
docker login <仓库地址>

# 查看已登录的仓库
cat ~/.docker/config.json

 

镜像构建

构建一个 Dockerfile:

 

docker build -t <镜像名>:<标签> -f Dockerfile .

 

构建过程中的问题排查:

 

# 构建时指定构建缓存(如果构建卡住,可能是构建步骤依赖网络下载)
docker build --no-cache -t <镜像名> .

# 构建时传入构建参数
docker build --build-arg VERSION=1.0.0 -t <镜像名> .

 

Docker 存储卷管理

容器存储卷相关的问题是数据丢失、存储空间耗尽、权限错误。

 

# 列出所有存储卷
docker volume ls

# 查看存储卷详情
docker volume inspect <卷名>

# 查看某个容器的存储卷挂载情况
docker inspect -f '{{.Mounts}}' <容器名>

 

清理未使用的存储卷:

 

# 删除未使用的存储卷
docker volume prune

# 删除指定存储卷(容器必须先删除)
docker volume rm <卷名>

 

如果存储卷权限问题导致容器启动失败:

 

# 查看存储卷在宿主机的实际路径
docker inspect -f '{{range .Mounts}}{{.Source}}:{{.Destination}}{{end}}' <容器名>

# 修改宿主机上的目录权限
chmod 777 /var/lib/docker/volumes/<卷名>/_data

# 或者在容器启动时指定用户
docker run -u $(id -u):$(id -g) -v /data:/data <镜像名>

 

Docker 常用运维操作

容器启停和重启

 

# 启动已停止的容器
docker start <容器名>

# 停止运行中的容器
docker stop <容器名>

# 重启容器
docker restart <容器名>

# 暂停容器(进程被冻结)
docker pause <容器名>

# 恢复暂停的容器
docker unpause <容器名>

# 杀死容器(强制停止)
docker kill <容器名>

 

docker stop 会向容器内的 PID 1 进程发送 SIGTERM 信号,让容器优雅退出(有 grace period)。docker kill 直接发送 SIGKILL,容器立即被强制终止,可能导致数据丢失。

容器生命周期管理

查看容器启动命令:

 

docker inspect -f '{{.Config.Cmd}}' <容器名>
docker inspect -f '{{.Config.Entrypoint}}' <容器名>

 

重建一个容器(保留存储卷,只修改配置或镜像):

 

docker create --name <容器名> 
  --restart unless-stopped 
  -p 8080:80 
  -v /data:/data 
  <镜像名>

docker start <容器名>

 

将运行中的容器提交为新镜像(不推荐用于持久化数据存储,容器文件系统变更后提交):

 

docker commit <容器名> <新镜像名>:<标签>

 

容器健康检查

Dockerfile 中可以定义 HEALTHCHECK:

 

HEALTHCHECK --interval=30s --timeout=3s --retries=3 
  CMD curl -f http://localhost:8080/health || exit 1

 

手动检查容器健康状态:

 

docker inspect -f '{{.State.Health}}' <容器名>

 

Kubernetes Pod 常用操作

Pod 基础状态查看

 

# 查看 namespace 下所有 Pod
kubectl get pods -n 

# 查看 Pod 详细信息
kubectl describe pod  -n 

# 查看 Pod 的 YAML 配置
kubectl get pod  -n  -o yaml

# 查看 Pod 的日志
kubectl logs  -n 

# 持续查看日志
kubectl logs -f  -n 

# 查看上一个退出容器的日志(Pod 重启过)
kubectl logs  -n  --previous

 

进入 Pod 内部

 

# 在 Pod 内执行命令
kubectl exec  -n  -- ls /tmp

# 进入 Pod 的 shell(需要容器内有 shell)
kubectl exec -it  -n  -- /bin/bash

# 如果是特权容器,可以进入宿主机(非常危险,生产环境慎用)
kubectl exec -it  -n  -- nsenter --target=1 -m -u -i -n

 

Pod 资源使用查看

 

# 查看 Pod 的资源请求和限制
kubectl get pod  -n  -o jsonpath='{.spec.containers[0].resources}'

# 如果集群安装了 metrics-server,可以查看实时资源使用
kubectl top pods -n 
kubectl top pod  -n 

# 查看所有 Pod 的资源使用并排序(CPU 或内存)
kubectl top pods -n  --sort-by='.cpu.usage'
kubectl top pods -n  --sort-by='.memory.usage'

 

Pod 调度和节点问题排查

 

# 查看 Pod 的调度结果(Node 选择)
kubectl get pod  -n  -o wide

# 查看 Node 的资源分配情况
kubectl describe node  | grep -A 10 "Allocatable"

# 查看 Pod 无法调度原因
kubectl describe pod  -n  | grep -A 20 "Events"

 

Pod 无法调度的常见原因:

资源不足:Node 没有足够的 CPU 或内存满足 Pod 的 requests。解决方案:增加 Node、减少 Pod 副本数、降低 Pod 的资源请求。

污点(Taints):Node 上有污点,Pod 没有对应的容忍。检查 kubectl describe node | grep Taints。

PVC 未绑定:Pod 依赖的 PersistentVolumeClaim 还没有绑定到 PV,Pod 卡在 Pending。检查 kubectl get pvc -n

Pod 驱逐和重启

 

# 手动删除 Pod(会触发 ReplicaSet 重新创建)
kubectl delete pod  -n 

# 强制删除 Terminating 状态的 Pod(不走 graceful shutdown)
kubectl delete pod  -n  --grace-period=0 --force

# 如果 Pod 卡在 Terminating 是因为 finalizers,检查是否有悬空的资源引用
kubectl patch pod  -n  -p '{"metadata":{"finalizers":[]}}' --type=merge

 

排查 Running 但不工作的 Pod

Pod 状态是 Running,但服务不响应,是一类隐蔽性很强的问题。

检查 Pod 内部进程状态:

 

kubectl exec -it  -n  -- ps aux

 

检查容器健康检查状态:

 

# 查看 Pod 的探针配置
kubectl get pod  -n  -o jsonpath='{.spec.containers[0].livenessProbe}'

# 查看为什么 Pod 被重启(最近的重启原因)
kubectl describe pod  -n  | grep -A 5 "Last State"

 

检查 Pod 的网络连通性(需要在 Pod 内执行):

 

kubectl exec -it  -n  -- curl -v localhost:<端口>/health

 

检查 Service 是否正确指向了 Pod:

 

# 查看 Service 的 endpoints(Pod IP 应该在 endpoints 列表中)
kubectl get endpoints  -n 

# 如果 endpoints 为空,说明 Service 没有匹配到 Pod
# 检查 Service 的 selector 和 Pod 的 labels 是否匹配
kubectl describe service  -n 
kubectl get pods -n 

 

故障排查实战

容器内 DNS 解析异常

Pod 内无法解析服务名,但能 ping 通 IP。这是高频问题。

 

# 在 Pod 内测试 DNS 解析
kubectl exec -it  -n  -- nslookup kubernetes.default
kubectl exec -it  -n  -- cat /etc/resolv.conf

# 查看 CoreDNS Pod 是否正常运行
kubectl get pods -n kube-system -l k8s-app=kube-dns

# 查看 CoreDNS 日志
kubectl logs -n kube-system -l k8s-app=kube-dns -c coredns

 

DNS 解析异常的原因:CoreDNS Pod 不正常(查看日志定位)、Pod 的 /etc/resolv.conf 配置错误、NetworkPolicy 阻止了 DNS 流量、KubeDNS 服务 IP 变化但 Pod 未重启刷新。

容器内无法访问外网

Pod 内 ping 不通外网,但服务间通信正常。

 

# 查看 Node 的 iptables 规则(需要权限)
# 查看 NAT 表中的 Docker 规则
iptables -t nat -L -n | grep POSTROUTING

# 检查 Pod 的网络策略
kubectl get networkpolicy -n 

# 检查是否有带宽限制(Kubernetes NetworkPolicy 不支持带宽限制,需要 CNI 插件支持)

 

常见原因:宿主机 NAT 规则被破坏、Pod 网络模式错误、NetworkPolicy 限制出站流量、CNI 插件 bug(重启 CNI 插件有时能解决问题)。

镜像拉取失败

Pod 卡在 ImagePullBackOff 状态。

 

# 查看拉取失败原因
kubectl describe pod  -n  | grep -A 10 "Events"

# 常见错误:
# 1. 镜像名拼写错误
# 2. 私有镜像仓库未配置 imagePullSecrets
# 3. 认证信息过期(docker login 到期)
# 4. 镜像 Tag 不存在(默认用 latest,容易踩坑)
# 5. 网络不通(registry 无法访问)

 

修复方法:如果是私有镜像,在 Pod 或 ServiceAccount 中配置 imagePullSecrets;如果是认证信息过期,重新 docker login 并更新 Secret;如果是镜像不存在,检查镜像仓库。

容器启动后立即退出

Pod 卡在 CrashLoopBackOff 状态,每次启动后立即退出。

 

# 查看容器退出日志
kubectl logs  -n  --previous

# 查看详细退出原因
kubectl describe pod  -n  | grep -A 20 "State:"

# 常见原因:
# 1. 启动命令(command/entrypoint)写错了
# 2. 配置文件缺失或路径错误
# 3. 健康检查失败(livenessProbe 连续失败)
# 4. 依赖的服务不可达(数据库、Redis 未启动)
# 5. 权限问题(挂载卷无权限写入)

 

存储卷挂载异常

Pod 卡在 ContainerCreating 状态,且挂载了 PVC。

 

# 检查 PVC 状态
kubectl get pvc -n 

# 检查 PV 状态
kubectl get pv

# 查看 PVC 绑定的 PV 详情
kubectl describe pvc  -n 

# 查看 Pod 的挂载配置
kubectl describe pod  -n  | grep -A 20 "Volumes"

# 检查存储类是否正常
kubectl get storageclass
kubectl describe storageclass 

 

常见问题:存储类缺失(StorageClass 不存在)、云盘/网络存储未挂载上(查看云厂商控制台)、PV 和 PVC 的 accessMode 不匹配、StorageClass 的 reclaimPolicy 为 Delete 导致 PV 被误删。

Pod 内存 OOM

容器内存使用超过 limits,kubelet 会强制终止容器(OOMKilled)。

 

# 查看 Pod 的重启原因
kubectl describe pod  -n  | grep -A 5 "Last State"

# 查看容器被 OOMKilled 的次数
kubectl get pod  -n  -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}'

# 查看 Node 上 kubelet 日志(需要 SSH 到 Node 上)
journalctl -u kubelet | grep -i oom

# 查看 Node 内存状态
kubectl describe node  | grep -A 5 "Allocatable"

 

内存 OOM 的原因:应用内存泄漏(最常见)、JVM 堆内存设置过大(-Xmx 超过容器 limits)、正常业务流量增加但 limits 未调整。修复方向:增加 limits 临时缓解、修复代码内存泄漏、优化 JVM GC 参数。

常用命令清单

Docker 容器管理:

 

docker ps -a                          # 列出所有容器
docker inspect <容器名>                # 查看容器详细信息
docker logs -f <容器名>                # 查看容器日志
docker exec -it <容器名> /bin/bash   # 进入容器
docker stats                          # 查看容器资源使用
docker top <容器名>                   # 查看容器内进程
docker start/stop/restart <容器名>    # 启停容器

 

Docker 镜像管理:

 

docker images                         # 列出本地镜像
docker pull <镜像名>                  # 拉取镜像
docker rmi <镜像名>                   # 删除镜像
docker image prune                    # 清理 dangling 镜像
docker system prune -a               # 清理所有未使用资源

 

Docker 存储卷和网络:

 

docker volume ls                      # 列出存储卷
docker volume inspect <卷名>          # 查看存储卷详情
docker network ls                     # 列出网络
docker network inspect <网络名>       # 查看网络详情

 

Kubernetes Pod 操作:

 

kubectl get pods -n       # 列出 Pod
kubectl describe pod  -n   # Pod 详情
kubectl logs -f  -n        # Pod 日志
kubectl exec -it  -n  -- /bin/bash  # 进入 Pod
kubectl top pod  -n         # Pod 资源使用
kubectl delete pod  -n     # 删除 Pod

 

Kubernetes 调度和网络:

 

kubectl get endpoints  -n   # 查看 Service endpoints
kubectl get pvc -n                 # 查看 PVC 状态
kubectl get events -n  --sort-by='.lastTimestamp'  # 查看事件

 

风险提醒

容器化运维中,有一些操作风险较高,需要特别谨慎。

docker exec 进入生产容器。docker exec 会消耗宿主机的资源,如果容器已经处于高负载状态,docker exec 可能进一步加剧负载。如果只需要查看日志,优先用 docker logs 而不是 exec。

删除运行中的容器。删除容器前确保应用已经停止服务,停止命令会等待 grace period 结束。如果应用不支持 graceful shutdown(不处理 SIGTERM 信号),直接删除会导致请求中断。

docker system prune -a。这个命令会删除所有未使用的镜像、容器、网络,是彻底的大清理。生产环境执行前必须确认所有重要镜像已经打过 tag 标识。

kubectl delete pod --force。强制删除 Pod 会立即释放资源,如果 Pod 有持久化数据且未正确同步到外部存储,会导致数据丢失。

删除 PVC。删除 PVC 之前,必须确认数据已经备份。如果 PV 的 reclaimPolicy 是 Delete,删除 PVC 会同时删除 PV 和盘上的数据(云存储可能不可恢复)。

修改运行中容器的配置。Docker 容器一旦创建,大部分配置(端口映射、存储卷挂载、环境变量)无法直接修改,必须重建容器。Kubernetes Pod 的镜像、镜像版本、端口可以通过滚动更新修改,但大部分 spec 字段不可修改。

Pod 的 livenessProbe 设置过激进。如果 livenessProbe 的 initialDelaySeconds 设置过小或 periodSeconds 过短,健康检查失败会导致容器不断重启,业务永远起不来。建议 initialDelaySeconds 大于应用启动时间,failureThreshold 不少于 3。

验证方式

完成容器操作后,需要验证以下内容。

容器/ Pod 启动成功:

 

# Docker
docker ps | grep <容器名>  # 确认容器状态为 Up

# Kubernetes
kubectl get pods -n  | grep   # 确认状态为 Running

 

业务端口监听正常:

 

# Docker
docker exec -it <容器名> netstat -tlnp | grep <端口>

# Kubernetes(需要进入 Pod 或用 port-forward)
kubectl exec -it  -n  -- netstat -tlnp

 

健康检查通过:

 

# Kubernetes 确认 Pod 的 Ready 条件为 True
kubectl get pods -n  | grep 
# 输出中 Ready 列应为 1/1 或更高

 

日志无异常:

 

# Docker
docker logs --tail 50 <容器名> | grep -i error

# Kubernetes
kubectl logs -f  -n  | grep -i error

 

资源使用在限制范围内:

 

# Docker
docker stats --no-stream <容器名>

# Kubernetes
kubectl top pod  -n 

 

回滚方案

容器化操作如果出错,回滚比虚拟机更容易,但也需要知道正确的回滚路径。

Docker 容器配置错误回滚:

 

# 删除错误容器,保留存储卷,重新创建
docker stop <容器名>
docker rm <容器名>
# 修复配置后重新创建
docker create --name <容器名> -p 8080:80 -v /data:/data <镜像名>
docker start <容器名>

 

Kubernetes Deployment 回滚:

 

# 查看 Deployment 的历史版本
kubectl rollout history deployment/ -n 

# 回滚到上一个版本
kubectl rollout undo deployment/ -n 

# 回滚到指定版本
kubectl rollout undo deployment/ -n  --to-revision=<版本号>

# 确认回滚状态
kubectl rollout status deployment/ -n 

 

Kubernetes 镜像版本回滚:

 

# 修改 Deployment 的镜像版本
kubectl set image deployment/ <容器名>=<镜像名>:<旧版本> -n 

# 或者编辑 YAML
kubectl edit deployment/ -n 

 

清理失败的容器/Pod:

 

# Docker:删除处于 Restarting 或 Exited 状态的容器
docker ps -a | grep Exit | awk '{print $1}' | xargs docker rm

# Kubernetes:删除处于 Error/Evicted 状态的 Pod(注意 Evicted 是被调度器驱逐的,可能需要先解决调度问题)
kubectl get pods -n  | grep -E "Error|Evicted|ImagePullBackOff|CrashLoopBackOff" | awk '{print $1}' | xargs kubectl delete -n 

 

Kubernetes Pod 高级操作

Pod 调度优化与调试

在 Kubernetes 中,Pod 的调度是由 kube-scheduler 完成的。理解调度过程对于排查"Pod 一直 Pending"的问题至关重要。

查看 Pod 的调度详情:

 

# 查看 Pod 调度决策(适用于还未调度的 Pod)
kubectl get pod  -n  -o wide

# 查看 Pod 的调度事件
kubectl describe pod  -n  | grep -A 10 "Events"

# 查看调度器的决策日志(需要 kube-scheduler 日志)
kubectl logs -n kube-system -l component=kube-scheduler --tail=100

 

Pod 调度失败的常见原因:

资源不足:Node 上没有足够的 CPU 或内存满足 Pod 的 requests。需要增加 Node 或减少 Pod 资源请求。

污点不容忍:Node 上有 Taints(如 NoSchedule),Pod 没有对应的 Tolerations。需要添加匹配的 Toleration。

节点选择器/亲和性不匹配:Pod 的 nodeSelector 或 nodeAffinity 没有匹配到任何 Node。

Pod 反亲和性冲突:Pod 的 podAntiAffinity 规则导致没有合适的 Node 可选。

PVC 无法绑定:依赖的 PVC 没有找到匹配的 PV,Pod 卡在 "Waiting for PVC" 状态。

检查 Node 的污点和 Pod 的容忍:

 

# 查看 Node 的污点
kubectl describe node  | grep -A 5 "Taints"

# 查看 Pod 的容忍
kubectl get pod  -n  -o jsonpath='{.spec.tolerations}'

 

Pod 探针配置与调优

Kubernetes 的健康检查有两种探针:

livenessProbe:判断容器是否存活,如果失败会重启容器。

readinessProbe:判断容器是否准备好接收流量,如果失败会从 Service endpoints 中摘除。

不合理的探针配置是生产环境的常见故障原因:

 

spec:
  containers:
-name:backend
    image:myregistry/backend:20240601
    livenessProbe:
      httpGet:
        path:/health
        port:8080
      initialDelaySeconds:30   # 等待容器启动完成的时间
      periodSeconds:10        # 检查频率
      timeoutSeconds:3        # 超时时间
      failureThreshold:3      # 连续失败多少次才重启
    readinessProbe:
      httpGet:
        path:/ready
        port:8080
      initialDelaySeconds:5
      periodSeconds:5
      timeoutSeconds:2
      failureThreshold:2       # 连续失败多少次才摘除
      successThreshold:1      # 成功后即认为 Ready

 

initialDelaySeconds 设置过短是最常见的错误。如果应用启动需要 30 秒,但 initialDelaySeconds 设置为 5,容器会在启动过程中被判定为不健康并重启,永远无法启动成功。

failureThreshold 和 periodSeconds 的关系也需要注意。如果 failureThreshold=3, periodSeconds=10,意味着一旦探针开始失败,需要等待 3×10=30 秒才会触发重启。如果应用在 30 秒内启动完成,探针会恢复成功,不会触发重启。

Pod 资源配额与 QoS

Pod 的资源声明决定了它的 QoS(Quality of Service)等级:

 

# Guaranteed(最高优先级)
resources:
requests:
    cpu:500m
    memory:512Mi
limits:
    cpu:500m
    memory:512Mi# requests 和 limits 相等

# Burstable(中等优先级)
resources:
requests:
    cpu:100m
    memory:128Mi
limits:
    cpu:1000m
    memory:1Gi   # requests 和 limits 不相等

# BestEffort(最低优先级)
# 没有声明任何 resources

 

QoS 等级决定了 kubelet 在资源压力下的驱逐顺序:BestEffort 最先被驱逐,Guaranteed 最后被驱逐。在内存紧张的 Node 上,Guaranteed Pod 几乎不会被驱逐。

Pod 生命周期与状态机

Pod 有几种状态,理解状态机有助于排查卡在特定状态的 Pod:

 

Pending → Running → Succeeded
              ↓
           Failed

Pending:Pod 已被 Kubernetes 系统接受,但镜像尚未拉取或调度未完成
Running:Pod 已绑定到 Node,所有容器已创建且至少有一个正在运行
Succeeded:所有容器已正常终止(exit code 0),不会重启
Failed:所有容器已终止,且至少有一个以非零退出码终止
Unknown:无法获取 Pod 状态(通常是 API Server 和 kubelet 通信问题)
CrashLoopBackOff:容器启动失败后被重启(BackOff = 指数退避重启间隔)
ImagePullBackOff:镜像拉取失败(可能是镜像不存在、私有仓库认证失败、网络问题)
OOMKilled:容器内存使用超过 limits,被 kubelet 强制终止

 

Pod 日志与事件分析

Pod 的事件(Events)是排查调度和启动问题的重要信息:

 

# 查看 Pod 的所有事件
kubectl describe pod  -n  | grep -A 20 "Events"

# 查看最近的事件(所有命名空间)
kubectl get events -n  --sort-by='.lastTimestamp' | tail -20

# 查看特定类型的事件
kubectl get events -n  --field-selector involvedObject.name=

# 统计各类型事件数量
kubectl get events -n  --no-headers | awk '{print $11}' | sort | uniq -c | sort -rn

 

常见的事件类型和含义:

Scheduled:Pod 已被调度到 Node

Pulling:正在拉取镜像

Pulled:镜像拉取成功

Created:容器已创建

Started:容器已启动

Unhealthy:健康检查失败

FailedScheduling:调度失败

FailedCreatePodSandBox:创建 Pod 沙箱失败(常见于网络插件问题)

容器化故障排查实战

容器内 DNS 解析异常

Pod 内无法解析服务名,但能 ping 通 IP。这是高频问题。

 

# 在 Pod 内测试 DNS 解析
kubectl exec -it  -n  -- nslookup kubernetes.default
kubectl exec -it  -n  -- cat /etc/resolv.conf

# 查看 CoreDNS Pod 是否正常运行
kubectl get pods -n kube-system -l k8s-app=kube-dns

# 查看 CoreDNS 日志
kubectl logs -n kube-system -l k8s-app=kube-dns -c coredns

 

DNS 解析异常的原因:

CoreDNS Pod 不正常(查看日志定位)

Pod 的 /etc/resolv.conf 配置错误(nameserver IP 不对)

NetworkPolicy 阻止了 DNS 流量(UDP 53 端口)

KubeDNS 服务 IP 变化但 Pod 未重启刷新(Pod 不会自动感知 ConfigMap 变化)

集群网络插件问题(Calico/Cilium/Flannel 等)

修复 DNS 配置的临时方案:

 

# 删除 Pod 让它重新调度,Pod 会读取最新的 resolv.conf
kubectl delete pod  -n 

# 如果是 CoreDNS 问题,尝试重启 CoreDNS(滚动重启)
kubectl rollout restart deployment/coredns -n kube-system

 

容器内无法访问外网

Pod 内 ping 不通外网,但服务间通信正常。

 

# 在 Pod 内测试外网连通性
kubectl exec -it  -n  -- ping 8.8.8.8
kubectl exec -it  -n  -- curl -I https://www.baidu.com

# 查看 Node 的 iptables 规则(需要权限)
iptables -t nat -L -n | grep POSTROUTING

# 检查 Pod 的网络策略
kubectl get networkpolicy -n 

# 检查 NAT 规则是否正确
iptables -t nat -L -n | grep -E "KUBE-POSTROUTING|MASQUERADE"

 

常见原因:

宿主机 NAT 规则被破坏(重启 network 服务可能恢复)

Pod 网络模式错误(使用了 host 网络但 SNAT 配置不对)

NetworkPolicy 限制了出站流量

CNI 插件 bug(重启 CNI 插件有时能解决问题:kubectl rollout restart daemonset/calico-node -n kube-system)

VPC/安全组规则变更阻止了出站流量

镜像拉取失败

Pod 卡在 ImagePullBackOff 状态。

 

# 查看拉取失败原因
kubectl describe pod  -n  | grep -A 10 "Events"

# 常见错误:
# 1. 镜像名拼写错误
# 2. 私有镜像仓库未配置 imagePullSecrets
# 3. 认证信息过期(docker login 到期)
# 4. 镜像 Tag 不存在(默认用 latest,容易踩坑)
# 5. 网络不通(registry 无法访问)

 

修复方法:

 

# 如果是私有镜像,需要创建 imagePullSecrets
kubectl create secret docker-registry myregistry-secret 
  --docker-server=registry.example.com 
  --docker-username=admin 
  --docker-password=password 
  --docker-email=admin@example.com 
  -n 

# 在 Pod 或 ServiceAccount 中引用这个 Secret
# 方法1:在 Pod spec 中引用
spec:
  imagePullSecrets:
  - name: myregistry-secret

# 方法2:在 ServiceAccount 中引用(所有使用这个 SA 的 Pod 都生效)
# kubectl patch serviceaccount default -n  -p '{"imagePullSecrets":[{"name":"myregistry-secret"}]}'

# 如果认证信息过期,更新 Secret
kubectl delete secret myregistry-secret -n 
kubectl create secret docker-registry myregistry-secret 
  --docker-server=registry.example.com 
  --docker-username=admin 
  --docker-password=newpassword 
  --docker-email=admin@example.com 
  -n 

 

容器启动后立即退出

Pod 卡在 CrashLoopBackOff 状态,每次启动后立即退出。

 

# 查看容器退出日志
kubectl logs  -n  --previous

# 查看详细退出原因
kubectl describe pod  -n  | grep -A 20 "State:"

# 常见原因:
# 1. 启动命令(command/entrypoint)写错了
# 2. 配置文件缺失或路径错误
# 3. 健康检查失败(livenessProbe 连续失败)
# 4. 依赖的服务不可达(数据库、Redis 未启动)
# 5. 权限问题(挂载卷无权限写入)
# 6. OOMKilled(内存超限)

 

逐步排查的方法:

 

# 1. 检查容器内进程退出码
kubectl logs  -n  --previous 2>&1

# 2. 如果是权限问题,修改 Pod 安全上下文或存储卷权限
# 查看 Pod 的安全上下文
kubectl get pod  -n  -o jsonpath='{.spec.securityContext}'

# 3. 如果是健康检查问题,先禁用探针验证服务本身是否正常
# 编辑 deployment 临时注释掉 livenessProbe 和 readinessProbe

# 4. 如果是配置文件问题,检查挂载的配置
kubectl exec -it  -n  -- ls -la /path/to/config

 

存储卷挂载异常

Pod 卡在 ContainerCreating 状态,且挂载了 PVC。

 

# 检查 PVC 状态
kubectl get pvc -n 
kubectl describe pvc  -n 

# 检查 PV 状态
kubectl get pv

# 查看 Pod 的挂载配置
kubectl describe pod  -n  | grep -A 20 "Volumes"

# 检查存储类是否正常
kubectl get storageclass
kubectl describe storageclass 

# 如果是云存储,检查云控制台
# AWS: 检查 EBS 卷状态
# GCP: 检查 PD 状态
# 阿里云: 检查云盘状态

 

常见问题:

存储类缺失(StorageClass 不存在)——需要创建 StorageClass

云盘/网络存储未挂载上(查看云厂商控制台)

PV 和 PVC 的 accessMode 不匹配(如 ReadWriteOnce 和 ReadOnlyMany 冲突)

StorageClass 的 reclaimPolicy 为 Delete 导致 PV 被误删——建议改为 Retain

PVC 的 selector 匹配不上 PV

Pod 内存 OOM

容器内存使用超过 limits,kubelet 会强制终止容器(OOMKilled)。

 

# 查看 Pod 的重启原因
kubectl describe pod  -n  | grep -A 5 "Last State"

# 查看容器被 OOMKilled 的次数
kubectl get pod  -n  -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}'

# 查看 Node 上 kubelet 日志(需要 SSH 到 Node 上)
journalctl -u kubelet | grep -i oom

# 查看 Node 内存状态
kubectl describe node  | grep -A 5 "Allocatable"

 

内存 OOM 的原因:

应用内存泄漏(最常见)——需要通过堆转储分析

JVM 堆内存设置过大(-Xmx 超过容器 limits)——需要调大 limits 或减小 -Xmx

正常业务流量增加但 limits 未调整——需要增加 limits

多个容器竞争内存——需要优化 Pod 布局或增加 Node

存储卷数据持久化

容器化应用的数据持久化是一个常见误区。很多初学者以为容器内写入的数据会"神奇地"保留,实际上除非使用存储卷,否则容器重启后数据会丢失。

 

# 查看 Pod 的存储卷挂载
kubectl describe pod  -n  | grep -A 10 "Volumes"

# 检查 PVC 是否正确绑定
kubectl get pvc -n 

# 测试存储卷是否可写
kubectl exec -it  -n  -- sh -c "echo test > /data/test.txt && cat /data/test.txt"

# 查看存储卷的详细信息
kubectl describe pvc  -n 

 

emptyDir 卷(临时存储)数据丢失的场景:

 

# emptyDir 的数据在以下情况会丢失:
# 1. Pod 被调度到其他 Node
# 2. Pod 被删除
# 3. Node 重启(取决于存储类型)
# 4. emptyDir.medium 设置为 Memory 且节点内存不足
volumes:
-name:cache
emptyDir:
    medium:Memory# 存储在内存中,性能高但节点内存不足时会丢数据
    sizeLimit:1Gi

 

容器化运维最佳实践

镜像管理最佳实践

生产环境镜像管理的一些原则:

不要用 latest tag。latest tag 是动态的,你今天构建的 latest 和明天构建的 latest 可能是完全不同的镜像。生产环境必须使用具体版本号(如 myapp:20240601-v1.2.3)。

镜像大小控制。大镜像拉取时间长,增加 Pod 启动时间,影响故障恢复速度。使用多阶段构建、分层缓存、最小化基础镜像(如 alpine、distroless)可以显著减小镜像大小。

 

# 多阶段构建示例
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

FROM alpine:3.19
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/myapp /usr/local/bin/
CMD ["myapp"]

 

定期清理未使用的镜像。可以用定时任务:

 

# 每天凌晨清理一次
0 3 * * * docker image prune -a --filter "until=168h"

 

容器安全配置

容器的安全配置经常被忽视,但生产环境必须关注:

 

spec:
  securityContext:
    runAsNonRoot:true           # 禁止以 root 用户运行
    runAsUser:1000              # 指定运行用户 UID
    fsGroup:1000                # 存储卷的组权限
containers:
-name:backend
    securityContext:
      allowPrivilegeEscalation:false# 禁止提权
      readOnlyRootFilesystem:true      # 根文件系统只读
      capabilities:
        drop:                        # 删除不必要的 Linux 能力
        -ALL
    resources:
      limits:
        memory:1Gi
        cpu:500m

 

使用 ReadOnlyRootFilesystem 时,需要确保应用写入的文件路径在存储卷中,否则会启动失败。

容器日志最佳实践

容器日志是排查问题的重要数据源,但管理不好会撑爆磁盘:

日志驱动选择。Docker 支持多种日志驱动(json-file、syslog、fluentd、awslogs、gcp_logs 等)。生产环境建议使用 JSON 文件日志驱动,并配置日志轮转:

 

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "10",
    "compress": "true"
  }
}

 

业务日志和容器日志分离。有些应用需要输出结构化日志(JSON 格式),不应该混在 Docker 日志里。可以用挂载卷让应用写日志到文件,通过 filebeat/fluentd 采集:

 

volumes:
- name: app-logs
  emptyDir: {}
volumeMounts:
- name: app-logs
  mountPath: /var/log/app

 

容器健康检查配置

健康检查配置不当会导致服务雪崩。以下是推荐配置:

 

livenessProbe:
  httpGet:
    path:/healthz
    port:8080
initialDelaySeconds:60    # 等待 60 秒(应用启动时间)
periodSeconds:15          # 每 15 秒检查一次
timeoutSeconds:5         # 超时 5 秒
failureThreshold:3        # 连续 3 次失败才重启(约 45 秒)

readinessProbe:
httpGet:
    path:/ready
    port:8080
initialDelaySeconds:10
periodSeconds:5
timeoutSeconds:3
failureThreshold:2        # 连续 2 次失败才摘除
successThreshold:1        # 成功 1 次即认为 Ready

startupProbe:
httpGet:
    path:/healthz
    port:8080
failureThreshold:30        # 30 次失败才放弃
periodSeconds:10          # 每 10 秒一次,等于给了应用 5 分钟启动时间

 

startupProbe 是 Kubernetes 1.20+ 支持的新探针,用于替代 initialDelaySeconds 的方案,特别适合启动时间较长的应用(如 JVM)。

常用命令清单

Docker 容器管理:

 

docker ps -a                          # 列出所有容器
docker inspect <容器名>                # 查看容器详细信息
docker logs -f <容器名>                # 查看容器日志
docker exec -it <容器名> /bin/bash   # 进入容器
docker stats                          # 查看容器资源使用
docker top <容器名>                   # 查看容器内进程
docker start/stop/restart <容器名>    # 启停容器

 

Docker 镜像管理:

 

docker images                         # 列出本地镜像
docker pull <镜像名>                  # 拉取镜像
docker rmi <镜像名>                   # 删除镜像
docker image prune                    # 清理 dangling 镜像
docker system prune -a               # 清理所有未使用资源
docker build -t <镜像名> .           # 构建镜像
docker history <镜像名>               # 查看镜像构建历史

 

Docker 存储卷和网络:

 

docker volume ls                      # 列出存储卷
docker volume inspect <卷名>          # 查看存储卷详情
docker network ls                     # 列出网络
docker network inspect <网络名>       # 查看网络详情

 

Kubernetes Pod 操作:

 

kubectl get pods -n       # 列出 Pod
kubectl describe pod  -n   # Pod 详情
kubectl logs -f  -n        # Pod 日志
kubectl exec -it  -n  -- /bin/bash  # 进入 Pod
kubectl top pod  -n         # Pod 资源使用
kubectl delete pod  -n     # 删除 Pod
kubectl apply -f            # 应用配置
kubectl get pod  -n  -o yaml  # 导出 Pod YAML

 

Kubernetes 调度和网络:

 

kubectl get endpoints  -n   # 查看 Service endpoints
kubectl get pvc -n                 # 查看 PVC 状态
kubectl get events -n  --sort-by='.lastTimestamp'  # 查看事件
kubectl get nodes -o wide                   # 查看 Node 详情
kubectl describe node               # Node 详细信息

 

Kubernetes 资源配额:

 

kubectl get resourcequota -n     # 查看配额
kubectl get limitrange -n        # 查看限制范围
kubectl top nodes                           # Node 资源使用(需要 metrics-server)

 

风险提醒

容器化运维中,有一些操作风险较高,需要特别谨慎。

docker exec 进入生产容器。docker exec 会消耗宿主机的资源,如果容器已经处于高负载状态,docker exec 可能进一步加剧负载。如果只需要查看日志,优先用 docker logs 而不是 exec。

删除运行中的容器。删除容器前确保应用已经停止服务,停止命令会等待 grace period 结束。如果应用不支持 graceful shutdown(不处理 SIGTERM 信号),直接删除会导致请求中断。

docker system prune -a。这个命令会删除所有未使用的镜像、容器、网络,是彻底的大清理。生产环境执行前必须确认所有重要镜像已经打过 tag 标识。

kubectl delete pod --force。强制删除 Pod 会立即释放资源,如果 Pod 有持久化数据且未正确同步到外部存储,会导致数据丢失。

删除 PVC。删除 PVC 之前,必须确认数据已经备份。如果 PV 的 reclaimPolicy 是 Delete,删除 PVC 会同时删除 PV 和盘上的数据(云存储可能不可恢复)。

修改运行中容器的配置。Docker 容器一旦创建,大部分配置(端口映射、存储卷挂载、环境变量)无法直接修改,必须重建容器。Kubernetes Pod 的镜像、镜像版本、端口可以通过滚动更新修改,但大部分 spec 字段不可修改。

Pod 的 livenessProbe 设置过激进。如果 livenessProbe 的 initialDelaySeconds 设置过小或 periodSeconds 过短,健康检查失败会导致容器不断重启,业务永远起不来。建议 initialDelaySeconds 大于应用启动时间,failureThreshold 不少于 3。

验证方式

完成容器操作后,需要验证以下内容。

容器/ Pod 启动成功:

 

# Docker
docker ps | grep <容器名>  # 确认容器状态为 Up

# Kubernetes
kubectl get pods -n  | grep   # 确认状态为 Running

 

业务端口监听正常:

 

# Docker
docker exec -it <容器名> netstat -tlnp | grep <端口>

# Kubernetes(需要进入 Pod 或用 port-forward)
kubectl exec -it  -n  -- netstat -tlnp

 

健康检查通过:

 

# Kubernetes 确认 Pod 的 Ready 条件为 True
kubectl get pods -n  | grep 
# 输出中 Ready 列应为 1/1 或更高

 

日志无异常:

 

# Docker
docker logs --tail 50 <容器名> | grep -i error

# Kubernetes
kubectl logs -f  -n  | grep -i error

 

资源使用在限制范围内:

 

# Docker
docker stats --no-stream <容器名>

# Kubernetes
kubectl top pod  -n 

 

回滚方案

容器化操作如果出错,回滚比虚拟机更容易,但也需要知道正确的回滚路径。

Docker 容器配置错误回滚:

 

# 删除错误容器,保留存储卷,重新创建
docker stop <容器名>
docker rm <容器名>
# 修复配置后重新创建
docker create --name <容器名> -p 8080:80 -v /data:/data <镜像名>
docker start <容器名>

 

Kubernetes Deployment 回滚:

 

# 查看 Deployment 的历史版本
kubectl rollout history deployment/ -n 

# 回滚到上一个版本
kubectl rollout undo deployment/ -n 

# 回滚到指定版本
kubectl rollout undo deployment/ -n  --to-revision=<版本号>

# 确认回滚状态
kubectl rollout status deployment/ -n 

 

Kubernetes 镜像版本回滚:

 

# 修改 Deployment 的镜像版本
kubectl set image deployment/ <容器名>=<镜像名>:<旧版本> -n 

# 或者编辑 YAML
kubectl edit deployment/ -n 

 

清理失败的容器/Pod:

 

# Docker:删除处于 Restarting 或 Exited 状态的容器
docker ps -a | grep Exit | awk '{print $1}' | xargs docker rm

# Kubernetes:删除处于 Error/Evicted 状态的 Pod(注意 Evicted 是被调度器驱逐的,可能需要先解决调度问题)
kubectl get pods -n  | grep -E "Error|Evicted|ImagePullBackOff|CrashLoopBackOff" | awk '{print $1}' | xargs kubectl delete -n 

 

总结

容器化运维的核心命令不需要记太多,但每一类问题要知道用哪个命令组合。

容器出问题了,第一步永远是看日志。docker logs 和 kubectl logs 是最快速的初步诊断手段,不要急着 exec 进容器。

exec 进入容器是第二选择,不是第一选择。能通过日志和 inspect 解决的问题,就不要进容器。exec 会消耗资源,进了容器也不知道该看什么,说明对容器的状态判断还不清晰。

Pod Running 但不工作,优先查网络和健康检查。Service endpoints 是否正确、livenessProbe 和 readinessProbe 是否通过、容器内进程是否真的在监听端口。

容器资源问题,核心是搞清楚 limits 和 requests。容器能用的资源受宿主机的限制,也受容器自身 limits 的限制。kubectl top 和 docker stats 看到的使用量是实际使用量,不一定是 limits。

镜像问题是生产环境最高频的故障。ImagePullBackOff、私有仓库认证过期、latest tag 不稳定,这些问题占了容器启动失败的很大比例。生产镜像一定要打具体 tag,永远不要用 latest。

回滚是容器化最大的优势之一。Deployment 的滚动更新和回滚机制让应用升级和降级变得可控。要养成每次发布前记录版本号的习惯,方便出问题后快速回滚。

容器化不是银弹,它解决了一类问题,同时也引入了新的问题。理解容器的隔离机制、资源模型、网络模型,是用好容器的基础。命令只是工具,背后的原理才是关键。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分