Ingress企業(yè)實(shí)戰(zhàn):URL重寫(xiě)與高級(jí)玩法
什么是URL重寫(xiě)
URL重寫(xiě)(URL rewriting)是一種在Web服務(wù)器上修改或轉(zhuǎn)換請(qǐng)求URL的過(guò)程。它通常涉及使用服務(wù)器配置或規(guī)則來(lái)更改傳入的URL,以便在不改變實(shí)際請(qǐng)求資源的情況下,實(shí)現(xiàn)不同的行為,如重定向、路徑映射、參數(shù)處理等。URL重寫(xiě)在服務(wù)器層面進(jìn)行,因此客戶端(如瀏覽器)對(duì)于URL的請(qǐng)求不會(huì)感知到這些更改,但服務(wù)器會(huì)根據(jù)配置進(jìn)行適當(dāng)?shù)奶幚怼RL重寫(xiě)可以用于多種目的,例如:
- 重定向: 將一個(gè)URL重寫(xiě)為另一個(gè)URL,實(shí)現(xiàn)301永久重定向或302臨時(shí)重定向。這可以用于更改站點(diǎn)結(jié)構(gòu)、修復(fù)錯(cuò)誤的URL、實(shí)現(xiàn)SEO優(yōu)化等。
- 路徑映射: 將一個(gè)URL的路徑映射到另一個(gè)位置,這對(duì)于隱藏實(shí)際文件路徑或路徑重組很有用。
- 查詢(xún)參數(shù)處理: 在URL中添加、刪除或修改查詢(xún)參數(shù),以適應(yīng)不同的應(yīng)用需求。
- 動(dòng)態(tài)URL到靜態(tài)URL: 將動(dòng)態(tài)生成的URL(帶有參數(shù))轉(zhuǎn)化為靜態(tài)URL,更友好且易于索引。
- 隱藏技術(shù)細(xì)節(jié): 可以通過(guò)URL重寫(xiě)隱藏后端服務(wù)器或應(yīng)用程序的實(shí)際技術(shù)細(xì)節(jié),提高安全性。
在Nginx、Apache等常見(jiàn)的Web服務(wù)器中,URL重寫(xiě)可以通過(guò)正則表達(dá)式、規(guī)則匹配等方式來(lái)實(shí)現(xiàn)。具體的語(yǔ)法和方法會(huì)因服務(wù)器軟件的不同而有所不同。通常,服務(wù)器配置文件中會(huì)有專(zhuān)門(mén)的部分用于配置URL重寫(xiě)規(guī)則,例如在Nginx中是使用rewrite指令。URL重寫(xiě)是一種強(qiáng)大的技術(shù),但在使用時(shí)需要小心,確保配置正確以避免潛在的問(wèn)題,例如無(wú)限循環(huán)重定向或錯(cuò)誤的重寫(xiě)規(guī)則可能導(dǎo)致網(wǎng)站不可用。
Ingress 內(nèi)置變量
內(nèi)置預(yù)定義變量即無(wú)需聲明就可以使用的變量,通常包括一個(gè)http請(qǐng)求或響應(yīng)中一部分內(nèi)容的值,以下為一些常用的內(nèi)置預(yù)定義變量:
變量名 定義
$arg_PARAMETER GET請(qǐng)求中變量名PARAMETER參數(shù)的值。
$args 這個(gè)變量等于GET請(qǐng)求中的參數(shù)。例如,foo=123&bar=blahblah;這個(gè)變量只可以被修改
$binary_remote_addr 二進(jìn)制碼形式的客戶端地址。
$body_bytes_sent 傳送頁(yè)面的字節(jié)數(shù)
$content_length 請(qǐng)求頭中的Content-length字段。
$content_type 請(qǐng)求頭中的Content-Type字段。
$cookie_COOKIE cookie COOKIE的值。
$document_root 當(dāng)前請(qǐng)求在root指令中指定的值。
$document_uri 與$uri相同。
$host 請(qǐng)求中的主機(jī)頭(Host)字段,如果請(qǐng)求中的主機(jī)頭不可用或者空,則為處理請(qǐng)求的server名稱(chēng)(處理請(qǐng)求的server的server_name指令的值)。值為小寫(xiě),不包含端口。
$hostname 機(jī)器名使用 gethostname系統(tǒng)調(diào)用的值
$http_HEADER HTTP請(qǐng)求頭中的內(nèi)容,HEADER為HTTP請(qǐng)求中的內(nèi)容轉(zhuǎn)為小寫(xiě),-變?yōu)開(kāi)(破折號(hào)變?yōu)橄聞澗€),例如:$http_user_agent(Uaer-Agent的值);
$http_user_agent : 客戶端agent信息;
$http_cookie : 客戶端cookie信息;
$sent_http_HEADER HTTP響應(yīng)頭中的內(nèi)容,HEADER為HTTP響應(yīng)中的內(nèi)容轉(zhuǎn)為小寫(xiě),-變?yōu)開(kāi)(破折號(hào)變?yōu)橄聞澗€),例如: $sent_http_cache_control, $sent_http_content_type…;
$is_args 如果$args設(shè)置,值為"?",否則為""。
$limit_rate 這個(gè)變量可以限制連接速率。
$nginx_version 當(dāng)前運(yùn)行的nginx版本號(hào)。
$query_string 與$args相同。
$remote_addr 客戶端的IP地址。
$remote_port 客戶端的端口。
$remote_user 已經(jīng)經(jīng)過(guò)Auth Basic Module驗(yàn)證的用戶名。
$request_filename 當(dāng)前連接請(qǐng)求的文件路徑,由root或alias指令與URI請(qǐng)求生成。
$request_body 這個(gè)變量(0.7.58+)包含請(qǐng)求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比較有意義。
$request_body_file 客戶端請(qǐng)求主體信息的臨時(shí)文件名。
$request_completion 如果請(qǐng)求成功,設(shè)為"OK";如果請(qǐng)求未完成或者不是一系列請(qǐng)求中最后一部分則設(shè)為空。
$request_method 這個(gè)變量是客戶端請(qǐng)求的動(dòng)作,通常為GET或POST。包括0.8.20及之前的版本中,這個(gè)變量總為main request中的動(dòng)作,如果當(dāng)前請(qǐng)求是一個(gè)子請(qǐng)求,并不使用這個(gè)當(dāng)前請(qǐng)求的動(dòng)作。
$request_uri 這個(gè)變量等于包含一些客戶端請(qǐng)求參數(shù)的原始URI,它無(wú)法修改,請(qǐng)查看$uri更改或重寫(xiě)URI,
包含請(qǐng)求參數(shù)的原始URI,不包含主機(jī)名,如:”/foo/bar.php?arg=baz”。
$scheme 所用的協(xié)議,比如http或者是https,比如rewrite ^(.+)$ $scheme://example.com$1 redirect;
$server_addr 服務(wù)器地址,在完成一次系統(tǒng)調(diào)用后可以確定這個(gè)值,如果要繞開(kāi)系統(tǒng)調(diào)用,則必須在listen中指定地址并且使用bind參數(shù)。
$server_name 服務(wù)器名稱(chēng)。
$server_port 請(qǐng)求到達(dá)服務(wù)器的端口號(hào)。
$server_protocol 請(qǐng)求使用的協(xié)議,通常是HTTP/1.0或HTTP/1.1。
$uri 請(qǐng)求中的當(dāng)前URI(不帶請(qǐng)求參數(shù),參數(shù)位于args),不同于瀏覽器傳遞的args),不同于瀏覽器傳遞的args),不同于瀏覽器傳遞的request_uri的值,它可以通過(guò)內(nèi)部重定向,或者使用index指令進(jìn)行修改。uri不包含主機(jī)名,如”/foo/bar.html”。
Ingress 正則表達(dá)式
正則表達(dá)式匹配,其中:
~ 為區(qū)分大小寫(xiě)匹配
~* 為不區(qū)分大小寫(xiě)匹配
!~和!~* 分別為區(qū)分大小寫(xiě)不匹配及不區(qū)分大小寫(xiě)不匹配
. 匹配除換行符以外的任意字符
\w 匹配字母或數(shù)字或下劃線或漢字
\s 匹配任意的空白符
\d 匹配數(shù)字
\b 匹配單詞的開(kāi)始或結(jié)束
^ 匹配字符串的開(kāi)始
$ 匹配字符串的結(jié)束
* 重復(fù)零次或更多次
+ 重復(fù)一次或更多次
? 重復(fù)零次或一次
{n} 重復(fù)n次
{n,} 重復(fù)n次或更多次
{n,m} 重復(fù)n到m次
*? 復(fù)任意次,但盡可能少重復(fù)
+? 重復(fù)1次或更多次,但盡可能少重復(fù)
?? 重復(fù)0次或1次,但盡可能少重復(fù)
{n,m}? 重復(fù)n到m次,但盡可能少重復(fù)
{n,}? 重復(fù)n次以上,但盡可能少重復(fù)
\W 匹配任意不是字母,數(shù)字,下劃線,漢字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非數(shù)字的字符
\B 匹配不是單詞開(kāi)頭或結(jié)束的位置
[^x] 匹配除了x以外的任意字符
[^aeiou] 匹配除了aeiou這幾個(gè)字母以外的任意字符
(exp) 匹配exp,并捕獲文本到自動(dòng)命名的組里
(?<name>exp) 匹配exp,并捕獲文本到名稱(chēng)為name的組里,也可以寫(xiě)成(?'name'exp)
(?:exp) 匹配exp,不捕獲匹配的文本,也不給此分組分配組號(hào)
(?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
(?#comment) 注釋分組不對(duì)正則表達(dá)式的處理產(chǎn)生任何影響
配置URL重寫(xiě)規(guī)則
在某些應(yīng)用場(chǎng)景中,后端服務(wù)提供的URL與Ingress規(guī)則中執(zhí)行的路徑不同,而Ingress訪將訪問(wèn)路徑直接轉(zhuǎn)發(fā)到后端相同路徑,如果不配置URL重寫(xiě)規(guī)則,所有訪問(wèn)都將返回404。比如如下案例,Ingress規(guī)則中配置的是/user/info,而后端服務(wù)提供的訪問(wèn)路徑是/info,在不配置重寫(xiě)的情況下,會(huì)直接轉(zhuǎn)發(fā)給后端/user/info與實(shí)際提供的訪問(wèn)路徑/info不匹配,會(huì)直接返回404。接下來(lái)咱們用案例的方式進(jìn)行驗(yàn)證。
不配置URL重寫(xiě)直接轉(zhuǎn)發(fā):
$ cat ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo
spec:
rules:
- host: demo.kubesre.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: demo-svc
port:
number: 8080
ingressClassName: nginx
$ kubectl apply -f ingress.yml
ingress.networking.k8s.io/demo configured
訪問(wèn)驗(yàn)證(/user/info):
# 訪問(wèn)/user/info,可以看出直接返回404
$ curl http://demo.kubesre.com/user/info
404 page not found
配置URL重寫(xiě):
$ cat ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: demo.kubesre.com
http:
paths:
- path: /user(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: demo-svc
port:
number: 8080
ingressClassName: nginx
$ kubectl apply -f ingress.yml
ingress.networking.k8s.io/demo configured
訪問(wèn)驗(yàn)證(/user/info):
# 訪問(wèn)/user/info,可以看出直接正常返回了
$ curl http://demo.kubesre.com/user/info
{"message":"云原生運(yùn)維圈!"}
注解說(shuō)明:
以上案例Ingress重寫(xiě)是通過(guò)nginx.ingress.kubernetes.io/rewrite-target注解實(shí)現(xiàn)不同路徑的重寫(xiě)規(guī)則。占位符$2表示將第二個(gè)括號(hào)即(.*)中匹配到的所有字符填寫(xiě)到nginx.ingress.kubernetes.io/rewrite-target注解中。想必大家都知道Ingress是基于Nginx開(kāi)發(fā)的,此時(shí)是通過(guò)Ingress CRD進(jìn)行創(chuàng)建的重寫(xiě)配置,其本質(zhì)也是修改Nginx配置文件的,此時(shí)從Ingress里的Nginx拷貝出來(lái)的配置如下:
server {
server_name demo.kubesre.com ;
listen 80 ;
listen [::]:80 ;
listen 443 ssl http2 ;
listen [::]:443 ssl http2 ;
set $proxy_upstream_name "-";
ssl_certificate_by_lua_block {
certificate.call()
}
location ~* "^/user(/|$)(.*)" {
set $namespace "default";
rewrite "(?i)/user(/|$)(.*)" /$2 break;
proxy_pass http://upstream_balancer;
proxy_redirect off;
}
高級(jí)URL重寫(xiě)規(guī)則
對(duì)于一些復(fù)雜的重寫(xiě)規(guī)則需求,可以通過(guò)如下注解來(lái)實(shí)現(xiàn),其本質(zhì)也是修改Nginx配置文件。
- nginx.ingress.kubernetes.io/server-snippet:在nginx.conf的“server”字段中添加自定義配置。
- nginx.ingress.kubernetes.io/configuration-snippet:在nginx.conf的“l(fā)ocation”字段中添加自定義配置。
URL重寫(xiě)Flag參數(shù):
- last:表示本條規(guī)則匹配完成后繼續(xù)向下匹配。
- break:表示本條規(guī)則匹配完成后停止匹配。
- redirect:表示臨時(shí)重定向,返回狀態(tài)碼302。
- permanent:表示永久重定向,返回狀態(tài)碼301。
重定向就是將網(wǎng)頁(yè)自動(dòng)轉(zhuǎn)向重定向:
- 301永久性重定向:新網(wǎng)址完全繼承舊網(wǎng)址,舊網(wǎng)址的SEO網(wǎng)絡(luò)搜索引擎的排名等完全清零
- 301重定向是網(wǎng)頁(yè)更改地址后對(duì)搜索引擎友好的最好方法,只要不是暫時(shí)搬移的情況,都建議使用301來(lái)做轉(zhuǎn)址。
- 302臨時(shí)性重定向:對(duì)舊網(wǎng)址沒(méi)有影響,但新網(wǎng)址不會(huì)有排名
- 搜索引擎爬蟲(chóng)會(huì)抓取新的內(nèi)容而保留舊的網(wǎng)址
配置Location:
通過(guò)Ingress注解nginx.ingress.kubernetes.io/server-snippet配置location,訪問(wèn)/sre,返回401錯(cuò)誤代碼,案例如下:
$ cat sre.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
location /sre {
return 401;
}
name: demo-redirect
spec:
rules:
- host: demo.kubesre.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: demo-svc
port:
number: 8080
ingressClassName: nginx
$ kubectl apply -f 1.yml
ingress.networking.k8s.io/demo-redirect configured
訪問(wèn)驗(yàn)證:
# 表示驗(yàn)證成功
$ curl http://demo.kubesre.com/sre/
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
URL重定向(permanent):
cat demo-permanent.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
rewrite ^/$ https://www.baidu.com redirect;
name: demo-redirect
spec:
rules:
- host: demo.kubesre.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: demo-svc
port:
number: 8080
ingressClassName: nginx
$ kubectl apply -f demo-permanent.yml
ingress.networking.k8s.io/demo-permanent created
訪問(wèn)驗(yàn)證:
# 301永久重定向,瀏覽器?地址欄會(huì)顯示跳轉(zhuǎn)后的URL地址,真實(shí)效果可以通過(guò)瀏覽器訪問(wèn)測(cè)試驗(yàn)證
$ curl http://demo.kubesre.com
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
URL重定向(redirect):
通過(guò)URL重定向,訪問(wèn)/test/info,直接重定向302跳轉(zhuǎn)到/user/info。
$ cat demo-redirect.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
rewrite ^/test/(.*)$ /user/$1 redirect;
name: demo-redirect
spec:
rules:
- host: demo.kubesre.com
http:
paths:
- path: /test
pathType: ImplementationSpecific
backend:
service:
name: demo-svc
port:
number: 8080
ingressClassName: nginx
$ kubectl apply -f demo-redirect.yml
ingress.networking.k8s.io/demo-redirect created
訪問(wèn)驗(yàn)證:
# 302 說(shuō)明已經(jīng)重定向了,實(shí)際效果可以通過(guò)瀏覽器訪問(wèn)查看
$ curl http://demo.kubesre.com/test/info
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
URL重寫(xiě)(last):
通過(guò)URL重寫(xiě)實(shí)現(xiàn),訪問(wèn)/sre,返回的是/kube的結(jié)果,可以利用重寫(xiě)Flag last參數(shù),當(dāng)URL重寫(xiě)后,會(huì)發(fā)送一個(gè)新的請(qǐng)求,再次進(jìn)入server塊,重試location匹配,匹配成功直接把結(jié)果直接返回。
$ cat sre.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
rewrite ^/sre /kube last;
nginx.ingress.kubernetes.io/server-snippet: |
location /sre {
return 401;
}
location /kube {
return 403;
}
name: demo-redirect
spec:
rules:
- host: demo.kubesre.com
http:
paths:
- path: /sre
pathType: ImplementationSpecific
backend:
service:
name: demo-svc
port:
number: 8080
ingressClassName: nginx
$ kubectl apply -f sre.yml
ingress.networking.k8s.io/demo-redirect configured
訪問(wèn)驗(yàn)證:
# 訪問(wèn)/sre,則返回/kube結(jié)果403
$ curl http://demo.kubesre.com/sre/
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
總結(jié)
本文介紹了URL重寫(xiě)的概念,并通過(guò)實(shí)際案例的方式講解了URL重寫(xiě)的方方面面,下一章將講解Ingress更多企業(yè)級(jí)實(shí)戰(zhàn),請(qǐng)敬請(qǐng)期待!