为什么需要负载均衡
只要不是可以简化到极致的demo或学习练习工程,一个完整的业务系统都至少分为前端和后端两大部分,前端和后端各自又可能由多个模块组成。
如果没有负载均衡,用户可能会像直接接触到系统内部架构一样,要小心区分自己应该访问哪个地址,要自己记住同一服务的多个实例的地址。
这样一来,用户使用极为不方便,把内部架构暴露在所有用户面前,系统本身面临的风险增大。
7层和4层负载均衡
以下是把最常见的几项功能逻辑出来,并不代表4层和7层负载的差别只有这些。
考虑到所在项目是一个B端系统,并不存在非常高的并发要求,反而要能比较低成本地灵活应对业务需求,选择Nginx更加合适。
| 功能项 | 4层负载均衡 | 7层负载均衡 |
|---|---|---|
| 分发所在层级 | 传输层 | 应用层 |
| 端口范围 | 无限制 | 最大端口65535 |
| 支持协议 | TCP/IP | HTTP、HTTPS和DNS等 |
| 改写请求头信息 | 不支持 | 支持 |
| 根据URL匹配 | 不支持 | 支持 |
| 运行效率 | 非常高 | 比较高,比4层的低 |
| 代表产品 | LVS、F5 | Nginx、HAProxy |
Nginx的常用能力
-
负载均衡;
-
反向代理;
-
缓存;
-
流量控制。
在这里,主要描述Nginx提供反向代理和负载均衡的实践用法。
如何在容器化环境中部署Nginx
- 使用Nginx镜像,创建有状态负载;
- 通过nginx-ingress插件,直接创建Ingress(路由)
通过Nginx镜像安装
- 获取Nginx镜像,如果系统本身的镜像仓库中还没有,最好从网络上的中心仓库拉取并上传到自身的仓库;
# 第1步
docker pull
# 第2步
docker tag
# 第3步
docker login
# 第4步
docker push
-
编写相应的yaml文件,通过命令行或可视化界面创建有状态负载;
-
通过configMap或hostPath的方式挂载配置文件nginx.conf;
-
在启动时指定配置文件nginx.conf,指定方式可以参考如下两种。
containers:
- name: nginx-test
image: 'nginx:alpine-perl'
command:
- nginx
args:
- '-g daemon off;'
- '-c /xxx/nginx.conf'
或者
containers:
- name: nginx-test
image: 'nginx:alpine-perl'
command:
- nginx
args:
- '-g daemon off;'
- '-c'
- /xxx/nginx.conf
我相信上述两种指定配置文件的方法没错,在特定版本的nginx镜像下可以生效。
但是我自己在实际操作过程中,指定的配置文件一直没有生效/(ㄒoㄒ)/~~。
折腾了两天后,最终在其他人的提示下,改用了集群路由,因为我的项目中所有模块都部署在容器中。
安装Nginx-Ingress路由
-
在某个命名空间安装插件nginx-ingress,可以通过命令行,或者可视化的操作界面,比如我们在华为云安装它就很方便;
-
因为是HTTPS协议的,要先创建密钥,命名为 mojiayi.com;
kind: Secret type: IngressTLS apiVersion: v1 metadata: name: mojiayi.com namespace: mojiayi-prod data: tls.crt: >- 证书内容 tls.key: >- 私钥内容 -
编写nginx ingress的yaml文件,通过命令行或可视化界面创建路由;
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: mojiayi.com namespace: mojiayi-prod annotations: kubernetes.io/ingress.class: nginx managedFields: - manager: nginx-ingress-controller operation: Update apiVersion: networking.k8s.io/v1 spec: tls: - secretName: mojiayi.com rules: - host: mojiayi.com http: paths: - path: /nacos pathType: ImplementationSpecific backend: serviceName: nacos-cluster servicePort: 8848 - path: /xxl-job-admin pathType: ImplementationSpecific backend: serviceName: xxl-job-admin servicePort: 8080 - path: /api pathType: ImplementationSpecific backend: serviceName: mojiayi-gateway servicePort: 8005 - path: /admin-web pathType: ImplementationSpecific backend: serviceName: admin-web servicePort: 8099 - path: /auth-web pathType: ImplementationSpecific backend: serviceName: auth-web servicePort: 8098 status: loadBalancer: ingress: - ip: 173.26.102.63 - ip: 173.26.102.62 -
本项目中部署了两个ingress实例,对ingress的负载在SLB那一层完成;
-
spec.rules.http.paths.path中配置的路径,是接口地址或浏览器访问页面时域名后面的那段相对地址
-
spec.rules.http.paths.backend.serviceName中指定的服务名,是集群访问类型ClusterIP Service中的服务名,为方便管理,一般与无状态负载实例名相同;
-
spec.rules.http.paths.backend.servicePort中指定的端口,是集群访问类型ClusterIP Service中的端口port,为方便管理,一般与无状态负载实例的容器端口相同。
实践中踩到的坑
-
多处添加HTTPS证书后,系统无法访问。因为本项目部署时,是在一个内网环境中。对外的域名解析有统一管理,已经添加了HTTPS证书。后来又在SLB和Ingress上添加了证书,导致请求收到之后不断地在域名解析、SLB和Ingress之间重定向。
解决办法:只保留最外层的证书配置,删除SLB和Ingress上的证书。
重要的题外话
从理论到实践,从设计方案到真实运行的项目,要经历多少波折,只有亲历者才知道。我所写的操作步骤,也许不能直接被其他人复用。
如果能激发一点点解决问题的灵感,是我最大的荣幸~(≧▽≦)/~。