bigywater.tech
ZooKeeper · 107

ZooKeeper 与 Kubernetes 控制面

ZooKeeper 与 Kubernetes 控制面是什么关系?答案是:没有关系。但「没有」这个结论值得验证。这篇从 Kubernetes 的源码编译出真正的 kube-apiserver,让它连本机 etcd,看它往 etcd 里写什么、resourceVersion 与 etcd 的 revision 是什么关系;读存储工厂的源码确认只有 etcd3;最后试一下把它指向 ZooKeeper,看它怎么失败。

57 个 key
空集群启动后 etcd 的 /registry 下已有的 key 数
= mod_revision
API 的 resourceVersion 与 etcd 该 key 的 mod_revision 相等
退出码 1
kube-apiserver 的 --etcd-servers 指向 ZooKeeper 时的结果

apiserver 在 etcd 里的数据

实测 · 本机执行输出
  GET /readyz → ok,启动到就绪 2 s
  etcd 里 /registry 下共 57 个 key,第一级分类 14 种,前 14 种:['apiregistration.k8s.io', 'configmaps', 'endpointslices', 'flowschemas', 'ipaddresses', 'leases', 'masterleases', 'namespaces', 'peerserverleases', 'priorityclasses', 'prioritylevelconfigurations', 'ranges', 'servicecidrs', 'services']
实测 · 本机执行输出
  POST /api/v1/namespaces → 201
  POST /api/v1/namespaces/demo/configmaps → 201 (201 = 创建成功)
  etcd 里含 demo / cm1 的 key: ['/registry/configmaps/demo/cm1', '/registry/namespaces/demo']
  值的前 12 个字节:b'k8s\x00\n\x0f\n\x02v1\x12\t'(Kubernetes 的 protobuf 存储格式前缀 k8s\x00),总长 191 字节;值里可见的字符串:['k8s', 'ConfigMap', 'cm1', 'demo"', '*$43f2ce51-6679-4ec2-b27a-b00f448a9e202', 'Python-urllib']

只启动了 kube-apiserver 与 etcd,没有 controller-manager、scheduler、kubelet,所以只验证了「控制面的存储」这一层,不是完整集群。

resourceVersion 就是 etcd revision

实测 · 本机执行输出
  resourceVersion = 73;etcd mod_revision = 73;相等:True
  PATCH 修改 data.a → c: 200
  修改后 resourceVersion = 74;etcd mod_revision = 74;相等:True

下一篇(ZooKeeper 与 etcd 对比)会具体比较两者的 watch 与版本模型。

源码:存储后端只有 etcd3

staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/factory.go(kubernetes v1.37.1) func Create(c storagebackend.ConfigForResource, newFunc, newListFunc func() runtime.Object, resourcePrefix string) (storage.Interface, DestroyFunc, error) { switch c.Type { case storagebackend.StorageTypeETCD2: return nil, nil, fmt.Errorf("%s is no longer a supported storage backend", c.Type) case storagebackend.StorageTypeUnset, storagebackend.StorageTypeETCD3: return newETCD3Storage(c, newFunc, newListFunc, resourcePrefix) default: return nil, nil, fmt.Errorf("unknown storage type: %s", c.Type) }

另外在 v1.37.1 源码目录里统计:go.mod 里有 go.etcd.io/etcd/client/v3 v3.7.0,go.mod 与 vendor/modules.txt 中 zookeeper 出现 0 次(这两个数字是我用 grep 查的,没有写进实验输出文件)。

指向 ZooKeeper

实测 · 本机执行输出
  60 秒后 GET https://127.0.0.1:7652/readyz → 0 URLError <urlopen error [Errno 61] Connection refused>;apiserver 进程已退出,退出码 1
  日志里的 E / W 行共 21 行,前 3 行与最后 1 行:
    W1001 14:13:47.792242   83619 authentication.go:841] AnonymousAuth is not allowed with the AlwaysAllow authorizer. Resetting AnonymousAuth to false. You should use a different authorizer
    E1001 14:13:47.975921   83619 options.go:205] The manifest file is empty, ignoring.
    W1001 14:13:47.977747   83619 logging.go:55] [core] [Channel #2 SubChannel #4] grpc: addrConn.createTransport failed to connect to {Addr: "127.0.0.1:7300", ServerName: "127.0.0.1:7300", }. Err: connection error: desc = "error read
    E1001 14:14:07.976197   83619 run.go:72] "command failed" err="error creating storage factory: etcd grpc connection not ready: context deadline exceeded"
  日志里是否出现 "Serving securely"(开始对外服务): False

分步演示

Kubernetes 与 ZooKeeper
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / ZooKeeper 3.9.6(brew 安装的 jar,用 java -cp 直接启动自己的实例,OpenJDK 17 Temurin,JVM 堆 128 MiB)/ 本机回环端口 7300+,数据在临时目录;默认 tickTime=2000;计时用 time.perf_counter,数字每次运行会有小幅抖动。kube-apiserver 由 kubernetes-src.tar.gz(v1.37.1)在本机 go build 得到(darwin/arm64,所以 --version 显示 v0.0.0-master+…);etcd 3.7.2;apiserver 用 --token-auth-file 与 AlwaysAllow 授权,只做 REST 调用(没有用 kubectl,因为本机 kubectl 在空 kubeconfig 下会交互式询问用户名);本机 Docker 无法拉取镜像,所以没有用 kind
    • 没有验证:完整的 Kubernetes 集群(kubelet、controller-manager)、etcd 的压缩与 defrag 行为、大规模集群的 etcd 负载、Kubernetes 以外的 etcd 使用者、其它版本的 apiserver
    京ICP备2026067502号-1京公网安备11010502063487号