Kubernetes暴力删除(rm -rf)与无影响恢复


新钛云服已为您服务979

Kubernetes(k8s)是Google开源的容器集群管理系统(谷歌内部:Borg)。在容器技术的基础上,为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等一系列完整功能,提高了大规模容器集群管理的便捷性。

Kubernetes是一个完备的分布式系统支撑平台,具有完备的集群管理能力,多扩多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和发现机制、內建智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制以及多粒度的资源配额管理能力。同时Kubernetes提供完善的管理工具,涵盖了包括开发、部署测试、运维监控在内的各个环节。

Kubernetes是一个全新的基于容器技术的分布式架构领先方案,是最佳的容器编排平台。最近,Kubernetes在功能,安全性和弹性方面取得了长足的进步。Kubernetes体系架构使您可以轻松地从各种故障中快速安全的恢复。

在本文中,我们也将进行集群的破坏性操作:人为损坏集群,删除证书,重新加入业务节点。最终的目的是为了通过这些破坏性操作,而不会造成已经正常运行的服务停机甚至数据丢失


集群故障模式的一般性概述

下面是一个不完整的列表,列举了一些可能的出错场景,以及通过调整集群配置来解决相关问题的方法。

  • VM(s) 关机

  • 集群之间,或者集群和用户之间网络分裂

  • Kubernetes 软件本身崩溃

  • 数据丢失或者持久化存储不可用(如:GCE PD 或 AWS EBS 卷)

  • 操作错误,如:Kubernetes 或者应用程序配置错误

同时以下也列出了官网所给出的常规故障详情以及导致的后果:

  • API 服务器所在的 VM 关机或者 API 服务器崩溃

  • 结果

    • 不能停止、更新或者启动新的 Pod、服务或副本控制器

    • 现有的 Pod 和服务在不依赖 Kubernetes API 的情况下应该能继续正常工作

  • API 服务器的后端存储丢失

  • 结果

    • API 服务器应该不能启动

    • kubelet 将不能访问 API 服务器,但是能够继续运行之前的 Pod 和提供相同的服务代理

    • 在 API 服务器重启之前,需要手动恢复或者重建 API 服务器的状态

  • Kubernetes 服务组件(节点控制器、副本控制器管理器、调度器等)所在的 VM 关机或者崩溃

  • 结果

    • 当前,这些控制器是和 API 服务器在一起运行的,它们不可用的现象是与 API 服务器类似的

    • 将来,这些控制器也会复制为多份,并且可能不在运行于同一节点上

    • 它们没有自己的持久状态

  • 单个节点(VM 或者物理机)关机

    • 此节点上的所有 Pod 都停止运行

  • 网络分裂

  • 结果

    • 分区 A 认为分区 B 中所有的节点都已宕机;分区 B 认为 API 服务器宕机 (假定主控节点所在的 VM 位于分区 A 内)。

  • kubelet 软件故障

  • 结果

    • 崩溃的 kubelet 就不能在其所在的节点上启动新的 Pod

    • kubelet 可能删掉 Pod 或者不删

    • 节点被标识为非健康态

    • 副本控制器会在其它的节点上启动新的 Pod

  • 集群操作错误

  • 结果

    • 丢失 Pod 或服务等等

    • 丢失 API 服务器的后端存储

    • 用户无法读取API

    • 等等

Kubernetns破坏(rm -rf)

因此,现在让我们开始进行验证。Kubernetes主控制平面仅包含以下几个组件:

  • etcd —Kubernetes提供默认的存储系统,保存所有集群数据,使用时需要为etcd数据提供备份计划

  • kube-apiserver — 作为Kubernetes系统的入口,其封装了核心对象的增删改查操作,以RESTful API接口方式提供给外部客户和内部组件调用。维护的REST对象持久化到Etcd中存储

  • kube-controller-manager —负责执行各种控制器,目前已经提供了很多控制器来保证Kubernetes的正常运行

  • kube-scheduler —为新建的Pod进行节点(node)选择(即分配机器),负责集群的资源调度。组件抽离,可以方便替换成其他调度器。

  • kubelet —负责管控容器,Kubelet会从Kubernetes API Server接收Pod的创建请求,启动和停止容器,监控容器运行状态并汇报给Kubernetes API Server

  • kube-proxy—负责为Pod创建代理服务,Kubernetes Proxy会从Kubernetes API Server获取所有的Service信息,并根据Service的信息创建代理服务,实现Service到Pod的请求路由和转发,从而实现Kubernetes层级的虚拟转发网络

