Service服务发现:CoreDNS组件

  在k8s系统上,Service为Pod中的服务类应用提供了一个稳定的访问入口,可以通过 Service 生成的 ClusterIP(VIP)来访问 Pod 提供的服务,但是Pod客户端中的应用如何得知某个特定的Service资源的IP和端口呢?如有两个应用,一个是 api 应用,一个是 db 应用,两个应用都是通过 Deployment 进行管理的,并且都通过 Service 暴露出了端口提供服务。api 需要连接到 db 这个应用,只知道 db 应用的名称和 db 对应的 Service 的名称,但是并不知道它的 VIP 地址,是可以通过 ClusterIP 就可以访问到后面的 Pod 服务,如果知道了 VIP 的地址是不是就行了?

1. apiServer

  可以从 apiserver 中直接查询获取到对应 service 的后端 Endpoints信息,所以最简单的办法是从 apiserver 中直接查询,如果偶尔一个特殊的应用,通过 apiserver 去查询到 Service 后面的 Endpoints 直接使用是没问题的,但是如果每个应用都在启动的时候去查询依赖的服务,这不但增加了应用的复杂度,这也导致了我们的应用需要依赖 Kubernetes 了,耦合度太高了,不具有通用性。

2. 环境变量

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

[root@k8s-master1 service]# vim test_nginx.yaml
You have new mail in /var/spool/mail/root
[root@k8s-master1 service]# cat test_nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
  labels:
    k8s-app: nginx-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    name: nginx-service
spec:
  ports:
  - port: 5000
    targetPort: 80
  selector:
    app: nginx
[root@k8s-master1 service]# kubectl apply -f test_nginx.yaml
deployment.apps/nginx-deploy created
service/nginx-service created
[root@k8s-master1 service]# kubectl get deployments
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deploy   2/2     2            2           11s
[root@k8s-master1 service]# kubectl get rs
NAME                      DESIRED   CURRENT   READY   AGE
nginx-deploy-75b69bd684   2         2         2       15s
[root@k8s-master1 service]# kubectl get pods -o wide
NAME                            READY   STATUS    RESTARTS   AGE   IP              NODE        NOMINATED NODE   READINESS GATES
nginx-deploy-75b69bd684-57ck4   1/1     Running   0          21s   10.244.36.110   k8s-node1   <none>           <none>
nginx-deploy-75b69bd684-dtntv   1/1     Running   0          21s   10.244.36.111   k8s-node1   <none>           <none>
[root@k8s-master1 service]# kubectl get svc -o wide
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE   SELECTOR
kubernetes      ClusterIP   10.96.0.1       <none>        443/TCP    43d   <none>
nginx-service   ClusterIP   10.103.68.123   <none>        5000/TCP   37s   app=nginx

  可以看到两个 Pod 和一个名为 nginx-service 的服务创建成功了,该 Service 监听的端口是 5000,同时它会把流量转发给它代理的所有 Pod(这里就是拥有 app: nginx 标签的两个 Pod)

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

[root@k8s-master1 service]# vim test_pod.yaml
You have new mail in /var/spool/mail/root
[root@k8s-master1 service]# cat test_pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - name: test-service-pod
    image: busybox:1.28
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh", "-c", "env"]
[root@k8s-master1 service]# kubectl apply -f test_pod.yaml
pod/test-pod created

  Pod 创建完成后,查看pod日志信息

[root@k8s-master1 service]# kubectl logs test-pod
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
HOSTNAME=test-pod
SHLVL=1
HOME=/root
NGINX_SERVICE_PORT_5000_TCP_ADDR=10.103.68.123
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.103.68.123
KUBERNETES_PORT_443_TCP_PORT=443
NGINX_SERVICE_PORT_5000_TCP=tcp://10.103.68.123:5000
KUBERNETES_PORT_443_TCP_PROTO=tcp
NGINX_SERVICE_SERVICE_PORT=5000
NGINX_SERVICE_PORT=tcp://10.103.68.123: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 等,现在如果需要在这个 Pod 里面访问 nginx-service 的服务,是可以直接通过 NGINX_SERVICE_SERVICE_HOST和 NGINX_SERVICE_SERVICE_PORT访问的,但是如果这个 Pod 启动起来的时候nginx-service 服务还没启动起来,在环境变量中是无法获取到这些信息的,当然可以通过 initContainer 之类的方法来确保 nginx-service 启动后再启动 Pod,但是这种方法毕竟增加了 Pod 启动的复杂性,所以这不是最优的方法。

  因此,基于环境变量的服务发现其功能比较简单、易用,但是存在一定的局限。例如,仅有那些与创建的pod对象在同一名称空间中且事先存在的Service对象的信息才会以环境变量的形式注入,那些处于非同一名称空间,或者是在pod资源创建之后才创建的Service对象相关的环境变量则不会添加。

