Featured image of post Loadbalancer与Ingress

Loadbalancer与Ingress

前言

Kubernetes Service

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

在平时开发或者本地环境中,我们常用的服务类型可能是:ClusterIP, NodePort

但是,还有两种重要的服务类型:

  • LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。
  • ExternalName: 通过返回 CNAME 记录和对应值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。 无需创建任何类型代理。

这里先简单说一下 ExternalName 的使用情景,如下面的 yaml 定义:

apiVersion: v1
kind: Service
metadata:
  name: backend-service
  namespace: default
spec:
  type: ExternalName
  externalName: backend-service.example.svc.cluster.local

与其他 Service 类型不同,ExternalName 类型不通过 Pod 选择器关联 Pod ,而是将 Service 和另外一个域名关联起来。这种方式可以实现:

  • 配置服务管理另一个命名空间中的服务,屏蔽服务在不同命名空间的调用差异,使得两个服务看起来像是在同一个命名空间下;
  • 通过 Service 名称的方式请求外部服务时,例如请求在集群外的数据库服务,也可以使用这种方法
  • 将微服务依赖的中间件地址配置为 ExternalName 类型,进一步把服务和使用的中间件解耦(将一些中间件服务地址直接用集群访问地址表示),在实际部署的时候通过 ExternalName 方式让微服务访问其他中间件

Ingress

“Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。可以提供负载均衡、SSL 终结和基于名称的虚拟托管。”

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

在生产中,Ingress 常与 LoadBalancer 配合使用。

Loadbalancer

Loadbalancer 通过负载均衡器来暴露 Service,通过云厂商的负载均衡器提供的外网 IP 来进行访问业务。

而在实际的业务中,通常将 Loadbalancer 与 Ingress 结合使用。原因有以下几点:

  1. 云厂商的 Loadbalancer 提供的公网 IP 和流量带宽是收费的,通常有多个服务需要暴露,没有必要创建多个 Loadbalancer ,这会增加开销。
  2. 每一个服务都用一个公网 IP 也不灵活。
  3. 而 Ingress 通过配置域名和路径规则,在一个入口统一接收外部请求,能够转发到不同的集群内部服务中,可以灵活实现集群服务的暴露。

Loadbalancer 类型的服务定义如下:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer

它由 type: LoadBalancer 确定。LoadBalancer 的具体实现依赖云厂商。

那么在本地集群想使用 LoadBalancer 怎么办呢?我们都知道 LoadBalancer 都是作为云厂商 IaaS 平台的一部分实现的,如果我们的集群没有使用这些公有云,LoadBalancers 类型的服务就会无限处于 Pending 状态。

MetalLB 解决的就是这个问题。

MetalLB

https://metallb.universe.tf/

目前,对于本地 Kubernetes 集群的一种称呼是 “裸金属集群”,或许 MetalLB 就是这个意思,在裸金属集群上配置本地的 LoadBalancer。它目前是 CNCF 的沙盒项目,使用标准路由协议实现裸金属集群的 load-balancer。

通过 MetalLB,我们在本地集群或者私有云中,也可以使用到 LoadBalancer 的能力。

概念

https://metallb.universe.tf/concepts/

Address Allocation

公有云会为 LoadBalancer 类型的服务自动分配一个 IP 地址。MetalLB 不能凭空把这些地址创建出来,我们需要为 MetalLB 分配一个 IP 地址池,这通常取决于我们的内网环境,例如在 192.168.0.0/24 中分配 192.168.0.128/26 作为可用 IP 地址池。这些本地地址都是内网地址,因此需要符合 RFC 1918 规范。

这样 MetalLB 就可以自动把地址池中的地址分配给服务。

External Announcement

在 LoadBalancer 类型的服务被分到地址之后,我们必须让内网的其他设备知道,这个 IP 的存在。这就像是内网的两个服务器可以知道彼此的 IP 一样。即:此时,这个内网服务器的,k8s集群中的,Service 对外的网络应该表现为,一个内网服务器在网络中的位置

MetalLB 使用标准的网络或路由协议来实现这一点,具体取决于使用哪种模式,如:ARP、NDP或BGP协议。

安装 MetalLB

https://metallb.universe.tf/installation/

前置条件

https://metallb.universe.tf/#requirements

注意 Kubernetes 网络插件的兼容性,常用的网络组件 Cilium、Flannel等都是兼容的。

使用 Manifest 安装

$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml

在对应命名空间下有两种组件:

