# 原理

# Node

  • 一个节点的配置示例:

    apiVersion: v1
    kind: Node
    metadata:
      labels:
        kubernetes.io/arch: amd64
        kubernetes.io/hostname: node-1
        kubernetes.io/os: linux
        node-role.kubernetes.io/controlplane: "true"
        node-role.kubernetes.io/etcd: "true"
        node-role.kubernetes.io/worker: "true"
      name: node-1
    spec:
      podCIDR: 10.42.1.0/24
    status:
      addresses:
      - address: 10.0.0.1
        type: InternalIP    # 内网 IP ,通常绑定到 eth0 网卡
      - address: node-1
        type: Hostname      # 主机名
      allocatable:          # 总共可分配的资源量
        cpu: "15"
        ephemeral-storage: "91546762160"
        hugepages-1Gi: "0"
        hugepages-2Mi: "0"
        memory: "60263227958"
        pods: "250"
      capacity:             # 硬件资源量
        cpu: "16"
        ephemeral-storage: 105144100Ki
        hugepages-1Gi: "0"
        hugepages-2Mi: "0"
        memory: 64155748Ki
        pods: "250"
      conditions:
        ...
    
  • k8s 有两种监控节点状态的方式:

    • kubelet 默认每隔 10s 上报一次节点状态到 apiserver 。
    • 为每个节点创建一个 Lease 对象。kubelet 默认每隔 10s 发送一次请求到 apiserver ,更新一次 Lease 对象,从而证明节点在线。
  • 当节点变为非 Ready 状态一段时间之后,k8s 会自动驱逐该节点上的 Pod 。然后将节点标记为 unschedulable ,直到节点恢复到 Ready 状态。

# kube-apiserver

  • 功能:提供 Restful API ,供用户、k8s 组件访问,从而管理 k8s 集群。
    • 用户可使用 kubectl 命令,作为客户端与 apiserver 交互,从而管理 k8s 。
    • k8s 组件之间一般不会直接通信,而是发送请求到 apiserver ,并默认采用 HTTPS 加密通信。
  • 默认监听 TCP 6443 端口。

# kube-controller-manager

  • 功能:包含一些控制器(controller),用于自动控制 Node、Pod、Service 等各种 k8s 资源。
  • k8s 内置的 controller 举例:
    • node controller
      • :负责管理节点。比如在新增节点时分配 CIDR 子网、当节点非 Ready 状态时发起驱逐。
    • namespace controller
    • deployment controller
    • replicaset controller
    • statefulset controller
    • daemonset controller
    • job controller
      • :负责根据 Job 创建 Pod 。
    • cronjob controller
    • endpoints controller
      • :负责管理所有 endpoints 对象。比如监控 Service、Pod 的状态,自动修改 endpoints 。
    • serviceaccounts controller
      • :负责为新建的 namespace 创建 default service account 。
  • 用户也可以开发自定义的 controller 。

# kube-scheduler

  • 功能:决定将 Pod 分配到哪个节点上部署,该过程称为调度。
  • 调度分为两个步骤:
    • 过滤
      • :遍历所有 Node ,筛选出允许调度该 Pod 的所有 Node ,称为可调度节点。比如 Node 必须满足 Pod 的 request 资源、Pod 必须容忍 Node 的污点。
      • 如果没有可调度节点,则 Pod 一直不会部署。
      • 对于节点总数低于 100 的 k8s ,默认设置了 percentageOfNodesToScore=50 ,即当可调度节点数量达到总数的 50% 时就停止遍历,从而减少耗时。
      • 为了避免某些节点一直未被遍历,每次遍历 Node 列表时,会以上一次遍历的终点作为本次遍历的起点。
    • 打分
      • :给每个可调度节点打分,选出一个最适合部署该 Pod 的 Node 。比如考虑亲和性。
      • 如果存在多个打分最高的 Node ,则随机选取一个。

