更新时间:2024-09-29 GMT+08:00

为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连接集群,使用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>  #替换为您的目标服务端口

    只要有一个Ingress使用了rewrite-target,则所有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/ingress-test created

  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 -- /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”字段中添加自定义配置。

snippet配置在NGINX Ingress控制器版本为2.4.6版本及以上时(对应社区版本为v1.9.3)不再默认启用,详情请参见Changelog。如果您仍需要使用snippet配置,可以通过allow-snippet-annotations启用。

通过以上两个注解可以在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”路径。