istio: 無法提供內部訪問外部服務


現象

能夠內部無法訪問外部服務。

  1. 在部署測試服務
kubectl apply -f samples/sleep/sleep.yaml
  1. 設置環境變量
export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
  1. 訪問外網服務
$ kubectl exec -it $SOURCE_POD -c sleep -- curl http://www.baidu.com
$ kubectl exec -it $SOURCE_POD -c sleep -- curl -I https://www.baidu.com | grep  "HTTP/"
command terminated with exit code 35

進入sleep容器,執行curl http://www.baidu.com,無數據返回;執行curl -I https://www.baidu.com也訪問失敗。這種現象就是內部無法訪問外部服務。

原因

由於某些情況下,來自Istio-enable Pod的所有出站流量都會重定向到其Sidecar代理,外部外部URL的可訪問性替代代理的配置。在其他情況下,Istio將使Envoy代理配置為允許傳遞未知服務的請求。但這不是Istio推薦的做法,因此,商服務或者其他安裝配置可能會修改默認設置為不允許傳遞未知服務的請求。

當遇到遇到內部無法訪問外部服務時,可運行以下命令檢查Sidecar的代理配置,查看是否允許傳遞未知服務的請求。

$ kubectl get configmap istio -n istio-system -o yaml | grep -o "mode: ALLOW_ANY"
mode: ALLOW_ANY
mode: ALLOW_ANY
# 或者
$ kubectl get configmap istio -n istio-system -o yaml | grep -o "mode: REGISTRY_ONLY"
mode: REGISTRY_ONLY
mode: REGISTRY_ONLY

如果命令輸出有mode: ALLOW_ANY表示Istio代理允許調用未知的服務;如果命令輸出有mode: REGISTRY_ONLY那么Istio代理會阻止任何沒有在網格中定義的HTTP服務或service entry的主機

方法

如果是因為Istio的配置項為mode: REGISTRY_ONLY而導致內部無法訪問外部服務。可有以下三種訪問外部服務的方法:

  • 方法一:修改配置為mode: ALLOW_ANY以允許Sidecar將請求傳遞到未在網格內部配置過的服務。
  • 方法二:配置ServiceEntry以提供對外部服務的預期訪問。
  • 方法三:對於特定范圍的IP,完全繞過Envoy代理。

方法一

運行以下命令,修改配置為 mode: ALLOW_ANY

$ kubectl get configmap istio -n istio-system -o yaml | sed 's/mode: REGISTRY_ONLY/mode: ALLOW_ANY/g' | kubectl replace -n istio-system -f -
configmap/istio replaced

驗證配置

$ kubectl get configmap istio -n istio-system -o yaml | grep -o "mode: ALLOW_ANY"
mode: ALLOW_ANY
mode: ALLOW_ANY

重啟部署

$ kubectl rollout restart deployment sleep
deployment.extensions/sleep restarted

訪問外部服務

$ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
$ kubectl exec -it $SOURCE_POD -c sleep -- curl http://www.baidu.com
<!DOCTYPE html>
......
京ICP證030173號&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
$ kubectl exec -it $SOURCE_POD -c sleep -- curl -I https://www.baidu.com | grep  "HTTP/"
HTTP/1.1 200 OK

注:這種訪問外部服務的簡單方法有一個缺點,即丟失了對外部服務流量的Istio監控和控制

方法二

在開始方法二之前,需要先修改Istio的配置項為mode: REGISTRY_ONLY

$ kubectl get configmap istio -n istio-system -o yaml | sed 's/mode: ALLOW_ANY/mode: REGISTRY_ONLY/g' | kubectl replace -n istio-system -f -
configmap "istio" replaced

允許訪問外部HTTP服務

  1. 添加ServiceEntry,允許訪問httpbin的HTTP服務
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: httpbin-ext
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 80
    name: http
    protocol: HTTP
  resolution: DNS
  location: MESH_EXTERNAL
EOF
  1. 從sleep向外部發送httpbin的HTTP服務請求
