1.基于 Kubernetes v1.25 (kubeadm) 和 Docker 部署高可用集群(一)

2,097 阅读38分钟

我正在参与掘金创作者训练营第6期

基于 Kubernetes v1.25 和 Docker 部署高可用集群

主要内容

  • Kubernetes 集群架构组成
  • 容器运行时 CRI
  • Kubernetes v1.25 新特性
  • Kubernetes v1.24 之后不再支持 Docker 的解决方案
  • Kubernetes v1.25 高可用集群架构
  • 基于 Kubernetes v1.25.0 和 Docker 部署高可用集群实战案例

1.Kubernetes v1.25 新特性

1.1 Kubernetes 组件

Docker 的运行机制

001.jpg

首先执行docker命令,这个docker命令实际上它是一个客户端,通过连接docker engine就是docker引擎,docker引擎和docker client有连接接口,然后docker引擎去调用containerd,然后再去连接containerd-shim这实际上就是一个所谓的中间的垫片,然后再去连接到runc,然后再去运行docker对应的容器,这就是docker的运行机制;这里边涉及到两个组件一个就是containerd,containerd属于docker的一部分,还有一个是runc,这两个合起来构成了docker运行的一个环境有一个专业的称呼叫运行时。

Kubernetes的设计初衷是支持可插拔架构,从而利于扩展kubernetes的功能。

002.jpg

k8s和docker的作用是不一样的,docker管理的是单个主机上的容器,而k8s它的目标是管理多个主机跨系统、跨平台的一个分布式的管理系统,可以说它是一个云原生的操作系统;

k8s里有很多丰富的组件,在k8s里边上图左侧是它的核心部分,右侧是它的一个配套的另外一个组件,可以分成两部分一个叫k8s的master节点,这个master节点可以认为就是主控节点,另外一个叫k8s的node节点叫work节点,master就相当于公司的总部,node就相当于它下辖的一些公司或者分公司,具体完成任务的都是由node对应的工作节点来完成具体任务的,而master相对于一个控制中心它是一个总的管理者,你可以认为master就是领导node就是员工这样的一个关系,将来在k8s部署的时候至少需要有两台主机,一个充当master一个充当node这样才有意义,生产中的master和node为了高可用为了性能通常可能会有很多组机器,但是从角色来讲可以分层两类一个是master一个是node,可能将来会部署多个master以及多个node节点,这是从节点角色来做的介绍,那也就是我们需要部署两类主机一类充当master节点的服务器,一类充当node节点或者work节点的服务器;

其中master节点上面它提供主控功能也就是控制功能,这个控制功能里边细分成若干个组件这些是k8s的核心组件,其中有一个叫做Kube-APIServer它相当于对外的总的联络中心,你可以认为它就相当于一个公司的总经办或者是秘书处,它对外提供一个总的接口,所有去访问k8s资源的都需要通过Kube-APIServer作为一个总的接口,你可以认为就是一个公司的前台一个总机,从外部进来都需要通过Kube-APIServer才能进入到公司内部,它是一个总的对外的连接的接口,大家都知道很多公司里边都有总经理办公室它下发指令给各个下属部门,这就是一个总出口也就是一个总的接口,所有相关的资源都要从Kube-APIServer进行接入,这是唯一的一个接口;

另外还有就是etcd它主要用来存储k8s的一些资源的,你可以认为它就是一个数据库,它本身实际上就是个数据库,这个数据库存放了k8s的各种信息、各种资源,你可以认为它就是类似于mysql一样的这样的数据库,当然它的数据库类型并不是用的关系型数据库,这里etcd数据库类似于公司里的档案、仓库,专门存储信息的这样一个组件;

第三个组件叫Kube-Scheduler称为计划调度器,在学习linux的时候学过所谓的调度就是crontab类似于的这种调度器就是周期性的任务,Kube-Scheduler也是有点相似的,有点像公交系统的调度系统,把整个k8s中接触的各种要求、工作给它下发到具体要完成的工作的某一个node节点,将来在企业里边node节点它是真正完成任务的节点,这个节点会很多,当我们收到一个具体的任务的时候,比方说要去运行一个java服务就需要通过Kube-Scheduler把任务转发到具体来完成这个任务的某一个工作节点,这个工作就交给Kube-Scheduler来进行调度、进行转发,这是个调度器;

另外因为k8s里边资源众多可能有几十个,资源不同管理方法也是不同的需要有一个Kube-Controller-Manager也叫控制器,控制器有很多,比如Route控制器、卷的控制器、node控制器等等,这个有点像比方说一个公司的总部里边有各个不同的科室,比如有财务室、人力资源、后勤等等,有各个部分这些部分分别来管控各种资源就是这样的一个大概的关系;

k8s的核心组件总结来说有几个组件,第一个叫Kube-APIServer相当于总经理办公室,是一个总的对外的一个接口,由它和别人进行接洽,另外就是etcd这就是数据库来存放相关的k8s的资源的,还有一个是Kube-Schduler调度把任务下发到node节点进行完成这些任务由调度来完成,再一个就是Kube-Controller-Manager它包含各种的控制器来管理各种资源,提供了各种控制功能,这就是整个k8s在master上的核心组件。

Kubernetes提供了三个特定功能的接口,kubernetes通过调用这几个接口,来完成相应的功能。

  • 容器运行时接口CRI: Container Runtime Interface

    kubernetes 对于容器的解决方案,只是预留了容器接口,只要符合CRI标准的解决方案都可以使用

  • 容器网络接口CNI: Container Network Interface

    kubernetes 对于网络的解决方案,只是预留了网络接口,只要符合CNI标准的解决方案都可以使用

  • 容器存储接口CSI: Container Storage Interface

    kubernetes 对于存储的解决方案,只是预留了存储接口,只要符合CSI标准的解决方案都可以使用此接口非必须

当然master它只是完成了领导性的任务,你可以理解为它就是一个管理中心,后续它要完成这些k8s里的工作,比如要跑一些服务还是要靠真正的node节点或者叫work节点来完成的,这些work节点和k8s之间怎么连接呢,就需要依赖于提供的三个接口,k8s为了支持更多的平台,k8s准确来说它实际上就是一个有点像淘宝一样的平台,它可以向外提供很多的第三方接入,你可以寄居在k8s的生态圈里边开发自己和k8s结合的各种组件,只要遵守它的相关接口规范即可,它的接口有3个接口,第一个叫容器运行时接口,这个容器运行时接口也就是说k8s管理的是容器,但这个容器到底用的是什么技术呢我们不关心,只要你遵守这个规范就可以了,有一个CRI的接口叫容器运行时接口,这个接口就是一种规范就是一种标准,你只要遵守这种规范不论你是用哪种容器技术都可以支持,当然目标来讲提供CRI最主流的技术仍然是docker,换句话说CRI以docker为主,但是目前已经并不唯一,也有一些其它的CRI也提供了,只要遵守这个CRI标准k8s就可以进行连接进行容器的管理;第二个叫CNI叫网络接口,这个网络接口简单的说就是通过k8s跨网络和node节点的的容器进行通讯,这个也需要有网络的解决方案,网络的解决方案也是要遵守这种k8s提供的网络接口规范的,这个接口规范目前来讲主流的有flannel和calico等等若干个解决方案这是用的比较多的,待会去部署k8s集群也必须要部署对应的CNI的某一种,如果不部署就相当于网络是没办法连通的,它是依赖于网络插件的,这些都是以插件方式提供的,因为k8s它只是提供了接口具体怎么实现我并不关心,你只要符合这个规范都是可以支持的;第三种叫CSI也就是存储,将来在k8s节点中运行的这些应用有可能需要有一些存储的需求,比如要存放数据、存放日志等这些需要有存储,存储也提供了一个所谓的叫CSI的接口,这个CSI接口来让我们这些工作节点将来把一些数据存放在某个地方,这个也是有一个规范的,不过应用有些没有存放数据的需求,所以CSI并不是一个必备的要求,你可以不需要。

容器运行时接口(CRI)

CRI是kubernetes定义的一组gRPC服务。Kubelet作为客户端,基于gRPC协议通过Socket和容器运行时通信。

CRI 是一个插件接口,它使 kubelet 能够使用各种容器运行时,无需重新编译集群组件。

Kubernetes 集群中需要在每个节点上都有一个可以正常工作的容器运行时, 这样 kubelet 能启动 Pod 及其容器。

容器运行时接口(CRI)是 kubelet 和容器运行时之间通信的主要协议。

CRI 括两类服务:镜像服务(Image Service)和运行时服务(Runtime Service)。

镜像服务提供下载、检查和删除镜像的远程程序调用。

运行时服务包含用于管理容器生命周期,以及与容器交互的调用的远程程序调用。

OCI(Open Container Initiative,开放容器计划)定义了创建容器的格式和运行时的开源行业标准,包括镜像规范(Image Specification)和运行时规范(Runtime Specification)。

CRI叫容器运行时接口,这个容器运行时接口它实际上就是让k8s能够通过CRI能够和k8s的work节点进行连接,并且去使用上面的容器技术,这个容器它是依赖于容器的运行时环境的,这个运行时环境目前来讲有很多,早期的时候只有k8s和docker是一个密切的结合,也就是说只有唯一的选择就是docker,但是现在经过多年的发展容器运行时也可以选择除了docker以外的其它的容器解决方案,CRI就是一个接口规范,容器的接口规范k8s已经定义了关于它的两种具体的服务,一种叫镜像服务一种叫运行时服务,镜像服务简单来说就是我定义了一个镜像的标准规范,比方说我们做这个容器的docker的时候这个镜像应该遵守哪种格式,它应该怎么去进行网络通讯等等,怎么去进行镜像的管理,另外就是容器运行的一个服务,这两个都是属于CRI的标准规范,当然k8s已经定义了这个标准规范,容器如果想使用k8s来进行通讯、进行相互的管理的话就要遵守这种规范。

对于容器运行时主要有两个级别:Low Level(使用接近内核层) 和 High Level(使用接近用户层)目前,市面上常用的容器引擎有很多,主要有下图的那几种。

003.jpg

CRI的运行时有两种,一种是低级运行时,还有一种是高级运行时,低级运行时可以认为就是更接近内核级,一个软件在运行的时候有的时候它属于易用性,有的时候它要和内核进行通讯,所以运行时是分成两类的,一个是低级的,比如说以runc为代表,runc主要是和内核进行通讯比较密切,而还有高级运行时,高级运行时主要是贴近用户的,也就是说用户去访问的时候是和高级运行时进行结合的,需要注意运行时这个概念是非常专业性的,对于很多人来讲不是很容易理解,什么叫运行时呢,简单说运行时就是一个程序运行的环境的集合,任何一个程序要运行都需要一套环境,例如java程序要运行它需要有java虚拟机,java虚拟机实际上就提供了java程序运行的运行时环境,简单说就是运行一个程序所需要的环境这就叫运行时,对应的还有一种叫编译时,比如一个程序刚刚开发出来它只是个源码,我要把它编译成一个二进制这时候就需要有编译时把它编译成一个可以执行的程序,这时候它就需要有一个叫编译这样的环境,它和运行时是两个不同阶段,一个程序先从源代码通过编译的这种环境把它编译成一个可以执行的二进制这就是编译时,然后最终要把它从二进制真正跑起来运行起来就需要用到运行时,所以这是不同的阶段需要依赖的环境,运行时表现主要是有两种,一种是这个程序运行所依赖的进程它有可能是相关的一些进程,也可以是它依赖的一些库,比如说java运行它有一些函数库它是要依赖的,简单的来说运行时它就是一套运行环境的依赖。

dockershim, containerd 和cri-o都是遵循CRI的容器运行时,我们称他们为高层级运行时(High-level Runtime)