3. ClusterDNS

  由于上面环境变量的发现机制的局限性,需要一种更加智能的方案,可以直接使用 Service 的名称,因为 Service 的名称不会变化,不需要去关心分配的 ClusterIP 的地址,因为这个ClusterIP地址并不是固定不变的,所以如果直接使用 Service 的名字,然后对应的 ClusterIP 地址的转换能够自动完成就很好了。名字和 IP 直接的转换通过 DNS 就可以解决了,同样的,Kubernetes 也提供了 DNS 的方案来解决上面的服务发现的问题。

  k8s系统上用于名称解析和服务发现的ClusterDNS是集群的核心附件之一,集群中创建的每个Service对象,都会由其自动生成相关的资源记录。默认情况下,集群中各pod资源会自动配置其作为名称解析服务器,并在其DNS搜索列表中包含它所属名称空间的域名后缀。

  kubernetes自1.3版本开始,其用于服务发现的DNS更新为kubeDNS,而类似的另一个基于较新的DNS的服务发现项目是由CNCF孵化的CoreDNS,它基于go语言开发,通过串接一组实现DNS功能的插件的插件链进行工作。自kubernetes 1.11版本起,CoreDNS取代kubeDNS成为默认的DNS插件。它通常是集群安装完成后立即部署的附加组件。使用kubeadm初始化一个集群时,它会自动进行部署。

  CoreDNS官网地址:https://coredns.io/

  1)域名格式:

  建立的 Service 如果支持域名形式进行解析,就可以解决服务发现的功能,那么利用 CoreDNS 可以将 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。

  2)测试CoreDNS

  使用一个 Pod 来测试下Service 的域名访问

[root@k8s-master1 service]# vim dig-pod.yaml
You have new mail in /var/spool/mail/root
[root@k8s-master1 service]# cat dig-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: dig
  namespace: default
spec:
  containers:
  - name: dig
    image:  test/dig:latest
    imagePullPolicy: IfNotPresent
    command:
      - sleep
      - "3600"
  restartPolicy: Always
[root@k8s-master1 service]# kubectl apply -f dig-pod.yaml
pod/dig created

  在创建的pod:dig客户端中尝试请求解析nginx-service和kubernetes的相关DNS记录

[root@k8s-master1 service]# kubectl get svc
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes      ClusterIP   10.96.0.1       <none>        443/TCP    44d
nginx-service   ClusterIP   10.103.68.123   <none>        5000/TCP   22h

[root@k8s-master1 service]# kubectl exec -it dig -- nslookup nginx-service
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   nginx-service.default.svc.cluster.local
Address: 10.103.68.123

[root@k8s-master1 service]# kubectl exec -it dig -- nslookup kubernetes
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   kubernetes.default.svc.cluster.local
Address: 10.96.0.1

  解析时,“nginx-service”服务名称的搜索次序依次是default.svc.cluster.local,svc.cluster.local,cluster.local和${WORK_NODE_DOMAIN},因此基于DNS的服务发现不受Service资源所在的名称空间和创建时间的限制。上面解析结果也正是默认的default名称空间中创建的nginx-service服务的IP地址。

  在k8s中创建service之后,service默认的FQDN是<service name>.<namespace>.svc.cluster.local,那么k8s集群内部的服务就可以通过FQDN访问。

posted @ 2022-09-14 21:44  出水芙蓉·薇薇  阅读(279)  评论(0编辑  收藏  举报