k8s-service


1. 简介

kubernets service 是将运行一组pods上的应用程序公开为网络服务的抽象方法。

有了 kubernets service,你就无需修改应用程序即可使用服务发现机制,kubernets 为 pods 提供自己的ip地址,并为一组pod提供相同的DNS名,并且可以在它们之间进行负载均衡。

2. 为什么要用services

创建和销毁 kubernets pod 以匹配集群状态。pod 是非永久性资源。如果你使用 Deployment 来运行你的应用程序,则它可以动态创建和销毁 Pod。

每个 Pod 都有自己的 IP 地址,但是在 Deployment 中,在同一时刻运行的 Pod 集合可能与稍后运行该应用程序的 Pod 集合不同。

这导致了一个问题: 如果一组 Pod(称为“后端”)为集群内的其他 Pod(称为“前端”)提供功能, 那么前端如何找出并跟踪要连接的 IP 地址,以便前端可以使用提供工作负载的后端部分?

3. quick start

3.1 创建svc

资源模板 svc-deploy-nginx.yaml信息如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-deploy-test
  name: nginx-test-1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-test
  template:
    metadata:
      labels:
        app: nginx-test
    spec:
      containers:
      - image: nginx:latest
        name: nginx
        imagePullPolicy: IfNotPresent

---

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    # must be match pod template .spec.template.labels
    app: nginx-test
  ports:
    - protocol: TCP
      port: 8000
      targetPort: 80

创建一个deploy,和一个 svc

注意 svc selector 对应的是 deploy 的 .spec.template.labels

创建资源

$ kubectl create -f svc-deploy-nginx.yaml

3.2 查看svc

  1. 使用资源文件查看

    $ kubectl get  -f svc-deploy-nginx.yaml -o wide
    # 输出内容如下
    # deploy 信息
    NAME                           READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES         SELECTOR
    deployment.apps/nginx-test-1   3/3     3            3           42m   nginx        nginx:latest   app=nginx-test
    # svc 信息
    NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE   SELECTOR
    service/my-service   ClusterIP   10.96.112.5           8000/TCP   42m   app=nginx-test
    
  2. 使用命令查看

    查看svc 信息

    $ kubectl get svc/my-service -owide
    NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE   SELECTOR
    my-service   ClusterIP   10.96.112.5           8000/TCP   45m   app=nginx-test
    

    查看endpoints信息

    可以看到成功的绑定了三个pod endpoint

    $ kubectl get ep/my-service
    NAME         ENDPOINTS                                               AGE
    my-service   10.100.132.133:80,10.100.132.139:80,10.100.132.140:80   46m
    

    查看svc 详细信息

    $ kubectl describe svc/my-service
    Name:              my-service
    Namespace:         default
    Labels:            
    Annotations:       
    Selector:          app=nginx-test
    Type:              ClusterIP
    IP:                10.96.112.5
    Port:                8000/TCP
    TargetPort:        80/TCP
    Endpoints:         10.100.132.133:80,10.100.132.139:80,10.100.132.140:80
    Session Affinity:  None
    Events:            
    

