这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

集群故障排查

调试常见的集群问题。

本篇文档是介绍集群故障排查的;我们假设对于你碰到的问题,你已经排除了是由应用程序造成的。 对于应用的调试,请参阅应用故障排查指南。 你也可以访问故障排查来获取更多的信息。

有关 kubectl 的故障排查, 请参阅 kubectl 故障排查

列举集群节点

调试的第一步是查看所有的节点是否都已正确注册。

运行以下命令:

kubectl get nodes

验证你所希望看见的所有节点都能够显示出来,并且都处于 Ready 状态。

为了了解你的集群的总体健康状况详情,你可以运行:

kubectl cluster-info dump

示例:调试关闭/无法访问的节点

有时在调试时查看节点的状态很有用 —— 例如,因为你注意到在节点上运行的 Pod 的奇怪行为, 或者找出为什么 Pod 不会调度到节点上。与 Pod 一样,你可以使用 kubectl describe nodekubectl get node -o yaml 来检索有关节点的详细信息。 例如,如果节点关闭(与网络断开连接,或者 kubelet 进程挂起并且不会重新启动等), 你将看到以下内容。请注意显示节点为 NotReady 的事件,并注意 Pod 不再运行(它们在 NotReady 状态五分钟后被驱逐)。

kubectl get nodes
NAME                     STATUS       ROLES     AGE     VERSION
kube-worker-1            NotReady     <none>    1h      v1.23.3
kubernetes-node-bols     Ready        <none>    1h      v1.23.3
kubernetes-node-st6x     Ready        <none>    1h      v1.23.3
kubernetes-node-unaj     Ready        <none>    1h      v1.23.3
kubectl describe node kube-worker-1
Name:               kube-worker-1
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=kube-worker-1
                    kubernetes.io/os=linux
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Thu, 17 Feb 2022 16:46:30 -0500
Taints:             node.kubernetes.io/unreachable:NoExecute
                    node.kubernetes.io/unreachable:NoSchedule
Unschedulable:      false
Lease:
  HolderIdentity:  kube-worker-1
  AcquireTime:     <unset>
  RenewTime:       Thu, 17 Feb 2022 17:13:09 -0500
Conditions:
  Type                 Status    LastHeartbeatTime                 LastTransitionTime                Reason              Message
  ----                 ------    -----------------                 ------------------                ------              -------
  NetworkUnavailable   False     Thu, 17 Feb 2022 17:09:13 -0500   Thu, 17 Feb 2022 17:09:13 -0500   WeaveIsUp           Weave pod has set this
  MemoryPressure       Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  DiskPressure         Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  PIDPressure          Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  Ready                Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
Addresses:
  InternalIP:  192.168.0.113
  Hostname:    kube-worker-1
Capacity:
  cpu:                2
  ephemeral-storage:  15372232Ki
  hugepages-2Mi:      0
  memory:             2025188Ki
  pods:               110
Allocatable:
  cpu:                2
  ephemeral-storage:  14167048988
  hugepages-2Mi:      0
  memory:             1922788Ki
  pods:               110
System Info:
  Machine ID:                 9384e2927f544209b5d7b67474bbf92b
  System UUID:                aa829ca9-73d7-064d-9019-df07404ad448
  Boot ID:                    5a295a03-aaca-4340-af20-1327fa5dab5c
  Kernel Version:             5.13.0-28-generic
  OS Image:                   Ubuntu 21.10
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  containerd://1.5.9
  Kubelet Version:            v1.23.3
  Kube-Proxy Version:         v1.23.3
Non-terminated Pods:          (4 in total)
  Namespace                   Name                                 CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                                 ------------  ----------  ---------------  -------------  ---
  default                     nginx-deployment-67d4bdd6f5-cx2nz    500m (25%)    500m (25%)  128Mi (6%)       128Mi (6%)     23m
  default                     nginx-deployment-67d4bdd6f5-w6kd7    500m (25%)    500m (25%)  128Mi (6%)       128Mi (6%)     23m
  kube-system                 kube-proxy-dnxbz                     0 (0%)        0 (0%)      0 (0%)           0 (0%)         28m
  kube-system                 weave-net-gjxxp                      100m (5%)     0 (0%)      200Mi (10%)      0 (0%)         28m
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests     Limits
  --------           --------     ------
  cpu                1100m (55%)  1 (50%)
  memory             456Mi (24%)  256Mi (13%)
  ephemeral-storage  0 (0%)       0 (0%)
  hugepages-2Mi      0 (0%)       0 (0%)
Events:
...
kubectl get node kube-worker-1 -o yaml
apiVersion: v1
kind: Node
metadata:
  annotations:
    kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
    node.alpha.kubernetes.io/ttl: "0"
    volumes.kubernetes.io/controller-managed-attach-detach: "true"
  creationTimestamp: "2022-02-17T21:46:30Z"
  labels:
    beta.kubernetes.io/arch: amd64
    beta.kubernetes.io/os: linux
    kubernetes.io/arch: amd64
    kubernetes.io/hostname: kube-worker-1
    kubernetes.io/os: linux
  name: kube-worker-1
  resourceVersion: "4026"
  uid: 98efe7cb-2978-4a0b-842a-1a7bf12c05f8
spec: {}
status:
  addresses:
  - address: 192.168.0.113
    type: InternalIP
  - address: kube-worker-1
    type: Hostname
  allocatable:
    cpu: "2"
    ephemeral-storage: "14167048988"
    hugepages-2Mi: "0"
    memory: 1922788Ki
    pods: "110"
  capacity:
    cpu: "2"
    ephemeral-storage: 15372232Ki
    hugepages-2Mi: "0"
    memory: 2025188Ki
    pods: "110"
  conditions:
  - lastHeartbeatTime: "2022-02-17T22:20:32Z"
    lastTransitionTime: "2022-02-17T22:20:32Z"
    message: Weave pod has set this
    reason: WeaveIsUp
    status: "False"
    type: NetworkUnavailable
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has sufficient memory available
    reason: KubeletHasSufficientMemory
    status: "False"
    type: MemoryPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has no disk pressure
    reason: KubeletHasNoDiskPressure
    status: "False"
    type: DiskPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has sufficient PID available
    reason: KubeletHasSufficientPID
    status: "False"
    type: PIDPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:15:15Z"
    message: kubelet is posting ready status. AppArmor enabled
    reason: KubeletReady
    status: "True"
    type: Ready
  daemonEndpoints:
    kubeletEndpoint:
      Port: 10250
  nodeInfo:
    architecture: amd64
    bootID: 22333234-7a6b-44d4-9ce1-67e31dc7e369
    containerRuntimeVersion: containerd://1.5.9
    kernelVersion: 5.13.0-28-generic
    kubeProxyVersion: v1.23.3
    kubeletVersion: v1.23.3
    machineID: 9384e2927f544209b5d7b67474bbf92b
    operatingSystem: linux
    osImage: Ubuntu 21.10
    systemUUID: aa829ca9-73d7-064d-9019-df07404ad448

查看日志

目前,深入挖掘集群需要登录相关机器。以下是相关日志文件的位置。 在基于 systemd 的系统上,你可能需要使用 journalctl 而不是检查日志文件。

控制平面节点

  • /var/log/kube-apiserver.log —— API 服务器,负责提供 API 服务
  • /var/log/kube-scheduler.log —— 调度器,负责制定调度决策
  • /var/log/kube-controller-manager.log —— 运行大多数 Kubernetes 内置控制器的组件,除了调度(kube-scheduler 处理调度)。

工作节点

  • /var/log/kubelet.log —— 负责在节点运行容器的 kubelet 所产生的日志
  • /var/log/kube-proxy.log —— 负责将流量转发到服务端点的 kube-proxy 所产生的日志

集群故障模式

这是可能出错的事情的不完整列表,以及如何调整集群设置以缓解问题。