其他的容器运营厂商最底层的runc仍然是Docker在维护的。

Google,CoreOS,RedHat都推出自已的运行时:lmctfy,rkt,cri-o,但到目前Docker仍然是最主流的容器引擎技术。

004.jpg

上图描述的就是k8s定义的两个规范,一个是CRI规范,k8s有个管理工具叫kubelet,它要去管理这些容器的话只要遵守CRI的规范那么这些容器都可以进行运行,这些运行目前来讲支持的容器遵守CRI的有很多,有docker为代表的、还有containerd、还有cri-o,这些都是遵守CRI的,也就是说目前来讲docker并不是唯一的选择,容器真正运行的时候它也有一个叫OCI的规范,OCI叫开放容器计划,它定义了创建容器的格式以及运行容器的标准规范,目前来讲这些都是行业标准,不论是用哪种的容器解决方案都是要左侧遵守CRI右侧要遵守OCI,容器的运行是遵守两套规范的,只有遵守两套规范才能上和k8s结合,下去调用这些所谓的标准的可以被别人所兼容的这种容器技术。

1.2 Kubernetes v1.24 之后不再支持 Docker ?

005.jpg

2014年 Docker & Kubernetes 蜜月期

2015~2016年 Kubernetes & RKT vs Docker, 最终Docker 胜出

2016年 Kubernetes逐渐赢得任务编排的胜利

2017年 rkt 和 containerd 捐献给 CNCF

2020年 kubernetes宣布废弃dockershim,但 Mirantis 和 Docker 宣布维护 dockershim

2022年5月3日,Kubernetes v1.24正式发布,此版本提供了很多重要功能。该版本涉及46项增强功能:其中14项已升级为稳定版,15项进入beta阶段,13项则刚刚进入alpha阶段。此外,另有2项功能被弃用、2项功能被删除。v1.24 之前的 Kubernetes 版本包括与 Docker Engine 的直接集成,使用名为 dockershim 的组件。 值得注意的是v1.24 的 Kubernetes 正式移除对Dockershim的支持,即默认不再支持 docker

2022年8月24日,Kubernetes v1.25 正式发布

在2013年docker开源之后,2014年k8s也开源,刚开始的时候在容器行业里边没有别的选择docker是唯一的选择,既然这样k8s当年和docker关系非常融洽,因为k8s属于弱者是后来者,它要想使用容器技术市面上没有别的选择只能和docker进行密切结合,当初在14年的时候它们是没有什么矛盾的,但是以谷歌为代表的k8s它不可能去屈从docker这样的小公司由你来主导,所以在后续就逐渐推出来一些相关的竞品来和docker竞争,k8s推出了相关的竞争产品RKT和docker进行竞争结果失败了没有成功docker胜出了,当然后续docker也希望能够进入到容器的编排领域它也开发了docker swarm产品,但是后期和k8s竞争失败了,也就是双方在各自擅长的领域都没有竞争过对方,双手只能在自己擅长的领域实现自己的目标,但是侵入到对方的地盘好像是非常困难的,后期k8s它把自己的一些相关技术就捐献到了CNCF,CNCF就是云原生的一个组织,在k8s逐渐占领企业级编排市场之后逐渐的它有了一定的主动权,在2020年它宣布要废除dockershim,这就是k8s 1.24以后不在支持docker的一个重要原因,当然宣布废止它没有立即废止,到了2年之后也就是2022年的5月3号它的1.24版发布之后正式剔除了dockershim这个组件,docker原来可以直接和k8s进行集成,现在不行了,需要额外的安装组件才可以,这就是它的一个重大变化,在3个月之后2022年的8月24号1.25正式发布。

官方说明

https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/

006.jpg

移除 Dockershim 的说明

https://kubernetes.io/zh-cn/blog/2022/02/17/dockershim-faq/

Dockershim的历史背景

https://mp.weixin.qq.com/s/elkfBVzN8-zC30111zFpMw
​
Kubernetes CNCF 2022-05-03 10:40 发表于香港
作者:Kat Cosgrove
​
从 Kubernetes v1.24 开始,Dockershim 会给移除,这对于项目来说是个积极的举措。然而,无论是在社会上,还是在软件开发中,上下文对于完全理解某些东西都是很重要的,这值得更深入的研究。在 Kubernetes v1.24 中移除 dockershim 的同时,我们在社区中看到了一些困惑(有时达到恐慌的程
度),和对这一决定的不满,很大程度上是由于缺乏关于这移除的上下文。弃用并最终将 dockershim 从 Kubernetes 移除的决定,并不是迅速或轻率做出的。尽管如此,它已经操作了很长时间,以至于今天的许多用户都比这个决定更新,当然也比导致 dockershim 首先成为必要的选择更新。
​
那么,dockershim 是什么,为什么它会消失?
​
在 Kubernetes 的早期,我们只支持一个容器运行时。那个运行时是 Docker Engine。当时,没有太多其他选择,Docker 是处理容器的主要工具,所以这不是个有争议的选择。最终,我们开始添加更多的容器运行时,比如 rkt 和 hypernetes,很明显 Kubernetes 用户希望选择最适合他们的运行时。因此 Kubernetes 需要种方法,来允许集群操作者灵活地使用他们选择的任何运行时。
​
发布CRI[1](Container Runtime Interface,容器运行时接口)就是为了提供这种灵活性。CRI 的引入对项目和用户来说都很棒,但它也引入了一个问题:Docker Engine 作为容器运行时的使用早于 CRI, Docker Engine 与 CRI 不兼容。为了解决这个问题,引入了一个小软件垫片("shim",dockershim)作为 kubelet 组件的一部分,专门用于填补 Docker Engine 和 CRI 之间的空白,允许集群运营商继续使用 Docker Engine 作为他们的容器运行时,基本上不会给中断。
​
然而,这个小小的软件垫片从来就不是永久的解决方案。多年来,它的存在给 kubelet 本身带来了许多不必要的复杂性。由于这个垫片,Docker 的一些集成实现不一致,导致维护人员的负担增加,并且维护特定于供应商的代码不符合我们的开源理念。为了减少这种维护负担,并向一个支持开放标准的更具协作性的社区发展,KEP-2221 获引入[2],它建议去掉 dockershim。随着 Kubernetes v1.20 的发布,这一弃用成为正式。
​
我们没有很好地传达这一点,不幸的是,弃用声明导致了社区内的一些恐慌。对于 Docker 作为一家公司来说这意味着什么,由 Docker 构建的容器镜像能否运行,以及 Docker Engine 实际是什么导致了社交媒体上的一场大火。这是我们的过失;我们应该更清楚地沟通当时发生了什么以及原因。为了解决这个问题,我们发布了一个博客[3]和相关的常见问题[4],以减轻社区的恐惧,并纠正一些关于 Docker 是什么,以及容器如何在 Kubernetes 中工作的误解。由于社区的关注,Docker 和 Mirantis 共同同意以cri-docker[5]的形式继续支持 dockershim 代码,允许你在需要时继续使用 Docker Engine 作为你的容器运行时。为了让那些想尝试其他运行时(如 containerd 或 cri-o)的用户感兴趣,编写了迁移文档[6]。
​
我们后来对社区进行了调查[7],发现仍然有许多用户有问题和顾虑[8]。作为回应,Kubernetes 维护者和 CNCF 致力于通过扩展文档和其他程序来解决这些问题。事实上,这篇博文就是这个计划的一部分。随着如此多的最终用户成功地迁移到其他运行时,以及文档的改进,我们相信现在每个人都有了迁移的道路。
​
无论是作为工具还是作为公司,Docker 都不会消失。它是云原生社区和 Kubernetes 项目历史的重要组成部分。没有他们我们不会有今天。也就是说,从 kubelet 中移除 dockershim 最终对社区、生态系统、项目和整个开源都有好处。这是我们所有人一起支持开放标准的机会,我们很高兴在 Docker 和社区的帮助下这样做。

Kubernetes 调用 runtime 的变化

007.jpg

k8s和docker之间的关系,上面4张图详细的描述了k8s和docker之间的从蜜月期到后期的分道扬镳中间的分分合合的历史,首先刚开始k8s刚刚诞生的时候docker已经在市面上有很强的市场号召力,而且它基本上已经是容器的唯一选择,在市面上没有容器的其它技术所竞争,所以k8s为了使用容器被逼无奈降低身段和docker密切结合,它当时去使用docker的话采用的机制很简单就是在k8s里面内置了一个docker的客户端,docker命令实际上它就是个客户端,在系统中运行docker它会开启一个守护进程,docker命令实际上就是充当客户端的,k8s为了使用docker它就开发在k8s里边内置了一个客户端直接连接docker从而就可以使用docker的容器了,这是最早时候的状态;

当然这种状态并没有持续太久,因为谷歌还是很有野心的,不可能一辈子依赖docker,如果是这样子的话永远跟着docker的脚步走这是很痛苦的,docker万一发生了一些接口的变化k8s就得改,k8s肯定是不甘心的,因此后期它就发布了一个所谓的概念叫CRI容器运行时接口,这个容器运行时接口简单的说就是k8s发布了一个接口,这个接口是一个公开的接口,这个接口尽可能保持稳定,有点像电源插座一样标准规范发布出来基本就不动了,通过这个接口再接入到k8s里边去,但是docker肯定不可能说主动的去上贴你的k8s,我去遵守你的CRI,docker当时的地位也是如日中天在容器领域一手遮天,所以它不可能去放下身段说我开发一个接口和你的CRI兼容,所以k8s当时为了让docker兼容它的CRI它就自己开发了一个软件叫dockershim,dockershim是k8s为了兼容docker开发的一个中间的组件,shim其实就是垫片的意思中间的一个小接口,这个小接口就是为了让k8s通过CRI接口连接到dockershim,dockershim是遵守CRI的,因为dockershim是k8s开发出来的,然后dockershim再去连接docker,从而让docker能够和k8s结合在一起,这就是后期的第二个阶段;

第三个阶段终于采取了进一步的手段,如果老是附着着docker k8s永无独立之日,因此它后期就变了,它把dockershim这个组件独立出来了,不再和k8s集成在一起,也就是说k8s发布的时候是没有dockershim的,这个dockershim是个独立的软件,你需要单独安装,但是早期的时候实际上它安装k8s的时候它会自动的把dockershim组件给你装上,但是这个组件已经不是内置在k8s的组件里了,它是个独立的软件了,只不过这个软件k8s安装会顺便给你装上,但是这个软件已经独立了,和刚才的状态已经不一样了,刚才dockershim是和k8s是在一起的,相当于一个软件内部就有了dockershim,现在它已经独立出来了,虽然是独立了这个软件仍然是由k8s开发出来的,因为docker并不遵守CRI;

接下来重头戏到了也就是1.24发布之后彻底的颠覆了我们之前的状态,因为1.24的时候k8s已经在江湖上赫赫有名,已经不在依附于docker,当时的容器技术也不仅仅只有docker可选,还有其它的容器可选,因此它就终于独立了,它决定把dockershim这个组件不在开发把它移除,这时候就变成了什么样子了,dockershim就彻底的没有了,1.24把dockershim抛弃了docker就没办法通过CRI和k8s进行通讯,自然k8s也就没有办法来使用docker的容器技术,当然docker也没有办法通过k8s来进行结合使用,这个对于k8s来讲已经不是问题,因为k8s除了docker以外它还有其它的容器技术可选,而docker对应的企业级的编排的领域只有k8s可选,因为k8s已经是垄断地位它没有第三方可选,在这种情况下docker放低身段只好自己开发一个软件贴在CRI基础之上这就是cri-dockerd,cri-dockerd是docker公司开发出来的,人家不兼容了抛弃你了,人家不理你了我就上杆子自己做个软件叫cri-dockerd来遵守CRI从而才能和k8s组合在一起。

