K8S概念和原理

K8S作为新型基础服务组件,吸收云计算运维特点、充分解耦、提供最大的扩展性和灵活性,自身创造了诸多全新概念和名词。

K8S官网有详细的文档[1],但官方文档的编写是以功能为维度去关联概念,本文尝试从概念维度关联功能和意义,提供不同的学习视角,加深理解。本文会较全面的串讲k8s核心概念及实际意义。

Docker基本概念

Docker要干的事就俩字:隔离。用os支持的、高性能方式,在app和宿主机os之间加一扇中间层:容器(就像JVM夹在CPU和字节码之间),想把app放到其他位置运行的话,直接端走容器和里面的app就可以了,或者一个更形象的例子:我要在煤气灶(宿主机)上烤鱼(app),煤气灶有不同的类型、环境和火力,我要用鱼去适配这个煤气灶就得把鱼切成不同大小,非常麻烦,最简单的方式就是加一个锅容器(docker container),我端着锅和里面的鱼可以随便换灶。

容器技术是app移植的关键

回到Docker这边,比如我们要部署一套nginx,之前你需要下载tar包、安装依赖库等、进行系统配置等,而docker的出现让app部署一个命令就能完成,工作简化、便于控制、部署高效[2]

同时docker生态具有共享的思维,和git类似,docker也有仓库(repository)的概念[3]。你可以找到官方或社区app的docker镜像,pull下来就可直接使用或进行定制化修改。

Docker基本概念

镜像(image)和容器(container)

既然我们可以利用docker端着我们的app到处部署安装,那我们就要区分安装包已经安装的程序

Image镜像可以理解为软件安装包,利用Dockerfile描述你app的配置信息。日常windows安装软件需要一个.exe文件吧,安装时设定路径、配置可选组件什么的,安装后桌面一个图标双击打开运行即可。Image是安装包则container就等于安装在你电脑上的程序实体(安装了就算,不管你打没打开)。

总结一下,镜像可类比app安装包、java中的class对象或一张光盘,是对app的静态描述。而经过docker run运行起来的镜像就是容器,可类比java中new出来的对象、是一个被安装的程序、是可直接使用的动态app实例[4]

镜像分层

镜像内部层次复杂,比如我们自己写了一个web app,镜像里就要带着tomcat、ubuntu系统和许许多多web app要调用的组件,这样安装之后的这个web app才能直接运行。上述每一个组件都会进行分层,每一层都是一个相应具有独立功能的rootfs文件系统,最底层由Kernel的bootfs驱动(boot file system)。

镜像分层

分层带来的最直观好处就是资源节约和共享。image是网络请求拉下来的,比如这个web app用到了Debian OS,如果有另一个机器学习项目也用到了Debian,直接用本地的 Debian OS image就行,不需要重复拉取。

理论上docker image分为三层,由下向上分为:

  1. 只读层:就是这些最原始的image,os、web容器等
  2. init层:存放不能提交到镜像的个性化数据。比如你的app要根据网络配置不同的host文件,那这个个性化数据就要写到init层,但不能提交到image里,因为如果换个网络环境配置就无效了。
  3. 读写层:存放需要提交到镜像的个性化数据(docker commit)。比如一个业务jar文件,因为是完成容器业务功能的一部分,所以必须提交到镜像的最顶层读写层,当容器运行时所有层的image都要merge,就可以把这个jar文件写到容器的os中了,到这里你可以思考下删除文件如何配合merge工作。

Docker隔离原理

Docker的隔离分为环境隔离和资源隔离。环境隔离指进程、文件系统、网络等内容的隔离,多维度的隔离让容器内部更像一个独立的空间,其基于操作系统 namespace 实现,namespace类型很多[5],包括:

  1. PID Namespace 独立进程
  2. Mount
  3. UTS
  4. IPC
  5. Network
  6. User

资源隔离指容器对硬件资源的使用限制,包括cpu、内存、磁盘及网络带宽,该限制基于os的cgroup实现。

另外为了避免容器内的操作影响宿主机安全,docker采用chroot/pviot_root彻底隔离了宿主机和容器的文件系统,但同时共享宿主机系统内核。

如果需要进一步Dockerfile的编写入门资料,可以参照这份教程[6]

