K8s提供的虚拟DNS服务名为skydns,由4个组件组成。
skydns服务由一个RC和一个Service的定义组成,分别由配置文件skydns-rc.yaml和skydns-svc.yaml定义。
skydns的RC配置文件skydns-rc.yaml的内容如下,包含了4个容器的定义:
skydns-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: kube-dns-v11
namespace: kube-system
labels:
k8s-app: kube-dns
version: v11
kubernetes.io/cluster-service: "true"
spec:
replicas: 1
selector:
k8s-app: kube-dns
version: v11
template:
metadata:
labels:
k8s-app: kube-dns
version: v11
kubernetes.io/cluster-service: "true"
spec:
containers:
- name: etcd
image: gcr.io/google_containers/etcd-amd64:2.2.1
resources:
limits:
cpu: 100m
memory: 50Mi
requests:
cpu: 100m
memory: 50Mi
command:
- /usr/local/bin/etcd
- -data-dir
- /tmp/data
- -listen-client-urls
- http://127.0.0.1:2379,http://127.0.0.1:4001
- -advertise-client-urls
- http://127.0.0.1:2379,http://127.0.0.1:4001
- -initial-cluster-token
- skydns-etcd
volumeMounts:
- name: etcd-storage
mountPath: /tmp/data
- name: kube2sky
image: gcr.io/google_containers/kube2sky-amd64:1.15
resources:
limits:
cpu: 100m
#kube2sky watches all pods
memory: 50Mi
requests:
cpu: 100m
memory: 50Mi
livenessProbe:
httpGet:
path: /healthz
port: 8080
schema: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 5
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /readiness
port: 8081
schema: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
args:
- --kube-master-url=http://192.168.18.3:8080
- --domain=cluster.local
- name: skydns
image: gcr.io/google_containers/skydns:2015-10-13-8c72f8c
resources:
limits:
cpu: 100m
memory: 50Mi
requests:
cpu: 100m
memory: 50Mi
args:
- -machines=http://127.0.0.1:4001
- -addr=0.0.0.0:53
- -ns-rotate=false
- -domain=cluster.local
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- name:
image: gcr.io/google_containers/exechealthz:1.0
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi
args:
- -cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1 >/dev/null
- -port=8080
ports:
- containerPort: 8080
protocol: TCP
volumes:
- name: etcd-storage
emptyDir: {}
dnsPolicy: Default
需要修改的几个配置参数如下:
kube2sky容器需要访问K8s Master,需要配置Master所在物理主机的IP地址和端口号,本例中设置参数–kube_master_url的值为http://192.168.18.3:8080
kube2sky容器和skydns容器的启动参数–domain,设置kubernetes集群中Service所属的域名,本例中为cluster.local。启动后,kube2sky会通过API Server监控集群中全部Service的定义,生成相应的记录并保存到etcd中。kube2sky为每个Service生成以下两条记录。
skydns的启动参数-addr=0.0.0.0:53表示使用本机TCP和UDP的53端口提供服务。
skydns-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "KubeDNS"
spec:
selector:
k8s-app: kube-dns
clusterIP: 169.169.0.100
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
注意:skydns服务使用的clusterIP需要指定一个固定的IP地址,每个Node的kubelet进程都将使用这个IP地址,不能通过Kubernetes自动分配。
这个IP地址需要在kube-apiserver启动参数–service-cluster-ip-range指定的IP地址范围内。
在创建skydns容器之前,先修改每个Node上kubelet的启动参数。
修改每台Node上kubelet的启动参数,加上以下两个参数:
重启kubelet服务
通过kubectl create完成skydns的RC和Service的创建:
kubectl create -f skydns-rc.yaml
kubectl create -f skydns-svc.yaml
查看RC、Pod和Service,确保容器成功启动:
kubectl get rc --namespace=kube-system
kubectl get pods --namespace=kube-system
kubectl get services --namespace=kube-system
为redis-master应用创建一个Service
redis-master-service.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-master
labels:
name: redis-master
spec:
ports:
- port: 6379
targetPort: 6379
selector:
name: redis-master
查看创建好的redis-master service:
kubectl get services
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
redis-master 169.169.8.10 <none> 6379/TCP 1h
系统为redis-master服务分配了一个虚拟IP地址:169.169.8.10。
至此在K8s集群内虚拟DNS服务就搭建好了。在需要访问redis-master的应用中,仅需要配置上redis-master Service的名称和服务的端口号,就能够访问到redis-master应用了。
redis-slave镜像的启动脚本/run.sh的内容为:
if [[ ${GET_HOSTS_FROM:-dns} == "env" ]]; then
redis-server --slaveof ${REDIS_MASTER_SERVICE_HOST} 6379
else
redis-server --slaveof redis-master 6379
fi
在使用DNS模式的情况下,redis-slave配置的Master地址为:redis-master:6379。通过服务名进行配置,能够极大地简化客户端应用对后端服务变化的感知,包括服务虚拟IP地址的变化、服务后端Pod的变化等,对应用程序的微服务架构实现提供了强有力的支撑。
接下来使用一个带有nslookup工具的Pod来验证DNS服务是否能够正常工作。
busybox.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- name: busybox
image: gcr.io/google_containers/busybox
command:
- sleep
- "3600"
运行kubectl create -f busybox.yaml完成创建。
在该容器成功启动后,通过kubectl exec
kubectl exec busybox -- nslookup redis-master
Server: 169.169.0.100
Address 1: 169.169.0.100
Name: redis-master
Address 1: 169.169.8.10
可以看到,通过DNS服务器169.169.0.100成功找到了名为"redis-master"服务的IP地址:169.169.8.10.
如果某个Service属于不同的命名空间,在进行Service查找时,需要带上namespace的名字。下面以查找kube-dns服务为例:
kubectl exec busybox -- nslookup kube-dns.kube-system
Server: 169.169.0.100
Address 1: 169.169.0.100
Name: kube-dns.kube-system
Address 1: 169.169.0.100
仅使用kube-dns来进行查找,将会失败:
nslookup: can’t resolve ‘kube-dns’
(1)kube2sky容器应用通过调用K8s Master的API获得集群中所有Service的信息,并持续监控新Service的生成,然后写入etcd中。
查看etcd中存储的Service信息:
kubectl exec kube-dns-v8-5tpm2 -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster
/skydns/local/cluster/default
/skydns/local/cluster/svc
/skydns/local/cluster/kube-system
查看redis-master服务对应的键值:
kubectl exec kube-dns-v8-5tpm2 -c etcd --namespace=kube-system etcdctl get /skydns/local/cluster/default/redis-master
{"host":"169.169.8.10","priority":10,"weight":10,"ttl":30,"targetstrip":0}
可以看到,redis-master服务对应的完整域名为redis-master.default.cluster.local,并且IP地址为169.169.8.10。
(2)根据kubelet启动参数的设置(–cluster_dns),kubelet会在每个新创建的Pod中设置DNS域名解析配置文件/etc/resolv.conf文件,在其中增加了一条namespace配置和一条search配置:
nameserver 169.169.0.100
search default.svc.cluster.local svc.cluster.local cluster.local localdomain
通过名字服务器169.169.0.100访问的实际上就是skydns在53端口上提供的DNS解析服务。
(3)最后,应用程序就能够像访问网站域名一样,仅仅通过服务的名字就能访问到服务了。
以redis-slave为例,假设启动了redis-slave Pod,登陆redis-slave容器进行查看,可以看到其通过DNS域名服务找到了redis-master的IP地址169.169.8.10,并成功进行了连接。