Kubernetes
0. Kubernetes是什么
Kubernetes(K8s) is a portable, extensible, open source platform for managing containerized workloads and services, that facilitates both declarative configuration and automation.
Kubernetes为云原生应用提供了定义规范。
Kubernetes 已经成为云时代的操作系统。我们可以对比一下经典的 Linux 和 Kubernetes 的概念模型,他们都是定义了开放的、标准化的访问接口;该通用API接口在类Unix下称为POSIX1。
The Portable Operating System Interface (POSIX) is a family of standards specified by the IEEE Computer Society for maintaining compatibility between operating systems. POSIX defines both the system- and user-level application programming interfaces (API), along with command line shells and utility interfaces, for software compatibility (portability) with variants of Unix and other operating systems.POSIX is also a trademark of the IEEE. POSIX is intended to be used by both application and system developers.
Kubernetes定义了云的分布式系统的API。如下是linux同kubernetes之间的类比:
| 内容 | Linux | Kubernetes |
|---|---|---|
| 隔离单元 | 进程 | Pod |
| 硬件 | 虚拟机/物理机 | 数据中心 |
| 并发单元 | 线程 | 容器进程 |
| 资源管理 | 进程内存及CPU | 内存、CPU |
| 存储 | 文件 | ConfigMap,Secrete,Volumn |
| 网络 | 访问端口 | Service |
| 终端控制台 | shell,bash | kubectl |
| 网络安全 | Iptables | NetworkPolicy |
| 权限管理 | 用户、文件权限 | UserAccount,ServiceAccount,RBAC |
Linux和kubernetes作为操作系统,体现在向下封装资源,向上支撑应用。

它们都提供了对底层计算、存储、网络、异构计算设备的资源抽象和安全访问模型。可以根据应用需求进行资源调度和编排。Linux 的计算调度单元是进程,调度范围限制在一台计算节点。而 Kubernetes 的调度单位是 Pod,可以在分布式集群中进行资源调度,甚至跨越不同的云环境。其主要业务是调度和管理容器服务。
1. Kubernetes架构
一个高层级的Kubernetes架构图如下:分为控制面Master和数据面Node节点池;集群上可以是一主多从和多主多从的架构;
Kubernetes 的控制平面包含四个主要的组件:API Server、Controller、Scheduler 以及 etcd。如下是其与数据面的交互图,均通过API server同kubelet交互实现的。同时数据面提供loadbalancer给到终端用户请求。K8s 可以在 IDC、云端、边缘等不同场景进行统一部署和交付。

