kubeadm 故障排除

与任何程序一样,您在安装或运行 kubeadm 时可能会遇到错误。 此页面列出了一些常见的故障场景,并提供了可以帮助您理解和修复问题的步骤。

如果您的问题未在下面列出,请按照以下步骤操作

  • 如果您认为您的问题是 kubeadm 中的一个错误

  • 如果您不确定 kubeadm 的工作方式,可以在 Slack#kubeadm 中提问,或在 StackOverflow 上提出问题。 请包含相关的标签,例如 #kubernetes#kubeadm,以便大家可以帮助您。

无法将 v1.18 节点加入到 v1.17 集群,因为缺少 RBAC

在 v1.18 中,kubeadm 添加了防止将具有相同名称的节点加入集群的功能。 这需要为 bootstrap-token 用户添加 RBAC,以便能够获取 Node 对象。

但是,这会导致 v1.18 的 kubeadm join 无法加入由 kubeadm v1.17 创建的集群。

要解决此问题,您有两种选择:

在控制平面节点上使用 kubeadm v1.18 执行 kubeadm init phase bootstrap-token。 请注意,这将启用其余的 bootstrap-token 权限。

或者

使用 kubectl apply -f ... 手动应用以下 RBAC

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kubeadm:get-nodes
rules:
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kubeadm:get-nodes
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kubeadm:get-nodes
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: Group
    name: system:bootstrappers:kubeadm:default-node-token

ebtables 或类似的执行文件在安装过程中未找到

如果在运行 kubeadm init 时看到以下警告

[preflight] WARNING: ebtables not found in system path
[preflight] WARNING: ethtool not found in system path

那么您的节点可能缺少 ebtablesethtool 或类似的执行文件。 您可以使用以下命令安装它们:

  • 对于 Ubuntu/Debian 用户,运行 apt install ebtables ethtool
  • 对于 CentOS/Fedora 用户,运行 yum install ebtables ethtool

kubeadm 在安装过程中阻塞等待控制平面

如果您注意到 kubeadm init 在打印以下行后挂起

[apiclient] Created API client, waiting for the control plane to become ready

这可能是由多种问题引起的。 最常见的原因是:

  • 网络连接问题。 在继续之前,请检查您的机器是否具有完整的网络连接。
  • 容器运行时的 cgroup 驱动程序与 kubelet 的 cgroup 驱动程序不同。 要了解如何正确配置它,请参阅 配置 cgroup 驱动程序
  • 控制平面容器崩溃循环或挂起。 您可以通过运行 docker ps 并通过运行 docker logs 调查每个容器来检查这一点。 对于其他容器运行时,请参阅 使用 crictl 调试 Kubernetes 节点

kubeadm 在删除托管容器时阻塞

如果容器运行时停止并且不删除任何 Kubernetes 托管容器,则可能会发生以下情况

sudo kubeadm reset
[preflight] Running pre-flight checks
[reset] Stopping the kubelet service
[reset] Unmounting mounted directories in "/var/lib/kubelet"
[reset] Removing kubernetes-managed containers
(block)

一种可能的解决方案是重新启动容器运行时,然后重新运行 kubeadm reset。 您还可以使用 crictl 调试容器运行时的状态。 请参阅 使用 crictl 调试 Kubernetes 节点

Pod 处于 RunContainerErrorCrashLoopBackOffError 状态

kubeadm init 之后,不应有任何 Pod 处于这些状态。

  • 如果在 kubeadm init 之后 有 Pod 处于这些状态之一,请在 kubeadm 仓库中打开一个 issue。
  • 如果在部署网络插件后看到 Pod 处于 RunContainerErrorCrashLoopBackOffError 状态,并且 coredns (或 kube-dns) 没有发生任何变化,那么很可能是您安装的 Pod 网络插件出现了一些问题。 您可能需要授予它更多的 RBAC 权限或使用较新版本。 请在 Pod 网络提供程序的 issue tracker 中提交 issue 并进行 triage。

coredns 停留在 Pending 状态

