kube-dns和coreDNS的使用

内部服务发现

前面咱们给你们讲解了 Service 的用法,咱们能够经过 Service 生成的 ClusterIP(VIP)来访问 Pod 提供的服务,可是在使用的时候还有一个问题:咱们怎么知道某个应用的 VIP 呢?好比咱们有两个应用,一个是 api 应用,一个是 db 应用,两个应用都是经过 Deployment 进行管理的,而且都经过 Service 暴露出了端口提供服务。api 须要链接到 db 这个应用,咱们只知道 db 应用的名称和 db 对应的 Service 的名称,可是并不知道它的 VIP 地址,咱们前面的 Service 课程中是否是学习到咱们经过 ClusterIP 就能够访问到后面的 Pod 服务,若是咱们知道了 VIP 的地址是否是就好了?html

apiserver

咱们知道能够从 apiserver 中直接查询获取到对应 service 的后端 Endpoints信息,因此最简单的办法是从 apiserver 中直接查询,若是偶尔一个特殊的应用,咱们经过 apiserver 去查询到 Service 后面的 Endpoints 直接使用是没问题的,可是若是每一个应用都在启动的时候去查询依赖的服务,这不但增长了应用的复杂度,这也致使了咱们的应用须要依赖 Kubernetes 了,耦合度过高了,不具备通用性。nginx

环境变量

为了解决上面的问题,在以前的版本中,Kubernetes 采用了环境变量的方法,每一个 Pod 启动的时候,会经过环境变量设置全部服务的 IP 和 port 信息,这样 Pod 中的应用能够经过读取环境变量来获取依赖服务的地址信息,这种方法使用起来相对简单,可是有一个很大的问题就是依赖的服务必须在 Pod 启动以前就存在,否则是不会被注入到环境变量中的。好比咱们首先建立一个 Nginx 服务:(test-nginx.yaml)web

apiVersion: apps/v1beta1 kind: Deployment metadata: name: nginx-deploy labels: k8s-app: nginx-demo spec: replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-service labels: name: nginx-service spec: ports: - port: 5000 targetPort: 80 selector: app: nginx 

建立上面的服务:shell

$ kubectl create -f test-nginx.yaml
deployment.apps "nginx-deploy" created service "nginx-service" created $ kubectl get pods NAME READY STATUS RESTARTS AGE ... nginx-deploy-75675f5897-47h4t 1/1 Running 0 53s nginx-deploy-75675f5897-mmm8w 1/1 Running 0 53s ... $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ... nginx-service ClusterIP 10.107.225.42 <none> 5000/TCP 1m ... 

咱们能够看到两个 Pod 和一个名为 nginx-service 的服务建立成功了,该 Service 监听的端口是 5000,同时它会把流量转发给它代理的全部 Pod(咱们这里就是拥有 app: nginx 标签的两个 Pod)。后端

如今咱们再来建立一个普通的 Pod,观察下该 Pod 中的环境变量是否包含上面的 nginx-service 的服务信息:(test-pod.yaml)api

apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-service-pod image: busybox command: ["/bin/sh", "-c", "env"] 

而后建立该测试的 Pod:缓存

$ kubectl create -f test-pod.yaml pod "test-pod" created 

等 Pod 建立完成后,咱们查看日志信息:服务器

$ kubectl logs test-pod
... KUBERNETES_PORT=tcp://10.96.0.1:443 KUBERNETES_SERVICE_PORT=443 HOSTNAME=test-pod HOME=/root NGINX_SERVICE_PORT_5000_TCP_ADDR=10.107.225.42 NGINX_SERVICE_PORT_5000_TCP_PORT=5000 NGINX_SERVICE_PORT_5000_TCP_PROTO=tcp KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin NGINX_SERVICE_SERVICE_HOST=10.107.225.42 NGINX_SERVICE_PORT_5000_TCP=tcp://10.107.225.42:5000 KUBERNETES_PORT_443_TCP_PORT=443 KUBERNETES_PORT_443_TCP_PROTO=tcp NGINX_SERVICE_SERVICE_PORT=5000 NGINX_SERVICE_PORT=tcp://10.107.225.42:5000 KUBERNETES_SERVICE_PORT_HTTPS=443 KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443 KUBERNETES_SERVICE_HOST=10.96.0.1 PWD=/ ... 

