Kubernetes實戰[2]: 服務發現機制與Cluster DNS的安裝(無CA認證版)

     服務發現機制

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中直接使用域名進行引用,避免了之前的變量氾濫問題,也避免了創建順序的尷尬局面。

QQ截圖20180604103352.png

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啓動參數

vim /etc/kubernetes/kubelet

在KUBELET_ARGS裏增加:

--cluster_dns=10.254.10.2 --cluster_domain=cluster.local

即:

# Add your own!

KUBELET_ARGS="--cluster_dns=10.254.10.2 --cluster_domain=cluster.local"


cluster_dns爲DNS服務的ClusterIP地址 
cluster_domain爲DNS服務中設置的域名

重啓kubelet服務

systemctl restart 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生成兩條記錄:

<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@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服務是否能夠正常工作:

[root@k8s-master ~]# cd /home
[root@k8s-master home]# mkdir -p k8s_service/demo
[root@k8s-master home]# cd k8s_service/demo
[root@k8s-master 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完成創建。 

[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 進行測試:

[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.16012345

可以看到,通過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-systemServer:    10.254.0.3Address 1: 10.254.0.3Name:      kube-dns.kube-system
Address 1: 10.254.0.312345

如果僅使用”kube-dns”來進行查找,則將會失敗: 
nslookup: can’t resolve ‘kube-dns’

查看多個容器組成的Pod時,要添加-c選項指定容器的名稱

# kubectl logs kube-dns-v9-curdr --namespace=kube-systemError from server: a container name must be specified for pod kube-dns-v9-curdr
# kubectl logs kube-dns-v9-curdr -c skydns --namespace=kube-system123

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-system1234

可以看到在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}12

可以看到,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.3nameserver 202.96.128.86search default.svc.cluster.local svc.cluster.local cluster.localoptions ndots:51234

通過名字服務器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的名字就能對其進行訪問,使得應用程序中的代碼更簡潔了。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章