K8S一些进阶运维知识

一、基础组件

Master节点上有:

kube-apiserver

整个系统唯一入口,面向用户的kubectl,还是所有其他组件都必须走apiserver进行访问或修改集群状态
其实就是etcd的前端,提供watch机制,提供给其他组件通过watch api实时获取资源对象的状态,这是实现声明式api和控制器模式的关键

etcd (可能单独部署?)

强一致性分布式数据库,所有资源状态存储,操作资源其实就是操作etcd中的数据,高可用部署、监控、备份都是重中之重

kube-controller-manager

k8s 中一系列控制器 (Controllers) 的守护进程。这些控制器负责确保集群的当前状态 (Current State) 向用户声明的期望状态 (Desired State) 收敛。
常见的控制器有 node、rs、deployment、statefulset、job、service等

kube-scheduler

负责将新创建、未分配状态的pod调度到适合的节点上,有评分优先级可以去官仓看

worker节点上有

kube-proxy

节点网络代理,配合service实现集群内负载均衡、代理
配合iptables或ipvs这种系统基础设施维护网络规则

kubelet

节点Agent,控制面与节点沟通的桥梁
负责节点上pod的生命周期维护、上报、挂载
通过runtime与容器交互

容器运行时

containerd、docker
原理, 从同道就开始玩容器

二、k8s资源创建流程

1. kubectl

本地资源格式校验, 转换资源为http请求
获取鉴权信息(x509 token basic) 加入http请求
与apiserver协商api版本, (apiserver/aps有openapi scahme)
提交http请求给apiserver

2. apiserver

  1. 鉴权信息验证
  2. Admission control验证(确保符合广泛的集群规则和限制) (包括资源管理 安全管理 默认值管理 引用一致性等类型)
  3. post写入etcd, etcd中的key默认格式是namespace/name
  4. 再get获取etcd中该key,确保写入成功
  5. 返回生成的http response

另外apiserver通过list-watch监听etcd中的pod资源, etcd的key创建好会返回给apiserver一个create事件, apiserver会将pod信息更新到它自己的in-memory cache里

这一套走完, etcd中有了, 但是kubectl 还get不到

3. Initializers

etcd中有了, apiserver先不回将其设置为对外可见, 而是先运行Initializers
Initializers可以设置一些通用的启动初始化任务, 比如 (sidecar注入 端口暴露 私钥注入等)
可以通过创建Kind: InitializerConfiguration 来维护, 后面就会被追加到每个pod的 metadata.initializers.pending字段

4. 控制器

下一步是设置资源拓扑, 其实一个deployment就是一组rs, 而一个rs就是一组pod
k8s通过大量内置的控制器维护这个层级关系

K8s 中大量使用 “controllers”,

  • 一个 controller 就是一个异步脚本(an asynchronous script)
  • 不断检查资源的当前状态(current state)和期望状态(desired state)是否一致
  • 如果不一致就尝试将其变成期望状态,这个过程称为 reconcile。

控制器处理完, etcd中会有对应拓补下所有资源, 例如 deployment * 1 + rs * 1 + pods * 2
此时kubectl可以get到, 但是pod是Pending的

5. 调度器 scheduler

scheduler 会循环寻找所有 nodeName 字段为空的 pod,为它们选择合适的 node

优先级 首先是直接指定nodename 污点容忍 亲和性
符合后再通过 显式 Resource requests/limits 过滤出可调度节点列表

  1. 只看pod的request 不看limit
  2. 所有符合资源需求的节点加入list
  3. 通过内置算法给节点打分, 得分最高的 node 会被选中
  4. 请求创建v1.Binding对象
  5. apiserver收到请求后, 更新pod的NodeName字段

此时, pod已经被指定调度到一个节点, etcd中的元数据准备好了, 下一步就是将这个pod的状态同步到对应节点上.
接下来就是节点上的kubelet开始干活

6. kubelet

  1. 通过 ListWatch 接口,从 kube-apiserver 根据spec.nodeName过滤属于本节点的 Pod 列表
  2. 与自己缓存的 pod 列表对比,如果有 pod 创建、删除、更新等操作,就开始同步状态
  3. 如果是 pod 创建事件,会记录一些 pod latency 相关的 metrics
  4. 生成一个 v1.PodStatus 对象 代表当前pod阶段的状态, 并且异步通过 apiserver 更新 etcd 记录
  5. 运行一系列 admission handlers,确保 pod 有正确的安全权限
  6. 创建容器数据目录, 比如默认的/var/lib/kubelet下的pod目录, volumes目录
  7. 从apiserver获取image pull secert, 注入容器
  8. runtime创建container

7. runtime

kubelet 从 v1.5.0 开始,使用 CRI(Container Runtime Interface)与具体的容器运行时交互
k8s集群版本从1.22 - 1.24(完全移除) 过渡中, 完全移除了docker作为容器运行时的支持, 必须使用cri兼容的运行时 如contaienrd
CRI 提供了 kubelet 和具体 runtime implementation 之间的抽象接口

  1. CRI create sandbox(pause) ,用于pod中容器的ns创建(资源隔离), 并作为Pod生命周期的锚点,确保Pod在各种状态下都能保持其网络环境和基本生命周期(也就是所谓的占坑)
  2. 调用 CNI 插件为容器设置网络
  3. 创建 init 容器及业务容器, pod内的容器对应挂载之前创建好的工作目录 \ 注入环境变量 secert等初始化任务
  4. 遵循pod的readiness liveness startup Probe 开始维护pod生命周期

三、升级

准备

  • etcd备份
  • 集群资源快照
  • pvc挂载情况快照
  • api弃用情况, 我们是通过每个版本的Changelog 加上历史维护集群资源使用的apiversion对比
  • helm 通过Helm MapkubeAPIs插件来检查
  • 可以提前用kubectl convert工具来迁移api版本, 也可以手搓加深更新了解

升级策略

  1. 先挨个升master (master上没有业务pod)
  2. worker 挨个 cordon排水, 升级, 取消污点恢复ready状态
  3. 资源检查 业务检查

四、进阶用法

helm

helm基于jinja2的Template , 配合argocd的自动化实现

network

NetworkPolicy,podselector选择给哪个服务配置, Ingress和egress控制出入流量, 每种gress下还有namespace和pod的selector, 以及ipblock控制src或dst

hpa

通过容器指标(一般通过summary-Exporter暴露pod cpu\内存基础指标)
自定义指标, 例如Springboot exporter这种是需要通过Prometheus Adapter进行转换, 转换为kubernetes metrics api才可以使用


转载请注明来源, 欢迎对文章中的引用来源进行考证, 欢迎指出任何有错误或不够清晰的表达, 可以邮件至 chinaops666@gmail.com
相册