問題簡述
通過istio實現灰度發布,瀏覽器訪問報404錯誤,但是通過curl傳遞一個Host請求頭就能訪問成功。
問題復現
Rancher UI界面啟動Istio,並開啟ingress網關
命名空間啟動Istio自動注入
部署nginx應用
###deploy-nginx-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
version: v1
name: nginx-v1
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
version: v1
template:
metadata:
labels:
app: nginx
version: v1
spec:
containers:
- image: satomic/nginx:v1
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: 80tcp02
protocol: TCP
---
##deploy-nginx-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
version: v2
name: nginx-v2
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
version: v2
template:
metadata:
labels:
app: nginx
version: v2
spec:
containers:
- image: satomic/nginx:v2
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: 80tcp02
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx
service: nginx
name: nginx
namespace: default
spec:
ports:
- name: 80tcp02
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: ClusterIP
---
##gw.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mynginx-gateway
namespace: default
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- hosts:
- 'web1.com'
port:
name: http
number: 80
protocol: HTTP
---
##vs-nginx.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: nginx
namespace: default
spec:
gateways:
- mynginx-gateway
hosts:
- 'web1.com'
http:
- match:
- uri:
exact: /index.html
route:
- destination:
host: nginx
subset: dr-nginx-v1
weight: 50
- destination:
host: nginx
subset: dr-nginx-v2
weight: 50
##dr-nginx.yaml
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: nginx
namespace: default
spec:
host: nginx
subsets:
- labels:
version: v1
name: dr-nginx-v1
- labels:
version: v2
name: dr-nginx-v2
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
瀏覽器訪問:http://web1.com:31380/index.html,訪問報錯
# windows添加主機映射,C:\Windows\System32\drivers\etc\hosts
172.16.0.211 web1.com
172.16.0.211 為訪問主機,31380則是ingressGateway使用NodePort映射 端口
curl訪問,直接curl訪問失敗,帶上Host請求頭,訪問成功
# Linux添加主機映射,/etc/hosts
172.16.0.211 web1.com
排查思路
查看默認發送的請求頭
[root@node02 ~]# curl -v http://web1.com:31380/index.html
* About to connect() to web1.com port 31380 (#0)
* Trying 172.16.0.211...
* Connected to web1.com (172.16.0.211) port 31380 (#0)
> GET /index.html HTTP/1.1
> User-Agent: curl/7.29.0
> Host: web1.com:31380
> Accept: */*
>
< HTTP/1.1 404 Not Found
< date: Wed, 29 Apr 2020 04:28:41 GMT
< server: istio-envoy
< content-length: 0
<
* Connection #0 to host web1.com left intact
可以看到請求的Host
是web1.com:31380
,而我們virtualservice
的hosts寫的是web.com
,所以請求的地址不對,自然就沒法訪問
問題處理
既然請求的Host不對,那么就要修改成相對應的Host才能訪問,可以有以下幾種處理方式。
1 請求頭(Request Header)手動指定Host字段
如果是應用內部自己調用,例如代碼或者腳本,可以手動指定Host請求頭,但是這種就無法再瀏覽器上訪問
[root@node02 ~]# curl -v -H Host:web1.com http://web1.com:31380/index.html
* About to connect() to web1.com port 31380 (#0)
* Trying 172.16.0.211...
* Connected to web1.com (172.16.0.211) port 31380 (#0)
> GET /index.html HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host:web1.com
>
< HTTP/1.1 200 OK
< server: istio-envoy
< date: Wed, 29 Apr 2020 04:37:21 GMT
< content-type: text/html
< content-length: 7
< last-modified: Wed, 25 Mar 2020 15:18:37 GMT
< etag: "5e7b764d-7"
< accept-ranges: bytes
< x-envoy-upstream-service-time: 1
<
app v2
* Connection #0 to host web1.com left intact
2 在VirtualService中設置authority
來支持port訪問
istio目前暫時還不支持直接添加DOMAIN+PORT,可以通過設置authority
來支持PORT訪問
在gateway和virtualservice設置hosts為"*"
,並在virtualservice設置authority
## gateway
spec:
- hosts:
- '*'
---
## virtualservice
spec:
hosts:
- '*'
http:
- authority:
exact: "web2.com:31380"
完整示例
##gw.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mynginx-gateway
namespace: default
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- hosts:
- '*'
port:
name: http
number: 80
protocol: HTTP
---
##vs-nginx.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: nginx
namespace: default
spec:
gateways:
- mynginx-gateway
hosts:
- '*'
http:
- match:
- uri:
exact: /index.html
authority:
exact: "web2.com:31380"
route:
- destination:
host: nginx
subset: dr-nginx-v1
weight: 50
- destination:
host: nginx
subset: dr-nginx-v2
weight: 50
kubectl apply刷新配置后,就可以在瀏覽器上訪問了
並且我們也可以看到,Host請求頭是web1.com:31380,如果使用web1.com Host請求頭訪問,則會失敗
[root@node02 ~]# curl -v http://web1.com:31380/index.html
* About to connect() to web1.com port 31380 (#0)
* Trying 172.16.0.211...
* Connected to web1.com (172.16.0.211) port 31380 (#0)
> GET /index.html HTTP/1.1
> User-Agent: curl/7.29.0
> Host: web1.com:31380
> Accept: */*
>
< HTTP/1.1 200 OK
< server: istio-envoy
< date: Wed, 29 Apr 2020 05:34:12 GMT
< content-type: text/html
< content-length: 7
< last-modified: Wed, 25 Mar 2020 15:18:37 GMT
< etag: "5e7b764d-7"
< accept-ranges: bytes
< x-envoy-upstream-service-time: 1
<
app v2
* Connection #0 to host web1.com left intact
[root@node02 ~]# curl -v -H Host:web1.com http://web1.com:31380/index.html
* About to connect() to web1.com port 31380 (#0)
* Trying 172.16.0.211...
* Connected to web1.com (172.16.0.211) port 31380 (#0)
> GET /index.html HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host:web1.com
>
< HTTP/1.1 404 Not Found
< date: Wed, 29 Apr 2020 05:34:25 GMT
< server: istio-envoy
< content-length: 0
<
* Connection #0 to host web1.com left intact
3 設置ingressGateway使用LoadBalancer
ingressGateway使用LoadBancer,設置好對應的地址即可
參考
相關issue參考:https://github.com/istio/istio/issues/11828
官方istio VirtualService設置選項:https://istio.io/zh/docs/reference/config/networking/virtual-service/#HTTPMatchRequest