问题背景
容器化已经成了现代运维的标准配置。不管你是用 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
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 -nkubectl 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 的滚动更新和回滚机制让应用升级和降级变得可控。要养成每次发布前记录版本号的习惯,方便出问题后快速回滚。
容器化不是银弹,它解决了一类问题,同时也引入了新的问题。理解容器的隔离机制、资源模型、网络模型,是用好容器的基础。命令只是工具,背后的原理才是关键。
全部0条评论
快来发表一下你的评论吧 !