这是预期的,并且是设计的一部分。 kubeadm 与网络提供程序无关,因此管理员应 安装所选的 Pod 网络插件。 在设置网络之前,CoreDNS 才能完全部署。 因此,在网络设置好之前,处于 Pending 状态。

HostPort 服务不起作用

HostPortHostIP 功能取决于您的 Pod 网络提供程序。 请联系 Pod 网络插件的作者,以了解是否可用 HostPortHostIP 功能。

Calico、Canal 和 Flannel CNI 提供程序已验证支持 HostPort。

有关更多信息,请参阅 CNI portmap 文档

如果您的网络提供程序不支持 portmap CNI 插件,则可能需要使用 服务的 NodePort 功能 或使用 HostNetwork=true

Pod 无法通过其 Service IP 访问

  • 许多网络插件尚未启用 hairpin 模式,该模式允许 Pod 通过其 Service IP 访问自身。 这是与 CNI 相关的问题。 请联系网络插件提供程序以获取其对 hairpin 模式的支持的最新状态。

  • 如果您使用的是 VirtualBox(直接或通过 Vagrant),则需要确保 hostname -i 返回可路由的 IP 地址。 默认情况下,第一个接口连接到不可路由的主机专用网络。 一种解决方法是修改 /etc/hosts,请参阅此 Vagrantfile 以获取示例。

TLS 证书错误

以下错误指示可能的证书不匹配。

# kubectl get pods
Unable to connect to the server: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "kubernetes")
  • 验证 $HOME/.kube/config 文件是否包含有效的证书,并在必要时重新生成证书。 kubeconfig 文件中的证书是 base64 编码的。 base64 --decode 命令可用于解码证书,openssl x509 -text -noout 可用于查看证书信息。

  • 使用以下命令取消设置 KUBECONFIG 环境变量:

    unset KUBECONFIG
    

    或者将其设置为默认的 KUBECONFIG 位置:

    export KUBECONFIG=/etc/kubernetes/admin.conf
    
  • 另一种解决方法是覆盖“admin”用户的现有 kubeconfig

    mv $HOME/.kube $HOME/.kube.bak
    mkdir $HOME/.kube
    sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    sudo chown $(id -u):$(id -g) $HOME/.kube/config
    

Kubelet 客户端证书轮换失败

默认情况下,kubeadm 配置一个 kubelet,通过使用 /var/lib/kubelet/pki/kubelet-client-current.pem 符号链接(在 /etc/kubernetes/kubelet.conf 中指定)自动轮换客户端证书。 如果此轮换过程失败,您可能会在 kube-apiserver 日志中看到类似 x509: certificate has expired or is not yet valid 的错误。 要修复此问题,您必须执行以下步骤:

  1. 备份并删除失败节点上的 /etc/kubernetes/kubelet.conf/var/lib/kubelet/pki/kubelet-client*

  2. 从集群中具有 /etc/kubernetes/pki/ca.key 的工作控制平面节点执行 kubeadm kubeconfig user --org system:nodes --client-name system:node:$NODE > kubelet.conf$NODE 必须设置为集群中现有失败节点的名称。 手动修改生成的 kubelet.conf 以调整集群名称和服务器端点,或传递 kubeconfig user --config(请参阅 生成其他用户的 kubeconfig 文件)。 如果您的集群没有 ca.key,则必须在外部签名 kubelet.conf 中嵌入的证书。

  3. 将此 kubelet.conf 复制到失败节点上的 /etc/kubernetes/kubelet.conf

  4. 重启 kubelet(systemctl restart kubelet)在失败的节点上,并等待 /var/lib/kubelet/pki/kubelet-client-current.pem 被重新创建。

  5. 手动编辑 kubelet.conf 以指向轮换后的 kubelet 客户端证书,方法是将 client-certificate-dataclient-key-data 替换为

    client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem
    client-key: /var/lib/kubelet/pki/kubelet-client-current.pem
    
  6. 重启 kubelet。

  7. 确保节点变为 Ready 状态。

Vagrant 中使用 flannel 作为 Pod 网络时的默认 NIC

以下错误可能表明 Pod 网络中出现问题

