深度理解kubenetes

kubenetes 核心概念介绍

Kubenetest 生态系统

1695215849738

Kubenetes 设计理念

1695215914016

Kubenetes Master 架构

1695216110095

Kubenetes API 设计原则

  • 所有 API 都应是声明式的

    • 相对于命令式操作,声明式操作对于重复操作的效果是稳定的,这对于容易出现数据丢失或重复的分布式环境来说是很重要的。
    • 声明式操作更易被用户使用,可以使系统向用户隐藏实现的细节,同时也保留了系统未来持续优化的可能性。
    • 此外,声明式的 API 还隐含了所有的 API 对象都是名词性质的,例如 Service、Volume 这些 API 都是名词,这些名词描述了用户所期望得到的一个目标对象。
  • API 对象是彼此互补而且可组合的

    • 这实际上鼓励 API 对象尽量实现面向对象设计时的要求,即“高内聚,松耦合”,对业务相关的概念有一个合适的分解,提高分解出来的对象的可重用性。
  • 高层 API 以操作意图为基础设计

    • 如何能够设计好 API,跟如何能用面向对象的方法设计好应用系统有相通的地方,高层设计一定是从业务出发,而不是过早的从技术实现出发。
    • 因此,针对 Kubernetes 的高层 API 设计,一定是以 Kubernetes 的业务为基础出发,也就是以系统调度管理容器的操作意图为基础 设计
  • 低层 API 根据高层 API 的控制需要设计

    • 设计实现低层 API 的目的,是为了被高层 API 使用,考虑减少冗余、提高重用性的目的,低层 API 的设计也要以需求为基础,要尽量抵抗受技术实现影响的诱惑。
  • 尽量避免简单封装,不要有在外部 API 无法显式知道的内部隐藏的机制

    • 简单的封装,实际没有提供新的功能,反而增加了对所封装 API 的依赖性。
    • 例如 StatefulSet 和 ReplicaSet,本来就是两种 Pod 集合,那么 Kubernetes 就用不同 API 对象来定义它们,而不会说只用同一个 ReplicaSet,内部通过特殊的算法再来区分这个 ReplicaSet 是有状态的还是无状态。
  • API 操作复杂度与对象数量成正比

    • API 的操作复杂度不能超过 O(N),否则系统就不具备水平伸缩性了。
  • API 对象状态不能依赖于网络连接状态

    • 由于众所周知,在分布式环境下,网络连接断开是经常发生的事情,因此要保证 API 对象状态能应 对网络的不稳定,API 对象的状态就不能依赖于网络连接状态。
  • 尽量避免让操作机制依赖于全局状态

    • 因为在分布式系统中要保证全局状态的同步是非常困难的。

Kubentes 的作业描述组合

1695216894844

kubeneted 架构设计原则

  • 只有 APIServer 可以直接访问 etcd 存储,其他服务必须通过 Kubernetes API 来访问集群状态;
  • 单节点故障不应该影响集群的状态;
  • 在没有新请求的情况下,所有组件应该在故障恢复后继续执行上次最后收到的请求 (比如网络分区或服务重启等);
  • 所有组件都应该在内存中保持所需要的状态,APIServer 将状态写入 etcd 存储,而其他组件则通过 APIServer 更新并监听所有的变化;
  • 优先使用事件监听而不是轮询。

Kubenetes 对象的属性

API 对象是 Kubernetes 集群中的管理操作单元。

Kubernetes 集群系统每支持一项新功能,引入一项新技术,一定会新引入对应的 API 对象,支持对该功能的管理操作。

每个 API 对象都有四大类属性:

  • TypeMeta
  • MetaData
    • Lable
    • Annotations
  • Spec
  • Status

TypeMeta

Kubernetes对象的最基本定义,它通过引入GKV(Group,Kind,Version)模型定义了一个对象的类型。

  1. Group

    Kubernetes 定义了非常多的对象,如何将这些对象进行归类是一门学问,将对象依据其功能范围归入不同的分组, 比如把支撑最基本功能的对象归入 core 组,把与应用部署有关的对象归入 apps 组,会使这些对象的可维护性和可理解性更高。

