DNS 代理
除了擷取應用程式流量外,Istio 還可以擷取 DNS 請求,以提高網格的效能和可用性。在代理 DNS 時,應用程式的所有 DNS 請求都將重新導向至 sidecar,後者會儲存網域名稱到 IP 位址的本機對應。如果 sidecar 可以處理請求,它會直接將回應傳回應用程式,避免往返上游 DNS 伺服器。否則,請求會按照標準 /etc/resolv.conf
DNS 設定向上游轉送。
雖然 Kubernetes 為 Kubernetes Service
提供開箱即用的 DNS 解析,但任何自訂的 ServiceEntry
都無法辨識。使用此功能,可以解析 ServiceEntry
位址,而無需自訂設定 DNS 伺服器。對於 Kubernetes Service
,DNS 回應會相同,但 kube-dns
上的負載會降低,且效能會提高。
此功能也適用於在 Kubernetes 外部執行的服務。這表示可以解析所有內部服務,而無需使用笨拙的解決方案將 Kubernetes DNS 項目公開到叢集外部。
開始使用
此功能目前預設為停用。若要啟用它,請使用下列設定安裝 Istio
$ cat <<EOF | istioctl install -y -f -
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
meshConfig:
defaultConfig:
proxyMetadata:
# Enable basic DNS proxying
ISTIO_META_DNS_CAPTURE: "true"
# Enable automatic address allocation, optional
ISTIO_META_DNS_AUTO_ALLOCATE: "true"
EOF
這也可以在每個 Pod 的基礎上啟用,使用 proxy.istio.io/config
註解。
kind: Deployment
metadata:
name: curl
spec:
...
template:
metadata:
annotations:
proxy.istio.io/config: |
proxyMetadata:
ISTIO_META_DNS_CAPTURE: "true"
ISTIO_META_DNS_AUTO_ALLOCATE: "true"
...
DNS 擷取實際運作
要嘗試 DNS 擷取功能,首先為某些外部服務設定一個 ServiceEntry
。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: external-address
spec:
addresses:
- 198.51.100.1
hosts:
- address.internal
ports:
- name: http
number: 80
protocol: HTTP
EOF
啟動一個用戶端應用程式來發起 DNS 請求。
$ kubectl label namespace default istio-injection=enabled --overwrite
$ kubectl apply -f @samples/curl/curl.yaml@
在沒有 DNS 擷取的情況下,對 address.internal
的請求可能會無法解析。一旦啟用此功能,您應該會根據設定的 address
得到回應。
$ kubectl exec deploy/curl -- curl -sS -v address.internal
* Trying 198.51.100.1:80...
位址自動分配
在上述範例中,您有一個預先定義的 IP 位址,用於您發送請求的服務。然而,存取沒有穩定位址,而是依賴 DNS 的外部服務是很常見的。在這種情況下,DNS 代理將沒有足夠的資訊來返回回應,並且需要將 DNS 請求轉發到上游。
這對於 TCP 流量尤其麻煩。與基於 Host
標頭路由的 HTTP 請求不同,TCP 攜帶的資訊少得多;您只能根據目標 IP 和埠號進行路由。因為您沒有後端的穩定 IP,所以您也無法根據該 IP 進行路由,只剩下埠號,這會導致多個 TCP 服務的 ServiceEntry
共享相同埠時產生衝突。請參閱以下章節以了解更多詳細資訊。
為了解決這些問題,DNS 代理還支援為未明確定義位址的 ServiceEntry
自動分配位址。這由 ISTIO_META_DNS_AUTO_ALLOCATE
選項配置。
啟用此功能時,DNS 回應將包含每個 ServiceEntry
的不同且自動分配的位址。然後,代理會被配置為匹配此 IP 位址的請求,並將請求轉發到對應的 ServiceEntry
。當使用 ISTIO_META_DNS_AUTO_ALLOCATE
時,只要服務不使用萬用字元主機,Istio 就會自動將不可路由的 VIP(來自 E 類子網路)分配給這些服務。Sidecar 上的 Istio 代理將使用 VIP 作為來自應用程式的 DNS 查詢的回應。Envoy 現在可以清楚地區分每個外部 TCP 服務的流量,並將其轉發到正確的目標。有關更多資訊,請查看有關智慧 DNS 代理的相關 Istio 部落格文章。
要嘗試此功能,請設定另一個 ServiceEntry
。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: external-auto
spec:
hosts:
- auto.internal
ports:
- name: http
number: 80
protocol: HTTP
resolution: DNS
EOF
現在,發送一個請求。
$ kubectl exec deploy/curl -- curl -sS -v auto.internal
* Trying 240.240.0.1:80...
如您所見,請求會被發送到自動分配的位址 240.240.0.1
。這些位址將從 240.240.0.0/16
保留的 IP 位址範圍中選取,以避免與真實服務衝突。
沒有 VIP 的外部 TCP 服務
預設情況下,Istio 在路由外部 TCP 流量時存在限制,因為它無法區分同一埠上的多個 TCP 服務。當使用 AWS Relational Database Service 等第三方資料庫或任何具有地理冗餘設定的資料庫時,此限制尤其明顯。預設情況下,類似但不同的外部 TCP 服務無法單獨處理。為了讓 Sidecar 區分網格外部的兩個不同 TCP 服務之間的流量,這些服務必須位於不同的埠上,或者它們需要具有全域唯一的 VIP。
例如,如果您有兩個外部資料庫服務 mysql-instance1
和 mysql-instance2
,並且您為兩者建立服務條目,用戶端 Sidecar 仍然會在 0.0.0.0:{port}
上有一個單一監聽器,它會從公共 DNS 伺服器查找 mysql-instance1
的 IP 位址,並將流量轉發到該位址。它無法將流量路由到 mysql-instance2
,因為它無法區分到達 0.0.0.0:{port}
的流量是發往 mysql-instance1
還是 mysql-instance2
。
以下範例說明如何使用 DNS 代理來解決此問題。每個服務條目都會分配一個虛擬 IP 位址,以便用戶端 Sidecar 可以清楚地區分每個外部 TCP 服務的流量。
更新 入門指南 章節中指定的 Istio 配置,也配置
discoverySelectors
,將網格限制為啟用istio-injection
的命名空間。這將允許我們在叢集中的任何其他命名空間中執行網格外部的 TCP 服務。$ cat <<EOF | istioctl install -y -f - apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: meshConfig: defaultConfig: proxyMetadata: # Enable basic DNS proxying ISTIO_META_DNS_CAPTURE: "true" # Enable automatic address allocation, optional ISTIO_META_DNS_AUTO_ALLOCATE: "true" # discoverySelectors configuration below is just used for simulating the external service TCP scenario, # so that we do not have to use an external site for testing. discoverySelectors: - matchLabels: istio-injection: enabled EOF
部署第一個外部範例 TCP 應用程式。
$ kubectl create ns external-1 $ kubectl -n external-1 apply -f samples/tcp-echo/tcp-echo.yaml
部署第二個外部範例 TCP 應用程式。
$ kubectl create ns external-2 $ kubectl -n external-2 apply -f samples/tcp-echo/tcp-echo.yaml
配置
ServiceEntry
以連線外部服務。$ kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: ServiceEntry metadata: name: external-svc-1 spec: hosts: - tcp-echo.external-1.svc.cluster.local ports: - name: external-svc-1 number: 9000 protocol: TCP resolution: DNS --- apiVersion: networking.istio.io/v1 kind: ServiceEntry metadata: name: external-svc-2 spec: hosts: - tcp-echo.external-2.svc.cluster.local ports: - name: external-svc-2 number: 9000 protocol: TCP resolution: DNS EOF
驗證用戶端上的每個服務是否已單獨配置監聽器。
$ istioctl pc listener deploy/curl | grep tcp-echo | awk '{printf "ADDRESS=%s, DESTINATION=%s %s\n", $1, $4, $5}' ADDRESS=240.240.105.94, DESTINATION=Cluster: outbound|9000||tcp-echo.external-2.svc.cluster.local ADDRESS=240.240.69.138, DESTINATION=Cluster: outbound|9000||tcp-echo.external-1.svc.cluster.local
清理
$ kubectl -n external-1 delete -f @samples/tcp-echo/tcp-echo.yaml@
$ kubectl -n external-2 delete -f @samples/tcp-echo/tcp-echo.yaml@
$ kubectl delete -f @samples/curl/curl.yaml@
$ istioctl uninstall --purge -y
$ kubectl delete ns istio-system external-1 external-2
$ kubectl label namespace default istio-injection-