一、基础组件
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
- 鉴权信息验证
- Admission control验证(确保符合广泛的集群规则和限制) (包括资源管理 安全管理 默认值管理 引用一致性等类型)
- post写入etcd, etcd中的key默认格式是namespace/name
- 再get获取etcd中该key,确保写入成功
- 返回生成的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 过滤出可调度节点列表
- 只看pod的request 不看limit
- 所有符合资源需求的节点加入list
- 通过内置算法给节点打分, 得分最高的 node 会被选中
- 请求创建v1.Binding对象
- apiserver收到请求后, 更新pod的NodeName字段
此时, pod已经被指定调度到一个节点, etcd中的元数据准备好了, 下一步就是将这个pod的状态同步到对应节点上.
接下来就是节点上的kubelet开始干活
6. kubelet
- 通过 ListWatch 接口,从 kube-apiserver 根据spec.nodeName过滤属于本节点的 Pod 列表
- 与自己缓存的 pod 列表对比,如果有 pod 创建、删除、更新等操作,就开始同步状态
- 如果是 pod 创建事件,会记录一些 pod latency 相关的 metrics
- 生成一个 v1.PodStatus 对象 代表当前pod阶段的状态, 并且异步通过 apiserver 更新 etcd 记录
- 运行一系列 admission handlers,确保 pod 有正确的安全权限
- 创建容器数据目录, 比如默认的/var/lib/kubelet下的pod目录, volumes目录
- 从apiserver获取image pull secert, 注入容器
- runtime创建container
7. runtime
kubelet 从 v1.5.0 开始,使用 CRI(Container Runtime Interface)与具体的容器运行时交互
k8s集群版本从1.22 - 1.24(完全移除) 过渡中, 完全移除了docker作为容器运行时的支持, 必须使用cri兼容的运行时 如contaienrd
CRI 提供了 kubelet 和具体 runtime implementation 之间的抽象接口
- CRI create sandbox(pause) ,用于pod中容器的ns创建(资源隔离), 并作为Pod生命周期的锚点,确保Pod在各种状态下都能保持其网络环境和基本生命周期(也就是所谓的占坑)
- 调用 CNI 插件为容器设置网络
- 创建 init 容器及业务容器, pod内的容器对应挂载之前创建好的工作目录 \ 注入环境变量 secert等初始化任务
- 遵循pod的readiness liveness startup Probe 开始维护pod生命周期
三、升级
准备
- etcd备份
- 集群资源快照
- pvc挂载情况快照
- api弃用情况, 我们是通过每个版本的Changelog 加上历史维护集群资源使用的apiversion对比
- helm 通过Helm MapkubeAPIs插件来检查
- 可以提前用kubectl convert工具来迁移api版本, 也可以手搓加深更新了解
升级策略
- 先挨个升master (master上没有业务pod)
- worker 挨个 cordon排水, 升级, 取消污点恢复ready状态
- 资源检查 业务检查
四、进阶用法
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