• Kubernetes-Serivce的原理和实践


    一、什么是service?

    Service服务(微服务)是 Kubernetes 的核心资源之一,它定义了一个服务的访问入口地址,每一个Service后面由一组Pod来提供支持,通过 Kube-Proxy (智能负载均衡器)的 port 和Service label selector 决定服务请求传递给后端的Pod,外部不需要了解后端如何运行,这给扩展或维护后端带来很大的好处,如下图1所示:

    图片1:来源于K8S权威指南第四版

    二、为什么需要Service?

      在集群内部,直接通过Pod(K8S部署的最小的单元)的IP地址和端口也可以访问到容器应用,但是Pod的IP并非固定的,当因异常情况导致的Pod重启或者被调度到其他node节点,其IP地址会发生变化,因此直接通过Pod的IP和端口访问服务是不可靠的,另外,在实际的应用中,通常会是多个Pod实例提供服务,需要在这些实例的前面设置一个负载均衡器来实现请求到后端Pod的转发,Service就是为解决这些问题而设计的,它可以屏蔽后端Pod的变化,提供集群内部唯一固定的虚拟IP——Cluster IP,可以通过Cluster IP加端口来访问服务,另外请求会通过负载均衡器(Kube-Proxy )自动转发到后端Pod,如图2所示

    图片2:来源于网络素材

    三、service的使用场景

    Service有多种类型,不同类型面对不同应用场景.

    1、Cluster IP:集群默认类型,为集群内部提供一个唯一的虚拟IP,集群内部可以相互访问,但是集群外部无法进行访问,如果提供的服务只需要在集群内部进行访问,可采用此类,Cluster IP支持2种Service如下

    1)普通Service:Cluster IP非NONE,服务接受到请求,通过kube-proxy负载均衡转发后,只会返回后端其中1个Pod的IP和端口(注意是1个),如果通过DNS解析Service的名称,会发现只会返回1个IP,且是此Service的cluster-ip,后文会有验证

    2)Headless Service:Cluster IP为NONE,在某些场景中,开发人员希望自己可以控制负载均衡器,不采用kube-proxy作为负载均衡器、或者希望返回同组Pod的所有Pod,Headless Service适用于此类场景,Cluster IP设置为NONE,kube-proxy将不会进行转发,

    也不会有具体的Cluster IP地址,对它的访问,将会返回所有Pod IP的1个列表,然后由客户端程序自行决定如何处理Pod列表。

    2、NodePort:适用于想从集群外部访问Service,需要将type设置为nodeport类型和指定node的port,设置成功后,用户可通过集群内部任何一台nodeIP:nodeport进行访问

    3、LoadBalance:通过设置LoadBalance映射到公有云服务提供的LoadBalance地址,对Service的访问将会转发到后端Pod上,而具体的负载均衡分发的方式由云服务商提供的LoadBalance的实现机制。

    四、创建和使用service

    1、普通Service类型

    创建Service

    [root@k8s-master zhanglei]# cat svc-clusterip.yaml 
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-svc-ord
      labels:
        app: nginx
    spec:
      type: ClusterIP          # 指定service的类型
      ports:
        - port: 80             # 指定service的端口
          targetPort: 80       # 指定目标Pod端口
      selector:  
        app: nginx-deployment-ord  # 此处就是与Pod建立关联的地方,对service的访问会转发到拥有此标签的Pod

    查看Service

    [root@k8s-master zhanglei]# kubectl get svc nginx-svc-ord -o wide
    NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE     SELECTOR
    nginx-svc-ord   ClusterIP   10.10.30.161   <none>        80/TCP    4d15h   app=nginx-deployment-ord

    可以看到,在创建的时候,并未指定Cluster-IP具体是多少,创建完成之后,系统会自动生成一个10.10.30.161,可以根据自己的需求,是否手动设置具体Cluster-IP,这是一个虚拟的IP,非物理的IP,可以通过ping验证下

    [root@k8s-master zhanglei]# ping 10.10.30.161
    PING 10.10.30.161 (10.10.30.161) 56(84) bytes of data.
    ^C
    --- 10.10.30.161 ping statistics ---
    12 packets transmitted, 0 received, 100% packet loss, time 288ms
    
    [root@k8s-master zhanglei]# ping www.baidu.com
    PING www.a.shifen.com (14.215.177.39) 56(84) bytes of data.
    64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=1 ttl=128 time=12.2 ms
    64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=2 ttl=128 time=54.5 ms
    ^C
    --- www.a.shifen.com ping statistics ---
    2 packets transmitted, 2 received, 0% packet loss, time 2ms
    rtt min/avg/max/mdev = 12.231/33.353/54.476/21.123 ms

    可以看到是10.10.30.161这个虚拟的IP是ping不通的,在看下详细Service信息

    [root@k8s-master zhanglei]# kubectl describe svc nginx-svc-ord 
    Name:              nginx-svc-ord
    Namespace:         default
    Labels:            app=nginx       # Sercice自己的标签
    Annotations:       <none>
    Selector:          app=nginx-deployment-ord   # 指定后端关联的Pod标签
    Type:              ClusterIP
    IP:                10.10.30.161
    Port:              <unset>  80/TCP
    TargetPort:        80/TCP
    Endpoints:         10.122.235.240:80,10.122.235.241:80,10.122.235.242:80
    Session Affinity:  None
    Events:            <none>

     看到Endpoints这里有3组IP和端口,这里记录的是Service和后端Pod的的对应表,那大家可能好奇了,这个具体是如何生成的,接下来看下如何和Pod关联的

    [root@k8s-master zhanglei]# cat dep-ord.yaml 
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment-ord
      namespace: default
      labels:
        app: nginx-deployment
    spec:
      replicas: 3                          # 指定副本数为3,创建成功后
      selector:
        matchLabels:
          app: nginx-deployment-ord
      template:                           # 设置的是Pod模板
        metadata:
          labels:
            app: nginx-deployment-ord    # Pod的标签,这个标签需要和上面副本控制器matchLabels、service的selector保持一致
        spec:
          containers:
          - name: nginx                  # 指定Pod中容器镜像
            image: nginx:latest
            ports:
            - containerPort: 80          # 指定容器的端口

    副本控制器deployment可以保证用户期望的实例数,如replicas设置的是3,如删除了Pod,副本控制器会一直尝试拉起新的Pod来保证期望数,关于更多deployment的介绍,在另外的文章再继续深究,deployment新建成功后,就可以通过Service访问后端的Pod了

    [root@k8s-master zhanglei]# curl 10.10.30.161:80                      # curl cluster-ip:port
    <!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>

    可以看到,通过Service可以访问到后端Pod的nginx容器,再解析下具体这个Service的域名

    [root@k8s-master zhanglei]# dig -t A nginx-svc-ord.default.svc.cluster.local. @10.10.0.12    # @后面是kube-dns的服务器地址     
    
    ; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> -t A nginx-svc-ord.default.svc.cluster.local. @10.10.0.12
    ;; global options: +cmd
    ;; Got answer:
    ;; WARNING: .local is reserved for Multicast DNS
    ;; You are currently testing what happens when an mDNS query is leaked to DNS
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17934
    ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
    ;; WARNING: recursion requested but not available
    
    ;; OPT PSEUDOSECTION:
    ; EDNS: version: 0, flags:; udp: 4096
    ; COOKIE: 5c8fb317c825d518 (echoed)
    ;; QUESTION SECTION:
    ;nginx-svc-ord.default.svc.cluster.local. IN A
    
    ;; ANSWER SECTION:
    nginx-svc-ord.default.svc.cluster.local. 30 IN A 10.10.30.161     #解析成功,返回了域名对应的Cluster-ip
    
    ;; Query time: 23 msec
    ;; SERVER: 10.10.0.12#53(10.10.0.12)
    ;; WHEN: 六 6月 06 16:38:22 CST 2020
    ;; MSG SIZE  rcvd: 135

    dig -t A nginx-svc-ord.default.svc.cluster.local. @10.10.0.10,nginx-svc-ord为service的名称,default为命名空间的名称,若未对集群重命名,则默认集群默认的名称为cluster,@后面是kube-dns服务器地址

    2、Headless Service

    创建一个Headless Service,其后端同样指向含有nginx-deployment-ord 标签的Pod

    [root@k8s-master zhanglei]# cat svc-headless-dep.yaml 
    apiVersion: v1
    kind: Service
    metadata:
      name: myapp-headless-service-dep
      labels:
        app: deployment
    spec:
      ports:
      - port: 80
        name: web
      clusterIP: None
      selector:
        app: nginx-deployment-ord      
    [root@k8s-master zhanglei]# kubectl create -f svc-headless-dep.yaml
    service/myapp-headless-service-dep created
    [root@k8s-master zhanglei]# kubectl get svc -o wide|grep myapp-headless-service-dep
    myapp-headless-service-dep   ClusterIP   None           <none>        80/TCP    3m36s   app=nginx-deployment-ord

    通过查看,可以看到Headless Service的ClusterIP为None,查看下详细信息

    [root@k8s-master zhanglei]# kubectl describe svc myapp-headless-service-dep 
    Name:              myapp-headless-service-dep
    Namespace:         default
    Labels:            app=deployment
    Annotations:       <none>
    Selector:          app=nginx-deployment-ord
    Type:              ClusterIP
    IP:                None
    Port:              web  80/TCP
    TargetPort:        80/TCP
    Endpoints:         10.122.235.240:80,10.122.235.241:80,10.122.235.242:80
    Session Affinity:  None
    Events:            <none>

    可以看到其Endpoints所对应的列表与前面nginx-svc-ord的Endpoints是一样的,也就是说,支持多个Service通过Selector关联到后端同一组Pod,解析下myapp-headless-service-dep 

    [root@k8s-master zhanglei]# dig -t A myapp-headless-service-dep.default.svc.cluster.local. @10.10.0.10
    
    ; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> -t A myapp-headless-service-dep.default.svc.cluster.local. @10.10.0.10
    ;; global options: +cmd
    ;; Got answer:
    ;; WARNING: .local is reserved for Multicast DNS
    ;; You are currently testing what happens when an mDNS query is leaked to DNS
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2879
    ;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
    ;; WARNING: recursion requested but not available
    
    ;; OPT PSEUDOSECTION:
    ; EDNS: version: 0, flags:; udp: 4096
    ; COOKIE: 52294ca2d8aec856 (echoed)
    ;; QUESTION SECTION:
    ;myapp-headless-service-dep.default.svc.cluster.local. IN A
    
    ;; ANSWER SECTION:                                                                            # 返回了后端所有的Pod,这里是区别普通service,普通service返回的Cluster-ip
    myapp-headless-service-dep.default.svc.cluster.local. 30 IN A 10.122.235.241
    myapp-headless-service-dep.default.svc.cluster.local. 30 IN A 10.122.235.240
    myapp-headless-service-dep.default.svc.cluster.local. 30 IN A 10.122.235.242
    
    ;; Query time: 0 msec
    ;; SERVER: 10.10.0.10#53(10.10.0.10)
    ;; WHEN: 六 6月 06 16:57:12 CST 2020
    ;; MSG SIZE  rcvd: 297

    可以看到这里解析返回了后端所有的Pod的IP,这里是区别普通Service,普通Service返回的Cluster-ip,拿到这个列表之后,可以做进一步的处理,有状态负载(statefulset)会给每个Pod配置DNS,结合返回所有的Pod,不同的Pod之间就可以通过域名互相访问了

    ,这里后续会在statefulset详细讲。

    3、NodePort

    创建一个type为nodeport类型的Service

    [root@k8s-master zhanglei]# cat svc-nodeport-dep.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service-nodeport
    spec:
      selector:
          app: nginx-deployment-ord  
      ports:
        - name: http
          port: 8000               # service端口1
          protocol: TCP            # 指定端口的协议
          targetPort: 80           # 指定容器的端口
        - name: https
          port: 8443               # service端口2
          protocol: TCP            
          targetPort: 443
      type: NodePort      # 指定NodePort类型
    [root@k8s-master zhanglei]# kubectl create -f svc-nodeport-dep.yaml 
    service/nginx-service-nodeport created
    [root@k8s-master zhanglei]# kubectl get svc nginx-service-nodeport  -o wide
    NAME                     TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                         AGE   SELECTOR
    nginx-service-nodeport   NodePort   10.10.38.153   <none>        8000:31128/TCP,8443:31642/TCP   48s   app=nginx-deployment-ord
    [root@k8s-master zhanglei]# curl 192.168.126.129: 31128   # 192.168这个为node节点的IP,nodeport范围为:30000~32767
    curl: (7) Failed to connect to 192.168.126.129 port 80: Connection refused
    curl: (7) Couldn't connect to server
    [root@k8s-master zhanglei]# curl 192.168.126.129:31128
    <!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>

    这里nodeport的端口范围必须是在30000~32767范围之内,建议在创建的不用手动设置指定此端口,让系统自动生成,可以避免端口冲突,我们也可以在宿主机的浏览器上输入nodeip:nodeport进行访问测试,如下图所示:

    六、总结

    从上文可以看出,Service是K8S中非常核心的一个资源,是整个集群应用访问的入口,通过灵活的配置Service和副本控制器,可以很好地满足各种部署应用的场景。

    作者简介:云计算容器K8S方向产品经理,学点技术,为更好地设计产品。

  • 相关阅读:
    The 4 Most Important Skills for a Software Developer
    Youth is not a time of life, it is a state of mind——青春不是一段年华,而是一种心境
    英雄所见略同——每个人都有的一套价值体系观念
    28法则————10分钟休息胜过半小时努力
    离职员工心声
    员工必备素质、能力——职场精英
    安卓sqlite数据库的使用
    安卓adb命令的使用
    windows使用命令行,提高效率
    命令行编译java文件(含第三方jar包)
  • 原文地址:https://www.cnblogs.com/gdut1425/p/13054217.html
Copyright © 2020-2023  润新知