$ kubectl exec -it $SOURCE_POD -c sleep -- curl http://httpbin.org/headers
{
  "headers": {
    "Accept": "*/*",
    "Content-Length": "0",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.64.0",
    "X-Amzn-Trace-Id": "Root=1-5e89e3ba-e22faf007e305d9897cddf1e",
    "X-B3-Sampled": "1",
    "X-B3-Spanid": "338f3faa38eb194e",
    "X-B3-Traceid": "d0ff4841f30240b2338f3faa38eb194e",
    "X-Envoy-Decorator-Operation": "httpbin.org:80/*",
    "X-Envoy-Peer-Metadata": "ChwKDElOU1RBTkNFX0lQUxIMGgoxMC4xLjAuMjQ1CsMBCgZMQUJFTFMSuAEqtQEKDgoDYXBwEgcaBXNsZWVwCiAKEXBvZC10ZW1wbGF0ZS1oYXNoEgsaCWM2OWQ5NmM5YwokChlzZWN1cml0eS5pc3Rpby5pby90bHNNb2RlEgcaBWlzdGlvCioKH3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLW5hbWUSBxoFc2xlZXAKLwojc2VydmljZS5pc3Rpby5pby9jYW5vbmljYWwtcmV2aXNpb24SCBoGbGF0ZXN0ChoKB01FU0hfSUQSDxoNY2x1c3Rlci5sb2NhbAofCgROQU1FEhcaFXNsZWVwLWM2OWQ5NmM5Yy0yZnh6ZAoWCglOQU1FU1BBQ0USCRoHZGVmYXVsdApJCgVPV05FUhJAGj5rdWJlcm5ldGVzOi8vYXBpcy9hcHBzL3YxL25hbWVzcGFjZXMvZGVmYXVsdC9kZXBsb3ltZW50cy9zbGVlcAoaCg9TRVJWSUNFX0FDQ09VTlQSBxoFc2xlZXAKGAoNV09SS0xPQURfTkFNRRIHGgVzbGVlcA==",
    "X-Envoy-Peer-Metadata-Id": "sidecar~10.1.0.245~sleep-c69d96c9c-2fxzd.default~default.svc.cluster.local"
  }
}

注:返回數據中可以看到Istio sidecar代理添加的標題:X-Envoy-Decorator-Operation,表示訪問外部的流量經過了sidecar

  1. 檢查睡眠的邊車
1
2
$  kubectl logs $SOURCE_POD -c istio-proxy | tail
[2020-04-05T13:57:13.948Z] "GET /headers HTTP/1.1" 200 - "-" "-" 0 1117 629 628 "-" "curl/7.64.0" "d94cc283-11ef-949c-8ba0-d948ad5785ab" "httpbin.org" "52.202.2.199:80" outbound|80||httpbin.org 10.1.0.245:48116 34.230.193.231:80 10.1.0.245:56246 - default

允許訪問外部HTTPS服務

  1. 添加ServiceEntry,允許訪問httpbin的HTTPS服務
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: httpbin-https-ext
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS
  location: MESH_EXTERNAL
EOF
  1. 從sleep向外部發送httpbin的HTTPS服務請求
$ kubectl exec -it $SOURCE_POD -c sleep -- curl -I https://httpbin.org/headers
HTTP/2 200
date: Sun, 05 Apr 2020 14:10:25 GMT
content-type: application/json
content-length: 173
server: gunicorn/19.9.0
access-control-allow-origin: *
access-control-allow-credentials: true
  1. 檢查睡眠的邊車
$ kubectl logs $SOURCE_POD -c istio-proxy | tail
[2020-04-05T14:10:23.939Z] "- - -" 0 - "-" "-" 941 5597 1695 - "-" "-" "-" "-" "3.232.168.170:443" outbound|443||httpbin.org 10.1.0.245:36844 3.232.168.170:443 10.1.0.245:36838 httpbin.org -

管理到外部服務的流量

