欢迎光临
我们一直在努力

K8s SRE 项目复盘:所有 C 开头的组件/协议,一篇讲透

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

最近把自己用 Ansible + Argo CD + Prometheus + Ollama + Go 搭的那套「K8s SRE 智能运维助手」从头到尾复盘了一遍。复盘时发现一个有意思的现象:整个技术栈里,C 开头的组件、协议、对象名密度异常高——从容器运行时到网络插件,从 K8s 原生对象到 Go 编译参数,几乎每一层都有它们的身影。

这篇文章把项目里真正用到过的 C 开头名词全部捞出来,写清全称、作用、以及我在项目里的实际用法。按「运行时 → 网络 → K8s 原生对象 → 工程协议」四层拆开,方便面试和复习时对照。


一、一张总览表

分类缩写 / 名词全称在本项目里的角色
运行时 containerd containerd K8s 节点上跑容器的底层运行时
运行时 CRI Container Runtime Interface K8s 与容器运行时之间的标准接口
运行时 crictl CRI CLI(kubernetes-sigs) 直接跟 CRI 运行时对话的排错命令
运行时 ctr containerd CLI containerd 自带的镜像 / 容器管理命令
运行时 cgroup Control Group Linux 用来限制容器 CPU / 内存的内核机制
运行时 CGO C Go bindings Go 调用 C 代码的机制,本项目用 CGO_ENABLED=0 关它
网络 CNI Container Network Interface K8s 与网络插件之间的标准接口
网络 Calico Project Calico 本项目实际选用的 CNI 网络插件
网络 CoreDNS CoreDNS 集群内部 DNS 服务,Service 域名解析靠它
网络 CIDR Classless Inter-Domain Routing 无类域间路由,用来划 Pod 网段
K8s 对象 client-go / Clientset official Go client for Kubernetes 我那只 Go SRE Agent 操作 K8s API 的 SDK
K8s 对象 CRD Custom Resource Definition PrometheusRule / ServiceMonitor 这类自定义资源
K8s 对象 ConfigMap Config Map K8s 里存放非敏感配置的对象
K8s 对象 ClusterRole Cluster Role 集群级 RBAC 权限定义
K8s 对象 ClusterRoleBinding Cluster Role Binding 把 ClusterRole 绑给 ServiceAccount
K8s 对象 ClusterIP Cluster IP K8s Service 的默认虚拟 IP 类型
K8s 对象 Context context.Context Go 标准库,用来传递取消 / 超时信号
工程 CI / CD Continuous Integration / Delivery 持续集成 / 持续交付,Argo CD 落地 CD 段
工程 curl Command Line Request URL 命令行 HTTP 客户端,排查 Ollama / Prometheus 接口全靠它
工程 Chart Helm Chart Helm 把一堆 K8s YAML 打成的可复用包

二、容器运行时层:让 Pod 真正跑起来

1. containerd

全称:containerd(本身就是名字,不是缩写)

是干什么的:一个开源的、工业级的容器运行时,负责镜像管理、容器生命周期、存储挂载和网络 namespace。K8s 从 1.24 开始把 dockershim 删了,现在 kubelet 直接通过 CRI 跟 containerd 这类运行时对话。

项目里怎么用:在 Ansible 的 common 角色里,我用 dnf 装 containerd.io,然后执行:

containerd config default > /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

把 cgroup driver 切成 systemd——这一步必须跟 kubelet 的 cgroup driver 一致,否则容器会反复重启,是新手最常见的坑之一。


2. CRI —— Container Runtime Interface

全称:Container Runtime Interface(容器运行时接口)

是干什么的:K8s 定义的一套 gRPC 标准接口,位于 kubelet 和真正的容器运行时之间。有了它,kubelet 不用关心底下是 containerd、CRI-O 还是别的,只要对方实现了 CRI,就能被 K8s 接管。

项目里怎么用:我没有手写 CRI 代码,但 kubelet 跟 containerd 之间的所有通信都走它。排错时用 crictl 直接跟 CRI 层对话,可以绕开 kubectl 看到 kubelet 视角下的真实容器状态。


3. crictl

全称:CRI CLI(Kubernetes SIG 维护的命令行工具)