新西兰种植园地主:Dockerfile编写理论到实战

K8S基本概念

介绍完docker,我们来了解下k8s和docker间的关系。docker的容器隔离让部署app变得可移植和傻瓜化,但在大规模集群场景下,需要一个集中控制中心,对各个节点(node,物理机,但也可以是虚拟机)发号施令。想象一个拥有一千台节点的集群,你无法挨个节点执行命令,也不知道上面到底存在什么容器、机器挂掉如何进行容器调度、如何自动扩容等,而k8s的出现就是解决这个问题的。

Pod

Pod(豆荚)是k8s调度的最小单位,有自身的生命周期[7],同时也是k8s最重要、最核心的概念。pod可以包含一个或多个容器,但通常情况只包含一个。

我们直接操纵容器就行了,为什么还要包一层pod呢?

这里体现了k8s概念的核心创造信条:系统设计中没有加一层解决不了的问题,如果有,就再加一层。

POD核心内容

首先,容器是docker的概念,k8s为了解耦,有必要对其进行包装,以后如果不需要docker或者容器概念发生升级,k8s仅处理pod对容器的控制逻辑即可,缩小影响范围。

其次,pod的本质是容器的增强器[8],能完成的功能包括:

  1. 多容器管理:多容器场景一般出现在几个容器间具有超耦合关系。比如一个web app可以有war包容器和tomcat容器,两者组合才能运行(init container+container=这个pod)。还有一类场景就是需要利用容器的设计模式:比如容器内应用需要统一外部可访问的接口,那就加装一个适配器(adapter)容器、要对app隐藏对外请求,则可以加装一个大使(ambassador)处理来自容器的本地请求、还可以部署一个边车(sidecar)容器收集日志后发给ELK等[9]
  2. 暴露对外访问:pod默认运行在k8s私有网段,外部无法访问,但也可以通过 kubectl port-forward 开放临时调试。
  3. 滚动更新:配合deployment组件实现部署和升级策略。
  4. 容器健康检查:pod使用探针(probe)通过shell、tcp或http的方式对容器进行健康探测工作,当发现容器出现异常,pod会根据配置的不同重启策略(restartPolicy)对container作出后续操作。probe的种类可分为:
    1. Startup:检查app是否已经启动成功,否则重启且不开始后续探测。
    2. Liveness:检查app是否正常运行否则重启。
    3. Readiness:检查app是否可以对外提供服务否则不切入流量。
  5. 资源限制配置:通过limits字段限制容器的cpu时间片或内存占用等,也可以引入Metrics Server +Prometheus对资源使用情况可视化。
  6. 自动扩/缩容:配合HPA(HorizontalPodAutoscaler)组件完成。阈值完全基于 Metrics Server 上报的node 资源指标,比如CPU占用率。实际来看,建议用Custom Metrics 的方式自定义扩展,比如采用系统负载+其他指标的方案。

POD 核心能力扩展

ReplicaSet和Deployment

Pod管理了容器,那谁来管理pod呢?考虑到高可用和高性能,app pod需要集群部署,所以维护集群内pod数量等信息就需要在pod上再加一层,这就是副本集(ReplicaSet,RS),RS中可以声明期望的副本数,从而保证整个k8s中某组pod永远存在指定数量的集群副本。

Deploymeny/RS 核心内容

Deployment的目的则是支持app“部署”活动,具体功能包括:

  1. 监控ReplicaSet状态(扩/缩容进行中、完成、失败)
  2. 新旧pod替换策略(金丝雀发布[10]、recreate 重造等)
  3. 回滚策略(可指定版本回滚)

Deployment的核心是RS配置,同时提供了诸多部署过程的策略(由deployment controller 控制RS,而RS又去控制创建pod),还支持在线的服务数量scale伸缩

Service

Deployment定义了pod工作组的部署活动及策略,但是有两个业务问题没有解决:

  1. 固定对外IP问题:k8s只为pod分配了默认的、非固定ip的请求地址,那如何对外暴露稳定地址?
  2. 负载均衡问题:对一组pod的访问如何负载均衡?