008.jpg

009.jpg

1.3 Kubernetes v1.25 新变化

010.jpg

2022年8月24日,Kubernetes v1.25 正式发布

Kubernetes 1.25主题是Combiner,即组合器。

Kubernetes 1.25中包含多达40项增强功能

https://mp.weixin.qq.com/s/PKoNkhPU6OhjuuPzELP-Rg

PodSecurityPolicy被移除;Pod Security Admission毕业为稳定版

PodSecurityPolicy是在1.21版本中被决定弃用的,到1.25版本则将被正式删除。之所以删除此项功能,是因为想要进一步提升其可用性,就必须引入重大变更。为了保持项目整体稳定,只得加以弃用。取而代之的正是在1.25版本中毕业至稳定版的Pod Security Admission。如果你当前仍依赖PodSecurityPolicy,请按照Pod Security Admission迁移说明[1]进行操作。

临时容器迎来稳定版

临时容器是指在Pod中仅存在有限时长的容器。当我们需要检查另一容器,但又不能使用kubectl exec时(例如在执行故障排查时),往往可以用临时容器替代已经崩溃、或者镜像缺少调试工具的容器。临时容器在Kubernetes 1.23版本中已经升级至Beta版,这一次则进一步升级为稳定版。

对cgroups v2的稳定支持

自Linux内核cgroups v2 API公布稳定版至今,已经过去两年多时间。如今,已经有不少发行版默认使用此API,Kubernetes自然需要支持该内核才能顺利对接这些发行版。Cgroups v2对cgroups v1做出了多项改进,更多细节请参见cgroups v2说明文档[2]。虽然cgroups v1将继续受到支持,但我们后续将逐步弃用v1并全面替换为v2。

更好的Windows系统支持

  • 性能仪表板添加了对Windows系统的支持
  • 单元测试增加了对Windows系统的支持
  • 一致性测试增加了对Windows系统的支持
  • 为Windows Operational Readiness创建了新的GitHub

将容器注册服务从k8s.gcr.io移动至registry.k8s.io

1.25版本已经合并将容器注册服务从k8s.gcr.io移动至registry.k8s.io的变更。关于更多细节信息,请参阅相应wiki页面[3],我们也通过Kubernetes开发邮件清单发出了全面通报。

SeccompDefault升级为Beta版

网络策略中的endPort已升级为稳定版

网络策略中的endPort已经迎来GA通用版。支持endPort字段的网络策略提供程序,现可使用该字段来指定端口范围以应用网络策略。在之前的版本中,每个网络策略只能指向单一端口。

请注意,endPort的起效前提是必须得到网络策略提供程序的支持。如果提供程序不支持endPort,而您又在网络策略中指定了此字段,则会创建出仅覆盖端口字段(单端口)的网络策略。

本地临时存储容量隔离迎来稳定版

本地临时存储容量隔离功能已经迎来GA通用版。这项功能最早于1.8版本中公布了alpha版,在1.10中升级至beta,如今终于成为稳定功能。它通解为各Pod之间的本地临时存储提供容量隔离支持,例如EmptyDir。因此如果Pod对本地临时存储容量的消耗超过了该上限,则会驱逐该Pod以限制其对共享资源的占用。

核心CSI迁移迎来稳定版

CSI迁移是SIG Storage在之前多个版本中做出的持续努力,目标是将树内存储卷插件移动到树外CSI驱动程序,并最终移除树内存储卷插件。此次核心CSI迁移已迎来GA通用版,GCE PD和AWS EBS的CSI迁移功能也同步达到GA阶段。vSphere的CSI迁移仍处于beta阶段(但也已经默认启用),Portworx的CSI迁移功能同样处于beta阶段(默认关闭)。

CSI临时存储卷提升至稳定版

CSI临时存储卷功能,允许用户在临时用例的pod规范中直接指定CSI存储卷。如此一来,即可使用已安装的存储卷直接在pod内注入任意状态,例如配置、机密、身份、变量或其他类似信息。这项功能最初于1.15版本中推出alpha版,现已升级为GA通用版。某些CSI驱动程序会使用此功能,例如负责存储秘密信息的CSI驱动程序。

CRD验证表达式语言升级至Beta版

CRD验证表达式语言现已升级为beta版,因此声明能够使用通用表达式语言(CEL)验证自定义资源。

服务器端未知字段验证升级为Beta版

ServerSideFieldValidation功能门现已升级为Beta版(默认启用),允许用户在检测到未知字段时,有选择地触发API服务器上的模式验证机制。如此一来,即可从kubectl中删除客户端验证,同时继续保持对包含未知/无效字段的请求报错。

引入KMS v2 API

引入KMS v2 alpha1 API以提升性能,实现轮替与可观察性改进。此API使用AES-GCM替代了AES-CBC,通过DEK实现静态数据(即Kubernetes Secrets)加密。过程中无需额外用户操作,而且仍然支持通过AES-GCM和AES-CBC进行读取

1.4 Kubernetes v1.25 集群创建方案

Kubernetes v1.25 集群创建

011.jpg

上图描述了k8s和docker的关系,用户通过k8s的REST API接口去连接kube-apiserver,然后通过kube-apiserver连接kubelet,kubelet通过CRI接口去连接下面的容器,目前来讲去使用容器的话有常见的3中选择,可以选择用cri-docker连接到docker再去通过containerd去runC再去container,可以使用右侧一条线来使用容器这是早期已经使用的调用方法,当然因为1.24已经抛弃dockershim换成cri-docker,而k8s希望能够尽可能摆脱docker的限制,它就默认CRI接口用的是containerd,containerd和CRI之间需要有一个垫片这个垫片通过CRI-Containerd连接到containerd再去运行容器,containerd不论是docker而好还是CRI-Containerd都用的这个组件,这个组件实际上也是docker开源出来的捐献给CNCF了,containerd究其根本还是docker公司的,只不过要想使用docker命令你就必须使用docker Engine,必须要安装cri-docker组件,如果用containerd可以不安装docker,直接用默认的就可以,只装containerd就行,但是containerd要操作容器的话就不方便,它用的工具并不是我们熟悉的工具,而目前大家更熟悉的是docker命令,目前来讲安装的话还是要用docker需要安装cri-docker包括docker Engine,当然底层它实际上还是走的是containerd,但是它缺少用户空间的工具,如果不去安装docker的话哪些工具就没办法使用了,除此之外还有一个CRI-O它是红帽公司研发出来的容器技术,不过这个在生产中是很少使用的技术,containerd是高级运行时,runC是低级运行时,这两个技术都是docker开源出来的,也就是说实际上还是离不开docker的影响,只不过表面上脱离了docker的管理了。

  • 方式1: Containerd

    默认情况下,Kubernetes在创建集群的时候,使用的就是Containerd 方式。

  • 方式2: Docker

    Docker使用的普及率较高,虽然Kubernetes-v1.24 默认情况下废弃了kubelet对于Docker的支持,但是我们还可以借助于Mirantis维护的cri-dockerd插件方式来实现Kubernetes集群的创建。

    Docker Engine 没有实现 CRI, 而这是容器运行时在 Kubernetes 中工作所需要的。 为此,必须安装一个额外的服务 cri-dockerd。 cri-dockerd 是一个基于传统的内置 Docker 引擎支持的项目, 它在 1.24 版本从 kubelet 中移除

    项目站点: github.com/Mirantis/cr…

  • 方式3: CRI-O

    CRI-O的方式是Kubernetes创建容器最直接的一种方式,在创建集群的时候,需要借助于cri-o插件的方式来实现Kubernetes集群的创建。

2.Kubernetes 高可用集群部署架构

本示例中的Kubernetes集群部署将基于以下环境进行。

012.jpg

表1-1 高可用Kubernetes集群规划

角色机器名机器配置ip地址安装软件
K8s 集群主节点 1,Master和etcdk8s-master01.example.local2C4G172.31.3.101chrony-client、docker、kubeadm 、kubelet、kubectl
K8s 集群主节点 2,Master和etcdk8s-master02.example.local2C4G172.31.3.102chrony-client、docker、kubeadm 、kubelet、kubectl
K8s 集群主节点 3,Master和etcdk8s-master03.example.local2C4G172.31.3.103chrony-client、docker、kubeadm 、kubelet、kubectl
K8s 主节点访问入口 1,提供高可用及负载均衡k8s-ha01.example.local2C2G172.31.3.104chrony-server、haproxy、keepalived
K8s 主节点访问入口 2,提供高可用及负载均衡k8s-ha02.example.local2C2G172.31.3.105chrony-server、haproxy、keepalived
容器镜像仓库1k8s-harbor01.example.local2C2G172.31.3.106chrony-client、docker、docker-compose、harbor
容器镜像仓库2k8s-harbor02.example.local2C2G172.31.3.107chrony-client、docker、docker-compose、harbor
K8s 集群工作节点 1k8s-node01.example.local2C4G172.31.3.108chrony-client、docker、kubeadm 、kubelet
K8s 集群工作节点 2k8s-node02.example.local2C4G172.31.3.109chrony-client、docker、kubeadm 、kubelet
K8s 集群工作节点 3k8s-node03.example.local2C4G172.31.3.110chrony-client、docker、kubeadm 、kubelet
VIP,在ha01和ha02主机实现k8s.example.local172.31.3.188

软件版本信息和Pod、Service网段规划:

配置信息备注
支持的操作系统版本CentOS 7.9/stream 8、Rocky 8、Ubuntu 18.04/20.04
Container Runtime:Docker CE 20.10.17
CRIcri-dockerd v0.2.5
kubeadm版本1.25.0
宿主机网段172.31.0.0/21
Pod网段192.168.0.0/12
Service网段10.96.0.0/12

3.基于Kubeadm 实现 Kubernetes v1.25.0集群部署流程说明

官方说明

https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/
​
https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
​
https://kubernetes.io/zh-cn/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/

使用 kubeadm,能创建一个符合最佳实践的最小化 Kubernetes 集群。 事实上,你可以使用 kubeadm配置一个通过 Kubernetes 一致性测试的集群。 kubeadm 还支持其他集群生命周期功能, 例如启动引导令牌和集群升级。

  • 每个节点主机的初始环境准备
  • Kubernetes集群API访问入口的高可用
  • 在所有Master和Node节点都安装容器运行时,实际Kubernetes只使用其中的Containerd在所有Master和Node节点安装kubeadm 、kubelet、kubectl
  • 在所有节点安装和配置 cri-dockerd
  • 在第一个 master 节点运行 kubeadm init 初始化命令 ,并验证 master 节点状态
  • 在第一个 master 节点安装配置网络插件
  • 在其它master节点运行kubeadm join 命令加入到控制平面集群中
  • 在所有 node 节点使用 kubeadm join 命令加入集群
  • 创建 pod 并启动容器测试访问 ,并测试网络通信

4.基于Kubeadm 部署 Kubernetes v1.25.0高可用集群案例

4.1 所有主机初始化

4.1.1 设置ip地址

