在k8s集群,我们通常通过 svc 做负载均衡来访问背后的 pod 实体,如果需要直接访问 pod 除了直接通过 pod IP 的方式还有什么方法呢?
首先我们需要知道,哪些对象具有DNS名字。
在k8s集群中每个Service都会有一个DNS名称,Services 包括两种,一种是普通的service服务,一种是 headless services。 普通的service 的DNS会解析到一个服务的集群IP,headless services 则会直接解析到所包含的pod的IP。
DNS 命名可以参考:https://github.com/kubernetes/dns/blob/master/docs/specification.md
service DNS 命名一般是:
<服务名>.<命名空间>.svc.cluster.local
下面是个案例:
apiVersion: v1
kind: Service
metadata:
name: nginx-test-headless
labels:
app: nginx-test
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx-test
---
apiVersion: v1
kind: Service
metadata:
name: nginx-test
labels:
app: nginx-test
spec:
ports:
- port: 80
name: web
type: ClusterIP
selector:
app: nginx-test
---
apiVersion: v1
kind: Pod
metadata:
labels:
app: nginx-test
statefulset.kubernetes.io/pod-name: web-test
name: web-test
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx
ports:
- containerPort: 80
name: web
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
hostname: web-test01
subdomain: nginx-test-headless
创建了一个headless service和一个普通的services,我们可以通过 kubectl 查看:
$ kubectl get svc | grep nginx-test
nginx-test ClusterIP 10.247.89.84 <none> 80/TCP 2m
nginx-test-headless ClusterIP None <none> 80/TCP 2m
$ kubectl get po web-test -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-test 1/1 Running 0 2m 10.0.2.156 10.213.20.91 <none> <none>
我们在集群内通过dig
测试一下:
$ dig nginx-test.default.svc.cluster.local
; <<>> DiG 9.16.1-Ubuntu <<>> nginx-test.default.svc.cluster.local
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18201
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 87477d087ad94e3a (echoed)
;; QUESTION SECTION:
;nginx-test.default.svc.cluster.local. IN A
;; ANSWER SECTION:
nginx-test.default.svc.cluster.local. 5 IN A 10.247.89.84
;; Query time: 0 msec
;; SERVER: 10.247.3.10#53(10.247.3.10)
;; WHEN: Wed Mar 10 13:16:03 UTC 2021
;; MSG SIZE rcvd: 129
$ dig nginx-test-headless.default.svc.cluster.local
; <<>> DiG 9.16.1-Ubuntu <<>> nginx-test-headless.default.svc.cluster.local
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37124
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 38ab01af006bccc0 (echoed)
;; QUESTION SECTION:
;nginx-test-headless.default.svc.cluster.local. IN A
;; ANSWER SECTION:
nginx-test-headless.default.svc.cluster.local. 5 IN A 10.0.2.156
;; Query time: 0 msec
;; SERVER: 10.247.3.10#53(10.247.3.10)
;; WHEN: Wed Mar 10 13:15:56 UTC 2021
;; MSG SIZE rcvd: 147
可以看到访问 nginx-test-headless.default.svc.cluster.local
的域名被解析到 pod 的ip,nginx-test.default.svc.cluster.local
解析的是 service 分配的集群IP。
如果service 对应的多个pod,那么如何定位到具体的pod呢,这里可以通过 pod 的hostname
和subdomain
两个字段实现。
比如再加入一个pod:
apiVersion: v1
kind: Pod
metadata:
labels:
app: nginx-test
statefulset.kubernetes.io/pod-name: web-test
name: web-test02
namespace: default
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx
ports:
- containerPort: 80
name: web
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
hostname: web-test02
subdomain: nginx-test-headless
通过 dig 查看:
$ dig nginx-test-headless.default.svc.cluster.local
; <<>> DiG 9.16.1-Ubuntu <<>> nginx-test-headless.default.svc.cluster.local
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23884
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 97fb1608fcf0bdf7 (echoed)
;; QUESTION SECTION:
;nginx-test-headless.default.svc.cluster.local. IN A
;; ANSWER SECTION:
nginx-test-headless.default.svc.cluster.local. 5 IN A 10.0.2.156
nginx-test-headless.default.svc.cluster.local. 5 IN A 10.0.1.232
;; Query time: 0 msec
;; SERVER: 10.247.3.10#53(10.247.3.10)
;; WHEN: Wed Mar 10 13:57:22 UTC 2021
;; MSG SIZE rcvd: 208
发现被解析到两个 POD 的 IP, hostname
和subdomain
会在 service 下面构建子域名,通过<hostname>.<subdomain>.<命名空间>.svc.cluster.local
访问:
$ dig web-test01.nginx-test-headless.default.svc.cluster.local
; <<>> DiG 9.16.1-Ubuntu <<>> web-test01.nginx-test-headless.default.svc.cluster.local
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23896
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 2213905f5ac299da (echoed)
;; QUESTION SECTION:
;web-test01.nginx-test-headless.default.svc.cluster.local. IN A
;; ANSWER SECTION:
web-test01.nginx-test-headless.default.svc.cluster.local. 5 IN A 10.0.2.157
;; Query time: 0 msec
;; SERVER: 10.247.3.10#53(10.247.3.10)
;; WHEN: Wed Mar 10 14:10:02 UTC 2021
;; MSG SIZE rcvd: 169
$ dig web-test02.nginx-test-headless.default.svc.cluster.local
; <<>> DiG 9.16.1-Ubuntu <<>> web-test02.nginx-test-headless.default.svc.cluster.local
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7948
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 71b4649549ed2014 (echoed)
;; QUESTION SECTION:
;web-test02.nginx-test-headless.default.svc.cluster.local. IN A
;; ANSWER SECTION:
web-test02.nginx-test-headless.default.svc.cluster.local. 5 IN A 10.0.1.241
;; Query time: 0 msec
;; SERVER: 10.247.3.10#53(10.247.3.10)
;; WHEN: Wed Mar 10 14:09:11 UTC 2021
;; MSG SIZE rcvd: 169
$ ping nginx-test-headless.default.svc.cluster.local
PING nginx-test-headless.default.svc.cluster.local (10.0.2.156) 56(84) bytes of data.
64 bytes from 10-0-2-156.nginx-test-headless.default.svc.cluster.local (10.0.2.156): icmp_seq=1 ttl=64 time=0.129 ms
64 bytes from 10-0-2-156.nginx-test-headless.default.svc.cluster.local (10.0.2.156): icmp_seq=2 ttl=64 time=0.154 ms
k8s重的 statfulset 就是这种方法来指定具体的 pod,通过创建带 hostname 和 subdomain 的 pod,再配合 headless service 实现指向具体的pod
statefulset DNS命名示例:
集群域名 | 命名空间 | 服务名 | StatefulSet | StatefulSet DNS | Pod 主机名 | Pod DNS |
---|---|---|---|---|---|---|
cluster.local | default | nginx | web | nginx.default.svc.cluster.local | web-0 | web-0.nginx.default.svc.cluster.local |
kube.local | rcmd | nginx | web | nginx.rcmd.svc.kube.local | web-0 | web-0.nginx.rcmd.svc.kube.local |
除了通过svc
的方式访问 pod 外,Pod 会对应如下 DNS 名字解析:
<pod ip地址>.<命名空间>.pod.cluster.local
比如一个pod ip 是 172.26.3.7 的pod,在命名空间rcmd
,DNS解析为172-26-3-7.rcmd.pod.cluster.local
不过这种 DNS 实用性比较低,因为如果有了 IP地址了,也不需要 DNS了。