这些组件中的每一个都受到一组针对客户端和服务器的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 直接由特定节点上的kubelet进程来管理,不通过 master节点上的apiserver。

无法与我们常用的控制器Deployment或者DaemonSet进行关联,它由kubelet进程自己来监控,当pod崩溃时重启该pod,kubelete也无法对他们进行健康检查。静态pod 始终绑定在某一个kubelet,并且始终运行在同一个节点上。

kubelet会自动为每一个静态 pod 在 Kubernetes 的apiserver 上创建一个镜像 Pod(Mirror Pod),因此我们可以在 apiserver 中查询到该 pod,但是不能通过apiserver 进行控制(例如不能删除)。

创建静态 Pod 有两种方式:配置文件和 HTTP 两种方式。而kubernetns的静态 Pod 清单的路径是/etc/kubernetes/manifests。正常安装的kubernetns集群,相关组件的静态Pod清单已经包含在该目录,故可自动运行。

关于这一点,本处我们不做详细介绍,因为这是额外的话题,且内容很多,如果需要详细了解,可以查看官网文档(https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/)。

在我们的案例中,我们主要对集群的配置管理,组件管理以及相关的其他组件管理感兴趣。但是首先,让我们稍微聚焦一下,假设我们拥有上述正常运行的Kubernetes所有组件,并且它们以某种方式相互交互通信。

通常,kubernetns组件交互方式如下所示:

对于通信,它们都需要TLS证书,因此,可以将TLS证书提取出,然后单独讨论。本处,我们依赖您的部署工具,它可以是kubeadm、kubespray或其适用的工具。在本文中,我们将使用kubeadm,因为它是最常见的Kubernetes部署工具。

kubeadm 是一个工具包,可帮助您以简单,合理安全和可扩展的方式引导最佳实践Kubernetes群集。它还支持为您管理Bootstrap Tokens并升级/降级群集。

假设我们已经有一个已部署的集群。现在,让我们从最有趣的地方开始:

rm -rf /etc/kubernetes/

