[技术向] Kubernetes 的核心组件与对象

14

Kubernetes 的目的是实现分布式大规模集群的自动化运维, 底层依赖于容器技术, 优势在于, (1) 根据面向对象的设计思想对常见的运维操作进行了高度抽象, (2) 使用声明式 API 简化操作.

总体架构:

(图片来源: https://medium.com/@keshiha/k8s-architecture-bb6964767c12)

K8s 采用"控制面 / 数据面" (Control Plane / Data Plane) 架构, 集群里的计算机称为节点, 可以是实机也可以是虚机, 少量的节点 (Master) 用作控制面来执行集群的管理维护工作, 其他的大部分节点 (Worker, 下文直接记为 Node) 都被划归数据面, 用于运行业务应用.

kubectl 是 K8s 的客户端工具, 用来发送集群操作请求, 它位于集群之外, 理论上不属于集群.

K8s 的节点内部是由多个模块构成的, 可以分成组件 & 插件.

Master 有 4 个组件:

  • [ apiserver ] 是整个 K8s 系统的唯一入口, 对外公开了一系列的 RESTful API, 并且加上了验证、授权等功能, 所有其他组件都只能和它直接通信.

  • [ etcd ] 是一个高可用的分布式 Key-Value 数据库, 用来持久化存储系统里的各种资源对象和状态; 它只与 apiserver 有直接联系, 其他组件想要读写 etcd 的数据都必须经过 apiserver.

  • [ scheduler ] 负责容器的编排工作, 检查节点的资源状态, 把 Pod 调度到最适合的节点上运行, 这些节点的状态和 Pod 信息都存储在 etcd 里.

  • [ controller-manager ] 负责维护容器和节点等资源的状态, 实现故障检测、服务迁移、应用伸缩等功能, 更多倾向于运维监控.

这 4 个组件也都被容器化了, 运行在集群的 kube-system 命名空间里.

Node 有 3 个组件:

  • [ kubelet ] 是 Node 的代理, 负责管理 Node 相关的绝大部分操作, Node 上只有它能够与 apiserver 通信, 实现状态报告、命令下发、启停容器等功能.

  • [ kube-proxy ] 是 Node 的网络代理, 只负责管理容器的网络通信, 为 Pod 转发 TCP/UDP 数据包.

  • [ container-runtime ] 是容器和镜像的实际使用者, 在 kubelet 的指挥下创建容器, 管理 Pod 的生命周期.

这 3 个组件中只有 kube-proxy 被容器化了; kubelet 要管理整个节点, 容器化会限制它的能力, 所以它必须在 container-runtime 之外运行.

对象关系

Pod 是对容器的"打包", 里面的容器是一个整体, 对容器的操作都在 Pod 中进行. 可见, Pod 的作用是让多个容器既保持相对独立, 又能够小范围共享网络 & 存储等资源, 而且总是一起调度, 一起运行.

K8s 让 Pod 去编排处理容器, 然后把 Pod 作为应用调度部署的最小单位. 所有的 K8s 资源都直接或者间接地依附在 Pod 之上.

K8s 中的对象关系遵循 "组合优于继承" 的思想.

Node 的 yaml 配置示例:

apiVersion: v1
kind: Pod
metadata: 
  name: busy-pod 
  labels:         # 标签, 方便 K8s 对不同应用的识别和管理
    owner: me 
    env: demo 
    region: north 
    tier: back

spec: 
  containers: 
    - image: busybox:latest 
      name: busy 
      imagePullPolicy: IfNotPresent # 若本地没有镜像文件, 从远程拉取
      restartPolicy: Always         # 发生异常会自动重启容器
      env:                          # 环境变量
        - name: os 
          value: "ubuntu" 
        - name: debug 
          value: "on" 
      command:                      # 运行命令
        - /bin/echo 
      args:                         # 运行参数
        - "$(os), $(debug)"

Job & CronJob

批处理(离线调度)的任务抽象:

ConfigMap & Secret

配置信息, 有明文和密文的区别:

Deployment

负责在线应用 (也就是真正意义上的"服务") 的部署:

Deployment 可以创建任意多个的 Pod 实例, 并且维护这些 Pod 的正常运行, 保证应用始终处于可用状态.

Deployment 和 Pod 是一种松散的组合关系, Deployment 实际上并不"持有" Pod 对象, 这种组合关系通过标签来实现.

DaemonSet

在集群的每个节点上运行且仅运行一个 Pod:

DaemonSet 是为了弥补 Deployment 的不足, 后者不关心 Pod 会在集群的哪些节点上运行, 在它看来, Pod 的运行环境与功能是无关的. 但是, 有一些业务不是完全独立于系统运行的, 而是与主机存在"绑定"关系, 必须要依附于节点才能产生价值, 比如:

网络应用 (kube-proxy), 监控应用 (Prometheus), 日志应用 (Fluentd), 安全应用等等.

Service

TCP/IP 协议层面的服务发现和负载均衡:

Service 本质上是一个由 kube-proxy 控制的四层负载均衡, 在 TCP/IP 协议栈上转发流量, 使用静态 IP 地址代理动态的 Pod, 后者在集群内部的 IP 往往会随着升级/重启而变化.

Ingress

HTTP 流量入口的管理:

PersistentVolume

数据持久化 (存储) 的抽象:

PV 代表了一些实际的存储设备、文件系统, 比如 Ceph、GlusterFS、NFS, 甚至是本地磁盘.

存储设备一般由系统管理员单独维护, 然后再在 Kubernetes 里创建对应的 PV, 它们属于集群的系统资源, 是和 Node 平级的一种对象, Pod 对它没有管理权, 只有使用权.

PersistentVolumeClaim (PVC), StorageClass 两个对象是把存储卷的分配管理过程再次细化, 体现了中间层的思想:

PVC 相当于是 Pod 的代理, 代表 Pod 向系统申请 PV, 一旦资源申请成功, K8s 就会把 PV 和 PVC 绑定在一起.

StorageClass 抽象了特定类型的存储系统 (比如 Ceph、NFS), 帮助 PVC 找到合适的 PV, 它简化了 Pod 挂载"虚拟盘"的过程, 让 Pod 看不到 PV 的实现细节.

示例:

-- PV

apiVersion: v1
kind: PersistentVolume
metadata:
  name: host-10m-pv

spec:
  storageClassName: host-test
  accessModes:
  - ReadWriteOnce             # 可读可写, 但只能被一个节点上的 Pod 挂载
  capacity:
    storage: 10Mi             # 存储设备的容量
  hostPath:
    path: /tmp/host-10m-pv/   # 指定了存储卷的本地路径


-- PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: host-5m-pvc

spec:
  storageClassName: host-test
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Mi  -- 期望申请的容量