k8s☞08负载均衡service

阅读量: zyh 2020-09-16 17:44:04
Categories: > Tags:

前言

在实际业务中,因业务压力问题,经常会有多个后端服务副本,它们共同承担请求.我们使用云服务的时候,可以购买阿里云的slb或者aws的elb/alb等负载均衡器向这些后端副本分发流量. 并通过这些负载均衡向公/内网发布后端程序。

k8s设计了一个service对象来实现这一目的.

k8sIP

在提Service对象之前,需要先知道k8s中存在的三种IP.即 NodeIP, ClusterIP, PodIP

NodeIP 就是物理节点ip, 这个没得说, 玩家自己定义

ClusterIP 是k8s的一个虚拟ip, 本身没有任何实体, 也就是VIP

PodIP 是容器共享的一个网络命名空间对应的ip, 一个pod里的容器共用

与kube-proxy

kube-proxy 是实现 svc 的重要组件,kube-proxy 通过代理方式转发流量到Pod。其代理方式分三种:namespace 方式,iptables 方式,ipvs方式。基本用的都是 ipvs 方式。

ipvs方式的前置要求是 ipvs 组件,如果 kube-proxy 没有检测到 ipvs 组件则会回退到 iptables 方式,如果依然不合适,则继续回退到 namespace。

类型

https://kubernetes.io/zh/docs/concepts/services-networking/service/

https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/

Service对象通过spec.type来设定类型, 共计4个类型: ClusterIP, NodePort, LoadBalancer, ExternalName. 这四个类型可以分为两类

ClusterIP

ClusterIP(默认类型): 反向代理集群内的pod. 供集群内其它服务访问. 流量过程是: 集群内部其它服务=>svc_name=>ClusterIP:端口=>Pod

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

spec.selector:选择器,选择目标pod的标签

spec.ports:

  • name 是端口名,由小写字母、数字、-组成

  • port 是Service暴露端口

  • targetPort是pod暴露端口. 默认情况下, targetPort将等于port

ℹ️若spec.type没有定义,则默认是ClusterIP

ExternalName

ExternalName: 构建一个CNAME解析(service对象-CNAME-其它域名). 我能想到的主要是让集群内部服务可以访问到集群外部服务.

例如:

kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

集群内部访问my-service.prod的时候, 将通过k8s的dns服务返回my.database.example.com

NodePort

NodePort: 反向代理集群内的pod(使用NAT在每一个集群node上的相同端口上公开Service, 是【ClusterIP类型的超集】). 供集群外服务访问. 流量过程是: 集群外=>任意节点ip:端口=>svc_name=>ClusterIP:端口=>Pod

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

集群外部此时可以访问任意物理节点ip:31000, 此时可以访问到集群内部app=myapp的pod.

这里nodePort是物理节点暴露端口, port是Service暴露端口, targetPort是pod暴露端口.

nodePort 可以不定义, 会自动从30000-32767随机分配.其范围的修改需要指定 apiserver 组件的--service-node-port-range

LoadBalancer

LoadBalancer: 对接云商的负载均衡服务, 给Service分配一个固定IP. 方便云服务商的LB服务绑定. 【是NodePort类型的超集。 流量过程是: 集群外=>云服务LB=>任意物理节点ip:端口=>svc_name=>ClusterIP:端口=>Pod

这种类型建议直接参考官方文档: https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer

Headless Service

正常情况下,dns服务会将svc的域名解析为clusterIP(vip),然后通过代理方式将到达vip的流量转发到endpoints列表。

而Headless类型下,svc没有vip,也没有转发规则,而是通过A记录解析将svc_name.ns_name直接解析为endpoints列表,即直达pod。

不同点:

常见于分布式应用部署场景,例如StatefulSet 控制器使用 Headless 结合Pod标识构建 Pod 唯一子域名,具体如何使用参考 StatefulSet,这里暂不考虑。

粘性会话

有些时候,我们需要会话黏性,你可以通过service.spec.sessionAffinity=ClientIP来设置.并同时可以通过service.spec.sessionAffinityConfig.clientIP.timeoutSeconds来设置会话保持时间,它默认是3小时.

服务发现

Pod 可以通过两种方式发现svc。

DNS

https://kubernetes.io/zh/docs/concepts/services-networking/service/#dns

Pod可以发现集群任何位置的svc

不同ns下,你可以通过svc_name.ns_name访问 svc

相同ns下,可只通过svc_name访问 svc

环境变量

Pod仅可以发现相同ns下的svc

https://kubernetes.io/docs/concepts/services-networking/service/#environment-variables

当service创建的时候,kubelet会生成一批环境变量。例如,svc 名称是 myservice,ClusterIP是10.0.0.11,暴漏的端口是6379,则生成以下环境变量:

MYSERVICE_SERVICE_HOST=10.0.0.11
MYSERVICE_SERVICE_PORT=6379
MYSERVICE_PORT=tcp://10.0.0.11:6379
MYSERVICE_PORT_6379_TCP=tcp://10.0.0.11:6379
MYSERVICE_PORT_6379_TCP_PROTO=tcp
MYSERVICE_PORT_6379_TCP_PORT=6379
MYSERVICE_PORT_6379_TCP_ADDR=10.0.0.11

ℹ️ 则不管你使用哪种方式,都应该提前通过init容器来校验svc对象已成功创建,例如通过until死循环来判断解析是否成功。

其它暴露方式

除了 svc 暴露服务,还可以通过 ingress 暴露服务,它是充当集群入口,可以在单ip下暴露多服务。这里暂不讨论。