通過ServiceEntry配置訪問的外部服務,可以向內部的請求相同的設置Istio路由規則。下面以httpbin作為替代,設置對服務訪問的超時規則。

  1. 向外部服務httpbin.org的/ delay端點發出curl請求:
$ kubectl exec -it $SOURCE_POD -c sleep sh
/ # time curl -o /dev/null -s -w "%{http_code}\n" http://httpbin.org/delay/5
200
real    0m 6.12s
user    0m 0.00s
sys     0m 0.00s
/ #
  1. 這個請求大約在5秒內返回200(OK)。

  2. 使用kubectl設置調用外部服務httpbin.org的超時時間為3秒。

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin-ext
spec:
  hosts:
    - httpbin.org
  http:
  - timeout: 3s
    route:
      - destination:
          host: httpbin.org
        weight: 100
EOF
  1. 幾秒后,重新發出curl請求:
1
2
3
4
5
6
$ kubectl exec -it $SOURCE_POD -c sleep sh
/ # time curl -o /dev/null -s -w "%{http_code}\n" http://httpbin.org/delay/5
504
real    0m 3.28s
user    0m 0.00s
sys     0m 0.00s

這一次,在3秒后出現了504(Gateway Timeout)。Istio在3秒后切斷了響應時間為5秒的httpbin.org服務。

方法三

在開始方法三之前,需要先清理對外部服務的預先訪問

$ kubectl delete serviceentry baidu-ext httpbin-ext httpbin-https-ext
serviceentry.networking.istio.io "baidu-ext" deleted
serviceentry.networking.istio.io "httpbin-ext" deleted
serviceentry.networking.istio.io "httpbin-https-ext" deleted
$ kubectl delete virtualservice httpbin-ext --ignore-not-found=true
virtualservice.networking.istio.io "httpbin-ext" deleted

直接訪問外部服務

如果要讓特定范圍的IP完全繞過Istio,則可以配置Envoy sidecars以防止其攔截外部請求。要設置繞過Istio,請更改global.proxy.includeIPRangesglobal.proxy.excludeIPRanges配置選項,並使用kubectl apply命令更新istio-sidecar-injector的配置。istio-sidecar-injector配置的更新,影響的是新部署應用的pod

注:與方法一使用mode: ALLOW_ANY流量策略來讓Istio sidecar代理將調用傳遞給未知服務不同。更改global.proxy.includeIPRangesglobal.proxy.excludeIPRanges配置選項的方法,完全繞過了sidecar,從而替換了指定IP的所有Istio功能。您不能mode: ALLOW_ANY方法為那樣的特定目標增量添加服務項。因此,僅當出於性能或其他原因無法使用三輪配置外部訪問時,才建議使用此配置方法

排除所有外部IP重置到Sidecar代理的一種簡單方法是將global.proxy.includeIPRanges配置選項設置為內部可用服務使用的IP范圍。這些IP范圍值可以取代所在的平台

確定平台內部的IP范圍
阿里雲(AKS)

注:這里先提下阿里雲,因為其設置與其他平台不同

  1. 查看更多信息,獲取Pod網絡CIDR和Service CIDR,如172.20.0.0/16和172.21.0.0/20
    image
  2. 點擊Istio管理,單擊更新,找到includeIPRanges並設置為Pod網絡CIDR和Service CIDR
    image
IBM Cloud Private
  1. 從IBM Cloud Private的配置文件cluster / config.yaml中獲取您的service_cluster_ip_range:
$ cat cluster/config.yaml | grep service_cluster_ip_range
service_cluster_ip_range: 10.0.0.1/24 //這里以 10.0.0.1/24 為例
  1. 使用 --set global.proxy.includeIPRanges="10.0.0.1/24"
IBM Cloud Kubernetes服務

使用 --set global.proxy.includeIPRanges="172.30.0.0/16\,172.21.0.0/16\,10.10.10.0/24"

Google容器引擎(GKE)

范圍是不固定的,你需要運行gcloud容器集群描述了命令來確定要使用的范圍。舉個例子:

