• K8S实战(十)| Service


    前言

    Pod 已经成功运行起来了,但是有两个问题。

    一是这些 Pod 无法从集群外部直接访问到,二是 Pod 出现故障自愈后,IP 会发生变化。

    如何解决这两个问题,这里有一个非常重要的概念:Service

    更新历史

    Service 的意义和特点

    1. 对一组 Pod 提供负载均衡(工作在 TCP/UDP 4 层)
    2. 防止 Pod 更换 IP 失联,即服务发现
    3. 通过 label selector 关联 Pod

    Service 工作原理

    Service 是由 kube-proxy 组件加上 iptables/LVS 共同实现。
    说白了就是通过 kube-proxy 生成了一堆 iptables 规则,通过 iptables 规则来转发数据。

    iptables 转发:

    1. K8S 默认的转发设置。
    2. 选择后端 Pod 为随机选择。
    3. 当 Pod 没有响应,连接会失败,并没有健康检查机制。
    4. 需要配合 Pod 就绪探测器来确保访问到健康的 Pod。
    5. 当集群规模达到上万个服务时,iptables 转发效率会显著降低。

    LVS转发:

    1. 基于内核哈希表,性能强大,具有更高网络吞吐量。
    2. 适用于 Pod 量级大,转发规则更多的大规模集群。
    3. LVS 支持更多的 Pod 负载均衡调度算法。
    4. LVS 只负责负载均衡和代理功能,剩余的包过滤和SNAT等操作还是需要 iptables 处理,但这些操作规则数量不会因 Pod 数量的增加而增加。
    5. 也叫 IPVS 。

    Service 的默认工作方式

    创建 Pod 和 默认Service,进行默认工作状态的测试。

    先创建3个 Pod

    cat nginx.yaml

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

    创建一个默认类型的 Service,名称为 nginx-service

    cat nginx-service.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service
    spec:
      selector:
        app: nginx
      ports:
      - port: 80
        targetPort: 80
        protocol: TCP
    

    port: 80
    是 service 在集群内部的VIP端口

    targetPort: 80
    是 Pod 的端口

    执行创建

    kubectl apply -f nginx.yaml 
    kubectl apply -f nginx-service.yaml 
    

    查看运行情况

    [root@master01 ~]# kubectl get pod -o wide
    NAME                               READY   STATUS    RESTARTS   AGE   IP               NODE     NOMINATED NODE   READINESS GATES
    nginx-deployment-d46f5678b-cldf4   1/1     Running   0          21m   192.10.137.153   work03   <none>           <none>
    nginx-deployment-d46f5678b-lnxh9   1/1     Running   0          21m   192.10.205.252   work01   <none>           <none>
    nginx-deployment-d46f5678b-th8xq   1/1     Running   0          21m   192.10.75.89     work02   <none>           <none>
    
    [root@master01 ~]# kubectl get service
    NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
    nginx-service   ClusterIP   192.20.150.26   <none>        80/TCP    13m
    

    查看名称为 nginx-service 的 service 成功挂载的后端 Pod

    [root@master01 ~]# kubectl get endpoints nginx-service
    NAME            ENDPOINTS                                             AGE
    nginx-service   192.10.137.153:80,192.10.205.252:80,192.10.75.89:80   14m
    

    可以看到我们创建的名为 nginx-service 的 Service 后端挂载了3个 Pod

    给3个 Pod 写入内容,访问 Pod 时返回自身的主机名

    kubectl exec nginx-deployment-d46f5678b-cldf4 -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html';
    kubectl exec nginx-deployment-d46f5678b-lnxh9 -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html';
    kubectl exec nginx-deployment-d46f5678b-th8xq -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html';
    

    我们访问 Service IP 看看

    [root@master01 ~]# curl 192.20.150.26
    nginx-deployment-d46f5678b-th8xq
    [root@master01 ~]# curl 192.20.150.26
    nginx-deployment-d46f5678b-cldf4
    [root@master01 ~]# curl 192.20.150.26
    nginx-deployment-d46f5678b-lnxh9
    

    可以看到 Service 成功将请求代理到了后端的一组 Pod,并且进行了流量的分配。

    这是 Service 的默认工作类型,只能在集群所属的节点上访问到,离开集群后无法被访问到。

    这种工作类型叫做 ClusterIP。

    Service 对外提供服务的三种方式

    上一节可以看到,Service 默认不对集群外部提供服务,那么如何才能在集群外部访问到呢,有三种方案。

    externalIPs 方式

    Service 中配置可以 externalIPs,IP 为本集群中 work 节点宿主机 IP。

    apiVersion: v1
    kind: Service
    。。。
    。。。
    spec:
      。。。
      。。。
      externalIPs:
        - 192.168.10.16
        - 192.168.10.17
    

    在 192.168.10.16/17 上执行 ss -lntp 可以看到 Service 定义的暴露端口。
    在集群外部访问 192.168.10.16/17 上 Service 暴露的端口即可。

    NodePort 方式

    改造 nginx-service.yaml,增加一行 type: NodePort

    cat nginx-service.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service
    spec:
      type: NodePort
      selector:
        app: nginx
      ports:
      - port: 80
        targetPort: 80
        protocol: TCP
    

    创建 service

    kubectl apply -f nginx-service.yaml
    

    查看运行情况

    [root@master01 ~]# kubectl get service
    NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
    nginx-service   NodePort    192.20.167.221   <none>        80:30913/TCP   13m
    

    参数 PORT(S) 80:30913/TCP,其中 30913 就是用来集群外部访问的端口。

    可以访问任何一台物理宿主机的 ip:30913 来访问到 Pod。

    30913 是 K8S 从固定范围 30000-32767 中选择的,也可以通过参数 nodePort 指定固定端口。

    可选择范围可以通过 kube-apiserver 的 –service-node-port-range 参数来修改。

    [root@master01 ~]# ss -nltp | grep 30913
    LISTEN     0      128          *:30913                    *:* 
    

    可以看到宿主机上监听了 30913 端口。

    测试

    在没有运行 K8S 集群的机器上访问 K8S 宿主机
    
    [root@192-168-10-18 ~]# curl 192.168.10.12:30913
    nginx-deployment-d46f5678b-2pmts
    [root@192-168-10-18 ~]# curl 192.168.10.12:30913
    nginx-deployment-d46f5678b-zv8m4
    [root@192-168-10-18 ~]# curl 192.168.10.12:30913
    nginx-deployment-d46f5678b-2pmts
    

    可以看到从集群外部可以成功访问到 Pod 中内容,并且为随机分配。

    原理

    kube-proxy 在宿主机上创建了 iptables 规则,对宿主机 IP:30913 的访问将被转发到 Service IP,然后 Service 再通过自己的 iptables 规则分发到 Pod
    

    LoadBalancer 方式

    NodePort 方式中,如果要正式对外提供服务,我们需要在集群外部再创建一个高可用的负载均衡器,以方便把流量转发到宿主机开放的端口上,如果宿主机开放端口发生了变更,我们需要手工修改前端负载均衡器。

    公有云的 LoadBalancer 自动化了这一过程。

    LoadBalancer 这种方式应用于公有云,提交一个 type: LoadBalancer 的 Service 创建申请后,公有云会帮我们创建一个负载均衡器,该负载均衡器会把请求直接分发给 Pod,同时 Pod IP 发生变化后,会自动更新到负载均衡器上。

    其他:Headless Service

    通过指定 spec.clusterIP 的值为 "None" 可以创建 Headless Service。

    Headless Service 不会分配 Cluster IP,kube-proxy 不会处理它们, 而且平台也不会为它们进行负载均衡和路由。

    定义了 selector 的无头服务,Endpoint 控制器会在 API 中创建 Endpoints 记录, 并且修改 DNS 配置返回 A 记录,通过这个地址,请求可以直接到达 Service 的后端 Pod 上。

    结束语

    Service 对 IP 信息易变的 Pod 提供了服务发现、负载均衡等管理功能,同时提供了外部访问的能力,从而使外部用户能够稳定的访问到运行在集群内部的 Pod 提供的服务。

    上面说的三种工作方式有如下问题:

    1. ClusterIP 方式默认工作在集群内部,使用参数 externalIPs 可指定哪个及诶按暴露端口,但无法进行7层的URL跳转等控制
    2. NodePort 方式下,全部节点都会暴露该端口,但一个端口只能对应一个业务,适合业务比较少的环境或者测试环境,业务多了以后无法有效管理
    3. LoadBalance 方式只适合于现有的公有云平台,无法用于自建集群,同时还需要额外费用

    这些问题导致无法直接应用于生产环境中。

    如果想提供给自建集群的生产环境使用,需要在 Service 前面再加一层 Ingress Controller。

    联系我

    微信公众号:zuolinux_com

    微信扫码关注

  • 相关阅读:
    TCP/IP
    Socket通信
    Dubbo详解
    高并发详解
    P3-DataBase
    JAVA基础学习之路(十)this关键字
    [SHELL]输出目录下所有的可执行文件,批量创建用户
    JAVA基础学习之路(八)[1]String类的基本特点
    [MYSQL][2]索引
    [MYSQL][1]创建,修改,删除表
  • 原文地址:https://www.cnblogs.com/zuolinux/p/13693751.html
Copyright © 2020-2023  润新知