IT 铁人赛 k8s 入门30天 -- day14 K8s Services explained

前言

这篇文章主要来介绍 Service 元件

内容会谈到 Service 的功能与不同种 Services 类型与适合使用时机

Service

在 k8s 丛集里, 每个 Pod 都有自己的内部 IP

内部 IP 是在 Pod 启动时动态给予的

因此, 当 Pod 重起之後内部 IP 的就会跟原本的不同

所以使用内部 IP 来存取 Pod 不是个好方法

Service 元件提供了一个固定 IP 给 Pod

所以一旦让 Pod 绑定 Service

就可以透过这个固定 IP 来存取 Pod

除了提供固定 IP 外, 当多个 Pod 挂载在同一个 Service 上时

Service 会对传入的 request 做负载平衡

由於 Pod 绑定 Service 所以对於其他相关的服务只依赖於 Service 这个抽象层

所以对 Pod 间做到了松耦合

而对於 Pod 来说对内与对外的接口都是透过 Service 来做处理

在 k8s 丛集中, Service 类别分为好几种, 以下就分别来介绍几种比较常见的

ClusterIP Service

k8s 丛集, 预设的 Service 就是 ClusterIP Service

设定档范例如下:

apiVersion: v1
kind: Service
metadata:
  name: microservice-one-service
spec:
  selector:
    app: microservice-one
  ports:
    - protocol: TCP
      port: 3200
      targetPort: 3000

上面设定档, 如果 kind 部份直接设定成 Service

则预设就是给 ClusterIP Service

而实际上关於 ClusterIP Service 给的固定 IP 值是看 Pode 所处的结点 IP 范围

如下图所示:

如果想要查询 Pod 所对应的 IP 可以使用以下指令

kubectl get pod -o wide

假设给定 Ingress, Service 与 Deployment 设定
ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress-no-third-host
spec:
  rules:
  - host: analytics.myapp.com
    http:
      paths:
      - path: "/"
        path: Prefix
        backend:
          service:
            name: microservice-one-service
            port:
              number: 3200

service-clusterIP.yaml 如下

apiVersion: v1
kind: Service:
metadata:
  name: microservice-one-service
spec:
  selector:
    app: microservice-one
  ports:
    - portocol: TCP
      port: 3200
      targetPort: 3000

deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: v
spec:
  replicas: 2
  ...
  template:
    metadata:
      labels:
        app: microservice-one

则 ingress 透过 Service 的 name 找到到相对应的路由

而 Service 透过 selector 比对 Deployment 中的 Pod 对应的 labels 找到对应的 Pod

举例来说: 上面的 Service selector 为 app: microservice

而 Deployment 中 template 内的 metadata labels 也是相同的值

而 Service 要路由到 Pod 的 Port 则定义在 Service 的 TargetPort

封包路由如下图:

EndPoint

当 k8s 丛集建立 Service, 同时也会建立 Endpoint 物件

EndPoint 物件会跟 Service 具有相同名称, 除了 IP 资讯外多了一个 Port 资讯

下面是 k8s 官网 EndPoint 物件的设定档范例

apiVersion: v1
kind: Endpoints
metadata:
  name: my-service
subsets:
  - addresses:
      - ip: 192.0.2.42
    ports:
      - port: 9376

EndPoint 物件用来纪录哪个 Pod 属於哪一个 Service

Service 定义的对外的 Port 可以由使用者定义

targetPort 则必须要开放到 Pod 内 Container 实际服务的 Port

Muti-Port Services

这类的 Service 代表同时会开放对应到两个 Port

举例来说: 假设 mongodb 除了本身外还有一个 mongodb-exporter 需要开发给 Prometheus app 存取, 设定档如下

apiVersion: v1
kind: Service
metadata:
  name: mongodb-service
spec:
  selector:
    app: mongodb
  ports:
    - name: mongodb
      protocol: 27017
      targetPort: 27017
    - name: mongodb-exporter
      protocol: 9216
      targetPort: 9216 

