中间件容器化部署是为了实现GitOps模式的持续交付,实现部署即代码。痛点在于大多数中间件都是有状态的,本篇介绍如何实现有状态中间件的容器化部署。
常见中间件要实现容器化部署,需要解决以下问题:
- 对于网关类中间件,作为流量入口,虽然是无状态类型的中间件,但由于需要提供固定ip配置SLB,因此容器化部署就需要解决固定ip的问题。
- 对于存储类中间件,是有状态类型的中间件,容器重启后需要能访问同一个“持久化存储设备”,数据不容忍丢失,因此容器化部署需要解决持久化存储问题。
网关项目容器化部署解决方案
关于Ingress Controller
Ingress是k8s提供将集群内部服务(Service)暴露外部访问的定义http路由规则的资源,Ingress资源本身只是定义http路由策略,需要依赖Ingress Controller实现Ingress资源的路由策略。
Ingress Controller本身也是一个Service,通过设置Service的类型为NodePort(使用节点IP)或者ExternalIP、云服务提供商提供的LoadBalancer暴露给外部访问。
BFE Ingress Controller
BFE Ingress Controller是基于BFE实现的Ingress Controller,用于支持在Kubernetes中部署使用BFE进行七层流量代理转发,并能使用Ingress进行流量接入。
BFE Ingress Controller通过监听Service、Endpoints、Secrets、Namespace资源实现服务发现,TLS证书配置;通过监听Ingress资源触发生成新的配置文件并Reload,将Ingress资源定义的http/https路由规则转为bfe的路由配置,并通过调用bfe暴露的基于http协议的local reload接口通知bfe进程刷新配置。
示例:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-http-ingress
annotations:
kubernetes.io/ingress.class: bfe
spec:
tls:
- hosts:
- test.com
secretName: test-com-tls-secret
rules:
- host: test.com
http:
paths:
- path: /api/user
pathType: Prefix
backend:
service:
name: test-user
port:
number: 8080
此示例的意思是,配置一个Ingress资源,名称为test-http-ingress,ingress类型是bfe(标识该Ingress由BFE Ingress Controller处理),配置了一个host路由规则,将通过test.com域名访问,且路径前缀为/api/user的http请求转发给test-user服务的8080端口。还为test.com配置了tls证书,对应的secret资源名称为test-com-tls-secret。
Nginx Ingress Controller
Nginx Ingress Controller是基于Nginx实现的Ingress Controller,用于支持在Kubernetes中部署使用Nginx进行流量代理转发,并能使用Ingress进行流量接入。原理同前面介绍的BFE Ingress Controller。
示例:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-http-ingress
annotations:
kubernetes.io/ingress.class: nginx
spec:
tls:
- hosts:
- test.com
secretName: test-com-tls-secret
rules:
- host: test.com
http:
paths:
- path: /api/user
pathType: Prefix
backend:
service:
name: test-user
port:
number: 8080
此示例的意思是,配置一个Ingress资源,名称为test-http-ingress,ingress类型是nginx(标识该Ingress由Nginx Ingress Controller处理),配置了一个host路由规则,将通过test.com域名访问,且路径前缀为/api/user的http请求转发给test-user服务的8080端口。同样还为test.com配置了tls证书,对应的secret资源名称为test-com-tls-secret。
总结
使用Ingress实现流量入口是k8s的推荐方案,只需要为域名配置tls secret资源、ingress资源,配置路由规则,就可以由Ingress Controller将流量转发给后端服务。每新增一个微服务只需要创建一个Ingress资源。
Ingress Controller只是让网关实现Ingress资源描述的能力,真是解决将网关暴露给外网访问,解决静态IP问题的,是将Ingress Controller的Service的类型配置为LoadBalancer,利用云服务提供商实现的LoadBalancer解决静态IP问题。或是将Ingress Controller的Service的类型配置为NodePort,可使用任意一个Pod所在的Node的IP访问;如果是ExternalIP,则使用ExternalIP访问。如果是私有云,可自己实现LoadBalancer。
Ingress Controller本质也是CRD(自定义资源定义)+ 自定义Controller(即Operator),只不过这是k8s官方提供的资源定义(Ingress),并将实现Ingress资源描述的功能的自定义控制器称为Ingress Controller,因此不叫Operator。目前istio、nginx、bfe、kong、haproxy等流量网关都提供了Ingress Controller的实现。
数据存储中间件容器化部署解决方案
数据存储中间件才涉及到数据的持久化存储,因此持久化存储解决方案,就是解决数据存储中间件容器化的方案。
在k8s集群上部署有状态应用
通常需要持久化存储且对数据一致性0容忍的应用都是有状态应用。k8s提供StatefulSet控制器用来部署有状态应用,它管理具有唯一身份标识的多个pod(每个pod都有一个持久化的、唯一的ID),每个 pod 可以有自己的持久化存储卷。即便StatefulSet中的单个pod发生故障,持久化的pod标识符能够将现有的Volume(卷)与Kubernetes新启的pod进行匹配,以取代发生故障的pod。
持久化存储Volume
- PV(Persistent Volume):PV是描述持久化存储数据卷的资源,描述一个具体的Volume的属性,如Volume的类型、挂载的目录、远程存储服务器地址等。持久卷是集群资源,就像节点也是集群资源一样。PV持久卷和普通的Volume一样,也是使用卷插件来实现的,只是它们拥有独立于任何使用PV的Pod的生命周期。
- PVC(Persistent Volume Claim):PVC描述的是Pod想要使用的PV的属性,例如存储的大小、访问模式等(要求PV卷能够以ReadWriteOnce、ReadOnlyMany或ReadWriteMany模式之一来挂载)。
- StorageClass:根据PVC创建PV,只有同属于一个StorageClass的PV和PVC,才可以绑定在一起。
K8S支持静态PV和动态PV:
- 静态PV:由运维事先创建好PV,开发人员根据PV定义PVC,缺点是PV一多就很难管理,且不利于自动化。
- 动态PV:由StorageClass的provisoner动态监听PVC,根据PVC动态的创建PV。
一、通过使用远程块存储实现计算存储分离
可以利用rdb、cephfs的k8s StorageClass,将mysql、kafka的数据存储到远程块存储/分布式文件系统。
CephFS-Provisioner
cephfs-provisioner是kubernetes官方社区提供的Cephfs的StorageClass支持,主要watch kubernetes中PVC资源的 CURD事件,然后创建PV。
RBD-Provisioner
rbd-provisioner是kubernetes官方社区提供的Ceph RBD的StorageClass支持,主要watch kubernetes中PVC资源的 CURD事件,然后创建PV。
二、通过节点亲和性实现持久化存储
在申请资源的时候,按照虚拟机部署的规格去购买虚拟机,满足CPU、内存、硬盘的要求,将购买的节点加入k8s集群,并为这些节点打标签。
例如,部署mysql的节点,标签设置为mysql-node,这样编写mysql的StatefulSet时,通过配置节点亲和性,将pod部署到mysql-node节点上。
但是该方案无法实现计算存储分离,动态扩缩容显的无能为力。另外要求其它pod不能部署到这几个节点。
总结
使用PV+PVC方式,实际就是挂载一个远程文件系统/块存储设备,存在网络I/O性能开销,具体性能影响,要看远程文件系统/块存储设备的实现原理,可能会利用本地磁盘暂存+内存缓存减少一些网络I/O的开销,但肯定比不上使用节点本地磁盘,并且出现问题较难排查,需要有这方面的专家才敢这么去做。因此对于Mysql这类有状态的中间件,我们目前还是当成IaC资源申请使用,不考虑容器化部署。