服務發現機制
Kubernetes提供了兩種發現Service的方法:
1.環境變量
當Pod運行的時候,Kubernetes會將之前存在的Service的信息通過環境變量寫到Pod中。
這種方法要求Pod必須要在Service之後啓動。
在Service之前啓動的Pod就不會有該Service的環境變量。
採用DNS的方式就沒有這個限制。
2.DNS
當有新的Service創建時,就會自動生成一條DNS記錄。
使用這種方法,需要安裝Cluster DNS。
在kubernetes中每一個service都會被分配一個虛擬IP,每一個Service在正常情況下都會長時間不會改變,這個相對於pod的不定IP,對於集羣中APP的使用相對是穩定的。但是Service的信息注入到pod目前使用的是環境變量的方式,並且十分依賴於pod(rc)和service的創建順序,這使得這個集羣看起來又不那麼完美,於是kubernetes以插件的方式引入了DNS系統,利用DNS對Service進行一個映射,這樣我們在APP中直接使用域名進行引用,避免了之前的變量氾濫問題,也避免了創建順序的尷尬局面。
Kubernetes1.2.7版本下Cluster DNS的安裝
閱讀github上官方源碼發現:
Cluster DNS自Kubernetes1.3版本開始,結構發生了變化。這裏先以1.2.7版本作爲研究。
Cluster DNS擴展插件用於支持Kubernetes的服務發現機制,1.2.7版本中,Cluster DNS主要包含如下幾項:
1)SkyDNS
提供DNS解析服務。
2)Etcd
用於DNS的存儲。
3)Kube2sky
監聽Kubernetes,當有新的Service創建時,將其註冊到etcd上。
4)healthz
提供對skydns服務的健康檢查功能。
在Master服務器上下載Kubernetes發佈包
Cluster DNS在Kubernetes發佈包的cluster/addons/dns目錄下
[root@k8s-master home]# yum -y install wget [root@k8s-master home]# wget https://codeload.github.com/kubernetes/kubernetes/tar.gz/v1.2.7 [root@k8s-master home]# tar -xf kubernetes-1.2.7.tar.gz [root@k8s-master home]# ls kubernetes-1.2.7 kubernetes-1.2.7.tar.gz sheng [root@k8s-master home]# cd kubernetes-1.2.7/cluster/addons/dns [root@k8s-master dns]# ls kube2sky MAINTAINERS.md OWNERS README.md skydns skydns-rc.yaml.in skydns-svc.yaml.in #skydns-rc.yaml.in和skydns-svc.yaml.in是兩個模板文件,通過設置的環境變量修改其中的相應屬性值,可以生成Replication Controller和Service的定義文件。
需要注意,skydns服務使用的clusterIP需要我們指定一個固定的IP地址,每個Node的kubelet進程都將使用這個IP地址,不能通過Kuberneters自動給skydns分配。
通過環境變量,配置參數
[root@k8s-master dns]# vi ~/.bash_profile #添加下面三行
export DNS_SERVER_IP="10.254.10.2"
export DNS_DOMAIN="cluster.local"
export DNS_REPLICAS=1
設置 Cluster DNS Service的IP爲 10.254.10.2(不能和已分配的IP重複,如10.0.10.0),Cluster DNS的本地域爲 cluster.local。
修改每臺Node上的kubelet啓動參數
在KUBELET_ARGS裏增加:
即:
# Add your own!
KUBELET_ARGS="--cluster_dns=10.254.10.2 --cluster_domain=cluster.local"
cluster_dns爲DNS服務的ClusterIP地址
cluster_domain爲DNS服務中設置的域名
重啓kubelet服務
生成dns-rc.yaml和dns-svc.yaml
1. skydns配置文件
創建DNS服務的RC配置文件,在這個RC配置中包含了3個Container的定義
[root@k8s-master dns]# cat skydns-rc.yaml 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://10.0.0.11: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生成兩條記錄:
(3)skydns的啓動參數-addr=0.0.0.0:53表示使用本機TCP和UDP的53端口提供服務。
創建DNS服務的Service配置文件如下:
[root@k8s-master dns]# cat 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: {{ pillar['dns_server'] }} #<---添加DNS_SERVER_IP clusterIP: 10.254.10.2 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地址範圍內。
3. 創建skydns Pod和服務
通過kubectl create完成RC和Service的創建:
(1)通過定義文件dns-rc.yaml創建Cluster DNS Replication Controller
[root@k8s-master dns]# kubectl create -f skydns-rc.yaml replicationcontroller "kube-dns-v11" created
驗證Cluster DNS Pod是否創建運行成功:
[root@k8s-master dns]# kubectl get pod --namespace=kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE
kube-dns-v11-plw4m 0/4 ContainerCreating 0 9m <none> k8s-node-3
containercreating表示創建不成功
查看錯誤原因:
[root@k8s-master dns]# kubectl describe pod --namespace=kube-system Name: kube-dns-v11-3pln8 Namespace: kube-system Node: k8s-node-3/10.0.0.14 Start Time: Thu, 07 Jun 2018 22:02:39 +0800 Labels: k8s-app=kube-dns kubernetes.io/cluster-service=true version=v11 Status: Pending IP: Controllers: ReplicationController/kube-dns-v11 Containers: etcd: Container ID: Image: kube-registry:5000/etcd Image ID: Port: 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 Limits: cpu: 100m memory: 500Mi Requests: cpu: 100m memory: 50Mi State: Waiting Reason: ContainerCreating Ready: False Restart Count: 0 Volume Mounts: /var/etcd/data from etcd-storage (rw) Environment Variables: <none> kube2sky: Container ID: Image: kube-registry:5000/kube2sky Image ID: Port: Args: --domain=cluster.local --kube-master-url=http://10.0.0.11:8080 Limits: cpu: 100m memory: 200Mi Requests: cpu: 100m memory: 50Mi State: Waiting Reason: ContainerCreating Ready: False Restart Count: 0 Liveness: http-get http://:8080/healthz delay=60s timeout=5s period=10s #success=1 #failure=5 Readiness: http-get http://:8081/readiness delay=30s timeout=5s period=10s #success=1 #failure=3 Volume Mounts: <none> Environment Variables: <none> skydns: Container ID: Image: kube-registry:5000/skydns Image ID: Ports: 53/UDP, 53/TCP Args: -machines=http://127.0.0.1:4001 -addr=0.0.0.0:53 -ns-rotate=false -domain=cluster.local. Limits: cpu: 100m memory: 200Mi Requests: cpu: 100m memory: 50Mi State: Waiting Reason: ContainerCreating Ready: False Restart Count: 0 Volume Mounts: <none> Environment Variables: <none> healthz: Container ID: Image: kube-registry:5000/exechealthz Image ID: Port: 8080/TCP Args: -cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1 >/dev/null -port=8080 Limits: cpu: 10m memory: 20Mi Requests: cpu: 10m memory: 20Mi State: Waiting Reason: ContainerCreating Ready: False Restart Count: 0 Volume Mounts: <none> Environment Variables: <none> Conditions: Type Status Initialized True Ready False PodScheduled True Volumes: etcd-storage: Type: EmptyDir (a temporary directory that shares a pod's lifetime) Medium: QoS Class: Burstable Tolerations: <none> Events: FirstSeen LastSeen Count From SubObjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 12h 2m 32 {kubelet k8s-node-3} Warning FailedSync Error syncing pod, skipping: failed to "StartContainer" for "POD" with ErrImagePull: "repository docker.io/kube-registry not found: does not exist or no pull access" 12h 9s 532 {kubelet k8s-node-3} Warning FailedSync Error syncing pod, skipping: failed to "StartContainer" for "POD" with ImagePullBackOff: "Back-off pulling image \"kube-registry:5000\""
(2)生成Service的定義文件dns-svc.yaml創建Service
[root@k8s-master dns]# kubectl create -f skydns-svc.yaml service "kube-dns" created
[root@k8s-master dns]# kubectl get svc --namespace=kube-system -o wide NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kube-dns 10.254.10.2 <none> 53/UDP,53/TCP 9s k8s-app=kube-dns
[root@k8s-master dns]# kubectl create -f skydns-svc.yaml
The Service "kube-dns" is invalid: spec.clusterIP: Invalid value: "10.0.10.0": provided IP is not in the valid range
表示地址有衝突
創建完成後,查看到系統創建的RC、Pod和Service都已創建成功:
然後我們創建一個普通的Service,以redis-master服務爲例:
[root@k8s-master home]# mkdir k8s_service [root@k8s-master home]# ls k8s_service kubernetes-1.2.7 kubernetes-1.2.7.tar.gz sheng [root@k8s-master home]# cd k8s_service/ [root@k8s-master k8s_service]# mkdir redis-yaml [root@k8s-master k8s_service]# cd redis-yaml/ [root@k8s-master redis-yaml]# vi 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 "redis-master-service.yaml" [New] 12L, 179C written
查看創建出來的Service:
[root@k8s-master redis-yaml]# kubectl create -f redis-master-service.yaml service "redis-master" created [root@k8s-master redis-yaml]# kubectl get service NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes 10.254.0.1 <none> 443/TCP 4d redis-master 10.254.195.220 <none> 6379/TCP 8s
4.Docker私有倉庫搭建和使用
然後在10.0.0.10上創建Docker-registry 具體步驟詳見: http://blog.51cto.com/sf1314/2124934
安裝並啓動docker(此步已安裝忽略)
yum -y install docker systemctl start docker systemctl enable docker
搭建私有倉庫
下載registry鏡像
docker pull registry
防火牆添加運行5000端口
iptables -I INPUT 1 -p tcp --dport 5000 -j ACCEPT
下載完之後我們通過該鏡像啓動一個容器
docker run -d -p 5000:5000 --privileged=true -v /opt/registry:/tmp/registry registry
參數說明:
-v /opt/registry:/tmp/registry :默認情況下,會將倉庫存放於容器內的/tmp/registry目錄下,指定本地目錄掛載到容器
–privileged=true :CentOS7中的安全模塊selinux把權限禁掉了,參數給容器加特權,不加上傳鏡像會報權限錯誤(OSError: [Errno 13] Permission denied: ‘/tmp/registry/repositories/liibrary’)或者(Received unexpected HTTP status: 500 Internal Server Error)錯誤
檢查5000端口
netstat -an | grep 5000
客戶端上傳鏡像
修改/etc/sysconfig/docker(Ubuntu下配置文件地址爲:/etc/init/docker.conf),增加啓動選項(已有參數的在後面追加),之後重啓docker,不添加報錯,https證書問題。
OPTIONS='--insecure-registry kube-registry:5000'
#CentOS 7系統,注意如果是kube-registry:5000,則需要在/etc/hosts配置 10.0.0.10 kube-registry
# 後續拉取 10.0.0.10:5000需要docker pull 10.0.0.10:5000/centos1
# kube-registry:5000需要 docker pull docker-registry:5000/centos1
#other_args='--insecure-registry 10.0.0.10:5000' #CentOS 6系統
因爲Docker從1.3.X之後,與docker registry交互默認使用的是https,而此處搭建的私有倉庫只提供http服務
在docker公共倉庫下載一個鏡像
[root@etcd ~]# docker search pod-infrastructure INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED docker.io docker.io/openshift/origin-pod The pod infrastructure image for OpenShift 3 8 ...... [root@k8s-master ~]# docker search busybox INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED docker.io docker.io/busybox Busybox base image. 1272 [OK] ...... [root@etcd ~]# docker pull docker.io/openshift/origin-pod [root@etcd ~]# docker pull docker.io/busybox
來修改一下該鏡像的tag
[root@etcd ~]# docker tag docker.io/openshift/origin-pod 10.0.0.10:5000/pod-infrastructure [root@etcd ~]# docker tag docker.io/busybox 10.0.0.10:5000/busybox
把打了tag的鏡像上傳到私有倉庫
[root@etcd ~]# docker push 10.0.0.10:5000/pod-infrastructure [root@etcd ~]# docker push 10.0.0.10:5000/busybox
客戶端添加私有倉庫地址
# 添加這一行,修改/etc/sysconfig/docker文件,需要在/etc/hosts添加10.0.0.10 kube-registry解析並重新啓動docker服務。
ADD_REGISTRY='--add-registry kube-registry:5000' systemctl restart docker
加上後,search鏡像,私有倉庫和docker hub上都會顯示;
不加搜索私有倉庫,需要命令中指定私有倉庫ip
使用倉庫中的鏡像
[root@etcd ~]# curl http://10.0.0.10:5000/v2/_catalog {"repositories":["busybox","pod-infrastructure"]}
5. 通過DNS查找Service
使用一個帶有nslookup工具的Pod來驗證DNS服務是否能夠正常工作:
運行kubectl create -f busybox.yaml完成創建。
[root@k8s-master demo]# kubectl create -f busybox.yaml pod "busybox" created
STATUS狀態爲Running則表示創建成功,若爲ContainerCreating,則表示創建不成功
[root@k8s-master demo]# kubectl get pod NAME READY STATUS RESTARTS AGE busybox 1/1 Running 1 5m
例子:kubectl describe pod busybox可以查看部署失敗的原因
[root@k8s-master demo]# kubectl describe pod busybox Name: busybox Namespace: default Node: k8s-node-1/10.0.0.12 Start Time: Thu, 07 Jun 2018 16:07:44 +0800 Labels: <none> Status: Pending IP: Controllers: <none> Containers: busybox: Container ID: Image: kube-registry:5000/busybox Image ID: Port: Command: sleep 3600 State: Waiting Reason: ContainerCreating Ready: False Restart Count: 0 Volume Mounts: <none> Environment Variables: <none> Conditions: Type Status Initialized True Ready False PodScheduled True No volumes. QoS Class: BestEffort Tolerations: <none> Events: FirstSeen LastSeen Count From SubObjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 20s 20s 1 {default-scheduler } Normal Scheduled Successfully assigned busybox to k8s-node-1 20s 9s 2 {kubelet k8s-node-1} Warning FailedSync Error syncing pod, skipping: failed to "StartContainer" for "POD" with ErrImagePull: "image pull failed for registry.access.redhat.com/rhel7/pod-infrastructure:latest, this may be because there are no credentials on this request. details: (open /etc/docker/certs.d/registry.access.redhat.com/redhat-ca.crt: no such file or directory)"
在該容器成功啓動後,通過kubectl exec nslookup 進行測試:
可以看到,通過DNS服務器10.254.0.3成功找到了名爲”redis-master”服務的IP地址:10.254.7.160
如果某個Service屬於自定義的命名空間,那麼進行Service查找時,需要帶個namespace的名字。下面以查看kube-dns服務爲例:
如果僅使用”kube-dns”來進行查找,則將會失敗:
nslookup: can’t resolve ‘kube-dns’
查看多個容器組成的Pod時,要添加-c選項指定容器的名稱
5. DNS服務的工作原理解析
(1)kube2sky容器應用通過調用Kubernetes Master的API獲得集羣中所有Service的信息,並持續監控新Service的生成,然後定稿etcd中。
查看etcd存儲的Service信息
可以看到在skydns鍵下面,根據我們配置的域名(cluster.local)生成了local/cluster子鍵,接下來是namespace(default和kube-system)和svc(下面也按namespace生成子鍵)。
查看redis-master服務對應的鍵值:
可以看到,redis-master服務對應的完整域名爲redis-master.default.cluster.local,並且其IP地址爲10.254.7.160。
(2)根據Kubelet啓動參數的設置(–cluster_dns),Kubelet會在每個新創建的Pod中設置DNS域名解析配置文件/etc/resolv.conf文件,在其中增加了一條nameserver配置和一條search配置:
通過名字服務器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的名字就能對其進行訪問,使得應用程序中的代碼更簡潔了。