本文整合了 Etcd 的基础知识体系,包括其核心简介、常用命令行工具 etcdctl 的操作、性能调优指南,以及 Etcd 在 Kubernetes 中的应用与数据管理。
1. etcd简介 简介 etcd 是一款分布式存储中间件,使用 Go 语言编写,并通过 Raft 一致性算法处理和确保分布式一致性,解决了分布式系统中数据一致性的问题
etcd 的核心架构
etcd Server : 对外接收和处理客户端的请求
gRPC Server : 面向客户端的接入层(对应 2379 端口、v3 API),把请求分发给 KV、Watch、Lease、Cluster 等 service,并通过 gRPC-gateway 兼容 HTTP/JSON 客户端。而 etcd 节点之间(peer,2380 端口)的 raft 消息与日志复制走的是 rafthttp 的 HTTP 流,不是 gRPC
MVCC : 多版本控制,etcd 的存储模块,键值对的每一次操作行为都会被记录存储,这些数据底层存储在 BoltDB 数据库中
Snapshot、WAL : WAL 预写式日志,etcd 中的数据提交前都会记录到日志。 Snapshot 快照,以防 WAL 日志过多,用于存储某一时刻 etcd 的所有数据。Snapshot 和 WAL 相结合,etcd 可以有效地进行数据存储和节点故障恢复等操作。
Raft 模块 : 实现分布式集群的一致性
MVCC:四个版本号和两个清理动作 “每一次操作行为都会被记录”这句话背后有一套版本号体系,理解它才用得动 watch 回放和历史查询。etcd 里有四个 版本号,分工完全不同:
版本号
作用域
含义
revision
全局
整个 keyspace 的逻辑时钟,任何一次写操作(含删除)都让它 +1,全局单调递增
create_revision
单个 key
这个 key 被创建时的 revision,key 被删掉重建会变
mod_revision
单个 key
这个 key 最后一次被修改时的 revision
version
单个 key
这个 key 被修改过多少次,创建时为 1,删除后重建归 1
1 2 3 4 etcdctl put /a 1 && etcdctl put /b 2 && etcdctl put /a 3 etcdctl get /a -w json
mod_revision 的典型用途是乐观并发控制 ——读的时候记下它,写的时候用事务断言”它没变过”,变了就说明有人插队:
1 2 3 4 etcdctl txn <<'EOF' mod("/a" ) = "4" put /a 5 EOF
按 revision 读历史,是 MVCC 最直接的收益:
1 2 etcdctl get /a --rev=2 etcdctl watch /a --rev=2
watch --rev 这个能力是 Kubernetes 各种 Informer 断线重连后能”续上”的基础:记住上次处理到哪个 revision,重连时从那里继续,不用重新 list 全量。
但历史不是永久的,compact 会把它砍掉。
1 2 etcdctl compact 100 etcdctl get /a --rev=2
那句 required revision has been compacted 是运维 etcd 时最常见的报错之一,K8s 里表现为 apiserver 日志刷 too old resource version,客户端不得不重新 list 全量。
compact 和 defrag 是两件不同的事,都得做:
作用
不做的后果
compact
丢弃旧版本,逻辑上 腾出空间
历史无限累积,撑爆 --quota-backend-bytes(默认 2GB),集群进入只读的 NOSPACE 告警状态
defrag
整理 BoltDB 文件碎片,把空间还给文件系统
db 文件大小一直不降,compact 完了 du 看着没变小
生产上 compact 应该交给 etcd 自己(--auto-compaction-mode=periodic --auto-compaction-retention=1h),defrag 需要逐个节点 手工做(它会短暂阻塞该节点的读写,所以别同时对所有节点执行):
1 2 etcdctl --endpoints=<node1> defrag etcdctl endpoint status -w table
lease 与分布式锁 lease(租约)是带 TTL 的对象,key 可以挂在 lease 上,lease 过期时挂在它上面的所有 key 一起被删除——服务注册与发现就靠这个做健康摘除。
1 2 3 etcdctl lease grant 60 etcdctl put --lease=694d71... /svc/a "1.2.3.4:80" etcdctl lease keep-alive 694d71...
有一点值得知道:lease 是走 raft 共识的对象,不是各节点本地的定时器 。所以 leader 切换后新 leader 会重新开始计时 ——这意味着一次 leader 选举可能让所有 lease 的实际存活时间比 TTL 更长,靠 lease 做故障检测时不能假设”TTL 到了立刻就删”。
基于 lease + 事务可以做分布式锁,etcdctl 直接内置了:
1 etcdctl lock mylock -- sh -c 'echo 拿到锁了; sleep 5'
它的实现思路值得知道:在 /mylock/ 前缀下用自己的 lease id 建一个 key,然后看这个前缀下 create_revision 最小的是不是自己——是就拿到锁,不是就 watch 前一个 key 等它消失。这样不会出现”惊群”(只有下一个候选者被唤醒),而 lease 保证了持锁进程崩溃后锁能自动释放。
版本选择 etcd 目前有 V2.x 和 V3.x 两个大版本
主流版本 V3.x。
安装 三节点部署, etcd 和 etcdctl 文件 在 /usr/bin 目录下
前置条件 节点 ip
1 2 3 172.20.40.173 172.20.40.196 172.20.40.107
生成 etcd 的server 证书 当前和 k8s 共同部署,k8s 文件均相同, 在 /etc/kubernetes/ssl/ 目录下
1 2 3 /etc/kubernetes/ssl/ca.pem /etc/kubernetes/ssl/ca-key.pem /etc/kubernetes/ssl/ca-config.json
/etc/kubernetes/ssl/ca-config.json 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@test-173 test]# cat /etc/kubernetes/ssl/ca-config.json { "signing": { "default": { "expiry": "87600h" }, "profiles": { "kubernetes": { "usages": [ "signing", "key encipherment", "server auth", "client auth" ], "expiry": "87600h" } } } }
etcd 使用的 etcd-csr.json 需要修改 IP , 在 /etc/etcd/ssl 目录下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [root@test-173 ssl]# pwd /etc/etcd/ssl [root@test-173 ssl]# cat etcd-csr.json { "CN": "etcd", "hosts": [ "127.0.0.1", "172.20.40.173" # 需要每个节点对应修改 ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "HangZhou", "L": "XS", "O": "k8s", "OU": "System" } ] }
在每个节点的 的 /etc/etcd/ssl 目录下执行, 生成 etcd.csr , etcd-key.pem , etcd.pem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@test-107 ssl]# pwd /etc/etcd/ssl [root@test-107 ssl]# ls etcd-csr.json [root@test-173 ssl]# cfssl gencert -ca=/etc/kubernetes/ssl/ca.pem -ca-key=/etc/kubernetes/ssl/ca-key.pem -config=/etc/kubernetes/ssl/ca-config.json -profile=kubernetes /etc/etcd/ssl/etcd-csr.json | cfssljson -bare etcd 2021/04/06 13:48:22 [INFO] generate received request 2021/04/06 13:48:22 [INFO] received CSR 2021/04/06 13:48:22 [INFO] generating key: rsa-2048 2021/04/06 13:48:23 [INFO] encoded CSR 2021/04/06 13:48:23 [INFO] signed certificate with serial number 518875750718489889129533277175826954994664517958 2021/04/06 13:48:23 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for websites. For more information see the Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org); specifically, section 10.2.3 ("Information Requirements"). cfssl gencert -ca=/etc/kubernetes/ssl/ca.pem -ca-key=/etc/kubernetes/ssl/ca-key.pem -config=/etc/kubernetes/ssl/ca-config.json -profile=kubernetes /etc/etcd/ssl/etcd-csr.json | cfssljson -bare etcd [root@test-107 ssl]# ls etcd.csr etcd-csr.json etcd-key.pem etcd.pem
创建 etcd.service 启动文件 每个节点创建文件,对应修改 eg: 172.20.40.107 名字为 etcd3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 [root@test-107 ssl]# cat /etc/systemd/system/etcd.service [Unit] Description=Etcd Server After=network.target After=network-online.target Wants=network-online.target Documentation=https://github.com/coreos [Service] Type=notify WorkingDirectory=/var/lib/etcd/ ExecStart=/usr/bin/etcd \ --name=etcd3 \ --cert-file=/etc/etcd/ssl/etcd.pem \ --key-file=/etc/etcd/ssl/etcd-key.pem \ --peer-cert-file=/etc/etcd/ssl/etcd.pem \ --peer-key-file=/etc/etcd/ssl/etcd-key.pem \ --trusted-ca-file=/etc/kubernetes/ssl/ca.pem \ --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \ --initial-advertise-peer-urls=https://172.20.40.107:2380 \ --listen-peer-urls=https://172.20.40.107:2380 \ --listen-client-urls=https://172.20.40.107:2379,http://127.0.0.1:2379 \ --advertise-client-urls=https://172.20.40.107:2379 \ --initial-cluster-token=etcd-cluster-0 \ --initial-cluster=etcd1=https://172.20.40.173:2380,etcd2=https://172.20.40.196:2380,etcd3=https://172.20.40.107:2380 \ --initial-cluster-state=new \ --data-dir=/mnt/etcd_data Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target [root@test-107 ssl]# systemctl daemon-reload && systemctl enable --now etcd.service
验证, 当前正常
客户端交互端口 : 2379
peer通信端口 : 2380
peer : 同一集群的另一个 member
1 2 3 4 5 6 7 8 9 10 [root@test-107 ssl]# etcdctl --endpoints=http://localhost:2379 member list # 查看集群成员 d20c6ab8456f5fd, started, etcd1, https://172.20.40.173:2380, https://172.20.40.173:2379, false 71c9147302874bdf, started, etcd3, https://172.20.40.107:2380, https://172.20.40.107:2379, false ce0a9eb4851e47ac, started, etcd2, https://172.20.40.196:2380, https://172.20.40.196:2379, false [root@test-107 ssl]# ETCDCTL_API=3 /usr/bin/etcdctl --endpoints=https://172.20.40.196:2379,https://172.20.40.107:2379,https://172.20.40.173:2379 --cacert=/etc/kubernetes/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem endpoint health # 查看节点健康 https://172.20.40.107:2379 is healthy: successfully committed proposal: took = 26.66478ms https://172.20.40.173:2379 is healthy: successfully committed proposal: took = 30.31433ms https://172.20.40.196:2379 is healthy: successfully committed proposal: took = 37.859764ms
etcd.service 部分参数说明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 --name:etcd 集群中的节点名,这里可以随意,方便区分且不重复即可。 --listen-client-urls:监听用于客户端通信的 url,同样可以监听多个。 --advertise-client-urls:建议使用的客户端通信 url,该值用于 etcd 代理或 etcd 成员与 etcd 节点通信。 --listen-peer-urls:监听用于节点之间通信的 url,可监听多个,集群内部将通过这些 url 进行数据交互(如选举、数据同步等)。 --initial-advertise-peer-urls:建议用于节点之间通信的 url,节点间将以该值进行通信。 --initial-cluster-token: etcd-cluster-0,节点的 token 值,设置该值后集群将生成唯一 ID,并为每个节点也生成唯一 ID。当使用相同配置文件再启动一个集群时,只要该 token 值不一样,etcd 集群就不会相互影响。 --initial-cluster:集群中所有的 initial-advertise-peer-urls 的合集。 --initial-cluster-state:new,新建集群的标志。
升级 仅需 替换二进制文件 etcd, etcdctl
备份恢复 在任一正常节点执行如下操作
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 [root@test-173 test]# export ETCDCTL_API=3; /usr/bin/etcdctl --endpoints=172.20.40.107:2379 --cacert=/etc/kubernetes/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem snapshot save /root/backup/etcd/snapshot-20210406-143852.db {"level":"info","ts":1617691138.681973,"caller":"snapshot/v3_snapshot.go:119","msg":"created temporary db file","path":"/root/backup/etcd/snapshot-20210406-143852.db.part"} {"level":"info","ts":"2021-04-06T14:38:58.698+0800","caller":"clientv3/maintenance.go:200","msg":"opened snapshot stream; downloading"} {"level":"info","ts":1617691138.698508,"caller":"snapshot/v3_snapshot.go:127","msg":"fetching snapshot","endpoint":"172.20.40.107:2379"} {"level":"info","ts":"2021-04-06T14:38:58.990+0800","caller":"clientv3/maintenance.go:208","msg":"completed snapshot read; closing"} {"level":"info","ts":1617691138.9978282,"caller":"snapshot/v3_snapshot.go:142","msg":"fetched snapshot","endpoint":"172.20.40.107:2379","size":"10 MB","took":0.315710239} {"level":"info","ts":1617691139.000513,"caller":"snapshot/v3_snapshot.go:152","msg":"saved","path":"/root/backup/etcd/snapshot-20210406-143852.db"} Snapshot saved at /root/backup/etcd/snapshot-20210406-143852.db [root@test-173 test]# export ETCDCTL_API=3; /usr/bin/etcdctl --endpoints=172.20.40.107:2379 --cacert=/etc/kubernetes/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem snapshot save /root/backup/etcd/snapshot-20210406-143852.db {"level":"info","ts":1617691138.681973,"caller":"snapshot/v3_snapshot.go:119","msg":"created temporary db file","path":"/root/backup/etcd/snapshot-20210406-143852.db.part"} {"level":"info","ts":"2021-04-06T14:38:58.698+0800","caller":"clientv3/maintenance.go:200","msg":"opened snapshot stream; downloading"} {"level":"info","ts":1617691138.698508,"caller":"snapshot/v3_snapshot.go:127","msg":"fetching snapshot","endpoint":"172.20.40.107:2379"} {"level":"info","ts":"2021-04-06T14:38:58.990+0800","caller":"clientv3/maintenance.go:208","msg":"completed snapshot read; closing"} {"level":"info","ts":1617691138.9978282,"caller":"snapshot/v3_snapshot.go:142","msg":"fetched snapshot","endpoint":"172.20.40.107:2379","size":"10 MB","took":0.315710239} {"level":"info","ts":1617691139.000513,"caller":"snapshot/v3_snapshot.go:152","msg":"saved","path":"/root/backup/etcd/snapshot-20210406-143852.db"} Snapshot saved at /root/backup/etcd/snapshot-20210406-143852.db ### 恢复 systemctl stop etcd.service cd /root/backup/etcd/ && \ ETCDCTL_API=3 /use/bin/etcdctl snapshot restore snapshot-20210406-143852.db\ --name etcd-3 \ --initial-cluster etcd1=https://172.20.40.173:2380,etcd2=https://172.20.40.196:2380,etcd3=https://172.20.40.107:2380 \ --initial-cluster-token etcd-cluster-0 \ --initial-advertise-peer-urls https://172.20.40.107:2380"
具体可以参考 之前编写的 ansible 文件
用 kubeasz 安装
1 etcdctl --endpoints=https://172.20.19.14:2379,https://172.20.19.9:2379,https://172.20.19.17:2379 --cacert=/etc/kubernetes/ssl/ca.pem --cert=/etc/kubernetes/ssl/etcd.pem --key=/etc/kubernetes/ssl/etcd-key.pem endpoint health
etcd 网关与 gRPC-Gateway etcd 网关通常用于 etcd 集群的门户,是一个简单的 TCP 代理,将客户端请求转发到 etcd 集群,对外屏蔽了 etcd 集群内部的实际情况,在集群出现故障或者异常时,可以通过 etcd 网关进行切换;
gRPC-Gateway 则是对于 etcd 的 gRPC 通信协议的补充,有些语言的客户端不支持 gRPC 通信协议,此时就可以使用 gRPC-Gateway 对外提供的 HTTP API 接口。通过 HTTP 请求,实现与 gRPC 调用协议同样的功能。
2. etcdctl 使用 v3 版本,export ETCDCTL_API=3
常用操作 etcdctl 支持命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@test-173 test]# etcdctl -h NAME: etcdctl - A simple command line client for etcd3. USAGE: etcdctl [flags] VERSION: 3.4.13 API VERSION: 3.4 ...
数据库操作 数据库操作基本围绕着对键值和目录的 CRUD 操作及其对应的生命周期管理。 etcd 这些操作符合 REST 风格的一套 API 操作
键操作 键操作包括最常用的增删改查操作,包括 PUT、GET、DELETE 等命令。
PUT 设置或者更新某个键的值
1 2 3 4 5 6 [root@test-173 test]# etcdctl put /test/foo1 "Hello world" OK [root@test-173 test]# etcdctl put /test/foo2 "Hello world2" OK [root@test-173 test]# etcdctl put /test/foo3 "Hello world3" OK
GET 获取指定键的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@test-173 test]# etcdctl get /test/foo1 /test/foo1 Hello world [root@test-173 test]# etcdctl get /test/foo2 --hex # 返回 十六进制格式 \x2f\x74\x65\x73\x74\x2f\x66\x6f\x6f\x32 \x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x32 [root@test-173 test]# etcdctl get /test/foo3 --hex --print-value-only # 返回 十六进制格式,只获取值 \x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x33 [root@test-173 test]# etcdctl get /test/foo1 /test/foo3 --print-value-only # 获取 [/test/foo1, /test/foo3) Hello world Hello world2 [root@test-173 test]# etcdctl get --prefix --limit=2 /test/foo --print-value-only # 获取 前缀为 /test/foo 的两个值 Hello world Hello world2
DELETE 删除一个键或者特定范围的键
1 2 3 4 5 6 7 8 9 10 11 12 [root@test-173 test]# etcdctl del /test/foo1 /test/foo2 # 删除 [/test/foo1, /test/foo2) 1 # 删除了一个健 [root@test-173 test]# etcdctl get /test/foo1 [root@test-173 test]# etcdctl get /test/foo2 /test/foo2 Hello world2 root@test-173 test]# etcdctl del --prev-kv /test/foo2 # 删除键并返回值 1 /test/foo2 Hello world2 [root@test-173 test]# etcdctl get /test/foo2
watch 键值对的改动 watch 监测一个或多个键值的变化。v3 的 etcdctl watch 是一条长连接的流式推送:命令执行后就一直挂着,这个 key(或前缀)每发生一次变更都会打印一个事件,直到客户端自己断开为止,不会收到第一个事件就退出——「输出一次就退出」是 v2 的行为。所以下面的 watch 窗口里能看到两次 PUT 事件
1 [root@test-173 ~]# etcdctl watch foo1
新开窗口
1 2 3 4 5 6 [root@test-173 ~]# etcdctl put foo1 Hello watch OK [root@test-173 ~]# etcdctl put foo bar OK [root@test-173 ~]# etcdctl put foo1 bar1 OK
查看 watch 窗口
1 2 3 4 5 6 7 [root@test-173 ~]# etcdctl watch foo1 PUT foo1 Hello PUT foo1 bar1
lease(租约) 一旦租约的 TTL 到期,租约就会过期并且所有附带的键都将被删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 [root@test-173 test]# etcdctl lease grant 100 # 授予租约,TTL 为 100 秒 lease 75fd78a5bc8db873 granted with TTL(100s) [root@test-173 test]# etcdctl put --lease=75fd78a5bc8db873 foo1 bar # 附加键 foo1 到租约 75fd78a5bc8db873 OK [root@test-173 test]# etcdctl get foo1 foo1 bar [root@test-173 test]# etcdctl get foo1 # 两分钟后,发现 已没有值 [root@test-173 test]# etcdctl put --lease=75fd78a5bc8db873 foo1 bar # 租约已失效 {"level":"warn","ts":"2021-04-06T15:14:17.315+0800","caller":"clientv3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"endpoint://client-71d3b0ce-8010-4407-ad17-71c5f1970309/127.0.0.1:2379","attempt":0,"error":"rpc error: code = NotFound desc = etcdserver: requested lease not found"} [root@test-173 test]# etcdctl lease grant 1000 lease 75fd78a5bc8dbb9b granted with TTL(1000s) [root@test-173 test]# etcdctl put --lease=75fd78a5bc8dbb9b foo1 bar OK [root@test-173 test]# etcdctl get foo1 foo1 bar [root@test-173 test]# etcdctl lease revoke 75fd78a5bc8dbb9b # 撤销租约 lease 75fd78a5bc8dbb9b revoked [root@test-173 test]# etcdctl get foo1 # 撤销租约将删除所有附带的 key ## 刷新租约 [root@test-173 test]# etcdctl lease keep-alive 75fd78a5bc8dbe37 # 一直刷新 lease 75fd78a5bc8dbe37 keepalived with TTL(100) lease 75fd78a5bc8dbe37 keepalived with TTL(100) lease 75fd78a5bc8dbe37 keepalived with TTL(100) lease 75fd78a5bc8dbe37 keepalived with TTL(100) ## 新开一个端口 root@test-173 ~]# etcdctl put --lease=75fd78a5bc8dbe37 foo1 bar OK [root@test-173 ~]# etcdctl lease timetolive 75fd78a5bc8dbe37 # 获取有关租赁信息 lease 75fd78a5bc8dbe37 granted with TTL(100s), remaining(68s) [root@test-173 ~]# etcdctl lease timetolive --keys 75fd78a5bc8dbe37 # 获取哪些 key 绑定了租赁信息: lease 75fd78a5bc8dbe37 granted with TTL(100s), remaining(91s), attached keys([foo1])
3. etcd调优 使用内存模式
⚠️ 这个办法只适合基准测试或者数据明确可丢的场景,不要用在生产上 。etcd 的线性一致性建立在 WAL 先 fsync 到持久设备这个前提上,把 data-dir 放到 tmpfs 之后这个前提就不成立了:节点一重启数据全清,放在 k8s 里就等于 apiserver 的全部数据没了。三节点集群平时靠其他副本还能重建一个节点,但一旦断电或者批量重启同时丢掉多数派,raft 就没法自愈,连已经 commit 的数据也会丢。而下面把挂载写进 /etc/fstab,意味着这是重启后依然生效的常态配置,而不是临时压测——真要压测,挂完别写 fstab。
生产环境想解决同一个问题,方向是换 NVMe/SSD 把 fsync 延迟压下来,配合调 --heartbeat-interval、--election-timeout、--snapshot-count,并且至少保留定时 snapshot save 的备份。
未安装时
1 2 3 mkdir /var/lib/etcd mount -t tmpfs -o size=10G,mode=0755 tmpfs /var/lib/etcd echo "tmpfs /var/lib/etcd tmpfs defaults,size=10G 0 0" >> /etc/fstab
已经安装了 etcd, 先备份,操作完再恢复
将 etcd 的流量排在第一优先级 2379 2380 端口流量
1 2 3 4 5 6 netDevice="ens33" tc qdisc add dev $netDevice root handle 1: prio bands 3 tc filter add dev $netDevice parent 1: protocol ip prio 1 u32 match ip sport 2380 0xffff flowid 1:1 tc filter add dev $netDevice parent 1: protocol ip prio 1 u32 match ip dport 2380 0xffff flowid 1:1 tc filter add dev $netDevice parent 1: protocol ip prio 2 u32 match ip sport 2379 0xffff flowid 1:1 tc filter add dev $netDevice parent 1: protocol ip prio 2 u32 match ip dport 2379 0xffff flowid 1:1
4. etcd与k8s 获取 Kubernetes 存储在 etcd 中的 keys 1 2 3 4 5 6 7 8 9 10 11 12 [root@test-173 ~]# ETCDCTL_API=3 etcdctl get / --prefix --keys-only |grep -Ev "^$" /calico/ipam/v2/assignment/ipv4/block/170.56.144.192-26 /calico/ipam/v2/assignment/ipv4/block/170.56.170.64-26 /calico/ipam/v2/assignment/ipv4/block/170.56.27.0-26 /calico/ipam/v2/assignment/ipv4/block/170.56.73.192-26 /calico/ipam/v2/handle/k8s-pod-network.006edea2a1463ad925976290e5a0fea9d87e3188731566465ec8459f20b54a9d /calico/ipam/v2/handle/k8s-pod-network.0397054208d1bce46053df4814d1006f14ae0dd98280555aa5b35d1b8b02c06e /calico/ipam/v2/handle/k8s-pod-network.070dee87e37e685f2f6adac7ea38a54bb2326ac1c5d56bcae3b9b46086ff5c2b /calico/ipam/v2/handle/k8s-pod-network.0fad3aa32d7c9bfe0392577fc44a9f7ad9482c9914e0a93b1b94363cd1173585 ## 略 [root@test-173 ~]# ETCDCTL_API=3 etcdctl --endpoints=https://172.20.40.196:2379,https://172.20.40.107:2379,https://172.20.40.173:2379 --cacert=/etc/kubernetes/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem get / --prefix --keys-only |grep -Ev "^$" # 同样结果
查看键值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 [root@test-107 ~]# ETCDCTL_API=3 etcdctl --endpoints=https://172.20.40.196:2379,https://172.20.40.107:2379,https://172.20.40.173:2379 --cacert=/etc/kubernetes/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem get /registry/services/endpoints/monitoring/grafana # 查看键值 /registry/services/endpoints/monitoring/grafana k8s v1 Endpoints⚌ ⚌ grafana monitoring"*$c5bca02d-dcde-4638-88fa-d32f1606cd722⚌⚌߃Z& pp.kubernetes.io/componentgrafanaZ! app.kubernetes.io/namegrafanaZ, app.kubernetes.io/part-ofkube-prometheusZ" app.kubernetes.io/version7.4.3bM 0endpoints.kubernetes.io/last-change-trigger-time2021-04-26T18:01:18+08:00z⚌⚌ kube-controller-managerUpdatev⚌FieldsV1:⚌ ⚌{"f:metadata":{"f:annotations":{".":{},"f:endpoints.kubernetes.io/last-change-trigger-time":{}},"f:labels":{".":{},"f:app.kubernetes.io/component":{},"f:app.kubernetes.io/name":{},"f:app.kubernetes.io/part-of":{},"f:app.kubernetes.io/version":{}}},"f:subsets":{}}⚌ ⚌ 170.56.73.220_ Pod 172.20.40.173fana-665447c488-245vl"$db4da82f-36ed-49b9-8003-197a61e8ed97*11359764:" http⚌TCP" [root@test-107 ~]# ETCDCTL_API=3 etcdctl --endpoints=https://172.20.40.196:2379,https://172.20.40.107:2379,https://172.20.40.173:2379 --cacert=/etc/kubernetes/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem get /registry/namespaces/default /registry/namespaces/default k8s v1 Namespace⚌ ⚌ default"*$fb03c357-9393-11eb-898b-fad65d90ce002⚌⚌⚌Z$ field.cattle.io/projectIdp-zrh2mZ istio-injectionenabledb⚌ cattle.io/status⚌{"Conditions":[{"Type":"ResourceQuotaInit","Status":"True","Message":"","LastUpdateTime":"2021-04-14T05:51:05Z"},{"Type":"InitialRolesPopulated","Status":"True","Message":"","LastUpdateTime":"2021-04-14T05:51:05Z"}]}b, field.cattle.io/projectIdc-hxllk:p-zrh2mb1 )lifecycle.cattle.io/create.namespace-authtruer#controller.cattle.io/namespace-authz kubernetes Active"
键类型 这些键定义了集群中所有资源的配置和状态:
1 2 3 4 5 6 7 8 9 10 Nodes Namespaces ServiceAccounts Roles and RoleBindings, ClusterRoles / ClusterRoleBindings ConfigMaps Secrets Workloads: Deployments, DaemonSets, Pods, … Cluster's certificates The resources within each apiVersion The events that bring the cluster in the current state
元数据资源在 etcd 中的存储格式由前缀、资源类型、namespace 和具体资源名组成
补 Kubernetes 集群使用了事务 Txn 接口防止并发创建、更新被覆盖等问题。当执行完 BeforeCreate 策略后,API Server 会调用 Storage 模块的 Create 接口写入资源。Storage.Create 接口调用底层存储模块 etcd3,将 user Deployment 资源对象写入 etcd。
Kubernetes 使用 watch 机制获取数据变化的事件,etcd watch 机制提供了流式推送机制,相比于定时轮询减少了高昂的查询开销,方便 API Server 实现数据的监听。服务器端的 store 对象利用 etcd 的 watch 机制,当 watch 机制触发时,数据的变化信息将封装成 event 对象并打包发送出去,客户端则通过不停地监听尝试读取 event chunk。
需要注意的是,Kubernetes 社区提供了通用的Informer 组件,实现了客户端与 API Server 之间的资源和事件同步。Informer 机制的 Reflector 封装了 Watch、List 操作,结合本地 Cache、Indexer,控制器加载完初始状态数据后,接下来的其他操作只需从本地缓存读取,极大降低了 API Server 和 etcd 的压力。