别再死记硬背K8s概念了!用Docker和几个Go程序亲手拆解API Server与etcd
2026/6/12 2:52:53 网站建设 项目流程

从零拆解Kubernetes控制平面:用Docker和Go还原API Server与etcd交互本质

很多工程师在初次接触Kubernetes时,都会被其复杂的控制平面组件搞得晕头转向。API Server、etcd、Controller Manager这些名词在文档里反复出现,但仅靠阅读理论资料很难真正理解它们如何协同工作。本文将带你用Docker容器和简单的Go程序,亲手搭建一个迷你Kubernetes控制平面,通过"造轮子"的方式逆向理解这些核心组件的工作机制。

1. 实验环境搭建:容器化组件模拟

我们先从最基础的环境准备开始。这个实验不需要完整的Kubernetes集群,只需要Docker和Go语言环境就能复现核心交互流程。

# 启动一个单节点etcd容器 docker run -d --name my-etcd \ -p 2379:2379 \ -e ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379 \ -e ETCD_ADVERTISE_CLIENT_URLS=http://localhost:2379 \ quay.io/coreos/etcd:v3.5.0

这个etcd容器将作为我们模拟Kubernetes的键值存储后端。接下来我们准备一个简化的API Server模拟程序:

package main import ( "context" "log" "net/http" "go.etcd.io/etcd/clientv3" ) type APIServer struct { etcdClient *clientv3.Client } func NewAPIServer() *APIServer { cli, err := clientv3.New(clientv3.Config{ Endpoints: []string{"localhost:2379"}, }) if err != nil { log.Fatal(err) } return &APIServer{etcdClient: cli} } func (s *APIServer) HandlePodCreate(w http.ResponseWriter, r *http.Request) { // 模拟处理Pod创建请求 _, err := s.etcdClient.Put(context.Background(), "/pods/default/nginx", "nginx-pod-data") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Write([]byte("Pod created")) } func main() { apiServer := NewAPIServer() http.HandleFunc("/pods", apiServer.HandlePodCreate) log.Fatal(http.ListenAndServe(":8080", nil)) }

这个简化版的API Server实现了最核心的功能:接收HTTP请求并将数据写入etcd。虽然真实的Kubernetes API Server要复杂得多,但这个模拟程序已经包含了最本质的交互模式。

2. etcd数据模型深度解析

Kubernetes将所有集群状态都存储在etcd中,理解其存储结构是掌握控制平面工作原理的关键。让我们通过几个实验来观察典型的数据组织方式。

Kubernetes在etcd中的核心路径结构:

  • /registry/pods/<namespace>/<pod-name>- Pod资源
  • /registry/services/<namespace>/<service-name>- Service资源
  • /registry/deployments/<namespace>/<deployment-name>- Deployment资源
  • /registry/events/<namespace>/<event-name>- 事件信息

我们可以用etcdctl工具直接查询这些数据:

# 查询所有Pod资源 docker exec my-etcd etcdctl get /registry/pods --prefix # 写入一个模拟的Deployment docker exec my-etcd etcdctl put /registry/deployments/default/nginx-deployment \ '{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"nginx-deployment"},"spec":{"replicas":3}}'

etcd数据版本控制机制:

Kubernetes严重依赖etcd的版本控制功能来实现watch机制。每个修改都会产生一个新的修订版本(revision),客户端可以监听特定版本的变更:

// 监听Pod资源变化 watchChan := etcdClient.Watch(context.Background(), "/registry/pods/", clientv3.WithPrefix()) for resp := range watchChan { for _, ev := range resp.Events { log.Printf("Event type: %s, Key: %s, Value: %s\n", ev.Type, ev.Kv.Key, ev.Kv.Value) } }

这个watch机制是Controller Manager等组件感知集群状态变化的基础。

3. API Server认证授权全流程实验

API Server是Kubernetes的网关,所有请求都要经过认证(Authentication)、授权(Authorization)和准入控制(Admission Control)三个阶段。我们来模拟实现这个流程。

3.1 认证(Authentication)实验

创建一个模拟ServiceAccount认证的中间件:

func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("Authorization") if token != "Bearer system:serviceaccount:default:default" { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } next(w, r) } }

然后修改API Server的路由:

http.HandleFunc("/pods", AuthMiddleware(apiServer.HandlePodCreate))

3.2 授权(RBAC)实验

在etcd中存储RBAC规则:

docker exec my-etcd etcdctl put /registry/roles/default/pod-creator \ '{"rules":[{"apiGroups":[""],"resources":["pods"],"verbs":["create"]}]}' docker exec my-etcd etcdctl put /registry/rolebindings/default/pod-creator-binding \ '{"subjects":[{"kind":"ServiceAccount","name":"default","namespace":"default"}],"roleRef":{"kind":"Role","name":"pod-creator"}}'

然后在API Server中添加授权检查:

func (s *APIServer) checkAuthorization(user, verb, resource string) bool { resp, err := s.etcdClient.Get(context.Background(), fmt.Sprintf("/registry/rolebindings/default/%s", user)) // 简化实现,实际Kubernetes有更复杂的RBAC评估逻辑 return err == nil && len(resp.Kvs) > 0 }

4. 控制器工作原理实践

Controller Manager中的各种控制器通过监听etcd中的资源变化来驱动集群达到期望状态。我们来模拟一个简单的Deployment控制器。

首先创建一个控制器程序监听Deployment和ReplicaSet的变化:

func (c *DeploymentController) Run() { deployChan := c.watch("/registry/deployments/") rsChan := c.watch("/registry/replicasets/") for { select { case event := <-deployChan: c.syncDeployment(event) case event := <-rsChan: c.syncReplicaSet(event) } } } func (c *DeploymentController) syncDeployment(event clientv3.Event) { // 解析Deployment // 检查对应的ReplicaSet是否存在 // 如果不存在就创建 _, err := c.etcdClient.Put(context.Background(), "/registry/replicasets/default/nginx-rs", `{"spec":{"replicas":3}}`) if err != nil { log.Printf("Failed to create ReplicaSet: %v", err) } }

这个简化版的控制器实现了Deployment控制器的核心逻辑:监听Deployment变化,并确保对应的ReplicaSet存在且符合期望状态。

5. 完整交互流程实验

现在让我们把这些组件串联起来,观察一个完整的Pod创建流程:

  1. 用户通过kubectl发送创建Pod请求
  2. API Server接收请求并验证认证信息
  3. API Server检查RBAC授权规则
  4. 通过验证后将Pod数据写入etcd
  5. 调度器监听Pod变化,为未调度Pod选择节点
  6. Kubelet监听已调度到本节点的Pod,启动容器

我们可以用cURL模拟这个过程:

# 模拟kubectl请求 curl -X POST http://localhost:8080/pods \ -H "Authorization: Bearer system:serviceaccount:default:default" # 查看etcd中的Pod数据 docker exec my-etcd etcdctl get /registry/pods --prefix

通过这些实验,你应该对Kubernetes控制平面各组件的协作有了更直观的理解。虽然真实的Kubernetes实现要复杂得多,但核心原理与我们这个简化模型是一致的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询