故障原因

  • 虚拟机关闭
  • 集群内或集群与用户之间的网络分区
  • Kubernetes 软件崩溃
  • 持久存储(例如 GCE PD 或 AWS EBS 卷)的数据丢失或不可用
  • 操作员错误,例如配置错误的 Kubernetes 软件或应用程序软件

具体情况

  • API 服务器所在的 VM 关机或者 API 服务器崩溃
    • 结果
      • 不能停止、更新或者启动新的 Pod、服务或副本控制器
      • 现有的 Pod 和服务在不依赖 Kubernetes API 的情况下应该能继续正常工作
  • API 服务器的后端存储丢失
    • 结果
      • kube-apiserver 组件未能成功启动并变健康
      • kubelet 将不能访问 API 服务器,但是能够继续运行之前的 Pod 和提供相同的服务代理
      • 在 API 服务器重启之前,需要手动恢复或者重建 API 服务器的状态
  • Kubernetes 服务组件(节点控制器、副本控制器管理器、调度器等)所在的 VM 关机或者崩溃
    • 当前,这些控制器是和 API 服务器在一起运行的,它们不可用的现象是与 API 服务器类似的
    • 将来,这些控制器也会复制为多份,并且可能不在运行于同一节点上
    • 它们没有自己的持久状态
  • 单个节点(VM 或者物理机)关机
    • 结果
      • 此节点上的所有 Pod 都停止运行
  • 网络分裂
    • 结果
      • 分区 A 认为分区 B 中所有的节点都已宕机;分区 B 认为 API 服务器宕机 (假定主控节点所在的 VM 位于分区 A 内)。
  • kubelet 软件故障
    • 结果
      • 崩溃的 kubelet 就不能在其所在的节点上启动新的 Pod
      • kubelet 可能删掉 Pod 或者不删
      • 节点被标识为非健康态
      • 副本控制器会在其它的节点上启动新的 Pod
  • 集群操作错误
    • 结果
      • 丢失 Pod 或服务等等
      • 丢失 API 服务器的后端存储
      • 用户无法读取 API
      • 等等

缓解措施

  • 措施:对于 IaaS 上的 VM,使用 IaaS 的自动 VM 重启功能

    • 缓解:API 服务器 VM 关机或 API 服务器崩溃
    • 缓解:Kubernetes 服务组件所在的 VM 关机或崩溃
  • 措施: 对于运行 API 服务器和 etcd 的 VM,使用 IaaS 提供的可靠的存储(例如 GCE PD 或者 AWS EBS 卷)

    • 缓解:API 服务器后端存储的丢失
  • 措施:使用高可用性的配置

    • 缓解:主控节点 VM 关机或者主控节点组件(调度器、API 服务器、控制器管理器)崩溃
      • 将容许一个或多个节点或组件同时出现故障
    • 缓解:API 服务器后端存储(例如 etcd 的数据目录)丢失
      • 假定你使用了高可用的 etcd 配置
  • 措施:定期对 API 服务器的 PD 或 EBS 卷执行快照操作

    • 缓解:API 服务器后端存储丢失
    • 缓解:一些操作错误的场景
    • 缓解:一些 Kubernetes 软件本身故障的场景
  • 措施:在 Pod 的前面使用副本控制器或服务

    • 缓解:节点关机
    • 缓解:kubelet 软件故障
  • 措施:应用(容器)设计成容许异常重启

    • 缓解:节点关机
    • 缓解:kubelet 软件故障

接下来

1 - kubectl 故障排查

本文讲述研判和诊断 kubectl 相关的问题。 如果你在访问 kubectl 或连接到集群时遇到问题,本文概述了各种常见的情况和可能的解决方案, 以帮助确定和解决可能的原因。

准备开始

  • 你需要有一个 Kubernetes 集群。
  • 你还需要安装好 kubectl,参见安装工具

验证 kubectl 设置

确保你已在本机上正确安装和配置了 kubectl。 检查 kubectl 版本以确保其是最新的,并与你的集群兼容。

检查 kubectl 版本:

kubectl version

你将看到类似的输出:

Client Version: version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.4",GitCommit:"fa3d7990104d7c1f16943a67f11b154b71f6a132", GitTreeState:"clean",BuildDate:"2023-07-19T12:20:54Z", GoVersion:"go1.20.6", Compiler:"gc", Platform:"linux/amd64"}
Kustomize Version: v5.0.1
Server Version: version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.3",GitCommit:"25b4e43193bcda6c7328a6d147b1fb73a33f1598", GitTreeState:"clean",BuildDate:"2023-06-14T09:47:40Z", GoVersion:"go1.20.5", Compiler:"gc", Platform:"linux/amd64"}

如果你看到 Unable to connect to the server: dial tcp <server-ip>:8443: i/o timeout, 而不是 Server Version,则需要解决 kubectl 与你集群的连接问题。

确保按照 kubectl 官方安装文档安装了 kubectl, 并正确配置了 $PATH 环境变量。

检查 kubeconfig

kubectl 需要一个 kubeconfig 文件来连接到 Kubernetes 集群。 kubeconfig 文件通常位于 ~/.kube/config 目录下。确保你有一个有效的 kubeconfig 文件。 如果你没有 kubeconfig 文件,可以从 Kubernetes 管理员那里获取,或者可以从 Kubernetes 控制平面的 /etc/kubernetes/admin.conf 目录复制这个文件。如果你在云平台上部署了 Kubernetes 集群并且丢失了你的 kubeconfig 文件,则可以使用云厂商的工具重新生成它。参考云厂商的文档以重新生成 kubeconfig 文件。

检查 $KUBECONFIG 环境变量是否配置正确。你可以设置 $KUBECONFIG 环境变量,或者在 kubectl 中使用 --kubeconfig 参数来指定 kubeconfig 文件的目录。

检查 VPN 连接

如果你正在使用虚拟专用网络(VPN)访问 Kubernetes 集群,请确保你的 VPN 连接是可用且稳定的。 有时,VPN 断开连接可能会导致与集群的连接问题。重新连接到 VPN,并尝试再次访问集群。

身份认证和鉴权

如果你正在使用基于令牌的身份认证,并且 kubectl 返回有关身份认证令牌或身份认证服务器地址的错误, 校验 Kubernetes 身份认证令牌和身份认证服务器地址是否被配置正确。

如果 kubectl 返回有关鉴权的错误,确保你正在使用有效的用户凭据,并且你具有访问所请求资源的权限。

验证上下文

Kubernetes 支持多个集群和上下文。 确保你正在使用正确的上下文与集群进行交互。

列出可用的上下文:

kubectl config get-contexts

切换到合适的上下文:

kubectl config use-context <context-name>

API 服务器和负载均衡器

kube-apiserver 服务器是 Kubernetes 集群的核心组件。如果 API 服务器或运行在 API 服务器前面的负载均衡器不可达或没有响应,你将无法与集群进行交互。

通过使用 ping 命令检查 API 服务器的主机是否可达。检查集群的网络连接和防火墙。 如果你使用云厂商部署集群,请检查云厂商对集群的 API 服务器的健康检查状态。

验证负载均衡器(如果使用)的状态,确保其健康且转发流量到 API 服务器。

TLS 问题

Kubernetes API 服务器默认只为 HTTPS 请求提供服务。在这种情况下, TLS 问题可能会因各种原因而出现,例如证书过期或信任链有效性。

你可以在 kubeconfig 文件中找到 TLS 证书,此文件位于 ~/.kube/config 目录下。 certificate-authority 属性包含 CA 证书,而 client-certificate 属性则包含客户端证书。

验证这些证书的到期时间:

openssl x509 -noout -dates -in $(kubectl config view --minify --output 'jsonpath={.clusters[0].cluster.certificate-authority}')

输出为:

notBefore=Sep  2 08:34:12 2023 GMT
notAfter=Aug 31 08:34:12 2033 GMT
openssl x509 -noout -dates -in $(kubectl config view --minify --output 'jsonpath={.users[0].user.client-certificate}')

输出为:

notBefore=Sep  2 08:34:12 2023 GMT
notAfter=Sep  2 08:34:12 2026 GMT