$ k get po -n metallb-system
NAME                          READY   STATUS    RESTARTS        AGE
controller-7597dd4f7b-kzhqd   1/1     Running   0               7h54m
speaker-g4rhm                 1/1     Running   0               7h54m
speaker-m6xtr                 1/1     Running   1 (6h38m ago)   7h54m
speaker-pmt5w                 1/1     Running   0               7h54m
speaker-wvvz6                 1/1     Running   0               7h54m
  • metallb-system/controller:这是处理 IP 地址分配的,集群维度的控制器。
  • metallb-system/speaker:是 daemonset 类型。这个组件使用我们所选择的协议,使服务可达。

配置二层模式下的 LoadBalancer

这是最简单且常用的模式。我们只需要分配一个 IP 空间作为 IP 池,然后将其告知 L2Advertisement 即可,L2Advertisement 将这个 IP 池发布出去:

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  # 我的内网环境是 192.168.0.0/24
  # 分配地址范围
  - 192.168.0.201-192.168.0.233
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
spec:
  ipAddressPools:
  - first-pool

创建 LoadBalancer 类型的服务

这里使用 Tekton 的 Dashboard 为例:

apiVersion: v1
kind: Service
metadata:
  labels:
	...
  name: tekton-dashboard
  namespace: tekton-pipelines
spec:
  ports:
    - name: http
      port: 9097
      protocol: TCP
      targetPort: 9097
  # 配置为 LoadBalancer 类型
  type: LoadBalancer

创建后:

$ k get svc -n tekton-pipelines
NAME                                TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)                              AGE
tekton-dashboard                    LoadBalancer   10.100.157.67    192.168.0.201   9097:32176/TCP                       18s

Metallb 为其分配了 192.168.0.201 这个地址,有两个端口 909732176。当然,9097 是我们指定的,那 32176 是怎么来的呢?

先试着访问一下 192.168.0.201:9097192.168.0.201:32176

image-20230126203748156

发现,192.168.0.201:32176 这个地址不能访问到我们的服务,那它为什么会存在呢?

LoadBalancer 的端口

为了解决这个疑惑,我重新检查了我的 yaml 文件,发现确实没有 32176 这个端口相关的配置,它像是一个 NodePort 类型服务所有的端口。

在查询文档后,我想应该是这个原因:

“要实现 type: LoadBalancer 的服务,Kubernetes 通常首先进行与请求 type: NodePort 服务等效的更改。 cloud-controller-manager 组件然后配置外部负载均衡器以将流量转发到已分配的节点端口。”

https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#type-nodeport

我们的负载均衡器先是使用一个 NodePort 端口,然后将 LB 服务从 EXTERNAL IP 来的外部流量转发到这个 NodePort ,也就是 32176 中。

为什么需要一个 NodePort 作为 LB 的中间端口呢?如果不用这个端口,那么 LB 会直接将流量转发到 Pod 中。我觉得应该是需要节点的 NodePort 做一层负载均衡吧。

于是,我尝试访问集群中工作节点的 32176,端口,果然,所有节点的该端口都可以访问到服务:

image-20230126205831162

image-20230126205857312

image-20230126205912808

它就是 NodePort。

LB 类型服务将来自 EXTERNAL-IP:Port 的流量,通过 NodePort 负载均衡到 Pod 中。这可能也是 LoadBalancer 名字的意思?

spec:
  ports:
    - name: http
      port: 9097	# EXTERNAL-IP 的端口是这个
      protocol: TCP
      targetPort: 9097

我进一步尝试 禁用负载均衡器节点端口分配,首先把之前的 Service 删除,修改 yaml,再 apply:

apiVersion: v1
kind: Service
metadata:
  ,,,
  name: tekton-dashboard
  namespace: tekton-pipelines
spec:
  # 在 spec 中加上这个字段 不再为其分配 NodePort
  allocateLoadBalancerNodePorts: false
  ports:
    - name: http
      port: 9097
      protocol: TCP
      targetPort: 9097
  type: LoadBalancer
  ...

果然,NodePort 没有了,也进一步验证了之前猜想:

$ k get svc -n tekton-pipelines
NAME                                TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)                              AGE
tekton-dashboard                    LoadBalancer   10.111.86.40     192.168.0.201   9097/TCP                             3s

小结

我们稍微讨论了关于 Loadbalancer 这一类型的服务,包括:

  • 本地集群/裸金属/私有云通过 MetaLB 实现 Loadbalancer;
  • MetaLB 的部署、配置、使用;
  • Loadbalancer 它的两个端口,以及通过 NodePort 进行负载均衡;
  • 还有不常用的 ExternalName 类型服务。

Ingress

