StatefulSet 的 Pod 名称解析不起作用
Pod name resolution for StatefulSet doesn't work
我有一个带有 StatefulSet 的 following Kubernetes YAML,我用它来部署带有 Patroni 的 PostgreSQL 集群。但是,问题是关于 Kubernetes 如何在 CoreDNS 中注册 Pod 名称。
根据稳定网络 ID 部分中的 this documentation,如果我为我的 Pods 创建一个名为 spilodemo-svc
的 Headless 服务,我可以使用短主机名访问它们(podname.servicename):
spilodemo-0.spilodemo-svc
基本上,我的代码在 VirtualBox 和 Vagrant 上使用 kubeadm 部署的 K8s 集群上可以正常运行很长时间。今天我想在 IBM Cloud 上部署它,但上面的主机名不起作用,奇怪的是,当我再次在 Vagrant/VirtualBox 上重复测试时,我无法再让它工作了,我不知道为什么。
现在 YAML 部署了 Spilo,这是一个由 Zalando 开发的开源项目,是一个带有 Patroni 和 PostgreSQL 的 Docker 镜像。我的代码来自他们的 example here.
基本上,他们创建了一个没有选择器的 ClusterIP 服务(而不是 Headless)。在这些情况下,Kubernetes 不会在其中创建端点。出于这个原因,我们在 YAML 中有一个与服务同名的端点(这似乎是 Kubernetes 期望的绑定)。
Spilo 有 Python 代码,始终使用主节点的 IP 更新此端点。
StatefulSet 的字段 serviceName 等于服务的名称:
serviceName: spilodemo-svc
而且,根据文档,这保证了 Kubernetes 在 CoreDNS 中为这个短主机名 (podname.servicename) 创建一个条目:
spilodemo-0.spilodemo-svc
它一直工作了很长时间直到今天,同时没有发生任何事情。老实说,到目前为止,我从未完全理解 DNS 名称 spilodemo-0.spilodemo-svc
是如何工作的,因为它使用 ClusterIP 服务而不是 Headless 服务。
另一件奇怪的事情是 Zalando 团队使用了另一个我称之为 spilodemo-config
的 Headless 服务,根据他们代码中的评论,它应该避免 Kubernetes 删除端点,但这没有多大意义对我来说。
然而,今天我也尝试将服务转换为无头服务,移除 spilodemo-config
服务,但没有成功。 Kubernetes 只在 CoreDNS 中为服务创建条目:
spilodemo.spilons.svc.cluster.local
但不是每个 Pod 一个:
spilodemo-0.spilodemo-svc
spilodemo-1.spilodemo-svc
spilodemo-2.spilodemo-svc
任何人都可以帮我弄清楚我的 YAML 文件发生了什么,以及如何让上面的三个短主机名在 CoreDNS 中工作?
PS
在 Whosebug 上,我发现了这些讨论:
- Hostname of pods in the same StatefulSet can not be resolved
- Stateful Pod hostname doesn't resolve
但他们没有解决我的问题。
经过将近三天的测试,我找到了解决办法。解决方案取决于两件事:
- Kubernetes 的工作原理;
- Patroni 的工作原理。
Kubernetes 的工作原理
当您创建一个 StatefulSet 部署时(但对于 Deployment 也是如此),假设有 3 pods,Kubernetes 在 CoreDNS 中注册了三个 DNS 名称:
IP-with-dashes.<namespace>.pod.cluster.local
然而,这些名称对我来说毫无用处,因为我无法提前在我的 YAML 文件中设置它们,因为它取决于分配给 Pods.
的 IP Kubernetes
但是,对于 StatefulSet 部署,根据 this documentation in the Stable Network ID section 如果我为我的 Pod 创建一个 Headless 服务,我可以使用 FQDN 的短主机名 (podname.servicename) 访问它们 (...svc.cluster.local).
这是我需要创建的 Headless 服务:
---
apiVersion: v1
kind: Service
metadata:
name: spilodemo-svc
labels:
application: spilo
spilo-cluster: spilodemo
spec:
clusterIP: None
selector:
application: spilo
spilo-cluster: spilodemo
此处将选择器设置为绑定所有三个 pods 很重要。另一件重要的事情是将以下行添加到您的 StatefulSet,其名称等于无头服务:
serviceName: spilodemo-svc
这是 Kubernetes 部分。现在您可以使用 DNS 名称引用您的 Pods:
spilodemo-0.spilodemo-svc
spilodemo-1.spilodemo-svc
spilodemo-2.spilodemo-svc
或 FQDN:
spilodemo-0.spilodemo-svc.<namespace>.svc.cluster.local
spilodemo-1.spilodemo-svc.<namespace>.svc.cluster.local
spilodemo-2.spilodemo-svc.<namespace>.svc.cluster.local
Patroni 的工作原理
但是,使用 Pods' DNS 名称对客户端没有意义,因为它们需要单点访问。为此,Patroni 团队建议像这样创建一个 ClusterIP 服务:
---
apiVersion: v1
kind: Service
metadata:
name: spilodemo
labels:
application: spilo
spilo-cluster: spilodemo
spec:
type: ClusterIP
ports:
- name: postgresql
port: 5432
targetPort: 5432
注意:没有选择器。这不是错误。当您创建这样的服务时,Kubernetes 会创建一个 ClusterIP 服务(然后可以使用 IP 或主机名来引用它)但没有端点。这意味着您连接到它的 IP 或它的 DNS 名称:spilodemo.<namespace>.svc.cluster.local
,连接挂起。
出于这个原因,Patroni 团队要求您在 YAML 文件中添加以下与 ClusterIP 服务同名的端点。
apiVersion: v1
kind: Endpoints
metadata:
name: spilodemo
labels:
application: spilo
spilo-cluster: spilodemo
subsets: []
Patroni 在内部 Python 中有一段代码,通过 Kubernetes API 使用 Master Pod IP 更新此端点。 Patroni 能够使用上面的相关标签(应用程序、spilo 集群)确定要更新的端点,您甚至可以自定义这些标签。
此时,Patroni 集群客户端只需要使用这个 DNS 名称(ClusterIP 之一)或相关 IP:
spilodemo.spilons.svc.cluster.local
连接自动重定向到Pod主节点IP。
到目前为止一切顺利。现在是令人困惑的部分。如果您查看 Spilo 代码中的 Patroni Kubernetes sample file,您会发现另一个无头服务已经存在。
---
# headless service to avoid deletion of patronidemo-config endpoint
apiVersion: v1
kind: Service
metadata:
name: spilodemo-config
labels:
application: spilo
spilo-cluster: spilodemo
spec:
clusterIP: None
让我感到困惑的是这种无头服务的存在。我不明白它的目的。一开始我以为是headless服务需要有上面提到的Pods DNS名称。但是我错了。这项服务的目的不同。基本上,Zalando 团队不知道用户如何编写 YAML 文件来部署 Patroni。如果用户创建端点但忘记将其关联到服务,则 Kubernetes 将其视为孤儿并将其删除。出于这个原因,Patroni 代码自己创建了这个服务。事实上,如果你不在 YAML 文件中定义它,Patroni 会为你创建它。
那么,如果 Patroni 为您创建了它,为什么他们将其添加到上面的示例 YAML 中?原因是权限。如果 Pod 没有权限则无法创建它。这就是他们将其添加到 YAML 中的原因。这有点令人困惑,但这就是整个故事。
我有一个带有 StatefulSet 的 following Kubernetes YAML,我用它来部署带有 Patroni 的 PostgreSQL 集群。但是,问题是关于 Kubernetes 如何在 CoreDNS 中注册 Pod 名称。
根据稳定网络 ID 部分中的 this documentation,如果我为我的 Pods 创建一个名为 spilodemo-svc
的 Headless 服务,我可以使用短主机名访问它们(podname.servicename):
spilodemo-0.spilodemo-svc
基本上,我的代码在 VirtualBox 和 Vagrant 上使用 kubeadm 部署的 K8s 集群上可以正常运行很长时间。今天我想在 IBM Cloud 上部署它,但上面的主机名不起作用,奇怪的是,当我再次在 Vagrant/VirtualBox 上重复测试时,我无法再让它工作了,我不知道为什么。
现在 YAML 部署了 Spilo,这是一个由 Zalando 开发的开源项目,是一个带有 Patroni 和 PostgreSQL 的 Docker 镜像。我的代码来自他们的 example here.
基本上,他们创建了一个没有选择器的 ClusterIP 服务(而不是 Headless)。在这些情况下,Kubernetes 不会在其中创建端点。出于这个原因,我们在 YAML 中有一个与服务同名的端点(这似乎是 Kubernetes 期望的绑定)。
Spilo 有 Python 代码,始终使用主节点的 IP 更新此端点。
StatefulSet 的字段 serviceName 等于服务的名称:
serviceName: spilodemo-svc
而且,根据文档,这保证了 Kubernetes 在 CoreDNS 中为这个短主机名 (podname.servicename) 创建一个条目:
spilodemo-0.spilodemo-svc
它一直工作了很长时间直到今天,同时没有发生任何事情。老实说,到目前为止,我从未完全理解 DNS 名称 spilodemo-0.spilodemo-svc
是如何工作的,因为它使用 ClusterIP 服务而不是 Headless 服务。
另一件奇怪的事情是 Zalando 团队使用了另一个我称之为 spilodemo-config
的 Headless 服务,根据他们代码中的评论,它应该避免 Kubernetes 删除端点,但这没有多大意义对我来说。
然而,今天我也尝试将服务转换为无头服务,移除 spilodemo-config
服务,但没有成功。 Kubernetes 只在 CoreDNS 中为服务创建条目:
spilodemo.spilons.svc.cluster.local
但不是每个 Pod 一个:
spilodemo-0.spilodemo-svc
spilodemo-1.spilodemo-svc
spilodemo-2.spilodemo-svc
任何人都可以帮我弄清楚我的 YAML 文件发生了什么,以及如何让上面的三个短主机名在 CoreDNS 中工作?
PS 在 Whosebug 上,我发现了这些讨论:
- Hostname of pods in the same StatefulSet can not be resolved
- Stateful Pod hostname doesn't resolve 但他们没有解决我的问题。
经过将近三天的测试,我找到了解决办法。解决方案取决于两件事:
- Kubernetes 的工作原理;
- Patroni 的工作原理。
Kubernetes 的工作原理
当您创建一个 StatefulSet 部署时(但对于 Deployment 也是如此),假设有 3 pods,Kubernetes 在 CoreDNS 中注册了三个 DNS 名称:
IP-with-dashes.<namespace>.pod.cluster.local
然而,这些名称对我来说毫无用处,因为我无法提前在我的 YAML 文件中设置它们,因为它取决于分配给 Pods.
的 IP Kubernetes但是,对于 StatefulSet 部署,根据 this documentation in the Stable Network ID section 如果我为我的 Pod 创建一个 Headless 服务,我可以使用 FQDN 的短主机名 (podname.servicename) 访问它们 (...svc.cluster.local).
这是我需要创建的 Headless 服务:
---
apiVersion: v1
kind: Service
metadata:
name: spilodemo-svc
labels:
application: spilo
spilo-cluster: spilodemo
spec:
clusterIP: None
selector:
application: spilo
spilo-cluster: spilodemo
此处将选择器设置为绑定所有三个 pods 很重要。另一件重要的事情是将以下行添加到您的 StatefulSet,其名称等于无头服务:
serviceName: spilodemo-svc
这是 Kubernetes 部分。现在您可以使用 DNS 名称引用您的 Pods:
spilodemo-0.spilodemo-svc
spilodemo-1.spilodemo-svc
spilodemo-2.spilodemo-svc
或 FQDN:
spilodemo-0.spilodemo-svc.<namespace>.svc.cluster.local
spilodemo-1.spilodemo-svc.<namespace>.svc.cluster.local
spilodemo-2.spilodemo-svc.<namespace>.svc.cluster.local
Patroni 的工作原理
但是,使用 Pods' DNS 名称对客户端没有意义,因为它们需要单点访问。为此,Patroni 团队建议像这样创建一个 ClusterIP 服务:
---
apiVersion: v1
kind: Service
metadata:
name: spilodemo
labels:
application: spilo
spilo-cluster: spilodemo
spec:
type: ClusterIP
ports:
- name: postgresql
port: 5432
targetPort: 5432
注意:没有选择器。这不是错误。当您创建这样的服务时,Kubernetes 会创建一个 ClusterIP 服务(然后可以使用 IP 或主机名来引用它)但没有端点。这意味着您连接到它的 IP 或它的 DNS 名称:spilodemo.<namespace>.svc.cluster.local
,连接挂起。
出于这个原因,Patroni 团队要求您在 YAML 文件中添加以下与 ClusterIP 服务同名的端点。
apiVersion: v1
kind: Endpoints
metadata:
name: spilodemo
labels:
application: spilo
spilo-cluster: spilodemo
subsets: []
Patroni 在内部 Python 中有一段代码,通过 Kubernetes API 使用 Master Pod IP 更新此端点。 Patroni 能够使用上面的相关标签(应用程序、spilo 集群)确定要更新的端点,您甚至可以自定义这些标签。
此时,Patroni 集群客户端只需要使用这个 DNS 名称(ClusterIP 之一)或相关 IP:
spilodemo.spilons.svc.cluster.local
连接自动重定向到Pod主节点IP。
到目前为止一切顺利。现在是令人困惑的部分。如果您查看 Spilo 代码中的 Patroni Kubernetes sample file,您会发现另一个无头服务已经存在。
---
# headless service to avoid deletion of patronidemo-config endpoint
apiVersion: v1
kind: Service
metadata:
name: spilodemo-config
labels:
application: spilo
spilo-cluster: spilodemo
spec:
clusterIP: None
让我感到困惑的是这种无头服务的存在。我不明白它的目的。一开始我以为是headless服务需要有上面提到的Pods DNS名称。但是我错了。这项服务的目的不同。基本上,Zalando 团队不知道用户如何编写 YAML 文件来部署 Patroni。如果用户创建端点但忘记将其关联到服务,则 Kubernetes 将其视为孤儿并将其删除。出于这个原因,Patroni 代码自己创建了这个服务。事实上,如果你不在 YAML 文件中定义它,Patroni 会为你创建它。
那么,如果 Patroni 为您创建了它,为什么他们将其添加到上面的示例 YAML 中?原因是权限。如果 Pod 没有权限则无法创建它。这就是他们将其添加到 YAML 中的原因。这有点令人困惑,但这就是整个故事。