验证 kubectl 助手

某些 kubectl 身份认证助手提供了便捷访问 Kubernetes 集群的方式。 如果你使用了这些助手并且遇到连接问题,确保必要的配置仍然存在。

查看 kubectl 配置以了解身份认证细节:

kubectl config view

如果你之前使用了辅助工具(例如 kubectl-oidc-login),确保它仍然安装和配置正确。

2 - 资源监控工具

要扩展应用程序并提供可靠的服务,你需要了解应用程序在部署时的行为。 你可以通过检测容器、PodService 和整个集群的特征来检查 Kubernetes 集群中应用程序的性能。 Kubernetes 在每个级别上提供有关应用程序资源使用情况的详细信息。 此信息使你可以评估应用程序的性能,以及在何处可以消除瓶颈以提高整体性能。

在 Kubernetes 中,应用程序监控不依赖单个监控解决方案。在新集群上, 你可以使用资源度量完整度量管道来收集监视统计信息。

资源度量管道

资源指标管道提供了一组与集群组件,例如 Horizontal Pod Autoscaler 控制器以及 kubectl top 实用程序相关的有限度量。 这些指标是由轻量级的、短期、内存存储的 metrics-server 收集, 并通过 metrics.k8s.io 公开。

metrics-server 发现集群中的所有节点,并且查询每个节点的 kubelet 以获取 CPU 和内存使用情况。 kubelet 充当 Kubernetes 主节点与节点之间的桥梁,管理机器上运行的 Pod 和容器。 kubelet 将每个 Pod 转换为其组成的容器,并通过容器运行时接口从容器运行时获取各个容器使用情况统计信息。 如果某个容器运行时使用 Linux cgroups 和名字空间来实现容器。 并且这一容器运行时不发布资源用量统计信息, 那么 kubelet 可以直接查找这些统计信息(使用来自 cAdvisor 的代码)。 无论这些统计信息如何到达,kubelet 都会通过 metrics-server Resource Metrics API 公开聚合的 Pod 资源用量统计信息。 该 API 在 kubelet 的经过身份验证和只读的端口上的 /metrics/resource/v1beta1 中提供。

完整度量管道

一个完整度量管道可以让你访问更丰富的度量。 Kubernetes 还可以根据集群的当前状态,使用 Pod 水平自动扩缩器等机制, 通过自动调用扩展或调整集群来响应这些度量。 监控管道从 kubelet 获取度量值,然后通过适配器将它们公开给 Kubernetes, 方法是实现 custom.metrics.k8s.ioexternal.metrics.k8s.io API。

Kubernetes 在设计上保证能够与 OpenMetrics 一同使用, OpenMetrics 是 CNCF 可观测性和分析 - 监控项目之一, 它构建于 Prometheus 暴露格式之上, 并对其进行了扩展,这些扩展几乎 100% 向后兼容。

如果你浏览 CNCF Landscape, 你可以看到许多监控项目,它们可以用在 Kubernetes 上,抓取指标数据并利用这些数据来观测你的集群, 选择哪种工具或哪些工具可以满足你的需求,这完全取决于你自己。 CNCF 的可观测性和分析景观包括了各种开源软件、付费的软件即服务(SaaS)以及其他混合商业产品。

当你设计和实现一个完整的指标监控数据管道时,你可以将监控数据反馈给 Kubernetes。 例如,HorizontalPodAutoscaler 可以使用处理过的指标数据来计算出你的工作负载组件运行了多少个 Pod。

将完整的指标管道集成到 Kubernetes 实现中超出了 Kubernetes 文档的范围,因为可能的解决方案具有非常广泛的范围。

监控平台的选择在很大程度上取决于你的需求、预算和技术资源。 Kubernetes 不推荐任何特定的指标管道; 可使用许多选项。 你的监控系统应能够处理 OpenMetrics 指标传输标准, 并且需要选择最适合基础设施平台的整体设计和部署。

接下来

了解其他调试工具,包括:

3 - 资源指标管道

对于 Kubernetes,Metrics API 提供了一组基本的指标,以支持自动伸缩和类似的用例。 该 API 提供有关节点和 Pod 的资源使用情况的信息, 包括 CPU 和内存的指标。如果将 Metrics API 部署到集群中, 那么 Kubernetes API 的客户端就可以查询这些信息,并且可以使用 Kubernetes 的访问控制机制来管理权限。

HorizontalPodAutoscaler (HPA) 和 VerticalPodAutoscaler (VPA) 使用 metrics API 中的数据调整工作负载副本和资源,以满足客户需求。

你也可以通过 kubectl top 命令来查看资源指标。

图 1 说明了资源指标管道的架构。

flowchart RL subgraph cluster[集群] direction RL S[

] A[Metrics-
Server] subgraph B[节点] direction TB D[cAdvisor] --> C[kubelet] E[容器
运行时] --> D E1[容器
运行时] --> D P[Pod 数据] -.- C end L[API
服务器] W[HPA] C ---->|节点层面
资源指标| A -->|metrics
API| L --> W end L ---> K[kubectl
top] classDef box fill:#fff,stroke:#000,stroke-width:1px,color:#000; class W,B,P,K,cluster,D,E,E1 box classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class S spacewhite classDef k8s fill:#326ce5,stroke:#fff,stroke-width:1px,color:#fff; class A,L,C k8s

图 1. 资源指标管道

图中从右到左的架构组件包括以下内容:

  • cAdvisor: 用于收集、聚合和公开 Kubelet 中包含的容器指标的守护程序。

  • kubelet: 用于管理容器资源的节点代理。 可以使用 /metrics/resource/stats kubelet API 端点访问资源指标。

  • 节点层面资源指标: kubelet 提供的 API,用于发现和检索可通过 /metrics/resource 端点获得的每个节点的汇总统计信息。

  • metrics-server: 集群插件组件,用于收集和聚合从每个 kubelet 中提取的资源指标。 API 服务器提供 Metrics API 以供 HPA、VPA 和 kubectl top 命令使用。Metrics Server 是 Metrics API 的参考实现。

  • Metrics API: Kubernetes API 支持访问用于工作负载自动缩放的 CPU 和内存。 要在你的集群中进行这项工作,你需要一个提供 Metrics API 的 API 扩展服务器。

Metrics API

特性状态: Kubernetes 1.8 [beta]

metrics-server 实现了 Metrics API。此 API 允许你访问集群中节点和 Pod 的 CPU 和内存使用情况。 它的主要作用是将资源使用指标提供给 K8s 自动缩放器组件。

下面是一个 minikube 节点的 Metrics API 请求示例,通过 jq 管道处理以便于阅读:

kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes/minikube" | jq '.'

这是使用 curl 来执行的相同 API 调用:

curl http://localhost:8080/apis/metrics.k8s.io/v1beta1/nodes/minikube

响应示例:

{
  "kind": "NodeMetrics",
  "apiVersion": "metrics.k8s.io/v1beta1",
  "metadata": {
    "name": "minikube",
    "selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/minikube",
    "creationTimestamp": "2022-01-27T18:48:43Z"
  },
  "timestamp": "2022-01-27T18:48:33Z",
  "window": "30s",
  "usage": {
    "cpu": "487558164n",
    "memory": "732212Ki"
  }
}

下面是一个 kube-system 命名空间中的 kube-scheduler-minikube Pod 的 Metrics API 请求示例, 通过 jq 管道处理以便于阅读:

kubectl get --raw "/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube" | jq '.'

这是使用 curl 来完成的相同 API 调用:

curl http://localhost:8080/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube

响应示例:

{
  "kind": "PodMetrics",
  "apiVersion": "metrics.k8s.io/v1beta1",
  "metadata": {
    "name": "kube-scheduler-minikube",
    "namespace": "kube-system",
    "selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube",
    "creationTimestamp": "2022-01-27T19:25:00Z"
  },
  "timestamp": "2022-01-27T19:24:31Z",
  "window": "30s",
  "containers": [
    {
      "name": "kube-scheduler",
      "usage": {
        "cpu": "9559630n",
        "memory": "22244Ki"
      }
    }
  ]
}

