🐘 Patroni 更新 Kubernetes Pod Annotations 的源码逻辑详解
📌 背景说明
Patroni 是一个用于实现 PostgreSQL 高可用的开源工具。它支持多种分布式配置存储后端,如 etcd、Consul 和 Kubernetes。在与 Kubernetes 集成部署时,Patroni 会定期更新当前 Pod 的 annotations 来反映 PostgreSQL 节点的状态,如角色(主/备)、连接地址等。


xlog_location 用来计算Lag
🧩 Patroni 源码结构概览
| 模块文件 | 主要职责 |
|---|---|
patroni/kubernetes.py | 封装 Kubernetes API 的交互逻辑 |
patroni/postmaster.py | 主要执行逻辑循环,控制 PostgreSQL 节点行为 |
patroni/dcs/kubernetes.py | 分布式配置与协调(如 leader key) |
patroni/config.py | 加载并管理 Patroni 配置参数 |
patroni/__init__.py | 应用主入口与初始化流程 |
🔄 Pod Annotations 更新机制详解
1. 更新入口:Kubernetes.update_pod_annotations()
此方法位于 patroni/kubernetes.py,封装了对 Kubernetes API 的调用:
from kubernetes.client import V1ObjectMeta, V1Pod
from kubernetes.client.rest import ApiException
class Kubernetes:
def __init__(self, config):
self.config = config
self.api = ... # 初始化 Kubernetes API 客户端
def update_pod_annotations(self, annotations):
meta = V1ObjectMeta(annotations=annotations)
body = V1Pod(metadata=meta)
try:
self.api.patch_namespaced_pod(
name=self.config['pod_name'],
namespace=self.config['namespace'],
body=body
)
except ApiException as e:
logger.error(f"Failed to update pod annotations: {e}")
🔍 说明:
- 使用的是
patch_namespaced_podAPI 调用,属于局部更新(非全量替换)。 - 更新的是
Pod.metadata.annotations字段。 - 触发更新的 annotations 通常包含一个
status字段(JSON 格式字符串),记录当前节点状态信息。
2. 调用位置:Postmaster.run() 主循环
在 patroni/postmaster.py 中,Patroni 的主逻辑循环会定期调用 update_pod_annotations:
class Postmaster:
def __init__(self, config):
self.config = config
self.kubernetes = Kubernetes(config)
self.state = ...
def run(self):
while True:
self.check_cluster_state() # 检查集群角色等信息
annotations = {
"status": json.dumps({
"conn_url": self.conn_url,
"api_url": self.api_url,
"state": self.state,
"role": self.role,
"version": self.version,
"xlog_location": self.xlog_location,
"timeline": self.timeline
})
}
self.kubernetes.update_pod_annotations(annotations)
time.sleep(self.config['loop_timeout'])
⏱️ 默认每隔 loop_timeout 秒(通常为 10 秒)执行一次更新。
🧪 调试与验证方式
✅ 日志验证
打开 DEBUG 日志级别,可观察到类似日志:
logging:
level: DEBUG
日志输出示例:
DEBUG:patroni.kubernetes: Updating pod annotations: {"status": "{\"role\": \"master\", ...}"}
✅ 手动验证更新效果
使用 kubectl 查看 annotations 是否更新成功:
kubectl get pod <pod-name> -o yaml | grep annotations -A 10
也可直接查看完整 YAML:
kubectl describe pod <pod-name>
🛠️ 排查异常连接问题
你提到链接 http://245.0.0.42:8009/patroni 解析失败,排查方向如下:
| 可能原因 | 建议操作 |
|---|---|
| ❌ 链接不合法或服务未启动 | 确认 Patroni 监听地址配置无误 |
| ❌ Pod 服务未暴露 | 检查是否设置了对应的 Service 对象 |
| ❌ 网络策略或防火墙限制 | 检查 Kubernetes 网络策略(NetworkPolicy)或容器间通信配置 |
| ❌ 非预期 IP 格式(如 Pod IP) | 推荐通过 Pod 名称或 Service 域名访问而非裸 IP |
📚 相关参考资料
Patroni 官方文档
Kubernetes 相关文档
中文实战与问题分析
🧭 小结
- Patroni 会定期将 PostgreSQL 实例状态写入当前 Pod 的 annotations。
- 核心方法是
update_pod_annotations(),通过 Kubernetes 的patch_namespaced_pod实现。 - 更新数据结构位于 annotations 的
status字段,JSON 格式。 - 排查连接或更新问题时,可结合日志、kubectl 命令和网络配置进行验证。
如需进一步帮助排查 Patroni 在你实际部署中的行为,可提供具体 Pod 日志或配置片段,我可以协助深入分析。
部分逻辑的源码调用链
以下是 Patroni 中更新 Kubernetes Pod annotations 的关键源码调用链及其对应的 GitHub 源码链接,帮助你深入理解其实现逻辑。(patroni.readthedocs.io)
🔗 源码调用链概览
Patroni 在 Kubernetes 环境中通过定期更新 Pod 的 annotations 来反映 PostgreSQL 实例的状态。这一过程主要涉及以下模块和方法:
-
主循环控制器:
Postmaster.run()方法位于patroni/postgresql/postmaster.py文件中。该方法是 Patroni 的主循环,负责定期检查集群状态并更新 Pod annotations。(github.com) -
Kubernetes 交互模块:
Kubernetes.update_pod_annotations()方法位于patroni/kubernetes.py文件中。该方法封装了与 Kubernetes API 的交互,用于更新当前 Pod 的 annotations。
🧩 关键方法详解
1. Postmaster.run() 方法
该方法是 Patroni 的主循环,每隔一定时间(由 loop_wait 参数配置,默认为 10 秒)执行一次。在每次循环中,它会:
- 检查集群状态。
- 构建包含当前节点状态的 annotations。
- 调用
update_pod_annotations()方法更新 Pod 的 annotations。
示例代码片段:
class Postmaster:
def run(self):
while True:
self.check_cluster_state()
annotations = {
"status": json.dumps({
"conn_url": self.conn_url,
"api_url": self.api_url,
"state": self.state,
"role": self.role,
"version": self.version,
"xlog_location": self.xlog_location,
"timeline": self.timeline
})
}
self.kubernetes.update_pod_annotations(annotations)
time.sleep(self.config['loop_timeout'])
该方法封装了对 Kubernetes API 的调用,使用 patch_namespaced_pod 方法更新当前 Pod 的 annotations。它构建了一个包含新 annotations 的 V1Pod 对象,并发送 PATCH 请求。
示例代码片段:
from kubernetes.client import V1ObjectMeta, V1Pod
from kubernetes.client.rest import ApiException
class Kubernetes:
def update_pod_annotations(self, annotations):
meta = V1ObjectMeta(annotations=annotations)
body = V1Pod(metadata=meta)
try:
self.api.patch_namespaced_pod(
name=self.config['pod_name'],
namespace=self.config['namespace'],
body=body
)
except ApiException as e:
logger.error(f"Failed to update pod annotations: {e}")
🔍 调试与验证建议
-
启用 DEBUG 日志级别:在 Patroni 的配置文件中设置
logging.level: DEBUG,以便捕获详细的日志信息。 -
查看日志输出:观察日志中是否有类似以下的输出,确认 annotations 已被更新:
DEBUG:patroni.kubernetes: Updating pod annotations: {‘status’: ‘{“conn_url”: “…”, “api_url”: “…”, …}’}
:contentReference[oaicite:66]{index=66}
- **使用 `kubectl` 验证**::contentReference[oaicite:68]{index=68}:contentReference[oaicite:70]{index=70}
```bash
kubectl get pod <pod-name> -o yaml | grep annotations -A 10
📘 参考资料
-
Patroni 官方文档:Using Patroni with Kubernetes

689

被折叠的 条评论
为什么被折叠?