1.1 容器相关概念
如下图介绍了Kubernetes以及docker中最核心的一些概念。
CRI是kubernetes定义的容器运行时接口,具体的实现由两个,一个是来自于docker的containerd,另一个是来自于RedHat的CRI-O。
- Docker,Kubernetes 等工具来运行一个容器时会调用容器运行时(CRI)比如 containerd,CRI-O
- 通过容器运行时来完成容器的创建、运行、销毁等实际工作
- Docker 使用的是 containerd 作为其运行时;Kubernetes 支持 containerd,CRI-O 等多种容器运行时
- 这些容器运行时都遵循了 OCI 规范,并通过 runc 来实现与操作系统内核交互来完成容器的创建和运行
- CRI(容器运行时接口)是 Kubernetes 用来控制创建和管理容器的不同运行时的 API,它使 Kubernetes 更容易使用不同的容器运行时。它一个插件接口,这意味着任何符合该标准实现的容器运行时都可以被 Kubernetes 所使用。
- OCI 开放容器倡议,是一个由科技公司组成的团体,其目的是围绕容器镜像和运行时创建开放的行业标准。他们维护容器镜像格式的规范,以及容器应该如何运行。
- runc 是轻量级的通用运行时容器,它遵守 OCI 规范,是实现 OCI 接口的最低级别的组件,它与内核交互创建并运行容器。runc 为容器提供了所有的低级功能,与现有的低级 Linux 功能交互,如命名空间和控制组,它使用这些功能来创建和运行容器进程。
具体详细内容参考下述图表2。
1.2 Kubernetes设计理念
Kubernetes 在容器编排中有几个关键设计理念:
**容错性:**容错性是保证K8s集群系统稳定性和安全性的基础,确保系统不崩溃,不出现业务错误,不做坏事。
**易扩展性:**易扩展性是保证K8s友好,后续快速迭代增加新功能的基础,让系统可以在用户看到的时间内做好事。
声明式 API:开发者可以关注于应用自身,而非系统执行细节。比如 Deployment(无状态应用)、 StatefulSet(有状态应用)、Job(任务类应用)等不同资源类型,提供了对不同类型工作负载的抽象;对 Kubernetes 实现而言,基于声明式 API 的 “level-triggered” 实现比 “edge-triggered” 方式可以提 供更加健壮的分布式系统实现。
可扩展性架构:所有 K8s 组件都是基于一致的、开放的 API 实现和交互;三方开发者也可通过 CRD(Custom Resource Definition)/Operator 等方法提供领域相关的扩展实现,极大提升了 K8s 的能力。Operator 是 Kubernetes 里面的一个核心思想,它代表着我的任何一个应用和它所需要的能力都可以定义成为一个 Kubernetes 的 API 对象,通过一个叫做 Controller 的机制让你去使用云的能力,再让你接入到各种各样的基础设施里面。这个 Operator 化带来的一个直接结果就是,我的应用本身是高度自动化的,包括自愈、健壮性、可靠性、运行的确定性,这些在今天都可以交给 Kubernetes去解决。
可移植性:K8s 通过一系列抽象如 Loadbalance Service(负载均衡服务)、CNI(容器网络接口)、CSI(容器存储接口)、CRI(容器运行时接口),帮助业务应用可以屏蔽底层基础设施的实现差异,实现容器灵活迁移的设计目标。
1.3 Kubernetes核心概念
Kubernetes中核心对象类型包括:资源对象、配置对象、存储对象、策略对象,详细的对象类别中包含一些核心概念。
上述对象均可理解为Kubernetes API Objects,每个对象包含三大类属性:元数据metadata,规范spec和状态status。
元数据用来标识对象,至少包含三个元数据:namespace,name,uid;除此一般还有label,可以用来标识和匹配不同的对象;规范描述了用户期望kubernetes集群中分布式系统达到的理想状态;状态描述了当前系统实际达到的状态
Kubernetes中所有的配置都是通过API对象的spec去设置的,也就是用户通过配置系统的理想状态来改变系统,所有的API均是声明式的。
1.3.1 资源对象
-
Pod,Kubernetes中最小的部署应用或服务单元,可以支持部署多容器;同一个Pod中共享网络地址和共享文件,可以通过进程间通信组合完成服务。目前Kubernetes业务主要包括:long-running、batch、node-deamon、stateful application,分别对应的控制器为Deployment,Job,DeamonSet,StatufulSet。
-
**Deployment **,表示对集群的Kubernetes的一次更新操作,比RS应用模式更广的API对象,可以是创建一个新的服务,更新一个新的服务,也可以是滚动升级一个服务。滚动升级实际是创建一个新的RS,逐步增加到理想的副本个数,旧的RS逐步减小到0的符合操作;以 Kubernetes 的发展方向,未来对所有长期伺服型的的业务的管理,都会通过 Deployment 来管理。Master 节点的中有一个子系统叫做 Deployment Controller,负责实际执行并使当前状态不断趋向于所需状态。
-
Replication Controller, 副本控制器RC, 用来保证Pod高可用的API对象,通过监控运行中的Pod个数来保证集群中运行指定数目的Pod副本。RC是Kubernetes早期技术概念,只适用于long-running业务类型,目前已经使用RS替代。
-
Replica Set, 副本集 RS,RS是新一代RC,提供同样高可用能力,支持更多种类的匹配模式,副本集对象一般不单独使用,而是作为Deployment的理想状态参数使用。ReplicaSet 是管理 pod 生命周期的组件,监控活动副本,当收到指令时或 pod 离线或意外停止时启动 pod,也会在收到指示时杀死 pod,也许是因为用户负载减少。
-
Job, Job是K8s中用来控制批任务的API对象。Job管理Pod根据用户的设置把任务成功完成就自动退出了。成功完成的标志根据不同的spec.completions策略而不同;
-
Deamon Set,后台服务集,重点关注K8s中的节点,确保每个节点上均有此类Pod(包括存储、日志、监控等)运行。节点可能是所有集群节点,也可能是通过NodeSelector选定的一些特定节点。
-
StatefulSet,有状态服务集,RC和RS主要控制的是无状态服务集,其所控制的Pod名字是随机设置的,1个Pod出故障了就被丢弃了,而另个地方重启的Pod名字就变了。对于无状态服务集重要的是数量,而不是名字和在哪。StatefulSet中每个Pod的名字都是事先确定的,不能改变的,用于后续关联Pod的状态。StatefulSet中的Pod每个独立挂载自己的独立存储,Pod出现故障后,还得挂载上原来的存储继续提供有状态服务;StatefuleSet做的事情是将确定的Pod和确定的存储关联起来保证状态的连续性。
1.3.2 配置对象
- Node节点, K8s中的计算能力,是所有Pod运行所在的工作主机,可以是物理机也可以是虚拟机,均需要基于kubelet管理节点上的容器。
- Service服务, RC/RS/Deployment只是保证了Pod的数量,没有解决如何访问这些服务的问题。由于Pod的调度机制决定了服务实例不能以确定的IP和端口来提供服务,需要依赖服务端的服务发现和负载均衡能力。在K8s内部,客户端需要访问的服务就是Service对象,每个Service对象都会有一个集群内部的虚拟IP,集群内部通过虚拟IP访问服务;Kube-proxy是K8s中的负载均衡器,在K8s的每个节点上都有一个,这是一个分布式的代理服务,需要访问的节点服务越多,提供负载均衡能力的kube-proxy就越多,高可用节点也越多。 另外,Service 是一组逻辑上的 pod分组,对提供同一个服务的多个Pod进行聚合;它提供了一个单一的虚拟的Cluster IP 地址和 DNS 名称,后端对应1个或多个提供服务的Pod,你可以通过它访问服务内的所有 pod。
- Secrete 密钥对象, 用来保存和传递密码、密钥、认证凭据等敏感信息。在对应的配置文件中通过Secrete对象应用敏感信息。这种方式的好处是:意图明确,避免重复,减少暴露机会。
- UserAccount用户账户 & ServiceAccount服务庄户, 用户账户为个人提供账户标识,服务账户为计算机进程和K8s集群中运行的Pod提供账户标识。用户账户与下述的服务命名空间无关,而服务账号的是运行绑定在某个命名空间下的。
- Namespace命名空间,提供集群虚拟的集群隔离作用,K8s初始由两个命名空间,1个是默认命名空间default,1个是系统命名空间kube-system。
- Ingress/Egress,Kubernetes pod 的流量称为 Ingress,而从 pod 到集群外的出站流量称为 Egress。
- Custom Resource Definition, CRD自定义的资源对象,需要满足K8s API定义规范;
- apiVersion,自定义资源对象的版本号
- Kind,自定义资源对象的名称,用户可以通过kubectl get ${kind_name}来获取对应的资源对象
- Metadata,继承了原生的K8s的metadata,用于添加lable/annotation等元数据
- Spec,可自定义设计的服务配置参数,如镜像版本号,节点数据量等
- Status,定义资源的相关状态
1.3.3 存储对象
- Volumn存储卷, Docker存储卷作用范围为一个容器,而K8s的存储卷作用范围为一个Pod,每个Pod申请的卷由Pod中所有容器共享。
- PersistentVolumn持久存储卷 & PersistentVolumnClaim持久存储卷声明, PV和PVC使K8s具有存储的逻辑抽象能力,使得配置Pod的存储卷时候可以忽略对应的存储技术配置。PV和PVC的关系类比于计算的Node和Pod,PV和Node是资源提供者,PVC和Pod是资源使用者。
1.3.4 策略对象
- Federation集群联邦, K8s最初的设计是单一集群在同一个地域内,同一个地区的网络性能才能满足K8s调度和计算存储连接要求。Federation就是让K8s有跨Region和跨服务商的能力。
- RBAC访问授权,RBAC中引入角色Role和角色绑定RoleBinding,访问策略是可以和某个角色绑定,具体的用户再和1个或多个角色绑定;
1.4 Kubernetes和服务网格ServiceMesh
将应用程序的功能划分为单独的进程运行在同一个最小调度单元中(例如 Kubernetes 中的 Pod)可以被视为 sidecar 模式。如下图所示,sidecar 模式允许您在应用程序旁边添加更多功能,而无需额外第三方组件配置或修改应用程序代码。而通过sidecar模式的组网即为服务网格架构形态。Sidecar 应用与主应用程序松散耦合。它可以屏蔽不同编程语言的差异,统一实现微服务的可观测性、监控、日志记录、配置、断路器等功能。
中间件实际上是大量的通过 Sidecar 方式去被使用到的,所以我的应用本身不需要再去引入一个库,或者引入一个特定的框架来去做很多事情,我甚至都不需要感知。应用跟云的交互,就通过一个叫 Sidecar 的一个旁路容器,让这个容器去代理应用本身所需要的进出流量,所以云就可以非常容易地通过这样一个代理,调节流量、做流量切分、网络管控,这就是非常简单的 Service Mesh 的原理。如下是两种形态的Sidecar模式。

