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

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

Kubernetes的设计初衷是支持可插拔架构,从而利于扩展kubernetes的功能。
在这里插入图片描述
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(使用接近用户层)目前,市面上常用的容器引擎有很多,主要有下图的那几种。

在这里插入图片描述
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仍然是最主流的容器引擎技术。

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

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

在这里插入图片描述
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/

在这里插入图片描述
移除 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 的变化

在这里插入图片描述
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组合在一起。

在这里插入图片描述
在这里插入图片描述

1.3 Kubernetes v1.25 新变化

在这里插入图片描述
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 集群创建方案

在这里插入图片描述
上图描述了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 中移除

    项目站点: https://github.com/Mirantis/cri-dockerd

  • 方式3: CRI-O

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

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

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

在这里插入图片描述
表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
  • 在所有节点安装和配置 cri-dockerd
  • 在所有Master和Node节点安装kubeadm 、kubelet、kubectl
  • 在第一个 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
EOF

for 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 设置时区

ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo 'Asia/Shanghai' >/etc/timezone

#Ubuntu还要设置下面内容
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内核是3.10,kubernetes需要内核是4.18或以上版本,CentOS7必须升级内核才可以安装kubernetes,其它系统根据自己的需求去升级

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

master和node节点安装内核

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

master和node节点更改内核启动顺序

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

4.1.14 安装ipvs相关工具并优化内核

master和node安装ipvsadm:

#CentOS
yum -y install ipvsadm ipset sysstat conntrack libseccomp

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

master和node配置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_ipv4

cat >> /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集群中必须的内核参数,master和node节点配置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=2310720

net.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 #当系统有容器运行时,需要设置为1

vm.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,默认262144

net.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-ha01 ~]# 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
done