是干什么的:直接跟 CRI 运行时对话的客户端,相当于「kubelet 视角下的 docker ps」。当 Pod 一直 ContainerCreating 或 CrashLoopBackOff,kubectl 看到的信息不够细时,就轮到它上场。

项目里怎么用:

crictl images # kubelet 视角看到的镜像
crictl ps -a # 所有容器(包括已经退出的)
crictl logs <id> # 容器日志


4. ctr

全称:containerd CLI(containerd 自带的命令行)

是干什么的:containerd 自己的管理工具,跟 crictl 不同——它直接操作 containerd 的命名空间,不经过 CRI。

项目里怎么用:这是本项目一个关键环节。因为实验环境没有中央镜像仓库,我用 Docker 构建 sre-agent:v1.0 后:

docker save sre-agent:v1.0 -o sre-agent-v1.0.tar
ctr -n k8s.io image import sre-agent-v1.0.tar

重点是 -n k8s.io:kubelet 通过 CRI 看的是 containerd 的 k8s.io 命名空间,不写这个参数,镜像导到默认 namespace 里,kubelet 根本看不见,Pod 照样 ImagePullBackOff。


5. cgroup

全称:Control Group(控制组)

是干什么的:Linux 内核的一个机制,用来把一组进程放到一个「组」里,给这个组限制 CPU、内存、磁盘 IO、PID 数等资源。K8s 里每个 Pod 的 requests / limits,最终都是通过 cgroup 落到节点内核上的。

项目里怎么用:配合 SystemdCgroup = true,让 systemd 作为 cgroup 管理器。这样 systemd、kubelet、containerd 三方在同一个 cgroup driver 下协作,不会出现「kubelet 以为限了 1G,containerd 以为没限」的错位。


6. CGO

全称:C Go bindings(Go 调用 C 代码的机制)

是干什么的:默认情况下,Go 编译涉及系统调用的部分(比如 DNS 解析、用户信息查询)可能会链接系统的 C 库(glibc),产出动态链接的二进制。

项目里怎么用:我的 Dockerfile 里有一行关键编译参数:

RUN CGO_ENABLED=0 GOOS=linux go build -o /sre-agent .

  • CGO_ENABLED=0:禁用 CGO,强制纯 Go 实现,产出完全静态链接的二进制。
  • 为什么要这么做?因为基础镜像是 alpine,它用的是 musl libc 而不是 glibc,动态链接会导致运行时库不匹配。
  • 静态链接之后,二进制不依赖任何外部库,甚至可以直接换成 scratch 空镜像,体积从几百 MB 砍到十几 MB。

三、网络层:让 Pod 之间、Pod 与外部能通信

7. CNI —— Container Network Interface

全称:Container Network Interface(容器网络接口)

是干什么的:跟 CRI 齐名的另一个 K8s 标准接口,定义了「一个网络插件应该长什么样」。只要插件实现了 CNI 规范,kubelet 在创建 Pod 时就会调用它,给 Pod 分配 IP、接好网线、配好路由。

项目里怎么用:我装的 Calico 就是一个 CNI 插件。kubeadm init 时传了 –pod-network-cidr=10.244.0.0/16,这个网段就是给 CNI 插件用的。


8. Calico

全称:Project Calico(本身是项目名,不是缩写)

是干什么的:主流 CNI 插件之一,用 BGP 路由的方式给每个 Pod 分配 IP,实现跨节点 Pod 互通。同时自带 NetworkPolicy 能力,可以在 IP 表之上做细粒度的网络访问控制。

项目里怎么用:

kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.3/manifests/calico.yaml

装完之后,kubectl get pods -n kube-system 里会看到 calico-node(以 DaemonSet 形式跑在每个节点上)和 calico-kube-controllers。这俩 Pod 不 Running,整个集群的 Pod 网络就是断的。


9. CoreDNS

全称:CoreDNS(本身是项目名)

是干什么的:K8s 集群内部的 DNS 服务器。Service 创建后会得到一个 DNS 名(比如 ollama-service.ai-services.svc.cluster.local),Pod 之间要靠它做服务发现。