#CentOS
[root@k8s-master01 ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0 
DEVICE=eth0
NAME=eth0
BOOTPROTO=none
ONBOOT=yes
IPADDR=172.31.3.101
PREFIX=21
GATEWAY=172.31.0.2
DNS1=223.5.5.5
DNS2=180.76.76.76#Ubuntu
root@k8s-master01:~# cat /etc/netplan/01-netcfg.yaml 
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      addresses: [172.31.3.101/21] 
      gateway4: 172.31.0.2
      nameservers:
        addresses: [223.5.5.5, 180.76.76.76]

4.1.2 设置主机名

hostnamectl set-hostname k8s-master01.example.local
hostnamectl set-hostname k8s-master02.example.local
hostnamectl set-hostname k8s-master03.example.local
hostnamectl set-hostname k8s-ha01.example.local
hostnamectl set-hostname k8s-ha02.example.local
hostnamectl set-hostname k8s-harbor01.example.local
hostnamectl set-hostname k8s-harbor02.example.local
hostnamectl set-hostname k8s-node01.example.local
hostnamectl set-hostname k8s-node02.example.local
hostnamectl set-hostname k8s-node03.example.local

4.1.3 配置镜像源

CentOS 7所有节点配置 yum源如下:

rm -f /etc/yum.repos.d/*.repo
​
cat > /etc/yum.repos.d/base.repo <<EOF
[base]
name=base
baseurl=https://mirrors.cloud.tencent.com/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-$releasever[extras]
name=extras
baseurl=https://mirrors.cloud.tencent.com/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-$releasever[updates]
name=updates
baseurl=https://mirrors.cloud.tencent.com/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-$releasever[centosplus]
name=centosplus
baseurl=https://mirrors.cloud.tencent.com/centos/$releasever/centosplus/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-$releasever[epel]
name=epel
baseurl=https://mirrors.cloud.tencent.com/epel/$releasever/$basearch/
gpgcheck=1
gpgkey=https://mirrors.cloud.tencent.com/epel/RPM-GPG-KEY-EPEL-$releasever
EOF

Rocky 8所有节点配置 yum源如下:

rm -f /etc/yum.repos.d/*.repo
​
cat > /etc/yum.repos.d/base.repo <<EOF
[BaseOS]
name=BaseOS
baseurl=https://mirrors.sjtug.sjtu.edu.cn/rocky/$releasever/BaseOS/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
​
[AppStream]
name=AppStream
baseurl=https://mirrors.sjtug.sjtu.edu.cn/rocky/$releasever/AppStream/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
​
[extras]
name=extras
baseurl=https://mirrors.sjtug.sjtu.edu.cn/rocky/$releasever/extras/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
enabled=1[plus]
name=plus
baseurl=https://mirrors.sjtug.sjtu.edu.cn/rocky/$releasever/plus/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
EOF

CentOS stream 8所有节点配置 yum源如下:

rm -f /etc/yum.repos.d/*.repo
​
cat > /etc/yum.repos.d/base.repo <<EOF
[BaseOS]
name=BaseOS
baseurl=https://mirrors.cloud.tencent.com/centos/$releasever-stream/BaseOS/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
​
[AppStream]
name=AppStream
baseurl=https://mirrors.cloud.tencent.com/centos/$releasever-stream/AppStream/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
​
[extras]
name=extras
baseurl=https://mirrors.cloud.tencent.com/centos/$releasever-stream/extras/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
​
[centosplus]
name=centosplus
baseurl=https://mirrors.cloud.tencent.com/centos/$releasever-stream/centosplus/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
EOF

Ubuntu 所有节点配置 apt源如下:

cat > /etc/apt/sources.list <<EOF
deb http://mirrors.cloud.tencent.com/ubuntu/ $(lsb_release -cs) main restricted universe multiverse
deb-src http://mirrors.cloud.tencent.com/ubuntu/ $(lsb_release -cs) main restricted universe multiverse
​
deb http://mirrors.cloud.tencent.com/ubuntu/ $(lsb_release -cs)-security main restricted universe multiverse
deb-src http://mirrors.cloud.tencent.com/ubuntu/ $(lsb_release -cs)-security main restricted universe multiverse
​
deb http://mirrors.cloud.tencent.com/ubuntu/ $(lsb_release -cs)-updates main restricted universe multiverse
deb-src http://mirrors.cloud.tencent.com/ubuntu/ $(lsb_release -cs)-updates main restricted universe multiverse
​
deb http://mirrors.cloud.tencent.com/ubuntu/ $(lsb_release -cs)-proposed main restricted universe multiverse
deb-src http://mirrors.cloud.tencent.com/ubuntu/ $(lsb_release -cs)-proposed main restricted universe multiverse
​
deb http://mirrors.cloud.tencent.com/ubuntu/ $(lsb_release -cs)-backports main restricted universe multiverse
deb-src http://mirrors.cloud.tencent.com/ubuntu/ $(lsb_release -cs)-backports main restricted universe multiverse
EOF
​
apt update

4.1.4 必备工具安装

#CentOS安装
yum -y install vim tree lrzsz wget jq psmisc net-tools telnet yum-utils device-mapper-persistent-data lvm2 git 
#Rocky除了安装上面工具,还需要安装rsync
yum -y install rsync
​
#Ubuntu安装
apt -y install tree lrzsz jq

4.1.5 配置 ssh key 验证

配置 ssh key 验证,方便后续同步文件

[root@k8s-master01 ~]# cat ssh_key_push.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2021-11-19
#FileName:      ssh_key_push.sh
#URL:           raymond.blog.csdn.net
#Description:   ssh_key_push for CentOS 7/8 & Ubuntu 18.04/24.04 & Rocky 8
#Copyright (C): 2021 All rights reserved
#*********************************************************************************************
COLOR="echo -e \033[01;31m"
END='\033[0m'
​
export SSHPASS=123456
HOSTS="
172.31.3.101
172.31.3.102
172.31.3.103
172.31.3.104
172.31.3.105
172.31.3.106
172.31.3.107
172.31.3.108
172.31.3.109
172.31.3.110"
​
os(){
    OS_ID=`sed -rn '/^NAME=/s@.*="([[:alpha:]]+).*"$@\1@p' /etc/os-release`
}
​
ssh_key_push(){
    rm -f ~/.ssh/id_rsa*
    ssh-keygen -f /root/.ssh/id_rsa -P '' &> /dev/null
    if [ ${OS_ID} == "CentOS" -o ${OS_ID} == "Rocky" ] &> /dev/null;then
        rpm -q sshpass &> /dev/null || { ${COLOR}"安装sshpass软件包"${END};yum -y install sshpass &> /dev/null; }
    else
        dpkg -S sshpass &> /dev/null || { ${COLOR}"安装sshpass软件包"${END};apt -y install sshpass &> /dev/null; }
    fi
    for i in $HOSTS;do
        {
            sshpass -e ssh-copy-id -o StrictHostKeyChecking=no -i /root/.ssh/id_rsa.pub $i &> /dev/null
            [ $? -eq 0 ] && echo $i is finished || echo $i is false
        }&
    done
    wait
}
​
main(){
    os
    ssh_key_push
}
​
main
​
[root@k8s-master01 ~]# bash ssh_key_push.sh 
安装sshpass软件包
172.31.3.105 is finished
172.31.3.108 is finished
172.31.3.109 is finished
172.31.3.106 is finished
172.31.3.101 is finished
172.31.3.110 is finished
172.31.3.104 is finished
172.31.3.107 is finished
172.31.3.102 is finished
172.31.3.103 is finished

4.1.6 设置域名解析

cat >> /etc/hosts <<EOF
172.31.3.101 k8s-master01.example.local k8s-master01
172.31.3.102 k8s-master02.example.local k8s-master02
172.31.3.103 k8s-master03.example.local k8s-master03
172.31.3.104 k8s-ha01.example.local k8s-ha01
172.31.3.105 k8s-ha02.example.local k8s-ha02
172.31.3.106 k8s-harbor01.example.local k8s-harbor01
172.31.3.107 k8s-harbor02.example.local k8s-harbor02
172.31.3.108 k8s-node01.example.local k8s-node01
172.31.3.109 k8s-node02.example.local k8s-node02
172.31.3.110 k8s-node03.example.local k8s-node03
172.31.3.188 kubeapi.raymonds.cc kubeapi
172.31.3.188 harbor.raymonds.cc
EOFfor i in {102..110};do scp /etc/hosts 172.31.3.$i:/etc/ ;done

4.1.7 关闭防火墙

#CentOS
systemctl disable --now firewalld
​
#CentOS 7
systemctl disable --now NetworkManager
​
#Ubuntu
systemctl disable --now ufw

4.1.8 禁用SELinux

#CentOS
setenforce 0
sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config#Ubuntu
Ubuntu没有安装SELinux,不用设置

4.1.9 禁用swap

sed -ri 's/.*swap.*/#&/' /etc/fstab
swapoff -a
​
#Ubuntu 20.04,执行下面命令
sed -ri 's/.*swap.*/#&/' /etc/fstab
SD_NAME=`lsblk|awk -F"[ └─]" '/SWAP/{printf $3}'`
systemctl mask dev-${SD_NAME}.swap
swapoff -a

4.1.10 时间同步

ha01和ha02上安装chrony-server:

[root@k8s-ha01 ~]# cat install_chrony_server.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2021-11-22
#FileName:      install_chrony_server.sh
#URL:           raymond.blog.csdn.net
#Description:   install_chrony_server for CentOS 7/8 & Ubuntu 18.04/20.04 & Rocky 8
#Copyright (C): 2021 All rights reserved
#*********************************************************************************************
COLOR="echo -e \033[01;31m"
END='\033[0m'os(){
    OS_ID=`sed -rn '/^NAME=/s@.*="([[:alpha:]]+).*"$@\1@p' /etc/os-release`
}
​
install_chrony(){
    if [ ${OS_ID} == "CentOS" -o ${OS_ID} == "Rocky" ] &> /dev/null;then
        yum -y install chrony &> /dev/null
        sed -i -e '/^pool.*/d' -e '/^server.*/d' -e '/^# Please consider .*/a\server ntp.aliyun.com iburst\nserver time1.cloud.tencent.com iburst\nserver ntp.tuna.tsinghua.edu.cn iburst' -e 's@^#allow.*@allow 0.0.0.0/0@' -e 's@^#local.*@local stratum 10@' /etc/chrony.conf
        systemctl enable --now chronyd &> /dev/null
        systemctl is-active chronyd &> /dev/null ||  { ${COLOR}"chrony 启动失败,退出!"${END} ; exit; }
        ${COLOR}"chrony安装完成"${END}
    else
        apt -y install chrony &> /dev/null
        sed -i -e '/^pool.*/d' -e '/^# See http:.*/a\server ntp.aliyun.com iburst\nserver time1.cloud.tencent.com iburst\nserver ntp.tuna.tsinghua.edu.cn iburst' /etc/chrony/chrony.conf
        echo "allow 0.0.0.0/0" >> /etc/chrony/chrony.conf
        echo "local stratum 10" >> /etc/chrony/chrony.conf
        systemctl enable --now chronyd &> /dev/null
        systemctl is-active chronyd &> /dev/null ||  { ${COLOR}"chrony 启动失败,退出!"${END} ; exit; }
        ${COLOR}"chrony安装完成"${END}
    fi
}
​
main(){
    os
    install_chrony
}
​
main
​
[root@k8s-ha01 ~]# bash install_chrony_server.sh 
chrony安装完成
​
[root@k8s-ha02 ~]# bash install_chrony_server.sh 
chrony安装完成
​
[root@k8s-ha01 ~]# chronyc sources -nv
210 Number of sources = 3
MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^* 203.107.6.88                  2   6    17    39  -1507us[-8009us] +/-   37ms
^- 139.199.215.251               2   6    17    39    +10ms[  +10ms] +/-   48ms
^? 101.6.6.172                   0   7     0     -     +0ns[   +0ns] +/-    0ns
​
[root@k8s-ha02 ~]# chronyc sources -nv
210 Number of sources = 3
MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^* 203.107.6.88                  2   6    17    40    +90us[-1017ms] +/-   32ms
^+ 139.199.215.251               2   6    33    37    +13ms[  +13ms] +/-   25ms
^? 101.6.6.172                   0   7     0     -     +0ns[   +0ns] +/-    0ns

master、node、harbor上安装chrony-client:

[root@k8s-master01 ~]# cat install_chrony_client.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2021-11-22
#FileName:      install_chrony_client.sh
#URL:           raymond.blog.csdn.net
#Description:   install_chrony_client for CentOS 7/8 & Ubuntu 18.04/20.04 & Rocky 8
#Copyright (C): 2021 All rights reserved
#*********************************************************************************************
COLOR="echo -e \033[01;31m"
END='\033[0m'
SERVER1=172.31.3.104
SERVER2=172.31.3.105
​
os(){
    OS_ID=`sed -rn '/^NAME=/s@.*="([[:alpha:]]+).*"$@\1@p' /etc/os-release`
}
​
install_chrony(){
    if [ ${OS_ID} == "CentOS" -o ${OS_ID} == "Rocky" ] &> /dev/null;then
        yum -y install chrony &> /dev/null
        sed -i -e '/^pool.*/d' -e '/^server.*/d' -e '/^# Please consider .*/a\server '${SERVER1}' iburst\nserver '${SERVER2}' iburst' /etc/chrony.conf
        systemctl enable --now chronyd &> /dev/null
        systemctl is-active chronyd &> /dev/null ||  { ${COLOR}"chrony 启动失败,退出!"${END} ; exit; }
        ${COLOR}"chrony安装完成"${END}
    else
        apt -y install chrony &> /dev/null
        sed -i -e '/^pool.*/d' -e '/^# See http:.*/a\server '${SERVER1}' iburst\nserver '${SERVER2}' iburst' /etc/chrony/chrony.conf
        systemctl enable --now chronyd &> /dev/null
        systemctl is-active chronyd &> /dev/null ||  { ${COLOR}"chrony 启动失败,退出!"${END} ; exit; }
        systemctl restart chronyd
        ${COLOR}"chrony安装完成"${END}
    fi
}
​
main(){
    os
    install_chrony
}
​
main
​
[root@k8s-master01 ~]# for i in k8s-master02 k8s-master03 k8s-harbor01 k8s-harbor02 k8s-node01 k8s-node02 k8s-node03;do scp install_chrony_client.sh $i:/root/ ; done
​
[root@k8s-master01 ~]# bash install_chrony_client.sh 
chrony安装完成
​
[root@k8s-master01 ~]# chronyc sources -nv
210 Number of sources = 2
MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^+ k8s-ha01                      3   6    17     8    +84us[  +74us] +/-   55ms
^* k8s-ha02                      3   6    17     8    -82us[  -91us] +/-   45ms

4.1.11 设置时区

#CentOS
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo 'Asia/Shanghai' >/etc/timezone
​
#Ubuntu
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo 'Asia/Shanghai' >/etc/timezone
​
cat >> /etc/default/locale <<-EOF
LC_TIME=en_DK.UTF-8
EOF

4.1.12 优化资源限制参数

ulimit -SHn 65535
​
cat >>/etc/security/limits.conf <<EOF
* soft nofile 65536
* hard nofile 131072
* soft nproc 65535
* hard nproc 655350
* soft memlock unlimited
* hard memlock unlimited
EOF

4.1.13 内核配置

CentOS7 需要升级内核至4.18+,本地升级的版本为4.19

在master01节点下载内核:

[root@k8s-master01 ~]# wget http://193.49.22.109/elrepo/kernel/el7/x86_64/RPMS/kernel-ml-devel-4.19.12-1.el7.elrepo.x86_64.rpm
​
[root@k8s-master01 ~]# wget http://193.49.22.109/elrepo/kernel/el7/x86_64/RPMS/kernel-ml-4.19.12-1.el7.elrepo.x86_64.rpm

从master01节点传到其他节点:

[root@k8s-master01 ~]# for i in k8s-master02 k8s-master03 k8s-node01 k8s-node02 k8s-node03;do scp kernel-ml-4.19.12-1.el7.elrepo.x86_64.rpm kernel-ml-devel-4.19.12-1.el7.elrepo.x86_64.rpm $i:/root/ ; done

所有节点安装内核

cd /root && yum localinstall -y kernel-ml*

所有节点更改内核启动顺序

grub2-set-default 0 && grub2-mkconfig -o /etc/grub2.cfg
​
grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"

检查默认内核是不是4.19

grubby --default-kernel
​
[root@k8s-master01 ~]# grubby --default-kernel
/boot/vmlinuz-4.19.12-1.el7.elrepo.x86_64

所有节点重启,然后检查内核是不是4.19

reboot
​
uname -a
​
[root@k8s-master01 ~]# uname -a
Linux k8s-master01 4.19.12-1.el7.elrepo.x86_64 #1 SMP Fri Dec 21 11:06:36 EST 2018 x86_64 x86_64 x86_64 GNU/Linux

master和node安装ipvsadm:

#CentOS
yum -y install ipvsadm ipset sysstat conntrack libseccomp
​
#Ubuntu
apt -y install ipvsadm ipset sysstat conntrack libseccomp-dev

所有节点配置ipvs模块,在内核4.19+版本nf_conntrack_ipv4已经改为nf_conntrack, 4.18以下使用nf_conntrack_ipv4即可:

modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack #内核小于4.18,把这行改成nf_conntrack_ipv4cat >> /etc/modules-load.d/ipvs.conf <<EOF
ip_vs
ip_vs_lc
ip_vs_wlc
ip_vs_rr
ip_vs_wrr
ip_vs_lblc
ip_vs_lblcr
ip_vs_dh
ip_vs_sh
ip_vs_fo
ip_vs_nq
ip_vs_sed
ip_vs_ftp
ip_vs_sh
nf_conntrack #内核小于4.18,把这行改成nf_conntrack_ipv4
ip_tables
ip_set
xt_set
ipt_set
ipt_rpfilter
ipt_REJECT
ipip
EOF
​
然后执行systemctl enable --now systemd-modules-load.service即可

开启一些k8s集群中必须的内核参数,所有节点配置k8s内核:

cat > /etc/sysctl.d/k8s.conf <<EOF 
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
fs.may_detach_mounts = 1
vm.overcommit_memory=1
vm.panic_on_oom=0
fs.inotify.max_user_watches=89100
fs.file-max=52706963
fs.nr_open=52706963
net.netfilter.nf_conntrack_max=2310720net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl =15
net.ipv4.tcp_max_tw_buckets = 36000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_orphans = 327680
net.ipv4.tcp_orphan_retries = 3
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.ip_conntrack_max = 65536
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_timestamps = 0
net.core.somaxconn = 16384
EOF
​
sysctl --system

Kubernetes内核优化常用参数详解:

net.ipv4.ip_forward = 1 #其值为0,说明禁止进行IP转发;如果是1,则说明IP转发功能已经打开。
net.bridge.bridge-nf-call-iptables = 1 #二层的网桥在转发包时也会被iptables的FORWARD规则所过滤,这样有时会出现L3层的iptables rules去过滤L2的帧的问题
net.bridge.bridge-nf-call-ip6tables = 1 #是否在ip6tables链中过滤IPv6包 
fs.may_detach_mounts = 1 #当系统有容器运行时,需要设置为1vm.overcommit_memory=1  
#0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
#1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
#2, 表示内核允许分配超过所有物理内存和交换空间总和的内存vm.panic_on_oom=0 
#OOM就是out of memory的缩写,遇到内存耗尽、无法分配的状况。kernel面对OOM的时候,咱们也不能慌乱,要根据OOM参数来进行相应的处理。
#值为0:内存不足时,启动 OOM killer。
#值为1:内存不足时,有可能会触发 kernel panic(系统重启),也有可能启动 OOM killer。
#值为2:内存不足时,表示强制触发 kernel panic,内核崩溃GG(系统重启)。fs.inotify.max_user_watches=89100 #表示同一用户同时可以添加的watch数目(watch一般是针对目录,决定了同时同一用户可以监控的目录数量)fs.file-max=52706963 #所有进程最大的文件数
fs.nr_open=52706963 #单个进程可分配的最大文件数
net.netfilter.nf_conntrack_max=2310720 #连接跟踪表的大小,建议根据内存计算该值CONNTRACK_MAX = RAMSIZE (in bytes) / 16384 / (x / 32),并满足nf_conntrack_max=4*nf_conntrack_buckets,默认262144net.ipv4.tcp_keepalive_time = 600  #KeepAlive的空闲时长,或者说每次正常发送心跳的周期,默认值为7200s(2小时)
net.ipv4.tcp_keepalive_probes = 3 #在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包次数,默认值为9(次)
net.ipv4.tcp_keepalive_intvl =15 #KeepAlive探测包的发送间隔,默认值为75s
net.ipv4.tcp_max_tw_buckets = 36000 #Nginx 之类的中间代理一定要关注这个值,因为它对你的系统起到一个保护的作用,一旦端口全部被占用,服务就异常了。 tcp_max_tw_buckets 能帮你降低这种情况的发生概率,争取补救时间。
net.ipv4.tcp_tw_reuse = 1 #只对客户端起作用,开启后客户端在1s内回收
net.ipv4.tcp_max_orphans = 327680 #这个值表示系统所能处理不属于任何进程的socket数量,当我们需要快速建立大量连接时,就需要关注下这个值了。net.ipv4.tcp_orphan_retries = 3
#出现大量fin-wait-1
#首先,fin发送之后,有可能会丢弃,那么发送多少次这样的fin包呢?fin包的重传,也会采用退避方式,在2.6.358内核中采用的是指数退避,2s,4s,最后的重试次数是由tcp_orphan_retries来限制的。net.ipv4.tcp_syncookies = 1 #tcp_syncookies是一个开关,是否打开SYN Cookie功能,该功能可以防止部分SYN攻击。tcp_synack_retries和tcp_syn_retries定义SYN的重试次数。
net.ipv4.tcp_max_syn_backlog = 16384 #进入SYN包的最大请求队列.默认1024.对重负载服务器,增加该值显然有好处.
net.ipv4.ip_conntrack_max = 65536 #表明系统将对最大跟踪的TCP连接数限制默认为65536
net.ipv4.tcp_max_syn_backlog = 16384 #指定所能接受SYN同步包的最大客户端数量,即半连接上限;
net.ipv4.tcp_timestamps = 0 #在使用 iptables 做 nat 时,发现内网机器 ping 某个域名 ping 的通,而使用 curl 测试不通, 原来是 net.ipv4.tcp_timestamps 设置了为 1 ,即启用时间戳
net.core.somaxconn = 16384  #Linux中的一个kernel参数,表示socket监听(listen)的backlog上限。什么是backlog呢?backlog就是socket的监听队列,当一个请求(request)尚未被处理或建立时,他会进入backlog。而socket server可以一次性处理backlog中的所有请求,处理后的请求不再位于监听队列中。当server处理请求较慢,以至于监听队列被填满后,新来的请求会被拒绝。

所有节点配置完内核后,重启服务器,保证重启后内核依旧加载

reboot
lsmod | grep --color=auto -e ip_vs -e nf_conntrack
​
[root@k8s-master01 ~]# lsmod | grep --color=auto -e ip_vs -e nf_conntrack
ip_vs_ftp              16384  0 
nf_nat                 32768  1 ip_vs_ftp
ip_vs_sed              16384  0 
ip_vs_nq               16384  0 
ip_vs_fo               16384  0 
ip_vs_sh               16384  0 
ip_vs_dh               16384  0 
ip_vs_lblcr            16384  0 
ip_vs_lblc             16384  0 
ip_vs_wrr              16384  0 
ip_vs_rr               16384  0 
ip_vs_wlc              16384  0 
ip_vs_lc               16384  0 
ip_vs                 151552  24 ip_vs_wlc,ip_vs_rr,ip_vs_dh,ip_vs_lblcr,ip_vs_sh,ip_vs_fo,ip_vs_nq,ip_vs_lblc,ip_vs_wrr,ip_vs_lc,ip_vs_sed,ip_vs_ftp
nf_conntrack          143360  2 nf_nat,ip_vs
nf_defrag_ipv6         20480  1 nf_conntrack
nf_defrag_ipv4         16384  1 nf_conntrack
libcrc32c              16384  4 nf_conntrack,nf_nat,xfs,ip_vs

4.2 部署 Kubernetes 集群 API 访问入口的高可用

(注意:如果不是高可用集群,haproxy和keepalived无需安装)

公有云要用公有云自带的负载均衡,比如阿里云的SLB,腾讯云的ELB,用来替代haproxy和keepalived,因为公有云大部分都是不支持keepalived的,另外如果用阿里云的话,kubectl控制端不能放在master节点,推荐使用腾讯云,因为阿里云的slb有回环的问题,也就是slb代理的服务器不能反向访问SLB,但是腾讯云修复了这个问题。

在172.31.3.104和172.31.3.105上实现如下操作

4.2.1 安装 HAProxy

利用 HAProxy 实现 Kubeapi 服务的负载均衡

[root@k8s-ha01 ~]# cat install_haproxy.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2021-12-29
#FileName:      install_haproxy.sh
#URL:           raymond.blog.csdn.net
#Description:   install_haproxy for CentOS 7/8 & Ubuntu 18.04/20.04 & Rocky 8
#Copyright (C): 2021 All rights reserved
#*********************************************************************************************
SRC_DIR=/usr/local/src
COLOR="echo -e \033[01;31m"
END='\033[0m'
CPUS=`lscpu |awk '/^CPU(s)/{print $2}'`
​
#lua下载地址:http://www.lua.org/ftp/lua-5.4.4.tar.gz
LUA_FILE=lua-5.4.4.tar.gz
​
#haproxy下载地址:https://www.haproxy.org/download/2.6/src/haproxy-2.6.4.tar.gz
HAPROXY_FILE=haproxy-2.6.4.tar.gz
HAPROXY_INSTALL_DIR=/apps/haproxy
​
STATS_AUTH_USER=admin
STATS_AUTH_PASSWORD=123456
​
VIP=172.31.3.188
MASTER01=172.31.3.101
MASTER02=172.31.3.102
MASTER03=172.31.3.103
HARBOR01=172.31.3.106
HARBOR02=172.31.3.107
​
os(){
    OS_ID=`sed -rn '/^NAME=/s@.*="([[:alpha:]]+).*"$@\1@p' /etc/os-release`
}
​
check_file (){
    cd ${SRC_DIR}
    ${COLOR}'检查Haproxy相关源码包'${END}
    if [ ! -e ${LUA_FILE} ];then
        ${COLOR}"缺少${LUA_FILE}文件,请把文件放到${SRC_DIR}目录下"${END}
        exit
    elif [ ! -e ${HAPROXY_FILE} ];then
        ${COLOR}"缺少${HAPROXY_FILE}文件,请把文件放到${SRC_DIR}目录下"${END}
        exit
    else
        ${COLOR}"相关文件已准备好"${END}
    fi
}
​
install_haproxy(){
    [ -d ${HAPROXY_INSTALL_DIR} ] && { ${COLOR}"Haproxy已存在,安装失败"${END};exit; }
    ${COLOR}"开始安装Haproxy"${END}
    ${COLOR}"开始安装Haproxy依赖包"${END}
    if [ ${OS_ID} == "CentOS" -o ${OS_ID} == "Rocky" ] &> /dev/null;then
        yum -y install gcc make gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel libtermcap-devel ncurses-devel libevent-devel readline-devel &> /dev/null
    else
        apt update &> /dev/null;apt -y install gcc make openssl libssl-dev libpcre3 libpcre3-dev zlib1g-dev libreadline-dev libsystemd-dev &> /dev/null
    fi
    tar xf ${LUA_FILE}
    LUA_DIR=`echo ${LUA_FILE} | sed -nr 's/^(.*[0-9]).([[:lower:]]).*/\1/p'`
    cd ${LUA_DIR}
    make all test
    cd ${SRC_DIR}
    tar xf ${HAPROXY_FILE}
    HAPROXY_DIR=`echo ${HAPROXY_FILE} | sed -nr 's/^(.*[0-9]).([[:lower:]]).*/\1/p'`
    cd ${HAPROXY_DIR}
    make -j ${CPUS} ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 USE_CPU_AFFINITY=1 USE_LUA=1 LUA_INC=${SRC_DIR}/${LUA_DIR}/src/ LUA_LIB=${SRC_DIR}/${LUA_DIR}/src/ PREFIX=${HAPROXY_INSTALL_DIR}
    make install PREFIX=${HAPROXY_INSTALL_DIR}
    [ $? -eq 0 ] && $COLOR"Haproxy编译安装成功"$END ||  { $COLOR"Haproxy编译安装失败,退出!"$END;exit; }
    cat > /lib/systemd/system/haproxy.service <<-EOF
[Unit]
Description=HAProxy Load Balancer
After=syslog.target network.target
​
[Service]
ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -q
ExecStart=/usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/haproxy.pid
ExecReload=/bin/kill -USR2 $MAINPID
​
[Install]
WantedBy=multi-user.target
EOF
    [ -L /usr/sbin/haproxy ] || ln -s ../..${HAPROXY_INSTALL_DIR}/sbin/haproxy /usr/sbin/ &> /dev/null
    [ -d /etc/haproxy ] || mkdir /etc/haproxy &> /dev/null  
    [ -d /var/lib/haproxy/ ] || mkdir -p /var/lib/haproxy/ &> /dev/null
    cat > /etc/haproxy/haproxy.cfg <<-EOF
global
maxconn 100000
chroot ${HAPROXY_INSTALL_DIR}
stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin
uid 99
gid 99
daemon
pidfile /var/lib/haproxy/haproxy.pid
log 127.0.0.1 local3 info
​
defaults
option http-keep-alive
option forwardfor
maxconn 100000
mode http
timeout connect 300000ms
timeout client 300000ms
timeout server 300000ms
​
listen stats
    mode http
    bind 0.0.0.0:9999
    stats enable
    log global
    stats uri /haproxy-status
    stats auth ${STATS_AUTH_USER}:${STATS_AUTH_PASSWORD}
​
listen kubernetes-6443
    bind ${VIP}:6443
    mode tcp
    log global
    server ${MASTER01} ${MASTER01}:6443 check inter 3s fall 2 rise 5
    server ${MASTER02} ${MASTER02}:6443 check inter 3s fall 2 rise 5
    server ${MASTER03} ${MASTER03}:6443 check inter 3s fall 2 rise 5
​
listen harbor-80
    bind ${VIP}:80
    mode http
    log global
    balance source
    server ${HARBOR01} ${HARBOR01}:80 check inter 3s fall 2 rise 5
    server ${HARBOR02} ${HARBOR02}:80 check inter 3s fall 2 rise 5
EOF
    cat >> /etc/sysctl.conf <<-EOF
net.ipv4.ip_nonlocal_bind = 1
EOF
    sysctl -p &> /dev/null
    echo "PATH=${HAPROXY_INSTALL_DIR}/sbin:${PATH}" > /etc/profile.d/haproxy.sh
    systemctl daemon-reload
    systemctl enable --now haproxy &> /dev/null
    systemctl is-active haproxy &> /dev/null && ${COLOR}"Haproxy 服务启动成功!"${END} ||  { ${COLOR}"Haproxy 启动失败,退出!"${END} ; exit; }
    ${COLOR}"Haproxy安装完成"${END}
}
​
main(){
    os
    check_file
    install_haproxy
}
​
main
​
[root@k8s-ha01 ~]# bash install_haproxy.sh
​
[root@k8s-ha02 ~]# bash install_haproxy.sh

4.2.2 安装 Keepalived

安装 keepalived 实现 HAProxy的高可用

所有ha节点配置KeepAlived健康检查文件:

[root@k8s-ha02 ~]# cat check_haproxy.sh 
#!/bin/bash
err=0
for k in $(seq 1 3);do
    check_code=$(pgrep haproxy)
    if [[ $check_code == "" ]]; then
        err=$(expr $err + 1)
        sleep 1
        continue
    else
        err=0
        break
    fi
doneif [[ $err != "0" ]]; then
    echo "systemctl stop keepalived"
    /usr/bin/systemctl stop keepalived
    exit 1
else
    exit 0
fi

在ha01和ha02节点安装KeepAlived,配置不一样,注意区分 [root@k8s-master01 pki]# vim /etc/keepalived/keepalived.conf ,注意每个节点的网卡(interface参数)

在ha01节点上安装keepalived-master:

[root@k8s-ha01 ~]# cat install_keepalived_master.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2021-12-29
#FileName:      install_keepalived_master.sh
#URL:           raymond.blog.csdn.net
#Description:   install_keepalived for CentOS 7/8 & Ubuntu 18.04/20.04 & Rocky 8
#Copyright (C): 2021 All rights reserved
#*********************************************************************************************
SRC_DIR=/usr/local/src
COLOR="echo -e \033[01;31m"
END='\033[0m'
KEEPALIVED_URL=https://keepalived.org/software/
KEEPALIVED_FILE=keepalived-2.2.7.tar.gz
KEEPALIVED_INSTALL_DIR=/apps/keepalived
CPUS=`lscpu |awk '/^CPU(s)/{print $2}'`
NET_NAME=`ip addr |awk -F"[: ]" '/^2: e.*/{print $3}'`
STATE=MASTER
PRIORITY=100
VIP=172.31.3.188
​
​
os(){
    OS_ID=`sed -rn '/^NAME=/s@.*="([[:alpha:]]+).*"$@\1@p' /etc/os-release`
    OS_RELEASE_VERSION=`sed -rn '/^VERSION_ID=/s@.*="?([0-9]+).?.*"?@\1@p' /etc/os-release`
}
​
check_file (){
    cd  ${SRC_DIR}
    if [ ${OS_ID} == "CentOS" -o ${OS_ID} == "Rocky" ] &> /dev/null;then
        rpm -q wget &> /dev/null || yum -y install wget &> /dev/null
    fi
    if [ ! -e ${KEEPALIVED_FILE} ];then
        ${COLOR}"缺少${KEEPALIVED_FILE}文件,如果是离线包,请放到${SRC_DIR}目录下"${END}
        ${COLOR}'开始下载Keepalived源码包'${END}
        wget ${KEEPALIVED_URL}${KEEPALIVED_FILE} || { ${COLOR}"Keepalived源码包下载失败"${END}; exit; }
    elif [ ! -e check_haproxy.sh ];then
        ${COLOR}"缺少check_haproxy.sh文件,请把文件放到${SRC_DIR}目录下"${END}
        exit
    else
        ${COLOR}"相关文件已准备好"${END}
    fi
}
​
install_keepalived(){
    [ -d ${KEEPALIVED_INSTALL_DIR} ] && { ${COLOR}"Keepalived已存在,安装失败"${END};exit; }
    ${COLOR}"开始安装Keepalived"${END}
    ${COLOR}"开始安装Keepalived依赖包"${END}
    if [ ${OS_ID} == "Rocky" -a ${OS_RELEASE_VERSION} == 8 ];then
        URL=mirrors.sjtug.sjtu.edu.cn
        if [ ! `grep -R "[PowerTools]" /etc/yum.repos.d/` ];then
            cat > /etc/yum.repos.d/PowerTools.repo <<-EOF
[PowerTools]
name=PowerTools
baseurl=https://${URL}/rocky/$releasever/PowerTools/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
EOF
        fi
    fi
    if [ ${OS_ID} == "CentOS" -a ${OS_RELEASE_VERSION} == 8 ];then
        URL=mirrors.cloud.tencent.com
        if [ ! `grep -R "[PowerTools]" /etc/yum.repos.d/` ];then
            cat > /etc/yum.repos.d/PowerTools.repo <<-EOF
[PowerTools]
name=PowerTools
baseurl=https://${URL}/centos/$releasever/PowerTools/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
EOF
        fi
    fi
    if [[ ${OS_RELEASE_VERSION} == 8 ]] &> /dev/null;then
        yum -y install make gcc ipvsadm autoconf automake openssl-devel libnl3-devel iptables-devel ipset-devel file-devel net-snmp-devel glib2-devel pcre2-devel libnftnl-devel libmnl-devel systemd-devel &> /dev/null
    elif [[ ${OS_RELEASE_VERSION} == 7 ]] &> /dev/null;then
        yum -y install make gcc libnfnetlink-devel libnfnetlink ipvsadm libnl libnl-devel libnl3 libnl3-devel lm_sensors-libs net-snmp-agent-libs net-snmp-libs openssh-server openssh-clients openssl openssl-devel automake iproute &> /dev/null
    elif [[ ${OS_RELEASE_VERSION} == 20 ]] &> /dev/null;then
        apt update &> /dev/null;apt -y install make gcc ipvsadm build-essential pkg-config automake autoconf libipset-dev libnl-3-dev libnl-genl-3-dev libssl-dev libxtables-dev libip4tc-dev libip6tc-dev libipset-dev libmagic-dev libsnmp-dev libglib2.0-dev libpcre2-dev libnftnl-dev libmnl-dev libsystemd-dev
    else
        apt update &> /dev/null;apt -y install make gcc ipvsadm build-essential pkg-config automake autoconf iptables-dev libipset-dev libnl-3-dev libnl-genl-3-dev libssl-dev libxtables-dev libip4tc-dev libip6tc-dev libipset-dev libmagic-dev libsnmp-dev libglib2.0-dev libpcre2-dev libnftnl-dev libmnl-dev libsystemd-dev &> /dev/null
    fi
    tar xf ${KEEPALIVED_FILE}
    KEEPALIVED_DIR=`echo ${KEEPALIVED_FILE} | sed -nr 's/^(.*[0-9]).([[:lower:]]).*/\1/p'`
    cd ${KEEPALIVED_DIR}
    ./configure --prefix=${KEEPALIVED_INSTALL_DIR} --disable-fwmark
    make -j $CPUS && make install
    [ $? -eq 0 ] && ${COLOR}"Keepalived编译安装成功"${END} ||  { ${COLOR}"Keepalived编译安装失败,退出!"${END};exit; }
    [ -d /etc/keepalived ] || mkdir -p /etc/keepalived &> /dev/null
    cat > /etc/keepalived/keepalived.conf <<EOF
! Configuration File for keepalived
​
global_defs {
    router_id LVS_DEVEL
    script_user root
    enable_script_security
}
​
vrrp_script check_haoroxy {
    script "/etc/keepalived/check_haproxy.sh"
    interval 5
    weight -5
    fall 2  
    rise 1
}
​
vrrp_instance VI_1 {
    state ${STATE}
    interface ${NET_NAME}
    virtual_router_id 51
    priority ${PRIORITY}
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        ${VIP} dev ${NET_NAME} label ${NET_NAME}:1
    }
    track_script {
       check_haproxy
    }
}
EOF
    cp ./keepalived/keepalived.service /lib/systemd/system/
    cd  ${SRC_DIR}
    mv check_haproxy.sh /etc/keepalived/check_haproxy.sh
    chmod +x /etc/keepalived/check_haproxy.sh
    echo "PATH=${KEEPALIVED_INSTALL_DIR}/sbin:${PATH}" > /etc/profile.d/keepalived.sh
    systemctl daemon-reload
    systemctl enable --now keepalived &> /dev/null 
    systemctl is-active keepalived &> /dev/null && ${COLOR}"Keepalived 服务启动成功!"${END} ||  { ${COLOR}"Keepalived 启动失败,退出!"${END} ; exit; }
    ${COLOR}"Keepalived安装完成"${END}
}
​
main(){
    os
    check_file
    install_keepalived
}
​
main
​
[root@k8s-ha01 ~]# bash install_keepalived_master.sh
​
[root@k8s-ha01 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:04:fa:9d brd ff:ff:ff:ff:ff:ff
    inet 172.31.3.104/21 brd 172.31.7.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet 172.31.3.188/32 scope global eth0:1
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe04:fa9d/64 scope link 
       valid_lft forever preferred_lft forever

在ha02节点上安装keepalived-backup:

[root@k8s-ha02 ~]# cat install_keepalived_backup.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2021-12-29
#FileName:      install_keepalived_backup.sh
#URL:           raymond.blog.csdn.net
#Description:   install_keepalived for CentOS 7/8 & Ubuntu 18.04/20.04 & Rocky 8
#Copyright (C): 2021 All rights reserved
#*********************************************************************************************
SRC_DIR=/usr/local/src
COLOR="echo -e \033[01;31m"
END='\033[0m'
KEEPALIVED_URL=https://keepalived.org/software/
KEEPALIVED_FILE=keepalived-2.2.7.tar.gz
KEEPALIVED_INSTALL_DIR=/apps/keepalived
CPUS=`lscpu |awk '/^CPU(s)/{print $2}'`
NET_NAME=`ip addr |awk -F"[: ]" '/^2: e.*/{print $3}'`
STATE=BACKUP
PRIORITY=90
VIP=172.31.3.188
​
​
os(){
    OS_ID=`sed -rn '/^NAME=/s@.*="([[:alpha:]]+).*"$@\1@p' /etc/os-release`
    OS_RELEASE_VERSION=`sed -rn '/^VERSION_ID=/s@.*="?([0-9]+).?.*"?@\1@p' /etc/os-release`
}
​
check_file (){
    cd  ${SRC_DIR}
    if [ ${OS_ID} == "CentOS" -o ${OS_ID} == "Rocky" ] &> /dev/null;then
        rpm -q wget &> /dev/null || yum -y install wget &> /dev/null
    fi
    if [ ! -e ${KEEPALIVED_FILE} ];then
        ${COLOR}"缺少${KEEPALIVED_FILE}文件,如果是离线包,请放到${SRC_DIR}目录下"${END}
        ${COLOR}'开始下载Keepalived源码包'${END}
        wget ${KEEPALIVED_URL}${KEEPALIVED_FILE} || { ${COLOR}"Keepalived源码包下载失败"${END}; exit; }
    elif [ ! -e check_haproxy.sh ];then
        ${COLOR}"缺少check_haproxy.sh文件,请把文件放到${SRC_DIR}目录下"${END}
        exit
    else
        ${COLOR}"相关文件已准备好"${END}
    fi
}
​
install_keepalived(){
    [ -d ${KEEPALIVED_INSTALL_DIR} ] && { ${COLOR}"Keepalived已存在,安装失败"${END};exit; }
    ${COLOR}"开始安装Keepalived"${END}
    ${COLOR}"开始安装Keepalived依赖包"${END}
    if [ ${OS_ID} == "Rocky" -a ${OS_RELEASE_VERSION} == 8 ];then
        URL=mirrors.sjtug.sjtu.edu.cn
        if [ ! `grep -R "[PowerTools]" /etc/yum.repos.d/` ];then
            cat > /etc/yum.repos.d/PowerTools.repo <<-EOF
[PowerTools]
name=PowerTools
baseurl=https://${URL}/rocky/$releasever/PowerTools/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
EOF
        fi
    fi
    if [ ${OS_ID} == "CentOS" -a ${OS_RELEASE_VERSION} == 8 ];then
        URL=mirrors.cloud.tencent.com
        if [ ! `grep -R "[PowerTools]" /etc/yum.repos.d/` ];then
            cat > /etc/yum.repos.d/PowerTools.repo <<-EOF
[PowerTools]
name=PowerTools
baseurl=https://${URL}/centos/$releasever/PowerTools/$basearch/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
EOF
        fi
    fi
    if [[ ${OS_RELEASE_VERSION} == 8 ]] &> /dev/null;then
        yum -y install make gcc ipvsadm autoconf automake openssl-devel libnl3-devel iptables-devel ipset-devel file-devel net-snmp-devel glib2-devel pcre2-devel libnftnl-devel libmnl-devel systemd-devel &> /dev/null
    elif [[ ${OS_RELEASE_VERSION} == 7 ]] &> /dev/null;then
        yum -y install make gcc libnfnetlink-devel libnfnetlink ipvsadm libnl libnl-devel libnl3 libnl3-devel lm_sensors-libs net-snmp-agent-libs net-snmp-libs openssh-server openssh-clients openssl openssl-devel automake iproute &> /dev/null
    elif [[ ${OS_RELEASE_VERSION} == 20 ]] &> /dev/null;then
        apt update &> /dev/null;apt -y install make gcc ipvsadm build-essential pkg-config automake autoconf libipset-dev libnl-3-dev libnl-genl-3-dev libssl-dev libxtables-dev libip4tc-dev libip6tc-dev libipset-dev libmagic-dev libsnmp-dev libglib2.0-dev libpcre2-dev libnftnl-dev libmnl-dev libsystemd-dev
    else
        apt update &> /dev/null;apt -y install make gcc ipvsadm build-essential pkg-config automake autoconf iptables-dev libipset-dev libnl-3-dev libnl-genl-3-dev libssl-dev libxtables-dev libip4tc-dev libip6tc-dev libipset-dev libmagic-dev libsnmp-dev libglib2.0-dev libpcre2-dev libnftnl-dev libmnl-dev libsystemd-dev &> /dev/null
    fi
    tar xf ${KEEPALIVED_FILE}
    KEEPALIVED_DIR=`echo ${KEEPALIVED_FILE} | sed -nr 's/^(.*[0-9]).([[:lower:]]).*/\1/p'`
    cd ${KEEPALIVED_DIR}
    ./configure --prefix=${KEEPALIVED_INSTALL_DIR} --disable-fwmark
    make -j $CPUS && make install
    [ $? -eq 0 ] && ${COLOR}"Keepalived编译安装成功"${END} ||  { ${COLOR}"Keepalived编译安装失败,退出!"${END};exit; }
    [ -d /etc/keepalived ] || mkdir -p /etc/keepalived &> /dev/null
    cat > /etc/keepalived/keepalived.conf <<EOF
! Configuration File for keepalived
​
global_defs {
    router_id LVS_DEVEL
    script_user root
    enable_script_security
}
​
vrrp_script check_haoroxy {
    script "/etc/keepalived/check_haproxy.sh"
    interval 5
    weight -5
    fall 2  
    rise 1
}
​
vrrp_instance VI_1 {
    state ${STATE}
    interface ${NET_NAME}
    virtual_router_id 51
    priority ${PRIORITY}
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        ${VIP} dev ${NET_NAME} label ${NET_NAME}:1
    }
    track_script {
       check_haproxy
    }
}
EOF
    cp ./keepalived/keepalived.service /lib/systemd/system/
    cd  ${SRC_DIR}
    mv check_haproxy.sh /etc/keepalived/check_haproxy.sh
    chmod +x /etc/keepalived/check_haproxy.sh
    echo "PATH=${KEEPALIVED_INSTALL_DIR}/sbin:${PATH}" > /etc/profile.d/keepalived.sh
    systemctl daemon-reload
    systemctl enable --now keepalived &> /dev/null 
    systemctl is-active keepalived &> /dev/null && ${COLOR}"Keepalived 服务启动成功!"${END} ||  { ${COLOR}"Keepalived 启动失败,退出!"${END} ; exit; }
    ${COLOR}"Keepalived安装完成"${END}
}
​
main(){
    os
    check_file
    install_keepalived
}
​
main 
​
[root@k8s-ha02 ~]# bash install_keepalived_backup.sh