if [[ $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

在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述
在harbor02新建项目google_containers

http://172.31.3.107/
在这里插入图片描述
在这里插入图片描述
在harbor02上新建目标
在这里插入图片描述

在harbor02上新建规则
在这里插入图片描述

在harbor01上新建目标
在这里插入图片描述

在harbor01上新建规则
在这里插入图片描述

4.4 安装 Docker

master和node安装docker-ce:

[root@k8s-master01 ~]# cat install_docker.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2021-12-07
#FileName:      install_docker.sh
#URL:           raymond.blog.csdn.net
#Description:   install_docker 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'
DOCKER_VERSION=20.10.17
URL='mirrors.cloud.tencent.com'
HARBOR_DOMAIN=harbor.raymonds.cc

os(){
    OS_ID=`sed -rn '/^NAME=/s@.*="([[:alpha:]]+).*"$@\1@p' /etc/os-release`
}

ubuntu_install_docker(){
    dpkg -s docker-ce &>/dev/null && ${COLOR}"Docker已安装,退出"${END} && exit
    ${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(){
    rpm -q docker-ce &> /dev/null && ${COLOR}"Docker已安装,退出"${END} && exit
	
	${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
    sed -i 's+download.docker.com+'''${URL}'''/docker-ce+' /etc/yum.repos.d/docker-ce.repo
    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
}

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
    if [ ${OS_ID} == "CentOS" -o ${OS_ID} == "Rocky" ] &> /dev/null;then
        centos_install_docker
    else
        ubuntu_install_docker
    fi
    mirror_accelerator
    set_alias
    set_swap_limit
}

main

[root@k8s-master01 ~]# bash install_docker.sh 
[root@k8s-master02 ~]# bash install_docker.sh
[root@k8s-master03 ~]# bash install_docker.sh

[root@k8s-node01 ~]# bash install_docker.sh
[root@k8s-node02 ~]# bash install_docker.sh
[root@k8s-node03 ~]# bash install_docker.sh

4.5 安装 cri-dockerd

Kubernetes自v1.24移除了对docker-shim的支持,而Docker Engine默认又不支持CRI规范,因而二者将无法直接完成整合。为此,Mirantis和Docker联合创建了cri-dockerd项目,用于为Docker Engine提供一个能够支持到CRI规范的垫片,从而能够让Kubernetes基于CRI控制Docker 。

项目地址:https://github.com/Mirantis/cri-dockerd

cri-dockerd项目提供了预制的二制格式的程序包,用户按需下载相应的系统和对应平台的版本即可完成安装,这里以Ubuntu 20.04 64bits系统环境,以及cri-dockerd目前最新的程序版本v0.2.5为例。

4.5.1 镜像包方式安装cri-dockerd

#CentOS
[root@k8s-master01 ~]# wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.2.5/cri-dockerd-0.2.5-3.el8.x86_64.rpm

[root@k8s-master01 ~]# rpm -ivh cri-dockerd-0.2.5-3.el8.x86_64.rpm

[root@k8s-master01 ~]# for i in {102..103};do scp cri-dockerd-0.2.5-3.el8.x86_64.rpm 172.31.3.$i: ; ssh 172.31.3.$i "rpm -ivh cri-dockerd-0.2.5-3.el8.x86_64.rpm";done

[root@k8s-master01 ~]# for i in {108..110};do scp cri-dockerd-0.2.5-3.el8.x86_64.rpm 172.31.3.$i: ; ssh 172.31.3.$i "rpm -ivh cri-dockerd-0.2.5-3.el8.x86_64.rpm";done

#Ubuntu
[root@k8s-master01 ~]# curl -LO https://github.com/Mirantis/cri-dockerd/releases/download/v0.2.5/cri-dockerd_0.2.5.3-0.ubuntu-focal_amd64.deb

[root@k8s-master01 ~]# dpkg -i cri-dockerd_0.2.5.3-0.ubuntu-focal_amd64.deb

[root@k8s-master01 ~]# for i in {102..103};do scp cri-dockerd_0.2.5.3-0.ubuntu-focal_amd64.deb 172.31.3.$i: ; ssh 172.31.3.$i "dpkg -i cri-dockerd_0.2.5.3-0.ubuntu-focal_amd64.deb";done

[root@k8s-master01 ~]# for i in {108..110};do scp cri-dockerd_0.2.5.3-0.ubuntu-focal_amd64.deb 172.31.3.$i: ; ssh 172.31.3.$i "dpkg -i cri-dockerd_0.2.5.3-0.ubuntu-focal_amd64.deb";done

配置 cri-dockerd

众所周知的原因,从国内 cri-dockerd 服务无法下载 k8s.gcr.io上面相关镜像,导致无法启动,所以需要修改cri-dockerd 使用国内镜像源

[root@k8s-master01 ~]# sed -ri '/ExecStart.*/s@(ExecStart.*)@\1 --pod-infra-container-image harbor.raymonds.cc/google_containers/pause:3.8@g' /lib/systemd/system/cri-docker.service

[root@k8s-master01 ~]# sed -nr '/ExecStart.*/p' /lib/systemd/system/cri-docker.service
ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --pod-infra-container-image harbor.raymonds.cc/google_containers/pause:3.8

#注意:如果没有harbor执行下面命令
[root@k8s-master01 ~]# sed -ri '/ExecStart.*/s@(ExecStart.*)@\1 --pod-infra-container-image registry.aliyuncs.com/google_containers/pause:3.8@g' /lib/systemd/system/cri-docker.service

[root@k8s-master01 ~]# systemctl daemon-reload && systemctl enable --now cri-docker

[root@k8s-master01 ~]# for i in {102..103};do scp /lib/systemd/system/cri-docker.service 172.31.3.$i:/lib/systemd/system/cri-docker.service; ssh 172.31.3.$i "systemctl daemon-reload && systemctl enable --now cri-docker.service";done

[root@k8s-master01 ~]# for i in {108..110};do scp /lib/systemd/system/cri-docker.service 172.31.3.$i:/lib/systemd/system/cri-docker.service; ssh 172.31.3.$i "systemctl daemon-reload && systemctl enable --now cri-docker.service";done

如果不配置,会出现下面日志提示

Aug 21 01:35:17 ubuntu2004 kubelet[6791]: E0821 01:35:17.999712    6791 remote_runtime.go:212] "RunPodSandbox from runtime service failed" err="rpc error: code = Unknown desc = failed pulling image \"k8s.gcr.io/pause:3.6\": Error response from daemon: Get \"https://k8s.gcr.io/v2/\": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)"

4.5.2 二进制包方式安装cri-dockerd

[root@k8s-master01 ~]# wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.2.5/cri-dockerd-0.2.5.amd64.tgz

[root@k8s-master01 ~]# tar xf cri-dockerd-0.2.5.amd64.tgz

[root@k8s-master01 ~]# mv cri-dockerd/* /usr/bin/
[root@k8s-master01 ~]# ll /usr/bin/cri-dockerd 
-rwxr-xr-x 1 root root 52351080 Sep  3 07:00 /usr/bin/cri-dockerd*

[root@k8s-master01 ~]# cat > /usr/lib/systemd/system/cri-docker.service <<EOF
[Unit]
Description=CRI Interface for Docker Application Container Engine
Documentation=https://docs.mirantis.com
After=network-online.target firewalld.service docker.service
Wants=network-online.target
Requires=cri-docker.socket

[Service]
Type=notify
ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd://
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229.
# Both the old, and new location are accepted by systemd 229 and up, so using the old location
# to make them work for either version of systemd.
StartLimitBurst=3

# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230.
# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make
# this option work for either version of systemd.
StartLimitInterval=60s

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not support it.
# Only systemd 226 and above support this option.
TasksMax=infinity
Delegate=yes
KillMode=process

[Install]
WantedBy=multi-user.target
EOF

[root@k8s-master01 ~]# cat > /usr/lib/systemd/system/cri-docker.socket <<EOF
[Unit]
Description=CRI Docker Socket for the API
PartOf=cri-docker.service

[Socket]
ListenStream=%t/cri-dockerd.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker

[Install]
WantedBy=sockets.target
EOF

[root@k8s-master01 ~]# sed -ri '/ExecStart.*/s@(ExecStart.*)@\1 --pod-infra-container-image harbor.raymonds.cc/google_containers/pause:3.8@g' /lib/systemd/system/cri-docker.service

#注意:如果没有harbor执行下面命令
[root@k8s-master01 ~]# sed -ri '/ExecStart.*/s@(ExecStart.*)@\1 --pod-infra-container-image registry.aliyuncs.com/google_containers/pause:3.8@g' /lib/systemd/system/cri-docker.service

[root@k8s-master01 ~]# systemctl daemon-reload && systemctl enable --now cri-docker

master02、master03和node安装:

[root@k8s-master02 ~]# cat install_cri_dockerd_binary.sh
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2022-09-03
#FileName:      install_cri_dockerd_binary.sh
#URL:           raymond.blog.csdn.net
#Description:   install_docker_binary 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'
#cri-dockerd下载地址:https://github.com/Mirantis/cri-dockerd/releases/download/v0.2.5/cri-dockerd-0.2.5.amd64.tgz
CRI_DOCKER_FILE=cri-dockerd-0.2.5.amd64.tgz
HARBOR_DOMAIN=harbor.raymonds.cc

check_file (){
    cd ${SRC_DIR}
    if [ ! -e ${CRI_DOCKER_FILE} ];then
        ${COLOR}"缺少${CRI_DOCKER_FILE}文件,如果是离线包,请把文件放到${SRC_DIR}目录下"${END}
        exit
    else
        ${COLOR}"相关文件已准备好"${END}
    fi
}

install(){ 
    [ -f /usr/bin/cri-dockerd ] && { ${COLOR}"cri-dockerd已存在,安装失败"${END};exit; }
    ${COLOR}"开始安装cri-dockerd..."${END}
    tar xf ${CRI_DOCKER_FILE} 
    mv cri-dockerd/* /usr/bin/
    cat > /usr/lib/systemd/system/cri-docker.service <<-EOF
[Unit]
Description=CRI Interface for Docker Application Container Engine
Documentation=https://docs.mirantis.com
After=network-online.target firewalld.service docker.service
Wants=network-online.target
Requires=cri-docker.socket

[Service]
Type=notify
ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --pod-infra-container-image ${HARBOR_DOMAIN}/google_containers/pause:3.8
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229.
# Both the old, and new location are accepted by systemd 229 and up, so using the old location
# to make them work for either version of systemd.
StartLimitBurst=3

# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230.
# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make
# this option work for either version of systemd.
StartLimitInterval=60s

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not support it.
# Only systemd 226 and above support this option.
TasksMax=infinity
Delegate=yes
KillMode=process

[Install]
WantedBy=multi-user.target
EOF
    cat > /usr/lib/systemd/system/cri-docker.socket <<-EOF
[Unit]
Description=CRI Docker Socket for the API
PartOf=cri-docker.service

[Socket]
ListenStream=%t/cri-dockerd.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker

[Install]
WantedBy=sockets.target
EOF
    systemctl daemon-reload
    systemctl enable --now cri-docker &> /dev/null
    systemctl is-active cri-docker &> /dev/null && ${COLOR}"cri-docker 服务启动成功"${END} || { ${COLOR}"cri-docker 启动失败"${END};exit; }
    cri-dockerd --version && ${COLOR}"cri-dockerd 安装成功"${END} || ${COLOR}"cri-dockerd 安装失败"${END}
}

main(){
    check_file
    install
}

main

[root@k8s-master02 ~]# bash install_cri_dockerd_binary.sh
[root@k8s-master03 ~]# bash install_cri_dockerd_binary.sh

[root@k8s-node01 ~]# bash install_cri_dockerd_binary.sh
[root@k8s-node02 ~]# bash install_cri_dockerd_binary.sh
[root@k8s-node03 ~]# bash install_cri_dockerd_binary.sh

4.6 安装kubeadm等组件

CentOS 配置k8s镜像仓库和安装k8s组件:

[root@k8s-master01 ~]# cat > /etc/yum.repos.d/kubernetes.repo <<-EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

[root@k8s-master01 ~]# yum list kubeadm.x86_64 --showduplicates | sort -r|grep 1.25
kubeadm.x86_64                       1.25.0-0                        kubernetes 

[root@k8s-master01 ~]# yum -y install kubeadm-1.25.0 kubelet-1.25.0 kubectl-1.25.0

Ubuntu:

root@k8s-master01:~# apt update
root@k8s-master01:~# apt install -y apt-transport-https
root@k8s-master01:~# curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
OK

root@k8s-master01:~# echo "deb https://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list

root@k8s-master01:~# apt update

root@k8s-master01:~# apt-cache madison kubeadm | grep 1.25
   kubeadm |  1.25.0-00 | https://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial/main amd64 Packages

root@k8s-master01:~# apt -y install kubelet=1.25.0-00 kubeadm=1.25.0-00 kubectl=1.25.0-00

设置Kubelet开机自启动:

[root@k8s-master01 ~]# systemctl daemon-reload
[root@k8s-master01 ~]# systemctl enable --now kubelet
Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /usr/lib/systemd/system/kubelet.service.

在master02和master03执行脚本安装:

[root@k8s-master02 ~]# cat install_kubeadm_for_master.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2022-01-11
#FileName:      install_kubeadm_for_master.sh
#URL:           raymond.blog.csdn.net
#Description:   The test script
#Copyright (C): 2022 All rights reserved
#*********************************************************************************************
COLOR="echo -e \\033[01;31m"
END='\033[0m'

KUBEADM_MIRRORS=mirrors.aliyun.com
KUBEADM_VERSION=1.25.0
HARBOR_DOMAIN=harbor.raymonds.cc

os(){
    OS_ID=`sed -rn '/^NAME=/s@.*="([[:alpha:]]+).*"$@\1@p' /etc/os-release`
}

install_ubuntu_kubeadm(){
    ${COLOR}"开始安装Kubeadm依赖包"${END}
    apt update &> /dev/null && apt install -y apt-transport-https &> /dev/null
    curl -fsSL https://${KUBEADM_MIRRORS}/kubernetes/apt/doc/apt-key.gpg | apt-key add - &> /dev/null
    echo "deb https://"${KUBEADM_MIRRORS}"/kubernetes/apt kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list
    apt update &> /dev/null

    ${COLOR}"Kubeadm有以下版本"${END}
    apt-cache madison kubeadm
    ${COLOR}"10秒后即将安装:Kubeadm-"${KUBEADM_VERSION}"版本......"${END}
    ${COLOR}"如果想安装其它Kubeadm版本,请按Ctrl+c键退出,修改版本再执行"${END}
    sleep 10

    ${COLOR}"开始安装Kubeadm"${END}
    apt -y install kubelet=${KUBEADM_VERSION}-00 kubeadm=${KUBEADM_VERSION}-00 kubectl=${KUBEADM_VERSION}-00 &> /dev/null
    ${COLOR}"Kubeadm安装完成"${END}
}

install_centos_kubeadm(){
    cat > /etc/yum.repos.d/kubernetes.repo <<-EOF
[kubernetes]
name=Kubernetes
baseurl=https://${KUBEADM_MIRRORS}/kubernetes/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=0
gpgkey=https://${KUBEADM_MIRRORS}/kubernetes/yum/doc/yum-key.gpg https://${KUBEADM_MIRRORS}/kubernetes/yum/doc/rpm-package-key.gpg
EOF
    ${COLOR}"Kubeadm有以下版本"${END}
    yum list kubeadm.x86_64 --showduplicates | sort -r
    ${COLOR}"10秒后即将安装:Kubeadm-"${KUBEADM_VERSION}"版本......"${END}
    ${COLOR}"如果想安装其它Kubeadm版本,请按Ctrl+c键退出,修改版本再执行"${END}
    sleep 10

    ${COLOR}"开始安装Kubeadm"${END}
    yum -y install kubelet-${KUBEADM_VERSION} kubeadm-${KUBEADM_VERSION} kubectl-${KUBEADM_VERSION} &> /dev/null
    ${COLOR}"Kubeadm安装完成"${END}
}

start_service(){
    systemctl daemon-reload
    systemctl enable --now kubelet
    systemctl is-active kubelet &> /dev/null && ${COLOR}"Kubelet 服务启动成功"${END} || { ${COLOR}"Kubelet 启动失败"${END};exit; }
    kubelet --version &&  ${COLOR}"Kubelet 安装成功"${END} || ${COLOR}"Kubelet 安装失败"${END}
}

main(){
    os
    if [ ${OS_ID} == "CentOS" -o ${OS_ID} == "Rocky" ] &> /dev/null;then
        install_centos_kubeadm
    else
        install_ubuntu_kubeadm
    fi
    start_service
}

main

[root@k8s-master02 ~]# bash install_kubeadm_for_master.sh 
[root@k8s-master03 ~]# bash install_kubeadm_for_master.sh 

node上安装kubeadm:

[root@k8s-node01 ~]# cat install_kubeadm_for_node.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2022-01-11
#FileName:      install_kubeadm_for_node.sh
#URL:           raymond.blog.csdn.net
#Description:   The test script
#Copyright (C): 2022 All rights reserved
#*********************************************************************************************
COLOR="echo -e \\033[01;31m"
END='\033[0m'

KUBEADM_MIRRORS=mirrors.aliyun.com
KUBEADM_VERSION=1.25.0
HARBOR_DOMAIN=harbor.raymonds.cc

os(){
    OS_ID=`sed -rn '/^NAME=/s@.*="([[:alpha:]]+).*"$@\1@p' /etc/os-release`
}

install_ubuntu_kubeadm(){
    ${COLOR}"开始安装Kubeadm依赖包"${END}
    apt update &> /dev/null && apt install -y apt-transport-https &> /dev/null
    curl -fsSL https://${KUBEADM_MIRRORS}/kubernetes/apt/doc/apt-key.gpg | apt-key add - &> /dev/null
    echo "deb https://"${KUBEADM_MIRRORS}"/kubernetes/apt kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list
    apt update &> /dev/null

    ${COLOR}"Kubeadm有以下版本"${END}
    apt-cache madison kubeadm
    ${COLOR}"10秒后即将安装:Kubeadm-"${KUBEADM_VERSION}"版本......"${END}
    ${COLOR}"如果想安装其它Kubeadm版本,请按Ctrl+c键退出,修改版本再执行"${END}
    sleep 10

    ${COLOR}"开始安装Kubeadm"${END}
    apt -y install kubelet=${KUBEADM_VERSION}-00 kubeadm=${KUBEADM_VERSION}-00 &> /dev/null
    ${COLOR}"Kubeadm安装完成"${END}
}

install_centos_kubeadm(){
    cat > /etc/yum.repos.d/kubernetes.repo <<-EOF
[kubernetes]
name=Kubernetes
baseurl=https://${KUBEADM_MIRRORS}/kubernetes/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=0
gpgkey=https://${KUBEADM_MIRRORS}/kubernetes/yum/doc/yum-key.gpg https://${KUBEADM_MIRRORS}/kubernetes/yum/doc/rpm-package-key.gpg
EOF
    ${COLOR}"Kubeadm有以下版本"${END}
    yum list kubeadm.x86_64 --showduplicates | sort -r
    ${COLOR}"10秒后即将安装:Kubeadm-"${KUBEADM_VERSION}"版本......"${END}
    ${COLOR}"如果想安装其它Kubeadm版本,请按Ctrl+c键退出,修改版本再执行"${END}
    sleep 10

    ${COLOR}"开始安装Kubeadm"${END}
    yum -y install kubelet-${KUBEADM_VERSION} kubeadm-${KUBEADM_VERSION} &> /dev/null
    ${COLOR}"Kubeadm安装完成"${END}
}

start_service(){
    systemctl daemon-reload
    systemctl enable --now kubelet
    systemctl is-active kubelet &> /dev/null && ${COLOR}"Kubelet 服务启动成功"${END} || { ${COLOR}"Kubelet 启动失败"${END};exit; }
    kubelet --version &&  ${COLOR}"Kubelet 安装成功"${END} || ${COLOR}"Kubelet 安装失败"${END}
}

main(){
    os
    if [ ${OS_ID} == "CentOS" -o ${OS_ID} == "Rocky" ] &> /dev/null;then
        install_centos_kubeadm
    else
        install_ubuntu_kubeadm
    fi
    start_service
}

main

[root@k8s-node01 ~]# bash install_kubeadm_for_node.sh
[root@k8s-node02 ~]# bash install_kubeadm_for_node.sh 
[root@k8s-node03 ~]# bash install_kubeadm_for_node.sh

4.7 提前准备 Kubernetes 初始化所需镜像

查看镜像版本:

[root@k8s-master01 ~]# kubeadm config images list --kubernetes-version v1.25.0
registry.k8s.io/kube-apiserver:v1.25.0
registry.k8s.io/kube-controller-manager:v1.25.0
registry.k8s.io/kube-scheduler:v1.25.0
registry.k8s.io/kube-proxy:v1.25.0
registry.k8s.io/pause:3.8
registry.k8s.io/etcd:3.5.4-0
registry.k8s.io/coredns/coredns:v1.9.3

#查看国内镜像
[root@k8s-master01 ~]# kubeadm  config images list  --image-repository registry.aliyuncs.com/google_containers
registry.aliyuncs.com/google_containers/kube-apiserver:v1.25.0
registry.aliyuncs.com/google_containers/kube-controller-manager:v1.25.0
registry.aliyuncs.com/google_containers/kube-scheduler:v1.25.0
registry.aliyuncs.com/google_containers/kube-proxy:v1.25.0
registry.aliyuncs.com/google_containers/pause:3.8
registry.aliyuncs.com/google_containers/etcd:3.5.4-0
registry.aliyuncs.com/google_containers/coredns:v1.9.3

下载镜像并上传至harbor:

#注意:如果没有harbor不用执行下面命令
[root@k8s-master01 ~]# docker login harbor.raymonds.cc
Username: admin
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

#注意:如果没有harbor不用执行下面脚本
[root@k8s-master01 ~]# cat download_kubeadm_images_1.25.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2022-01-11
#FileName:      download_kubeadm_images.sh
#URL:           raymond.blog.csdn.net
#Description:   The test script
#Copyright (C): 2022 All rights reserved
#*********************************************************************************************
COLOR="echo -e \\033[01;31m"
END='\033[0m'

KUBEADM_VERSION=1.25.0
images=$(kubeadm config images list --kubernetes-version=v${KUBEADM_VERSION} | awk -F "/"  '{print $NF}')
HARBOR_DOMAIN=harbor.raymonds.cc

images_download(){
    ${COLOR}"开始下载Kubeadm镜像"${END}
    for i in ${images};do 
        docker pull registry.aliyuncs.com/google_containers/$i
        docker tag registry.aliyuncs.com/google_containers/$i ${HARBOR_DOMAIN}/google_containers/$i
        docker rmi registry.aliyuncs.com/google_containers/$i
        docker push ${HARBOR_DOMAIN}/google_containers/$i
    done
    ${COLOR}"Kubeadm镜像下载完成"${END}
}

images_download

[root@k8s-master01 ~]# bash download_kubeadm_images_1.25.sh 

[root@k8s-master01 ~]# docker images
REPOSITORY                                                     TAG       IMAGE ID       CREATED        SIZE
harbor.raymonds.cc/google_containers/kube-apiserver            v1.25.0   4d2edfd10d3e   5 days ago     128MB
harbor.raymonds.cc/google_containers/kube-controller-manager   v1.25.0   1a54c86c03a6   5 days ago     117MB
harbor.raymonds.cc/google_containers/kube-scheduler            v1.25.0   bef2cf311509   5 days ago     50.6MB
harbor.raymonds.cc/google_containers/kube-proxy                v1.25.0   58a9a0c6d96f   5 days ago     61.7MB
harbor.raymonds.cc/google_containers/pause                     3.8       4873874c08ef   2 months ago   711kB
harbor.raymonds.cc/google_containers/etcd                      3.5.4-0   a8a176a5d5d6   2 months ago   300MB
harbor.raymonds.cc/google_containers/coredns                   v1.9.3    5185b96f0bec   3 months ago   48.8MB

4.8 基于命令初始化高可用master方式

kubeadm init 命令参考说明

--kubernetes-version:#kubernetes程序组件的版本号,它必须要与安装的kubelet程序包的版本号相同
--control-plane-endpoint:#多主节点必选项,用于指定控制平面的固定访问地址,可是IP地址或DNS名称,会被用于集群管理员及集群组件的kubeconfig配置文件的API Server的访问地址,如果是单主节点的控制平面部署时不使用该选项,注意:kubeadm 不支持将没有 --control-plane-endpoint 参数的单个控制平面集群转换为高可用性集群。
--pod-network-cidr:#Pod网络的地址范围,其值为CIDR格式的网络地址,通常情况下Flannel网络插件的默认为10.244.0.0/16,Calico网络插件的默认值为192.168.0.0/16
--service-cidr:#Service的网络地址范围,其值为CIDR格式的网络地址,默认为10.96.0.0/12;通常,仅Flannel一类的网络插件需要手动指定该地址
--service-dns-domain string #指定k8s集群域名,默认为cluster.local,会自动通过相应的DNS服务实现解析
--apiserver-advertise-address:#API 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认网络接口。apiserver通告给其他组件的IP地址,一般应该为Master节点的用于集群内部通信的IP地址,0.0.0.0表示此节点上所有可用地址,非必选项
--image-repository string #设置镜像仓库地址,默认为 k8s.gcr.io,此地址国内可能无法访问,可以指向国内的镜像地址
--token-ttl #共享令牌(token)的过期时长,默认为24小时,0表示永不过期;为防止不安全存储等原因导致的令牌泄露危及集群安全,建议为其设定过期时长。未设定该选项时,在token过期后,若期望再向集群中加入其它节点,可以使用如下命令重新创建token,并生成节点加入命令。kubeadm token create --print-join-command
--ignore-preflight-errors=Swap” #若各节点未禁用Swap设备,还需附加选项“从而让kubeadm忽略该错误
--upload-certs #将控制平面证书上传到 kubeadm-certs Secret
--cri-socket  #v1.24版之后指定连接cri的socket文件路径,注意;不同的CRI连接文件不同
#如果是cRI是containerd,则使用--cri-socket unix:///run/containerd/containerd.sock #如果是cRI是docker,则使用--cri-socket unix:///var/run/cri-dockerd.sock
#如果是CRI是CRI-o,则使用--cri-socket unix:///var/run/crio/crio.sock
#注意:CRI-o与containerd的容器管理机制不一样,所以镜像文件不能通用。

初始化集群:

#注意:如果没有harbor下面“--image-repository”后面的地址改成“registry.aliyuncs.com/google_containers”

[root@k8s-master01 ~]# kubeadm init --control-plane-endpoint="kubeapi.raymonds.cc" --kubernetes-version=v1.25.0  --pod-network-cidr=192.168.0.0/12 --service-cidr=10.96.0.0/12 --token-ttl=0 --cri-socket unix:///run/cri-dockerd.sock --image-repository harbor.raymonds.cc/google_containers  --upload-certs
...
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of the control-plane node running the following command on each as root:

  kubeadm join kubeapi.raymonds.cc:6443 --token ndloct.qmgzvq90q864dmqn \
	--discovery-token-ca-cert-hash sha256:65848696b3ad7c838728b75ef484bcf68f5c5f16dc9e1f4c35d42c0b744eb1b2 \
	--control-plane --certificate-key e488972b1ed8ccaa1916e1de397adae42151f7f67b61cac0f5e03f32a40240e0

Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join kubeapi.raymonds.cc:6443 --token ndloct.qmgzvq90q864dmqn \
	--discovery-token-ca-cert-hash sha256:65848696b3ad7c838728b75ef484bcf68f5c5f16dc9e1f4c35d42c0b744eb1b2 

4.9 生成 kubectl 命令的授权文件

kubectl是kube-apiserver的命令行客户端程序,实现了除系统部署之外的几乎全部的管理操作,是kubernetes管理员使用最多的命令之一。kubectl需经由API server认证及授权后方能执行相应的管理操作,kubeadm部署的集群为其生成了一个具有管理员权限的认证配置文件/etc/kubernetes/admin.conf,它可由kubectl通过默认的“$HOME/.kube/config”的路径进行加载。当然,用户也可在kubectl命令上使用–kubeconfig选项指定一个别的位置。

下面复制认证为Kubernetes系统管理员的配置文件至目标用户(例如当前用户root)的家目录下:

#可复制4.9的结果执行下面命令
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 
sudo chown $(id -u):$(id -g) $HOME/.kube/config

4.10 实现 kubectl 命令补全

kubectl 命令功能丰富,默认不支持命令补会,可以用下面方式实现

#CentOS
[root@k8s-master01 ~]# yum -y install bash-completion

#Ubuntu
[root@k8s-master01 ~]# apt -y install bash-completion

# 在 bash 中设置当前 shell 的自动补全,要先安装 bash-completion 包。
[root@k8s-master01 ~]# source <(kubectl completion bash) 

# 在您的 bash shell 中永久的添加自动补全
[root@k8s-master01 ~]# echo "source <(kubectl completion bash)" >> ~/.bashrc 

[root@k8s-master01 ~]# kubectl get nodes 
NAME                         STATUS     ROLES           AGE     VERSION
k8s-master01.example.local   NotReady   control-plane   9m12s   v1.25.0

4.11 高可用Master

如果是配置文件初始化集群,不用申请证书,命令行初始化,执行下面命令,申请证书,当前maste生成证书用于添加新控制节点

添加master02和master03:

kubeadm join kubeapi.raymonds.cc:6443 --token ndloct.qmgzvq90q864dmqn \
	--discovery-token-ca-cert-hash sha256:65848696b3ad7c838728b75ef484bcf68f5c5f16dc9e1f4c35d42c0b744eb1b2 \
	--control-plane --certificate-key e488972b1ed8ccaa1916e1de397adae42151f7f67b61cac0f5e03f32a40240e0 --cri-socket unix:///run/cri-dockerd.sock

[root@k8s-master01 ~]# kubectl get nodes
NAME                         STATUS     ROLES           AGE    VERSION
k8s-master01.example.local   NotReady   control-plane   21m    v1.25.0
k8s-master02.example.local   NotReady   control-plane   116s   v1.25.0
k8s-master03.example.local   NotReady   control-plane   38s   v1.25.0

4.12 高可用Node

Node节点上主要部署公司的一些业务应用,生产环境中不建议Master节点部署系统组件之外的其他Pod,测试环境可以允许Master节点部署Pod以节省系统资源。

添加node:

kubeadm join kubeapi.raymonds.cc:6443 --token ndloct.qmgzvq90q864dmqn \
	--discovery-token-ca-cert-hash sha256:65848696b3ad7c838728b75ef484bcf68f5c5f16dc9e1f4c35d42c0b744eb1b2 --cri-socket unix:///run/cri-dockerd.sock

[root@k8s-master01 ~]# kubectl get nodes
NAME                         STATUS     ROLES           AGE    VERSION
k8s-master01.example.local   NotReady   control-plane   74m    v1.25.0
k8s-master02.example.local   NotReady   control-plane   60m    v1.25.0
k8s-master03.example.local   NotReady   control-plane   105s   v1.25.0
k8s-node01.example.local     NotReady   <none>          59m    v1.25.0
k8s-node02.example.local     NotReady   <none>          59m    v1.25.0
k8s-node03.example.local     NotReady   <none>          6s     v1.25.0

4.13 网络组件flannel部署

Kubernetes系统上Pod网络的实现依赖于第三方插件进行,这类插件有近数十种之多,较为著名的有flannel、calico、canal和kube-router等,简单易用的实现是为CoreOS提供的flannel项目。下面的命令用于在线部署flannel至Kubernetes系统之上:

首先,下载适配系统及硬件平台环境的flanneld至每个节点,并放置于/opt/bin/目录下。我们这里选用flanneld-amd64,目前最新的版本为v0.19.1,因而,我们需要在集群的每个节点上执行如下命令:

提示:下载flanneld的地址为 https://github.com/flannel-io/flannel

随后,在初始化的第一个master节点k8s-master01上运行如下命令,向Kubernetes部署kube-flannel。

[root@k8s-master01 ~]# wget https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

[root@k8s-master01 ~]# grep  '"Network":' kube-flannel.yml 
      "Network": "10.244.0.0/16",

[root@k8s-master01 ~]# sed -ri '/"Network":/s@("Network": ).*@\1"192.168.0.0/12",@g' kube-flannel.yml
[root@k8s-master01 ~]# grep  '"Network":' kube-flannel.yml 
      "Network": "192.168.0.0/12",

[root@k8s-master01 ~]# grep  '[^#]image:' kube-flannel.yml 
        image: docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin:v1.1.0
        image: docker.io/rancher/mirrored-flannelcni-flannel:v0.19.1
        image: docker.io/rancher/mirrored-flannelcni-flannel:v0.19.1

#注意:如果没有harbor不用执行下面脚本
[root@k8s-master01 ~]# cat download_flannel_images.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2022-01-11
#FileName:      download_flannel_images.sh
#URL:           raymond.blog.csdn.net
#Description:   The test script
#Copyright (C): 2022 All rights reserved
#*********************************************************************************************
COLOR="echo -e \\033[01;31m"
END='\033[0m'

images=$(awk -F "/"  '/[^#]image:/{print $NF}' kube-flannel.yml |uniq)
HARBOR_DOMAIN=harbor.raymonds.cc

images_download(){
    ${COLOR}"开始下载Flannel镜像"${END}
    for i in ${images};do 
        docker pull registry.cn-beijing.aliyuncs.com/raymond9/$i
        docker tag registry.cn-beijing.aliyuncs.com/raymond9/$i ${HARBOR_DOMAIN}/google_containers/$i
        docker rmi registry.cn-beijing.aliyuncs.com/raymond9/$i
        docker push ${HARBOR_DOMAIN}/google_containers/$i
    done
    ${COLOR}"Flannel镜像下载完成"${END}
}

images_download

[root@k8s-master01 ~]# bash download_flannel_images.sh

[root@k8s-master01 ~]# docker images |grep flannel
harbor.raymonds.cc/google_containers/mirrored-flannelcni-flannel              v0.19.1   252b2c3ee6c8   3 weeks ago    62.3MB
harbor.raymonds.cc/google_containers/mirrored-flannelcni-flannel-cni-plugin   v1.1.0    fcecffc7ad4a   3 months ago   8.09MB

[root@k8s-master01 ~]# sed -ri 's@([^#]image:) docker.io/rancher(/.*)@\1 harbor.raymonds.cc/google_containers\2@g' kube-flannel.yml

[root@k8s-master01 ~]# grep  '[^#]image:' kube-flannel.yml
        image: harbor.raymonds.cc/google_containers/mirrored-flannelcni-flannel-cni-plugin:v1.1.0
        image: harbor.raymonds.cc/google_containers/mirrored-flannelcni-flannel:v0.19.1
        image: harbor.raymonds.cc/google_containers/mirrored-flannelcni-flannel:v0.19.1

#注意:如果没有harbor执行下面命令
[root@k8s-master01 ~]# sed -ri 's@([^#]image:) docker.io/rancher(/.*)@\1 registry.cn-beijing.aliyuncs.com/raymond9\2@g' kube-flannel.yml 

[root@k8s-master01 ~]# kubectl apply -f kube-flannel.yml

#查看容器状态
[root@k8s-master01 ~]# kubectl get pod -n kube-flannel 
NAME                    READY   STATUS    RESTARTS   AGE
kube-flannel-ds-447kh   1/1     Running   0          113s
kube-flannel-ds-5b2cq   1/1     Running   0          113s
kube-flannel-ds-jgkdp   1/1     Running   0          113s
kube-flannel-ds-pksgj   1/1     Running   0          113s
kube-flannel-ds-wqcz6   1/1     Running   0          113s
kube-flannel-ds-z8hlk   1/1     Running   0          113s

#查看集群状态
[root@k8s-master01 ~]# kubectl get nodes 
NAME                         STATUS   ROLES           AGE    VERSION
k8s-master01.example.local   Ready    control-plane   145m   v1.25.0
k8s-master02.example.local   Ready    control-plane   131m   v1.25.0
k8s-master03.example.local   Ready    control-plane   72m    v1.25.0
k8s-node01.example.local     Ready    <none>          130m   v1.25.0
k8s-node02.example.local     Ready    <none>          129m   v1.25.0
k8s-node03.example.local     Ready    <none>          70m    v1.25.0

重要:如果安装了keepalived和haproxy,需要测试keepalived是否是正常的

#测试VIP
[root@k8s-master01 ~]# ping 172.31.3.188
PING 172.31.3.188 (172.31.3.188) 56(84) bytes of data.
64 bytes from 172.31.3.188: icmp_seq=1 ttl=64 time=0.526 ms
64 bytes from 172.31.3.188: icmp_seq=2 ttl=64 time=0.375 ms
^C
--- 172.31.3.188 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1015ms
rtt min/avg/max/mdev = 0.375/0.450/0.526/0.078 ms

[root@k8s-ha01 ~]# systemctl stop keepalived
[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 pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:05:9b:2a brd ff:ff:ff:ff:ff:ff
    inet 172.31.3.104/21 brd 172.31.7.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe05:9b2a/64 scope link 
       valid_lft forever preferred_lft forever
 
 [root@k8s-ha02 ~]# 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 pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:5e:d8:f8 brd ff:ff:ff:ff:ff:ff
    inet 172.31.3.105/21 brd 172.31.7.255 scope global 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:fe5e:d8f8/64 scope link 
       valid_lft forever preferred_lft forever
 
 [root@k8s-ha01 ~]# systemctl start keepalived
 [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 pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:05:9b:2a brd ff:ff:ff:ff:ff:ff
    inet 172.31.3.104/21 brd 172.31.7.255 scope global 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:fe05:9b2a/64 scope link 
       valid_lft forever preferred_lft forever

[root@k8s-master01 ~]# telnet 172.31.3.188 6443
Trying 172.31.3.188...
Connected to 172.31.3.188.
Escape character is '^]'.
Connection closed by foreign host.

如果ping不通且telnet没有出现 ] ,则认为VIP不可以,不可在继续往下执行,需要排查keepalived的问题,比如防火墙和selinux,haproxy和keepalived的状态,监听端口等

所有节点查看防火墙状态必须为disable和inactive:systemctl status firewalld

所有节点查看selinux状态,必须为disable:getenforce

master节点查看haproxy和keepalived状态:systemctl status keepalived haproxy

master节点查看监听端口:netstat -lntp

查看haproxy状态
http://kubeapi.raymonds.cc:9999/haproxy-status
在这里插入图片描述

4.14 测试应用编排及服务访问

demoapp是一个web应用,可将demoapp以Pod的形式编排运行于集群之上,并通过在集群外部进行访问:

[root@k8s-master01 ~]# kubectl create deployment demoapp --image=registry.cn-hangzhou.aliyuncs.com/raymond9/demoapp:v1.0 --replicas=3
deployment.apps/demoapp created

[root@k8s-master01 ~]# kubectl get pod -o wide
NAME                      READY   STATUS    RESTARTS   AGE   IP            NODE                       NOMINATED NODE   READINESS GATES
demoapp-c4787f9fc-bcrlh   1/1     Running   0          28s   192.160.2.2   k8s-node01.example.local   <none>           <none>
demoapp-c4787f9fc-fbfnq   1/1     Running   0          28s   192.160.3.2   k8s-node02.example.local   <none>           <none>
demoapp-c4787f9fc-zv9mp   1/1     Running   0          28s   192.160.5.2   k8s-node03.example.local   <none>           <none>

[root@k8s-master01 ~]# curl 192.160.2.2
raymond demoapp v1.0 !! ClientIP: 192.160.0.0, ServerName: demoapp-c4787f9fc-bcrlh, ServerIP: 192.160.2.2!
[root@k8s-master01 ~]# curl 192.160.3.2
raymond demoapp v1.0 !! ClientIP: 192.160.0.0, ServerName: demoapp-c4787f9fc-fbfnq, ServerIP: 192.160.3.2!
[root@k8s-master01 ~]# curl 192.160.5.2
raymond demoapp v1.0 !! ClientIP: 192.160.0.0, ServerName: demoapp-c4787f9fc-zv9mp, ServerIP: 192.160.5.2!

#使用如下命令了解Service对象demoapp使用的NodePort,格式:<集群端口>:<POd端口>,以便于在集群外部进行访问
[root@k8s-master01 ~]# kubectl create service nodeport demoapp --tcp=80:80
service/demoapp created

[root@k8s-master01 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
demoapp      NodePort    10.102.135.254   <none>        80:30589/TCP   13s
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP        160m

[root@k8s-master01 ~]# curl 10.102.135.254
raymond demoapp v1.0 !! ClientIP: 192.160.0.0, ServerName: demoapp-c4787f9fc-zv9mp, ServerIP: 192.160.5.2!
[root@k8s-master01 ~]# curl 10.102.135.254
raymond demoapp v1.0 !! ClientIP: 192.160.0.0, ServerName: demoapp-c4787f9fc-bcrlh, ServerIP: 192.160.2.2!
[root@k8s-master01 ~]# curl 10.102.135.254
raymond demoapp v1.0 !! ClientIP: 192.160.0.0, ServerName: demoapp-c4787f9fc-fbfnq, ServerIP: 192.160.3.2!

#用户可以于集群外部通过“http://NodeIP:30589”这个URL访问demoapp上的应用,例如于集群外通过浏览器访问“http://<kubernetes-node>:30589”。
[root@rocky8 ~]# curl http://172.31.3.101:30589
raymond demoapp v1.0 !! ClientIP: 192.160.0.0, ServerName: demoapp-c4787f9fc-bcrlh, ServerIP: 192.160.2.2!
[root@rocky8 ~]# curl http://172.31.3.102:30589
raymond demoapp v1.0 !! ClientIP: 192.160.1.0, ServerName: demoapp-c4787f9fc-fbfnq, ServerIP: 192.160.3.2!
[root@rocky8 ~]# curl http://172.31.3.103:30589
raymond demoapp v1.0 !! ClientIP: 192.160.4.0, ServerName: demoapp-c4787f9fc-zv9mp, ServerIP: 192.160.5.2!

#扩容
[root@k8s-master01 ~]# kubectl scale deployment demoapp --replicas 5
deployment.apps/demoapp scaled
[root@k8s-master01 ~]# kubectl get pod
NAME                      READY   STATUS    RESTARTS   AGE
demoapp-c4787f9fc-4snfh   1/1     Running   0          5s
demoapp-c4787f9fc-bcrlh   1/1     Running   0          9m47s
demoapp-c4787f9fc-fbfnq   1/1     Running   0          9m47s
demoapp-c4787f9fc-mgnfc   1/1     Running   0          5s
demoapp-c4787f9fc-zv9mp   1/1     Running   0          9m47s

#缩容
[root@k8s-master01 ~]# kubectl scale deployment demoapp --replicas 2
deployment.apps/demoapp scaled

#可以看到销毁pod的过程
[root@k8s-master01 ~]# kubectl get pod
NAME                      READY   STATUS        RESTARTS   AGE
demoapp-c4787f9fc-4snfh   1/1     Terminating   0          34s
demoapp-c4787f9fc-bcrlh   1/1     Running       0          10m
demoapp-c4787f9fc-fbfnq   1/1     Running       0          10m
demoapp-c4787f9fc-mgnfc   1/1     Terminating   0          34s
demoapp-c4787f9fc-zv9mp   1/1     Terminating   0          10m

#再次查看,最终缩容成功
[root@k8s-master01 ~]# kubectl get pod
NAME                      READY   STATUS    RESTARTS   AGE
demoapp-c4787f9fc-bcrlh   1/1     Running   0          11m
demoapp-c4787f9fc-fbfnq   1/1     Running   0          11m

4.15 基于文件初始化高可用master方式

Master01节点创建kubeadm-config.yaml配置文件如下:

Master01:(# 注意,如果不是高可用集群,172.31.3.188:6443改为master01的地址,注意更改v1.18.5自己服务器kubeadm的版本:kubeadm version)

注意

以下文件内容,宿主机网段、podSubnet网段、serviceSubnet网段不能重复

[root@k8s-master01 ~]# kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"25", GitVersion:"v1.25.0", GitCommit:"a866cbe2e5bbaa01cfd5e969aa3e033f3282a8a2", GitTreeState:"clean", BuildDate:"2022-08-23T17:43:25Z", GoVersion:"go1.19", Compiler:"gc", Platform:"linux/amd64"}

#将默认配置输出⾄⽂件
[root@k8s-master01 ~]# kubeadm config print init-defaults > kubeadm-config.yaml

#修改后的配置文件
[root@k8s-master01 ~]# cat kubeadm-config.yaml 
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: 7t2weq.bjbawausm0jaxury
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 172.31.3.101 #master01的IP地址
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///run/cri-dockerd.sock #容器运行时
  imagePullPolicy: IfNotPresent
  name: k8s-master01.example.local #设置master01的hostname
  taints:
  - effect: NoSchedule
    key: node-role.kubernetes.io/master
---
apiServer:
  certSANs:
  - kubeapi.raymonds.cc #VIP地址
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controlPlaneEndpoint: kubeapi.raymonds.cc:6443 #haproxy代理后端地址
controllerManager: {}
dns: {}
etcd:
  local:
    dataDir: /var/lib/etcd
imageRepository: harbor.raymonds.cc/google_containers #harbor镜像地址,如果没有harbor改成“registry.aliyuncs.com/google_containers”
kind: ClusterConfiguration
kubernetesVersion: v1.25.0 #更改版本号
networking:
  dnsDomain: cluster.local #dnsdomain
  podSubnet: 192.168.0.0/12 #pod网段
  serviceSubnet: 10.96.0.0/12 #service网段
scheduler: {}

更新kubeadm文件

[root@k8s-master01 ~]# kubeadm config migrate --old-config kubeadm-config.yaml --new-config new.yaml

[root@k8s-master01 ~]# cat new.yaml 
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: 7t2weq.bjbawausm0jaxury
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 172.31.3.101
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///run/cri-dockerd.sock
  imagePullPolicy: IfNotPresent
  name: k8s-master01.example.local
  taints:
  - effect: NoSchedule
    key: node-role.kubernetes.io/master
---
apiServer:
  certSANs:
  - kubeapi.raymonds.cc
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controlPlaneEndpoint: kubeapi.raymonds.cc:6443
controllerManager: {}
dns: {}
etcd:
  local:
    dataDir: /var/lib/etcd
imageRepository: harbor.raymonds.cc/google_containers
kind: ClusterConfiguration
kubernetesVersion: v1.25.0
networking:
  dnsDomain: cluster.local
  podSubnet: 192.168.0.0/12
  serviceSubnet: 10.96.0.0/12
scheduler: {}

Master01节点初始化,初始化以后会在/etc/kubernetes目录下生成对应的证书和配置文件,之后其他Master节点加入Master01即可:

#如果已经初始化过,重新初始化用下面命令reset集群后,再进行初始化
#master和node上执行
kubeadm reset -f --cri-socket unix:///run/cri-dockerd.sock 
rm -rf /etc/cni/net.d/
rm -rf $HOME/.kube/config
reboot

[root@k8s-master01 ~]# kubeadm init --config /root/new.yaml  --upload-certs
...
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of the control-plane node running the following command on each as root:

  kubeadm join kubeapi.raymonds.cc:6443 --token 7t2weq.bjbawausm0jaxury \
	--discovery-token-ca-cert-hash sha256:f3c7558578e131d50c3aef5324635dfd1e8b768c74bafe844e68992a88494ad8 \
	--control-plane --certificate-key e9b299145e9f51cdd3faf9a0a2eff12606105a4674f1e8fd05d128a5e4937a3b

Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join kubeapi.raymonds.cc:6443 --token 7t2weq.bjbawausm0jaxury \
	--discovery-token-ca-cert-hash sha256:f3c7558578e131d50c3aef5324635dfd1e8b768c74bafe844e68992a88494ad8 

生成 kubectl 命令的授权文件,重复4.10

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 
sudo chown $(id -u):$(id -g) $HOME/.kube/config

[root@k8s-master01 ~]# kubectl get nodes 
NAME                         STATUS     ROLES           AGE    VERSION
k8s-master01.example.local   NotReady   control-plane   3m8s   v1.25.0

高可用master,参考4.12

#添加master02和master03
kubeadm join kubeapi.raymonds.cc:6443 --token 7t2weq.bjbawausm0jaxury \
	--discovery-token-ca-cert-hash sha256:f3c7558578e131d50c3aef5324635dfd1e8b768c74bafe844e68992a88494ad8 \
	--control-plane --certificate-key e9b299145e9f51cdd3faf9a0a2eff12606105a4674f1e8fd05d128a5e4937a3b --cri-socket unix:///run/cri-dockerd.sock

[root@k8s-master01 ~]# kubectl get nodes 
NAME                         STATUS     ROLES           AGE     VERSION
k8s-master01.example.local   NotReady   control-plane   5m53s   v1.25.0
k8s-master02.example.local   NotReady   control-plane   93s     v1.25.0
k8s-master03.example.local   NotReady   control-plane   38s     v1.25.0

高可用node,参考4.13

kubeadm join kubeapi.raymonds.cc:6443 --token 7t2weq.bjbawausm0jaxury \
	--discovery-token-ca-cert-hash sha256:f3c7558578e131d50c3aef5324635dfd1e8b768c74bafe844e68992a88494ad8 --cri-socket unix:///run/cri-dockerd.sock

[root@k8s-master01 ~]# kubectl get nodes 
NAME                         STATUS     ROLES           AGE     VERSION
k8s-master01.example.local   NotReady   control-plane   8m6s    v1.25.0
k8s-master02.example.local   NotReady   control-plane   3m46s   v1.25.0
k8s-master03.example.local   NotReady   control-plane   2m51s   v1.25.0
k8s-node01.example.local     NotReady   <none>          57s     v1.25.0
k8s-node02.example.local     NotReady   <none>          39s     v1.25.0
k8s-node03.example.local     NotReady   <none>          19s     v1.25.0

4.16 网络组件calico部署

https://docs.projectcalico.org/maintenance/kubernetes-upgrade#upgrading-an-installation-that-uses-the-kubernetes-api-datastore

calico安装:https://docs.projectcalico.org/getting-started/kubernetes/self-managed-onprem/onpremises

[root@k8s-master01 ~]# curl https://docs.projectcalico.org/manifests/calico.yaml -O

[root@k8s-master01 ~]# POD_SUBNET=`cat /etc/kubernetes/manifests/kube-controller-manager.yaml | grep cluster-cidr= | awk -F= '{print $NF}'`
[root@k8s-master01 ~]# echo $POD_SUBNET
192.168.0.0/12

[root@k8s-master01 ~]# grep -E "(.*CALICO_IPV4POOL_CIDR.*|.*192.168.0.0.*)" calico.yaml 
            # - name: CALICO_IPV4POOL_CIDR
            #   value: "192.168.0.0/16"

[root@k8s-master01 ~]# sed -i 's@# - name: CALICO_IPV4POOL_CIDR@- name: CALICO_IPV4POOL_CIDR@g; s@#   value: "192.168.0.0/16"@  value: '"${POD_SUBNET}"'@g' calico.yaml 
[root@k8s-master01 ~]# grep -E "(.*CALICO_IPV4POOL_CIDR.*|.*192.168.0.0.*)" calico.yaml 
            - name: CALICO_IPV4POOL_CIDR
              value: 192.168.0.0/12

[root@k8s-master01 ~]# grep "image:" calico.yaml 
          image: docker.io/calico/cni:v3.24.1
          image: docker.io/calico/cni:v3.24.1
          image: docker.io/calico/node:v3.24.1
          image: docker.io/calico/node:v3.24.1
          image: docker.io/calico/kube-controllers:v3.24.1

下载calico镜像并上传harbor:

#注意:如果没有harbor不用执行下面脚本
[root@k8s-master01 ~]# cat download_calico_images.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2022-01-11
#FileName:      download_calico_images.sh
#URL:           raymond.blog.csdn.net
#Description:   The test script
#Copyright (C): 2022 All rights reserved
#*********************************************************************************************
COLOR="echo -e \\033[01;31m"
END='\033[0m'

images=$(awk -F "/"  '/image:/{print $NF}' calico.yaml |uniq)
HARBOR_DOMAIN=harbor.raymonds.cc

images_download(){
    ${COLOR}"开始下载Calico镜像"${END}
    for i in ${images};do 
        docker pull registry.cn-beijing.aliyuncs.com/raymond9/$i
        docker tag registry.cn-beijing.aliyuncs.com/raymond9/$i ${HARBOR_DOMAIN}/google_containers/$i
        docker rmi registry.cn-beijing.aliyuncs.com/raymond9/$i
        docker push ${HARBOR_DOMAIN}/google_containers/$i
    done
    ${COLOR}"Calico镜像下载完成"${END}
}

images_download

[root@k8s-master01 ~]# bash download_calico_images.sh

[root@k8s-master01 ~]# sed -ri 's@(.*image:) docker.io/calico(/.*)@\1 harbor.raymonds.cc/google_containers\2@g' calico.yaml 

[root@k8s-master01 ~]# grep "image:" calico.yaml 
          image: harbor.raymonds.cc/google_containers/cni:v3.24.1
          image: harbor.raymonds.cc/google_containers/cni:v3.24.1
          image: harbor.raymonds.cc/google_containers/node:v3.24.1
          image: harbor.raymonds.cc/google_containers/node:v3.24.1
          image: harbor.raymonds.cc/google_containers/kube-controllers:v3.24.1

#注意:如果没有harbor执行下面命令
[root@k8s-master01 ~]# sed -ri 's@(.*image:) docker.io/calico(/.*)@\1 registry.cn-beijing.aliyuncs.com/raymond9\2@g' calico.yaml 

[root@k8s-master01 ~]# kubectl apply -f calico.yaml 

#查看容器状态
[root@k8s-master01 ~]# kubectl get pod -n kube-system |grep calico
calico-kube-controllers-5477499cbc-sc47h             1/1     Running   0             63s
calico-node-75wtg                                    1/1     Running   0             63s
calico-node-bdqmk                                    1/1     Running   0             63s
calico-node-fhvl7                                    1/1     Running   0             63s
calico-node-j5tx4                                    1/1     Running   0             63s
calico-node-l5pnw                                    1/1     Running   0             63s
calico-node-zvztr                                    1/1     Running   0             63s

#查看集群状态
[root@k8s-master01 ~]# kubectl get nodes 
NAME                         STATUS   ROLES           AGE   VERSION
k8s-master01.example.local   Ready    control-plane   67m   v1.25.0
k8s-master02.example.local   Ready    control-plane   63m   v1.25.0
k8s-master03.example.local   Ready    control-plane   62m   v1.25.0
k8s-node01.example.local     Ready    <none>          60m   v1.25.0
k8s-node02.example.local     Ready    <none>          60m   v1.25.0
k8s-node03.example.local     Ready    <none>          59m   v1.25.0

测试应用编排及服务访问,参考4.15

[root@k8s-master01 ~]# kubectl create deployment demoapp --image=registry.cn-hangzhou.aliyuncs.com/raymond9/demoapp:v1.0 --replicas=3
deployment.apps/demoapp created

[root@k8s-master01 ~]# kubectl get pod -o wide
NAME                      READY   STATUS    RESTARTS   AGE   IP                NODE                       NOMINATED NODE   READINESS GATES
demoapp-c4787f9fc-6kzzz   1/1     Running   0          11s   192.170.21.193    k8s-node03.example.local   <none>           <none>
demoapp-c4787f9fc-kbl7m   1/1     Running   0          11s   192.167.195.129   k8s-node02.example.local   <none>           <none>
demoapp-c4787f9fc-lb9nn   1/1     Running   0          11s   192.169.111.129   k8s-node01.example.local   <none>           <none>

[root@k8s-master01 ~]# curl 192.170.21.193
raymond demoapp v1.0 !! ClientIP: 192.162.55.64, ServerName: demoapp-c4787f9fc-6kzzz, ServerIP: 192.170.21.193!
[root@k8s-master01 ~]# curl 192.167.195.129
raymond demoapp v1.0 !! ClientIP: 192.162.55.64, ServerName: demoapp-c4787f9fc-kbl7m, ServerIP: 192.167.195.129!
[root@k8s-master01 ~]# curl 192.169.111.129
raymond demoapp v1.0 !! ClientIP: 192.162.55.64, ServerName: demoapp-c4787f9fc-lb9nn, ServerIP: 192.169.111.129!

#使用如下命令了解Service对象demoapp使用的NodePort,格式:<集群端口>:<POd端口>,以便于在集群外部进行访问
[root@k8s-master01 ~]# kubectl create service nodeport demoapp --tcp=80:80
service/demoapp created

[root@k8s-master01 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
demoapp      NodePort    10.106.167.169   <none>        80:31101/TCP   9s
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP        71m

[root@k8s-master01 ~]# curl 10.106.167.169
raymond demoapp v1.0 !! ClientIP: 192.162.55.64, ServerName: demoapp-c4787f9fc-kbl7m, ServerIP: 192.167.195.129!
[root@k8s-master01 ~]# curl 10.106.167.169
raymond demoapp v1.0 !! ClientIP: 192.162.55.64, ServerName: demoapp-c4787f9fc-6kzzz, ServerIP: 192.170.21.193!
[root@k8s-master01 ~]# curl 10.106.167.169
raymond demoapp v1.0 !! ClientIP: 192.162.55.64, ServerName: demoapp-c4787f9fc-lb9nn, ServerIP: 192.169.111.129!

#用户可以于集群外部通过“http://NodeIP:31101”这个URL访问demoapp上的应用,例如于集群外通过浏览器访问“http://<kubernetes-node>:31101”。
[root@rocky8 ~]# curl http://172.31.3.101:31101
raymond demoapp v1.0 !! ClientIP: 192.162.55.64, ServerName: demoapp-c4787f9fc-6kzzz, ServerIP: 192.170.21.193!
[root@rocky8 ~]# curl http://172.31.3.102:31101
raymond demoapp v1.0 !! ClientIP: 192.171.30.64, ServerName: demoapp-c4787f9fc-6kzzz, ServerIP: 192.170.21.193!
[root@rocky8 ~]# curl http://172.31.3.103:31101
raymond demoapp v1.0 !! ClientIP: 192.165.109.64, ServerName: demoapp-c4787f9fc-6kzzz, ServerIP: 192.170.21.193!

#扩容
[root@k8s-master01 ~]# kubectl scale deployment demoapp --replicas 5
deployment.apps/demoapp scaled
[root@k8s-master01 ~]# kubectl get pod
NAME                      READY   STATUS    RESTARTS   AGE
demoapp-c4787f9fc-6kzzz   1/1     Running   0          3m31s
demoapp-c4787f9fc-8l7n8   1/1     Running   0          9s
demoapp-c4787f9fc-kbl7m   1/1     Running   0          3m31s
demoapp-c4787f9fc-lb9nn   1/1     Running   0          3m31s
demoapp-c4787f9fc-rlljj   1/1     Running   0          9s

#缩容
[root@k8s-master01 ~]# kubectl scale deployment demoapp --replicas 2
deployment.apps/demoapp scaled

#可以看到销毁pod的过程
[root@k8s-master01 ~]# kubectl get pod
NAME                      READY   STATUS        RESTARTS   AGE
demoapp-c4787f9fc-6kzzz   1/1     Terminating   0          3m50s
demoapp-c4787f9fc-8l7n8   1/1     Terminating   0          28s
demoapp-c4787f9fc-kbl7m   1/1     Running       0          3m50s
demoapp-c4787f9fc-lb9nn   1/1     Running       0          3m50s
demoapp-c4787f9fc-rlljj   1/1     Terminating   0          28s

#再次查看,最终缩容成功
[root@k8s-master01 ~]# kubectl get pod
NAME                      READY   STATUS    RESTARTS   AGE
demoapp-c4787f9fc-kbl7m   1/1     Running   0          6m3s
demoapp-c4787f9fc-lb9nn   1/1     Running   0          6m3s

4.17 Metrics部署

在新版的Kubernetes中系统资源的采集均使用Metrics-server,可以通过Metrics采集节点和Pod的内存、磁盘、CPU和网络的使用率。

https://github.com/kubernetes-sigs/metrics-server

[root@k8s-master01 ~]# wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

将Master01节点的front-proxy-ca.crt复制到所有Node节点

[root@k8s-master01 ~]# for i in k8s-node01 k8s-node02 k8s-node03;do scp -o StrictHostKeyChecking=no /etc/kubernetes/pki/front-proxy-ca.crt $i:/etc/kubernetes/pki/front-proxy-ca.crt ; done

修改下面内容:

[root@k8s-master01 ~]# vim components.yaml
...
    spec:
      containers:
      - args:
        - --cert-dir=/tmp
        - --secure-port=4443
        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
        - --kubelet-use-node-status-port
        - --metric-resolution=15s
#添加下面内容
#添加下面内容
        - --kubelet-insecure-tls
        - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt #kubeadm证书文件是front-proxy-ca.crt
        - --requestheader-username-headers=X-Remote-User
        - --requestheader-group-headers=X-Remote-Group
        - --requestheader-extra-headers-prefix=X-Remote-Extra- 
...
        volumeMounts:
        - mountPath: /tmp
          name: tmp-dir
#添加下面内容
        - name: ca-ssl
          mountPath: /etc/kubernetes/pki 
...
      volumes:
      - emptyDir: {}
        name: tmp-dir
#添加下面内容
      - name: ca-ssl
        hostPath:
          path: /etc/kubernetes/pki 
...  

下载镜像并修改镜像地址:

[root@k8s-master01 ~]# grep "image:" components.yaml 
        image: k8s.gcr.io/metrics-server/metrics-server:v0.6.1

#注意:如果没有harbor不用执行下面脚本
[root@k8s-master01 ~]# cat download_metrics_images.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2022-01-11
#FileName:      download_metrics_images.sh
#URL:           raymond.blog.csdn.net
#Description:   The test script
#Copyright (C): 2022 All rights reserved
#*********************************************************************************************
COLOR="echo -e \\033[01;31m"
END='\033[0m'

images=$(awk -F "/"  '/image:/{print $NF}' components.yaml)
HARBOR_DOMAIN=harbor.raymonds.cc

images_download(){
    ${COLOR}"开始下载Metrics镜像"${END}
    for i in ${images};do 
        docker pull registry.aliyuncs.com/google_containers/$i
        docker tag registry.aliyuncs.com/google_containers/$i ${HARBOR_DOMAIN}/google_containers/$i
        docker rmi registry.aliyuncs.com/google_containers/$i
        docker push ${HARBOR_DOMAIN}/google_containers/$i
    done
    ${COLOR}"Metrics镜像下载完成"${END}
}

images_download

[root@k8s-master01 ~]# bash download_metrics_images.sh

[root@k8s-master01 ~]# sed -ri 's@(.*image:) k8s.gcr.io/metrics-server(/.*)@\1 harbor.raymonds.cc/google_containers\2@g' components.yaml

[root@k8s-master01 ~]# grep "image:" components.yaml 
        image: harbor.raymonds.cc/google_containers/metrics-server:v0.6.1

#注意:如果没有harbor执行下面命令
[root@k8s-master01 ~]# sed -ri 's@(.*image:) k8s.gcr.io/metrics-server(/.*)@\1 registry.aliyuncs.com/google_containers\2@g' components.yaml

[root@k8s-master01 ~]# kubectl apply -f components.yaml

查看状态

[root@k8s-master01 ~]# kubectl get pod -n kube-system |grep metrics
metrics-server-6dcf48c9dc-pdghg                      1/1     Running   0             35s

[root@k8s-master01 ~]# kubectl top node
NAME                         CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
k8s-master01.example.local   339m         16%    1658Mi          43%       
k8s-master02.example.local   319m         15%    1177Mi          30%       
k8s-master03.example.local   299m         14%    1318Mi          34%       
k8s-node01.example.local     133m         6%     759Mi           19%       
k8s-node02.example.local     136m         6%     723Mi           19%       
k8s-node03.example.local     156m         7%     764Mi           20%  

4.18 Dashboard部署

Dashboard用于展示集群中的各类资源,同时也可以通过Dashboard实时查看Pod的日志和在容器中执行一些命令等。

https://github.com/kubernetes/dashboard/releases

查看对应版本兼容的kubernetes版本

在这里插入图片描述
可以看到上图dashboard v2.7.0是支持kuberneres 1.25版本的

[root@k8s-master01 ~]# wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml

[root@k8s-master01 ~]# vim recommended.yaml
...
kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  type: NodePort #添加这行
  ports:
    - port: 443
      targetPort: 8443
      nodePort: 30005 #添加这行
  selector:
    k8s-app: kubernetes-dashboard
...

[root@k8s-master01 ~]# grep "image:" recommended.yaml 
          image: kubernetesui/dashboard:v2.7.0
          image: kubernetesui/metrics-scraper:v1.0.8

#注意:如果没有harbor不用执行下面脚本
[root@k8s-master01 ~]# cat download_dashboard_images.sh 
#!/bin/bash
#
#**********************************************************************************************
#Author:        Raymond
#QQ:            88563128
#Date:          2022-01-11
#FileName:      download_dashboard_images.sh
#URL:           raymond.blog.csdn.net
#Description:   The test script
#Copyright (C): 2022 All rights reserved
#*********************************************************************************************
COLOR="echo -e \\033[01;31m"
END='\033[0m'

images=$(awk -F "/"  '/image:/{print $NF}' recommended.yaml)
HARBOR_DOMAIN=harbor.raymonds.cc

images_download(){
    ${COLOR}"开始下载Dashboard镜像"${END}
    for i in ${images};do 
        docker pull registry.aliyuncs.com/google_containers/$i
        docker tag registry.aliyuncs.com/google_containers/$i ${HARBOR_DOMAIN}/google_containers/$i
        docker rmi registry.aliyuncs.com/google_containers/$i
        docker push ${HARBOR_DOMAIN}/google_containers/$i
    done
    ${COLOR}"Dashboard镜像下载完成"${END}
}

images_download

[root@k8s-master01 ~]# bash download_dashboard_images.sh

[root@k8s-master01 ~]# docker images |grep -E "(dashboard|metrics-scraper)"
harbor.raymonds.cc/google_containers/dashboard                                v2.7.0    783e2b6d87ed   2 weeks ago    246MB
harbor.raymonds.cc/google_containers/metrics-scraper                          v1.0.8    115053965e86   3 months ago   43.8MB

[root@k8s-master01 ~]# sed -ri 's@(.*image:) kubernetesui(/.*)@\1 harbor.raymonds.cc/google_containers\2@g' recommended.yaml 

[root@k8s-master01 ~]# grep "image:" recommended.yaml 
          image: harbor.raymonds.cc/google_containers/dashboard:v2.7.0
          image: harbor.raymonds.cc/google_containers/metrics-scraper:v1.0.8

#注意:如果没有harbor执行下面命令
[root@k8s-master01 ~]# sed -ri 's@(.*image:) kubernetesui(/.*)@\1 registry.aliyuncs.com/google_containers\2@g' recommended.yaml

[root@k8s-master01 ~]# kubectl apply -f recommended.yaml

创建管理员用户admin.yaml

[root@k8s-master01 ~]# cat > admin.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard
EOF

[root@k8s-master01 ~]# kubectl apply -f admin.yaml

4.18.1 登录dashboard

在谷歌浏览器(Chrome)启动文件中加入启动参数,用于解决无法访问Dashboard的问题,参考图1-1:

--test-type --ignore-certificate-errors

在这里插入图片描述
​ 图1-1 谷歌浏览器 Chrome的配置

[root@k8s-master01 ~]# kubectl get svc kubernetes-dashboard -n kubernetes-dashboard
NAME                   TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)         AGE
kubernetes-dashboard   NodePort   10.110.52.91   <none>        443:30005/TCP   13m

访问Dashboard:https://172.31.3.101:30005,参考图1-2
在这里插入图片描述
​ 图1-2 Dashboard登录方式

4.18.2 token登录

创建token:

[root@k8s-master01 ~]# kubectl -n kubernetes-dashboard create token admin-user
eyJhbGciOiJSUzI1NiIsImtpZCI6IlZ5QWEtZjkzVW51eWJkZlJESTA4ZGNvLUdXM0lIeTVkMktRakhzckxIZTQifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjYxODc5NTA1LCJpYXQiOjE2NjE4NzU5MDUsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJhZG1pbi11c2VyIiwidWlkIjoiNmY0ZTk5N2MtNDlhNC00MDMwLWE4NTQtNWNjNGE5NzRmYWQ4In19LCJuYmYiOjE2NjE4NzU5MDUsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlcm5ldGVzLWRhc2hib2FyZDphZG1pbi11c2VyIn0.mAm2OGxsRTLCL-NKJpWM3NanRLHGSE9zwf80HayF_spIkD_w5KWRWUplvTVfks6sSgcZT38IzVZAEtHWwOf_qvDZNfp5-aVq9t4eb_jnbtRFSZVpDarF-AeHNAlbZk--DI-U--nsc8xsl-YjmVjhAYqL5xrqAPjnZdo7ewTIuj94MWOcwN4I3OjJCq0vPoOqTf2r4pkgadjZJIV1Shvcn304Ol-Sxt0OBtKhwrXQDKGvJGGBCxQdq8LD8uFKRSf1-gjwOgK_f617UzoDjpZB0wy0JodS0Q0G8HOMs1pmpiqVIhi_azcd8-961Q4eynDuHAKO9Hgt3gRp5wxhqV5L1A

将token值输入到令牌后,单击登录即可访问Dashboard,参考图1-3:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fYQoiCKR-1663421631772)(images/027.jpg)]

在这里插入图片描述
在这里插入图片描述

4.18.3 使用kubeconfig文件登录dashboard

[root@k8s-master01 ~]# cp /etc/kubernetes/admin.conf kubeconfig

[root@k8s-master01 ~]# vim kubeconfig 
...
#在最下面添加token
    token: eyJhbGciOiJSUzI1NiIsImtpZCI6IlZ5QWEtZjkzVW51eWJkZlJESTA4ZGNvLUdXM0lIeTVkMktRakhzckxIZTQifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjYxODc5NTA1LCJpYXQiOjE2NjE4NzU5MDUsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJhZG1pbi11c2VyIiwidWlkIjoiNmY0ZTk5N2MtNDlhNC00MDMwLWE4NTQtNWNjNGE5NzRmYWQ4In19LCJuYmYiOjE2NjE4NzU5MDUsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlcm5ldGVzLWRhc2hib2FyZDphZG1pbi11c2VyIn0.mAm2OGxsRTLCL-NKJpWM3NanRLHGSE9zwf80HayF_spIkD_w5KWRWUplvTVfks6sSgcZT38IzVZAEtHWwOf_qvDZNfp5-aVq9t4eb_jnbtRFSZVpDarF-AeHNAlbZk--DI-U--nsc8xsl-YjmVjhAYqL5xrqAPjnZdo7ewTIuj94MWOcwN4I3OjJCq0vPoOqTf2r4pkgadjZJIV1Shvcn304Ol-Sxt0OBtKhwrXQDKGvJGGBCxQdq8LD8uFKRSf1-gjwOgK_f617UzoDjpZB0wy0JodS0Q0G8HOMs1pmpiqVIhi_azcd8-961Q4eynDuHAKO9Hgt3gRp5wxhqV5L1A

在这里插入图片描述

在这里插入图片描述

5.一些必须的配置更改

将Kube-proxy改为ipvs模式,因为在初始化集群的时候注释了ipvs配置,所以需要自行修改一下:

在master01节点执行

[root@k8s-master01 ~]# curl 127.0.0.1:10249/proxyMode
iptables

[root@k8s-master01 ~]# kubectl edit cm kube-proxy -n kube-system
...
    mode: "ipvs"

更新Kube-Proxy的Pod:

[root@k8s-master01 ~]# kubectl patch daemonset kube-proxy -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"date\":\"`date +'%s'`\"}}}}}" -n kube-system
daemonset.apps/kube-proxy patched

验证Kube-Proxy模式

[root@k8s-master01 ~]# curl 127.0.0.1:10249/proxyMode
ipvs

[root@k8s-master01 ~]# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.17.0.1:30005 rr
  -> 192.169.111.132:8443         Masq    1      0          0         
TCP  172.17.0.1:31101 rr
  -> 192.167.195.129:80           Masq    1      0          0         
  -> 192.169.111.129:80           Masq    1      0          0         
TCP  172.31.3.101:30005 rr
  -> 192.169.111.132:8443         Masq    1      0          0         
TCP  172.31.3.101:31101 rr
  -> 192.167.195.129:80           Masq    1      0          0         
  -> 192.169.111.129:80           Masq    1      0          0         
TCP  192.162.55.64:30005 rr
  -> 192.169.111.132:8443         Masq    1      0          0         
TCP  192.162.55.64:31101 rr
  -> 192.167.195.129:80           Masq    1      0          0         
  -> 192.169.111.129:80           Masq    1      0          0         
TCP  10.96.0.1:443 rr
  -> 172.31.3.101:6443            Masq    1      0          0         
  -> 172.31.3.102:6443            Masq    1      0          0         
  -> 172.31.3.103:6443            Masq    1      1          0         
TCP  10.96.0.10:53 rr
  -> 192.162.55.65:53             Masq    1      0          0         
  -> 192.162.55.67:53             Masq    1      0          0         
TCP  10.96.0.10:9153 rr
  -> 192.162.55.65:9153           Masq    1      0          0         
  -> 192.162.55.67:9153           Masq    1      0          0         
TCP  10.101.15.4:8000 rr
  -> 192.170.21.198:8000          Masq    1      0          0         
TCP  10.102.2.33:443 rr
  -> 192.170.21.197:4443          Masq    1      0          0         
TCP  10.106.167.169:80 rr
  -> 192.167.195.129:80           Masq    1      0          0         
  -> 192.169.111.129:80           Masq    1      0          0         
TCP  10.110.52.91:443 rr
  -> 192.169.111.132:8443         Masq    1      0          0         
UDP  10.96.0.10:53 rr
  -> 192.162.55.65:53             Masq    1      0          0         
  -> 192.162.55.67:53             Masq    1      0          0 
  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Raymond运维

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值