Service通过k8s设置的VIP和虚拟网卡,请求会最终访问到具体pod的ip(此为推荐的内核态的IPVS模式,并非iptables、userspace模式)。service其实就是一个工作在四层的负载均衡组件,反向代理了label selector能获取到的pod,如果pod发生变化,Service会通过 controller manager 实时监控到[11](集群状态信息注册发现实际存在etcd中)。

Service默认是“ClusterIP”模式只能在集群内部访问,对外开放则需要用“NodePort”模式开一个对外端口,但此模式限制多多,不推荐。综合来看,k8s的service更接近dubbo功能,定位于内部服务注册发现和负载均衡。另外,如果不需要负载均衡(如自建注册中心、有状态服务场景),也可以采用headless service

Service概念思维导图

Ingress、Ingress Controller和Ingress Class

Service完成了注册发现和内部负载均衡,但四层负载均衡性能虽好,却拿不到HTTP信息、不够灵活,存在局限性。所以我们现在需要一个真正对外的网关(七层负载均衡设备),利用网关细分外部请求再转给各个Service(就像nginx通常做的那样,“代理Service的Service”[12])。Ingress的出现解决了这个问题,可进行更精细的请求规则转发(毕竟工作在7层嘛)。

但实际上k8s的设计没这么简单,为提高网关的灵活性和可替换性,网关又作出了三层逻辑拆分。我们可以思考一下,网关层转发功能实际可拆分为两部分:

  1. 转发的配置定义
  2. 实际做请求转发的程序

为了做逻辑划分,k8s将配置定义放在Ingress对象的rule中,真正做转发的程序定义为Ingress Controller,一般由社区实现,最有名的IC当然还是ng家的nginx-ingress-controller。

随着集群规模的扩大,出现内网外网需不同ingress-controller、多租户隔离等需求,于是k8s又引入可配置在Ingress内的Ingress Class[13],为选择不同的Ingress Controller使用。

DaemonSet

好,以上一套服务体系就基本形成了闭环,但现在pod的部署、宿主机的情况对我们来说依然是个黑盒子。DaemonSet就是一种可在每个宿主机仅调度部署一套的特殊pod,作用一般是配合集群监控、收集宿主机负载、收集日志等工作,这种app在一台node上部署多个也毫无意义。

另外DaemonSet还存在污点/容忍度概念,与pod和node间调度相关,可自行了解。

K8S架构[14]

K8S架构图

明确k8s基本概念后其架构会容易理解很多,k8s是中心化服务,分为控制面(Control Plane,master)和数据面(即Node)。控制面是K8S的大脑,组件包含:

  1. etcd数据库,负责注册服务、保存k8s配置和所有的API对象等数据
  2. scheduler 负责监视pod的调度情况
  3. controller-manager 负责节点、任务、端点和账户的总体调度
  4. apiserver 对外提供操作接口,可以通过kubectl调用。

数据面Node是具体部署app的虚拟机或宿主机,最核心的组件就是kubelet,负责启动node并管理container,此外还有kube-proxy做k8s service的网络代理。

K8S存储体系

PV和PVC

K8S提供了一套管理docker volume的存储体系,用同Ingress类似的方式进行了分层解耦,包括:

  1. PersistentVolume(PV):一个预制的资源实体或数据卷,比如指定大小的磁盘空间(hostPath)、临时资源(emptyDir)或NFS等,支持的Volume种类非常丰富[15]
  2. PersistentVolumeClaim(PVC):PVC和PV不同,PV是具体的存储实体,而PVC代表一份使用空间的“申请 claim”,描述了pod需要申请多大空间、什么类型等,然后通过 PersistentVolumeController不断尝试进行PV与PVC的自动绑定,如果能找到合法的PV则申请成功,否则pod启动失败。比如PVC要申请10MB的磁盘空间,但是找了半天才发现PV最大的才8MB,因此PV管理会非常麻烦,平时不能申请太多浪费空间,但有时又面临可用PV不足的问题,于是为了方便运维又引入一个子概念StorageClass。
  3. StorageClass:就是用来自动创建PV并执行PV与PVC绑定的。StorageClass实际就是创建PV的模板,在设定PVC时,将你定义的StorageClass对象设置上去即可,就不需要手动分配PV空间了。

PersistentVolume相关概念