项目里怎么用:它是 kubeadm init 后自动以 Deployment 形式装在 kube-system 命名空间里的。我在排错时踩过一个大坑:Argo CD 的 repo-server Pod 因为 DNS 解析不了 github.com,所有应用状态都变成 Unknown。最后通过修 CoreDNS 的上游配置才解决——这让我意识到 GitOps 工具自身的网络连通性,是整个自动化体系的基石。


10. CIDR —— Classless Inter-Domain Routing

全称:Classless Inter-Domain Routing(无类域间路由)

是干什么的:一种表示 IP 网段的标准写法,比如 10.244.0.0/16 表示从 10.244.0.0 到 10.244.255.255 的整整 65536 个 IP。

项目里怎么用:在 Ansible inventory 里定义:

[all:vars]
pod_network_cidr = "10.244.0.0/16"

然后 kubeadm init –pod-network-cidr={{ pod_network_cidr }} 传给集群,Calico 就从这个大池子里给 Pod 分 IP。这个网段不能跟节点物理网段、NodePort 网段冲突,否则会出现诡异的路由黑洞。


四、K8s 原生对象与 SDK:Agent 跟集群对话的语言

11. client-go / Clientset

全称:official Go client for Kubernetes

是干什么的:K8s 官方出的 Go 语言 SDK。kubernetes.Clientset 是它的核心对象,实例化之后就能用它去 list / get / watch / delete Pod、读日志、查事件——我那只 SRE Agent 就是靠它干活的。

项目里怎么用:

import "k8s.io/client-go/kubernetes"
clientset, _ := kubernetes.NewForConfig(config)

// 感知:读 Pod 详情和日志
pod, _ := clientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})

// 行动:删 Pod 触发 Deployment 重建
clientset.CoreV1().Pods(namespace).Delete(ctx, podName, metav1.DeleteOptions{})

并且我改造了客户端初始化逻辑,优先 rest.InClusterConfig() 读 Pod 内挂载的 ServiceAccount Token,失败再回退本地 kubeconfig——同一份代码既能本地调试,又能丢进集群里跑。


12. CRD —— Custom Resource Definition

全称:Custom Resource Definition(自定义资源定义)

是干什么的:K8s 的扩展机制。默认 K8s 只认识 Deployment、Service 这些内置对象;但你可以用 CRD 「发明」自己的对象类型,比如 PrometheusRule、ServiceMonitor、Application(Argo CD 的)。

项目里怎么用:我写的告警规则:

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule

这里的 PrometheusRule 就是 kube-prometheus-stack 通过 CRD 注册进来的。Argo CD 的 kind: Application 同理。


13. ConfigMap

全称:Config Map

是干什么的:K8s 里存放非敏感配置的对象,把配置从镜像里解耦出来,挂载进 Pod 当文件或环境变量用。

项目里怎么用:本项目没有显式写 ConfigMap(因为 Agent 的 URL 和模型名直接硬编码在常量里了——这是我复盘时标记的一个改进点:应该挪到 ConfigMap,做到改配置不重新打镜像)。


14. ClusterRole / ClusterRoleBinding

全称:Cluster Role / Cluster Role Binding(集群角色 / 集群角色绑定)

是干什么的:K8s RBAC 体系里的两个对象。

  • ClusterRole:定义一组权限(能 get/list/watch 哪些资源、能不能 delete)。
  • ClusterRoleBinding:把这组权限绑给某个 ServiceAccount / User / Group。

项目里怎么用:我给 SRE Agent 配了最小权限:

rules:
– apiGroups: [""]
resources: ["pods", "pods/log", "events"]
verbs: ["get", "list", "watch"]
– apiGroups: [""]
resources: ["pods"]
verbs: ["delete"]

只让它看 Pod、日志、事件,并且只能删 Pod——不能改 Deployment、不能碰 Secret。这是「最小权限原则」的实践:Agent 被打爆了,攻击者能拿到的也只有这点权限。


15. ClusterIP

全称:Cluster IP

是干什么的:K8s Service 的默认类型。会给 Service 分配一个集群内部的虚拟 IP,只能在集群内部访问,配合 CoreDNS 用域名调用。

