5.6 使用headless服务来发现独立的pod
已经看到如何使用服务来提供稳定的IP地址,从而允许客户端连接到支持服务的每个pod(或其他端点)。到服务的每个连接都被转发到一个随机选择的pod上。但是如果客户端需要链接到所有的pod呢?如果后端的pod都需要连接到所有其他pod呢?通过服务连接显然不是这样的,那是怎样的呢?
要让客户端连接到所有pod,需要找出每个pod的IP。一种选择是让客户端调用Kubernetes API服务器并通过API调用获取pod及其IP地址列表,但由于应始终努力保持应用程序与Kubernetes无关,因此使用API 服务器并不理想。
幸运的是,Kubernetes允许客户通过DNS查找发现pod IP。通常,当执行服务的DNS查找时,DNS服务器会返回单个IP——服务的集群IP。但是,如果告诉Kubernetes,不需要为服务提供集群IP(通过在服务 spec中将clusterIP字段设置为None来完成此操作),则DNS服务器将返回pod IP而不是单个服务IP。
DNS服务器不会返回单个DNS A记录,而是会为该服务返回多个A记录,每个记录指向当时支持该服务的单个pod的IP。客户端因此可以做一个简单的DNS A记录查找并获取属于该服务一部分的所有pod的IP。客户端可以使用该信息连接到其中的一个、多个或全部。
5.6.1 创建headless 服务
将服务 spec中的clusterIP字段设置为None会使服务成为headless服务,因为Kubernetes不会为其分配集群IP,客户端可通过该IP将其连接到支持它的pod。
现在将创建一个名为kubia-headless的headless 服务。以下代码清单显示了它的定义。
代码清单5.18 一个headless服务:kubia-svc-headless.yaml
apiVersion: v1
kind: Service
metadata:
name: kubia-headless
spec:
clusterIP: None
ports:
- port: 80
targetPort: 8080
selector:
app: kubia
在使用 kubectl create
创建服务之后,可以通过 kubectl get
和 kubectl describe
来查看服务,你会发现它没有集群IP,并且它的后端包含与pod选择器匹配的(部分)pod。“部分”是因为pod包含就绪探针,所以只有准备就绪的pod会被列出作为服务的后端文件来确保至少有两个pod报告已准备就绪,如上例所示:
$ kubectl exec <pod name> -- touch /var/ready
5.6.2 通过DNS发现pod
准备好pod后,现在可以尝试执行DNS查找以查看是否获得了实际的pod IP。需要从其中一个pod中执行查找。不幸的是,kubia容器镜像不包含nslookup(或dig)二进制文件,因此无法使用它执行DNS查找。
所要做的就是在集群中运行的一个pod中执行DNS查询。为什么不寻找一个包含所需二进制文件的镜像来运行新的容器?要执行与DNS相关的操作,可以使用Docker Hub上提供的tutum/dnsutils容器镜像,它包含nslookup和dig二进制文件。要运行pod,可以完成创建YAML清单并将其传给 kubectl create
的整个过程。但是太烦琐了,对吗?幸运的是,有一个更快的方法。
不通过YAML文件运行pod
在第1章中,已经使用 kubectl run
命令在没有YAML清单的情况下创建了pod。但是这次只想创建一个pod,不需要创建一个ReplicationController来管理pod。可以这样做:
$ kubectl create deployment dnsutils --image=tutum/dnsutils -- sleep infinity
理解headless服务的DNS A记录解析
使用新创建的pod执行DNS查找:
$ kubectl exec dnsutils-77ccd77fb6-q2f92 -- nslookup kubia-headless
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: kubia-headless.default.svc.cluster.local
Address: 172.18.0.8
Name: kubia-headless.default.svc.cluster.local
Address: 172.18.0.7
Name: kubia-headless.default.svc.cluster.local
Address: 172.18.0.9
DNS服务器为 kubia-headless.default.svc.cluster.local
FQDN返回两个不同的IP。这些是报告准备就绪的两个pod的IP。可以通过使用 kubectl get pods -o wide
列出pod来确认此问题,该清单显示了pod的IP。
nslookup(意为name server lookup)是一个网络管理命令行界面工具,用户可以利用nslookup查询域名的ip地址以及ip地址所对应的域名
这与常规(非headless服务)服务返回的DNS不同,比如kubia服务,返回的IP是服务的集群IP:
$ kubectl exec dnsutils-77ccd77fb6-q2f92 -- nslookup kubia-nodeport
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: kubia-nodeport.default.svc.cluster.local
Address: 10.111.77.199
尽管headless服务看起来可能与常规服务不同,但在客户的视角上它们并无不同。即使使用headless服务,客户也可以通过连接到服务的DNS名称来连接到pod上,就像使用常规服务一样。但是对于headless服务,由于DNS返回了pod的IP,客户端直接连接到该pod,而不是通过服务代理。
注意 headless服务仍然提供跨pod的负载平衡,但是通过DNS轮询机制不是通过服务代理。
5.6.3 发现所有的pod——包括未就绪的pod
只有准备就绪的pod能够作为服务的后端。但有时希望即使pod没有准备就绪,服务发现机制也能够发现所有匹配服务标签选择器的pod。
幸运的是,不必通过查询Kubernetes API服务器,可以使用DNS查找机制来查找那些未准备好的pod。要告诉Kubernetes无论pod的准备状态如何,希望将所有pod添加到服务中:
apiVersion: v1
kind: Service
metadata:
name: kubia-headless
spec:
publishNotReadyAddresses: true #这个决定了未准备好的endpoints是否在DNS的记录中
clusterIP: None
ports:
- port: 80
targetPort: 8080
selector:
app: kubia
检查是否生效
$ kubectl exec dnsutils-77ccd77fb6-q2f92 -- nslookup kubia-headless
5.7 排除服务故障
服务是Kubernetes的一个重要概念,也是让许多开发人员感到困扰的根源。许多开发人员为了弄清楚无法通过服务IP或FQDN连接到他们的pod的原因花费了大量时间。出于这个原因,了解一下如何排除服务故障是很有必要的:
如果无法通过服务访问pod,应该根据下面的列表进行排查:
- 首先,确保从集群内连接到服务的集群IP,而不是从外部。
- 不要通过ping服务IP来判断服务是否可访问(请记住,服务的集群IP是虚拟IP,是无法ping通的)。
- 如果已经定义了就绪探针,请确保它返回成功;否则该pod不会成为服务的一部分。
- 要确认某个容器是服务的一部分,请使用
kubectl get endpoints
来检查相应的端点对象。 - 如果尝试通过FQDN或其中一部分来访问服务(例如,
myservice.mynamespace.svc.cluster.local
或myservice.mynamespace
),但并不起作用,请查看是否可以使用其集群 IP而不是FQDN来访问服务。 - 检查是否连接到服务公开的端口,而不是目标端口。
- 尝试直接连接到pod IP以确认pod正在接收正确端口上的连接。
- 如果甚至无法通过pod的IP访问应用,请确保应用不是仅绑定到本地主机。
这应该可以帮助解决大部分与服务相关的问题。将在第11章中了解更多有关服务如何工作的内容。通过了解它们的实现方式,应该可以更轻松地对它们进行故障排除。
网友评论