K8s详细学习笔记 第十章:集群运维与故障排查
第十章:集群运维与故障排查
部署应用只是Kubernetes旅程的开始。真正的挑战在于如何持续、可靠地运维一个动态、复杂的分布式系统。这需要我们具备“可观测性”(Observability)的能力,能够洞察集群内部的运行状态,并在问题发生时,拥有系统化的故障排查思路和熟练的工具使用技巧。
本章将聚焦于Kubernetes的日常运维,涵盖三大核心领域:
- 监控与日志:如何为集群安装“眼睛”和“耳朵”,实时了解其健康状况。
- 常用运维命令:掌握
kubectl的“瑞士军刀”,高效地进行日常检查和操作。 - 常见故障与排查思路:建立一套科学的故障诊断流程,从容应对各种疑难杂症。
10.1 监控与日志:构建集群的可观测性
在一个由成百上千个短暂的容器组成的集群中,传统的“登录服务器看日志”的运维方式已彻底失效。我们必须建立一套集中化的、自动化的监控和日志系统。
监控(Monitoring)
监控的目标是回答“系统现在怎么样了?”以及“系统即将发生什么?”。我们需要从多个维度采集和分析指标数据。
核心监控指标:
- 集群层面:
- 节点状态: 健康节点的数量,
Ready/NotReady状态的节点分布。 - API Server健康度: 请求延迟、错误率(4xx/5xx)。
- 资源分配: 整个集群的CPU/内存/磁盘的容量、请求值(Requests)、限制值(Limits)和实际使用量。
- 节点状态: 健康节点的数量,
- 节点层面:
- 每个节点的CPU、内存、磁盘I/O、网络I/O。这有助于发现资源瓶颈或硬件故障。
- Pod/容器层面:
- 每个Pod/容器的CPU、内存使用量、网络流量。
- Pod的重启次数(
restarts),这是一个非常关键的应用健康指标。
- 应用层面(业务指标):
- 这是最有价值的监控。通过应用自身暴露的指标(如使用Prometheus客户端库),我们可以监控QPS(每秒查询率)、请求延迟、错误率、处理队列长度等直接反映业务健康状况的指标。
业界标准监控方案:Prometheus + Grafana
-
Prometheus: 已成为CNCF(云原生计算基金会)的毕业项目,是云原生监控领域的事实标准。
- 工作模式: Prometheus采用拉取(Pull)模型。它会定期访问已配置的目标(如Pod、Service),抓取它们暴露的HTTP端点(通常是
/metrics)上的指标数据。 - 核心组件:
- Prometheus Server: 负责数据的抓取、存储和查询。
- Exporters: 用于为那些本身不暴露Prometheus格式指标的服务(如数据库、硬件、消息队列)提供一个“翻译”层。例如
node-exporter负责收集节点级别的指标。 - kube-state-metrics: 一个非常重要的组件,它监听Kubernetes API,并将集群中各种资源对象(Deployment, Pod, Service等)的状态转换成Prometheus可以理解的指标。
- Alertmanager: 负责处理来自Prometheus的告警规则,并进行去重、分组,然后通过邮件、Slack、Webhook等方式发送通知。
- PromQL: Prometheus提供了一套强大而灵活的查询语言(Prometheus Query Language),用于对采集到的时序数据进行复杂的查询和聚合分析。
- 工作模式: Prometheus采用拉取(Pull)模型。它会定期访问已配置的目标(如Pod、Service),抓取它们暴露的HTTP端点(通常是
-
Grafana: 一个开源的数据可视化平台。它与Prometheus是天作之合。你可以使用Grafana连接到Prometheus数据源,通过其丰富的图表和面板,创建出信息量巨大且美观的监控仪表盘(Dashboard),将集群和应用的健康状况直观地呈现出来。
日志(Logging)
日志的目标是回答“系统刚才发生了什么?”。当问题发生后,日志是进行事后根因分析(RCA)的最重要依据。
Kubernetes的标准日志模式:
Kubernetes本身不提供日志收集的解决方案,但它推荐了一个简单而有效的模式:让应用程序将日志写入其进程的标准输出(stdout)和标准错误(`stderr``)。
容器运行时(如Docker)会捕获这些输出流,并将其重定向到节点上的文件中(通常位于/var/log/pods/目录下)。
集中式日志收集架构(EFK/ELK Stack)
最流行和成熟的方案是在每个节点上部署一个日志代理(Logging Agent)。
- 部署日志代理: 使用DaemonSet在集群的每一个工作节点上都运行一个日志代理的Pod。最常用的日志代理是Fluentd或其轻量级版本Fluent Bit。
- 收集日志: 这个日志代理Pod会挂载宿主机的日志目录(如
/var/log)。它会持续地“监听”(tail)该目录下所有容器的日志文件。 - 丰富与转发: 当捕获到新的日志条目时,代理会用丰富的元数据来“装饰”它,例如Pod名称、命名空间、标签、容器ID等。这些元数据在后续的查询和分析中至关重要。然后,它将处理过的日志转发到一个集中的、可搜索的日志存储后端。
- 存储与分析: 最常见的日志存储后端是Elasticsearch,一个强大的分布式搜索引擎。
- 可视化: 使用Kibana来连接Elasticsearch,用户可以通过一个Web界面对所有日志进行全文搜索、过滤、聚合和可视化。
这个由Elasticsearch、Fluentd/Fluent Bit、Kibana组成的架构,通常被称为EFK或ELK技术栈,是云原生日志处理的黄金标准。
10.2 常用运维命令:kubectl的十八般武艺
kubectl是与Kubernetes集群交互的“瑞士军刀”。熟练掌握以下命令,将极大地提升你的运维效率。
I. 资源检查与概览 (Inspection)
kubectl get <resource_type>: 列出资源。这是所有操作的起点。kubectl get pods -n my-namespace: 查看my-namespace下的所有Pod。kubectl get pods --all-namespaces或kubectl get pods -A: 查看所有命名空间下的Pod。kubectl get pods -o wide: 显示更详细的信息,包括Pod所在的Node IP和节点名。kubectl get pods -l app=my-app: 使用标签选择器来过滤。
kubectl describe <resource_type> <resource_name>: 排查问题的首选命令,信息量最大。- 它提供了资源的详细信息,包括配置、状态、最近的事件(Events)。90%的问题都能在
Events部分找到线索。
- 它提供了资源的详细信息,包括配置、状态、最近的事件(Events)。90%的问题都能在
kubectl top pod/node: 快速查看资源(CPU/内存)使用情况(需要Metrics Server)。
II. 深入Pod内部 (Debugging)
kubectl logs <pod_name> [-c <container_name>]: 查看容器的日志。-f: 实时跟踪日志(follow)。-p: 查看上一个被终止的容器实例的日志(对于CrashLoopBackOff状态的Pod极其有用)。--tail=50: 只看最后50行。
kubectl exec -it <pod_name> -- <command>: 在容器内执行一个命令。kubectl exec -it my-pod -- /bin/bash: 获取一个交互式的Shell,进入容器内部进行“现场勘查”。这是进行网络测试(ping,curl)、文件检查、进程查看的终极手段。
III. 集群与应用管理 (Management)
kubectl apply -f <filename.yaml>: 通过YAML文件声明式地创建或更新资源。kubectl delete -f <filename.yaml>: 删除YAML文件中定义的资源。kubectl edit <resource_type> <resource_name>: 直接在终端中打开编辑器修改资源的实时配置。kubectl rollout status/history/undo deployment/<name>: 检查发布状态、查看历史版本、一键回滚。kubectl config get-contexts / use-context <context_name>: 管理和切换不同的集群配置。kubectl get events --sort-by='.lastTimestamp' -A: 按时间顺序查看整个集群最近发生的所有事件。
10.3 常见故障与排查思路
面对故障时,最重要的是保持冷静,并遵循一个系统化的、由表及里的诊断流程。切忌无头苍蝇式地乱试。
【重难点】建立系统的故障排查思维
核心排查流程:describe -> logs -> exec
- 确认现象 (Get): 使用
kubectl get确认资源的状态(如Pending,CrashLoopBackOff,Running)。 - 寻找线索 (Describe): 使用
kubectl describe查看该资源的详细状态和事件(Events)。这是定位问题的第一步,也是最关键的一步。 - 深入应用 (Logs): 如果
describe表明是容器内部的问题(如启动失败),立即使用kubectl logs查看应用的日志。 - 现场勘查 (Exec): 如果日志信息不足,使用
kubectl exec进入容器内部,进行更深入的检查。
场景一:Pod处于Pending(挂起)状态
现象: kubectl get pods显示Pod长时间处于Pending状态,从未进入Running。
排查思路: Pending意味着Pod已被集群接受,但无法被调度到任何一个Node上,或者仍在准备阶段。问题出在调度器或Kubelet的准备环节。
kubectl describe pod <pod-name>: 查看Events。- 线索1:
Insufficient cpu/memory- 原因: 集群中所有节点的可用资源都不足以满足该Pod的
resources.requests。 - 解决:
- 检查
kubectl top nodes或kubectl describe node确认节点资源情况。 - 降低Pod的资源
requests。 - 向集群中添加新的Node。
- 删除一些不必要的Pod以释放资源。
- 检查
- 原因: 集群中所有节点的可用资源都不足以满足该Pod的
- 线索2:
didn't match node selector/affinity或had taints the pod didn't tolerate- 原因: Pod的调度策略(亲和性、污点容忍)与集群中节点的标签、污点不匹配。
- 解决:
- 检查Pod YAML中的
nodeSelector,affinity部分。 - 使用
kubectl get nodes --show-labels检查节点的标签。 - 使用
kubectl describe node <node-name>检查节点的污点(Taints)。 - 修改Pod的调度策略或节点的标签/污点。
- 检查Pod YAML中的
- 线索3:
persistentvolumeclaim "my-pvc" not found or not bound- 原因: Pod依赖的PVC尚未成功绑定到PV。
- 解决: 转而去排查PVC的问题,
kubectl describe pvc my-pvc。可能是没有可用的PV,或是StorageClass动态供给失败。
- 线索1:
场景二:Pod处于ImagePullBackOff / ErrImagePull状态
现象: Pod启动失败,反复在Running和这两个状态之间切换。
排查思路: ImagePull明确告诉我们问题出在拉取容器镜像的环节。
kubectl describe pod <pod-name>: 查看Events。- 线索1:
manifest for ... not found或repository does not exist- 原因: 镜像的名称或标签(tag)写错了,仓库中不存在这个镜像。
- 解决: 仔细检查Deployment YAML中的
image字段,确保仓库、镜像名和标签都准确无误。
- 线索2:
authentication required或pull access denied- 原因: 这是一个私有镜像仓库,但Pod没有提供正确的认证凭据。
- 解决:
- 确认已经创建了包含认证信息的
docker-registry类型的Secret。 - 确认在Pod的
spec中通过imagePullSecrets字段引用了这个Secret。 - 确认Secret中的用户名和密码/Token是正确的。
- 确认已经创建了包含认证信息的
- 线索3:
timeout或connection refused- 原因: Pod所在的Node节点与镜像仓库之间的网络不通。
- 解决:
exec进入同一节点上的其他Pod,尝试ping或curl镜像仓库的地址。- 检查节点的DNS配置、网络策略(Network Policies)、防火墙规则或云环境的安全组设置。
- 线索1:
场景三:Pod处于CrashLoopBackOff状态
现象: Pod启动后很快就退出,Kubernetes根据重启策略又尝试重启它,如此循环往复。
排查思路: CrashLoop意味着容器进程能够启动,但无法维持运行。这绝大多数是应用程序自身的问题。
kubectl logs <pod-name> -p: 这是第一步也是最关键的一步! 使用-p参数查看上一次失败容器的日志。因为当前正在尝试启动的容器可能还没来得及打印出有用的错误信息。kubectl describe pod <pod-name>:- 查看
State和Last State部分,可能会显示容器的退出码(Exit Code)。非零的退出码通常能提供错误类型线索(如Exit Code 1是通用错误,Exit Code 137通常表示被OOMKilled)。 - 如果
Reason是**OOMKilled**,说明容器使用的内存超过了其limits.memory。- 解决: 增加Pod的内存限制,或者优化应用代码以减少内存消耗。
- 查看
- 检查应用配置:
- 配置错误: 应用启动时依赖的配置文件(通过ConfigMap挂载)、环境变量或密钥(通过Secret挂载)是否正确?
- 依赖服务: 应用是否依赖数据库或其他服务?这些依赖服务是否可用?
- 检查探针配置:
- 不合理的Liveness Probe(例如,探测过于频繁或超时太短)可能会在应用尚未完全准备好时就将其杀死,导致
CrashLoop。尝试临时移除Liveness Probe,看Pod是否能稳定运行。
- 不合理的Liveness Probe(例如,探测过于频繁或超时太短)可能会在应用尚未完全准备好时就将其杀死,导致
场景四:服务(Service)无法访问
现象: Pod运行正常,但通过Service的ClusterIP或NodePort无法访问。
排查思路: 这是一个端到端的网络路径问题,需要逐段排查。
- 从Pod开始: 确认应用在Pod内部是正常的。
kubectl exec -it <pod-name> -- curl localhost:<containerPort>。如果这一步失败,说明是应用本身的问题,回头去查日志。
- 检查Service与Pod的连接: 这是最常见的出问题环节。
kubectl describe svc <service-name>。检查**Endpoints字段。这里必须**列出你后端健康Pod的IP地址。- 如果
Endpoints为空: 那么Service的**selector和Pod模板中的labels肯定不匹配**!请逐个字符检查,确保它们完全一致。
- 检查端口配置:
- 在Service的定义中,
targetPort是否正确指向了Pod的containerPort?
- 在Service的定义中,
- 检查DNS解析:
- 从集群内的另一个Pod
exec进去,执行nslookup <service-name>.<namespace>。是否能正确解析到Service的ClusterIP?如果不能,可能是CoreDNS出了问题。
- 从集群内的另一个Pod
- 检查网络策略:
- 集群中是否配置了
NetworkPolicy?它可能阻止了客户端Pod到服务端Pod的流量。
- 集群中是否配置了
- (针对NodePort/LoadBalancer) 检查外部路径:
kubectl get svc <service-name> -o wide确认NodePort已分配。- 确认你的防火墙/安全组规则允许访问该NodePort。
- 如果是
LoadBalancer类型,去你的云服务商控制台检查负载均衡器的健康状况和监听器配置。
通过掌握这一整套从监控告警到系统化故障排查的流程和工具,你将能够自信地管理和维护生产级别的Kubernetes集群,确保其高效、稳定地支撑业务的运行。
K8s 详细学习笔记 目录
以下是整个系列的10章目录,点击章节标题即可跳转阅读:
- K8s详细学习笔记 第一章:K8s入门与核心概念
- K8s详细学习笔记 第二章:部署你的第一个应用:Pod与Deployment
- K8s详细学习笔记 第三章:应用访问与服务发现:Service与Ingress
- K8s详细学习笔记 第四章:应用配置管理:ConfigMap与Secret
- K8s详细学习笔记 第五章:数据持久化:Volume与Persistent Storage
- K8s详细学习笔记 第六章:应用的健康检查与自愈能力
- K8s详细学习笔记 第七章:资源的限制与调度
- K8s详细学习笔记 第八章:应用的扩缩容与更新策略
- K8s详细学习笔记 第九章:K8s安全机制:认证、授权与准入控制
- K8s详细学习笔记 第十章:集群运维与故障排查
更多推荐
所有评论(0)