在kubernetes中每一個service都會被分配一個虛擬IP,每一個Service在正常情況下都會長時間不會改變,這個相對於pod的不定IP,對於集羣中APP的使用相對是穩定的。但是Service的信息注入到pod目前使用的是環境變量的方式,並且十分依賴於pod(rc)和service的創建順序,這使得這個集羣看起來又不那麼完美,於是kubernetes以插件的方式引入了DNS系統,利用DNS對Service進行一個映射,這樣我們在APP中直接使用域名進行引用,避免了之前的變量氾濫問題,也避免了創建順序的尷尬局面。
skyDNS: 提供DNS解析服務
etcd:用於skyDNS的存儲
kube2sky:連接Kubernetes和skyDNS
1. skydns配置文件
創建DNS服務的RC配置文件,在這個RC配置中包含了3個Container的定義
[root@docker1 dns]# cat skydns-rc.yaml | grep -v '#'
apiVersion: v1
kind: ReplicationController
metadata:
name: kube-dns-v9
namespace: kube-system
labels:
k8s-app: kube-dns
version: v9
kubernetes.io/cluster-service: "true"
spec:
replicas: 1
selector:
k8s-app: kube-dns
version: v9
template:
metadata:
labels:
k8s-app: kube-dns
version: v9
kubernetes.io/cluster-service: "true"
spec:
containers:
- name: etcd
image: kube-registry:5000/etcd
resources:
limits:
cpu: 100m
memory: 50Mi
command:
- /usr/local/bin/etcd
- -data-dir
- /var/etcd/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: /var/etcd/data
- name: kube2sky
image: kube-registry:5000/kube2sky
resources:
limits:
cpu: 100m
memory: 50Mi
args:
- -domain=cluster.local
- -kube_master_url=http://192.168.123.201:8080
- name: skydns
image: kube-registry:5000/skydns
resources:
limits:
cpu: 100m
memory: 50Mi
args:
- -machines=http://localhost:4001
- -addr=0.0.0.0:53
- -domain=cluster.local
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
volumes:
- name: etcd-storage
emptyDir: {}
(1)kube2sky容器需要訪問Kubernetes Master,需要配置Master所在物理主機的IP地址和端口
(2)kube2sky容器和skydns容器的啓動參數-domain,設置Kubernetes集羣中Service所屬的域名,本例中爲cluster.local。啓動後,kube2sky會監聽Kubernetes,當有新的Service創建時,就會生成相應的記錄並保存到etcd中。kube2sky爲每個Service生成兩條記錄:
<service_name>.<namespace_name>.<domain>
<service_name>.<namespace_name>.svc.<domain>
(3)skydns的啓動參數-addr=0.0.0.0:53表示使用本機TCP和UDP的53端口提供服務。
創建DNS服務的Service配置文件如下:
[root@docker1 dns]# cat skydns-svc.yaml | grep -v '#'
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: 10.254.0.3
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地址範圍內。
2. 修改每個Node上的Kubelet啓動參數
–cluster_dns=10.254.0.3 爲DNS服務的ClusterIP地址
–cluster_domain=cluster.local 爲DNS服務中設置的域名
修改/etc/kubernetes/kubelet:
# vim /etc/kubernetes/kubelet
KUBELET_ARGS="--cluster_dns=10.254.0.3 --cluster_domain=cluster.local"
或者修改/usr/lib/systemd/system/kubelet.service
[root@docker2 ~]# cat /usr/lib/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service
[Service]
WorkingDirectory=/var/lib/kubelet
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/kubelet
ExecStart=/usr/bin/kubelet --cluster_dns=10.254.0.3 --cluster_domain=cluster.local \
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBELET_API_SERVER \
$KUBELET_ADDRESS \
$KUBELET_PORT \
$KUBELET_HOSTNAME \
$KUBE_ALLOW_PRIV \
$KUBELET_ARGS
Restart=on-failure
[Install]
WantedBy=multi-user.target
3. 創建skydns Pod和服務
通過kubectl create完成RC和Service的創建:
[root@docker1 dns]# kubectl create -f skydns-rc.yaml
replicationcontrollers/kube-dns-v9
[root@docker1 dns]# kubectl create -f skydns-svc.yaml
services/kube-dns
創建完成後,查看到系統創建的RC、Pod和Service都已創建成功:
然後我們創建一個普通的Service,以redis-master服務爲例:
[root@docker1 redis_yaml]# cat redis-master-service.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-master
labels:
name: redis-master
spec:
selector:
name: redis-master
ports:
- port: 6379
targetPort: 6379
查看創建出來的Service:
[root@docker1 redis_yaml]# kubectl create -f redis-master-service.yaml
[root@docker1 redis_yaml]# kubectl get services
NAME LABELS SELECTOR IP(S) PORT(S)
redis-master name=redis-master name=redis-master 10.254.7.160 6379/TCP
4. 通過DNS查找Service
使用一個帶有nslookup工具的Pod來驗證DNS服務是否能夠正常工作:
[root@docker1 demo]# cat busybox.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- image: kube-registry:5000/busybox
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
name: busybox
restartPolicy: Always
運行kubectl create -f busybox.yaml完成創建。
在該容器成功啓動後,通過kubectl exec nslookup 進行測試:
[root@docker1 demo]# kubectl exec busybox -- nslookup redis-master
Server: 10.254.0.3
Address 1: 10.254.0.3
Name: redis-master
Address 1: 10.254.7.160
可以看到,通過DNS服務器10.254.0.3成功找到了名爲”redis-master”服務的IP地址:10.254.7.160
如果某個Service屬於自定義的命名空間,那麼進行Service查找時,需要帶個namespace的名字。下面以查看kube-dns服務爲例:
[root@docker1 demo]# kubectl exec busybox -- nslookup kube-dns.kube-system
Server: 10.254.0.3
Address 1: 10.254.0.3
Name: kube-dns.kube-system
Address 1: 10.254.0.3
如果僅使用”kube-dns”來進行查找,則將會失敗:
nslookup: can’t resolve ‘kube-dns’
查看多個容器組成的Pod時,要添加-c選項指定容器的名稱
# kubectl logs kube-dns-v9-curdr --namespace=kube-system
Error from server: a container name must be specified for pod kube-dns-v9-curdr
# kubectl logs kube-dns-v9-curdr -c skydns --namespace=kube-system
5. DNS服務的工作原理解析
(1)kube2sky容器應用通過調用Kubernetes Master的API獲得集羣中所有Service的信息,並持續監控新Service的生成,然後定稿etcd中。
查看etcd存儲的Service信息
# kubectl exec kube-dns-v9-evgl6 -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster
/skydns/local/cluster/default
/skydns/local/cluster/svc
/skydns/local/cluster/kube-system
可以看到在skydns鍵下面,根據我們配置的域名(cluster.local)生成了local/cluster子鍵,接下來是namespace(default和kube-system)和svc(下面也按namespace生成子鍵)。
查看redis-master服務對應的鍵值:
# kubectl exec kube-dns-v9-evgl6 -c etcd --namespace=kube-system etcdctl get /skydns/local/cluster/default/redis-master
{"host":"10.254.7.160","priority":10,"weight":10,"ttl":30,"targetstrip":0}
可以看到,redis-master服務對應的完整域名爲redis-master.default.cluster.local,並且其IP地址爲10.254.7.160。
(2)根據Kubelet啓動參數的設置(–cluster_dns),Kubelet會在每個新創建的Pod中設置DNS域名解析配置文件/etc/resolv.conf文件,在其中增加了一條nameserver配置和一條search配置:
nameserver 10.254.0.3
nameserver 202.96.128.86
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
通過名字服務器10.254.0.3訪問的實際上是skydns在53端口上提供的DNS解析服務。
(3)應用程序就能夠像訪問網站域名一樣,僅僅通過服務的名字就能訪問到服務了。
例如,設置redis-slave的啓動腳本爲:
redis-server –slaveof redis-master 6379
創建redis-slave的Pod並啓動它。
之後,我們可以登錄redis-slave容器中查看,其通過DNS域名服務找到了redis-master的IP地址10.254.7.160,併成功建立了連接。
通過DNS設置,對於其他Service(服務)的查詢將可以不再依賴系統爲每個Pod創建的環境變量,而是直接使用Service的名字就能對其進行訪問,使得應用程序中的代碼更簡潔了。
參考資料
書:Kubernetes權威指南 P250