同一个Group, 同一类对象 复用tcp连接

  1. Kind

    定义一个对象的基本类型,比如 Node、Pod、Deployment 等。

  2. Version

    社区每个季度会推出一个 Kubernetes 版本,随着 Kubernetes 版本的演进,对象从创建之初到能够完全生产化就绪的版本是不断变化的。与软件版本类似,通常社区提出一个模型定义以后,随着该对象不断成熟,其版本可能会从 v1alpha1 到 v1alpha2,或者到 v1beta1,最终变成生产就绪版本 v1。

    k8s 官方承诺只会同时兼容三个以内的版本

Metadata

Metadata 中有两个最重要的属性:Namespace和Name,分别定义了对象的 Namespace 归属及名字,这两个属性唯一定义了某个对象实例。

  1. Label

    顾名思义就是给对象打标签,一个对象可以有任意对标签,其存在形式是键值对。

    Label 定义了对象的可识别属性,Kubernetes API 支持以 Label 作为过滤条件 查询对象。

  2. Annotation

    Annotation 与 Label 一样用键值对来定义,但 Annotation 是作为属性扩展, 更多面向于系统管理员和开发人员,因此需要像其他属性一样做合理归类。

  3. Finalizer

    Finalizer 本质上是一个资源锁,Kubernetes 在接收某对象的删除请求时,会检查 Finalizer 是否为空,如果不为空则只对其做逻辑删除,即只会更新对象中的metadata.deletionTimestamp 字段。

  4. ResourceVersion

    ResourceVersion 可以被看作一种乐观锁,每个对象在任意时刻都有其 ResourceVersion,当 Kubernetes 对象被客户端读取以后,ResourceVersion 信息也被一并读取。此机制确保了分布式系统中任意多线程能够无锁并发访问对 象,极大提升了系统的整体效率。

Label

  • Label 是识别 Kubernetes 对象的标签,以 key/value 的方式附加到对象上。
  • key 最长不能超过 63 字节,value 可以为空,也可以是不超过 253 字节的字符串。
  • Label 不提供唯一性,并且实际上经常是很多对象(如 Pods)都使用相同的 label 来标志具体的应用。
  • Label 定义好后其他对象可以使用 Label Selector 来选择一组相同 label 的对象
  • Label Selector 支持以下几种方式:
    • 等式,如 app=nginx 和 env!=production;
    • 集合,如 env in (production, qa);
    • 多个 label(它们之间是 AND 关系),如 app=nginx,env=test。

1695304981317

Annotations

  • Annotations 是 key/value 形式附加于对象的注解。
  • 不同于 Labels 用于标志和选择对象,Annotations 则是用来记录一些附加信息,用来辅助应用部署、安全策略以及调度策略等。
  • 比如 deployment 使用 annotations 来记录 rolling update 的状态。

Spec 和Status (非通用属性)

  • Spec 和 Status 才是对象的核心。
  • Spec 是用户的期望状态,由创建对象的用户端来定义。
  • Status 是对象的实际状态,由对应的控制器收集实际状态并更新。
  • 与 TypeMeta 和 Metadata 等通用属性不同,Spec 和 Status 是每个对象独有的。

核心对象概览

常用的Kubernetes 对象及其分组

1695305743856

Node

  • Node 是 Pod 真正运行的主机,可以物理机,也可以是虚拟机。
  • 为了管理 Pod,每个 Node 节点上至少要运行 container runtime (比如 Docker 或者 Rkt)、Kubelet 和 Kube-proxy 服务。

Namespace

Namespace 是对一组资源和对象的抽象集合,比如可以用来将系统内部的对 象划分为不同的项目组或用户组。

常见的 pods, services, replication controllers 和 deployments 等都是属于 某一个 Namespace 的(默认是 default),而 Node, persistentVolumes 等则不属于任何 Namespace。

Pod 对象

  • Pod 是一组紧密关联的容器集合,它们共享 PID、IPC、Network 和 UTS namespace,是 Kubernetes 调度的基本单位。
  • Pod 的设计理念是支持多个容器在一个 Pod 中共享网络和文件系统,可以通过进程间通信和文件共享这 种简单高效的方式组合完成服务。
  • 同一个 Pod 中的不同容器可共享资源:
    • 共享网络 Namespace;
    • 可通过挂载存储卷共享存储;
    • 共享 Security Context。

1695306014925

支撑Pod运行的对象

环境变量相关对象:

  • 直接设置值;
  • 取 Pod Spec 的某些属性;
  • 从 ConfigMap 读取某个值;
  • 从 Secret 读取某个值。1695306422433