在主节点上,此目录包含:

  • 一组用于etcd的证书和CA(位于/etc/kubernetes/pki/etcd

  • 一组Kubernetes的证书和CA(位于/etc/kubernetes/pki

  • 用于cluster-admin,kube-controller-manager,kube-scheduler和kubelet的Kubeconfigs(有针对我们的集群的base64编码的CA证书/etc/kubernetes/*.conf

  • 一组用于etcd,kube-apiserver,kube-scheduler和kube-controller-manager的静态pod的配置清单(位于/etc/kubernetes/manifests

本次假设的就是因意外故障或者误操作,导致我们丢失了所有相关的配置。


修复控制平面

为避免混淆,我们还要确保所有kubernetns控制平面pod也都已经停止:

crictl rm `crictl ps -aq`

注意:默认情况下,kubeadm不会覆盖现有证书和kubeconfigs,要重新发行它们,必须首先手动删除旧证书和kubeconfigs。crictl 是 CRI 兼容的容器运行时命令行接口。你可以使用它来检查和调试 Kubernetes 节点上的容器运行时和应用程序。crictl 和它的源代码在 cri-tools(https://github.com/kubernetes-sigs/cri-tools) 代码库。

让我们从恢复etcd开始。如果您有一个etcd仲裁节点(etcd拥有3个或更多主节点),那么直到大多数etcd节点都联机后,服务才能正常访问etcd群集。

kubeadm init phase certs etcd-ca

上面的命令将为我们的etcd集群生成一个新的CA。由于所有的证书都必须由它签名,因此,我们还需要将其和私钥复制到其他主节点:

/etc/kubernetes/pki/etcd/ca.{key,crt}

现在,让我们在所有控制平面节点上为其重新生成其余的etcd证书和静态pod清单:

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服务而言,在其中一个主节点上执行:

kubeadm init phase certs all
kubeadm init phase kubeconfig all
kubeadm init phase control-plane all
cp -f /etc/kubernetes/admin.conf ~/.kube/config

上面的命令将为Kubernetes生成所有SSL证书,以及为Kubernetes服务生成的静态Pod清单和kubeconfigs。

如果您使用kubeadm来连接kubeletes,则还需要更新kube-public名称空间中的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,因此您可以按以下步骤注册主服务器:

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有另一个配置,用于保存前代理客户端的CA证书。它用于验证从apiserver到webhooks和聚合层服务的请求。幸运的是,kube-apiserver会自动更新它。

但是,您可能需要手动从旧证书中清除它:

kubectl get cm -n kube-system extension-apiserver-authentication -o yaml

无论如何,在这个阶段,我们已经有一个可以正常工作的控制平面。


修复worker节点

该命令将列出集群的所有节点,尽管当前所有节点的状态均为NotReady

kubectl get node

这是因为它们仍然使用旧证书,并一直在等待来自由旧CA签名的apiserver的请求。为了解决这个问题,我们将使用kubeadm,对集群worker节点执行重新加入命令。

当主节点可以访问主CA时,它们就可以在本地加入:

systemctl stop kubelet
rm -rf /var/lib/kubelet/pki/ /etc/kubernetes/kubelet.conf
kubeadm init phase kubeconfig kubelet
kubeadm init phase kubelet-start

但是要加入worker-node,我们必须生成一个新令牌:

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/主节点上的目录,因为该目录已经包含所有必需的证书。

以上步骤将把所有kubelets重新加入集群。它不应该影响已经在上面运行的任何容器。但是,如果集群中有多个节点,并且没有同时执行此操作,则可能会遇到这样的情况:controller-manager开始从NotReady节点重新创建容器,并尝试在正常节点上重新调度它们。

为了防止这种情况,我们可以暂时停止主机上的controller-manager容器:

rm /etc/kubernetes/manifests/kube-controller-manager.yaml
crictl rmp `crictl ps --name kube-controller-manager -q`

仅需要最后一条命令即可确保控制器管理器已真正停止。一旦所有节点正常加入了集群,就可以为controller-manager生成一个静态清单。

为此,请在所有masters-nodes上运行以下命令:

kubeadm init phase control-plane controller-manager

请注意,您需要在已经生成连接令牌的阶段执行这些步骤。否则,连接过程将挂起,尝试从cluster-info configmap中读取令牌。

如果将kubelets配置为请求由您的CA签名的证书(选项serverTLSBootstrap: true),则还需要从kubelets中批准CSR。

kubectl get csr
kubectl certificate approve <csr>



修复ServiceAccounts

由于我们丢失了/etc/kubernetes/pki/sa.key。此密钥用于为群集中的所有ServiceAccounts签名jwt令牌。因此,我们必须为其重新创建令牌。

通过匹配kubernetes.io/service-account-token字段,可以非常简单的从所有的secret中删除令牌字段:

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将自动生成使用新密钥签名的新令牌。

有一点需要注意,并非所有的微服务都能即时更新令牌,因此很可能需要手动重新启动所有使用令牌的容器。

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"}'

以上命令将生成一个命令列表,以删除所有serviceAccountName不是default的pod。推荐从kube-system namespace开始,因为该namespace中安装了kube-proxy和CNI插件。它们对于处理微服务之间的通信至关重要。

至此,Kubernetns集群已经恢复完成,且工作正常。

*本文翻译自:

  • https://itnext.io/breaking-down-and-fixing-kubernetes-4df2f22f87c3

  • https://kubernetes.io/docs/tasks/debug-application-cluster/debug-cluster/

*本文部分图片源于网络,如有侵权请联系删除

了解新钛云服

当IPFS遇见云服务|新钛云服与冰河分布式实验室达成战略协议

新钛云服正式获批工信部ISP/IDC(含互联网资源协作)牌照

深耕专业,矗立鳌头,新钛云服获千万Pre-A轮融资

新钛云服,打造最专业的Cloud MSP+,做企业业务和云之间的桥梁

新钛云服一周年,完成两轮融资,服务五十多家客户

上海某仓储物流电子商务公司混合云解决方案

往期技术干货

低代码开发,全民开发,淘汰职业程序员!

国内主流公有云VPC使用对比及总结

万字长文:云架构设计原则|附PDF下载

刚刚,OpenStack 第 19 个版本来了,附28项特性详细解读!

Ceph OSD故障排除|万字经验总结

七个用于Docker和Kubernetes防护的安全工具

运维人的终身成长,从清单管理开始|万字长文!

OpenStack与ZStack深度对比:架构、部署、计算存储与网络、运维监控等

什么是云原生?

IT混合云战略:是什么、为什么,如何构建?

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值