服务发现机制

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启动参数

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生成两条记录:

..
..svc.

(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            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:      
  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:              
    Environment Variables:      
  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:              
    Environment Variables:      
  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:              
    Environment Variables:      
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:    
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           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               443/TCP    4d
redis-master     10.254.195.220            6379/TCP    8s


4.Docker私有仓库搭建和使用

 然后在10.0.0.10上创建Docker-registry 具体步骤详见: https://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:        
Status:        Pending
IP:
Controllers:    
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:       
    Environment Variables: 
Conditions:
  Type       Status
  Initialized   True 
  Ready       False 
  PodScheduled  True 
No volumes.
QoS Class:      BestEffort
Tolerations:    
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的名字就能对其进行访问,使得应用程序中的代码更简洁了。