咱们能够看到打印了不少环境变量处理,其中就包括咱们刚刚建立的 nginx-service 这个服务,有 HOST、PORT、PROTO、ADDR 等,也包括其余已经存在的 Service 的环境变量,如今若是咱们须要在这个 Pod 里面访问 nginx-service 的服务,咱们是否是能够直接经过 NGINX_SERVICE_SERVICE_HOST 和 NGINX_SERVICE_SERVICE_PORT 就能够了,可是咱们也知道若是这个 Pod 启动起来的时候若是 nginx-service 服务还没启动起来,在环境变量中咱们是没法获取到这些信息的,固然咱们能够经过 initContainer 之类的方法来确保 nginx-service 启动后再启动 Pod,可是这种方法毕竟增长了 Pod 启动的复杂性,因此这不是最优的方法。app

KubeDNS

因为上面环境变量这种方式的局限性,咱们须要一种更加智能的方案,其实咱们能够本身想学一种比较理想的方案:那就是能够直接使用 Service 的名称,由于 Service 的名称不会变化,咱们不须要去关心分配的 ClusterIP 的地址,由于这个地址并非固定不变的,因此若是咱们直接使用 Service 的名字,而后对应的 ClusterIP 地址的转换可以自动完成就很好了。咱们知道名字和 IP 直接的转换是否是和咱们平时访问的网站很是相似啊?他们之间的转换功能经过 DNS 就能够解决了,一样的,Kubernetes 也提供了 DNS 的方案来解决上面的服务发现的问题。less

kubedns 介绍

DNS 服务不是一个独立的系统服务,而是做为一种 addon 插件而存在,也就是说不是 Kubernetes 集群必须安装的,固然咱们强烈推荐安装,能够将这个插件当作是一种运行在 Kubernetes 集群上的一直比较特殊的应用,如今比较推荐的两个插件:kube-dns 和 CoreDNS。咱们在前面使用 kubeadm 搭建集群的时候直接安装的 kube-dns 插件,若是不记得了能够回头去看一看。固然若是咱们想使用 CoreDNS 的话也很方便,只须要执行下面的命令便可:

$ kubeadm init --feature-gates=CoreDNS=true 

Kubernetes DNS pod 中包括 3 个容器,能够经过 kubectl 工具查看:

$ kubectl get pods -n kube-system
NAME                                    READY     STATUS    RESTARTS   AGE
... kube-dns-5868f69869-zp5kz 3/3 Running 0 19d ... 

READY 一栏能够看到是 3/3,用以下命令能够很清楚的看到 kube-dns 包含的3个容器:

$ kubectl describe pod kube-dns-5868f69869-zp5kz -n kube-system

kube-dns、dnsmasq-nanny、sidecar 这3个容器分别实现了什么功能?

  • kubedns: kubedns 基于 SkyDNS 库,经过 apiserver 监听 Service 和 Endpoints 的变动事件同时也同步到本地 Cache,实现了一个实时的 Kubernetes 集群内 Service 和 Pod 的 DNS服务发现
  • dnsmasq: dsnmasq 容器则实现了 DNS 的缓存功能(在内存中预留一块默认大小为 1G 的地方,保存当前最经常使用的 DNS 查询记录,若是缓存中没有要查找的记录,它会到 kubedns 中查询,并把结果缓存起来),经过监听 ConfigMap 来动态生成配置
  • sider: sidecar 容器实现了可配置的 DNS 探测,并采集对应的监控指标暴露出来供 prometheus 使用

kube dnskube dns

对 Pod 的影响

DNS Pod 具备静态 IP 并做为 Kubernetes 服务暴露出来。该静态 IP 被分配后,kubelet 会将使用 --cluster-dns = <dns-service-ip>参数配置的 DNS 传递给每一个容器。DNS 名称也须要域名,本地域可使用参数--cluster-domain = <default-local-domain>在 kubelet 中配置。

咱们说 dnsmasq 容器经过监听 ConfigMap 来动态生成配置,能够自定义存根域和上下游域名服务器。

例如,下面的 ConfigMap 创建了一个 DNS 配置,它具备一个单独的存根域和两个上游域名服务器:

apiVersion: v1 kind: ConfigMap metadata: name: kube-dns namespace: kube-system data: stubDomains: | {"acme.local": ["1.2.3.4"]} upstreamNameservers: | ["8.8.8.8", "8.8.4.4"] 

按如上说明,具备.acme.local后缀的 DNS 请求被转发到 DNS 1.2.3.4。Google 公共 DNS 服务器 为上游查询提供服务。下表描述了具备特定域名的查询如何映射到它们的目标 DNS 服务器:

域名 响应查询的服务器
kubernetes.default.svc.cluster.local kube-dns
foo.acme.local 自定义 DNS (1.2.3.4)
widget.com 上游 DNS (8.8.8.8, 8.8.4.4,其中之一)

