zoukankan      html  css  js  c++  java
  • Kubernetes中Service学习笔记

    我们知道 Pod 的生命周期是有限的。可以用 ReplicaSet 和Deployment 来动态的创建和销毁 Pod,每个 Pod 都有自己的 IP 地址,但是如果 Pod 重建了的话那么他的 IP 很有可能也就变化了。

    这就会带来一个问题:比如我们有一些后端的 Pod 集合为集群中的其他应用提供 API 服务,如果我们在前端应用中把所有的这些后端的 Pod 的地址都写死,然后以某种方式去访问其中一个 Pod 的服务,这样看上去是可以工作的,但是如果这个 Pod 挂掉了,然后重新启动起来了,是不是 IP 地址非常有可能就变了,这个时候前端就极大可能访问不到后端的服务了。

    为解决这个问题 Kubernetes 就为我们提供Service对象,Service 是一种抽象的对象,它定义了一组 Pod 的逻辑集合和一个用于访问它们的策略,其实这个概念和微服务非常类似。一个 Serivce 下面包含的 Pod 集合是由 Label Selector 来决定的。

    image

    定义Service

    假定我们有一组 Pod 服务,它们对外暴露了 8080 端口,同时都被打上了 app=myapp 这样的标签,那么我们就可以像下面这样来定义一个 Service 对象:

    apiVersion: v1
    kind: Service
    metadata:
      name: myservice
    spec:
      selector:
        app: myapp
      ports:
        - protocol: TCP
          port: 80
          targetPort: 8080
          name: myapp-http
    

    通过kubectl create命令就会创建一个名为myservice的Service对象,它会将请求代理到使用TCP端口为8080,具有标签app=myapp的Pod上,这个 Service 会被系统分配 Cluster IP,该 Service 还会持续的监听 selector 下面的 Pod,会把这些 Pod 信息更新到一个名为 myservice 的Endpoints 对象上去,这个对象就类似于我们上面说的 Pod 集合了。

    需要注意的是,Service 能够将一个接收端口映射到任意的 targetPort。

    默认情况下,targetPort 将被设置为与 port 字段相同的值。可能更有趣的是,targetPort 可以是一个字符串,引用了 backend Pod 的一个端口的名称。因实际指派给该端口名称的端口号,在每个 backend Pod 中可能并不相同,所以对于部署和设计 Service,这种方式会提供更大的灵活性。

    另外 Service 能够支持 TCP 和 UDP 协议,默认是 TCP 协议。

    kube-proxy

    Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行了一个kube-proxy的服务进程。当创建Service的时候会通过API Server向etcd写入创建的Service的信息,而kube-proxy会基于监听的机制发现这种Service的变化,然后它会将最新的Service信息转换为对应的访问规则。

    kube-proxy目前支持三种工作模式:1、userspace; 2、iptables; 3、ipvs

    image

    userspace

    userspace模式下,kube-proxy会为每一个Service创建一个监听端口,发向Cluster IP的请求被iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据LB算法(负载均衡算法)选择一个提供服务的Pod并和其建立连接,以便将请求转发到Pod上。

    该模式下,kube-proxy充当了一个四层负载均衡器的角色。由于kube-proxy运行在userspace中,在进行转发处理的时候会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率非常低下。

    iptables

    kube-proxy 会监视 apiserver 对 Service 对象和 Endpoints 对象的添加和移除。对每个 Service,它会添加上 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某一个 Pod 上面。

    我们还可以使用 Pod readiness 探针 验证后端 Pod 可以正常工作,以便 iptables 模式下的 kube-proxy 仅看到测试正常的后端,这样做意味着可以避免将流量通过 kube-proxy 发送到已知失败的 Pod 中,所以对于线上的应用来说一定要做 readiness 探针。

    iptables模式下,kube-proxy为Service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod的IP上。

    该模式下kube-proxy不承担四层负载均衡器的角色,只负责创建iptables规则。该模式的优点在于较userspace模式效率更高,但是不能提供灵活的LB策略,当后端Pod不可用的时候无法进行重试。

    image

    ipvs

    ipvs模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高,除此之外,ipvs支持更多的LB算法。

    image

    IPVS 提供了更多选项来平衡后端 Pod 的流量,默认是 rr,有如下一些策略:

    ​ 1、rr: round-robin

    ​ 2、lc: least connection (smallest number of open connections)

    ​ 3、dh: destination hashing

    ​ 4、sh: source hashing

    ​ 5、sed: shortest expected delay

    ​ 6、nq: never queue

    开启ipvs(必须安装ipvs内核木块,否则会降级为iptables)。

    $ kubectl edit cm kube-proxy -n kube-system
    ......
    ipvs:
          excludeCIDRs: null
          minSyncPeriod: 0s
          scheduler: ""
          strictARP: false
          syncPeriod: 0s
          tcpFinTimeout: 0s
          tcpTimeout: 0s
          udpTimeout: 0s
        kind: KubeProxyConfiguration
        metricsBindAddress: ""
        mode: "ipvs"   # 修改mode
        nodePortAddresses: null
        oomScoreAdj: null
    ......
    

    删除原来标签为k8s-app=kube-proxy的pod,测试ipvs模块是否开启成功。

    $ kubectl delete pod -l k8s-app=kube-proxy -n kube-system
    pod "kube-proxy-5hcrh" deleted
    pod "kube-proxy-87kkp" deleted
    pod "kube-proxy-tqw56" deleted
    
    $ ipvsadm -Ln   # 测试ipvs模块是否开启成功
    IP Virtual Server version 1.2.1 (size=4096)
    Prot LocalAddress:Port Scheduler Flags
      -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
    TCP  10.96.0.1:443 rr
      -> 192.168.47.133:6443          Masq    1      0          0         
    TCP  10.96.0.10:53 rr
      -> 10.244.0.2:53                Masq    1      0          0         
      -> 10.244.0.4:53                Masq    1      0          0         
    TCP  10.96.0.10:9153 rr
      -> 10.244.0.2:9153              Masq    1      0          0         
      -> 10.244.0.4:9153              Masq    1      0          0         
    UDP  10.96.0.10:53 rr
      -> 10.244.0.2:53                Masq    1      0          0         
      -> 10.244.0.4:53                Masq    1      0          0        
    

    Service

    我们在定义 Service 的时候可以指定一个自己需要的类型的 Service,如果不指定的话默认是 ClusterIP类型。

    我们可以使用的服务类型如下:

    • ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的服务类型。

    • NodePort:通过每个 Node 节点上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 NodeIp:NodePort,可以从集群的外部访问一个 NodePort 服务。

    • LoadBalancer:使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务,这个需要结合具体的云厂商进行操作。

    • ExternalName:把集群外部的服务引入集群内部,直接使用。

    在使用Service之前,首先利用Deployment创建三个Pod。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: pc-deployment
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nginx-pod
      template:
        metadata:
          labels:
            app: nginx-pod
        spec:
          containers:
            - name: nginx
              image: nginx:1.17.1
              ports:
                - containerPort: 80
    

    创建后进行查看:

    kubectl get pods -o wide
    NAME                             READY   STATUS    RESTARTS   AGE   IP           NODE  .. 
    pc-deployment-7d7dd5499b-9qcmz   1/1     Running   0          19s   10.244.2.2   node1 .. 
    pc-deployment-7d7dd5499b-gxhcf   1/1     Running   0          19s   10.244.1.2   node2 .. 
    pc-deployment-7d7dd5499b-kmq7g   1/1     Running   0          19s   10.244.1.3   node2 .. 
    

    为了后面的测试方便,以此修改三个Pod钟Nginx的index.html。

    $ kubectl exec -it pc-deployment-7d7dd5499b-9qcmz /bin/bash
    root@pc-deployment-7d7dd5499b-9qcmz:/# echo 10.244.2.2 > /usr/share/nginx/html/index.html 
    
    $ kubectl exec -it pc-deployment-7d7dd5499b-gxhcf /bin/bash
    root@pc-deployment-7d7dd5499b-gxhcf:/# echo 10.244.1.2 > /usr/share/nginx/html/index.html 
    
    $ kubectl exec -it pc-deployment-7d7dd5499b-kmq7g /bin/bash
    root@pc-deployment-7d7dd5499b-kmq7g:/# echo 10.244.1.3 > /usr/share/nginx/html/index.html 
    

    修改完成后,进行访问测试:

    $ curl 10.244.2.2
    10.244.2.2
    $ curl 10.244.1.2
    10.244.1.2
    $ curl 10.244.1.3
    10.244.1.3
    

    ClusterIP

    直接创建一个type为ClusterIP类型的Service,指定service的虚拟IP为10.97.97.97:

    apiVersion: v1
    kind: Service
    metadata:
      name: service-clusterip
    spec:
      selector:
        app: nginx-pod
      type: ClusterIP
      clusterIP: 10.97.97.97  # 如果不指定,系统会默认生成一个
      ports:
        - port: 80  # service的端口
          targetPort: 80  # pod的端口
    

    创建后进行查看:

    $ kubectl get svc -o wide
    NAME                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE   SELECTOR
    kubernetes          ClusterIP   10.96.0.1     <none>        443/TCP   97m   <none>
    service-clusterip   ClusterIP   10.97.97.97   <none>        80/TCP    10s   app=nginx-pod
    
    $ kubectl describe svc service-clusterip
    Name:              service-clusterip
    Namespace:         default
    Labels:            <none>
    Annotations:       Selector:  app=nginx-pod
    Type:              ClusterIP
    IP:                10.97.97.97
    Port:              <unset>  80/TCP
    TargetPort:        80/TCP
    Endpoints:         10.244.1.2:80,10.244.1.3:80,10.244.2.2:80
    Session Affinity:  None
    Events:            <none>
    

    从上面的信息中我们看到有个字段叫EndpointsEndpoint是Kubernetes中的一个资源对象,存储在etcd中,用来记录一个service对应的所有Pod的访问地址,它是根据service配置文件中的selector描述产生的。

    一个service由一组Pod组成,这些Pod通过Endpoints暴露出来,Endpoints是实现实际服务的端点集合。换言之,service和Pod之间的联系是通过Endpoints实现的。

    还记得我们上边已经设置了kube-proxy的工作方式为ipvs,而且也查看了映射规则是轮训。

    接下来我们进行访问10.97.97.97观察效果:

    $ while true;do curl 10.97.97.97:80; sleep 2;done;
    10.244.2.2
    10.244.1.3
    10.244.1.2
    10.244.2.2
    10.244.1.3
    10.244.1.2
    10.244.2.2
    ......
    

    对Service的访问被分发到了后端的Pod上,目前Kubernetes提供了两种负载分发策略:

    • 如果不定义,默认使用kube-proxy的策略,比如随机、轮询等。
    • 基于客户端地址的会话保持模式,即来自同一个客户端发起的所有请求都会转发到固定的一个Pod上,这对于传统基于Session的认证项目来说很友好,此模式可以在spec中添加sessionAffinity: ClusterIP选项。

    我们给Service服务添加上基于Session的分发策略:

    $ kubectl edit svc service-clusterip
    ....
    spec:
      clusterIP: 10.97.97.97
      ports:
      - port: 80
        protocol: TCP
        targetPort: 80
      selector:
        app: nginx-pod
      sessionAffinity: ClientIP # 修改分发策略基于客户端地址会话保持模式
      type: ClusterIP
    ......
    

    再次进行访问10.97.97.97观察效果:

    $ while true;do curl 10.97.97.97:80; sleep 2;done;
    10.244.1.3
    10.244.1.3
    10.244.1.3
    10.244.1.3
    ......
    

    NodePort类型

    如果设置 type 的值为 "NodePort",Kubernetes master 将从给定的配置范围内(默认:30000-32767)分配端口,每个 Node 将从该端口(每个 Node 上的同一端口)代理到 Service。该端口将通过 Service 的 spec.ports[*].nodePort 字段被指定,如果不指定的话会自动生成一个端口。

    需要注意的是,Service 将能够通过 spec.ports[].nodePortspec.clusterIp:spec.ports[].port 而对外可见。

    image

    接下来创建一个 NodePort 的服务来访问我们前面的 Nginx 服务:

    apiVersion: v1
    kind: Service
    metadata:
      name: service-nodeport
    spec:
      selector:
        app: nginx-pod
      type: NodePort
      ports:
        - port: 80  # service的端口
          targetPort: 80  # pod的端口
          nodePort: 30002  # 指定绑定端口
    

    创建后进行查看Service对象的信息:

    $ kubectl get svc
    NAME                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
    kubernetes          ClusterIP   10.96.0.1       <none>        443/TCP        127m
    service-clusterip   ClusterIP   10.97.97.97     <none>        80/TCP         30m
    service-nodeport    NodePort    10.107.187.88   <none>        80:30002/TCP   5s
    

    我们可以看到 service-nodeport 的 TYPE 类型已经变成了 NodePort,后面的 PORT(S) 部分也多了一个 30002 的映射端口。

    LoadBalancer类型

    LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境的支持,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。

    image

    ExternalName

    ExternalName 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。

    image

    直接创建一个externamlName类型的service:

    apiVersion: v1
    kind: Service
    metadata:
      name: service-externalname
    spec:
      type: ExternalName # Service类型为ExternalName
      externalName: my.database.example.com
    

    当访问地址 service-externalname.svc.cluster.local时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 Service 的 type,完全不需要修改调用的代码,这样就完全解耦了。

    除了可以直接通过 externalName 指定外部服务的域名之外,我们还可以通过自定义 Endpoints 来创建 Service,前提是 clusterIP=None,名称要和 Service 保持一致,如下所示:

    apiVersion: v1
    kind: Service
    metadata:
      name: etcd-k8s
      namespace: kube-system
      labels:
        k8s-app: etcd
    spec:
      type: ClusterIP
      clusterIP: None
      ports:
      - name: port
        port: 2379
    
    ---
    
    apiVersion: v1
    kind: Endpoints
    metadata:
      name: etcd-k8s  # 名称必须和 Service 一致
      namespace: kube-system
      labels:
        k8s-app: etcd
    subsets:
    - addresses:
      - ip: 10.151.30.57  # Service 将连接重定向到 endpoint
      ports:
      - name: port
        port: 2379   # endpoint 的目标端口
    

    上面这个服务就是将外部的 etcd 服务引入到 Kubernetes 集群中来。

    服务发现

    我们可以通过 Service 生成的 ClusterIP(VIP) 来访问 Pod 提供的服务,但是在使用的时候还有一个问题:我们怎么知道某个应用的 VIP 呢?比如我们有两个应用,一个是 api 应用,一个是 db 应用,两个应用都是通过 Deployment 进行管理的,并且都通过 Service 暴露出了端口提供服务。api 需要连接到 db 这个应用,我们只知道 db 应用的名称和 db 对应的 Service 的名称,但是并不知道它的 VIP 地址。

    环境变量

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

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

    直接创建后进行查看:

    $ kubectl get pods 
    NAME                             READY   STATUS    RESTARTS   AGE
    nginx-deploy-f8c77cd9d-2qzhv     1/1     Running   0          7s
    nginx-deploy-f8c77cd9d-j96kl     1/1     Running   0          7s
    ...
    
    $ kubectl get svc
    NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
    nginx-service          ClusterIP      10.96.134.91    <none>          5000/TCP       14s
    ...
    

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

    现在我们再来创建一个普通的 Pod,观察下该 Pod 中的环境变量是否包含上面的 nginx-service 的服务信息:

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

    等pod创建完成后,我们查看日志信息:

    $ kubectl logs test-pod
    ...
    NGINX_SERVICE_PORT_5000_TCP_ADDR=10.96.134.91
    NGINX_SERVICE_PORT_5000_TCP_PORT=5000
    NGINX_SERVICE_PORT_5000_TCP_PROTO=tcp
    KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
    SERVICE_NODEPORT_SERVICE_HOST=10.107.187.88
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    NGINX_SERVICE_SERVICE_HOST=10.96.134.91
    NGINX_SERVICE_PORT_5000_TCP=tcp://10.96.134.91:5000
    SERVICE_CLUSTERIP_SERVICE_HOST=10.97.97.97
    KUBERNETES_PORT_443_TCP_PORT=443
    KUBERNETES_PORT_443_TCP_PROTO=tcp
    SERVICE_NODEPORT_SERVICE_PORT=80
    SERVICE_NODEPORT_PORT=tcp://10.107.187.88:80
    NGINX_SERVICE_SERVICE_PORT=5000
    NGINX_SERVICE_PORT=tcp://10.96.134.91:5000
    ...
    

    我们可以看到打印了很多环境变量信息,其中就包括我们刚刚创建的 nginx-service 这个服务,有 HOST、PORT、PROTO、ADDR 等,也包括其他已经存在的 Service 的环境变量,现在如果我们需要在这个 Pod 里面访问 nginx-service 的服务,我们是不是可以直接通过 NGINX_SERVICE_SERVICE_HOSTNGINX_SERVICE_SERVICE_PORT 就可以了,但是如果这个 Pod 启动起来的时候 nginx-service 服务还没启动起来,在环境变量中我们是无法获取到这些信息的,当然我们可以通过 initContainer 之类的方法来确保 nginx-service 启动后再启动 Pod,但是这种方法毕竟增加了 Pod 启动的复杂性,所以这不是最优的方法,局限性太多了。

    DNS

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

    DNS 服务不是一个独立的系统服务,而是作为一种 addon 插件而存在,现在比较推荐的两个插件:kube-dns 和 CoreDNS,实际上在比较新点的版本中已经默认是 CoreDNS 了,因为 kube-dns 默认一个 Pod 中需要3个容器配合使用,CoreDNS 只需要一个容器即可,我们在前面使用 kubeadm 搭建集群的时候直接安装的就是 CoreDNS 插件:

    $ kubectl get pod -n kube-system -l k8s-app=kube-dns
    NAME                       READY   STATUS    RESTARTS   AGE
    coredns-7ff77c879f-jldzr   1/1     Running   0          4h54m
    coredns-7ff77c879f-nfxm4   1/1     Running   0          4h54m
    

    CoreDns 是用 Go编写的高性能,高扩展性的 DNS 服务,基于 HTTP/2 Web 服务 Caddy 进行编写的。CoreDns 内部采用插件机制,所有功能都是插件形式编写,用户也可以扩展自己的插件,以下是 Kubernetes 部署 CoreDns 时的默认配置:

    $ kubectl get cm coredns -n kube-system -o yaml
    apiVersion: v1
    data:
      Corefile: |
        .:53 {
            errors   # 启用错误记录
            health { 
               lameduck 5s  # 启动检查端点
            }
            ready
            # 处理k8s域名解析
            kubernetes cluster.local in-addr.arpa ip6.arpa {  
               pods insecure
               fallthrough in-addr.arpa ip6.arpa
               ttl 30
            }
            prometheus :9153   # 启用 prometheus metrics 指标,9153:metrics
            forward . /etc/resolv.conf # 通过 resolv.conf 内的 nameservers 解析
            cache 30   # 启用缓存,所有内容限制为 30s 的TTL
            loop       # 检查简单的转发循环并停止服务
            reload	   # 运行自动重新加载 corefile,热更新
            loadbalance # 负载均衡,默认 round_robin
        }
    kind: ConfigMap
    metadata:
      creationTimestamp: "2022-01-06T01:59:24Z"
      managedFields:
      - apiVersion: v1
        fieldsType: FieldsV1
        fieldsV1:
          f:data:
            .: {}
            f:Corefile: {}
        manager: kubeadm
        operation: Update
        time: "2022-01-06T01:59:24Z"
      name: coredns
      namespace: kube-system
      resourceVersion: "178"
      selfLink: /api/v1/namespaces/kube-system/configmaps/coredns
      uid: be5a5948-577b-4532-a21a-1dd46aafd4df
    
    • 每个 {} 代表一个 zone,格式是 “Zone:port{}”, 其中"."代表默认zone
    • {} 内的每个名称代表插件的名称,只有配置的插件才会启用,当解析域名时,会先匹配 zone(都未匹配会执行默认 zone),然后 zone 内的插件从上到下依次执行(这个顺序并不是配置文件内谁在前面的顺序,而是core/dnsserver/zdirectives.go内的顺序),匹配后返回处理(执行过的插件从下到上依次处理返回逻辑),不再执行下一个插件

    CoreDNS 的 Service 地址一般情况下是固定的,类似于 kubernetes 这个 Service 地址一般就是第一个 IP 地址 10.96.0.1,CoreDNS 的 Service 地址就是 10.96.0.10,该 IP 被分配后,kubelet 会将使用 --cluster-dns=<dns-service-ip> 参数配置的 DNS 传递给每个容器。DNS 名称也需要域名,本地域可以使用参数--cluster-domain = <default-local-domain> 在 kubelet 中配置:

    $ cat /var/lib/kubelet/config.yaml 
    ......
    clusterDNS:
    - 10.96.0.10
    clusterDomain: cluster.local
    ......
    

    我们前面说了如果我们建立的 Service 如果支持域名形式进行解析,就可以解决我们的服务发现的功能,那么利用 kubedns 可以将 Service 生成怎样的 DNS 记录呢?

    ​ 1、普通的 Service:会生成 servicename.namespace.svc.cluster.local 的域名,会解析到 Service 对应的 ClusterIP 上,在 Pod 之间的调用可以简写成 servicename.namespace,如果处于同一个命名空间下面,甚至可以只写成 servicename 即可访问

    ​ 2、Headless Service:无头服务,就是把 clusterIP 设置为 None 的,会被解析为指定 Pod 的 IP 列表,同样还可以通过 podname.servicename.namespace.svc.cluster.local 访问到具体的某一个 Pod。

    接下来我们来使用一个简单 Pod 来测试下 Service 的域名访问:

    $ kubectl run -it --image busybox:1.30.0 test-dns --restart=Never --rm /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 地址即是在安装 CoreDNS 插件的时候集群分配的一个固定的静态 IP 地址,我们可以通过下面的命令进行查看:

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

    也就是说我们这个 Pod 现在默认的 nameserver 就是 kube-dns 的地址,现在我们来访问下前面我们创建的 nginx-service 服务:

    / # wget -q -O- nginx-service.default.svc.cluster.local:5000
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
        body {
             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>
    

    再试一试访问:nginx-service.default.svcnginx-service.defaultnginx-service,不出意外这些域名都可以正常访问到期望的结果。

    另外我们可以测试下无头服务:

    apiVersion: v1
    kind: Service
    metadata:
      name: service-headliness
    spec:
      selector:
        app: nginx
      clusterIP: None # 将clusterIP设置为None,即可创建headliness Service
      type: ClusterIP
      ports:
        - port: 80 # Service的端口
          targetPort: 80 # Pod的端口
    

    创建完成后查看service的详情:

    $ kubectl describe svc service-headliness
    Name:              service-headliness
    Namespace:         default
    Labels:            <none>
    Annotations:       Selector:  app=nginx
    Type:              ClusterIP
    IP:                None
    Port:              <unset>  80/TCP
    TargetPort:        80/TCP
    Endpoints:         10.244.1.4:80,10.244.2.3:80
    Session Affinity:  None
    Events:            <none>
    

    进行访问测试:

    / # wget -q -O- service-headliness.default.svc.cluster.local
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
        body {
             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>
    
    
  • 相关阅读:
    android studio学习---怎么创建一个新的module并且再次运行起来(在当前的project里面)
    你真的了解WebSocket吗?
    vue学习(十二)vue全家桶 Vue-router&Vuex
    GoJs的使用
    vue学习(十一)vue-cli3开发单文件组件
    vue学习(十)mixin 偷懒
    vue学习(九)对象变更检测注意事项
    vue学习(八)nextTick[异步更新队列]的使用和应用
    django的url 传不传参
    vue学习(七)refs的使用
  • 原文地址:https://www.cnblogs.com/huiyichanmian/p/15771246.html
Copyright © 2011-2022 走看看