需要强调的是,hostPath这种本地磁盘型的pv是不支持pod在不同节点间迁移的,可以尝试引入pod的原地重建插件或直接使用其他网络类型的PV如NFS。

有状态服务

有状态服务的有状态指的是:

  1. 拓扑结构:服务间依赖关系、主从依赖关系
  2. 数据持久化,PV和pod之间的强依赖关系

StatefulSet

为了解决拓扑依赖问题,StatefulSet采用headless service避免负载均衡并为每个pod设置了一个DNS,重启时会按照最初按顺序命名的app0、app1、app2... one by one 的顺序启动,保证了拓扑状态的稳定性。

StatefulSet会直接控制pod,在数据持久化时就固定了要声明的PVC编号,且PVC不会因为pod消失而消失,新pod建立时会寻找旧pod的PVC,重新完成绑定关系。

StatefulSet 相关概念

不过有的问题依然存在:比如一个redis集群,redis0 pod启动之后作为master,其余redis1、redis2相继启动且都作为redis0的slave去同步rdb文件,问题体现在:

  1. 拓扑关系死板固定。我们要进行写操作只能固定的使用redis0网络地址, 不支持自动选主切换机制。
  2. 主从等设置非常麻烦。比如读写分离,要在yml配置中获取pod自身的启动序号,当自身是redis0则用master配置,其他则用slave配置;或者设置主、从两组StatefulSet分别配置,总之非常死板麻烦。

Operator[16]

Operator是用编程的方式解决 StatefulSet 僵化问题的方案,其利用自定义API,相当于自定义了类型并自动化了一系列工作,其本质是对k8s的扩展,比如可以定义好你对集群的期望后,启动种子集群,然后逐步启动新pod加入。

ConfigMap/Secret

类似分布式配置中心工作,ConfigMap(cm)存储在k8s控制面,而Pod引入的cm支持实时更新,同时还支持文件类型。Secret就是加密的cm。

总结

现在我们知道,K8S以容器技术为依托,逐步解决了大规模服务运维、部署和调度和问题。

Pod是k8s中最核心的概念,绝大部分能力都依托pod这一核心,并进行能力扩展。

这些扩展的能力融合了现代运维和分布式服务治理工作,包括耳熟能详的:滚动更新、灰度发布、健康检查、负载监控、自动扩容、资源隔离、服务隔离、路由网关负载均衡、主从关系运维、服务注册发现、分布式配置等等诸多方面,更高级别的服务治理由istio以pod边车的方式提供,包括分布式追踪、限流等功能。最后,k8s并没有放弃复杂的有状态服务部署,依然在这个领域的自动化运维上作出努力。

最后,我们可以再次思考k8s体现的分层设计理念,分层虽然创造了许多复杂的概念、提高了学习门槛,却强化了整体架构的灵活性和扩展性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Kubernetes中,Pod是一个逻辑概念,它是通过声明式定义的一个YAML文件来创建的。最终,Pod会被调度到Kubernetes节点上的kubelet服务调用Linux操作系统的namespace、cgroup、docker等底层原理来实现。\[1\] Pod是Kubernetes中最小的可调度和管理的单位,它可以包含一个或多个容器。Pod中的容器共享相同的网络命名空间、存储卷和IP地址,它们可以通过localhost进行通信。Pod还提供了一种共享资源的机制,使得容器之间可以共享文件和环境变量。 Pod的创建原理是通过Kubernetes的调度器将Pod调度到可用的节点上。调度器会根据节点的资源情况、调度策略和Pod的需求来选择最合适的节点。一旦Pod被调度到节点上,kubelet服务会负责创建和管理Pod中的容器。 总结来说,Kubernetes中的Pod是通过声明式定义的YAML文件创建的,它通过调度器将Pod调度到节点上,并由kubelet服务负责创建和管理Pod中的容器。\[1\]\[2\] #### 引用[.reference_title] - *1* [kubernetes中Pod网络的创建原理](https://blog.csdn.net/buppt/article/details/123288339)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [2、k8s pod原理详解](https://blog.csdn.net/scjava/article/details/123292545)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [k8s篇-理解POD本质(实现原理与设计模式)](https://blog.csdn.net/qq_19676401/article/details/119996434)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

写代码的小泽

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

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

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

打赏作者

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

抵扣说明:

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

余额充值