3.3 访问svc。
  • 集群外部的资源无法访问ClusterIp类型的svc。
  • 4.2 NodePort

    node(节点)port(端口),顾名思义 NodePort类型的svc是通过在集群的宿主机上开放访问的端口,并通过 <节点 IP>:<节点端口>(集群任意节点ip:nodeport 都可以访问)来实现从集群外部访问其svc的。

    nodePort 的原理在于在 node 上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy 进一步到给对应的 pod。(能够将内部服务暴露给外部的一种方式)

    1. 创建资源

      一个deploy 和 一个 nodeport svc,且指定了node 端口为30007(如果不指定,k8s会默认分配一个)

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        labels:
          app: nginx-deploy-test
        name: nginx-test-2
      spec:
        replicas: 3
        selector:
          matchLabels:
            app: nginx-test-02
        template:
          metadata:
            labels:
              app: nginx-test-02
          spec:
            containers:
            - image: nginx:latest
              name: nginx
              imagePullPolicy: IfNotPresent
      
      ---
      
      apiVersion: v1
      kind: Service
      metadata:
        name: my-service-2
      spec:
        type: NodePort
        selector:
          app: nginx-test-02
        ports:
          - protocol: TCP
            port: 8000
            targetPort: 80
            # 可选字段
            # 默认情况下,为了方便起见,Kubernetes 会从范围内分配一个端口号(默认:30000-32767)
            nodePort: 30007
      
    2. 访问svc

    4.3 LoadBalancer

    使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。

    4.4 ExternalName类型的服务,查找其 CNAME 记录
  • 对所有其他类型的服务,查找与 Service 名称相同的任何 Endpoints 的记录
  • 关于Headless Services 在博文中 有详细的记录,感兴趣的可以访问了解下。

    5. externalIPs

    Service 的 externalIPs(spec.externalIPs)可以设置一组外部的 IP 地址,并且将流量导入到集群内部。

    如图:

    启动一个springboot项目 并初始化一个接口,访问后返回 Hello World~

    在本机上访问:

    接下来我们就使用externalIPs192.168.0.101流量导入到集群内部

    目标:集群pod中访问192.168.0.101:8080时不是访问此时的这个springboot项目,而是访问集群内的externalIPs svc

    1. 首先到集群master节点访问一下springboot项目

      访问成功

      $ curl 192.168.0.101:8080
      Hello World~
      
    2. 创建externalIPs svc,和想导入到集群的目标pod

      本实例主要是为了展示流量的切换,但其实spec.externalIPs 可以指定符合ip 规则的任意地址(即使设置的ip不可达),k8s都会帮你 在访问设置地址的服务时,将流量导入到spec.selector的服务中去

      实际上就是 如果你把spec.externalIPs地址设置成123.123.123.123,当你在服务中访问123.123.123.123时,其实访问的是spec.selector指定的服务

      其实更像是 给spec.selector到服务指定了一个 ipv4 的别名

      apiVersion: v1
      kind: Service
      metadata:
        name: nginx
        labels:
          app: nginx
      spec:
        ports:
        - port: 8080
          targetPort: 80
          name: web
        externalIPs:
        - "192.168.0.101"
        # 将 192.168.0.101 流量导入到 下面的Deployment中
        selector:
          app: nginx
          
      ---
      
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: web
      spec:
        replicas: 2
        selector:
          matchLabels:
            app: nginx
        template:
          metadata:
            labels:
              app: nginx
          spec:
            containers:
            - name: nginx
              image: nginx
              ports:
              - containerPort: 80
                name: web
      

      服务信息如下:

      $ kubectl get deploy -owide
      NAME   READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES   SELECTOR
      web    2/2     2            2           3h25m   nginx        nginx    app=nginx
      
      $ kubectl get svc/nginx -owide
      NAME         TYPE        CLUSTER-IP     EXTERNAL-IP     PORT(S)    AGE    SELECTOR
      nginx        ClusterIP   10.96.41.170   192.168.0.101   8080/TCP   4m     app=nginx
      
    3. 创建一个pod 并进入pod ,在pod中访问192.168.0.101:8080查看访问的是外部的springboot项目还是集群内的deploy/web

      很显然:k8s成功的将流量导入到了集群内部

      如果在集群节点上访问192.168.0.101:8080,是不能将结果导入到deploy上的,只有在集群服务内部访问才有效

    4. 此时我们将svc/nginx 删除掉,看下访问的结果

      符合预期

    6. 管理外部的服务

    场景:如果我们的中间件或者数据库服务不是在k8s集群内的,而是在其他的服务器上,但是我们还是想通过访问svc的方式去访问这些外部的服务,我们应该怎么做呢?

    1. 声明一个没有选择符的svc
    2. 创建一个同名(和第一步中的svc name 相同)的 endpoints 服务去管理这些外部的服务地址

    实例:

    实例通过创建svc 和 endpoints,实现访问svc-name,svc 将流量导出到外部的springboot(项目信息如5.0中提到的)项目中

    1. 创建服务

      apiVersion: v1
      kind: Service
      metadata:
        name: my-service
      spec:
        ports:
          - protocol: TCP
            port: 80
            targetPort: 8080
      
      ---
      
      apiVersion: v1
      kind: Endpoints
      metadata:
        # must be match svc-name
        name: my-service
      subsets:
        - addresses:
            # 指向外部的springboot 服务
            - ip: 192.168.0.101
          ports:
            - port: 8080
      
    2. 访问测试

    7. 选择自己的 IP 地址

    Service 创建的请求中,可以通过设置 spec.clusterIP 字段来指定自己的集群 IP 地址。