4.2.3 测试访问

172.31.3.188 kubeapi.raymonds.cc

浏览器访问验证,用户名密码: admin:123456

http://kubeapi.raymonds.cc:9999/haproxy-status

013.jpg

014.jpg

4.3 安装harbor

4.3.1 安装harbor

在harbor01和harbor02上安装harbor:

[root@k8s-harbor01 ~]# cat install_docker_compose_harbor.sh 
#!/bin/bash
#
#**************************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2021-12-16
#FileName:      install_docke_compose_harbor.sh
#URL:           raymond.blog.csdn.net
#Description:   install_docker_compose_harbor for CentOS 7/8 & Ubuntu 18.04/20.04 & Rocky 8
#Copyright (C): 2021 All rights reserved
#**************************************************************************************************
SRC_DIR=/usr/local/src
COLOR="echo -e \033[01;31m"
END='\033[0m'
​
DOCKER_VERSION=20.10.17
URL='mirrors.cloud.tencent.com'#docker-compose下载地址:https://github.com/docker/compose/releases/download/v2.10.2/docker-compose-linux-x86_64
DOCKER_COMPOSE_FILE=docker-compose-linux-x86_64
​
#harbor下载地址:https://github.com/goharbor/harbor/releases/download/v2.6.0/harbor-offline-installer-v2.6.0.tgz
HARBOR_FILE=harbor-offline-installer-v
HARBOR_VERSION=2.6.0
TAR=.tgz
HARBOR_INSTALL_DIR=/apps
HARBOR_DOMAIN=harbor.raymonds.cc
NET_NAME=`ip addr |awk -F"[: ]" '/^2: e.*/{print $3}'`
IP=`ip addr show ${NET_NAME}| awk -F" +|/" '/global/{print $3}'`
HARBOR_ADMIN_PASSWORD=123456
​
os(){
    OS_ID=`sed -rn '/^NAME=/s@.*="([[:alpha:]]+).*"$@\1@p' /etc/os-release`
    OS_RELEASE_VERSION=`sed -rn '/^VERSION_ID=/s@.*="?([0-9]+).?.*"?@\1@p' /etc/os-release`
}
​
check_file (){
    cd ${SRC_DIR}
    if [ ! -e ${DOCKER_COMPOSE_FILE} ];then
        ${COLOR}"缺少${DOCKER_COMPOSE_FILE}文件,请把文件放到${SRC_DIR}目录下"${END}
        exit
    elif [ ! -e ${HARBOR_FILE}${HARBOR_VERSION}${TAR} ];then
        ${COLOR}"缺少${HARBOR_FILE}${HARBOR_VERSION}${TAR}文件,请把文件放到${SRC_DIR}目录下"${END}
        exit
    else
        ${COLOR}"相关文件已准备好"${END}
    fi
}
​
ubuntu_install_docker(){
    ${COLOR}"开始安装DOCKER依赖包"${END}
    apt update &> /dev/null
    apt -y install apt-transport-https ca-certificates curl software-properties-common &> /dev/null
    curl -fsSL https://${URL}/docker-ce/linux/ubuntu/gpg | sudo apt-key add - &> /dev/null
    add-apt-repository  "deb [arch=amd64] https://${URL}/docker-ce/linux/ubuntu  $(lsb_release -cs) stable" &> /dev/null 
    apt update &> /dev/null
​
    ${COLOR}"Docker有以下版本"${END}
    apt-cache madison docker-ce
    ${COLOR}"10秒后即将安装:Docker-"${DOCKER_VERSION}"版本......"${END}
    ${COLOR}"如果想安装其它Docker版本,请按Ctrl+c键退出,修改版本再执行"${END}
    sleep 10
​
    ${COLOR}"开始安装DOCKER"${END}
    apt -y install docker-ce=5:${DOCKER_VERSION}~3-0~ubuntu-$(lsb_release -cs) docker-ce-cli=5:${DOCKER_VERSION}~3-0~ubuntu-$(lsb_release -cs) &> /dev/null || { ${COLOR}"apt源失败,请检查apt配置"${END};exit; }
}
​
centos_install_docker(){
    ${COLOR}"开始安装DOCKER依赖包"${END}
    yum -y install yum-utils &> /dev/null
    yum-config-manager --add-repo https://${URL}/docker-ce/linux/centos/docker-ce.repo &> /dev/null
    yum clean all &> /dev/null
    yum makecache &> /dev/null
​
    ${COLOR}"Docker有以下版本"${END}
    yum list docker-ce.x86_64 --showduplicates
    ${COLOR}"10秒后即将安装:Docker-"${DOCKER_VERSION}"版本......"${END}
    ${COLOR}"如果想安装其它Docker版本,请按Ctrl+c键退出,修改版本再执行"${END}
    sleep 10
​
    ${COLOR}"开始安装DOCKER"${END}
    yum -y install docker-ce-${DOCKER_VERSION} docker-ce-cli-${DOCKER_VERSION} &> /dev/null || { ${COLOR}"yum源失败,请检查yum配置"${END};exit; }
}
​
mirror_accelerator(){
    mkdir -p /etc/docker
    cat > /etc/docker/daemon.json <<-EOF
{
    "registry-mirrors": [
        "https://registry.docker-cn.com",
        "http://hub-mirror.c.163.com",
        "https://docker.mirrors.ustc.edu.cn"
    ],
    "insecure-registries": ["${HARBOR_DOMAIN}"],
    "exec-opts": ["native.cgroupdriver=systemd"],
    "max-concurrent-downloads": 10,
    "max-concurrent-uploads": 5,
    "log-opts": {
        "max-size": "300m",
        "max-file": "2"  
    },
    "live-restore": true
}
EOF
    systemctl daemon-reload
    systemctl enable --now docker
    systemctl is-active docker &> /dev/null && ${COLOR}"Docker 服务启动成功"${END} || { ${COLOR}"Docker 启动失败"${END};exit; }
    docker version &&  ${COLOR}"Docker 安装成功"${END} || ${COLOR}"Docker 安装失败"${END}
}
​
set_alias(){
    echo 'alias rmi="docker images -qa|xargs docker rmi -f"' >> ~/.bashrc
    echo 'alias rmc="docker ps -qa|xargs docker rm -f"' >> ~/.bashrc
}
​
install_docker_compose(){
    ${COLOR}"开始安装 Docker compose....."${END}
    sleep 1
    mv ${SRC_DIR}/${DOCKER_COMPOSE_FILE} /usr/bin/docker-compose
    chmod +x /usr/bin/docker-compose
    docker-compose --version &&  ${COLOR}"Docker Compose 安装完成"${END} || ${COLOR}"Docker compose 安装失败"${END}
}
​
install_harbor(){
    ${COLOR}"开始安装 Harbor....."${END}
    sleep 1
    [ -d ${HARBOR_INSTALL_DIR} ] || mkdir ${HARBOR_INSTALL_DIR}
    tar xf ${SRC_DIR}/${HARBOR_FILE}${HARBOR_VERSION}${TAR} -C ${HARBOR_INSTALL_DIR}/
    mv ${HARBOR_INSTALL_DIR}/harbor/harbor.yml.tmpl ${HARBOR_INSTALL_DIR}/harbor/harbor.yml
    sed -ri.bak -e 's/^(hostname:) .*/\1 '${IP}'/' -e 's/^(harbor_admin_password:) .*/\1 '${HARBOR_ADMIN_PASSWORD}'/' -e 's/^(https:)/#\1/' -e 's/  (port: 443)/#  \1/' -e 's@  (certificate: .*)@#  \1@' -e 's@  (private_key: .*)@#  \1@' ${HARBOR_INSTALL_DIR}/harbor/harbor.yml
    if [ ${OS_ID} == "CentOS" -o ${OS_ID} == "Rocky" ] &> /dev/null;then
        if [ ${OS_RELEASE_VERSION} == "8" ];then
            yum -y install python3 &> /dev/null || { ${COLOR}"安装软件包失败,请检查网络配置"${END}; exit; }
        else
            yum -y install python &> /dev/null || { ${COLOR}"安装软件包失败,请检查网络配置"${END}; exit; }
        fi
    else
        apt -y install python3 &> /dev/null || { ${COLOR}"安装软件包失败,请检查网络配置"${END}; exit; }
    fi
    ${HARBOR_INSTALL_DIR}/harbor/install.sh && ${COLOR}"Harbor 安装完成"${END} ||  ${COLOR}"Harbor 安装失败"${END}
    cat > /lib/systemd/system/harbor.service <<-EOF
[Unit]
Description=Harbor
After=docker.service systemd-networkd.service systemd-resolved.service
Requires=docker.service
Documentation=http://github.com/vmware/harbor
​
[Service]
Type=simple
Restart=on-failure
RestartSec=5
ExecStart=/usr/bin/docker-compose -f /apps/harbor/docker-compose.yml up
ExecStop=/usr/bin/docker-compose -f /apps/harbor/docker-compose.yml down
​
[Install]
WantedBy=multi-user.target
EOF
​
    systemctl daemon-reload 
    systemctl enable harbor &>/dev/null && ${COLOR}"Harbor已配置为开机自动启动"${END}
}
​
set_swap_limit(){
    if [ ${OS_ID} == "Ubuntu" ];then
        ${COLOR}'设置Docker的"WARNING: No swap limit support"警告'${END}
        sed -ri '/^GRUB_CMDLINE_LINUX=/s@"$@ swapaccount=1"@' /etc/default/grub
        update-grub &> /dev/null
        ${COLOR}"10秒后,机器会自动重启"${END}
        sleep 10
        reboot
    fi
}
​
main(){
    os
    check_file
    if [ ${OS_ID} == "CentOS" -o ${OS_ID} == "Rocky" ] &> /dev/null;then
        rpm -q docker-ce &> /dev/null && ${COLOR}"Docker已安装"${END} || centos_install_docker
    else
        dpkg -s docker-ce &>/dev/null && ${COLOR}"Docker已安装"${END} || ubuntu_install_docker
    fi
    [ -f /etc/docker/daemon.json ] &>/dev/null && ${COLOR}"Docker镜像加速器已设置"${END} || mirror_accelerator
    grep -Eqoi "(.*rmi=|.*rmc=)" ~/.bashrc && ${COLOR}"Docker别名已设置"${END} || set_alias
    docker-compose --version &> /dev/null && ${COLOR}"Docker Compose已安装"${END} || install_docker_compose
    systemctl is-active harbor &> /dev/null && ${COLOR}"Harbor已安装"${END} || install_harbor
    grep -q "swapaccount=1" /etc/default/grub && ${COLOR}'"WARNING: No swap limit support"警告,已设置'${END} || set_swap_limit
}
​
main
​
[root@k8s-harbor01 ~]# bash install_docker_compose_harbor.sh
​
[root@k8s-harbor02 ~]# bash install_docker_compose_harbor.sh

4.3.2 创建harbor仓库

在harbor01新建项目google_containers

http://172.31.3.106/

用户名:admin 密码:123456

015.jpg

![016.jpg](p1-juejin.byteimg.com/tos-cn-i-k3…

在harbor02新建项目google_containers

http://172.31.3.107/

017.jpg

018.jpg

在harbor02上新建目标

019.jpg

在harbor02上新建规则

020.jpg

在harbor01上新建目标

021.jpg

在harbor01上新建规则

022.jpg