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 才能获取节点对象。
然而,这导致了 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 网络附加组件。你必须安装 Pod 网络,CoreDNS 才能完全部署。因此,在网络设置之前,它会处于 Pending
状态。
HostPort
服务不起作用
HostPort
和 HostIP
功能是否可用取决于你的 Pod 网络提供商。请联系 Pod 网络附加组件的作者,以了解 HostPort
和 HostIP
功能是否可用。
Calico、Canal 和 Flannel CNI 提供商已验证支持 HostPort。
有关更多信息,请参阅 CNI portmap 文档。
如果你的网络提供商不支持 portmap CNI 插件,你可能需要使用 服务的 NodePort 功能 或使用 HostNetwork=true
。
Pod 无法通过其 Service IP 访问
许多网络附加组件尚未启用 发夹模式 (hairpin mode),该模式允许 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
被重新创建。手动编辑
kubelet.conf
,通过替换client-certificate-data
和client-key-data
来指向轮换后的 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 出现问题,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 将一个公共 IP 分配给
eth0
,同时将一个私有 IP 分配给内部使用,作为其浮动 IP 功能的锚点,但kubelet
会选择后者作为节点的InternalIP
,而不是公共 IP。使用
ip addr show
而不是ifconfig
来检查此情况,因为ifconfig
不会显示有问题的别名 IP 地址。或者,DigitalOcean 特定的 API 端点允许从 droplet 查询锚点 IPcurl 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 Pods 持续重启
如果你遇到以下错误
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 Pods 中可以看到以下错误
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
作为只读文件系统挂载。对于 FlexVolume 支持,Kubernetes 组件(例如 kubelet 和 kube-controller-manager)使用 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/
的默认路径,但 FlexVolume 目录 必须可写 才能使此功能正常工作。
注意
FlexVolume 在 Kubernetes v1.23 版本中已弃用。要解决此问题,你可以使用 kubeadm 配置文件 配置 FlexVolume 目录。
在主控制平面节点(使用 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
升级带有外部 etcd 的 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 集群中,可以通过向 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
...
此故障的原因是受影响的版本生成的 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
有关此 bug 的更多信息,请参见 跟踪问题。