Error from server (NotFound): the server could not find the requested resource
  • 如果您在 Vagrant 内部使用 flannel 作为 Pod 网络,则需要指定 flannel 的默认接口名称。

    Vagrant 通常会将两个接口分配给所有 VM。第一个接口,所有主机都分配了 IP 地址 10.0.2.15,用于通过 NAT 进行外部流量传输。

    这可能会导致 flannel 出现问题,flannel 默认使用主机上的第一个接口。这会导致所有主机认为它们具有相同的公共 IP 地址。为了防止这种情况,将 --iface eth1 标志传递给 flannel,以便选择第二个接口。

容器使用的非公共 IP

在某些情况下,kubectl logskubectl run 命令可能会在其他方面功能正常的集群中返回以下错误

Error from server: Get https://10.19.0.41:10250/containerLogs/default/mysql-ddc65b868-glc5m/mysql: dial tcp 10.19.0.41:10250: getsockopt: no route to host
  • 这可能是因为 Kubernetes 使用了一个无法与其他位于看似相同子网上的 IP 通信的 IP,可能是由机器提供商的策略造成的。

  • DigitalOcean 为 eth0 分配一个公共 IP,以及一个用于内部使用的私有 IP,作为其浮动 IP 功能的锚点,但 kubelet 会选择后者作为节点的 InternalIP,而不是公共 IP。

    使用 ip addr show 检查此场景,而不是 ifconfig,因为 ifconfig 不会显示有问题的别名 IP 地址。或者,一个特定于 DigitalOcean 的 API 端点允许查询 droplet 的锚点 IP。

    curl http://169.254.169.254/metadata/v1/interfaces/public/0/anchor_ipv4/address
    

    解决方法是使用 --node-ip 告诉 kubelet 使用哪个 IP。在使用 DigitalOcean 时,可以使用公共 IP(分配给 eth0)或私有 IP(分配给 eth1),如果您想使用可选的私有网络。kubeadm NodeRegistrationOptions 结构kubeletExtraArgs 部分可用于此目的。

    然后重启 kubelet

    systemctl daemon-reload
    systemctl restart kubelet
    

coredns Pod 处于 CrashLoopBackOffError 状态

如果您有运行 SELinux 且 Docker 版本较旧的节点,您可能会遇到 coredns Pod 未启动的情况。要解决此问题,您可以尝试以下选项之一

kubectl -n kube-system get deployment coredns -o yaml | \
  sed 's/allowPrivilegeEscalation: false/allowPrivilegeEscalation: true/g' | \
  kubectl apply -f -

CoreDNS 处于 CrashLoopBackOff 的另一个原因是,在 Kubernetes 中部署的 CoreDNS Pod 检测到循环。 有许多解决方法 可用,以避免 Kubernetes 每次 CoreDNS 检测到循环并退出时尝试重新启动 CoreDNS Pod。

etcd Pod 不断重启

如果您遇到以下错误

rpc error: code = 2 desc = oci runtime error: exec failed: container_linux.go:247: starting container process caused "process_linux.go:110: decoding init error from pipe caused \"read parent: connection reset by peer\""

如果运行 CentOS 7 和 Docker 1.13.1.84,则会出现此问题。此版本的 Docker 可能会阻止 kubelet 进入 etcd 容器。

要解决此问题,您可以选择以下选项之一

  • 回滚到较早版本的 Docker,例如 1.13.1-75

    yum downgrade docker-1.13.1-75.git8633870.el7.centos.x86_64 docker-client-1.13.1-75.git8633870.el7.centos.x86_64 docker-common-1.13.1-75.git8633870.el7.centos.x86_64
    
  • 安装其中一个更推荐的最新版本,例如 18.06

    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    yum install docker-ce-18.06.1.ce-3.el7.x86_64
    

无法将逗号分隔的值列表传递给 --component-extra-args 标志中的参数

kubeadm init 标志,例如 --component-extra-args,允许您将自定义参数传递给 kube-apiserver 等控制平面组件。但是,由于用于解析值的底层类型(mapStringString)的限制,此机制受到限制。