Volumnn 存储卷

  • 通过存储卷可以将外挂存储挂载到 Pod 内部使用。
  • 存储卷定义包括两个部分: Volume 和 VolumeMounts。
    • Volume:定义 Pod 可以使用的存储卷来源;
    • VolumeMounts:定义存储卷如何 Mount 到容器内部。

1695306490565

Pod的网络

Pod的多个容器是共享网络 Namespace 的,这意味着:

  • 同一个 Pod 中的不同容器可以彼此通过 Loopback 地址访问:
  • 这种方法常用于不同容器的互相协作。

Pod资源限制

Kubernetes 通过 Cgroups 提供容器资源管理的功能,可以限制每个容器的 CPU 和内存使用,比如对于刚才创建的 deployment,可以通过下面的命令限制 nginx 容器最多只用 50% 的 CPU 和 128MB 的内存:

1
2
3
$ kubectl set resources deployment nginx-app -c=nginx --limits=cpu=500m, memory=128Mi  

# deployment "nginx" resource requirements updated

等同于在每个 Pod 中设置 resources limits

1695306849887

Pod健康检查

Kubernetes 作为一个面向应用的集群管理工具,需要确保容器在部署后确实处在正常的运行状态。

  1. 探针类型:

    • LivenessProbe
      • 探测应用是否处于健康状态,如果不健康则删除并重新创建容器。
    • ReadinessProbe
      • 探测应用是否就绪并且处于正常服务状态,如果不正常则不会接收来自 Kubernetes Service 的流量。
    • StartupProbe
      • 探测应用是否启动完成,如果在 failureThreshold*periodSeconds 周期内未就绪,则会应用进程会被重启。
  2. 探活方式:

    • Exec
    • TCP socket
    • HTTP

健康检查spec

1695307263645

秘钥对象

  • Secret 是用来保存和传递密码、密钥、认证凭证这些敏感信息的对象。
  • 使用 Secret 的好处是可以避免把敏感信息明文写在配置文件里。
  • Kubernetes 集群中配置和使用服务不可避免的要用到各种敏感信息实现登录、认证等功能,例如访问 AWS 存储的用户名密码。
  • 为了避免将类似的敏感信息明文写在所有需要使用的配置文件中,可以将这些信息存入一个 Secret 对象,而在配置文件中通过 Secret 对象引用这些敏感信息。
  • 这种方式的好处包括:意图明确,避免重复,减少暴漏机会

用户User Account & 账号服务 Service Account

  • 顾名思义,用户帐户为人提供账户标识,而服务账户为计算机进程和 Kubernetes 集群中运行的 Pod 提供账户标识。
  • 用户帐户和服务帐户的一个区别是作用范围:
    • 用户帐户对应的是人的身份,人的身份与服务的 Namespace 无关,所以用户账户是跨Namespace 的;
    • 而服务帐户对应的是一个运行中程序的身份,与特定 Namespace 是相关的。

Service

Service 是应用服务的抽象,通过 labels 为应用提供负载均衡和服务发现。匹配 labels 的 Pod IP 和端口列表组成 endpoints,由 Kube-proxy 负责将服务IP 负载均衡到这些 endpoints 上。每个 Service 都会自动分配一个 cluster IP(仅在集群内部可访问的虚拟地址)和 DNS 名,其他容器可以通过该地址或 DNS 来访问服务,而不需要了解后端容器的运行。

image-20230922220312057

Service Spec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- port: 8078 # the port that this service should serve on
name: http
# the container on each pod to connect to, can be a name
# (e.g. 'www') or a number (e.g. 80)
targetPort: 80
protocol: TCP
selector:
app: nginx

Replica Set

  • Pod 只是单个应用实例的抽象,要构建高可用应用,通常需要构建多个同样的副本,提供同一个服务。
  • Kubernetes 为此抽象出副本集 ReplicaSet,其允许用户定义 Pod 的副本数,每一个 Pod 都会被当作一个无状态的成员进行管理,Kubernetes 保证总是有用户期望的数量的 Pod 正常运行。
  • 当某个副本宕机以后,控制器将会创建一个新的副本。
  • 当因业务负载发生变更而需要调整扩缩容时,可以方便地调整副本数量。