Metrics API 在 k8s.io/metrics 代码库中定义。 你必须启用 API 聚合层并为 metrics.k8s.io API 注册一个 APIService

要了解有关 Metrics API 的更多信息, 请参阅资源 Resource Metrics API Designmetrics-server 代码库Resource Metrics API

度量资源用量

CPU

CPU 报告为以 cpu 为单位测量的平均核心使用率。在 Kubernetes 中, 一个 cpu 相当于云提供商的 1 个 vCPU/Core,以及裸机 Intel 处理器上的 1 个超线程。

该值是通过对内核提供的累积 CPU 计数器(在 Linux 和 Windows 内核中)取一个速率得出的。 用于计算 CPU 的时间窗口显示在 Metrics API 的窗口字段下。

要了解更多关于 Kubernetes 如何分配和测量 CPU 资源的信息,请参阅 CPU 的含义

内存

内存报告为在收集度量标准的那一刻的工作集大小,以字节为单位。

在理想情况下,“工作集”是在内存压力下无法释放的正在使用的内存量。 然而,工作集的计算因主机操作系统而异,并且通常大量使用启发式算法来产生估计。

Kubernetes 模型中,容器工作集是由容器运行时计算的与相关容器关联的匿名内存。 工作集指标通常还包括一些缓存(文件支持)内存,因为主机操作系统不能总是回收页面。

要了解有关 Kubernetes 如何分配和测量内存资源的更多信息, 请参阅内存的含义

Metrics 服务器

metrics-server 从 kubelet 中获取资源指标,并通过 Metrics API 在 Kubernetes API 服务器中公开它们,以供 HPA 和 VPA 使用。 你还可以使用 kubectl top 命令查看这些指标。

metrics-server 使用 Kubernetes API 来跟踪集群中的节点和 Pod。metrics-server 服务器通过 HTTP 查询每个节点以获取指标。 metrics-server 还构建了 Pod 元数据的内部视图,并维护 Pod 健康状况的缓存。 缓存的 Pod 健康信息可通过 metrics-server 提供的扩展 API 获得。

例如,对于 HPA 查询,metrics-server 需要确定哪些 Pod 满足 Deployment 中的标签选择器。

metrics-server 调用 kubelet API 从每个节点收集指标。根据它使用的 metrics-server 版本:

  • 版本 v0.6.0+ 中,使用指标资源端点 /metrics/resource
  • 旧版本中使用 Summary API 端点 /stats/summary

接下来

了解更多 metrics-server,参阅 metrics-server 代码库

你还可以查看以下内容:

若要了解 kubelet 如何提供节点指标以及你可以如何通过 Kubernetes API 访问这些指标, 请阅读节点指标数据

4 - 节点健康监测

节点问题检测器(Node Problem Detector) 是一个守护程序,用于监视和报告节点的健康状况。 你可以将节点问题探测器以 DaemonSet 或独立守护程序运行。 节点问题检测器从各种守护进程收集节点问题,并以节点 ConditionEvent 的形式报告给 API 服务器。

要了解如何安装和使用节点问题检测器,请参阅 节点问题探测器项目文档

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

局限性

启用节点问题检测器

一些云供应商将节点问题检测器以插件形式启用。 你还可以使用 kubectl 或创建插件 DaemonSet 来启用节点问题探测器。

使用 kubectl 启用节点问题检测器

kubectl 提供了节点问题探测器最灵活的管理。 你可以覆盖默认配置使其适合你的环境或检测自定义节点问题。例如:

  1. 创建类似于 node-strought-detector.yaml 的节点问题检测器配置:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: node-problem-detector-v0.1
      namespace: kube-system
      labels:
        k8s-app: node-problem-detector
        version: v0.1
        kubernetes.io/cluster-service: "true"
    spec:
      selector:
        matchLabels:
          k8s-app: node-problem-detector  
          version: v0.1
          kubernetes.io/cluster-service: "true"
      template:
        metadata:
          labels:
            k8s-app: node-problem-detector
            version: v0.1
            kubernetes.io/cluster-service: "true"
        spec:
          hostNetwork: true
          containers:
          - name: node-problem-detector
            image: registry.k8s.io/node-problem-detector:v0.1
            securityContext:
              privileged: true
            resources:
              limits:
                cpu: "200m"
                memory: "100Mi"
              requests:
                cpu: "20m"
                memory: "20Mi"
            volumeMounts:
            - name: log
              mountPath: /log
              readOnly: true
          volumes:
          - name: log
            hostPath:
              path: /var/log/
  2. 使用 kubectl 启动节点问题检测器:

    kubectl apply -f https://k8s.io/examples/debug/node-problem-detector.yaml
    

使用插件 Pod 启用节点问题检测器

如果你使用的是自定义集群引导解决方案,不需要覆盖默认配置, 可以利用插件 Pod 进一步自动化部署。

创建 node-strick-detector.yaml,并在控制平面节点上保存配置到插件 Pod 的目录 /etc/kubernetes/addons/node-problem-detector

覆盖配置文件

构建节点问题检测器的 docker 镜像时,会嵌入 默认配置

不过,你可以像下面这样使用 ConfigMap 将其覆盖:

  1. 更改 config/ 中的配置文件

  2. 创建 ConfigMap node-strick-detector-config

    kubectl create configmap node-problem-detector-config --from-file=config/
    
  3. 更改 node-problem-detector.yaml 以使用 ConfigMap:

    apiVersion: apps/v1
       kind: DaemonSet
       metadata:
         name: node-problem-detector-v0.1
         namespace: kube-system
         labels:
           k8s-app: node-problem-detector
           version: v0.1
           kubernetes.io/cluster-service: "true"
       spec:
         selector:
           matchLabels:
             k8s-app: node-problem-detector  
             version: v0.1
             kubernetes.io/cluster-service: "true"
         template:
           metadata:
             labels:
               k8s-app: node-problem-detector
               version: v0.1
               kubernetes.io/cluster-service: "true"
           spec:
             hostNetwork: true
             containers:
             - name: node-problem-detector
               image: registry.k8s.io/node-problem-detector:v0.1
               securityContext:
                 privileged: true
               resources:
                 limits:
                   cpu: "200m"
                   memory: "100Mi"
                 requests:
                   cpu: "20m"
                   memory: "20Mi"
               volumeMounts:
               - name: log
                 mountPath: /log
                 readOnly: true
               - name: config # 使用 ConfigMap 卷中的数据覆盖 config/ 目录内容
                 mountPath: /config
                 readOnly: true
             volumes:
             - name: log
               hostPath:
                 path: /var/log/
             - name: config # 定义 ConfigMap 卷
               configMap:
                 name: node-problem-detector-config
  4. 使用新的配置文件重新创建节点问题检测器:

    # 如果你正在运行节点问题检测器,请先删除,然后再重新创建
    kubectl delete -f https://k8s.io/examples/debug/node-problem-detector.yaml
    kubectl apply -f https://k8s.io/examples/debug/node-problem-detector-configmap.yaml
    

如果节点问题检测器作为集群插件运行,则不支持覆盖配置。 插件管理器不支持 ConfigMap

问题守护程序

问题守护程序是节点问题检测器的子守护程序。 它监视特定类型的节点问题并报告给节点问题检测器。 支持下面几种类型的问题守护程序。

  • SystemLogMonitor 类型的守护程序根据预定义的规则监视系统日志并报告问题和指标。 你可以针对不同的日志源自定义配置如 filelogkmsgkernelabrtsystemd
  • SystemStatsMonitor 类型的守护程序收集各种与健康相关的系统统计数据作为指标。 你可以通过更新其配置文件来自定义其行为。
  • CustomPluginMonitor 类型的守护程序通过运行用户定义的脚本来调用和检查各种节点问题。 你可以使用不同的自定义插件监视器来监视不同的问题,并通过更新 配置文件 来定制守护程序行为。
  • HealthChecker 类型的守护程序检查节点上的 kubelet 和容器运行时的健康状况。