如果您决定传递支持多个逗号分隔值的参数,例如 --apiserver-extra-args "enable-admission-plugins=LimitRanger,NamespaceExists",此标志将失败并显示 flag: malformed pair, expect string=string。这是因为 --apiserver-extra-args 的参数列表期望 key=value 对,并且在本例中,NamespacesExists 被视为缺少值的键。

或者,您可以尝试像这样分隔 key=value 对:--apiserver-extra-args "enable-admission-plugins=LimitRanger,enable-admission-plugins=NamespaceExists",但这会导致键 enable-admission-plugins 仅具有值 NamespaceExists

一个已知的解决方法是使用 kubeadm 配置文件

kube-proxy 在云控制器管理器初始化节点之前被调度

在云提供商场景中,kube-proxy 最终可能会在新工作节点上被调度,在云控制器管理器初始化节点地址之前。这会导致 kube-proxy 无法正确获取节点的 IP 地址,并对代理功能管理负载均衡器产生连锁反应。

可以在 kube-proxy Pod 中看到以下错误

server.go:610] Failed to retrieve node IP: host IP unknown; known addresses: []
proxier.go:340] invalid nodeIP, initializing kube-proxy with 127.0.0.1 as nodeIP

一个已知的解决方案是修补 kube-proxy DaemonSet,以允许将其调度到控制平面节点,无论其条件如何,使其远离其他节点,直到其初始保护条件减弱。

kubectl -n kube-system patch ds kube-proxy -p='{
  "spec": {
    "template": {
      "spec": {
        "tolerations": [
          {
            "key": "CriticalAddonsOnly",
            "operator": "Exists"
          },
          {
            "effect": "NoSchedule",
            "key": "node-role.kubernetes.io/control-plane"
          }
        ]
      }
    }
  }
}'

此问题的跟踪问题 在此

/usr 在节点上以只读方式挂载

在 Fedora CoreOS 或 Flatcar Container Linux 等 Linux 发行版上,目录 /usr 以只读文件系统挂载。对于 flex-volume 支持,Kubernetes 组件(如 kubelet 和 kube-controller-manager)使用默认路径 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/,但 flex-volume 目录必须是可写的,才能使该功能正常工作。

要解决此问题,可以使用 kubeadm 配置文件来配置 flex-volume 目录。

在主控制平面节点(使用 kubeadm init 创建)上,使用 --config 传递以下文件

apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
nodeRegistration:
  kubeletExtraArgs:
  - name: "volume-plugin-dir"
    value: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
controllerManager:
  extraArgs:
  - name: "flex-volume-plugin-dir"
    value: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"

在加入的节点上

apiVersion: kubeadm.k8s.io/v1beta4
kind: JoinConfiguration
nodeRegistration:
  kubeletExtraArgs:
  - name: "volume-plugin-dir"
    value: "/opt/libexec/kubernetes/kubelet-plugins/volume/exec/"

或者,您可以修改 /etc/fstab 以使 /usr 挂载可写,但请注意,这会修改 Linux 发行版的的设计原则。

kubeadm upgrade plan 打印出 context deadline exceeded 错误消息

当使用外部 etcd 在具有 kubeadm 的 Kubernetes 集群中升级时,会显示此错误消息。这不是一个关键错误,因为旧版本的 kubeadm 会对外部 etcd 集群执行版本检查。您可以继续使用 kubeadm upgrade apply ...

此问题已在版本 1.19 中修复。

kubeadm reset 卸载 /var/lib/kubelet

如果 /var/lib/kubelet 正在被挂载,则执行 kubeadm reset 将有效地卸载它。

要解决此问题,请在执行 kubeadm reset 操作后重新挂载 /var/lib/kubelet 目录。

这是在 kubeadm 1.15 中引入的回归。该问题已在 1.20 中修复。

无法在 kubeadm 集群中安全地使用 metrics-server

在 kubeadm 集群中,可以通过将 --kubelet-insecure-tls 传递给它来不安全地使用 metrics-server。不建议在生产集群中使用此方法。

如果您想在 metrics-server 和 kubelet 之间使用 TLS,则存在一个问题,因为 kubeadm 为 kubelet 部署了一个自签名服务证书。这可能会导致 metrics-server 侧出现以下错误

