
# 为Nginx Ingress配置URL重写规则
在一些使用场景中后端服务提供访问的URL与Ingress规则中指定的路径不同，而Ingress会将访问路径直接转发到后端相同路径，如果不进行URL重写配置，所有访问都将返回404。例如，Ingress规则中的访问路径设置为/app/demo，而后端服务提供的访问路径为/demo，在实际访问Ingress时会直接转发到后端服务的/app/demo路径，与后端实际提供的访问路径（/demo）不匹配，导致404的情况发生。
此时，您可以通过Rewrite方法实现URL重写，即使用"nginx.ingress.kubernetes.io/rewrite-target"注解可以实现不同路径的重写规则。
#### 配置重写规则
1. 请参见[通过kubectl连接集群](https://support.huaweicloud.com/usermanual-cce/cce_10_0107.html)，使用kubectl连接集群。
2. 创建名为"**ingress-test.yaml** "的YAML文件，此处文件名可自定义。
   
   ```
   vi ingress-test.yaml
   ```
   **1.23及以上版本集群** ：
   ```
   apiVersion: networking.k8s.io/v1
   kind: Ingress 
   metadata: 
     name: ingress-test
     namespace: default
     annotations:
       nginx.ingress.kubernetes.io/rewrite-target: /$2
   spec:
     rules:
       - host: ''
         http:
           paths:
             - path: '/something(/|$)(.*)'
               backend:
                 service:
                   name: <your_service_name>  #替换为您的目标服务名称
                   port:
                     number: <your_service_port>  #替换为您的目标服务端口
               property:
                 ingress.beta.kubernetes.io/url-match-mode: STARTS_WITH
               pathType: ImplementationSpecific
     ingressClassName: nginx
   ```
   **1.21及以下版本集群** ：
   ```
   apiVersion: networking.k8s.io/v1beta1
   kind: Ingress
   metadata:
     name: ingress-test
     namespace: default
     annotations:
       kubernetes.io/ingress.class: nginx
       nginx.ingress.kubernetes.io/rewrite-target: /$2
   spec:
     rules:
       - host: ''
         http:
           paths:
             - path: '/something(/|$)(.*)'
               backend:
                 serviceName: <your_service_name>  #替换为您的目标服务名称
                 servicePort: <your_service_port>  #替换为您的目标服务端口
   ```
   ![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/note_3.0-zh-cn.png)
   在同一个Nginx Ingress控制器中，只要有一个Nginx Ingress使用了rewrite-target，则所有Nginx Ingress中同一个host下的所有path都对正则大小写**不敏感**，包括没有使用rewrite-target的Ingress。
   以上示例中，占位符$2表示将第二个括号即(.\*)中匹配到的所有字符填写到"nginx.ingress.kubernetes.io/rewrite-target"注解中，作为重写后的URL路径。
   例如，上面的Ingress正则匹配式将导致多种情况的URL重写，可能的情形如下：
   - 访问"/something"路径重写为"/"路径
   
   - 访问"/something/"路径重写为"/"路径
   
   - 访问"/something/new"路径重写为"/new"路径
    
   
   
3. 创建Ingress。 
   ```
   kubectl create -f ingress-test.yaml
   ```
   回显类似如下，表示Ingress服务已创建。
   ```
   ingress.networking.k8s.io/ingress-test createdd
   ```
   
   
4. 查看已创建的Ingress。 
   ```
   kubectl get ingress
   ```
   回显如下，表示Ingress服务创建成功。
   ```
   NAME          CLASS   HOSTS     ADDRESS          PORTS   AGE
   ingress-test  nginx   *         121.**.**.**     80      10s
   ```
   
   
5. 访问Ingress，其中${ELB_IP}为Nginx Ingress所绑定的ELB IP。 
   ```
   curl ${ELB_IP}/something
   ```
   最终访问路径会被重定向至"/"路径。
   
   
 
#### Ingress配置文件说明
在nginx-ingress-controller容器中，"/etc/nginx"路径下的nginx.conf文件可查看所有Ingress配置。
1. 查询nginx-ingress-controller的Pod名称。 
   ```
   kubectl get pod -n kube-system | grep nginx-ingress-controller
   ```
   回显如下：
   ```
   cceaddon-nginx-ingress-controller-66855ccb9b-52qcd        1/1     Running   0          37m
   ```
   
   
2. 登录nginx-ingress-controller容器。 
   ```
   kubectl exec -it cceaddon-nginx-ingress-controller-66855ccb9b-52qcd -n kube-system -- /bin/bash
   ```
   
   
3. 查看"/etc/nginx"路径下的nginx.conf文件。 
   ```
   cat /etc/nginx/nginx.conf
   ```
   本文示例中的重写规则将生成一条Rewrite指令，并写入到nginx.conf的location字段中，如下所示：
   ```
   ## start server _
           server {
                   server_name _ ;
                   ...
                   location ~* "^/something(/|$)(.*)" {
                           set $namespace      "default";
                           set $ingress_name   "ingress-test";
                           set $service_name   "<your_service_name>";
                           set $service_port   "80";
                           ...
                           rewrite "(?i)/something(/|$)(.*)" /$2 break;
                           ...
                   }
           }
           ## end server _
   ```
   上面的Rewrite指令基本语法结构为：
   ```
   rewrite regex replacement [flag];
   ```
   - regex：匹配URI的正则表达式。在上述例子中，"(?i)/something(/\|$)(.\*)"即为匹配URI的正则表达式，其中"(?i)"表示不区分大小写。
   
   - replacement：重写内容。在上述例子中，"/$2"即为重写内容，表示把路径重写为第二个括号"(.\*)"中匹配到的所有字符。
   
   - flag：表示重写形式的标记，包括：
     - last：表示本条规则匹配完成后继续向下匹配。
     
     - break：表示本条规则匹配完成后停止匹配。
     
     - redirect：表示临时重定向，返回状态码302。
     
     - permanent：表示永久重定向，返回状态码301。
      
    
   
   
 
#### 高级Rewrite配置
对于一些复杂高级的Rewrite需求，可以通过如下注解来实现，其本质也是修改Nginx的配置文件（nginx.conf），可以实现上面提到的"nginx.ingress.kubernetes.io/rewrite-target"注解的功能，但是自定义程度更高，适合更加复杂的Rewrite需求。
- nginx.ingress.kubernetes.io/server-snippet：在nginx.conf的"server"字段中添加自定义配置。
- nginx.ingress.kubernetes.io/configuration-snippet：在nginx.conf的"location"字段中添加自定义配置。
![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/note_3.0-zh-cn.png)
snippet配置在NGINX Ingress控制器版本为2.4.6版本及以上时（对应社区版本为v1.9.3）不再默认启用，详情请参见[Changelog](https://github.com/kubernetes/ingress-nginx/releases/tag/controller-v1.9.3)。如果您仍需要使用snippet配置，可以通过[allow-snippet-annotations](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#allow-snippet-annotations)启用。
在4.0.4及以上NGINX Ingress控制器插件版本需同时添加annotations-risk-level配置。这是因为在该版本中annotations-risk-level默认值降级为High，详情请参见[社区ChangeLog](https://github.com/kubernetes/ingress-nginx/releases/tag/controller-v1.12.0)。由于snippet注解为Critical级别，需要同时调整annotations-risk-level的参数值为Critical，否则snippet注解仍不可用。
通过以上两个注解可以在nginx.conf中的"server"或"location"字段中插入Rewrite指令，完成URL的重写，示例如下：
```
annotations:
     kubernetes.io/ingress.class: "nginx"
     nginx.ingress.kubernetes.io/configuration-snippet: |
        rewrite ^/stylesheets/(.*)$ /something/stylesheets/$1 redirect;  # 添加 /something 前缀
        rewrite ^/images/(.*)$ /something/images/$1 redirect;  # 添加 /something 前缀
```
如上两条规则在访问URL中添加了"/something"路径前缀，即：
- 当用户访问示例中的"/stylesheets/new.css"路径时，重写为"/something/stylesheets/new.css"路径。
- 当用户访问示例中的"/images/new.jpg"路径时，重写为"/something/images/new.jpg"路径。
 