另外咱们还能够为每一个 Pod 设置 DNS 策略。 当前 Kubernetes 支持两种 Pod 特定的 DNS 策略:“Default” 和 “ClusterFirst”。 能够经过 dnsPolicy 标志来指定这些策略。

注意:Default 不是默认的 DNS 策略。若是没有显式地指定dnsPolicy,将会使用 ClusterFirst

  • 若是 dnsPolicy 被设置为 “Default”,则名字解析配置会继承自 Pod 运行所在的节点。自定义上游域名服务器和存根域不可以与这个策略一块儿使用
  • 若是 dnsPolicy 被设置为 “ClusterFirst”,这就要依赖因而否配置了存根域和上游 DNS 服务器
    • 未进行自定义配置:没有匹配上配置的集群域名后缀的任何请求,例如 “www.kubernetes.io”,将会被转发到继承自节点的上游域名服务器。
    • 进行自定义配置:若是配置了存根域和上游 DNS 服务器(相似于 前面示例 配置的内容),DNS 查询将基于下面的流程对请求进行路由:
      • 查询首先被发送到 kube-dns 中的 DNS 缓存层。
      • 从缓存层,检查请求的后缀,并根据下面的状况转发到对应的 DNS 上:
        • 具备集群后缀的名字(例如 “.cluster.local”):请求被发送到 kubedns。
        • 具备存根域后缀的名字(例如 “.acme.local”):请求被发送到配置的自定义 DNS 解析器(例如:监听在 1.2.3.4)。
        • 未能匹配上后缀的名字(例如 “widget.com”):请求被转发到上游 DNS(例如:Google 公共 DNS 服务器,8.8.8.8 和 8.8.4.4)。 kube dns

域名格式

咱们前面说了若是咱们创建的 Service 若是支持域名形式进行解析,就能够解决咱们的服务发现的功能,那么利用 kubedns 能够将 Service 生成怎样的 DNS 记录呢?

  • 普通的 Service:会生成 servicename.namespace.svc.cluster.local 的域名,会解析到 Service 对应的 ClusterIP 上,在 Pod 之间的调用能够简写成 servicename.namespace,若是处于同一个命名空间下面,甚至能够只写成 servicename 便可访问
  • Headless Service:无头服务,就是把 clusterIP 设置为 None 的,会被解析为指定 Pod 的 IP 列表,一样还能够经过 podname.servicename.namespace.svc.cluster.local 访问到具体的某一个 Pod。

CoreDNS 实现的功能和 KubeDNS 是一致的,不过 CoreDNS 的全部功能都集成在了同一个容器中,在最新版的1.11.0版本中官方已经推荐使用 CoreDNS了,你们也能够安装 CoreDNS 来代替 KubeDNS,其余使用方法都是一致的:https://coredns.io/

测试

如今咱们来使用一个简单 Pod 来测试下 Service 的域名访问:

$ kubectl run --rm -i --tty test-dns --image=busybox /bin/sh If you don't see a command prompt, try pressing enter. / # cat /etc/resolv.conf nameserver 10.96.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5 / # 

咱们进入到 Pod 中,查看/etc/resolv.conf中的内容,能够看到 nameserver 的地址10.96.0.10,该 IP 地址便是在安装 kubedns 插件的时候集群分配的一个固定的静态 IP 地址,咱们能够经过下面的命令进行查看:

$ kubectl get svc kube-dns -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S) AGE kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 62d 

也就是说咱们这个 Pod 如今默认的 nameserver 就是 kubedns 的地址,如今咱们来访问下前面咱们建立的 nginx-service 服务:

/ # wget -q -O- nginx-service.default.svc.cluster.local 

能够看到上面咱们使用 wget 命令去访问 nginx-service 服务的域名的时候被 hang 住了,没有获得指望的结果,这是由于上面咱们创建 Service 的时候暴露的端口是 5000:

/ # wget -q -O- nginx-service.default.svc.cluster.local:5000 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html> 

加上 5000 端口,就正常访问到服务,再试一试访问:nginx-service.default.svc、nginx-service.default、nginx-service,不出意外这些域名均可以正常访问到指望的结果。

到这里咱们是否是就实现了在集群内部经过 Service 的域名形式进行互相通讯了,你们下去试着看看访问不一样 namespace 下面的服务呢?下节课咱们来给你们讲解使用 ingress 来实现集群外部的服务发现功能。

相关文章
相关标签/搜索