vlambda博客
学习文章列表

K8S服务暴露: HAProxy在RDS场景下的妙用




作者  谢钢鹏 · 沃趣科技高级开发工程师

出品  沃趣科技

K8S服务暴露: HAProxy在RDS场景下的妙用


01

 前  言 



随着kubernetes的飞速发展,越来越多的公司,团队将服务以容器化的形式部署在k8s集群上。在集群内成百上千的服务里,总会有一些作为交互入口需要对用户提供服务,但集群本身是一个封闭的网络环境,如何将集群内部的服务暴露出来供人使用便成了一个值得讨论的话题。


02

 概  念 



POD
用户在集群内运行的服务可以被抽象为pod。Pod是 Kubernetes 应用程序的基本执行单元,它是 Kubernetes 对象模型中创建或部署的最小和最简单的单元。Pod 表示在集群上运行的进程。

K8S服务暴露: HAProxy在RDS场景下的妙用

Services
每个 Pod 都有自己的 IP 地址,这导致了一个问题:如果一组 Pod(称为“后端”)为群集内的其他 Pod(称为“前端”)提供功能,那么前端如何找出并跟踪要连接的 IP 地址,以便前端确保可以访问到正确的后端?
Kubernetes Service定义了这样一种抽象:逻辑上的一组Pod,一种可以访问它们的策略 —— 通常称为微服务。这一组Pod能够被Service访问到。
举个例子,考虑一个图片处理 backend,它运行了3个副本。这些副本是可互换的 —— frontend 不需要关心它们调用了哪个 backend 副本。然而组成这一组 backend 程序的Pod实际上可能会发生变化,frontend 客户端不应该也没必要知道,而且也不需要跟踪这一组 backend 的状态。Service定义的抽象能够解耦这种关联。

K8S服务暴露: HAProxy在RDS场景下的妙用 

简言之,通过对POD和Services关系的阐述,我们将k8s服务暴露的问题简化为:如何让外部用户可以访问到集群内的Service? 
官方提供了NodePort,LoadBalancer形式让Service对外暴露,两种方式配置起来相对简便,但各有短板。
NodePort存在端口上限的瓶颈,虽然可以更改配置扩容,但由于实现方式的问题,NodePort的存在不可忽视的性能损耗。
而LoadBalancer需要云提供商支持,在私有云环境下无法实现(况且云提供商的lb服务并不免费!)。

除此之外,官方还提供了第三种方式,也是相对DIY的一种实现Ingress,接下来将对ingress进行详细展开。


03

 Ingress  



什么是ingress?
官方对此这样描述:
Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. Traffic routing is controlled by rules defined on the Ingress resource.
    internet
          |
   [ Ingress ]
   --|-----|--
   [ Services ]
An Ingress may be configured to give Services externally-reachable URLs, load balance traffic, terminate SSL / TLS, and offer name based virtual hosting. An Ingress controller is responsible for fulfilling the Ingress, usually with a load balancer, though it may also configure your edge router or additional frontends to help handle the traffic.
An Ingress does not expose arbitrary ports or protocols. Exposing services other than HTTP and HTTPS to the internet typically uses a service of type Service.Type=NodePort or Service.Type=LoadBalancer .
简而言之,通过ingress的形式暴露服务只需两个条件:
1、创建Ingress资源,该资源声明了转发规则,即流量到达ingress后将会转发到哪个(哪些)service。
2、搭建一个Ingress Controller监听ingress资源,并实现ingress声明的转发规则。
相比于NodePort和LoadBalancer,ingress的配置可能相对复杂,但带来的强大功能会完全掩盖开发部署的繁琐。
有哪些Ingress Controller可供选择?
Nginx Ingress Controller
作为反代巨头,nginx提供的Ingress Controller被官方作为默认的L7 Ingress Controller实现。

K8S服务暴露: HAProxy在RDS场景下的妙用

