Kubernetes 服务发现

2023年 5月 4日 36.8k 0

服务发现在微服务架构里,服务之间经常进行通信,服务发现就是解决不同服务之间通信的问题。比如一个nginx的pod,要访问一个mysql服务,就需要知道mysql服务的ip和port,获取ip和port的过程就是服务发现。

服务发现方式

1.环境变量
Pod创建的时候,服务的ip和port会以环境变量的形式注入到pod里,比如pod创建时有一个redis-master服务,服务ip地址是10.4.82.11,port是6379,则会把下面一系列环境变量注入到pod里,通过这些环境变量访问redis-master服务。

REDIS_MASTER_SERVICE_HOST=10.4.82.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.4.82.11:6379

2. DNS服务
K8s集群会内置一个dns服务器,service创建成功后,会在dns服务器里导入一些记录,想要访问某个服务,通过dns服务器解析出对应的ip和port,从而实现服务访问

Kubenetes 1.13.5 集群二进制安装

新闻联播老司机

  • 19年6月13日
  • 喜欢:0
  • 浏览:1.3k
  • 常见的DNS解析服务有CoreDNS和Kube-DNS,我们先简单介绍一下这两个DNS区别
    kube-dns是将多个容器kubedns、dnsmasq、sidecar放入同一个POD中来实现

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

    安全问题。在过去,dnsmasq的安全缺陷已经导致过K8S需要发布安全补丁来修复了
    由于dnsmasq实现的stub domian,但External Service是由kubedns来实现的,所以你就不能在External Service中使用stub domain,这是一种巨大的限制
    

    CoreDNS 在Kubernetes 1.11中,CoreDNS已经实现了基于DNS的服务发现的GA,可作为kube-dns插件的替代品。这意味着CoreDNS将作为各种安装工具未来发布版本中的一个选项来提供。事实上,kubeadm团队选择将其作为Kubernetes 1.11的默认选项
    重要的一点,CoreDNS是只有一个容器的存在
    在kube-dns中,你可以修改ConfigMap以改变服务发现的行为。这允许添加诸如提供服务存根域、修改上游名称服务器以及启用联合之类的功能。
    在CoreDNS中,你同样可以修改CoreDNS Corefile的ConfigMap以更改服务发现的工作方式。Corefile配置提供了比kube-dns更多的选项,因为它是CoreDNS用于配置其所有功能(甚至是那些与Kubernetes无关的功能)的主要配置文件。
    使用kubeadm从kube-dns升级到CoreDNS时,现有的ConfigMap将用于为你生成自定义Corefile,包括存根域、联合和上游名称服务器的所有配置。

    环境演示

    首先我们使用环境变量进行演示

    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
            ports:
            - containerPort: 80
    
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service
      labels:
        name: nginx-service
    spec:
      ports:
      - port: 18080
        targetPort: 80
      selector:
        app: nginx
    
    
    [root@k8s-01 nginx-test]# kubectl apply -f nginx-demo.yaml
    deployment.apps/nginx-deploy created
    service/nginx-service created
    

    这里我们检查一下创建pod的状态,这里我们可以看到已经创建了一个端口为18080的svc

    [root@k8s-01 nginx-test]# kubectl get pod
    NAME                                      READY   STATUS    RESTARTS   AGE
    nginx-deploy-56db997f77-74qfj             1/1     Running   0          52s
    nginx-deploy-56db997f77-khv26             1/1     Running   0          52s
    
    [root@k8s-01 nginx-test]# kubectl get svc
    NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)       AGE
    kubernetes      ClusterIP   10.254.0.1               443/TCP       3d4h
    nginx-service   ClusterIP   10.254.159.126           18080/TCP     62s
    

    接下来,我们创建一个普通的Pod,查看Pod环境变量是否有我们刚刚创建svc一些信息

    apiVersion: v1
    kind: Pod
    metadata:
      name: test-pod
    spec:
      containers:
      - name: test-service
        image: nginx
        command: ["/bin/sh", "-c", "env"]
    
    
    [root@k8s-01 nginx-test]# kubectl apply -f test-demo.yaml
    pod/test-pod created
    

    上面在Pod文件中,我们只让Pod执行了一条环境变量,接下来我们就可以直接看pod的日志查看状态

    [root@k8s-01 nginx-test]# kubectl get pod
    NAME                                      READY   STATUS             RESTARTS   AGE
    ...
    test-pod                                  0/1     CrashLoopBackOff   3          99s
    ...
    
    [root@k8s-01 nginx-test]# kubectl logs test-pod|grep NGINX
    ...
    NGINX_SERVICE_PORT=tcp://10.254.159.126:18080
    NGINX_SERVICE_SERVICE_PORT=18080
    NGINX_SERVICE_PORT_18080_TCP_ADDR=10.254.159.126
    NGINX_DS_SERVICE_HOST=10.254.86.215
    NGINX_SERVICE_PORT_18080_TCP_PORT=18080
    NGINX_SERVICE_PORT_18080_TCP_PROTO=tcp
    
    #这里我们可以看到在Pod默认的环境就已经有刚刚创建svc的相关信息,这时候如果我们test-pod的应用想访问之前创建nginx的svc就可以直接访问它的环境变量就可以访问到。但是前提必须是nginx-svc服务必须提前创建,也可以使用initContainer等待nginx-svc创建完成后,才进行通信,或者是通过DNS来解决
    

    Kube-DNS、CoreDNS
    这里我们使用的是CoreDNS,比Kube-DNS更强大。关于安装这里不再细说,需要安装请参考下面文章,包含kube-dns和coredns选择一个就行

    Kubenetes 1.13.5 集群二进制安装

    新闻联播老司机

  • 19年6月13日
  • 喜欢:0
  • 浏览:1.3k
  • DNS Pod具有静态IP并作为Kubernetes服务暴露出来。该静态 IP 被分配后,kubelet 会将使用--cluster-dns = 参数配置的 DNS 传递给每个容器。DNS 名称也需要域名,本地域可以使用参数--cluster-domain = 在 kubelet中配置。如果使用的是coredns则是下面的配置clusterDomain

    cluster_dns为DNS服务的ClusterIP地址
    cluster_domain为DNS服务中设置的域名
    

    image_1e03vi1av1hvn1usllbn122pu1m.png-196.3kB
    前面也说了,DNS实际上就是通过configmap进行配置监听的,所以我们可以看到在kube-system命名空间下有一个名称为coredns的configmap

    [root@k8s-01 kubelet]# kubectl get cm -n kube-system coredns -o yaml
    apiVersion: v1
    data:
      Corefile: |
        .:53 {
            errors
            health
            kubernetes cluster.local in-addr.arpa ip6.arpa {
                pods insecure
                upstream
                fallthrough in-addr.arpa ip6.arpa
            }
            prometheus :9153
            forward . /etc/resolv.conf
            cache 30
            loop
            reload
            loadbalance
        }
    kind: ConfigMap
    metadata:
      creationTimestamp: "2020-01-30T18:10:18Z"
      labels:
        addonmanager.kubernetes.io/mode: EnsureExists
      name: coredns
      namespace: kube-system
      resourceVersion: "2592"
      selfLink: /api/v1/namespaces/kube-system/configmaps/coredns
      uid: c58bed8b-438b-11ea-af72-000c29eeccce
    

    如果我们想自定义dns,可以参考下面的例子

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

    举个栗子
    当访问kubernetes.default.svc.cluster.local DNS服务器为CoreDNS && Kube-DNS
    当访问为*.abc.local DNS解析服务为1.2.3.4
    当访问i4t.com DNS解析地址为Google的8.8.8.8和8.8.4.4其中之一
    除了修改coredns或者kube-dns的configmap以外,还可以通过修改deployment文件定义dns解析服务器
    当前Kubernetes支持三种 Pod 特定的 DNS 策略:“ClusterFirstWithHostNet”、“Default” 和 “ClusterFirst”。 可以通过 dnsPolicy 标志来指定这些策略
    image_1e040njudhm91mb213g24oo1qr113.png-161kB
    注意:Default不是默认的 DNS 策略。如果没有显式地指定dnsPolicy,将会使用 ClusterFirst

  • 如果 dnsPolicy 被设置为“ Default” 让kubelet来决定使用何种DNS策略,而kubelet默认使用宿主机的 /etc/resolv.conf(使用宿主机的DNS策略)但kubelet可以配置使用什么文件来进行DNS 策略,使用kubelet的参数:–resolv-conf=/etc/resolv.conf来决定DNS解析文件地址
  • 如果 dnsPolicy 被设置为“ClusterFirstWithHostNet” POD是用HOST模式启动的(HOST模式),用HOST模式表示POD中的所有容器,都使用宿主机的/etc/resolv.conf进行DNS查询,但如果使用了HOST模式,还继续使用Kubernetes的DNS服务,那就将dnsPolicy设置为 ClusterFirstWithHostNet
  • 如果 dnsPolicy 被设置为 “ClusterFirst”,这就要依赖于是否配置了存根域和上游 DNS 服务器
  • 1.未进行自定义配置:没有匹配上配置的集群域名后缀的任何请求,例如 “www.abcdocker.com”,将会被转发到继承自节点的上游域名服务器。(Pod所在的Node节点为主)
  • 2.进行自定义配置:如果配置了存根域和上游 DNS 服务器(类似于前面示例 配置的内容),DNS 查询将基于下面的流程对请求进行路由:
  • 查询首先被发送到 kube-dns 中的 DNS 缓存层
      从缓存层,检查请求的后缀,并根据下面的情况转发到对应的 DNS 上:
        具有集群后缀的名字(例如 “.cluster.local”):请求被发送到 kubedns。
        具有存根域后缀的名字(例如 “.abc.local”):请求被发送到配置的自定义 DNS 解析器(例如:监听在 1.2.3.4)
        未能匹配上后缀的名字(例如 “i4t.com”):请求被转发到上游 DNS(例如:Google 公共 DNS 服务器,8.8.8.8 和 8.8.4.4)。 
    

    域名格式

    前面说了可以通过域名的格式访问svc,这里就简单说一下域名格式是如何建立

  • 普通Service
  • 普通service会生成一个service_name.namespace.svc.cluster.local会将这个域名解析到对应的ClusterIP上,如果在Pod之间调用可以简写为service_name.namespace,或者在一个命名空间下,可以只写service_name
  • 举个栗子,例如我们有个mysql服务,service名称为mysql-test1,命名空间为rrfq,那么我们调用的时候可以直接写

    mysql-test1.rrfq.svc.cluster.local
    也可以简写为mysql-test1.rrfq
    
  • Headless Service
  • 简单的来说就是将clusterIP设置为None,这样就可以解析到某一个Pod上,可以通过pod名称进行访问podname.servicename.namespace.svc.cluster.local这种一般常见于statefulset
  • #这里使用mysql举例,当我们创建了3个statefulset的Pod,svc名称为mysql pod名称为mysql-web。则会产生3个名称为mysql-web01、mysql-web02、mysql-web03
    
    这时候比如我们想通过域名的方式访问到mysql-web02,就可以使用下面的域名进行访问
    
    mysql-web02.mysql.default.svc.cluster.local
    
    #如果不设置命名空间,默认就是default
    

    测试DNS

    在前面的步骤我们已经创建了nginx,接下来我们使用busybox镜像测试一下是否可以解析正常

    kubectl run --rm -i --tty test-dns --image=busybox /bin/sh
    
    
    #从下面的svc我们可以看到,我们有一个名称为nginx-service的nginx,端口为18080,所属命名空间为default,那么接下来就看可以进入busybox进行访问测试
    [root@k8s-01 nginx-test]# kubectl get svc
    NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)       AGE
    kubernetes      ClusterIP   10.254.0.1               443/TCP       3d4h
    nginx-service   ClusterIP   10.254.159.126           18080/TCP     62s
    

    在下面截图我们可以看到使用nginx-service.default.svc.cluster.local:18080是可以直接访问到我们的nginx
    image_1e042tuj92k2crk1llr1ene1pu420.png-348.7kB
    除了上面的格式,也可以缩写

    / # wget -q -O- nginx-service.default:18080
    / # wget -q -O- nginx-service:18080
    
    #因为在一个命名空间下,所以不加namespace也是可以的
    

    相关文章:

    1. Kubernetes 1.14 二进制集群安装
    2. Kuerbernetes 1.11 集群二进制安装
    3. Kubenetes 1.13.5 集群二进制安装
    4. CentOS 7 ETCD集群配置大全

    相关文章

    KubeSphere 部署向量数据库 Milvus 实战指南
    探索 Kubernetes 持久化存储之 Longhorn 初窥门径
    征服 Docker 镜像访问限制!KubeSphere v3.4.1 成功部署全攻略
    那些年在 Terraform 上吃到的糖和踩过的坑
    无需 Kubernetes 测试 Kubernetes 网络实现
    Kubernetes v1.31 中的移除和主要变更

    发布评论