项目里怎么用:Ollama、Prometheus、Grafana 我都显式改成了 NodePort 以便从外部浏览器访问,但 Agent 在 Pod 内部调用 Ollama 时,最终走的还是 ClusterIP + DNS 这条内部路径——这也是我前面提到那个「硬编码 NodePort 导致 Pod 内调用静默失败」bug 的根因。


16. context

全称:context.Context(Go 标准库包)

是干什么的:Go 官方用来在 API 边界之间传递取消信号、超时和请求域数据的工具。client-go 的所有方法第一个参数都是 ctx context.Context。

项目里怎么用:

pod, err := clientset.CoreV1().Pods(namespace).Get(context.TODO(), podName, metav1.GetOptions{})

context.TODO() 是先占个位;生产环境应该传带超时的 context.WithTimeout,防止 kube-apiserver 卡住时整个 Agent 跟着挂死。


五、工程与协议层:贯穿整个项目的基础设施

17. CI / CD

全称:Continuous Integration / Continuous Delivery(持续集成 / 持续交付)

是干什么的:

  • CI:代码合并后自动跑测试、构建镜像。
  • CD:把构建好的产物自动部署到目标环境。

项目里怎么用:我没有搭完整的 CI 流水线(用 Git 手动 push),但 CD 段完全交给了 Argo CD——Git 仓库就是唯一真相源,Argo CD 自动把 Git 里的 YAML 同步到集群,还开了 selfHeal:谁要是敢手动 kubectl patch 改线上,它会立刻把状态纠回 Git 里的样子。


18. curl

全称:Command Line Request URL(命令行 URL 请求工具)

是干什么的:最常用的命令行 HTTP 客户端。排查任何 HTTP 接口是否通、返回什么,第一反应就是 curl -v 或 curl -s。

项目里怎么用:排错三板斧:

curl -s http://<prom>:9090/api/v1/alerts | jq '.data.alerts[] | select(.state=="firing")'
curl -s 'http://<prom>:9090/api/v1/query?query=up'
curl -s http://${PODIP}:11434/api/tags

以及最初装 Helm 也是用 curl 下载官方脚本。它看起来不起眼,但没有 curl,整个 SRE 排错链路会慢十倍。


19. Helm Chart

全称:Helm Chart(Helm 的打包格式)

是干什么的:Helm 把一个应用涉及的一堆 K8s YAML 加上 Go 模板和 values.yaml 打成一个标准化包,就像 apt 的 .deb、yum 的 .rpm。

项目里怎么用:Prometheus 那一套就是用 kube-prometheus-stack 这个 Chart 装的:

helm install prometheus-stack prometheus-community/kube-prometheus-stack \\
–namespace monitoring –create-namespace –version 57.2.0

一个 Chart 下来,Prometheus、Alertmanager、Grafana、operator、node-exporter、kube-state-metrics 全齐了。对于有状态、社区已经有成熟方案的应用,直接用 Chart 比手写 YAML 高效太多。


六、复盘时还提到,但本项目没真部署的 C 开头名词

这几个名词在「生产环境应该怎么改进」那一段出现过,但没有真的在我的实验集群里部署,列出来防止以后混淆:

名词全称为什么没部署
CSI Container Storage Interface 实验环境没有云盘也没有 StorageClass,我用 hostPath PV 顶了;上生产才需要 CSI 动态供给
CronJob Cron Job 我的 Agent 是常驻 Deployment,靠 for 循环轮询;真要做定时清理任务才会用 CronJob
Ceph Ceph Distributed Storage 太重,单机三台虚机扛不动;改进方向里会换成 Longhorn 或 OpenEBS LocalPV

七、写在最后

把这一串 C 开头名词串起来看,会发现它们其实是一条完整的链:

kubelet 通过 CRI 调用 containerd,containerd 通过 CNI(Calico)给 Pod 插网线,靠 CoreDNS 做服务发现,用 cgroup 限制资源;Pod 里跑着我那个用 client-go 写的 SRE Agent,Agent 通过 curl 调 Prometheus 和 Ollama,再用 ClusterRole 授权自己删故障 Pod;整个部署由 Helm Chart 打包、Argo CD 按 GitOps 同步。


赞(0)
未经允许不得转载:171主机测评 » K8s SRE 项目复盘:所有 C 开头的组件/协议,一篇讲透
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址