
# 通过Nginx Ingress对多个应用进行弹性伸缩
在实际的生产环境中，应用多实例部署可以提高应用的稳定性和可靠性，但也会增加资源的浪费和成本。因此，在进行多实例部署时，需要权衡资源利用率和应用性能之间的平衡，但手动调节实例数量存在伸缩不及时的问题，难以达到最佳的效果。
如果该应用使用Nginx Ingress实现对外的流量路由转发，您可以使用nginx_ingress_controller_requests的指标为应用配置HPA策略，可以实现随着流量的变化动态调整Pod实例数的功能，优化资源利用率。
#### 操作视频
<video controls="controls" preload="none" id="object11998821154019" class="idp-external-video" src="https://res-video.hc-cdn.com/cloudbu-site/china/zh-cn/video/cce/nginxingress.mp4" title="通过Nginx Ingress对多个应用进行弹性伸缩" poster="https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000002366947408.jpg" height="340.0000" width="600.0000"></video>
#### 前提条件
- 集群中已安装NGINX Ingress控制器插件。
- 集群中已安装云原生监控插件（部署模式需要选择"本地数据存储"）。
- 已使用kubectl命令行工具或CloudShell连接集群。
- 已安装压力测试工具Apache Benchmark。
 
#### 创建业务负载和对应的Service
本文以两个服务通过Nginx Ingress实现对外的流量路由为例进行演示。
1. 创建应用test-app和对应Service。 
   1. 创建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
      ```
      
   
   2. 部署test-app和对应Service。
      ```
      kubectl apply -f test-app.yaml
      ```
      
   
   
   
   
2. 创建应用sample-app和对应Service。 
   1. 创建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
      ```
      
   
   2. 部署sample-app和对应Service。
      ```
      kubectl apply -f sample-app.yaml
      ```
      
   
   
   
   
3. 部署Ingress资源。 
   1. 创建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。
       
   
   2. 部署Ingress资源。
      ```
      kubectl apply -f ingress.yaml
      ```
      
   
   3. 获取Ingress资源。
      ```
      kubectl get ingress -o wide
      ```
      ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001924445109.png "点击放大")
      
   
   4. 部署成功后，登录集群节点，将服务域名和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
      ```
      
   
   5. 登录集群节点通过/和/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!
      ```
      
   
   
   
   
 
#### 修改普罗配置项user-adapter-config
1. 执行以下命令编辑user-adapter-config配置项。 
   ```
   kubectl -n monitoring edit configmap user-adapter-config
   ```
   
   
2. 添加以下规则到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
   ```
   
   
3. 重启custom-metrics-apiserver服务。 
   ```
   kubectl -n monitoring delete pod -l app=custom-metrics-apiserver
   ```
   
   
4. 登录集群节点通过/和/home两个路径持续访问Host地址。 
   ```
   # curl test.example.com/
   Hello from '/' path!
   # curl test.example.com/home
   Hello from '/home' path!
   ```
   
   
5. 执行命令查看指标是否添加成功。 
   ```
   kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/default/services/*/nginx_ingress_controller_per_second | python -m json.tool
   ```
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001924447009.png "点击放大")
   
   
 
#### 创建HPA规则
1. 创建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
   ```
   
   
2. 部署HPA策略。 
   ```
   kubectl apply -f hpa.yaml
   ```
   
   
3. 查看HPA部署情况。 
   ```
   kubectl get hpa
   ```
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001924409489.png "点击放大")
   
   
 
#### 压力测试验证
1. 登录集群节点，对/home路径进行压力测试。 
   ```
   ab -c 50 -n 5000 test.example.com/home
   ```
   
   
2. 查看HPA情况 
   ```
   kubectl get hpa
   ```
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001924463305.png "点击放大")
   
   
3. 登录集群节点，对根路径进行压力测试。 
   ```
   ab -c 50 -n 5000 test.example.com/
   ```
   
   
4. 查看HPA情况。 
   ```
   kubectl get hpa
   ```
   ![](https://support.huaweicloud.com/bestpractice-cce/zh-cn_image_0000001924463973.png "点击放大")
   和压力测试前查询的HPA指标对比，业务应用在请求量增大超过阈值的情况下成功扩容。
   
   
 
