目录
一、概述
Kubernetes是谷歌以Borg为前身,基于谷歌15年生产环境经验开源的一个项目。Kubernetes致力于提供跨主机集群的自动部署、扩展、高可用以及运行应用程序容器的平台,其遵循主从式架构设计,其组件可以分为工作节点(Node)组件和控制平面组件。
Kubernetes Master是集群的主要控制单元,用于管理其工作负载并指导整个系统的通信。Kubernetes控制平面由各自的进程组成,每个组件都可以在单个主节点上运行,也可以在支持高可用集群的多个节点上运行。本文主要介绍Kubernetes的重要概念和相关组件。
二、为什么要用Kubernetes
2.1 从技术层面分析
2.1.1 问题解答
很多人会有疑问,有Docker了为什么还用Kubernetes?
在业务开始进行容器化时,前期需要容器化的项目可能并不多,涉及的容器也并不多,此时基于Docker容器直接部署至宿主机也能实现自己的需求。
但是随着项目越来越多,管理的容器也越来越多,此时使用“裸容器”部署的方式管理起来就显得很吃力,并且随着业务量的增加,会明显体会到“裸容器”的不足。
2.1.2 Docker等“裸容器”的不足
2.1.2.1 宕机无法自动恢复
宿主机宕机造成该宿主机上的容器不可用,且无法自动恢复。
2.1.2.2 健康检查不到位
容器明明在运行,接口就是不通(健康检查做得不到位)。
2.1.2.3 部署、回滚、扩容问题
应用程序部署、回滚、扩缩容困难。
2.1.2.4 运维难
成百上千的容器和涉及的端口难以维护。
2.1.3 总结
上面的问题只是做一个简单的罗列,真正使用时还有很多其他的问题。可能大家也体验过像docker-compose、docker-swarm等编排工具,但这些工具的功能和Kubernetes的功能还是相差甚远,所以注定Kubernetes编排工具将成为主流的容器编排工具。
2.2 从开发人员层面分析
2.2.1 分析日志
2.2.1.1 使用Kubernetes前
由于公司业务多,开发环境、测试环境、预生产环境和生产环境都是隔离的,而且除了生产环境,为了节省成本,其他环境可能没有进行日志收集。在没有用Kubernetes的时候,查看线下测试的日志,需要开发或者测试人员找到对应的机器,再找到对应的容器,才能查看日志。
2.2.1.2 使用Kubernetes后
在使用Kubernetes之后,开发和测试人员直接在Kubernetes的Dashboard上找到对应的Namespace,即可定位到业务的容器,然后可以直接通过控制台查看到对应的日志,大大降低了操作时间。
2.2.2 代码发布
2.2.2.1 使用Kubernetes前
在没有使用容器之前,使用传统的运维方式部署,比如Tomcat容器部署,Jetty 容器部署,都是运维通过远程工具ssh连接上服务器去部署,即使使用了Docker等容器,也是需要ssh连接到服务器使用Docker命令启动;即使使用了docker-compose、docker-swarm等编排工具进行编排任务的管理,也是一样需要连接到服务器进行部署,发布、回滚以及蓝绿发布、金丝雀发布等都变得不可控,运维比较消耗人员工时和人员精力。
2.2.2.2 使用Kubernetes后
把应用部署到Kubernetes之后,代码的发布、回滚以及蓝绿发布、金丝雀发布等都变得简单可控,不仅加快了业务代码的迭代速度,而且全程无须人工干预。生产环境可以使用Jenkins、GitRunner等工具进行发版或回滚等。从开发环境到测试环境,最后到生产环境,完全遵守一次构建,多集群、多环境部署,通过不同的启动参数、不同的环境变量、不同的配置文件区分不同的环境。
2.2.3 环境搭建
2.2.3.1 使用Kubernetes前
在测试过程中,可能同时存在多套环境,当然也会创建其他环境或临时环境,之前测试环境的创建需要找运维人员或者自行手工搭建。
2.2.3.2 使用Kubernetes后
在迁移至Kubernetes集群后,开发人员如果需要新的环境,无须再找运维,只需要在Jenkins上点点鼠标即可在Kubernetes集群上创建一套新的测试环境。
2.3 从运维人员层面分析
2.3.1 使用Kubernetes前
对于运维人员,可能经常因为一些重复、烦琐的工作感觉厌倦,比如一个项目需要一套新的测试环境,另一个项目需要迁移测试环境至其他平台。传统架构可能需要装系统、装依赖环境、部署域名、开通权限等,这一整套下来,不仅耗时,而且可能会因为有某些遗漏而造成诸多问题。
在传统架构体系下,公司业务故障可能是因为基础环境不一致、依赖不一致、端口冲突等问题。
另外,也有可能公司业务由于服务器宕机、网络等问题造成服务不可用,此类情况均需要运维人员及时去修复。
业务应用的扩容和缩容都需要人工去处理,从采购服务器、上架到部署依赖环境,不仅需要大量的人力物力,而且非常容易在中间过程出现问题,又要花费大量的时间去查找问题。
在反向代理配置方面,可能对Nginx的配置规则并不熟悉,一些高级的功能也很难实现。
在负载均衡方面,之前负载均衡可能是Nginx、LVS、HAProxy、F5等,每次添加删除节点时,都需要手动去配置前端负载均衡,手动去匹配后端节点。
在应用端口方面,传统架构中,一台服务器可能跑了很多进程,每个进程都有一个端口,需要人为地去配置端口,并且还需要考虑端口冲突的问题,如果有防火墙的话,还需要配置防火墙。
2.3.2 使用Kubernetes后
直接使用Kubernetes包管理工具,一键式部署一套新的测试环境,甚至全程无须自己干预,开发人员通过Jenkins或者自动化运维平台即可一键式创建,大大降低了运维成本。
现在使用Docker镜像部署,Kubernetes进行编排,所有的依赖、基础都是一样的,并且环境的自动化扩容、健康检查、容灾、恢复都是全自动的,大大减少了因为这类基础问题引发的故障。
可以利用Kubernetes的弹性计算一键式扩容和缩容,不仅大大提高了运维效率,而且还节省了不少的服务器资源,提高了资源利用率。
在Kubernetes上,利用Kubernetes的Ingress即可简单地实现那些复杂的逻辑,并且不会再遇到Nginx少加一个斜杠和多加一个斜杠的问题。
使用Kubernetes进行编排服务时,使用Kubernetes内部的Service即可实现自动管理节点,并且支持自动扩容、缩容。
在Kubernetes中,端口统一管理、统一配置,每个应用的端口都可以设置成一样的,之后通过Service进行负载均衡,大大降低了端口管理的复杂度和端口冲突。
三、Kubernetes带来的挑战
Kubernetes从诞生至今,一路突飞猛进,在容器编排的领域里过三关斩六将,最终拿下了容器编排的冠军宝座,成为最无可替代、不可撼动的佼佼者,但是针对Kubernetes的学习和使用始终是一个很大的难题。
首先Kubernetes本身的学习就很困难,因为Kubernetes概念太多,涉及的知识面也非常广泛,可能学习了一个月也无法入门,甚至连集群也搭建不出来,使人望而却步。
并且Kubernetes对运维的技术能力要求也比较高,因为运维不仅仅局限于传统运维,有时候可能要修改业务代码、制定业务上线体系、给研发人员在开发应用中给出更好的建议等。
需要掌握的知识也有很多,可能需要掌握公司内所有使用到的代码,比如代码如何进行编译、如何正确发布、如何修改代码配置文件等,这对于运维人员也是一种挑战。
Kubernetes之所以被叫作K8s,业界有两种说法,通俗的说法是k和s之间有8个字母;另一种说法是K8s集群至少需要搭建8遍才能搭建成功。
当然,在实际使用时,可能不止8遍。Kubernetes的诞生把运维从传统运维转变到了DevOps方向,需要面临的问题更多,需要面临的新技术也很多,但是当真正掌握了Kubernetes的核心和设计理念,就会受益终身。
四、Kubernetes架构解析
4.1 Kubernetes的架构图
由图可知,Kubernetes架构可简单分为主(Master)节点、从(工作/Worker/Node)节点和数据库Etcd。其中主节点为集群的控制单元,一般不会运行业务应用程序,主要包含的组件有Kube-APIServer、Kube-ControllerManager、Kube-Scheduler。
从节点为工作节点,也就是部署应用程序容器的节点,主要包含的组件有Kubelet、Kube-Proxy,当然如果Master节点也要部署容器,也会包含这两个组件。
同时,可以看出一个集群中可以有很多Node节点,用以保证集群容器的分布式部署用于实现业务的高可用性,也可以有很多Master节点,之后通过一个负载均衡保证集群控制节点的高可用。
负载均衡可以使用软件负载均衡Nginx/LVS/HAProxy+KeepAlived或者硬件负载均衡F5等,通过负载均衡对Kube-APIServer提供的VIP即可实现Master节点的高可用,其他组件通过该VIP连接至Kube-APIServer。
Etcd集群可以和Master节点部署在同一个宿主机,也可以单独部署,生产环境建议部署大于3的奇数台Etcd节点实现Etcd集群的高可用。
4.2 Master节点
4.2.1 概述
Master节点是Kubernetes集群的控制节点,在生产环境中不建议部署集群核心组件外的任何容器(在Kubeadm安装方式下,系统组件以容器方式运行在Master节点的宿主机上;二进制安装方式下,系统组件以守护进程的方式运行,Master节点可以不运行任何容器),公司业务程序的容器更是不建议部署到Master节点上,以免升级或者维护时对业务造成影响。
4.2.2 Master节点组成
4.2.2.1 APIServer
整个集群的控制中枢,提供集群中各个模块之间的数据交换,并将集群状态和信息存储到分布式键-值(key-value)存储系统Etcd集群中。同时,它也是集群管理、资源配额、提供完备的集群安全机制的入口,为集群各类资源对象提供增删改查以及watch的REST API接口。APIServer作为Kubernetes的关键组件,使用Kubernetes API和JSON over HTTP提供Kubernetes的内部和外部接口。
4.2.2.2 Scheduler
集群Pod的调度中心,主要通过调度算法将Pod分配到最佳的Node节点,它通过APIServer监听所有Pod的状态,一旦发现新的未被调度到任何Node节点的Pod(PodSpec.NodeName为空),就会根据一系列策略选择最佳节点进行调度,对每一个Pod创建一个绑定(Binding),然后被调度的节点上的Kubelet负责启动该Pod。Scheduler是集群可插拔式组件,它跟踪每个节点上的资源利用率以确保工作负载不会超过可用资源。因此,Scheduler必须知道资源需求、资源可用性以及其他约束和策略,例如服务质量、亲和力/反关联性要求、数据位置等。Scheduler将资源供应与工作负载需求相匹配以维持系统的稳定和可靠性,因此Scheduler在调度的过程中需要考虑公平、资源高效利用、效率等方面的问题。
4.2.2.3 Controller Manager
集群状态管理器(它的英文直译名为控制器管理器),以保证Pod或其他资源达到期望值。当集群中某个Pod的副本数或其他资源因故障和错误导致无法正常运行,没有达到设定的值时,Controller Manager会尝试自动修复并使其达到期望状态。
Controller Manager包含NodeController、ReplicationController、EndpointController、NamespaceController、ServiceAccountController、ResourceQuotaController、ServiceController和TokenController等,该控制器管理器可与API服务器进行通信,以在需要时创建、更新或删除它所管理的资源,如Pod、服务断点等。
Scheduler和Controller Manager虽然部署了多个节点,但同时工作的节点只有一个,因为Scheduler和Controller Manager属于有状态服务,为了防止重复调度,多个节点的Scheduler和Controller Manager进行了选主工作,工作节点(主节点)信息保存在Scheduler和Controller Manager的EndPoint中,可以通过kubectl describe ep kube-scheduler kube-controller-manager -n kube-system查看(Kubernetes 1.20版本以上需要在leases中查看:kubectl get leases -n kube-system)。
4.2.2.4 Etcd
CoreOS开发,用于可靠地存储集群的配置数据,是一种持久型、轻量型、分布式的键-值(key-value)数据存储组件。
Etcd作为Kubernetes集群的持久化存储系统,集群的灾难恢复、状态信息存储都与其密不可分,所以在Kubernetes高可用集群中,Etcd的高可用是至关重要的一部分,在生产环境中建议部署大于3的奇数个数的Etcd,以保证数据的安全性和可恢复性。
Etcd可与Master组件部署在同一个节点上,大规模集群环境下建议部署在集群外,并且使用高性能服务器来提高Etcd的性能和降低Etcd同步数据的延迟。
4.3 Node节点
4.3.1 概述
Node节点也被称为Worker、Node和Minion,是主要负责部署容器(工作负载)的单机(或虚拟机),集群中的每个节点都必须具备容器的Runtime(运行时),比如Docker或其他遵循CRI标准的Runtime等。
Kubelet作为守护进程运行在Node节点上,负责监听该节点上所有的Pod,同时负责上报该节点上所有Pod的运行状态,确保节点上的所有容器都能正常运行。当Node节点宕机或故障(NotReady状态)时,该节点上运行的Pod会被自动转移到其他节点上。
4.3.2 Node节点组成
4.3.2.1 Kubelet
负责与Master通信协作,管理该节点上的Pod,对容器进行健康检查及监控,同时负责上报节点和节点上面Pod的状态。
4.3.2.2 Kube-Proxy
负责各Pod之间的通信和负载均衡,将指定的流量分发到后端正确的机器上。
4.3.2.3 Docker Engine
Docker引擎,负载对容器的管理。
4.3.2.4 其他组件及工具
4.3.2.4.1 CoreDNS
用于Kubernetes集群内部Service的解析,可以让Pod把Service名称解析成Service的IP,然后通过Service的IP地址连接到对应的应用上。
4.3.2.4.2 Calico
符合CNI标准的一个网络插件,它负责给每个Pod分配一个不会重复的IP,并且把每个节点当作一个“路由器”,这样一个节点的Pod就可以通过IP地址访问其他节点的Pod。
4.4 Pod的概念
4.4.1 概述
在讲解Docker基础时,我们把一个应用程序封装进一个镜像,之后启动这个镜像并映射一个宿主机端口,就能访问这个应用。既然单个容器即可部署我们的应用,为什么Kubernetes不直接管理容器还要设计一个Pod呢?接下来我们带着这个问题学习Kubernetes最小的单元——Pod。
4.4.2 什么是Pod
4.4.2.1 从使用方来看
在实际使用时,单个容器是无法单独来支撑我们的应用,往往需要很多微服务才能组成一个系统,并且还会存在A服务依赖B服务,B服务需要和C服务共用某个目录,实现数据共享。另外,在使用裸容器时,很难实现对容器内进程的健康检查及横向扩容等,而Pod可以轻轻松松解决上述问题。
4.4.2.2 从Kubernetes角度看
Docker只是容器Runtime(运行时)中的一种,市面上还有很多容器的Runtime,比如Rkt、CRI-O等,而Kubernetes作为目前最流行的容器编排工具,需要支持各个Runtime并且不依赖于底层Runtime的实现技术,于是就抽象了Pod这个概念,用于管理多个紧密相连的符合CRI标准的容器,可以从下图来理解Pod。
由图可知,Pod可简单地理解为一组、一个或多个容器,每个Pod还包含一个Pause容器,Pause容器是Pod的父容器,它主要负责僵尸进程的回收管理,同时通过Pause容器可以使同一个Pod里面的不同容器共享存储、网络、PID、IPC等,容器之间可以使用localhost:port相互访问,可以使用Volume等实现数据共享。根据Docker的构造,Pod可被建模为一组具有共享命名空间、卷、IP地址和端口的容器。
使用裸容器时,需要将容器内应用程序的端口映射到宿主机,如果容器过多,端口管理就会比较困难,而且容易引起端口冲突。而Kubernetes为每个Pod都分配一个唯一的IP地址,这样就可以保证不同应用程序可以使用同一个端口,之后通过Kubernetes的内部Service进行访问,这样就避免了发生端口冲突的问题。
4.4.3 Pod 的状态
和裸容器部署一样,Pod在运行时也会有不同的状态,Pod的状态信息保存在PodStatus对象中,在PodStatus中有一个Phase字段,用于描述Pod在其生命周期中的不同状态。可以使用Kubernetes的客户端工具Kubectl查看某个Pod的Phase字段,比如查看kube-system命名空间下的metrics-server的状态:
可以看到此时Pod的状态是Running,当然状态不仅仅只有Running,常见的状态如下表所示。
4.4.4 Pod 探针
在生产环境下,进程正常启动并不代表应用能正常处理请求,所以合理地设计应用的健康检查尤为重要。在使用裸机或者裸容器部署时,一般很难对应用做很完善的健康检查,而Pod提供的探针可以很方便地用来检测容器内的应用是否正常。目前探针有3种检测方式,可以根据不同的场景选择合适的健康检查方式。检测方式如下表所示。
上述检查方式可以被周期性执行,每次检查容器后可能得到的容器状态如下表所示。
Kubelet实现上述检查有3种检测方式,在生产环境中多加利用可以提高应用的可用率。目前支持的探测器类型有3种,可以选择性地对容器进行检测,参考下表。
4.4.5 Pod镜像拉取策略和重启策略
在发布应用或更改控制器配置时,会触发Pod的滚动更新,此时针对容器的镜像有不同的拉取方式,参考下表所示。
Pod进行部署或运行时,难免会出现故障,对于故障Pod也有不同的处理方式,如下表所示。
4.4.6 Pod 的基本操作
4.4.6.1 Pod 的创建
在生产环境中,很少单独运行一个Pod,因为单独创建的Pod并不能实现一些高级的发布策略,所以在实际使用中经常会用Deployment、DaemonSet、StatefulSet等高级控制器调度并管理Pod。
当然有时候也会单独启动一个Pod用于测试业务等,此时可以单独创建一个临时Pod。创建一个Pod的标准格式如下(下面定义的内容可以直接用在Deployment、DaemonSet、StatefulSet中):
使用命令创建:
4.4.6.2 查看创建的Pod
上述pod.yaml定义了labels字段,可以根据该标签过滤查看该Pod的状态:
4.4.6.3 删除Pod
当不再使用该Pod时,可以删除该Pod(由于是单独创建的Pod,删除后不会重建,高级控制器删除后会自动重建):
# kubectl delete -f pod.yaml # 或者 kubectl delete po nginx -n default pod "nginx" deleted
# kubectl get po -l app=nginx
好了,本次内容就分享到这,欢迎大家关注《Kubernetes》专栏,后续会继续输出相关内容文章。如果有帮助到大家,欢迎大家点赞+关注+收藏,有疑问也欢迎大家评论留言!
标签:容器,Kubernetes,基础,概念,2.1,2.2,Pod,节点 From: https://blog.csdn.net/qq_25409421/article/details/137383439