通过Nginx Ingress对多个应用进行弹性伸缩
在实际的生产环境中,应用多实例部署可以提高应用的稳定性和可靠性,但也会增加资源的浪费和成本。因此,在进行多实例部署时,需要权衡资源利用率和应用性能之间的平衡,但手动调节实例数量存在伸缩不及时的问题,难以达到最佳的效果。
如果该应用使用Nginx Ingress实现对外的流量路由转发,您可以使用nginx_ingress_controller_requests的指标为应用配置HPA策略,可以实现随着流量的变化动态调整Pod实例数的功能,优化资源利用率。
前提条件
- 集群中已安装NGINX Ingress控制器插件。
- 集群中已安装云原生监控插件(server模式)。
- 已使用kubectl命令行工具或CloudShell连接集群。
- 已安装压力测试工具Apache Benchmark。
创建业务负载和对应的Service
本文以两个服务通过Nginx Ingress实现对外的流量路由为例进行演示。
- 创建应用test-app和对应Service。
- 创建test-app.yaml文件。
apiVersion: apps/v1 kind: Deployment metadata: name: test-app labels: app: test-app spec: replicas: 1 selector: matchLabels: app: test-app template: metadata: labels: app: test-app spec: containers: - image: skto/sample-app:v2 name: metrics-provider ports: - name: http containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: test-app namespace: default labels: app: test-app spec: ports: - port: 8080 name: http protocol: TCP targetPort: 8080 selector: app: test-app type: ClusterIP
- 部署test-app和对应Service。
kubectl apply -f test-app.yaml
- 创建test-app.yaml文件。
- 创建应用sample-app和对应Service。
- 创建sample-app.yaml文件。
apiVersion: apps/v1 kind: Deployment metadata: name: sample-app labels: app: sample-app spec: replicas: 1 selector: matchLabels: app: sample-app template: metadata: labels: app: sample-app spec: containers: - image: skto/sample-app:v2 name: metrics-provider ports: - name: http containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: sample-app namespace: default labels: app: sample-app spec: ports: - port: 80 name: http protocol: TCP targetPort: 8080 selector: app: sample-app type: ClusterIP
- 部署sample-app和对应Service。
kubectl apply -f sample-app.yaml
- 创建sample-app.yaml文件。
- 部署Ingress资源。
- 创建ingress.yaml文件。
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: test-ingress namespace: default spec: ingressClassName: nginx rules: - host: test.example.com http: paths: - backend: service: name: sample-app port: number: 80 path: / pathType: ImplementationSpecific - backend: service: name: test-app port: number: 8080 path: /home pathType: ImplementationSpecific
- host:指定服务访问域名。本示例使用test.example.com。
- path:指定访问的URL路径。请求到来之后会根据路由规则匹配相应的Service,然后通过Service访问相应的Pod。
- backend:由Service名称和Service端口组成,指定当前path转发的Service。
- 部署Ingress资源。
kubectl apply -f ingress.yaml
- 获取Ingress资源。
kubectl get ingress -o wide
- 部署成功后,登录集群节点,将服务域名和nginx-ingress的ELB服务地址添加到节点本地hosts文件。nginx-ingress的ELB服务地址即为3.c查询的ADDRESS IP地址。
export NGINXELB=xx.xx.xx.xx echo -n "${NGINXELB} test.example.com" >> /etc/hosts
- 登录集群节点通过/和/home两个路径分别访问Host地址。Nginx Ingress Controller会根据上方配置分别访问sample-app和test-app。
# curl test.example.com/ Hello from '/' path! # curl test.example.com/home Hello from '/home' path!
- 创建ingress.yaml文件。
修改普罗配置项user-adapter-config
- 执行以下命令编辑user-adapter-config配置项。
kubectl -n monitoring edit configmap user-adapter-config
- 添加以下规则到adapter的configmap。
apiVersion: v1 data: config.yaml: | rules: - metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>) name: as: ${1}_per_second matches: ^(.*)_requests resources: namespaced: false overrides: exported_namespace: resource: namespace service: resource: service seriesQuery: nginx_ingress_controller_requests resourceRules: cpu: containerQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>,container!="",pod!=""}[1m])) by (<<.GroupBy>>) nodeQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>, id='/'}[1m])) by (<<.GroupBy>>) resources: overrides: instance: resource: node namespace: resource: namespace pod: resource: pod containerLabel: container memory: containerQuery: sum(container_memory_working_set_bytes{<<.LabelMatchers>>,container!="",pod!=""}) by (<<.GroupBy>>) nodeQuery: sum(container_memory_working_set_bytes{<<.LabelMatchers>>,id='/'}) by (<<.GroupBy>>) resources: overrides: instance: resource: node namespace: resource: namespace pod: resource: pod containerLabel: container
- 重启custom-metrics-apiserver服务。
kubectl -n monitoring delete pod -l app=custom-metrics-apiserver
- 登录集群节点通过/和/home两个路径持续访问Host地址。
# curl test.example.com/ Hello from '/' path! # curl test.example.com/home Hello from '/home' path!
- 执行命令查看指标是否添加成功。
kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/default/services/*/nginx_ingress_controller_per_second | python -m json.tool
创建HPA规则
- 创建hpa.yaml文件,配置test-app和sample-app工作负载通过普罗指标进行弹性伸缩。
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: sample-hpa # HPA名称 spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: sample-app # 无状态工作负载名称 minReplicas: 1 # 最小实例数 maxReplicas: 10 # 最大实例数 metrics: - type: Object object: metric: name: nginx_ingress_controller_per_second # 指标 describedObject: apiVersion: v1 kind: service name: sample-app # 无状态工作负载的service target: type: Value value: 30 # 指标满足(实际值/30)±0.1范围就会触发扩缩容 --- apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: test-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: test-app minReplicas: 1 maxReplicas: 10 metrics: - type: Object object: metric: name: nginx_ingress_controller_per_second describedObject: apiVersion: v1 kind: service name: test-app target: type: Value value: 30
- 部署HPA策略。
kubectl apply -f hpa.yaml
- 查看HPA部署情况。
kubectl get hpa
压力测试验证
- 登录集群节点,对/home路径进行压力测试。
ab -c 50 -n 5000 test.example.com/home
- 查看HPA情况
kubectl get hpa
- 登录集群节点,对根路径进行压力测试。
ab -c 50 -n 5000 test.example.com/
- 查看HPA情况。
kubectl get hpa
和压力测试前查询的HPA指标对比,业务应用在请求量增大超过阈值的情况下成功扩容。