Traefik Ingress Controller
Traefik作为边缘路由新贵,凭借他和容器平台的良好适性(go编写)以及创新的配置读取功能(其他代理仅支持纯文本配置)在容器领域获取了大量的簇拥,我自己也是Traefik粉丝之一。
K8S服务暴露: HAProxy在RDS场景下的妙用
上述两种Ingress Controller在L7代理方面非常稳定高效且功能齐全,完全可以在生产环境使用。但在RDS场景下,或说在TCP长连接需求的场景下却显得不尽人意:
  • Nginx在热加载配置时将会断开所有已经建立的连接,这在RDS场景下肯定是不允许的,试想一个客户创建了一个新的RDS集群,结果其他客户创建的几百个集群全部连接中断。即使是瞬时的中断也会对业务造成不良影响。
  • Traefik在2019年末更新的大版本v2.0上推出了对tcp的支持,功能方面非常稳定高效。但由于traefik的架构限制,用户无法在运行时新增或删除一个服务器端口。这意味着需要在一开始就根据集群的规模来确定需要监听n个端口,并且在运行时无法变更端口。这其实违背了k8s作为容器编排工具动态扩缩容的初衷。

04

  HAProxy Ingress Controller  



上述内容为我们引出了本文的主角haproxy
什么是HAProxy?
The world's fastest and most widely used software load balancer. -- haproxy.com
为什么选用HAProxy?
1、HAProxy支持动态配置加载. 用户只需将变更后的配置文件提交给HAProxy, 就可以实现无缝的规则变更.
2、HAProxy支持Hitless Reload.。顾名思义,在HAProxy热加载过程中,单一配置的变更不会影响其他无关的配置。HAProxy通过reusesocket的形式,将old process持有的已经建立的连接fd传递给new process,对于使用者来说整个热加载的过程是无感知的。
3、按需监听端口。只有在配置中显示bind端口时,HAProxy才会去监听宿主机上的端口。且相较于NodePort,haproxy只在任意指定的机器上暴露了端口。
上述三点满足了RDS进行服务暴露的需求。
如何实现HAProxy Ingress Controller?
HAProxy Ingress Controller由两部分组成,HAProxy和Ingress Controller。

K8S服务暴露: HAProxy在RDS场景下的妙用

 
Ingress Controller是一个由go编写的程序,通过监听service资源来发现用户需要暴露的服务,例如:
# mysql-svc.yaml
 apiVersion: v1
 kind: Service
 metadata:
  annotations:
    haproxy-ingress/3306"26735" # 为service3306端口分配了宿主机26735端口, 由Ingress Controller写入
    ingress: haproxy-ingress # 指定ingress类型为haproxy, Ingress Controller将发现此标记并为service分配端口
  labels:    
    app: mysqlcluster
  name: mysqlcluster
  namespacedefault
 spec:
  ports:
  - name: mysql
    port: 3306
    protocol: TCP
    targetPort: 3306
  selector:
    Name: mysqlcluster
  type: ClusterIP

在上面的mysql-svc.yaml中声明了一个service,,并在annotation中打了ingress=haproxy-ingress标记。Ingress Controller监听了service资源的变更,当发现带有指定标记的service配置出现,就在预先指定的端口池中找到一个(多个)空闲的端口并分配给service。

如图所示, 整个流程可以整理如下:
1、用户创建service资源,并打上ingress=haproxy-ingress标记。
2、Ingress Controller监听到service资源的变更,并根据标记得知该service需要haproxy进行服务暴露。
3、Ingress Controller遍历service spec中声明的ports,并从端口池中取出等量空闲宿主机port分配。
4、Ingress Controller将处理后的service配置通过jinja2模板转换为haproxy配置文件。
5、发送信号通知haproxy进行热加载,此过程中haproxy监听了新的宿主机端口并将流量导向service中指定的服务。
流程结束后, 用户即可通过haproxy所在的机器(负载机器)的ip+分配的端口访问到自己的服务。
更进一步

通过将HAProxy Ingress Controller部署在多台宿主机上,在前端通过keepalived/f5等手段进行机器级别的负载来达到高可用。


05

  总  结  



由于RDS需求的特殊网络环境,k8s提供的常规服务暴露手段很难完全契合业务需求。HAProxy作为一个20年老品牌负载均衡器为我们提供了必要的网络技术支持,结合依赖k8s标准包开发的Ingress Controller,让我们用更超然的手段实现了RDS的服务暴露。

references

k8s official:https://kubernetes.io 
ingress-controller: https://github.com/jcmoraisjr/haproxy-ingress

| 作者简介

谢钢鹏·沃趣科技高级开发工程师

沃趣QFusion项目组研发,云原生技术求道者,致力于容器和云技术的推广。多年golang及python编程经验,主要司职服务端开发,目前着眼于RDS相关技术研究,kubernetes crd控制器编写。

相关链接



更多干货,欢迎来撩~