Kubernetes——服务发现方式

服务发现方式

  微服务意味着存在更多的独立服务,但它们并非独立的个体,而是存在着复杂的依赖关系且彼此之间通常需要进行非常频繁地交互和通信的群体。然而,建立通信之前,服务和服务之间该如何获知彼此的地址呢?

  在 Kubernetes 系统上,Service 为 Pod 中的服务类应用提供了一个稳定的入口,但 Pod 客户端中的应用如何得知某个特定 Service 资源的 IP 和端口呢? 这个时候就需要引入服务发现(Service Discovery)的机制。

一、服务发现概述

  服务发现简单的来说,就是服务或者应用之间的互相定位的过程。

  不过服务发现并发新概念,传统的单体应用架构时代也会用到,只不过单体应用的动态性不强,更新和重新发布的频度较低,通常以月甚至以年计,基本上不会进行自动伸缩,因此服务发现的概念无须显性强调。在传统的单体应用网络位置发生变化时,由IT运维手动更新一下相关的配置文件基本上就能解决问题。

  但在微服务应用场景中,应用被拆分成众多的小服务,它们按需创建且变动频繁,配置信息基本无法事先写入配置文件中并及时跟踪和反映动态变化,因此服务发现的重要性便随之突显。

  根据服务发现过程的实现方式,服务发现可以分为两种类型:

    • 客户端发现:由客户端到服务注册中心发现其依赖到的服务的相关信息,因此,它需要内置特定的服务发现程序和发现逻辑。
    • 服务端发现:这种方式需要额外用到一个称为中央路由器或服务均衡器的组件;服务消费者将请求发往中央路由器或者负载均衡器,由它们负载查询服务注册中心获取服务提供者的位置信息,并将服务消费者的请求转发给服务提供者。

  可见,服务注册中心是服务发现的一落地的核心组件。

  事实上,DNS 可以算是最为原始的服务发现系统之一,不过在服务的动态性很强的业务场景中,DNS记录的传播速度可能会跟不上服务的变更速度,因此它并不适用于微服务环境。微服务中常见的服务注册中心,比如 zookeeper、eureka、nacos 和 etcd 等分布式键值存储系统。

  尽管传统 DNS 系统不适于微服务环境中的服务发现,但 SkyDNS 项目(后来成为 kubedns)却是一个有趣的实现,它结合了古老的 DNS 技术和时髦的 Go 语言、Raft 算法,并构建于 etcd 存储系统之上,为 Kubernetes 系统实现了一种服务发现机制。Servie 资源为 Kubernetes 提供了一个较为稳定的抽象层,这点类似于服务端发现的方式,于是也就不存在 DNS 服务的时间窗口的问题了。

  Kubernetes 自 1.3 版本开始,其用于服务发现的 DNS 更新为了 kubeDNS,而类似的另一个基于较新的 DNS 服务发现项目是 CNCF(Cloud Native Computing Foundaton)孵化的 CoreDNS,它基于 Go 语言开发,通过串接一组实现 DNS 功能的插件的插件链进行工作。自 Kubernetes 1.11 版本起,CoreDNS 取代 kubeDNS 成为默认的 DNS 福建。不过 Kubernetes 依然支持使用环境变量进行服务发现。

二、服务发现方式一:环境变量

  创建 Pod 资源时,kubelet 会将其所属名称空间的每个活动的 Service 对象以一系列环境变量的形式注入其中。它支持使用 Kubernetes Service 环境变量以及与 Docker 的 links 兼容的变量。

(1)Kubernetes Servcie 环境变量

  Kubernetes 为每个 Service 资源生成包括以下形式的环境变量在内的一系列环境变量,在同一名称空间中创建的 Pod 对象都会自动拥有这些变量。

    • {SVCNAME}_SERVICE_HOST
    • {SVCNAME}_SERVICE_PORT

(2)Docker Link 形式的环境变量

  Docker 使用 --link 选项实现容器连接时所设置的环境变量形式,具体使用方式请参考 Docker 官网文档。

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

 三、ClusterDNS 和服务发现

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

 (1)创建一个coredns 服务:

[root@mh-k8s-master-prd-243-24 ~]# kubectl get deployment/coredns -n kube-system -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: "2022-04-11T09:49:54Z"
  generation: 1
  labels:
    k8s-app: kube-dns
  managedFields:
  - apiVersion: apps/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:labels:
          .: {}
          f:k8s-app: {}
      f:spec:
        f:progressDeadlineSeconds: {}
        f:replicas: {}
        f:revisionHistoryLimit: {}
        f:selector:
          f:matchLabels:
            .: {}
            f:k8s-app: {}
        f:strategy:
          f:rollingUpdate:
            .: {}
            f:maxSurge: {}
            f:maxUnavailable: {}
          f:type: {}
        f:template:
          f:metadata:
            f:labels:
              .: {}
              f:k8s-app: {}
          f:spec:
            f:containers:
              k:{"name":"coredns"}:
                .: {}
                f:args: {}
                f:image: {}
                f:imagePullPolicy: {}
                f:livenessProbe:
                  .: {}
                  f:failureThreshold: {}
                  f:httpGet:
                    .: {}
                    f:path: {}
                    f:port: {}
                    f:scheme: {}
                  f:initialDelaySeconds: {}
                  f:periodSeconds: {}
                  f:successThreshold: {}
                  f:timeoutSeconds: {}
                f:name: {}
                f:ports:
                  .: {}
                  k:{"containerPort":53,"protocol":"TCP"}:
                    .: {}
                    f:containerPort: {}
                    f:name: {}
                    f:protocol: {}
                  k:{"containerPort":53,"protocol":"UDP"}:
                    .: {}
                    f:containerPort: {}
                    f:name: {}
                    f:protocol: {}
                  k:{"containerPort":9153,"protocol":"TCP"}:
                    .: {}
                    f:containerPort: {}
                    f:name: {}
                    f:protocol: {}
                f:readinessProbe:
                  .: {}
                  f:failureThreshold: {}
                  f:httpGet:
                    .: {}
                    f:path: {}
                    f:port: {}
                    f:scheme: {}
                  f:periodSeconds: {}
                  f:successThreshold: {}
                  f:timeoutSeconds: {}
                f:resources:
                  .: {}
                  f:limits:
                    .: {}
                    f:memory: {}
                  f:requests:
                    .: {}
                    f:cpu: {}
                    f:memory: {}
                f:securityContext:
                  .: {}
                  f:allowPrivilegeEscalation: {}
                  f:capabilities:
                    .: {}
                    f:add: {}
                    f:drop: {}
                  f:readOnlyRootFilesystem: {}
                f:terminationMessagePath: {}
                f:terminationMessagePolicy: {}
                f:volumeMounts:
                  .: {}
                  k:{"mountPath":"/etc/coredns"}:
                    .: {}
                    f:mountPath: {}
                    f:name: {}
                    f:readOnly: {}
            f:dnsPolicy: {}
            f:nodeSelector:
              .: {}
              f:kubernetes.io/os: {}
            f:priorityClassName: {}
            f:restartPolicy: {}
            f:schedulerName: {}
            f:securityContext: {}
            f:serviceAccount: {}
            f:serviceAccountName: {}
            f:terminationGracePeriodSeconds: {}
            f:tolerations: {}
            f:volumes:
              .: {}
              k:{"name":"config-volume"}:
                .: {}
                f:configMap:
                  .: {}
                  f:defaultMode: {}
                  f:items: {}
                  f:name: {}
                f:name: {}
    manager: kubeadm
    operation: Update
    time: "2022-04-11T09:49:54Z"
  - apiVersion: apps/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          .: {}
          f:deployment.kubernetes.io/revision: {}
      f:status:
        f:availableReplicas: {}
        f:conditions:
          .: {}
          k:{"type":"Available"}:
            .: {}
            f:lastTransitionTime: {}
            f:lastUpdateTime: {}
            f:message: {}
            f:reason: {}
            f:status: {}
            f:type: {}
          k:{"type":"Progressing"}:
            .: {}
            f:lastTransitionTime: {}
            f:lastUpdateTime: {}
            f:message: {}
            f:reason: {}
            f:status: {}
            f:type: {}
        f:observedGeneration: {}
        f:readyReplicas: {}
        f:replicas: {}
        f:updatedReplicas: {}
    manager: kube-controller-manager
    operation: Update
    time: "2022-05-23T22:13:18Z"
  name: coredns
  namespace: kube-system
  resourceVersion: "44798610"
  selfLink: /apis/apps/v1/namespaces/kube-system/deployments/coredns
  uid: c65ae3e2-c843-43fd-8ac2-9b1c14d5c9a8