# kubelet

  • 功能:
    • 将当前节点注册到 apiserver ,并定期上报状态。
    • 调用容器运行时,来创建、管理、监控 Pod 。
  • 默认监听 TCP 10250 端口。
  • kubelet 部署 Pod 时,会调用 CRI 接口 RuntimeService.RunPodSandbox ,先创建一个沙盒(Pod Sandbox),再启动 Pod 中的容器。
    • Sandbox 负责提供一个 Pod 运行环境,比如设置网络。
    • Sandbox 可以基于 Linux namespace 实现,也可以基于虚拟机实现,比如 kata-containers 。
    • 基于 Linux namespace 实现 Sandbox 时,kubelet 会先在每个 Pod 中运行一个 pause 容器。
      • pause 容器内运行一个简单的 pause 程序,循环执行 pause() 函数进行睡眠。
      • pause 容器的存在能方便管理 Linux namespace ,比如创建 network namespace 并共享给其它容器。还能避免 Pod 中所有容器进程终止时,Linux namespace 被自动删除。
  • kubelet 中的 PLEG(Pod Lifecycle Event Generator)模块负责执行 relist 任务:获取本机的容器列表,检查所有 Pod 的状态,如果状态变化则生成 Pod 的生命周期事件。
    • 每执行一次 relist ,会等 1s 再执行下一次 list 。
    • 如果某次 relist 耗时超过 3min ,则报错 PLEG is not healthy ,并将当前 Node 标记为 NotReady 状态。

# kube-proxy

  • 功能:管理节点的网络,将访问 Service 的流量反向代理到 EndPoints 中的 Pod 。

有多种代理模式:

  • userspace
    • k8s v1.2 之前的默认模式。
    • 原理:
      • 监听所有 Service、EndPoints 的变化。
      • kube-proxy 监听一些端口,反向代理到各个 Service 的 EndPoints 中的 pod_ip:port 。
        • EndPoints 包含多个 pod_ip 时,默认按轮询算法进行负载均衡。
      • 配置 iptables 规则,将访问 service_ip:port 的流量转发到 kube-proxy 监听的端口。
    • 缺点:
      • 流量会先后被 iptables、kube-proxy 转发,总共转发两次,而且需要从内核态传递到用户态,性能较低。
  • iptables
    • k8s v1.2 之后的默认模式。
    • 原理:
      • 监听所有 Service、EndPoints 的变化。据此配置 iptables 规则,将访问 Service IP 的流量转发到 EndPoints 中的 pod_ip:port 。
        • EndPoints 包含多个 pod_ip 时,默认按随机算法进行负载均衡,还可选轮询算法。
      • 转发数据包时会进行 NAT ,实现透明代理。
        • 将数据包转发给 EndPoints 时,会将数据包的目标 IP 改为 pod_ip ,即 DNAT 。
        • 转发 EndPoints 返回的数据包时,会将数据包的源 IP 改为 pod_ip ,即 SNAT 。
    • 缺点:
      • 修改 iptables 规则时,需要先用 iptables-save 导出,然后修改,最后用 iptables-restore 导入,有一定耗时。
      • 处理每个数据包时,需要线性查找与其匹配的 iptables 规则,时间复杂度为 O(n) 。因此 Service 数量较多时,耗时较久。
  • IPVS
    • k8s v1.8 新增的模式,相当于在 iptables 模式的基础上增加 IPVS 负载均衡器。
    • 原理:
      • 为每个 Service IP 创建一个 IPVS 负载均衡器。
      • 通过 ipset 命令创建一些包含 Service IP 的哈希集合。然后配置 iptables 规则,将访问 ipset 集合的流量交给 IPVS 处理,并进行 NAT 。
    • 优点:
      • 通过 ipset 大幅减少了 iptables 规则的数量,并且哈希查找的速度更快。
      • 支持多种负载均衡算法。

# etcd

  • 功能:提供分布式数据库,存储 k8s 的配置、状态数据。
  • 默认监听 TCP 2379、2380 端口。
    • 一般将 etcd 部署在主节点上,仅供本机的 apiserver 访问。
    • 也可以将 etcd 部署在无 apiserver 的主机上,或者部署在 k8s 集群之外。