Kubernetes是 Google 团队发起并维护的基于 Docker 的开源容器集群管理系统,它不仅支持常见的云平台,而且支持内部数据中心。
建于 容器技术之上的 Kubernetes 可以构建一个容器的调度服务,其目的是让用户透过 Kubernetes 集群来进行云端容器集群的管理,而无需用户进行复杂的设置工作。系统会自动选取合适的工作节点来执行具体的容器集群调度处理工作。其核心概念是 Container Pod。一个 Pod 由一组工作于同一物理工作节点的容器构成。这些组容器拥有相同的网络命名空间、IP以及存储配额,也可以根据实际情况对每一个 Pod 进行端口映射。此外,Kubernetes 工作节点会由主系统进行管理,节点包含了能够运行 Docker 容器所用到的服务。
Kubernetes最初源于谷歌内部的Borg,提供了面向应用的容器集群部署和管理系统。Kubernetes的目标旨在消除编排物理/虚拟计算,网络和存储基础设施的负担,并使应用程序运营商和开发人员完全将重点放在以容器为中心的原语上进行自助运营。Kubernetes 也提供稳定、兼容的基础(平台),用于构建定制化的workflows 和更高级的自动化任务。 Kubernetes 具备完善的集群管理能力,包括多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和服务发现机制、内建负载均衡器、故障发现和自我修复能力、服务滚动升级和在线扩容、可扩展的资源自动调度机制、多粒度的资源配额管理能力。 Kubernetes 还提供完善的管理工具,涵盖开发、部署测试、运维监控等各个环节。
Kubermetes 主要有一下几个核心组件组成:
etcd保存了整个集群的状态;
piserver提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上;
kubelet负责维护容器的生命周期,同时也负责Volume(CSI)和网络(CNI)的管理;
Container runtime负责镜像管理以及Pod和容器的真正运行(CRI);
kube-proxy负责为Service提供cluster内部的服务发现和负载均衡;
除了核心组件,还有一些推荐的插件,其中有的已经成为CNCF中的托管项目:
CoreDNS负责为整个集群提供DNS服务
Ingress Controller为服务提供外网入口
Prometheus提供资源监控
Dashboard提供GUI
Federation提供跨可用区的集群
下图清晰表明了Kubernetes的架构设计以及组件之间的通信协议。
Kubernetes设计理念和功能其实就是一个类似Linux的分层架构,如下图所示。
内核层(核心层):Kubernetes最核心的功能,对外提供API构建高层的应用,对内提供插件式应用执行环境
应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS解析等)、Service Mesh(部分位于应用层)
治理层(管理层):系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy等)、Service Mesh(部分位于管理层)
接口层:kubectl命令行工具、客户端SDK以及集群联邦
生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
Kubernetes外部:日志、监控、配置管理、CI/CD、Workflow、FaaS、OTS应用、ChatOps、GitOps、SecOps等
Kubernetes内部:CRI、CNI、CSI、镜像仓库、Cloud Provider、集群自身的配置和管理等
对于云计算系统,系统API实际上处于系统设计的统领地位,正如本文前面所说,Kubernetes集群系统每支持一项新功能,引入一项新技术,一定会新引入对应的API对象,支持对该功能的管理操作,理解掌握的API,就好比抓住了Kubernetes系统的牛鼻子。Kubernetes系统API的设计有以下几条原则:
所有API应该是声明式的。正如前文所说,声明式的操作,相对于命令式操作,对于重复操作的效果是稳定的,这对于容易出现数据丢失或重复的分布式环境来说是很重要的。另外,声明式操作更容易被用户使用,可以使系统向用户隐藏实现的细节,隐藏实现的细节的同时,也就保留了系统未来持续优化的可能性。此外,声明式的API,同时隐含了所有的API对象都是名词性质的,例如Service、Volume这些API都是名词,这些名词描述了用户所期望得到的一个目标分布式对象。
API对象是彼此互补而且可组合的。这里面实际是鼓励API对象尽量实现面向对象设计时的要求,即“高内聚,松耦合”,对业务相关的概念有一个合适的分解,提高分解出来的对象的可重用性。事实上,Kubernetes这种分布式系统管理平台,也是一种业务系统,只不过它的业务就是调度和管理容器服务。
高层API以操作意图为基础设计。如何能够设计好API,跟如何能用面向对象的方法设计好应用系统有相通的地方,高层设计一定是从业务出发,而不是过早的从技术实现出发。因此,针对Kubernetes的高层API设计,一定是以Kubernetes的业务为基础出发,也就是以系统调度管理容器的操作意图为基础设计。
低层API根据高层API的控制需要设计。设计实现低层API的目的,是为了被高层API使用,考虑减少冗余、提高重用性的目的,低层API的设计也要以需求为基础,要尽量抵抗受技术实现影响的诱惑。
尽量避免简单封装,不要有在外部API无法显式知道的内部隐藏的机制。简单的封装,实际没有提供新的功能,反而增加了对所封装API的依赖性。内部隐藏的机制也是非常不利于系统维护的设计方式,例如StatefulSet和ReplicaSet,本来就是两种Pod集合,那么Kubernetes就用不同API对象来定义它们,而不会说只用同一个ReplicaSet,内部通过特殊的算法再来区分这个ReplicaSet是有状态的还是无状态。
API操作复杂度与对象数量成正比。这一条主要是从系统性能角度考虑,要保证整个系统随着系统规模的扩大,性能不会迅速变慢到无法使用,那么最低的限定就是API的操作复杂度不能超过O(N),N是对象的数量,否则系统就不具备水平伸缩性了。
API对象状态不能依赖于网络连接状态。由于众所周知,在分布式环境下,网络连接断开是经常发生的事情,因此要保证API对象状态能应对网络的不稳定,API对象的状态就不能依赖于网络连接状态。
尽量避免让操作机制依赖于全局状态,因为在分布式系统中要保证全局状态的同步是非常困难的。
控制逻辑应该只依赖于当前状态。这是为了保证分布式系统的稳定可靠,对于经常出现局部错误的分布式系统,如果控制逻辑只依赖当前状态,那么就非常容易将一个暂时出现故障的系统恢复到正常状态,因为你只要将该系统重置到某个稳定状态,就可以自信的知道系统的所有控制逻辑会开始按照正常方式运行。
假设任何错误的可能,并做容错处理。在一个分布式系统中出现局部和临时错误是大概率事件。错误可能来自于物理系统故障,外部系统故障也可能来自于系统自身的代码错误,依靠自己实现的代码不会出错来保证系统稳定其实也是难以实现的,因此要设计对任何可能错误的容错处理。
尽量避免复杂状态机,控制逻辑不要依赖无法监控的内部状态。因为分布式系统各个子系统都是不能严格通过程序内部保持同步的,所以如果两个子系统的控制逻辑如果互相有影响,那么子系统就一定要能互相访问到影响控制逻辑的状态,否则,就等同于系统里存在不确定的控制逻辑。
假设任何操作都可能被任何操作对象拒绝,甚至被错误解析。由于分布式系统的复杂性以及各子系统的相对独立性,不同子系统经常来自不同的开发团队,所以不能奢望任何操作被另一个子系统以正确的方式处理,要保证出现错误的时候,操作级别的错误不会影响到系统稳定性。
每个模块都可以在出错后自动恢复。由于分布式系统中无法保证系统各个模块是始终连接的,因此每个模块要有自我修复的能力,保证不会因为连接不到其他模块而自我崩溃。
每个模块都可以在必要时优雅地降级服务。所谓优雅地降级服务,是对系统鲁棒性的要求,即要求在设计实现模块时划分清楚基本功能和高级功能,保证基本功能不会依赖高级功能,这样同时就保证了不会因为高级功能出现故障而导致整个模块崩溃。根据这种理念实现的系统,也更容易快速地增加新的高级功能,因为不必担心引入高级功能影响原有的基本功能。
API对象是Kubernetes集群中的管理操作单元。Kubernetes集群系统每支持一项新功能,引入一项新技术,一定会新引入对应的API对象,支持对该功能的管理操作。例如副本集Replica Set对应的API对象是RS。
每个API对象都有3大类属性:元数据metadata、规范spec和状态status。元数据是用来标识API对象的,每个对象都至少有3个元数据:namespace,name和uid;除此以外还有各种各样的标签labels用来标识和匹配不同的对象,例如用户可以用标签env来标识区分不同的服务部署环境,分别用env=dev、env=testing、env=production来标识开发、测试、生产的不同服务。规范描述了用户期望Kubernetes集群中的分布式系统达到的理想状态(Desired State),例如用户可以通过复制控制器Replication Controller设置期望的Pod副本数为3;status描述了系统实际当前达到的状态(Status),例如系统当前实际的Pod副本数为2;那么复制控制器当前的程序逻辑就是自动启动新的Pod,争取达到副本数为3。
Kubernetes中所有的配置都是通过API对象的spec去设置的,也就是用户通过配置系统的理想状态来改变系统,这是Kubernetes重要设计理念之一,即所有的操作都是声明式(Declarative)的而不是命令式(Imperative)的。声明式操作在分布式系统中的好处是稳定,不怕丢操作或运行多次,例如设置副本数为3的操作运行多次也还是一个结果,而给副本数加1的操作就不是声明式的,运行多次结果就错了。
Kubernetes有很多技术概念,同时对应很多API对象,最重要的也是最基础的是Pod。Pod是在Kubernetes集群中运行部署应用或服务的最小单元,它是可以支持多容器的。Pod的设计理念是支持多个容器在一个Pod中共享网络地址和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。Pod对多容器的支持是K8最基础的设计理念。比如你运行一个操作系统发行版的软件仓库,一个Nginx容器用来发布软件,另一个容器专门用来从源仓库做同步,这两个容器的镜像不太可能是一个团队开发的,但是他们一块儿工作才能提供一个微服务;这种情况下,不同的团队各自开发构建自己的容器镜像,在部署的时候组合成一个微服务对外提供服务。
Pod是Kubernetes集群中所有业务类型的基础,可以看作运行在Kubernetes集群中的小机器人,不同类型的业务就需要不同类型的小机器人去执行。目前Kubernetes中的业务主要可以分为长期伺服型(long-running)、批处理型(batch)、节点后台支撑型(node-daemon)和有状态应用型(stateful application);分别对应的小机器人控制器为Deployment、Job、DaemonSet和StatefulSet,本文后面会一一介绍。
RC是Kubernetes集群中最早的保证Pod高可用的API对象。通过监控运行中的Pod来保证集群中运行指定数目的Pod副本。指定的数目可以是多个也可以是1个;少于指定数目,RC就会启动运行新的Pod副本;多于指定数目,RC就会杀死多余的Pod副本。即使在指定数目为1的情况下,通过RC运行Pod也比直接运行Pod更明智,因为RC也可以发挥它高可用的能力,保证永远有1个Pod在运行。RC是Kubernetes较早期的技术概念,只适用于长期伺服型的业务类型,比如控制小机器人提供高可用的Web服务。
部署表示用户对Kubernetes集群的一次更新操作。部署是一个比RS应用模式更广的API对象,可以是创建一个新的服务,更新一个新的服务,也可以是滚动升级一个服务。滚动升级一个服务,实际是创建一个新的RS,然后逐渐将新RS中副本数增加到理想状态,将旧RS中的副本数减小到0的复合操作;这样一个复合操作用一个RS是不太好描述的,所以用一个更通用的Deployment来描述。以Kubernetes的发展方向,未来对所有长期伺服型的的业务的管理,都会通过Deployment来管理。
RC、RS和Deployment只是保证了支撑服务的微服务Pod的数量,但是没有解决如何访问这些服务的问题。一个Pod只是一个运行服务的实例,随时可能在一个节点上停止,在另一个节点以一个新的IP启动一个新的Pod,因此不能以确定的IP和端口号提供服务。要稳定地提供服务需要服务发现和负载均衡能力。服务发现完成的工作,是针对客户端访问的服务,找到对应的的后端服务实例。在K8集群中,客户端需要访问的服务就是Service对象。每个Service会对应一个集群内部有效的虚拟IP,集群内部通过虚拟IP访问一个服务。在Kubernetes集群中微服务的负载均衡是由Kube-proxy实现的。Kube-proxy是Kubernetes集群内部的负载均衡器。它是一个分布式代理服务器,在Kubernetes的每个节点上都有一个;这一设计体现了它的伸缩性优势,需要访问服务的节点越多,提供负载均衡能力的Kube-proxy就越多,高可用节点也随之增多。与之相比,我们平时在服务器端做个反向代理做负载均衡,还要进一步解决反向代理的负载均衡和高可用问题。
Job是Kubernetes用来控制批处理型任务的API对象。批处理业务与长期伺服业务的主要区别是批处理业务的运行有头有尾,而长期伺服业务在用户不停止的情况下永远运行。Job管理的Pod根据用户的设置把任务成功完成就自动退出了。成功完成的标志根据不同的spec.completions策略而不同:单Pod型任务有一个Pod成功就标志完成;定数成功型任务保证有N个任务全部成功;工作队列型任务根据应用确认的全局成功而标志成功。
长期伺服型和批处理型服务的核心在业务应用,可能有些节点运行多个同类业务的Pod,有些节点上又没有这类Pod运行;而后台支撑型服务的核心关注点在Kubernetes集群中的节点(物理机或虚拟机),要保证每个节点上都有一个此类Pod运行。节点可能是所有集群节点也可能是通过nodeSelector选定的一些特定节点。典型的后台支撑型服务包括,存储,日志和监控等在每个节点上支持Kubernetes集群运行的服务。
Kubernetes在1.3版本里发布了Alpha版的PetSet功能,在1.5版本里将PetSet功能升级到了Beta版本,并重新命名为StatefulSet,最终在1.9版本里成为正式GA版本。在云原生应用的体系里,有下面两组近义词;第一组是无状态(stateless)、牲畜(cattle)、无名(nameless)、可丢弃(disposable);第二组是有状态(stateful)、宠物(pet)、有名(having name)、不可丢弃(non-disposable)。RC和RS主要是控制提供无状态服务的,其所控制的Pod的名字是随机设置的,一个Pod出故障了就被丢弃掉,在另一个地方重启一个新的Pod,名字变了。名字和启动在哪儿都不重要,重要的只是Pod总数;而StatefulSet是用来控制有状态服务,StatefulSet中的每个Pod的名字都是事先确定的,不能更改。StatefulSet中Pod的名字的作用,并不是《千与千寻》的人性原因,而是关联与该Pod对应的状态。
对于RC和RS中的Pod,一般不挂载存储或者挂载共享存储,保存的是所有Pod共享的状态,Pod像牲畜一样没有分别(这似乎也确实意味着失去了人性特征);对于StatefulSet中的Pod,每个Pod挂载自己独立的存储,如果一个Pod出现故障,从其他节点启动一个同样名字的Pod,要挂载上原来Pod的存储继续以它的状态提供服务。
适合于StatefulSet的业务包括数据库服务MySQL和PostgreSQL,集群化管理服务ZooKeeper、etcd等有状态服务。StatefulSet的另一种典型应用场景是作为一种比普通容器更稳定可靠的模拟虚拟机的机制。传统的虚拟机正是一种有状态的宠物,运维人员需要不断地维护它,容器刚开始流行时,我们用容器来模拟虚拟机使用,所有状态都保存在容器里,而这已被证明是非常不安全、不可靠的。使用StatefulSet,Pod仍然可以通过漂移到不同节点提供高可用,而存储也可以通过外挂的存储来提供高可靠性,StatefulSet做的只是将确定的Pod与确定的存储关联起来保证状态的连续性。
Kubernetes在1.3版本里发布了beta版的Federation功能。在云计算环境中,服务的作用距离范围从近到远一般可以有:同主机(Host,Node)、跨主机同可用区(Available Zone)、跨可用区同地区(Region)、跨地区同服务商(Cloud Service Provider)、跨云平台。Kubernetes的设计定位是单一集群在同一个地域内,因为同一个地区的网络性能才能满足Kubernetes的调度和计算存储连接要求。而联合集群服务就是为提供跨Region跨服务商Kubernetes集群服务而设计的。
每个Kubernetes Federation有自己的分布式存储、API Server和Controller Manager。用户可以通过Federation的API Server注册该Federation的成员Kubernetes Cluster。当用户通过Federation的API Server创建、更改API对象时,Federation API Server会在自己所有注册的子Kubernetes Cluster都创建一份对应的API对象。在提供业务请求服务时,Kubernetes Federation会先在自己的各个子Cluster之间做负载均衡,而对于发送到某个具体Kubernetes Cluster的业务请求,会依照这个Kubernetes Cluster独立提供服务时一样的调度模式去做Kubernetes Cluster内部的负载均衡。而Cluster之间的负载均衡是通过域名服务的负载均衡来实现的。
Federation V1的设计是尽量不影响Kubernetes Cluster现有的工作机制,这样对于每个子Kubernetes集群来说,并不需要更外层的有一个Kubernetes Federation,也就是意味着所有现有的Kubernetes代码和机制不需要因为Federation功能有任何变化。
目前正在开发的Federation V2,在保留现有Kubernetes API的同时,会开发新的Federation专用的API接口,详细内容可以在这里找到。
Kubernetes集群中的存储卷跟Docker的存储卷有些类似,只不过Docker的存储卷作用范围为一个容器,而Kubernetes的存储卷的生命周期和作用范围是一个Pod。每个Pod中声明的存储卷由Pod中的所有容器共享。Kubernetes支持非常多的存储卷类型,特别的,支持多种公有云平台的存储,包括AWS,Google和Azure云;支持多种分布式存储包括GlusterFS和Ceph;也支持较容易使用的主机本地目录emptyDir, hostPath和NFS。Kubernetes还支持使用Persistent Volume Claim即PVC这种逻辑存储,使用这种存储,使得存储的使用者可以忽略后台的实际存储技术(例如AWS,Google或GlusterFS和Ceph),而将有关存储实际技术的配置交给存储管理员通过Persistent Volume来配置。
1.4.9.1 持久存储卷(Persistent Volume,PV)和持久存储卷声明(Persistent Volume Claim,PVC)
PV和PVC使得Kubernetes集群具备了存储的逻辑抽象能力,使得在配置Pod的逻辑里可以忽略对实际后台存储技术的配置,而把这项配置的工作交给PV的配置者,即集群的管理者。存储的PV和PVC的这种关系,跟计算的Node和Pod的关系是非常类似的;PV和Node是资源的提供者,根据集群的基础设施变化而变化,由Kubernetes集群管理员配置;而PVC和Pod是资源的使用者,根据业务服务的需求变化而变化,有Kubernetes集群的使用者即服务的管理员来配置。
Kubernetes集群中的计算能力由Node提供,最初Node称为服务节点Minion,后来改名为Node。Kubernetes集群中的Node也就等同于Mesos集群中的Slave节点,是所有Pod运行所在的工作主机,可以是物理机也可以是虚拟机。不论是物理机还是虚拟机,工作主机的统一特征是上面要运行kubelet管理节点上运行的容器。
Secret是用来保存和传递密码、密钥、认证凭证这些敏感信息的对象。使用Secret的好处是可以避免把敏感信息明文写在配置文件里。在Kubernetes集群中配置和使用服务不可避免的要用到各种敏感信息实现登录、认证等功能,例如访问AWS存储的用户名密码。为了避免将类似的敏感信息明文写在所有需要使用的配置文件中,可以将这些信息存入一个Secret对象,而在配置文件中通过Secret对象引用这些敏感信息。这种方式的好处包括:意图明确,避免重复,减少暴漏机会。
顾名思义,用户帐户为人提供账户标识,而服务账户为计算机进程和Kubernetes集群中运行的Pod提供账户标识。用户帐户和服务帐户的一个区别是作用范围;用户帐户对应的是人的身份,人的身份与服务的namespace无关,所以用户账户是跨namespace的;而服务帐户对应的是一个运行中程序的身份,与特定namespace是相关的。
命名空间为Kubernetes集群提供虚拟的隔离作用,Kubernetes集群初始有两个命名空间,分别是默认命名空间default和系统命名空间kube-system,除此以外,管理员可以可以创建新的命名空间满足需要。
Kubernetes在1.3版本中发布了alpha版的基于角色的访问控制(Role-based Access Control,RBAC)的授权模式。相对于基于属性的访问控制(Attribute-based Access Control,ABAC),RBAC主要是引入了角色(Role)和角色绑定(RoleBinding)的抽象概念。在ABAC中,Kubernetes集群中的访问策略只能跟用户直接关联;而在RBAC中,访问策略可以跟某个角色关联,具体的用户在跟一个或多个角色相关联。显然,RBAC像其他新功能一样,每次引入新功能,都会引入新的API对象,从而引入新的概念抽象,而这一新的概念抽象一定会使集群服务管理和使用更容易扩展和重用。
从Kubernetes的系统架构、技术概念和设计理念,我们可以看到Kubernetes系统最核心的两个设计理念:一个是容错性,一个是易扩展性。容错性实际是保证Kubernetes系统稳定性和安全性的基础,易扩展性是保证Kubernetes对变更友好,可以快速迭代增加新功能的基础。
按照分布式系统一致性算法Paxos发明人计算机科学家Leslie Lamport的理念,一个分布式系统有两类特性:安全性Safety和活性Liveness。安全性保证系统的稳定,保证系统不会崩溃,不会出现业务错误,不会做坏事,是严格约束的;活性使得系统可以提供功能,提高性能,增加易用性,让系统可以在用户“看到的时间内”做些好事,是尽力而为的。Kubernetes系统的设计理念正好与Lamport安全性与活性的理念不谋而合,也正是因为Kubernetes在引入功能和技术的时候,非常好地划分了安全性和活性,才可以让Kubernetes能有这么快版本迭代,快速引入像RBAC、Federation和PetSet这种新功能。
安装包
1.1.1 环境说明
注:如果是克隆主机请删除网卡中的UUID并重启网络服务。
IP |
主机名 |
角色 |
内存 |
10.10.10.20 |
node20 |
master01 |
4G |
10.10.10.20 |
node21 |
master02 |
4G |
10.10.10.20 |
node22 |
master03 |
4G |
10.10.10.10 |
node10 |
minion01 |
4G |
10.10.10.11 |
node11 |
minion02 |
4G |
10.10.10.12 |
node12 |
minion03 |
4G |
10.10.10.40 |
node40 |
ha01 |
4G |
10.10.10.41 |
node41 |
ha02 |
4G |
10.10.10.100 |
VIP |
----- |
------ |
21.2 所有节点关闭防火墙、selinux、swap
#关闭防火墙
systemctl stop firewalld && systemctl disable firewalld
#关闭selinux
sed-ri's/(^SELINUX=).*/\1disabled/' /etc/selinux/config
setenforce 0
#关闭swap分区
#如果不关闭,初始化k8s时,会报以下错误:
#Kubelet: Running with swap on is not supported, please disable swap! or set --fail-swap-on flag to false.
sed-ri's@(^.*swap *swap.*0 0$)@#\1@' /etc/fstab
swapoff -a
2.1.3 配置地址映射
echo'10.10.10.20 node20' >> /etc/hosts
echo'10.10.10.21 node21' >> /etc/hosts
echo'10.10.10.22 node22' >> /etc/hosts
echo'10.10.10.10 node10' >> /etc/hosts
echo'10.10.10.11 node11' >> /etc/hosts
echo'10.10.10.12 node12' >> /etc/hosts
echo'10.10.10.40 node40' >> /etc/hosts
echo'10.10.10.41 node41' >> /etc/hosts
2.1.4 初始化内核参数
modprobe br_netfilter
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables =1
net.bridge.bridge-nf-call-iptables =1
net.ipv4.ip_nonlocal_bind =1
net.ipv4.ip_forward =1
EOF
sysctl --system
# 安装haproxy和keepalived服务
yum -y install haproxy keepalived
2.2.1 配置keepalived
# 修改ha01的keepalived配置文件
vim /etc/keepalived/keepalived.conf
global_defs {
.....
router_id ha01
# vrrp_strict
.....
}
vrrp_script chk_apiserver {
script "/etc/keepalived/check_apiserver.sh"
interval 5
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51 #修改对应的id
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.10.10.100 dev ens33 label ens33:0 #添加vip
}
track_script {
chk_apiserver
}
}
# 修改ha02的keepalived配置文件
vim /etc/keepalived/keepalived.conf
global_defs {
.....
router_id ha02
# vrrp_strict
.....
}
vrrp_script chk_apiserver {
script "/etc/keepalived/check_apiserver.sh"
interval 5
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51 #修改对应的id
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.10.10.100 dev ens33 label ens33:0 #添加vip
}
track_script {
chk_apiserver
}
}
2.2.2 检测脚本
## 检查ha进程脚本
vim /etc/keepalived/check_apiserver.sh
#!/bin/bash
err=0
for k in$(seq 1 3)
do
check_code=$(pgrep haproxy)
if [[ $check_code=="" ]]; then
err=$(expr $err + 1)
sleep1
continue
else
err=0
break
fi
done
if [[ $err !="0" ]]; then
echo"systemctl stop keepalived"
/usr/local/bin/systemctl stop keepalived
exit1
else
exit0
fi
#给脚本授权
chmod+x /etc/keepalived/check_apiserver.sh
2.2.3 配置haproxy
mv /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak
cat > /etc/haproxy/haproxy.cfg << EOF
global
log /dev/log local0
log /dev/log local1 notice
pidfile /var/run/haproxy.pid
chroot /var/lib/haproxy
stats socket /var/run/haproxy-admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
nbproc 1
defaults
log global
timeout connect 5000
timeout client 10m
timeout server 10m
listen admin_stats
bind 0.0.0.0:10080
mode http
log 127.0.0.1 local0 err
stats refresh 30s
stats uri /status
stats realm welcome login\ Haproxy
stats auth admin:123456
stats hide-version
stats admin if TRUE
listen kube-master
bind 0.0.0.0:8443
mode tcp
option tcplog
balance source
server master1 10.10.10.20:6443 check inter 2000 fall 2 rise 2 weight 1
server master2 10.10.10.21:6443 check inter 2000 fall 2 rise 2 weight 1
server master2 10.10.10.22:6443 check inter 2000 fall 2 rise 2 weight 1
EOF
2.3.1 安装Kubernetes管理工具
# 在master和node上安装kubeadm组件,用于后期安装k8s使用,现在还没有安装k8s,注意安装kubelet的软件包在我们离线yum源中已经包含,可以直接安装,如需安装其他版本k8s可以配置在线源进行安装。
yum install -y kubelet-1.25.5 kubeadm-1.25.5 kubectl-1.25.5
systemctl enable kubelet && systemctl start kubelet
# 注:每个软件包的作用
## kubelet :运行在集群所有节点上,用于启动Pod和容器等对象的工具
## kubeadm :用于初始化集群,启动集群的命令工具
## kubectl :用于和集群通信的命令行,通过kubectl可以部署和管理应用,查看各种资源,创建、删除和更新各种组件
2.3.2 安装containerd容器运行时
harbor 配置https
#安装containerd
yum install containerd -y
containerd config default > /etc/containerd/config.toml
#修改配置10.10.10.2/k8s/pause:3.8
#首先我们修改默认的pause镜像为国内的地址,替换 [plugins."io.containerd.grpc.v1.cri"] 下面的 sandbox_image:
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "10.10.10.2/k8s/pause:3.8"
#配置私有仓库地址
#查看harbor搭建仓库https
all="node20 node21 node22 node10 node11 node12"
for i in $all;do
scp /etc/containerd/config.toml $i:/etc/containerd/config.toml
done
#启动
systemctl enable --now containerd.service
#测试
crictl info
2.3.3 使用kubeadm初始化集群
kubeadm init --control-plane-endpoint 10.10.10.100:8443 --image-repository 10.10.10.2/k8s --kubernetes-version v1.25.5 --service-cidr 10.100.0.0/16 --pod-network-cidr 10.244.0.0/16 --upload-certs
# --control-plane-endpoint 指定集群IP,这个IP为VIP,代理所有apiserver的地址
# --image-repository 指定镜像仓库(默认为:registry.k8s.io)
# --kubernetes-version 安装kubernetes的版本
# --service-cidr 为服务的虚拟IP地址另外指定IP地址段
# --pod-network-cidr 指明pod网络可以使用的IP地址段。如果设置了这个参数,控制平面将会为每一个节点自动分配CIDRs。
# --upload-certs 将主节点的证书上传到kubeadm-certs Secret。
# 保存一个证书,这样kubectl命令可以使用这个证书对k8s集群进行管理
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
# 保存kubeadm join 10.10.10.100:8443开头的两段字符
# 其中带有--control-plane选项的为主节点加入方式
# 加入完成后查看主机
kubectl get nodes
NAME STATUS ROLES AGE VERSION
node10 NotReady 5s v1.25.5
node11 NotReady 2s v1.25.5
node12 NotReady 25s v1.25.5
node20 NotReady control-plane 10m v1.25.5
node21 NotReady control-plane 4m4s v1.25.5
node22 NotReady control-plane 3m47s v1.25.5
# 给主机添加权限标签
kubectl label nodes node20 node-role.kubernetes.io/master=master01
kubectl label nodes node21 node-role.kubernetes.io/master=master02
kubectl label nodes node22 node-role.kubernetes.io/master=master03
kubectl label nodes node10 node-role.kubernetes.io/node=monion01
kubectl label nodes node11 node-role.kubernetes.io/node=monion02
kubectl label nodes node12 node-role.kubernetes.io/node=monion03
#此时集群状态还是NotReady状态,因为网络组建没有启动。
3.1.1 Calico简介
Calico 是一种容器之间互通的网络方案。在虚拟化平台中,比如 OpenStack、Docker 等都需要实现 workloads 之间互连,但同时也需要对容器做隔离控制。而在多数的虚拟化平台实现中,通常都使用二层隔离技术来实现容器的网络,这些二层的技术有一些弊端,比如需要依赖 VLAN、bridge 和隧道等技术,其中 bridge 带来了复杂性,vlan 隔离和 tunnel 隧道在拆包或加包头时,则消耗更多的资源并对物理环境也有要求。随着网络规模的增大,整体会变得越加复杂。
Calico把 Host 当作 Internet 中的路由器,使用 BGP 同步路由,并使用 iptables 来做安全访问策略。
设计思想:Calico 不使用隧道或 NAT 来实现转发,而是巧妙的把所有二三层流量转换成三层流量,并通过host 上路由配置完成跨 Host 转发。
设计优势
1.更优的资源利用
二层网络使用 VLAN 隔离技术,最多有 4096 个规格限制,即便可以使用 vxlan 解决,但 vxlan 又带来了隧道开销的新问题。而 Calico 不使用 vlan 或 vxlan 技术,使资源利用率更高。
2.可扩展性
Calico 使用与 Internet 类似的方案,Internet 的网络比任何数据中心都大,Calico 同样天然具有可扩展性。
3.简单而更容易 debug
因为没有隧道,意味着 workloads 之间路径更短更简单,配置更少,在 host 上更容易进行 debug 调试。
4.更少的依赖
Calico 仅依赖三层路由可达。
5.可适配性
Calico 较少的依赖性使它能适配所有 VM、Container或者混合环境场景。
3.1.2 k8s组网方案对比
flannel方案: 需要在每个节点上把发向容器的数据包进行封装后,再用隧道将封装后的数据包发送到运行着目标Pod的node节点上。目标node节点再负责去掉封装,将去除封装的数据包发送到目标Pod上。数据通信性能则大受影响
Overlay方案:在下层主机网络的上层,基于隧道封装机制,搭建层叠网络,实现跨主机的通信;Overlay无疑是架构最简单清晰的网络实现机制,但数据通信性能则大受影响。
calico方案:在k8s多个网路解决方案中选择了延迟表现最好的-calico方案
总结:Flannel网络非常类似于Docker网络的Overlay驱动,基于二层的层叠网络。
层叠网络的优势:
对底层网络依赖较少,不管底层是物理网络还是虚拟网络,对层叠网络的配置管理影响较少;
配置简单,逻辑清晰,易于理解和学习,非常适用于开发测试等对网络性能要求不高的场景。
层叠网络的劣势:
网络封装是一种传输开销,对网络性能会有影响,不适用于对网络性能要求高的生产场景;
由于对底层网络结构缺乏了解,无法做到真正有效的流量工程控制,也会对网络性能产生影响;
某些情况下也不能完全做到与下层网络无关,例如隧道封装会对网络的MTU限制产生影响。
Calico,就是非层叠网络:
Calico网络的优势
没有隧道封装的网络开销;
相比于通过Overlay构成的大二层层叠网络,用iBGP构成的扁平三层网络扩展模式更符合传统IP网络的分布式结构;
不会对物理层网络的二层参数如MTU引入新的要求。
Calico网络的劣势:
最大的问题是不容易支持多租户,由于没有封装,所有的虚拟机或者容器只能通过真实的IP来区分自己,这就要求所有租户的虚拟机或容器统一分配一个地址空间;而在典型的支持多租户的网络环境中,每个租户可以分配自己的私有网络地址,租户之间即使地址相同也不会有冲突;
不容易与其他基于主机路由的网络应用集成。
总结:calico 提供一种不同物理机之间,docker容器高速通信的组网方案
了解一下Calico架构图
Calico网络模型主要工作组件:
1.Felix:运行在每一台 Host 的 agent 进程,主要负责网络接口管理和监听、路由、ARP 管理、ACL 管理和同步、状态上报等。felix [ˈfiːlɪks] 费力克斯制导炸弹
2.etcd:分布式键值存储,相当于k8s集群中的数据库,存储着Calico网络模型中IP地址等相关信息。
3.BGP Client(BIRD):Calico 为每一台 Host 部署一个 BGP Client,即每台host上部署一个BIRD。BIRD 是一个单独的持续发展的项目,实现了众多动态路由协议比如 BGP、OSPF、RIP 等。在 Calico 的角色是监听 Host 上由 Felix 注入的路由信息,然后通过 BGP 协议广播告诉剩余 Host 节点,从而实现网络互通。
4.BGP Route Reflector:在大型网络规模中,如果仅仅使用 BGP client 形成 mesh 全网互联的方案就会导致规模限制,因为所有节点之间俩俩互联,需要 N^2 个连接,为了解决这个规模问题,可以采用 BGP 的 Router Reflector 的方法,使所有 BGP Client 仅与特定 RR 节点互联并做路由同步,从而大大减少连接数。[meʃ] Reflector [rɪˈflektə(r)] 反射器
3.1.3 安装Calico网络组件
# https://github.com/projectcalico/calico/tree/v3.24.5/manifests
vim calico.yaml
#修改配置
- name: CALICO_IPV4POOL_CIDR
value: "10.100.0.0/16"
kubectl apply -f calico.yaml
#查看pod运行情况
[root@node20 ~]# kubectl get pods -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
calico-kube-controllers-d8fb77c4-2wdvt 1/1 Running 0 61m 10.100.103.130 node10
calico-node-8plhr 1/1 Running 0 61m 10.10.10.11 node11
calico-node-9mv5r 1/1 Running 0 61m 10.10.10.22 node22
calico-node-ct8gr 1/1 Running 0 61m 10.10.10.12 node12
calico-node-mfpf7 1/1 Running 0 61m 10.10.10.20 node20
calico-node-ppt88 1/1 Running 0 61m 10.10.10.10 node10
calico-node-tqbdr 1/1 Running 0 61m 10.10.10.21 node21
2.2.1 metrics介绍 metrics-server 是 Kubernetes 监控体系中的核心组件之一,它负责从 kubelet 收集资源指标,然后对这些指标监控数据进行聚合(依赖 kube-aggregator),并在 Kubernetes Apiserver 中通过 Metrics API ( /apis/metrics.k8s.io/)公开暴露它们,但是 metrics-server 只存储最新的指标数据(CPU/Memory),
你的 kube-apiserver 要能访问到 metrics-server;
需要 kube-apiserver 启用聚合层;
组件要有认证配置并且绑定到 metrics-server;
Pod / Node 指标需要由 Summary API 通过 kubelet 公开。
3.2.2 安装监控组件metrics
#下载地址wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.1/components.yaml
#修改配置
vim components.yaml
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
image: 10.10.10.2/metrics-servermetrics-server:v0.6.2
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /tmp
name: tmp-dir
- mountPath: /etc/kubernetes/pki
name: ca-ssl
nodeSelector:
kubernetes.io/os: linux
priorityClassName: system-cluster-critical
serviceAccountName: metrics-server
volumes:
- emptyDir: {}
name: tmp-dir
- name: ca-ssl
hostPath:
path: /etc/kubernetes/pki
#创建
kubectl apply -f components.yaml
#验证:
[root@node30 ~]# kubectl get pod -n kube-system metrics-server-94bfb5978-v2sbd
NAME READY STATUS RESTARTS AGE
metrics-server-94bfb5978-v2sbd 1/1 Running 0 66m
[root@node30 ~]# kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
node10 269m 3% 1214Mi 15%
node11 215m 2% 1138Mi 14%
node12 215m 2% 1048Mi 13%
node20 611m 7% 1962Mi 51%
node21 484m 6% 1184Mi 62%
node22 533m 6% 1843Mi 48%
# 安装calico客户端工具
chmod +x calicoctl-linux-amd64 && mv calicoctl-linux-amd64 /usr/local/bin/calicoctl
#创建配置文件
mkdir /etc/calico -p
cat >/etc/calico/calicoctl.cfg <
# 安装etcdctl客户端
tar xf etcd-v3.5.6-linux-amd64.tar.gz
mv etcd-v3.5.6-linux-amd64/etcdctl /usr/local/bin/
##配置etcdctl使用v3 api
cat > /etc/profile.d/etcdctl.sh <
Dashboard 是基于网页的 Kubernetes 用户界面。 你可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中,也可以对容器应用排错,还能管理集群资源。 你可以使用 Dashboard 获取运行在集群中的应用的概览信息,也可以创建或者修改 Kubernetes 资源 (如 Deployment,Job,DaemonSet 等等)。 例如,你可以对 Deployment 实现弹性伸缩、发起滚动升级、重启 Pod 或者使用向导创建新的应用。
Dashboard 同时展示了 Kubernetes 集群中的资源状态信息和所有报错信息。
默认情况下不会部署 Dashboard。可以通过以下命令部署:
#github下载,recommended.yaml文件namespace之后添加内容
vim recommended.yaml
#创建一个admin-user用户用于登录
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
kubectl apply -f recommended.yaml
#修改集群访问方式
kubectl edit services kubernetes-dashboard -n kubernetes-dashboard
将:type: ClusterIP
改:type: NodePort
#保存后再次查看会有一个随机的可用于访问的端口
kubectl get services kubernetes-dashboard -n kubernetes-dashboard
#创建用于登录的admin-user的token
kubectl -n kubernetes-dashboard create token admin-user
#记录下获取到的token字符串
#访问https://10.10.10.30:port
#输入上面记录的token