作者 | Andrei Kvapil
编译 | 云原生计算编辑部
原文链接:https://itnext.io/breaking-down-and-fixing-kubernetes-4df2f22f87c3
Kubernetes 是当下最流行的容器编排平台,不管是生产环境的采用率,还是云原生生态都很强大。最近,Kubernetes 在功能,安全性和弹性方面取得了长足的进步,Kubernetes 架构使您可以轻松地应对各种故障并始终保持正常运行。
不相信它这么顽强?下面,我们将破坏集群,删除证书......一起来看看这些破坏性的动作会不会让运行的服务宕机。
在 Kubernetes 控制平面(主)节点上仅包含以下几个组件:
etcd —用作数据库
kube-apiserver — 提供集群所的API服务,负责在Kubernetes节点和Kubernetes主组件之间建立通信。
kube-controller-manager —对Kubernetes资源执行操作
kube-scheduler —主调度程序
kubelet —负责容器的创建与起停
以上每个组件都受到一组针对客户端和服务器的TLS证书的保护。 它们用于对彼此之间的组件进行身份验证和授权。 它们一般不会存储在 Kubernetes 数据库中,而是以普通文件的形式存在:
# tree /etc/kubernetes/pki/
/etc/kubernetes/pki/
├── apiserver.crt
├── apiserver-etcd-client.crt
├── apiserver-etcd-client.key
├── apiserver.key
├── apiserver-kubelet-client.crt
├── apiserver-kubelet-client.key
├── ca.crt
├── ca.key
├── CTNCA.pem
├── etcd
│ ├── ca.crt
│ ├── ca.key
│ ├── healthcheck-client.crt
│ ├── healthcheck-client.key
│ ├── peer.crt
│ ├── peer.key
│ ├── server.crt
│ └── server.key
├── front-proxy-ca.crt
├── front-proxy-ca.key
├── front-proxy-client.crt
├── front-proxy-client.key
├── sa.key
└── sa.pub
这些组件作为静态 pod 在 master 节点上运行,资源清单在/ etc / kubernetes / manifests目录下。
关于这一点,我们将不做详细介绍, 在本案例中,我们主要对如何从所有这些组件中获得一个有效运行的集群感兴趣。 首先,假设我们拥有上述 Kubernetes 组件,它们以某种方式相互通信。
通常如下所示:
(箭头表示客户端到服务器的连接)
为了进行通信,他们需要TLS证书,部署工具可以是 kubeadm,kubespray 或其他任何工具。 在本文中,我们将使用kubeadm,因为它是最常见的 Kubernetes 部署工具,并且经常在其他解决方案中使用。
假设我们已经有一个部署好的集群。 让我们开始破坏行动吧:
rm -rf /etc/kubernetes/
在 master节点上,此目录包含:
-etcd的一组证书和CA(在/ etc / kubernetes / pki / etcd中)
-Kubernetes 的一组证书和CA(在/ etc / kubernetes / pki中)
-用于cluster-admin,kube-controller-manager,kube-scheduler和kubelet使用的Kubeconfig文件(每个文件在/etc/kubernetes/*.conf中都有针对我们集群的base64编码的CA证书)
-etcd,kube-apiserver,kube-scheduler和kube-controller-manager使用的static manifest文件(在/ etc / kubernetes / manifests中)
假设我们一下子把什么都删了......
修复控制平面
为避免混淆,我们还要确保所有control-plane pods 也已停止:
crictl rm $(crictl ps -aq)
注意:默认情况下,kubeadm 不会覆盖现有证书和 kubeconfig,要重新发布它们,必须首先手动删除旧证书和 kubeconfig。
接下来,让我们从恢复 etcd开始。 如果您有一个 quorum (3个或更多主节点),则直到大多数节点都联机后,才能访问 etcd群集。
kubeadm init phase certs etcd-ca
上面的命令将为我们的 etcd 集群生成一个新的CA。 由于所有其他证书都必须由它签名,因此,我们还将其和私钥复制到其他 master节点:
/etc/kubernetes/pki/etcd/ca.{key,crt}
现在,让我们在所有控制平面节点上为其重新生成其余的 etcd 证书和 static-manifest:
kubeadm init phase certs etcd-healthcheck-client
kubeadm init phase certs etcd-peer
kubeadm init phase certs etcd-server
kubeadm init phase etcd local
此时,您应该已经了有一个可以正常工作的 etcd 集群:
# crictl ps
CONTAINER ID IMAGE CREATED STATE NAME ATTEMPT POD ID
ac82b4ed5d83a 0369cf4303ffd 2 seconds ago Running etcd 0 bc8b4d568751b
然后,我们执行相同的任务,但是对于 Kubernetes 服务,在 master节点之一上执行:
kubeadm init phase certs all
kubeadm init phase kubeconfig all
kubeadm init phase control-plane all
cp -f /etc/kubernetes/admin.conf ~/.kube/config
如果您使用 kubeadm 来连接 kubeletes,则还需要更新kube-public namespace中的 cluster-info 配置,因为它仍然包含旧CA的哈希。
kubeadm init phase bootstrap-token
由于其他实例上的所有证书也必须由单个CA签名,因此我们将其复制到其他控制平面节点,然后在每个证书上重复上述命令。
/etc/kubernetes/pki/{ca,front-proxy-ca}.{key,crt}
/etc/kubernetes/pki/sa.{key,pub}
顺便说一下,作为手动复制证书的替代方法,您还可以使用 Kubernetes API,例如以下命令:
kubeadm init phase upload-certs --upload-certs
加密并上传证书到Kubernetes 需要2小时,因此您可以按以下方式注册 master 节点:
kubeadm join phase control-plane-prepare all kubernetes-apiserver:6443 --control-plane --token cs0etm.ua7fbmwuf1jz946l --discovery-token-ca-cert-hash sha256:555f6ececd4721fed0269d27a5c7f1c6d7ef4614157a18e56ed9a1fd031a3ab8 --certificate-key 385655ee0ab98d2441ba8038b4e8d03184df1806733eac131511891d1096be73
kubeadm join phase control-plane-join all
请注意,Kubernetes API具有另一个配置,用于保存 front-proxy客户端的CA证书。 它用于验证从 apiserver 到 webhooks 和聚合层服务的请求。 幸运的是,kube-apiserver 会自动更新它。
但是,您可能需要手动从旧证书中清除它:
kubectl get cm -n kube-system extension-apiserver-authentication -o yaml
无论如何,在这个阶段,我们已经有一个可以正常工作的控制平面。
修复工作节点
该命令将列出集群的所有节点,尽管当前所有节点的状态均为“NotReady”:
kubectl get node
这是因为它们仍然使用旧证书,并期望来自由旧CA签名的 apiserver 的请求。 为了解决这个问题,我们将使用 kubeadm,并对群集执行重新加入节点。
当主节点可以访问主CA时,它们可以在本地加入:
systemctl stop kubelet
rm -rf /var/lib/kubelet/pki/ /etc/kubernetes/kubelet.conf
kubeadm init phase kubeconfig kubelet
kubeadm init phase kubelet-start
但是要加入工作节点,我们必须生成一个新token:
kubeadm token create --print-join-command
并分别对它们运行以下命令:
systemctl stop kubelet
rm -rf /var/lib/kubelet/pki/ /etc/kubernetes/pki/ /etc/kubernetes/kubelet.conf
kubeadm join phase kubelet-start kubernetes-apiserver:6443 --token cs0etm.ua7fbmwuf1jz946l --discovery-token-ca-cert-hash sha256:555f6ececd4721fed0269d27a5c7f1c6d7ef4614157a18e56ed9a1fd031a3ab8
注意:您不需要删除主节点上的/ etc / kubernetes / pki /目录,因为它已经包含所有需要的证书。
上面的操作会将您的所有 kubelet 重新加入集群。 它不会影响已经在其上运行的任何容器。 但是,如果集群中有多个节点并且不同时进行,则可能会遇到一种情况,即controller-manager开始从NotReady节点重新创建容器,并尝试在alive的节点上重新调度它们。
为了防止这种情况,我们可以暂时停止 master上的controller-manager pod:
rm /etc/kubernetes/manifests/kube-controller-manager.yaml
crictl rmp $(crictl ps --name kube-controller-manager -q)
仅需要最后一条命令即可确保controller-manager已真正停止。 一旦集群中的所有节点被加入,就可以为controller-manager生成一个static-manifest 。
为此,请在所有 master节点上运行以下命令:
kubeadm init phase control-plane controller-manager
请注意,您需要在已经生成join token的阶段执行这些步骤。 否则,join过程将继续尝试从cluster-info configmap读取token。
如果将kubelet配置为请求由您的CA签名的证书(选项serverTLSBootstrap:true),则还需要批准来自kubelet的CSR请求。
kubectl get csr
kubectl certificate approve <csr>
修复ServiceAccounts
还有一件事,因为我们丢失了/etc/kubernetes/pki/sa.key。 该密钥用于为群集中所有 ServiceAccounts 的jwt令牌签名。 因此,我们必须为其重新创建token。
通过从kubernetes.io/service-account-token类型的 Secret中删除token字段,可以非常简单地完成此操作:
kubectl get secret --all-namespaces | awk '/kubernetes.io\/service-account-token/ { print "kubectl patch secret -n " $1 " " $2 " -p {\\\"data\\\":{\\\"token\\\":null}}"}' | sh -x
之后,kube-controller-manager将自动生成使用新密钥签名的新token。
不幸的是,并非所有的微服务都能即时更新token,因此很可能需要手动重新启动所有使用token的容器。
kubectl get pod --field-selector 'spec.serviceAccountName!=default' --no-headers --all-namespaces | awk '{print "kubectl delete pod -n " $1 " " $2 " --wait=false --grace-period=0"}'
例如,此命令将生成命令列表,以使用非默认 serviceAccount删除所有pod。 我建议从kube-system的namespace开始,因为在那里安装了kube-proxy和CNI插件。 它们对于处理微服务之间的通信至关重要。
此时,集群恢复就算完成了。
Kubernetes 在生产环境如何落地?
目前,Kubernetes 技术已然成为了容器编排领域的事实标准。百度从 2010 年开始探索容器和集群管理技术,2016年自主研发的 Matrix 集群管理系统已经管理了数十万台机器和服务。随着 Kubernetes 技术的成熟,百度看到了开源技术强大的生命力,从 2018 年开始尝试向 Kubernetes 架构演化。试点成功之后,启动了大规模 Kubernetes 架构融合项目。一方面保留百度在 Matrix 上积累的核心技术能力,另一方面让存量业务可以更低成本的迁移到 Kubernetes 之上。
百度于 2020 年获得 InfoQ 十大云原生创新技术方案,对百度云原生来说仅仅是个开始。目前大规模 Kubernetes 融合架构的业务正在百度云原生各产品技术架构中稳定运行并持续增长,百度云原生团队也将会在继续服务好客户的同时,利用Kubernetes技术实践经验不断优化产品,更好地助力各行各业的客户实现基于云原生架构的数字化转型。
百度智能云云原生平台,为客户建设容器化和无服务器化的基础设施,提供企业级的微服务治理能力,同时集成源自百度自身多年实践的DevOps工具链。保障开发者享受到高效、灵活、弹性的开发与运维体验,助力企业更高效率低风险地构建云原生应用,广泛应用于金融、互联网、制造等各行各业的云原生转型阶段。
了解更多百度云原生产品请访问:
https://cloud.baidu.com/product/cnap.html
重磅!云原生计算交流群成立
扫码添加小助手即可申请加入,一定要备注:名字-公司/学校-地区,根据格式备注,才能通过且邀请进群。。
了解更多微服务、云原生技术的相关信息,请关注我们的微信公众号【云原生计算】!