$ gcloud container clusters describe XXXXXXX --zone=XXXXXX | grep -e clusterIpv4Cidr -e servicesIpv4Cidr
clusterIpv4Cidr: 10.4.0.0/14
servicesIpv4Cidr: 10.7.240.0/20

使用 --set global.proxy.includeIPRanges="10.4.0.0/14\,10.7.240.0/20"

Azure容器服務(ACS)

使用 --set global.proxy.includeIPRanges="10.244.0.0/16\,10.240.0.0/16

Minikube,用於桌面的Docker,裸機

默認值10.96.0.0/12,但不是固定的。使用以下命令確定您的實際值:

$ kubectl describe pod kube-apiserver -n kube-system | grep 'service-cluster-ip-range'
      --service-cluster-ip-range=10.96.0.0/12

使用–set global.proxy.includeIPRanges =“ 10.96.0.0/12”

配置代理繞行

使用平台的IP范圍更新istio-sidecar-injector的配置。例如,如果IP范圍是10.0.0.1/24,則使用一下命令:

istioctl manifest apply <the flags you used to install Istio> --set values.global.proxy.includeIPRanges="10.0.0.1/24"

安裝Istio命令的基礎上增加--set values.global.proxy.includeIPRanges="10.0.0.1/24"

注:若你是按照本專欄開發環境搭建教程構建環境的話,則使用istioctl manifest apply --set profile=demo --set values.global.proxy.includeIPRanges="10.96.0.0/12"命令配置代理繞行即可。

訪問外部服務

由於繞行配置僅影響新的部署,因此需要重新部署sleep程序。

$ kubectl delete deployment sleep
deployment.extensions "sleep" deleted
$ kubectl apply -f samples/sleep/sleep.yaml
serviceaccount/sleep unchanged
service/sleep unchanged
deployment.apps/sleep created

在更新istio-sidecar-injector configmap和重新部署sleep程序后,Istio sidecar將僅攔截並管理其內部的內部請求。任何外部請求都會繞過Sidecar,並直接到達其預期的目的地。舉個例子:

$ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
$ kubectl exec -it $SOURCE_POD -c sleep curl http://httpbin.org/headers
{
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.64.0",
    "X-Amzn-Trace-Id": "Root=1-5e89f4e2-524fd7a41c99025a43c403ce"
  }
}

與通過HTTP和HTTPS訪問外部服務不同,你不會看到任何與Istio sidecar有關的請求頭,並且發送到外部服務的請求不會出現在Sidecar的日志中。對外部服務的訪問。

總結

本文先介紹了允許內部無法訪問外部服務的現象,然后擴展了該現象的原因,最后提供了三個方法供大家替換。

方法一通過修改Istio sidecar代理配置為mode: ALLOW_ANY以允許訪問外部服務。使用這種方法時,您將無法監控對外部服務的訪問無法利用Istio的流量控制功能

方法二通過添加ServiceEntry,以允許訪問外部服務。可以讓你使用Istio服務網格所有的功能去調用內部或轉換外部的服務,這是官方推薦的方法

三方法直接繞過了Istio邊門代理,使你的服務可以直接訪問任意的外部服務。但是,這種以配置方式代理需要了解集群提供商相關知識狀語從句:配置。與方法一類似,也。你將失去對外部服務訪問的監控,並且無法將Istio功能添加到外部服務

方法二的做法對准“白名單”,不但能達到訪問外部服務的目的,並且可以像內置內部服務一樣對待(可使用Istio的流量控制功能)。另外,甚至服務遭到入侵,由於“白名單”的設置入侵者也無法(或較難)將流量回傳到入侵機器,進一步保證了服務的安全性。因此,強烈推薦大家使用方法二

最后,特別提醒一下大家。某些教程,如Istio服務無法訪問外網,開放外網權限includeOutboundIPRanges設置為空是有問題的,這相當於將所有的服務都配置代理繞行,那sidecar就沒起作用了,沒了sidecar的Istio就沒有靈魂了。。

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM