Kubernetes出入向的流量管理可以通過Service Mesh和Ingress Controller兩個功能來實現,這兩個功能通常是分開進行配置的,這樣Kubernetes 環境的管理就會變得非常複雜。
這樣的配置不僅會增加延遲,同時還會增加配置錯誤的概率,進而阻礙適當的流量路由,甚至導致出現安全漏洞(例如攻擊者獲得過多的應用訪問許可權)和不好的使用體驗(例如客戶無法訪問其授權訪問的應用)。 另外分開配置也會消耗更多的配置時間,出現錯誤的時候還需要更多的時間來處理。
NGINX Plus同時整合了NGINX Ingress Controller 和 NGINX Service Mesh,也就是將Service Mesh和Ingress Controller兩個功能在一個元件上完成,通過這個整合成功能可以同時控制出入向的mTLS流量,這樣不僅可以避免分開配置引起的問題,也會節省時間。
下面通過幾個章節進行說明:
- 先決條件
- 使用 NGINX Service Mesh 部署 NGINX Ingress Controller
- 使用標準 Kubernetes Ingress 資源發佈應用
- 使用 NGINX Virtual Server 資源發佈應用
- 使用 NGINX Ingress Controller 配置安全出向路由
先決條件
在開始演示之前,我們需要具備以下前提條件
・在 Kubernetes 集群中安裝 NGINX Server Mesh 控制平面,併為Service Mesh設置 mTLS 和strict 策略。
・在 Kubernetes 集群中安裝基於 NGINX Plus 的 NGINX Ingress Controller(使用 Deployment ,而不要使用DaemonSet),啟用出向 (egress) 功能,並將其作為LoadBalancer 類型的服務進行發佈。
注:本演示不適用於 NGINX 開源版NGINX Ingress Controller。 為方便閱讀,我們在後文把基於 NGINX Plus 的 NGINX Ingress Controller 簡稱為“NGINX Ingress Controller”。
・根據我們的部署文檔完成三個內容:下載bookinfo 示例應用,注入 NGINX Service Mesh sidecar,部署該應用。
由於我們在第 1 步中設置了strict 策略,sidecar 會拒絕來自Service Mesh外用戶端的bookinfo 應用請求。 為了解決這個問題,我們需要在演示中設置埠轉發,具體命令如下:
> kubectl port-forward svc/product-page 9080
Forwarding from 127.0.0.1:9080 -> 9080
Forwarding from [::1]:9080 -> 9080
Handling connection for 9080
當我們試圖訪問該應用時,會得到狀態碼503,因為我們的本地設備不在Service Mesh中:
> curl localhost:9080
503
使用 NGINX Service Mesh
部署 NGINX Ingress Controller
在發佈應用的第一階段,我們需要部署一個 NGINX Ingress Controller 實例。
NGINX 為此提供了 Deployment 和 Daemonset的manifest檔。 本演示中使用的是Deployment manifest,檔為:nginx-plus-ingress.yaml。
它包括了下面的annotations,配置這個annotations的NGINX Ingress Controller會同時實現入向流量和出向流量的管理:
nsm.nginx.com/enable-ingress: “true”
nsm.nginx.com/enable-egress: “true”
查看在 GitHub 托管的nginx-plus-ingress.yaml
該 manifest 直接整合了 NGINX Ingress Controller 和 Spire,Spire是NGINX Service Mesh 的證書頒發機構 (CA),因此NGINX Service Mesh sidecar不需要注入 NGINX Ingress Controller。
NGINX Ingress Controller 將直接從 Spire CA 獲取證書和秘鑰,用於Service Mesh中 pod 的 mTLS。 該 manifest 指定了 Spire 的代理位址:
args:
– -spire-agent-address=/run/spire/sockets/agent.sock
查看在 GitHub 托管的nginx-plus-ingress.yaml
並將 Spire 代理的 UNIX 套接字安裝到 NGINX Ingress Controller pod:
volumes:
– hostPath:
path: /run/spire/sockets
type: DirectoryOrCreate
name: spire-agent-socket
查看在 GitHub 托管的nginx-plus-ingress.yaml
關於 manifest,最後需要注意的一點是-enable-internal-routes
CLI 參數,該參數用於出向路由服務:
args:
– -enable-internal-routes
查看在 GitHub 托管的nginx-plus-ingress.yaml
在開始演示之前,我們通過運行kubectl apply -f nginx-plus-ingress.yaml
命令來安裝 NGINX Ingress Controller,然後檢查一下 nginx-ingress
命名空間中的部署情況。
如下面輸出結果中的READY
列所示,NGINX Ingress Controller pod 中只有一個容器,是因為我們還沒有為其注入 NGINX Service Mesh sidecar。
此外,我們還部署了LoadBalancer
類型的服務,以便將 NGINX Ingress Controller 的外部 IP 位址(此處為 35.233.133.188)發佈到整合外部。 後續我們將訪問位於該IP位址的bookinfo
示例應用。
> kubectl get pods --namespace=nginx-ingress NAME READY STATUS RESTARTS AGE pod/nginx-ingress-867f954b8f0fzdrm 1/1 Running 0 3d3hNAME TYPE CLUSTER-IP EXTERNAL-IP ... service-nginx-ingress LoadBalancer 10.31.245.207 35.233.133.188 ... ... PORT(S) AGE ... 80:31469/TCP,443:32481/TCP 4d2h ...
使用標準 Kubernetes Ingress 資源
發佈應用
現在,我們使用 bookinfo-ingress.yaml 中定義的標準 Kubernetes Ingress 資源在Service Mesh中發佈bookinfo
應用。
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: bookinfo-ingress spec: ingressClassName: nginx # use only with k8s version >= 1.18.0 tls: - hosts: - bookinfo.example.com secretName: bookinfo-secret rules: - host: bookinfo.example.com http: paths: - path: / backend: serviceName: productpage servicePort: 9080 查看在 GitHub 托管的bookinfo-ingress.yaml
在bookinfo-ingress.yaml的第10行引用了Kubernetes Secret,這個配置提供了路由規則,該規則將對bookinfo.example.com 的請求轉發給productpage
服務(該配置在第11–18 行)。
該Kubernetes Secret 在bookinfo-secret.yaml 中進行了定義:
apiVersion: v1 kind: Secret metadata: name: bookinfo-secret type: kubernetes.io/tls data: tls.crt:LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhZQ0NRREFPRjl0THNhWFdqQU5CZ2txaGtpRzl3MEJBUXNGQURCYU1Rc3dDUVlEVlFRR0V3SlYKVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MApaREViTUJrR0ExVUVBd3dTWTJGbVpTNWxlR0Z0Y0d4bExtTnZiU0FnTUI0WERURTRNRGt4TWpFMk1UVXpOVm9YCkRUSXpNRGt4TVRFMk1UVXpOVm93V0RFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVNFd0h3WUQKVlFRS0RCaEpiblJsY201bGRDQlhhV1JuYVhSeklGQjBlU0JNZEdReEdUQVhCZ05WQkFNTUVHTmhabVV1WlhoaApiWEJzWlM1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDcDZLbjdzeTgxCnAwanVKL2N5ayt2Q0FtbHNmanRGTTJtdVpOSzBLdGVjcUcyZmpXUWI1NXhRMVlGQTJYT1N3SEFZdlNkd0kyaloKcnVXOHFYWENMMnJiNENaQ0Z4d3BWRUNyY3hkam0zdGVWaVJYVnNZSW1tSkhQUFN5UWdwaW9iczl4N0RsTGM2SQpCQTBaalVPeWwwUHFHOVNKZXhNVjczV0lJYTVyRFZTRjJyNGtTa2JBajREY2o3TFhlRmxWWEgySTVYd1hDcHRDCm42N0pDZzQyZitrOHdnemNSVnA4WFprWldaVmp3cTlSVUtEWG1GQjJZeU4xWEVXZFowZXdSdUtZVUpsc202OTIKc2tPcktRajB2a29QbjQxRUUvK1RhVkVwcUxUUm9VWTNyemc3RGtkemZkQml6Rk8yZHNQTkZ4MkNXMGpYa05MdgpLbzI1Q1pyT2hYQUhBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLSEZDY3lPalp2b0hzd1VCTWRMClJkSEliMzgzcFdGeW5acS9MdVVvdnNWQTU4QjBDZzdCRWZ5NXZXVlZycTVSSWt2NGxaODFOMjl4MjFkMUpINnIKalNuUXgrRFhDTy9USkVWNWxTQ1VwSUd6RVVZYVVQZ1J5anNNL05VZENKOHVIVmhaSitTNkZBK0NuT0Q5cm4yaQpaQmVQQ0k1ckh3RVh3bm5sOHl3aWozdnZRNXpISXV5QmdsV3IvUXl1aTlmalBwd1dVdlVtNG52NVNNRzl6Q1Y3ClBwdXd2dWF0cWpPMTIwOEJqZkUvY1pISWc4SHc5bXZXOXg5QytJUU1JTURFN2IvZzZPY0s3TEdUTHdsRnh2QTgKN1dqRWVxdW5heUlwaE1oS1JYVmYxTjM0OWVOOThFejM4Zk9USFRQYmRKakZBL1BjQytHeW1lK2lHdDVPUWRGaAp5UkU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcWVpcCs3TXZOYWRJN2lmM01wUHJ3Z0pwYkg0N1JUTnBybVRTdENyWG5LaHRuNDFrCkcrZWNVTldCUU5semtzQndHTDBuY0NObzJhN2x2S2wxd2k5cTIrQW1RaGNjS1ZSQXEzTVhZNXQ3WGxZa1YxYkcKQ0pwaVJ6ejBza0lLWXFHN1BjZXc1UzNPaUFRTkdZMURzcGRENmh2VWlYc1RGZTkxaUNHdWF3MVVoZHErSkVwRwp3SStBM0kreTEzaFpWVng5aU9WOEZ3cWJRcCt1eVFvT05uL3BQTUlNM0VWYWZGMl 查看在 GitHub 托管的bookinfo-ingress.yaml
我們執行以下命令,可以載入金鑰和證書(本演示中為自簽名證書):
> kubectl apply -f bookinfo-secret.yaml
secret/bookinfo-secret unchanged
然後我們啟動 Ingress 資源:
> kubectl apply -f bookinfo-ingress.yaml
ingress.networking.k8s.io/bookinfo-ingress deleted
並驗證 Ingress Controller 是否添加了 Ingress 資源中定義的路由,輸出結果的最後一行的event就說明已經確認了這個資訊:
> kubectl describe ingress bookinfo-ingress ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal AddedOrUpdated 5s nginx-ingress-controller Configuration for ... ...default/bookinfo-ingress was added or updated
在本演示中,使用瀏覽器通過訪問 https://bookinfo.example.com/ 來查看bookinfo
應用(在本機的/etc/hosts檔中,已經添加了域名和IP的映射,這裡的域名為bookinfo.example.com,IP為35.233.133.188)。
頁面中“Book Reviews”部分的信息會根據對 bookinfo.yaml(下載)中定義的三個reviews
服務版本的依次請求而發生週期性改變。
接下來,我們開始檢查進入集群的流量。 首先運行 generate-traffic.sh 腳本,然後通過訪問 NGINX Ingress Controller 發佈的外網 IP 位址來請求productpage
服務,再運行nginx-meshctl top
命令來監控流量:
> nginxmesh-ctl top deploy/productpage-v1 Deployment Direction Resource Success Rate P99 P90 P50 ... productpage-v1 To details-v1 100.00% 3ms 3ms 2ms To reviews-v2 100.00% 99ms 90ms 20ms To reviews-v3 100.00% 99ms 85ms 18ms To reviews-v1 100.00% 20ms 17ms 9ms From nginx-ingress 100.00% 192ms 120ms 38ms ... NumRequests ... 14 ... 5 ... 5 ... 12
使用標準 NGINX VirtualServer 資源
發佈應用
下面我們來展示另一種發佈應用的方法,即使用 NGINX VirtualServer 資源發佈應用。 它是一種自定義NGINX Ingress Controller 資源,支援更複雜的流量處理,例如流量分割和基於內容的路由。
首先,我們刪除標準 Ingress 資源:
> kubectl delete -f bookinfo-ingress.yaml
ingress.networking.k8s.io “bookinfo-ingress” deleted
通過bookinfo-vs.yaml 檔使用Secret配置MTLS(第 7-8 行),這個Secret與bookinfo-ingress.yaml 中的Secret相同。
在bookinfo-vs.yaml的第 9–12 行的配置將productpage
服務定義為上游服務,第 13–24 行的配置將bookinfo.example.com 的所有GET
請求發送到上游。 對於除GET
以外的 HTTP 方法,它將返回狀態代碼405
。
apiVersion: k8s.nginx.org/v1 kind: VirtualServer metadata: name: bookinfo-vs spec: host: bookinfo.example.com tls: secret: bookinfo-secret upstreams: - name: product service: productpage port: 9080 routes: - path: / matches: - conditions: - variable: $request_method value: GET action: pass: product action: return: code: 405 body: "Method not allowed\n" 查看在 GitHub 托管的 bookinfo-vs.yaml
下面來建立 NGINX VirtualServer 資源:
> kubectl apply -f bookinfo-vs.yaml
virtualserver.kubernetes.nginx.org/bookinfo-vs created
然後執行與 Ingress 資源相同的步驟,即通過運行kubectl describe
命令來確認應用已經部署正常,然後通過瀏覽器訪問該應用。 還可以通過看該應用是否拒絕POST方法來驗證應用的正常運行,具體操作如下:
> curl -k -X POST https://bookinfo.example.com/
Method not allowed
使用NGINX Ingress Controller
配置安全出向路由
現在我們展示如何通過 NGINX Ingress Controller 處理出向流量。
我們在bash.yaml中定義了一個簡單的bash
pod ,並將其部署在default的命名空間中,然後在該命名空間中進行查看。 如下方輸出結果中的READY
列所示,我們已向其注入了 NGINX Service Mesh sidecar。
> kubectl get all NAME READY STATUS RESTARTS AGE pod/bash-6ccb678958-zsgm7 2/2 Running 0 77s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.31.240.1 <none> 443/TCP 4d2h ...
在一些其他案例中,您可能想要從 pod 中請求出向服務,請求的服務不屬於 NGINX Service Mesh提供的物件。 這些部署的服務示例位於:
- 在整合外
- 在另一個整合上
- 在同一整合中,但未注入 NGINX Service Mesh sidecar
在本演示中,我們確定了示例的最終狀態。 我們在legacy 命名空間(不受 NGINX Service Mesh 控制,並禁用 NGINX Service Mesh sidecar 自動注入功能)中部署了一個應用,並只有一個 pod 運行。
> kubectl get all --namespaces=legacy NAME READY STATUS RESTARTS AGE pod/target-5f7bcb96c6-km9lz 1/1 Running 0 27m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/target-svc ClusterIP 10.31.245.213 <none> 80/TCP,443/TCP 27m ...
由於我們為 NGINX Service Mesh 配置了一個strict
mTLS 策略,因此,我們無法直接從bash
pod 向目標服務發送請求,原因是他們之間沒有互相進行身份驗證。 如果我們堅持進行訪問的話,我們會得到狀態代碼503
(如下圖所示):
> kubectl exec -it bash-6ccb678958-zsgm7 -c bash — curl target-svc.legacy
curl: (56) Recv failure: connection reset by peer
503command terminated with exit code 56
解決方案是 讓bash
pod發送出向流量時,該流量通過NGINX Ingress Controller來發送,操作方式是取消bash.yaml 第 14-15 行的annotations:
#annotations:
#config.nsm.nginx.com/default-egress-allowed: “true” # uncomment to route egress traffic to NGINX Ingress Controller
查看在 GitHub 托管的 bash.yaml
然後應用新設定:
> kubectl apply -f bash.yaml
deployment.apps/bash configured
並驗證新的bash
pod 是否已正常啟動:
> kubectl get pods
NAME READY STATUS RESTARTS AGE
bash-678c8b4579-7sfml 2/2 Running 0 6s
bash-6ccb678958-zsgm7 2/2 Terminating 0 3m28s
現在,我們運行和前面一樣的kubectl exec
命令,使得bash
pod 向目標服務發送請求,這樣會得到狀態代碼404
,而不是503
。 這表明bash
已經成功將請求發送到 NGINX Ingress Controller,但由於沒有定義路由,所以不知道將它轉發到哪裡。
我們在 legacy-route.yaml 中使用以下 Ingress 資源創建所需的路由。 第 7 行配置的internal-route
,表示目標服務不會發佈給互聯網,而只發佈給 NGINX Service Mesh 中的工作負載。
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: target-internal-route namespace: legacy annotations: nsm.nginx.com/internal-route: "true" spec: ingressClassName: nginx # use only with k8s version >= 1.18.0 tls: rules: - host: target-svc.legacy https: paths: - path: / backend: serviceName: target-svc servicePort: 80 查看在 GitHub 托管的legacy-route.yaml
通過下列命令啟動新資源並確認 NGINX Ingress Controller 已添加相應的路由:
> kubectl apply -f legacy-route.yaml ingress.networking.k8s.io/target-internal-route created > kubectl describe ingress target-internal-route -n legacy... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal AddedOrUpdated 6s nginx-ingress-controller Configuration for ... ...legacy/target-internal-route was added or updated
現在,當我們運行kubectl exec
命令時,就可以訪問目標服務了:
{“req”: {“method”: “GET”
“url”: “/”,
“host”: “target-svc.legacy”,
“remoteAddr”: “10.28.2.76:56086”}}
通過 NGINX Ingress Controller 路由出向流量的優勢是,您可以準確控制從集群內部訪問的外部服務(僅指定義了路由的外部服務)。
最後一項演示內容是如何監控出向流量。 通過運行kubectl exec
命令來發送幾個請求,然後運行以下命令:
> nginxmesh-ctl top deploy/nginx-ingress -n nginx-ingress Deployment Direction Resource Success Rate P99 P90 P50 NumRequests nginx-ingress To target 100.00% 1ms 1ms 1ms 9 From bash 100.00% 0ms 0ms 0ms 9
拒絕延遲
整合NGINX Service Mesh
與NGINX Ingress Controller
雖然許多服務Service mesh都提供出入向閘道選項,但NGINX還具有低延遲優勢。 大多數服務Service mesh都需要向Ingress Controller 注入 sidecar,因此流量需要額外一跳才能到達目的應用。 雖然延遲很小,但這額外一跳會影響用戶體驗,進而導致客戶流失。
NGINX Service Mesh 不會增加這樣的延遲,因為它不需要將 sidecar 注入到 NGINX Ingress Controller。 通過直接集成 Spire(服務Service mesh的證書授權中心),NGINX Ingress Controller 可以成為NGINX Service Mesh 的一部分。
NGINX Ingress Controller 只從 Spire 代理獲取證書和秘鑰,並使用它們參與 MTLS 與網狀 pod 的證書交換。
Kubernetes NGINX Ingress Controller 有兩個版本:NGINX 開源版和 NGINX Plus 版。
如要使用本文所述的使用 NGINX Service Mesh 部署 NGINX Ingress Controller,您必須使用 NGINX Plus 版(支援 30 天免費試用)。
NGINX Service Mesh 完全免費,您可立即下載並在 10 分鐘內完成部署! 有關使用方法,請查看我們的文檔,並通過 GitHub 告訴我們您的使用體驗。