kubeadm 故障排除
与任何程序一样,您在安装或运行 kubeadm 时可能会遇到错误。此页面列出了一些常见的失败场景,并提供了可以帮助您理解和修复问题的步骤。
如果你的问题没有在下面列出,请按照以下步骤操作
如果您认为您的问题是 kubeadm 的 bug
- 访问 github.com/kubernetes/kubeadm 并搜索现有问题。
- 如果不存在问题,请创建一个问题 并遵循问题模板。
如果您不确定 kubeadm 的工作原理,可以在 Slack 的
#kubeadm
频道中提问,或者在 StackOverflow 上提问。请包括#kubernetes
和#kubeadm
等相关标签,以便大家帮助您。
由于缺少 RBAC,无法将 v1.18 节点加入 v1.17 集群
在 v1.18 中,kubeadm 添加了防止将节点加入集群的措施,如果已存在同名的节点。这需要为 bootstrap-token 用户添加 RBAC,使其能够 GET 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
那么您的节点上可能缺少 ebtables
、ethtool
或类似的执行程序。您可以使用以下命令安装它们
- 对于 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 处于 RunContainerError
、CrashLoopBackOff
或 Error
状态
在 kubeadm init
之后,不应该有任何 Pod 处于这些状态。
- 如果 在
kubeadm init
之后 有 Pod 处于这些状态之一,请在 kubeadm 仓库中打开一个问题。coredns
(或kube-dns
)应该处于Pending
状态,直到您部署了网络附加组件。 - 如果您在部署网络附加组件后看到 Pod 处于
RunContainerError
、CrashLoopBackOff
或Error
状态,并且coredns
(或kube-dns
)没有任何反应,则很可能是您安装的 Pod 网络附加组件存在问题。您可能需要授予它更多的 RBAC 权限或使用更新的版本。请在 Pod 网络提供商的问题跟踪器中提交问题,并在那里对问题进行分类。
coredns
卡在 Pending
状态
这是 预期 的,也是设计的一部分。kubeadm 与网络提供商无关,因此管理员应安装所选的 Pod 网络附加组件。您必须在完全部署 CoreDNS 之前安装 Pod 网络。因此,在设置网络之前处于 Pending
状态。
HostPort
服务不起作用
HostPort
和 HostIP
功能的可用性取决于您的 Pod 网络提供商。请联系 Pod 网络附加组件的作者,以了解 HostPort
和 HostIP
功能是否可用。
已验证 Calico、Canal 和 Flannel CNI 提供商支持 HostPort。
有关更多信息,请参阅 CNI portmap 文档。
如果您的网络提供商不支持 portmap CNI 插件,您可能需要使用服务的 NodePort 功能或使用 HostNetwork=true
。
无法通过其 Service IP 访问 Pod
许多网络附加组件尚未启用发夹模式,该模式允许 Pod 通过其 Service 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 通过使用 /etc/kubernetes/kubelet.conf
中指定的 /var/lib/kubelet/pki/kubelet-client-current.pem
符号链接,配置具有客户端证书自动轮换的 kubelet。如果此轮换过程失败,您可能会在 kube-apiserver 日志中看到诸如 x509: certificate has expired or is not yet valid
之类的错误。要解决此问题,您必须执行以下步骤
备份并删除失败节点上的
/etc/kubernetes/kubelet.conf
和/var/lib/kubelet/pki/kubelet-client*
。从集群中具有
/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
中的嵌入式证书。将此生成的
kubelet.conf
复制到失败节点上的/etc/kubernetes/kubelet.conf
。在失败的节点上重新启动 kubelet (
systemctl restart kubelet
),并等待重新创建/var/lib/kubelet/pki/kubelet-client-current.pem
。通过将
client-certificate-data
和client-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
重新启动 kubelet。
确保节点变为
Ready
。
在 Vagrant 中使用 flannel 作为 Pod 网络时的默认 NIC
以下错误可能表明 Pod 网络存在问题
Error from server (NotFound): the server could not find the requested resource
如果你在 Vagrant 中使用 flannel 作为 Pod 网络,则必须为 flannel 指定默认接口名称。
Vagrant 通常会为所有虚拟机分配两个接口。第一个接口的所有主机都被分配 IP 地址
10.0.2.15
,用于进行 NAT 的外部流量。这可能会导致 flannel 出现问题,因为它默认使用主机上的第一个接口。这会导致所有主机认为它们具有相同的公有 IP 地址。为了防止这种情况,请将
--iface eth1
标志传递给 flannel,以便选择第二个接口。
用于容器的非公共 IP
在某些情况下,即使集群功能正常,kubectl logs
和 kubectl 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
),如果您想使用可选的私有网络。可以使用 kubeadmNodeRegistrationOptions
结构的kubeletExtraArgs
部分来配置。然后重启
kubelet
systemctl daemon-reload systemctl restart kubelet
coredns
Pod 处于 CrashLoopBackOff
或 Error
状态
如果您的节点运行的是带有旧版本 Docker 的 SELinux,则可能会遇到 coredns
Pod 无法启动的情况。要解决此问题,您可以尝试以下选项之一
升级到 较新版本的 Docker。
修改
coredns
部署以将allowPrivilegeEscalation
设置为true
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。
警告
禁用 SELinux 或将allowPrivilegeEscalation
设置为 true
可能会危及群集的安全性。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 在 cloud-controller-manager 初始化节点之前被调度
在云提供商场景中,kube-proxy 最终可能会在新工作节点上调度,而 cloud-controller-manager 尚未初始化节点地址。这会导致 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 支持,诸如 kubelet 和 kube-controller-manager 之类的 Kubernetes 组件使用 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/
的默认路径,但是 flex-volume 目录*必须是可写的*才能使该功能正常工作。
注意
FlexVolume 在 Kubernetes v1.23 版本中已弃用。要解决此问题,可以使用 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 升级 Kubernetes 集群时,使用 kubeadm
会显示此错误消息。这不是一个严重的错误,发生的原因是旧版本的 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 永远不会更新哈希。
如果您的集群中出现此问题,有两种方法可以解决此问题
可以使用以下命令跳过受影响的版本和 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
有关更多信息,请参阅此错误的跟踪问题。