Deployment

  • 部署表示用户对 Kubernetes 集群的一次更新操作。
  • 部署是一个比 RS 应用模式更广的 API 对象,可以是创建一个新的服务,更新一个新的服务,也可以是滚动升级一个服务。
  • 滚动升级一个服务,实际是创建一个新的 RS,然后逐渐将新 RS 中副本数增加到理想状态,将旧 RS 中的副本数减小到 0 的复合操作。
  • 这样一个复合操作用一个 RS 是不太好描述的,所以用一个更通用的 Deployment 来描述。
  • 以 Kubernetes 的发展方向,未来对所有长期伺服型的的业务的管理,都会通过 Deployment 来管理。

image-20230922221250621

StatefulSet 有状态服务集

  • 对于 StatefulSet 中的 Pod,每个 Pod 挂载自己独立的存储,如果一个 Pod 出现故障,从其他节点启动一个同样名字的Pod,要挂载上原来 Pod 的存储继续以它的状态提供服务。
  • 适合于 StatefulSet 的业务包括数据库服务 MySQL 和 PostgreSQL,集群化管理服务 ZooKeeper、etcd 等有状态服务。
  • 使用 StatefulSet,Pod 仍然可以通过漂移到不同节点提供高可用,而存储也可以通过外挂的存储来提供高可靠性,StatefulSet 做的只是将确定的 Pod 与确定的存储关联起来保证状态的连续性。

image-20230922223614970

StatefulSet每个副本并不被视为完全一样, 每个标号的副本被视为一个独立的单元,滚动发布的时候, create场景下, 从小号往大号创建, update和删除的时候从大号向小号删除

Statefulset 与 Deployment 的差异

  • 身份标识
    • StatefulSet Controller 为每个 Pod 编号,序号从0开始。
  • 数据存储
    • StatefulSet 允许用户定义 volumeClaimTemplates,Pod 被创建的同时,Kubernetes 会以volumeClaimTemplates 中定义的模板创建存储卷,并挂载给 Pod。
  • StatefulSet 的升级策略不同
    • 滚动升级
    • onDelete
    • 分片升级

后台支撑服务集 DaemonSet

  • 长期伺服型和批处理型服务的核心在业务应用,可能有些节点运行多个同类业务的 Pod,有些节点上又没有这类 Pod 运行;
  • 而后台支撑型服务的核心关注点在 Kubernetes 集群中的节点(物理机或虚拟机),要保证每个节点上都有一个此类 Pod 运行。
  • 节点可能是所有集群节点也可能是通过 nodeSelector 选定的一些特定节点。
  • 典型的后台支撑型服务包括存储、日志和监控等在每个节点上支撑 Kubernetes 集群运行的服务。

image-20230922224639775

Job 任务

  • Job 是 Kubernetes 用来控制批处理型任务的 API 对象。
  • Job 管理的 Pod 根据用户的设置把任务成功完成后就自动退出。
  • 成功完成的标志根据不同的 spec.completions 策略而不同:
  • 单 Pod 型任务有一个 Pod 成功就标志完成;
  • 定数成功型任务保证有 N 个任务全部成功;
  • 工作队列型任务根据应用确认的全局成功而标志成功。

image-20230922224419129

存储 PV 和 PVC

  • pv : 相当于磁盘分区 pvc: 相当于磁盘请求
  • PersistentVolume(PV)是集群中的一块存储卷,可以由管理员手动设置,或当用户创建 PersistentVolumeClaim(PVC)时根据 StorageClass 动态设置。
  • PV 和 PVC 与 Pod 生命周期无关。也就是说,当 Pod 中的容器重新启动、Pod 重新调度或者删除时,PV 和 PVC 不会受到影响,Pod 存储于 PV 里的数据得以保留。
  • 对于不同的使用场景,用户通常需要不同属性(例如性能、访问模式等)的 PV。

CustomResourceDefinition (CRD)

  • CRD 就像数据库的开放式表结构,允许用户自定义 Schema。
  • 有了这种开放式设计,用户可以基于 CRD 定义一切需要的模型,满足不同业务的需求。
  • 社区鼓励基于 CRD 的业务抽象,众多主流的扩展应用都是基于 CRD 构建的,比如 Istio、Knative。
  • 甚至基于 CRD 推出了 Operator Mode 和 Operator SDK,可以以极低的开发成本定义新对象,并构建新对象的控制器。
刘小恺(Kyle) wechat
如有疑问可联系博主