kubeadm 故障排除

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

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

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

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

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

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

但是,这会导致 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 驱动程序
  • 控制平面容器出现循环崩溃或挂起。您可以通过运行 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 节点

处于 RunContainerErrorCrashLoopBackOffError 状态的 Pod

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

  • 如果在 kubeadm init 之后立即存在 Pod 处于这些状态之一,请在 kubeadm 存储库中打开一个问题。coredns(或 kube-dns)应该处于 Pending 状态,直到您部署网络附加组件。
  • 如果您在部署网络附加组件后看到 Pod 处于 RunContainerErrorCrashLoopBackOffError 状态,并且 coredns(或 kube-dns)没有任何变化,那么很可能是您安装的 Pod 网络附加组件出现问题。您可能需要授予它更多 RBAC 权限或使用更新的版本。请在 Pod 网络提供商的问题跟踪器中提交问题,并在那里获得问题的分类。

coredns 停滞在 Pending 状态

这是预期的,并且是设计的一部分。kubeadm 与网络提供程序无关,因此管理员应该 安装其选择的 Pod 网络附加组件。您必须安装 Pod 网络,然后才能完全部署 CoreDNS。因此,在设置网络之前处于 Pending 状态。

HostPort 服务无法正常工作

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

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

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

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

无法通过其服务 IP 访问 Pod

  • 许多网络附加组件尚未启用 回环模式,该模式允许 Pod 通过其服务 IP 访问自身。这是一个与 CNI 相关的问题。请联系网络附加组件提供商以获取其对回环模式支持的最新状态。

  • 如果您使用的是 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 通过使用 /var/lib/kubelet/pki/kubelet-client-current.pem 符号链接(指定在 /etc/kubernetes/kubelet.conf 中)来配置 kubelet 以自动轮换客户端证书。如果此轮换过程失败,您可能会在 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. 通过使用以下内容替换 client-certificate-dataclient-key-data,手动编辑 kubelet.conf 以指向轮换的 kubelet 客户端证书

    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错误消息

在使用kubeadm升级Kubernetes集群时,如果运行外部etcd,就会显示此错误消息。这不是一个严重错误,并且发生是因为旧版本的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集群中,metrics-server可以通过将--kubelet-insecure-tls传递给它来不安全地使用。不建议在生产集群中这样做。

如果您想在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
...

此失败的原因是受影响的版本在PodSpec中生成了带有不必要的默认值的etcd清单文件。这会导致与清单比较时出现差异,kubeadm会期望Pod哈希发生变化,但kubelet永远不会更新哈希。

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

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

    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)