x509: certificate signed by unknown authority
x509: certificate is valid for IP-foo not IP-bar

请参阅 启用签名的 kubelet 服务证书,以了解如何在 kubeadm 集群中配置 kubelet 以使用正确签名的服务证书。

另请参阅 如何安全地运行 metrics-server

升级由于 etcd 哈希未更改而失败

仅适用于使用 kubeadm 二进制文件 v1.28.3 或更高版本升级控制平面节点,并且该节点当前由 kubeadm 版本 v1.28.0、v1.28.1 或 v1.28.2 管理。

您可能会遇到以下错误消息

[upgrade/etcd] Failed to upgrade etcd: couldn't upgrade control plane. kubeadm has tried to recover everything into the earlier state. Errors faced: static Pod hash for component etcd on Node kinder-upgrade-control-plane-1 did not change after 5m0s: timed out waiting for the condition
[upgrade/etcd] Waiting for previous etcd to become available
I0907 10:10:09.109104    3704 etcd.go:588] [etcd] attempting to see if all cluster endpoints ([https://172.17.0.6:2379/ https://172.17.0.4:2379/ https://172.17.0.3:2379/]) are available 1/10
[upgrade/etcd] Etcd was rolled back and is now available
static Pod hash for component etcd on Node kinder-upgrade-control-plane-1 did not change after 5m0s: timed out waiting for the condition
couldn't upgrade control plane. kubeadm has tried to recover everything into the earlier state. Errors faced
k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade.rollbackOldManifests
	cmd/kubeadm/app/phases/upgrade/staticpods.go:525
k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade.upgradeComponent
	cmd/kubeadm/app/phases/upgrade/staticpods.go:254
k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade.performEtcdStaticPodUpgrade
	cmd/kubeadm/app/phases/upgrade/staticpods.go:338
...

出现此故障的原因是受影响的版本会生成一个 etcd 清单文件,其中包含 PodSpec 中不需要的默认值。这将导致清单比较出现差异,并且 kubeadm 将期望 Pod 哈希发生更改,但 kubelet 永远不会更新哈希。

如果您在集群中看到此问题,有两种解决方法

  • etcd 升级可以在受影响的版本和 v1.28.3(或更高版本)之间跳过,方法是使用

    kubeadm upgrade {apply|node} [version] --etcd-upgrade=false
    

    不建议这样做,因为后续的 v1.28 补丁版本可能会引入新的 etcd 版本。

  • 在升级之前,修补 etcd 静态 Pod 的清单,以删除有问题的默认属性

    diff --git a/etc/kubernetes/manifests/etcd_defaults.yaml b/etc/kubernetes/manifests/etcd_origin.yaml
    index d807ccbe0aa..46b35f00e15 100644
    --- a/etc/kubernetes/manifests/etcd_defaults.yaml
    +++ b/etc/kubernetes/manifests/etcd_origin.yaml
    @@ -43,7 +43,6 @@ spec:
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
    -      successThreshold: 1
          timeoutSeconds: 15
        name: etcd
        resources:
    @@ -59,26 +58,18 @@ spec:
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
    -      successThreshold: 1
          timeoutSeconds: 15
    -    terminationMessagePath: /dev/termination-log
    -    terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /var/lib/etcd
          name: etcd-data
        - mountPath: /etc/kubernetes/pki/etcd
          name: etcd-certs
    -  dnsPolicy: ClusterFirst
    -  enableServiceLinks: true
      hostNetwork: true
      priority: 2000001000
      priorityClassName: system-node-critical
    -  restartPolicy: Always
    -  schedulerName: default-scheduler
      securityContext:
        seccompProfile:
          type: RuntimeDefault
    -  terminationGracePeriodSeconds: 30
      volumes:
      - hostPath:
          path: /etc/kubernetes/pki/etcd
    

有关更多信息,请参阅此错误的 跟踪问题

最后修改时间为 2024 年 7 月 5 日下午 4:06 PST:kubeadm:在所有文档示例中使用 v1beta4 (efc1133fa4)