使用容器作为Service和Sidecar进程的承载;

使用Function作为Servcie和Sidecar逻辑的承载;
2. Kubernetes原理机制
2.1 Kubernetes服务之间通信
2.1.1 ClusterIp
在K8s中,Service对应提供一个虚拟的ClusterIp,通过ClusterIp即可访问到服务对应的Pod。而负责这一转发的模块是Kube-proxy。在该场景下,ClusterIp和PodId都是K8s集群内部的访问地址;
Kube-proxy是一个运行在K8s每个节点上的Go应用程序,支持三种工作模式:userspace、iptables、ipvs。后端选择pod进行转发的时候依赖Label Selector。
-
userspace
该模式下kubep-proxy为每个service创建一个监听端口,发向ClusterIp的请求被iptables规则重定向到Kuber-proxy监听的端口上,Kube-proxy依据LB算法选择一个服务的Pod进行请求转发。该模式下,Kube-proxy是4层loadbalancer。但是由于在用户空间,数据拷贝效率较低。后端Pod不可用时,kube-proxy会重试其他的pod。

-
iptables
kube-proxy为service后端的每个pod创建对应的iptables规则,直接将发向ClusterIp的请求重定向到一个Pod Ip。该场景下,避免了数据的用户空间和内核空间的拷贝,kube-proxy不充当4层负载均衡,而只负责创建iptables规则。但是当后端pod故障后,无法进行重试;
-
ipvs
kube-proxy监控pod的变化并且创建对应的ipvs rules。ipvs是在kernel模式下通过netfilter实现,采用hash table存储规则,支持更多的loadbalance的算法,依赖在操作系统中安装ipvs内核模块。
2.1.2 NodePort
NodePort在集群中的主机节点上为Servcie提供一个代理端口,允许从主机网络对Service进行访问。
在创建NodePort时,kube-proxy也会同时为Service创建Cluster Ip相关的iptables规则,kube-proxy并不不会直接接受主机的端口进入的流量,而是创建相应的iptables规则,通过从该带瘤端口收到的流量直接转发到后端的Pod中。
2.1.3 LoadBalancer
K8s支持将Service定义为LoadBalancer类型;LoadBalancer类型的Service创建后,Cloud Provider Controller会将配置下发到Cloud Provider网络中的四层负载均衡器,该负载均衡器负责将外部的流量分发到后面多个节点的NodePort端口上。也就是说外部流量需要依赖NodePort端口才能把流量转发到K8s内部。而LoadBalancer需要依赖各个云服务商提供支持。
2.1.4 Ingress
当一个应用需要对外提供多个服务时,需要借用Kubernetes Ingress来统一网络入口。其声明了一个应用层的负载均衡器,可以根据HTTP请求的内容将来自同一个TCP端口的请求分发到不同的Kubernetes Service。支持按Http URL进行路由,同时也支持按照请求的Host进行路由。
Kubernetes使用Ingress Controller来监控Ingress规则,并通过一个七层网关来实现这些要求,一般可以使用Nginx,HAProxy等;由于Ingress是不是在K8s集群中,对外部流量来说,需要配合NodePort和LoadBalancer才能提供对外的流量入口。
在NodePort,Ingress,Pod等不同的接入层面都可以对系统进行水平扩展,以应对不同的外部流量要求。详细的局部细节图如下:
REFERENCE: