容器网络域名解析
使用service的原因
1. Pod的IP不是固定的。
2. Pod实例之间需要负载均衡。
部署Service和Deploy
使用selector字段来声明这个Service只代理携带了app=hostnames标签的Pod。这个Service的80端口,代理的是Pod的9376端口。
apiVersion: v1
kind: Service
metadata:
name: hostnames
spec:
selector:
app: hostnames
ports:
- name: default
protocol: TCP
port: 80
targetPort: 9376
应用的作用是每次访问9376端口时返回自己的hostname。
apiVersion: apps/v1
kind: Deployment
metadata:
name: hostnames
spec:
selector:
matchLabels:
app: hostnames
replicas: 3
template:
metadata:
labels:
app: hostnames
spec:
containers:
- name: hostnames
image: k8s.gcr.io/serve_hostname
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9376
protocol: TCP
手动下载serve_hostname镜像
docker pull anjia0532/google-containers.serve_hostname
docker tag docker.io/anjia0532/google-containers.serve_hostname:latest k8s.gcr.io/serve_hostname
查看ep
被selector选中的Pod称为Service的Endpoints。
只有处于Running状态且readinessProbe检查通过的Pod,才会出现在Service的Endpoints列表里。
$ kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hostnames-7c89f997cf-pdg7p 1/1 Running 0 4m29s 10.244.1.8 192.168.0.106 <none> <none>
hostnames-7c89f997cf-tzmcn 1/1 Running 0 3m22s 10.244.1.10 192.168.0.106 <none> <none>
hostnames-7c89f997cf-vc4qc 1/1 Running 0 4m29s 10.244.1.9 192.168.0.106 <none> <none>
$ kubectl get ep hostnames
NAME ENDPOINTS AGE
hostnames 10.244.1.10:9376,10.244.1.8:9376,10.244.1.9:9376 12m
访问service(负载均衡方式是轮询)
$ kubectl get svc hostnames
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hostnames ClusterIP 10.102.11.183 <none> 80/TCP 16m
$ curl 10.102.11.183:80
hostnames-7c89f997cf-tzmcn
实现原理
Service是由kube-proxy + iptables来实现的。kube-proxy通过Service的Informer感知hostnames Service对象的添加,在每个主机上创建iptables规则。
无法ping通cluster ip的原因
因为10.99.149.144只是一条iptables规则上的配置,没有真正的网络设备,所以ping这个地址是不会有响应的。
iptables规则
PREROUTING nat
# KUBE-SERVICES链插在DOCKER链前面
$ iptables -t nat -nvL PREROUTING
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
138 44896 KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
9 932 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
# DNAT 通过轮询的方式把包传给ep
# KUBE-MARK-MASQ对流入的IP包设置标志(--set-xmark)
$ iptables -t nat -nvL KUBE-SERVICES
Chain KUBE-SERVICES (2 references)
pkts bytes target prot opt in out source destination
...
0 0 KUBE-MARK-MASQ tcp -- * * !10.244.0.0/16 10.102.11.183 /* default/hostnames:default cluster IP */ tcp dpt:80
0 0 KUBE-SVC-ODX2UBAZM7RQWOIU tcp -- * * 0.0.0.0/0 10.102.11.183 /* default/hostnames:default cluster IP */ tcp dpt:80
...
# 为了保证每条被选中的概率都相同,probability字段的值分别设置为 1/3(0.333…)、1/2 和 1。
# 第一条规则被选中的概率就是1/3;如果第一条规则没有被选中,那么只剩下两条规则了,第二条规则的probability设置为1/2;最后一条设置为1。
$ iptables -t nat -nvL KUBE-SVC-ODX2UBAZM7RQWOIU
Chain KUBE-SVC-ODX2UBAZM7RQWOIU (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SEP-N6BO7JPFL3IPM3GK all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.33332999982
0 0 KUBE-SEP-XBIWV66JPBUXLYCY all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.50000000000
0 0 KUBE-SEP-ZDT36JFL743NS3VU all -- * * 0.0.0.0/0 0.0.0.0/0
$ iptables -t nat -nvL KUBE-SEP-N6BO7JPFL3IPM3GK
Chain KUBE-SEP-N6BO7JPFL3IPM3GK (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.244.1.10 0.0.0.0/0
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp to:10.244.1.10:9376
$ iptables -t nat -nvL KUBE-SEP-XBIWV66JPBUXLYCY
Chain KUBE-SEP-XBIWV66JPBUXLYCY (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.244.1.8 0.0.0.0/0
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp to:10.244.1.8:9376
$ iptables -t nat -nvL KUBE-SEP-ZDT36JFL743NS3VU
Chain KUBE-SEP-ZDT36JFL743NS3VU (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.244.1.9 0.0.0.0/0
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp to:10.244.1.9:9376
如果源地址不是容器网络网段,那么需要设置标志,即不设置标志的情况是源地址不是容器网络网段中该Service后端Pod IP,DNAT处理后变成了容器之间直接访问。
针对源地址不是容器网络网段的场景,设置标志的原因是nodePort场景:
client
\ v
\ \
v \
node 1 <--- node 2
| v SNAT
| | --->
v |
endpoint
当外部client通过node 2的地址访问Service时,node2把请求转发给Pod所在的node1,node1中Pod处理完请求后响应node2,node2响应client。
设置了标志的IP包,需要执行SNAT,即node2执行SNAT 如果不执行SNAT,那么client发送给node2的请求,会由node1来响应,报错。
$ iptables -t nat -nvL POSTROUTING
Chain POSTROUTING (policy ACCEPT 6 packets, 360 bytes)
pkts bytes target prot opt in out source destination
...
1984 122K KUBE-POSTROUTING all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes postrouting rules */
...
$ iptables -t nat -nvL KUBE-POSTROUTING
Chain KUBE-POSTROUTING (1 references)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */ mark match 0x4000/0x4000
解析内部域名
Deployment的yaml文件中不能指定spec.serviceName,StatefulSet的yaml文件中需要指定spec.serviceName。
Deployment常用ClusterIP模式的Service,解析svc的域名。
StatefulSet常用Headless模式的Service,解析Pod的域名。
解析service域名
ClusterIP模式的Service,记录格式是svc-name.namespace.svc.cluster.local,指向Cluster IP。
指定coredns后端实例来解析svc域名。
$ nslookup kubernetes.default.svc.cluster.local -po=53 10.244.1.2
Server: 10.244.1.2
Address: 10.244.1.2#53
Name: kubernetes.default.svc.cluster.local
Address: 10.96.0.1
指定了clusterIP=None的Headless Service,记录格式也是svc-name.namespace.svc.cluster.local,返回所有被代理的Pod的IP地址的集合。
# nslookup hostnames.default.svc.cluster.local -po=53 10.244.1.2
Server: 10.244.1.2
Address: 10.244.1.2#53
Name: hostnames.default.svc.cluster.local
Address: 10.244.1.22
Name: hostnames.default.svc.cluster.local
Address: 10.244.1.21
Name: hostnames.default.svc.cluster.local
Address: 10.244.1.20
解析Pod域名
ClusterIP模式和Headless模式的Service,代理的Pod记录格式是pod-name.svc-name.namespace.svc.cluster.local,指向Pod的IP地址。
1. 针对无状态应用的Pod,解析失败。
$ nslookup hostnames-7c89f997cf-bx2f7.hostnames.default.svc.cluster.local -po=53 10.244.1.2
Server: 10.244.1.2
Address: 10.244.1.2#53
** server can't find hostnames-7c89f997cf-bx2f7.hostnames.default.svc.cluster.local: NXDOMAIN
2. 针对有状态应用的Pod,解析成功。
$ nslookup hostnames-0.hostnames.default.svc.cluster.local -po=53 10.244.1.2
Server: 10.244.1.2
Address: 10.244.1.2#53
Name: hostnames-0.hostnames.default.svc.cluster.local
Address: 10.244.1.23
配置域名解析配置文件
$ cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 10.96.0.10 # kube-dns service clusterIP 解析k8s域名
nameserver 192.168.1.1
nameserver 192.168.0.1
# 域名解析成功
$ nslookup hostnames-0.hostnames.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: hostnames-0.hostnames.default.svc.cluster.local
Address: 10.244.1.23
域名解析出IPv6地址
# -query=AAAA 表示 IPv6。
nslookup -query=AAAA -po=53 域名 一个kube-dns的Pod的ipv6地址
或者
nslookup -query=AAAA 域名
参考资料
《深入剖析Kubernetes》