spec:
  progressDeadlineSeconds: 600
  replicas: 2
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      k8s-app: kube-dns
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        k8s-app: kube-dns
    spec:
      containers:
      - args:
        - -conf
        - /etc/coredns/Corefile
        image: registry.cn-beijing.aliyuncs.com/kubesphereio/coredns:1.6.9
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 5
          httpGet:
            path: /health
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 60
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 5
        name: coredns
        ports:
        - containerPort: 53
          name: dns
          protocol: UDP
        - containerPort: 53
          name: dns-tcp
          protocol: TCP
        - containerPort: 9153
          name: metrics
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /ready
            port: 8181
            scheme: HTTP
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        resources:
          limits:
            memory: 170Mi
          requests:
            cpu: 100m
            memory: 70Mi
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            add:
            - NET_BIND_SERVICE
            drop:
            - all
          readOnlyRootFilesystem: true
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /etc/coredns
          name: config-volume
          readOnly: true
      dnsPolicy: Default
      nodeSelector:
        kubernetes.io/os: linux
      priorityClassName: system-cluster-critical
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      serviceAccount: coredns
      serviceAccountName: coredns
      terminationGracePeriodSeconds: 30
      tolerations:
      - key: CriticalAddonsOnly
        operator: Exists
      - effect: NoSchedule
        key: node-role.kubernetes.io/master
      volumes:
      - configMap:
          defaultMode: 420
          items:
          - key: Corefile
            path: Corefile
          name: coredns
        name: config-volume
status:
  availableReplicas: 2
  conditions:
  - lastTransitionTime: "2022-04-11T09:50:43Z"
    lastUpdateTime: "2022-04-11T09:50:43Z"
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: "2022-04-11T09:50:10Z"
    lastUpdateTime: "2022-04-11T09:50:48Z"
    message: ReplicaSet "coredns-674d655c65" has successfully progressed.
    reason: NewReplicaSetAvailable
    status: "True"
    type: Progressing
  observedGeneration: 1
  readyReplicas: 2
  replicas: 2
  updatedReplicas: 2

cm配置:

[root@mh-k8s-master-prd-243-24 ~]# kubectl describe cm/coredns -n kube-system
Name:         coredns
Namespace:    kube-system
Labels:       <none>
Annotations:  <none>

Data
====
Corefile:
----
.:53 {
    errors
    health {
       lameduck 5s
    }
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
       pods insecure
       fallthrough in-addr.arpa ip6.arpa
       ttl 30
    }
    prometheus :9153
    forward . /etc/resolv.conf
    cache 30
    loop
    reload
    loadbalance
}

Events:  <none>
[root@mh-k8s-master-prd-243-24 ~]# 

ServiceAccount 配置:

[root@mh-k8s-master-prd-243-24 ~]# kubectl describe ServiceAccount/coredns -n kube-system
Name:                coredns
Namespace:           kube-system
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   coredns-token-s8sqx
Tokens:              coredns-token-s8sqx
Events:              <none>
[root@mh-k8s-master-prd-243-24 ~]# kubectl get ServiceAccount/coredns -n kube-system
NAME      SECRETS   AGE
coredns   1         70d
[root@mh-k8s-master-prd-243-24 ~]# 

rbac 配置:

[root@mh-k8s-master-prd-243-24 ~]# kubectl describe ClusterRole/system:coredns -n kube-system
Name:         system:coredns
Labels:       <none>
Annotations:  <none>
PolicyRule:
  Resources   Non-Resource URLs  Resource Names  Verbs
  ---------   -----------------  --------------  -----
  nodes       []                 []              [get]
  endpoints   []                 []              [list watch]
  namespaces  []                 []              [list watch]
  pods        []                 []              [list watch]
  services    []                 []              [list watch]
[root@mh-k8s-master-prd-243-24 ~]# kubectl describe ClusterRoleBinding/system:coredns -n kube-system
Name:         system:coredns
Labels:       <none>
Annotations:  <none>
Role:
  Kind:  ClusterRole
  Name:  system:coredns
Subjects:
  Kind            Name     Namespace
  ----            ----     ---------
  ServiceAccount  coredns  kube-system
[root@mh-k8s-master-prd-243-24 ~]# 

svc 配置:

[root@mh-k8s-master-prd-243-24 ~]# kubectl get svc/coredns -n kube-system -o yaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{"prometheus.io/port":"9153","prometheus.io/scrape":"true"},"labels":{"addonmanager.kubernetes.io/mode":"Reconcile","k8s-app":"kube-dns","kubernetes.io/cluster-service":"true","kubernetes.io/name":"coredns"},"name":"coredns","namespace":"kube-system"},"spec":{"clusterIP":"10.233.0.3","ports":[{"name":"dns","port":53,"protocol":"UDP"},{"name":"dns-tcp","port":53,"protocol":"TCP"},{"name":"metrics","port":9153,"protocol":"TCP"}],"selector":{"k8s-app":"kube-dns"}}}
    prometheus.io/port: "9153"
    prometheus.io/scrape: "true"
  creationTimestamp: "2022-04-11T09:50:05Z"
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: coredns
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          .: {}
          f:kubectl.kubernetes.io/last-applied-configuration: {}
          f:prometheus.io/port: {}
          f:prometheus.io/scrape: {}
        f:labels:
          .: {}
          f:addonmanager.kubernetes.io/mode: {}
          f:k8s-app: {}
          f:kubernetes.io/cluster-service: {}
          f:kubernetes.io/name: {}
      f:spec:
        f:clusterIP: {}
        f:ports:
          .: {}
          k:{"port":53,"protocol":"TCP"}:
            .: {}
            f:name: {}
            f:port: {}
            f:protocol: {}
            f:targetPort: {}
          k:{"port":53,"protocol":"UDP"}:
            .: {}
            f:name: {}
            f:port: {}
            f:protocol: {}
            f:targetPort: {}
          k:{"port":9153,"protocol":"TCP"}:
            .: {}
            f:name: {}
            f:port: {}
            f:protocol: {}
            f:targetPort: {}
        f:selector:
          .: {}
          f:k8s-app: {}
        f:sessionAffinity: {}
        f:type: {}
    manager: kubectl
    operation: Update
    time: "2022-04-11T09:50:05Z"
  name: coredns
  namespace: kube-system
  resourceVersion: "323"
  selfLink: /api/v1/namespaces/kube-system/services/coredns
  uid: 739362ef-f448-4ccc-a569-d4c6280d9ebe
spec:
  clusterIP: 10.233.0.3
  ports:
  - name: dns
    port: 53
    protocol: UDP
    targetPort: 53
  - name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: 53
  - name: metrics
    port: 9153
    protocol: TCP
    targetPort: 9153
  selector:
    k8s-app: kube-dns
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}
[root@mh-k8s-master-prd-243-24 ~]# 

(2)apply后结果运行

[root@mh-k8s-master-prd-243-24 ~]# kubectl get deployment/coredns -n kube-system
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
coredns   2/2     2            2           70d
[root@mh-k8s-master-prd-243-24 ~]# kubectl get pods -n kube-system |grep coredns
coredns-674d655c65-5scd4                           1/1     Running   1          70d
coredns-674d655c65-wdgll                           1/1     Running   1          70d
[root@mh-k8s-master-prd-243-24 ~]# 

(3)测试 CoreDNS:

[root@mh-k8s-master-prd-243-24 ~]# kubectl get svc -o wide -n kube-system
NAME                          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                        AGE   SELECTOR
coredns                       ClusterIP   10.233.0.3      <none>        53/UDP,53/TCP,9153/TCP         70d   k8s-app=kube-dns
etcd                          ClusterIP   None            <none>        2379/TCP                       69d   <none>
kube-controller-manager-svc   ClusterIP   None            <none>        10257/TCP                      69d   component=kube-controller-manager
kube-scheduler-svc            ClusterIP   None            <none>        10259/TCP                      69d   component=kube-scheduler
kubelet                       ClusterIP   None            <none>        10250/TCP,10255/TCP,4194/TCP   69d   <none>
metrics-server                ClusterIP   10.233.47.168   <none>        443/TCP                        69d   k8s-app=metrics-server
[root@mh-k8s-master-prd-243-24 ~]# dig -t A www.baidu.com @10.233.0.3 +short
www.a.shifen.com.
14.215.177.38
14.215.177.39
[root@mh-k8s-master-prd-243-24 ~]# 

(4)配置解析

  CoreDNS 提供基于 DNS 的服务发现解决方案会负责解析以下资源记录(Resource Record)类型以实现服务发现。

(1)拥有ClusterIP的Service资源,需要具有以下类型的资源记录

A记录:<service>.<ns>.svc.<zone>.<ttl> IN a <cluster-ip>
SRV记录:_<port>._<proto>.<service>.<ns>.svc.<zone>.<ttl> IN SRV <weight><priority><port-number><serivce>.<ns>.svc.<zone>
PTR记录:<d>.<c>.<b>.<a>.in-addr-arpa.<ttl> IN PTR <service>.<ns>.svc.<zone>

(2)Headless类型的Service资源,需要具有以下类型的资源记录。

A记录:<service>.<ns>.svc.<zone>.<ttl> IN a <endpoint-ip>
SRV记录:_<port>._<proto>.<service>.<ns>.svc.<zone>.<ttl> IN SRV <weight><priority><port-number><hostname>.<serivce>.<ns>.svc.<zone>
PTR记录:<d>.<c>.<b>.<a>.in-addr-arpa.<ttl> IN PTR <hostname>.<service>.<ns>.svc.<zone>

(3)ExternalName类型的service资源,需要具有CNAME类型的资源记录

CNAME记录:<serivce>.<ns>.svc.<zone>.<ttl> IN CNAME <extname>

  名称解析和服务发现是 Kubernetes 系统许多功能得以实现的基础服务,它通常是集群安装完成后应该立即部署的附加组件。使用 kubeadm 初始化一个集群时,它甚至会自动进行部署。

四、服务发现方式:DNS

  创建 Service 资源对象时,ClusterDNS 会为它自动创建资源记录用于名称解析和服务注册,于是,Pod 资源科直接使用标准的 DNS 名称来访问这些 Service 资源。每个 Service 对象相关的 DNS 记录包括如下两个:

  • {SVCNAME}.{NAMESPACE}.{CLUSTER_DOMAIN}
  • {SVCNAME}.{NAMESPACE}.svc.{CLUSTER_DOMAIN}

  另外,在部署 Kubernetes 时,"--cluster-dns" 指定了集群 DNS 服务的工作地址,"--cluster-domain" 定义了集群使用的本地域名,因此,系统初始化时默认会将 "cluster.local." 和 主机所在的域  "xxx.com“ 作为 DNS 的本地域使用,这些信息会在 Pod 创建时以 DNS 配置的相关信息注入它的 /etc/resolv.conf 配置文件中。

nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local xxx.com

  上述 search 参数中指定的 DNS 各搜索域,是以次序指定的几个域名后缀,具体如下所示:

    • {NAMESPACE}.svc{CLUSTER_DOMAIN}: 如 default.svc.cluster.lcoal。
    • svc.{CLUSTER_DOMAIN}: 如 svc.cluster.local。
    • {CLUSTER_DOMAIN}: 如 cluster.local。
    • {WORK_NODE_DOMAIN}: xxx.com
posted @ 2022-06-21 09:01  左扬  阅读(449)  评论(0编辑  收藏  举报
levels of contents