注意的是, 如果同时服务两个以上的 Port 就必须要设定名称

Headless Service

Headless 只会连接一个 Pod

设定档范例如下:

apiVersion: v1
kind: Service
metadata:
  name: mongodb-service
  selector:
    app: mongodb
  ports:
    - protocol: TCP
      port: 27017
      targetPort: 27017

当不需要做平衡负载时, 就可以使用 Headless Service

使用时机当想要只跟某个一特定的 Pod 做通讯

这种 Service 会使用在有状态的应用, 比如 资料库(mysql, mongodb), elastic search

因为有状态的应用每个 Pod 并非相同, 像是资料库只有 master Pod 才能做写入

这类 Service 的 IP 会直接回应 Pod IP address

Sevice type 设定值

当要建立 Service 时, type 有三种值可以设定 ClusterIP, NodePort, LoadBalancer

ClusterIP

是 type 的预设值, 不需要特别设定

属於内部 Service, 外面读取不到

范例如下:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: ClusterIP

NodePort

就是对於每个结点 IP 开一个固定的 Port 来做端点

这类 Service 可以从外部经过以下形式的路径来存取:

NodeIP: 开启的 Port

设定档范例如下:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort

假设一个 NodePort Service 定义如下:

apiVersion: v1
kind: Service
metadata:
  name: ms-service-nodeport
spec:
  type: NodePort
  selector: 
    app: microservice-one
  ports:
    - protocol: TCP
      port: 3200
      targetPort: 3000
      nodePort: 30008

则代表这个 microservice-one 服务的 Port 在定义的 nodePort 30008

而这个 NodePort 有一个范围限定 30000-32767

然而, NodePort Service 因为直接开放结点的 Port 给外部来连接

因此不是一个安全的作法

LoadBalancer

LoadBalancer 类别让 Service 可以透过 cloud provider 的负载平衡器来对外开放服务

其结构概念如下:

由结构图可以发现当 type 为 LoadBalancer Service 建立时, 相对应的 NodePort Service 与 ClusterIP Service 也会自动建立

设定档范例如下

apiVersion: v1
kind: Service
metadata:
  name: my-service-loadbalancer
spec:
  type: LoadBalancer
  selector:
    app: microservice-one
  ports:
    - protocol: TCP
      port: 3200
      targetPort: 3000
      nodePort: 30010

注意的是, 这边的 nodePort 只开放给 loadBalancer 存取

loadBalancer 透过 nodePort 传递封包到结点然後透过对应的 ClusterIP Service 传递到对应的 Pod

後记

一般而言, 实务上不太会使用 NodePort Service 在正式环境上, NodePort Service 只会在测试阶段拿来验证服务
在 k8s 丛集, 用来把应用对外开放的元件一般来说是使用 loadBalancer 类型的 Service 外面对接 cloud provider 提供的 loadbalancer 或是直接使用 Ingress 对接 ClusterIP Service 来使用


<<:  DAY28 进行式--工作日志003

>>:  Day13 - [丰收款] 将Django程序,放上Heroku!

ISO 27001 资讯安全管理系统 【解析】(十二)

资通安全责任等级 依照资通安全责任等级分级办法,由主管机关核定相对应之等级,按照等级决定导入系统之...

【Day 24】半监督式学习(Semi-supervised Learning)(上)

我们知道监督式学习(Supervised Learning)就是有一堆Label好的训练资料,而半监...

中台的技术系统(Technology System)

中台作为一种生态系统层级的架构,倚赖业界主流的技术系统,包含开源技术平台与框架: 业务中台:微服务─...

【Day01】楔子-关於永丰金融APIs

iT邦帮忙一直以来都是我查询技术问题的好夥伴;而铁人赛为IT界名闻遐迩的年度盛事。 在友人极力鼓吹报...

Vue [笔记] Dom元素无生成完毕、API来不及抓取之处理、传值方式

1. Dom元素无生成完毕,使用this.$nextTick 情境:Dom元素无生成 导致 refs...