增加对其他日志格式的支持

系统日志监视器目前支持基于文件的日志、journald 和 kmsg。 可以通过实现一个新的 log watcher 来添加额外的日志源。

添加自定义插件监视器

你可以通过开发自定义插件来扩展节点问题检测器,以执行以任何语言编写的任何监控脚本。 监控脚本必须符合退出码和标准输出的插件协议。 有关更多信息,请参阅 插件接口提案.

导出器

导出器(Exporter)向特定后端报告节点问题和/或指标。 支持下列导出器:

  • Kubernetes exporter:此导出器向 Kubernetes API 服务器报告节点问题。 临时问题报告为事件,永久性问题报告为节点状况。

  • Prometheus exporter:此导出器在本地将节点问题和指标报告为 Prometheus(或 OpenMetrics)指标。 你可以使用命令行参数指定导出器的 IP 地址和端口。

  • Stackdriver exporter:此导出器向 Stackdriver Monitoring API 报告节点问题和指标。 可以使用配置文件自定义导出行为。

建议和限制

建议在集群中运行节点问题检测器以监控节点运行状况。 运行节点问题检测器时,你可以预期每个节点上的额外资源开销。 通常这是可接受的,因为:

  • 内核日志增长相对缓慢。
  • 已经为节点问题检测器设置了资源限制。
  • 即使在高负载下,资源使用也是可接受的。有关更多信息,请参阅节点问题检测器 基准结果

5 - 使用 crictl 对 Kubernetes 节点进行调试

特性状态: Kubernetes v1.11 [stable]

crictl 是 CRI 兼容的容器运行时命令行接口。 你可以使用它来检查和调试 Kubernetes 节点上的容器运行时和应用程序。 crictl 和它的源代码在 cri-tools 代码库。

准备开始

crictl 需要带有 CRI 运行时的 Linux 操作系统。

安装 crictl

你可以从 cri-tools 发布页面 下载一个压缩的 crictl 归档文件,用于几种不同的架构。 下载与你的 kubernetes 版本相对应的版本。 提取它并将其移动到系统路径上的某个位置,例如 /usr/local/bin/

一般用法

crictl 命令有几个子命令和运行时参数。 有关详细信息,请使用 crictl helpcrictl <subcommand> help 获取帮助信息。

你可以用以下方法之一来为 crictl 设置端点:

  • 设置参数 --runtime-endpoint--image-endpoint
  • 设置环境变量 CONTAINER_RUNTIME_ENDPOINTIMAGE_SERVICE_ENDPOINT
  • 在配置文件 --config=/etc/crictl.yaml 中设置端点。 要设置不同的文件,可以在运行 crictl 时使用 --config=PATH_TO_FILE 标志。

你还可以在连接到服务器并启用或禁用调试时指定超时值,方法是在配置文件中指定 timeoutdebug 值,或者使用 --timeout--debug 命令行参数。

要查看或编辑当前配置,请查看或编辑 /etc/crictl.yaml 的内容。 例如,使用 containerd 容器运行时的配置会类似于这样:

runtime-endpoint: unix:///var/run/containerd/containerd.sock
image-endpoint: unix:///var/run/containerd/containerd.sock
timeout: 10
debug: true

要进一步了解 crictl,参阅 crictl 文档

crictl 命令示例

打印 Pod 清单

打印所有 Pod 的清单:

crictl pods

输出类似于:

POD ID              CREATED              STATE               NAME                         NAMESPACE           ATTEMPT
926f1b5a1d33a       About a minute ago   Ready               sh-84d7dcf559-4r2gq          default             0
4dccb216c4adb       About a minute ago   Ready               nginx-65899c769f-wv2gp       default             0
a86316e96fa89       17 hours ago         Ready               kube-proxy-gblk4             kube-system         0
919630b8f81f1       17 hours ago         Ready               nvidia-device-plugin-zgbbv   kube-system         0

根据名称打印 Pod 清单:

crictl pods --name nginx-65899c769f-wv2gp

输出类似于这样:

POD ID              CREATED             STATE               NAME                     NAMESPACE           ATTEMPT
4dccb216c4adb       2 minutes ago       Ready               nginx-65899c769f-wv2gp   default             0

根据标签打印 Pod 清单:

crictl pods --label run=nginx

输出类似于这样:

POD ID              CREATED             STATE               NAME                     NAMESPACE           ATTEMPT
4dccb216c4adb       2 minutes ago       Ready               nginx-65899c769f-wv2gp   default             0

打印镜像清单

打印所有镜像清单:

crictl images

输出类似于这样:

IMAGE                                     TAG                 IMAGE ID            SIZE
busybox                                   latest              8c811b4aec35f       1.15MB
k8s-gcrio.azureedge.net/hyperkube-amd64   v1.10.3             e179bbfe5d238       665MB
k8s-gcrio.azureedge.net/pause-amd64       3.1                 da86e6ba6ca19       742kB
nginx                                     latest              cd5239a0906a6       109MB

根据仓库打印镜像清单:

crictl images nginx

输出类似于这样:

IMAGE               TAG                 IMAGE ID            SIZE
nginx               latest              cd5239a0906a6       109MB

只打印镜像 ID:

crictl images -q

输出类似于这样:

sha256:8c811b4aec35f259572d0f79207bc0678df4c736eeec50bc9fec37ed936a472a
sha256:e179bbfe5d238de6069f3b03fccbecc3fb4f2019af741bfff1233c4d7b2970c5
sha256:da86e6ba6ca197bf6bc5e9d900febd906b133eaa4750e6bed647b0fbe50ed43e
sha256:cd5239a0906a6ccf0562354852fae04bc5b52d72a2aff9a871ddb6bd57553569

打印容器清单

打印所有容器清单:

crictl ps -a

输出类似于这样:

CONTAINER ID        IMAGE                                                                                                             CREATED             STATE               NAME                       ATTEMPT
1f73f2d81bf98       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   7 minutes ago       Running             sh                         1
9c5951df22c78       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   8 minutes ago       Exited              sh                         0
87d3992f84f74       nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f                                     8 minutes ago       Running             nginx                      0
1941fb4da154f       k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a   18 hours ago        Running             kube-proxy                 0

打印正在运行的容器清单:

crictl ps

输出类似于这样:

CONTAINER ID        IMAGE                                                                                                             CREATED             STATE               NAME                       ATTEMPT
1f73f2d81bf98       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   6 minutes ago       Running             sh                         1
87d3992f84f74       nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f                                     7 minutes ago       Running             nginx                      0
1941fb4da154f       k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a   17 hours ago        Running             kube-proxy                 0

在正在运行的容器上执行命令

crictl exec -i -t 1f73f2d81bf98 ls

输出类似于这样:

bin   dev   etc   home  proc  root  sys   tmp   usr   var

获取容器日志

获取容器的所有日志:

crictl logs 87d3992f84f74

输出类似于这样:

