服务发现机制
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
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.253/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.1443/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的名字就能对其进行访问,使得应用程序中的代码更简洁了。