从官网这张图来看,Ingress 的作用就比较清晰了,通过一组路由规则,将集群外的请求负载均衡到内部服务,实现集群流量统一接入。

我们将上文中 tekton-dashboard 服务修改为 ClusterIP,然后通过 Ingress 暴露服务,完成我们这部分的实践。

$ k get svc -n tekton-pipelines
NAME                                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                              AGE
tekton-dashboard                    ClusterIP   10.101.20.90     <none>        9097/TCP                             5s

这里使用 Ingress-Nginx 作为我们集群的 ingress。

Ingress-Nginx

安装 Ingress-Nginx

Nginx 实现的 Ingress Controller 提供了 Cloud 和 Metal 版本的 manifest。主要的区别是 Cloud 版本使用 LB 暴露服务,而 Metal 使用 NodePort 暴露服务。如果你跟随上文,部署了 MetaLB 在本地集群,就推荐使用 Cloud 版本的 manifest。

注意,原始 manifest 中包含 registry.k8s.io 的镜像,可能不方便拉取,我更换了 docker hub 中对应的镜像,主要有三处地方,共两个镜像:

  • dyrnq/ingress-nginx-controller:v1.4.0
  • liangjw/kube-webhook-certgen:v1.1.1
$ kubectl apply -f https://ghproxy.com/https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.4.0/deploy/static/provider/cloud/deploy.yam

如果镜像无法拉取,注意修改其中的镜像仓库。

$ k get po -n ingress-nginx
NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-ptg6f        0/1     Completed   0          19m
ingress-nginx-admission-patch-lndpj         0/1     Completed   0          19m
ingress-nginx-controller-8445cd49cf-l4q4r   1/1     Running     0          19m
$ k get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.106.83.156   192.168.0.201   80:31527/TCP,443:32376/TCP   20m
ingress-nginx-controller-admission   ClusterIP      10.97.1.125     <none>          443/TCP                      20m

部署完成后,看到 LB 分配的 IP 是 192.168.0.201。接下来我们配置 ingress,从 192.168.0.201:80 端口访问我们想要的服务。

配置 Ingress 暴露 Tekton Dashboard 服务

首先确保我们的 Tekton Dashboard 服务以及创建。

$ k get svc -n tekton-pipelines
NAME                                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                              AGE
tekton-dashboard                    ClusterIP   10.101.20.90     <none>        9097/TCP                             5s

一个 Ingress 的配置文件如下:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-resource
  namespace: tekton-pipelines
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  rules:
  	# 我们为其设置了域名 但是公网 DNS 可不会解析它
  	# 需要在我们本地配置 host 来正确解析
    - host: tekton.k8s.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: tekton-dashboard
                port:
                  number: 9097

部署:

$ k apply -f dashboard-ingress.yaml

部署完成之后,我们需要在本地配置域名解析,让 tekton.k8s.local 能够解析到 192.168.0.201

然后通过浏览器访问 http://tekton.k8s.local/。在浏览器中默认解析到 192.168.0.201:80 的根 / 路由路径下。

image-20230126223316998

这样就实现了通过 ingress 暴露集群内部服务。

关于配置 host

我们在 ingress 的配置中指定了 host 字段,并配置了本地域名解析去访问这个域名:

spec:
  rules:
    - host: tekton.k8s.local
	  ...

虽然我们知道 ingress 暴露的 IP 是 192.168.0.201:80 ,但是直接访问这个地址并不能找到服务:

image-20230126224456243

如果我们先通过 IP:Port 的形式,而不通过域名,就不能在 spec.rules 中为其配置 host,也就是使用这种配置:

https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/#ingress-rules

“未指定 host 的规则适用于通过指定 IP 地址的所有入站 HTTP 通信”。

apiVersion: networking.k8s.io/v1
kind: Ingress
...
spec:
  rules:
  	# 不指定 host
    #- host: tekton.k8s.local
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: tekton-dashboard
                port:
                  number: 9097

这样进行部署,就可以直接通过 ingress 控制器的 LB 地址和端口 (默认 http 为 80),从其根路径下访问到我们的服务了。

image-20230126223824349

小结

本部分我们简单介绍和实践了 Ingress,主要有以下内容:

  • 简要阐述 cloud 和 metal 环境下,ingress-nginx 控制器 service 类型的不同;

  • 安装 LoadBalancer 类型的 ingress-nginx 控制器;

  • 为 ClusterIP 类型的 Tekton Dashboard 服务配置 ingress 规则;

  • 关于配置 ingress 中 host,使用 域名/IP 访问的小坑。

自认为是幻象波普星的来客
Built with Hugo
主题 StackJimmy 设计