10.240.0.96 - - [06/Jun/2018:02:45:49 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:50 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

获取最近的 N 行日志:

crictl logs --tail=1 87d3992f84f74

输出类似于这样:

10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

运行 Pod 沙盒

crictl 运行 Pod 沙盒对容器运行时排错很有帮助。 在运行的 Kubernetes 集群中,沙盒会随机地被 kubelet 停止和删除。

  1. 编写下面的 JSON 文件:

    {
      "metadata": {
        "name": "nginx-sandbox",
        "namespace": "default",
        "attempt": 1,
        "uid": "hdishd83djaidwnduwk28bcsb"
      },
      "log_directory": "/tmp",
      "linux": {
      }
    }
    
  1. 使用 crictl runp 命令应用 JSON 文件并运行沙盒。

    crictl runp pod-config.json
    

    返回了沙盒的 ID。

创建容器

crictl 创建容器对容器运行时排错很有帮助。 在运行的 Kubernetes 集群中,容器最终将被 kubelet 停止和删除。

  1. 拉取 busybox 镜像

    crictl pull busybox
    
    Image is up to date for busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
    
  1. 创建 Pod 和容器的配置:

    Pod 配置

    {
      "metadata": {
        "name": "busybox-sandbox",
        "namespace": "default",
        "attempt": 1,
        "uid": "aewi4aeThua7ooShohbo1phoj"
      },
      "log_directory": "/tmp",
      "linux": {
      }
    }
    

    容器配置

    {
      "metadata": {
        "name": "busybox"
      },
      "image":{
        "image": "busybox"
      },
      "command": [
        "top"
      ],
      "log_path":"busybox.log",
      "linux": {
      }
    }
    
  1. 创建容器,传递先前创建的 Pod 的 ID、容器配置文件和 Pod 配置文件。返回容器的 ID。

    crictl create f84dd361f8dc51518ed291fbadd6db537b0496536c1d2d6c05ff943ce8c9a54f container-config.json pod-config.json
    
  1. 查询所有容器并确认新创建的容器状态为 Created

    crictl ps -a
    

    输出类似于这样:

    CONTAINER ID        IMAGE               CREATED             STATE               NAME                ATTEMPT
    3e025dd50a72d       busybox             32 seconds ago      Created             busybox             0
    

启动容器

要启动容器,要将容器 ID 传给 crictl start

crictl start 3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60

输出类似于这样:

3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60

确认容器的状态为 Running

crictl ps

输出类似于这样:

CONTAINER ID   IMAGE    CREATED              STATE    NAME     ATTEMPT
3e025dd50a72d  busybox  About a minute ago   Running  busybox  0

接下来

6 - Windows 调试技巧

工作节点级别排障

  1. 我的 Pod 都卡在 “Container Creating” 或者不断重启

    确保你的 pause 镜像跟你的 Windows 版本兼容。 查看 Pause 容器 以了解最新的或建议的 pause 镜像,或者了解更多信息。

  1. 我的 Pod 状态显示 'ErrImgPull' 或者 'ImagePullBackOff'

    保证你的 Pod 被调度到兼容的 Windows 节点上。

    关于如何为你的 Pod 指定一个兼容节点, 可以查看这个指可以查看这个指南 以了解更多的信息。

网络排障

  1. 我的 Windows Pod 没有网络连接

    如果你使用的是虚拟机,请确保所有 VM 网卡上都已启用 MAC spoofing。

  1. 我的 Windows Pod 不能 ping 通外界资源

    Windows Pod 没有为 ICMP 协议编写出站规则,但 TCP/UDP 是支持的。当试图演示与集群外部资源的连接时, 可以把 ping <IP> 替换为 curl <IP> 命令。

    如果你仍然遇到问题,很可能你需要额外关注 cni.conf 的配置。你可以随时编辑这个静态文件。更新配置将应用于新的 Kubernetes 资源。

    Kubernetes 的网络需求之一(查看 Kubernetes 模型) 是集群通信不需要内部的 NAT。 为了遵守这一要求,对于你不希望发生的出站 NAT 通信,这里有一个 ExceptionList。 然而,这也意味着你需要从 ExceptionList 中去掉你试图查询的外部 IP。 只有这样,来自你的 Windows Pod 的流量才会被正确地 SNAT 转换,以接收来自外部环境的响应。 就此而言,你的 cni.conf 中的 ExceptionList 应该如下所示:

    "ExceptionList": [
        "10.244.0.0/16",  # 集群子网
        "10.96.0.0/12",   # 服务子网
        "10.127.130.0/24" # 管理(主机)子网
    ]
    
  1. 我的 Windows 节点无法访问 NodePort 类型 Service

    从节点本身访问本地 NodePort 失败,是一个已知的限制。 你可以从其他节点或外部客户端正常访问 NodePort。

  1. 容器的 vNIC 和 HNS 端点正在被删除

    hostname-override 参数没有传递给 kube-proxy 时可能引发这一问题。想要解决这个问题,用户需要将主机名传递给 kube-proxy,如下所示:

    C:\k\kube-proxy.exe --hostname-override=$(hostname)
    
  1. 我的 Windows 节点无法通过服务 IP 访问我的服务

    这是 Windows 上网络栈的一个已知限制。但是 Windows Pod 可以访问 Service IP。

  1. 启动 kubelet 时找不到网络适配器

    Windows 网络栈需要一个虚拟适配器才能使 Kubernetes 网络工作。 如果以下命令没有返回结果(在管理员模式的 shell 中), 则意味着创建虚拟网络失败,而虚拟网络的存在是 kubelet 正常工作的前提:

    Get-HnsNetwork | ? Name -ieq "cbr0"
    Get-NetAdapter | ? Name -Like "vEthernet (Ethernet*"
    

    如果主机的网络适配器不是 "Ethernet",通常有必要修改 start.ps1 脚本的 InterfaceName 参数。否则,如果虚拟网络创建过程出错,请检查 start-kubelet.ps1 脚本的输出。

  1. DNS 解析工作异常

    查阅这一节了解 Windows 系统上的 DNS 限制。

  1. kubectl port-forward 失败,错误为 "unable to do port forwarding: wincat not found"

    在 Kubernetes 1.15 中,pause 基础架构容器 mcr.microsoft.com/oss/kubernetes/pause:3.6 中包含 wincat.exe 来实现端口转发。 请确保使用 Kubernetes 的受支持版本。如果你想构建自己的 pause 基础架构容器, 请确保其中包含 wincat

  1. 我的 Kubernetes 安装失败,因为我的 Windows 服务器节点使用了代理服务器

    如果使用了代理服务器,必须定义下面的 PowerShell 环境变量:

    [Environment]::SetEnvironmentVariable("HTTP_PROXY", "http://proxy.example.com:80/", [EnvironmentVariableTarget]::Machine)
    [Environment]::SetEnvironmentVariable("HTTPS_PROXY", "http://proxy.example.com:443/", [EnvironmentVariableTarget]::Machine)
    

Flannel 故障排查

  1. 使用 Flannel 时,我的节点在重新加入集群后出现问题

    当先前删除的节点重新加入集群时, flannelD 尝试为节点分配一个新的 Pod 子网。 用户应该在以下路径中删除旧的 Pod 子网配置文件:

    Remove-Item C:\k\SourceVip.json
    Remove-Item C:\k\SourceVipRequest.json
    
  1. Flanneld 卡在 "Waiting for the Network to be created"

    关于这个问题有很多报告; 很可能是 Flannel 网络管理 IP 的设置时机问题。 一个变通方法是重新启动 start.ps1 或按如下方式手动重启:

    [Environment]::SetEnvironmentVariable("NODE_NAME", "<Windows 工作节点主机名>")
    C:\flannel\flanneld.exe --kubeconfig-file=c:\k\config --iface=<Windows 工作节点 IP> --ip-masq=1 --kube-subnet-mgr=1
    
  1. 我的 Windows Pod 无法启动,因为缺少 /run/flannel/subnet.env

    这表明 Flannel 没有正确启动。你可以尝试重启 flanneld.exe 或者你可以将 Kubernetes 控制节点的 /run/flannel/subnet.env 文件手动拷贝到 Windows 工作节点上,放在 C:\run\flannel\subnet.env; 并且将 FLANNEL_SUBNET 行修改为不同取值。例如,如果期望节点子网为 10.244.4.1/24:

    FLANNEL_NETWORK=10.244.0.0/16
    FLANNEL_SUBNET=10.244.4.1/24
    FLANNEL_MTU=1500
    FLANNEL_IPMASQ=true
    

进一步探查

如果这些步骤都不能解决你的问题,你可以通过以下方式获得关于在 Kubernetes 中运行 Windows 容器的帮助:

7 - 审计

Kubernetes 审计(Auditing) 功能提供了与安全相关的、按时间顺序排列的记录集, 记录每个用户、使用 Kubernetes API 的应用以及控制面自身引发的活动。

审计功能使得集群管理员能够回答以下问题:

  • 发生了什么?
  • 什么时候发生的?
  • 谁触发的?
  • 活动发生在哪个(些)对象上?
  • 在哪观察到的?
  • 它从哪触发的?
  • 活动的后续处理行为是什么?

审计记录最初产生于 kube-apiserver 内部。每个请求在不同执行阶段都会生成审计事件;这些审计事件会根据特定策略被预处理并写入后端。 策略确定要记录的内容和用来存储记录的后端,当前的后端支持日志文件和 webhook。

每个请求都可被记录其相关的阶段(stage)。已定义的阶段有:

  • RequestReceived - 此阶段对应审计处理器接收到请求后, 并且在委托给其余处理器之前生成的事件。
  • ResponseStarted - 在响应消息的头部发送后,响应消息体发送前生成的事件。 只有长时间运行的请求(例如 watch)才会生成这个阶段。
  • ResponseComplete - 当响应消息体完成并且没有更多数据需要传输的时候。
  • Panic - 当 panic 发生时生成。

审计日志记录功能会增加 API server 的内存消耗,因为需要为每个请求存储审计所需的某些上下文。 内存消耗取决于审计日志记录的配置。

审计策略

审计策略定义了关于应记录哪些事件以及应包含哪些数据的规则。 审计策略对象结构定义在 audit.k8s.io API 组。 处理事件时,将按顺序与规则列表进行比较。第一个匹配规则设置事件的审计级别(Audit Level)。 已定义的审计级别有:

  • None - 符合这条规则的日志将不会记录。
  • Metadata - 记录请求的元数据(请求的用户、时间戳、资源、动词等等), 但是不记录请求或者响应的消息体。
  • Request - 记录事件的元数据和请求的消息体,但是不记录响应的消息体。 这不适用于非资源类型的请求。
  • RequestResponse - 记录事件的元数据,请求和响应的消息体。这不适用于非资源类型的请求。

你可以使用 --audit-policy-file 标志将包含策略的文件传递给 kube-apiserver。 如果不设置该标志,则不记录事件。 注意 rules 字段必须在审计策略文件中提供。没有(0)规则的策略将被视为非法配置。

以下是一个审计策略文件的示例:

apiVersion: audit.k8s.io/v1 # 这是必填项。
kind: Policy
# 不要在 RequestReceived 阶段为任何请求生成审计事件。
omitStages:
  - "RequestReceived"
rules:
  # 在日志中用 RequestResponse 级别记录 Pod 变化。
  - level: RequestResponse
    resources:
    - group: ""
      # 资源 "pods" 不匹配对任何 Pod 子资源的请求,
      # 这与 RBAC 策略一致。
      resources: ["pods"]
  # 在日志中按 Metadata 级别记录 "pods/log"、"pods/status" 请求
  - level: Metadata
    resources:
    - group: ""
      resources: ["pods/log", "pods/status"]

  # 不要在日志中记录对名为 "controller-leader" 的 configmap 的请求。
  - level: None
    resources:
    - group: ""
      resources: ["configmaps"]
      resourceNames: ["controller-leader"]

  # 不要在日志中记录由 "system:kube-proxy" 发出的对端点或服务的监测请求。
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
    - group: "" # core API 组
      resources: ["endpoints", "services"]

  # 不要在日志中记录对某些非资源 URL 路径的已认证请求。
  - level: None
    userGroups: ["system:authenticated"]
    nonResourceURLs:
    - "/api*" # 通配符匹配。
    - "/version"

  # 在日志中记录 kube-system 中 configmap 变更的请求消息体。
  - level: Request
    resources:
    - group: "" # core API 组
      resources: ["configmaps"]
    # 这个规则仅适用于 "kube-system" 名字空间中的资源。
    # 空字符串 "" 可用于选择非名字空间作用域的资源。
    namespaces: ["kube-system"]

  # 在日志中用 Metadata 级别记录所有其他名字空间中的 configmap 和 secret 变更。
  - level: Metadata
    resources:
    - group: "" # core API 组
      resources: ["secrets", "configmaps"]

  # 在日志中以 Request 级别记录所有其他 core 和 extensions 组中的资源操作。
  - level: Request
    resources:
    - group: "" # core API 组
    - group: "extensions" # 不应包括在内的组版本。

  # 一个抓取所有的规则,将在日志中以 Metadata 级别记录所有其他请求。
  - level: Metadata
    # 符合此规则的 watch 等长时间运行的请求将不会
    # 在 RequestReceived 阶段生成审计事件。
    omitStages:
      - "RequestReceived"

你可以使用最低限度的审计策略文件在 Metadata 级别记录所有请求:

# 在 Metadata 级别为所有请求生成日志
apiVersion: audit.k8s.io/v1beta1
kind: Policy
rules:
- level: Metadata

如果你在打磨自己的审计配置文件,你可以使用为 Google Container-Optimized OS 设计的审计配置作为出发点。你可以参考 configure-helper.sh 脚本,该脚本能够生成审计策略文件。你可以直接在脚本中看到审计策略的绝大部份内容。

你也可以参考 Policy 配置参考 以获取有关已定义字段的详细信息。

审计后端

审计后端实现将审计事件导出到外部存储。kube-apiserver 默认提供两个后端:

  • Log 后端,将事件写入到文件系统
  • Webhook 后端,将事件发送到外部 HTTP API

在这所有情况下,审计事件均遵循 Kubernetes API 在 audit.k8s.io API 组 中定义的结构。

Log 后端

Log 后端将审计事件写入 JSONlines 格式的文件。 你可以使用以下 kube-apiserver 标志配置 Log 审计后端:

  • --audit-log-path 指定用来写入审计事件的日志文件路径。不指定此标志会禁用日志后端。- 意味着标准化
  • --audit-log-maxage 定义保留旧审计日志文件的最大天数
  • --audit-log-maxbackup 定义要保留的审计日志文件的最大数量
  • --audit-log-maxsize 定义审计日志文件轮转之前的最大大小(兆字节)

如果你的集群控制面以 Pod 的形式运行 kube-apiserver,记得要通过 hostPath 卷来访问策略文件和日志文件所在的目录,这样审计记录才会持久保存下来。例如:

  - --audit-policy-file=/etc/kubernetes/audit-policy.yaml
  - --audit-log-path=/var/log/kubernetes/audit/audit.log

接下来挂载数据卷:

...
volumeMounts:
  - mountPath: /etc/kubernetes/audit-policy.yaml
    name: audit
    readOnly: true
  - mountPath: /var/log/kubernetes/audit/
    name: audit-log
    readOnly: false

最后配置 hostPath

...
volumes:
- name: audit
  hostPath:
    path: /etc/kubernetes/audit-policy.yaml
    type: File

- name: audit-log
  hostPath:
    path: /var/log/kubernetes/audit/
    type: DirectoryOrCreate

Webhook 后端

Webhook 后端将审计事件发送到远程 Web API,该远程 API 应该暴露与 kube-apiserver 形式相同的 API,包括其身份认证机制。你可以使用如下 kube-apiserver 标志来配置 Webhook 审计后端:

  • --audit-webhook-config-file 设置 Webhook 配置文件的路径。Webhook 配置文件实际上是一个 kubeconfig 文件
  • --audit-webhook-initial-backoff 指定在第一次失败后重发请求等待的时间。随后的请求将以指数退避重试。

Webhook 配置文件使用 kubeconfig 格式指定服务的远程地址和用于连接它的凭据。

事件批处理

日志和 Webhook 后端都支持批处理。以 Webhook 为例,以下是可用参数列表。要获取日志 后端的同样参数,请在参数名称中将 webhook 替换为 log。 默认情况下,在 webhook 中批处理是被启用的,在 log 中批处理是被禁用的。 同样,默认情况下,在 webhook 中启用带宽限制,在 log 中禁用带宽限制。

  • --audit-webhook-mode 定义缓存策略,可选值如下:
    • batch - 以批处理缓存事件和异步的过程。这是默认值。
    • blocking - 在 API 服务器处理每个单独事件时,阻塞其响应。
    • blocking-strict - 与 blocking 相同,不过当审计日志在 RequestReceived 阶段失败时,整个 API 服务请求会失效。

以下参数仅用于 batch 模式:

  • --audit-webhook-batch-buffer-size 定义 batch 之前要缓存的事件数。 如果传入事件的速率溢出缓存区,则会丢弃事件。
  • --audit-webhook-batch-max-size 定义一个 batch 中的最大事件数。
  • --audit-webhook-batch-max-wait 无条件 batch 队列中的事件前等待的最大事件。
  • --audit-webhook-batch-throttle-qps 每秒生成的最大批次数。
  • --audit-webhook-batch-throttle-burst 在达到允许的 QPS 前,同一时刻允许存在的最大 batch 生成数。

参数调整

需要设置参数以适应 API 服务器上的负载。

例如,如果 kube-apiserver 每秒收到 100 个请求,并且每个请求仅在 ResponseStartedResponseComplete 阶段进行审计,则应该考虑每秒生成约 200 个审计事件。 假设批处理中最多有 100 个事件,则应将限制级别设置为每秒至少 2 个查询。 假设后端最多需要 5 秒钟来写入事件,你应该设置缓冲区大小以容纳最多 5 秒的事件, 即 10 个 batch,即 1000 个事件。

但是,在大多数情况下,默认参数应该足够了,你不必手动设置它们。 你可以查看 kube-apiserver 公开的以下 Prometheus 指标,并在日志中监控审计子系统的状态。

  • apiserver_audit_event_total 包含所有暴露的审计事件数量的指标。
  • apiserver_audit_error_total 在暴露时由于发生错误而被丢弃的事件的数量。

日志条目截断

日志后端和 Webhook 后端都支持限制所输出的事件大小。 例如,下面是可以为日志后端配置的标志列表:

  • audit-log-truncate-enabled:是否弃用事件和批次的截断处理。
  • audit-log-truncate-max-batch-size:向下层后端发送的各批次的最大字节数。
  • audit-log-truncate-max-event-size:向下层后端发送的审计事件的最大字节数。

默认情况下,截断操作在 webhooklog 后端都是被禁用的,集群管理员需要设置 audit-log-truncate-enabledaudit-webhook-truncate-enabled 标志来启用此操作。

接下来

8 - 使用 telepresence 在本地开发和调试服务

Kubernetes 应用程序通常由多个独立的服务组成,每个服务都在自己的容器中运行。 在远端的 Kubernetes 集群上开发和调试这些服务可能很麻烦, 需要在运行的容器上打开 Shell, 以运行调试工具。

telepresence 是一个工具,用于简化本地开发和调试服务的过程,同时可以将服务代理到远程 Kubernetes 集群。 telepresence 允许你使用自定义工具(例如调试器和 IDE)调试本地服务, 并能够让此服务完全访问 ConfigMap、Secret 和远程集群上运行的服务。

本文档描述如何在本地使用 telepresence 开发和调试远程集群上运行的服务。

准备开始

  • Kubernetes 集群安装完毕
  • 配置好 kubectl 与集群交互
  • Telepresence 安装完毕

从本机连接到远程 Kubernetes 集群

安装 telepresence 后,运行 telepresence connect 来启动它的守护进程并将本地工作站连接到远程 Kubernetes 集群。

$ telepresence connect
 
Launching Telepresence Daemon
...
Connected to context default (https://<cluster public IP>)

你可以通过 curl 使用 Kubernetes 语法访问服务,例如:curl -ik https://kubernetes.default

开发和调试现有的服务

在 Kubernetes 上开发应用程序时,通常对单个服务进行编程或调试。 服务可能需要访问其他服务以进行测试和调试。 一种选择是使用连续部署流水线,但即使最快的部署流水线也会在程序或调试周期中引入延迟。

使用 telepresence intercept $SERVICE_NAME --port $LOCAL_PORT:$REMOTE_PORT 命令创建一个 "拦截器" 用于重新路由远程服务流量。

环境变量:

  • $SERVICE_NAME 是本地服务名称
  • $LOCAL_PORT 是服务在本地工作站上运行的端口
  • $REMOTE_PORT 是服务在集群中侦听的端口

运行此命令会告诉 Telepresence 将远程流量发送到本地服务,而不是远程 Kubernetes 集群中的服务中。 在本地编辑保存服务源代码,并在访问远程应用时查看相应变更会立即生效。 还可以使用调试器或任何其他本地开发工具运行本地服务。

Telepresence 是如何工作的?

Telepresence 会在远程集群中运行的现有应用程序容器旁边安装流量代理 Sidecar。 当它捕获进入 Pod 的所有流量请求时,不是将其转发到远程集群中的应用程序, 而是路由所有流量(当创建全局拦截器时) 或流量的一个子集(当创建自定义拦截器时) 到本地开发环境。

接下来

如果你对实践教程感兴趣, 请查看本教程, 其中介绍了如何在 Google Kubernetes Engine 上本地开发 Guestbook 应用程序。

如需进一步了解,请访问 Telepresence 官方网站

9 - 用 Kubectl 调试 Kubernetes 节点

本页演示如何使用 kubectl debug 命令调试在 Kubernetes 集群上运行的节点

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 1.2. 要获知版本信息,请输入 kubectl version.

你需要有权限创建 Pod 并将这些新 Pod 分配到任意节点。 你还需要被授权创建能够访问主机上文件系统的 Pod。

使用 kubectl debug node 调试节点

使用 kubectl debug node 命令将 Pod 部署到要排查故障的节点上。 此命令在你无法使用 SSH 连接节点时比较有用。 当 Pod 被创建时,Pod 会在节点上打开一个交互的 Shell。 要在名为 “mynode” 的节点上创建一个交互式 Shell,运行:

kubectl debug node/mynode -it --image=ubuntu
Creating debugging pod node-debugger-mynode-pdx84 with container debugger on node mynode.
If you don't see a command prompt, try pressing enter.
root@mynode:/#

调试命令有助于收集信息和排查问题。 你可能使用的命令包括 ipifconfigncpingps 等等。 你还可以从各种包管理器安装 mtrtcpdumpcurl 等其他工具。

用于调试的 Pod 可以访问节点的根文件系统,该文件系统挂载在 Pod 中的 /host 路径。 如果你在 filesystem 名字空间中运行 kubelet, 则正调试的 Pod 将看到此名字空间的根,而不是整个节点的根。 对于典型的 Linux 节点,你可以查看以下路径找到一些重要的日志:

/host/var/log/kubelet.log
负责在节点上运行容器的 kubelet 所产生的日志。
/host/var/log/kube-proxy.log
负责将流量导向到 Service 端点的 kube-proxy 所产生的日志。
/host/var/log/containerd.log
在节点上运行的 containerd 进程所产生的日志。
/host/var/log/syslog
显示常规消息以及系统相关信息。
/host/var/log/kern.log
显示内核日志。

当在节点上创建一个调试会话时,需谨记:

  • kubectl debug 根据节点的名称自动生成新 Pod 的名称。
  • 节点的根文件系统将被挂载在 /host
  • 尽管容器运行在主机 IPC、Network 和 PID 名字空间中,但 Pod 没有特权。 这意味着读取某些进程信息可能会失败,这是因为访问这些信息仅限于超级用户 (superuser)。 例如,chroot /host 将失败。如果你需要一个有特权的 Pod,请手动创建。

清理现场

当你使用正调试的 Pod 完成时,将其删除:

kubectl get pods
NAME                          READY   STATUS       RESTARTS   AGE
node-debugger-mynode-pdx84    0/1     Completed    0          8m1s
# 相应更改 Pod 名称
kubectl delete pod node-debugger-mynode-pdx84 --now
pod "node-debugger-mynode-pdx84" deleted