从零开始入门 K8s | 手把手带你理解 etcd
从零开始入门 K8s | 手把手带你理解 etcd当我们谈论 KubernetesK8s时常常会听到一个神秘而重要的组件——etcd。它就像是 K8s 集群的“大脑”存储着所有关键数据。想象一下如果某天 etcd 崩溃了整个集群可能就会像失忆一样忘记所有配置和状态。今天我们就从零开始用通俗的语言和代码示例彻底搞懂 etcd 是什么、怎么用、以及它和 K8s 的关系。## 什么是 etcdetcd 是一个分布式键值存储系统由 CoreOS 团队开发目前是云原生计算基金会CNCF的毕业项目。它的名字很有意思etcd 读作“et-see-dee”灵感来源于 Unix 的/etc目录存放系统配置和分布式系统的“distributed”组合——意思是“分布式的配置存储”。核心特点-分布式多个节点组成集群保证高可用-键值存储像一个大字典通过 key 找到 value-强一致性使用 Raft 共识算法确保数据一致-Watch 机制可以监听数据变化实时通知在 K8s 中etcd 存储了所有资源对象的状态比如 Pod、Service、ConfigMap 等。当你在kubectl apply -f deployment.yaml时数据最终都会写入 etcd。## etcd 如何工作——Raft 共识算法etcd 的核心是 Raft 算法它让多个节点达成共识。简单来说Raft 将节点分为三类-Leader负责处理所有写请求并向其他节点同步数据-Follower被动接收 Leader 的数据-Candidate当 Leader 失效时发起选举一个生动的比喻假设你们团队要记录一个“今天谁值班”的名单。Leader 就像班长他负责写名单并通知所有组员如果班长失联了其他组员会投票选出新班长。这个“投票”过程就是 Raft 的选举机制。### 代码示例1用 Python 模拟 Raft 选举为了直观理解我们用 Python 写一个简化版的 Raft 选举流程。pythonimport randomimport time# 定义节点状态FOLLOWER 0CANDIDATE 1LEADER 2class RaftNode: def __init__(self, node_id, nodes): self.node_id node_id self.state FOLLOWER # 初始为跟随者 self.current_term 0 # 当前任期 self.voted_for None # 投票给的候选人 self.nodes nodes # 所有节点列表 self.leader_id None # 当前领导者 self.election_timeout random.uniform(1.0, 2.0) # 随机超时时间 def start_election(self): 发起选举 self.state CANDIDATE self.current_term 1 self.voted_for self.node_id votes 1 # 自己投自己 print(f节点 {self.node_id} 开始选举任期 {self.current_term}) # 模拟向其他节点请求投票 for node in self.nodes: if node.node_id ! self.node_id and node.state ! LEADER: # 模拟网络延迟和随机投票 if random.random() 0.3: # 30%概率拒绝 node.voted_for self.node_id votes 1 print(f节点 {node.node_id} 投票给 {self.node_id}) # 如果获得多数票成为领导者 if votes len(self.nodes) / 2: self.state LEADER self.leader_id self.node_id print(f节点 {self.node_id} 成为领导者 (任期 {self.current_term})) # 领导者发送心跳 for node in self.nodes: if node.node_id ! self.node_id: node.leader_id self.node_id node.state FOLLOWER else: self.state FOLLOWER print(f节点 {self.node_id} 选举失败) def tick(self): 模拟时间流逝 time.sleep(self.election_timeout) if self.state FOLLOWER and self.leader_id is None: self.start_election()# 创建3个节点的etcd集群模拟nodes [RaftNode(i, []) for i in range(3)]for node in nodes: node.nodes nodes # 互相引用# 启动所有节点for node in nodes: node.tick()运行这段代码你会看到节点如何通过投票选出 Leader。这就是 etcd 集群高可用的基础。## etcd 在 K8s 中的角色在 Kubernetes 中etcd 存储了集群的“状态”——包括- 节点信息Node- Pod 运行状态- Service 配置- Secret 和 ConfigMap 数据- 控制器状态Deployment、StatefulSet 等重要原则所有对集群状态的修改都必须经过 API Serverkube-apiserver而 API Server 则直接与 etcd 交互。这意味着 etcd 是 K8s 的“唯一真相源”。### 代码示例2用 Python 操作 etcd模拟 K8s 数据存储我们模拟 K8s 将 Pod 信息存储到 etcd 的过程。假设 etcd 的 key 格式为/registry/pods/namespace/pod-name。pythonimport etcd3 # 需要安装: pip install etcd3# 连接到本地 etcd 服务假设已启动client etcd3.client(hostlocalhost, port2379)def create_pod(pod_name, namespacedefault, imagenginx:latest): 模拟在K8s中创建一个Pod并存储到etcd # 构建Pod数据简化版实际K8s数据更复杂 pod_data { apiVersion: v1, kind: Pod, metadata: { name: pod_name, namespace: namespace, }, spec: { containers: [{ name: nginx-container, image: image, ports: [{containerPort: 80}] }] }, status: {phase: Running} } # 将数据序列化为JSON字符串 import json pod_json json.dumps(pod_data) # 生成etcd的key遵循K8s的存储模式 key f/registry/pods/{namespace}/{pod_name} # 写入etcd client.put(key, pod_json) print(fPod {pod_name} 已保存到 etcd (key: {key}))def get_pod(pod_name, namespacedefault): 从etcd读取Pod信息 key f/registry/pods/{namespace}/{pod_name} result client.get(key) if result[0] is not None: pod_json result[0].decode(utf-8) import json pod_data json.loads(pod_json) print(f从 etcd 读取 Pod: {pod_data[metadata][name]}) print(f 镜像: {pod_data[spec][containers][0][image]}) print(f 状态: {pod_data[status][phase]}) return pod_data else: print(fPod {pod_name} 在 etcd 中未找到) return None# 测试创建并读取一个Podcreate_pod(my-nginx)get_pod(my-nginx)# 注意实际K8s中API Server会负责序列化和反序列化# 这里只是模拟底层存储机制运行这段代码前需要确保本地有 etcd 服务。可以启动 Docker 版 etcdbashdocker run -d --name etcd \ -p 2379:2379 -p 2380:2380 \ quay.io/coreos/etcd:latest \ /usr/local/bin/etcd \ --data-dir/etcd-data \ --listen-client-urls http://0.0.0.0:2379 \ --advertise-client-urls http://0.0.0.0:2379## 如何与 etcd 交互除了通过 Python 客户端常用的交互方式还有1.etcdctl命令行工具类似kubectl2.REST API通过 HTTP 请求操作3.K8s 内部由 API Server 自动管理etcdctl 常用命令示例bash# 写入数据etcdctl put /myapp/config {port: 8080}# 读取数据etcdctl get /myapp/config# 监听变化etcdctl watch /myapp/config# 列出所有 keyetcdctl get / --prefix --keys-only## etcd 的维护与备份由于 etcd 存储了集群的“生命线”定期备份非常重要。K8s 官方推荐使用etcdctl snapshot save命令备份bash# 创建快照ETCDCTL_API3 etcdctl --endpointshttps://127.0.0.1:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ snapshot save /backup/etcd-snapshot-$(date %Y%m%d).db# 恢复快照在空目录中恢复etcdctl snapshot restore /backup/etcd-snapshot-20231201.db \ --data-dir/var/lib/etcd-restore重要提示生产环境中etcd 集群通常由 3 或 5 个节点组成每个节点都应备份。恢复时需要所有节点一致。## 总结etcd 是 K8s 的核心组件之一它通过分布式键值存储和 Raft 共识算法为集群提供了强一致性的数据底座。理解 etcd 的工作原理有助于我们更好地诊断集群问题、进行备份恢复甚至优化性能。**关键点回顾**1.etcd 分布式的 /etc 目录存储所有配置和状态2.Raft 算法通过 Leader 选举和日志复制保证一致性3.K8s 集成API Server 是唯一入口所有数据最终在 etcd 中持久化4.运维要点定期备份、监控磁盘 IO、确保网络稳定当你下次遇到 K8s 集群故障时不妨先检查 etcd 的健康状态。记住etcd 健康集群就健康etcd 挂了集群就失忆了。希望这篇文章能帮你迈出理解 etcd 的第一步后续可以继续探索 etcd 的性能调优和高级特性。

相关新闻