K8S 太强了,我搞了这么多破坏居然还没宕机

Wesley13
• 阅读 661

K8S 太强了,我搞了这么多破坏居然还没宕机

作者 | Andrei Kvapil

编译 | 云原生计算编辑部

原文链接:https://itnext.io/breaking-down-and-fixing-kubernetes-4df2f22f87c3

Kubernetes 是当下最流行的容器编排平台,不管是生产环境的采用率,还是云原生生态都很强大。最近,Kubernetes 在功能,安全性和弹性方面取得了长足的进步,Kubernetes 架构使您可以轻松地应对各种故障并始终保持正常运行。

不相信它这么顽强?下面,我们将破坏集群,删除证书......一起来看看这些破坏性的动作会不会让运行的服务宕机。

K8S 太强了,我搞了这么多破坏居然还没宕机

在 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 组件,它们以某种方式相互通信。
通常如下所示:

K8S 太强了,我搞了这么多破坏居然还没宕机

(箭头表示客户端到服务器的连接)

为了进行通信,他们需要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


重磅!云原生计算交流群成立

扫码添加小助手即可申请加入,一定要备注:名字-公司/学校-地区,根据格式备注,才能通过且邀请进群。。

K8S 太强了,我搞了这么多破坏居然还没宕机

了解更多微服务、云原生技术的相关信息,请关注我们的微信公众号【云原生计算】!

K8S 太强了,我搞了这么多破坏居然还没宕机

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这