Istio 常見問題 - 集羣內無法訪問外部服務

原文:https://makeoptim.com/istio-faq/accessing-external-services

現象

集羣內無法訪問外部服務。

  1. 在集羣中部署 sleep

    kubectl apply -f samples/sleep/sleep.yaml
    
    
  2. 設置環境變量

    export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
    
    
  3. 訪問外網服務

    $ 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
    
    
  2. 從 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 代理添加的 headers: X-Envoy-Decorator-Operation,表示訪問外部的流量經過了 sidecar

  3. 檢查 sleep 的 sidecar 日誌

    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
    
    
  2. 從 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
    
    
  3. 檢查 sleep 的 sidecar 日誌

    $ 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 endpoint 發出 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
    / #
    
    

    這個請求大約在 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
    
    
  3. 幾秒後,重新發出 curl 請求:

    $ 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 方法那樣爲特定的目標增量添加 service entries。 因此,僅當出於性能或其他原因無法使用 sidecar 配置外部訪問時,才建議使用此配置方法

排除所有外部 IP 重定向到 Sidecar 代理的一種簡單方法是將 global.proxy.includeIPRanges 配置選項設置爲內部集羣服務使用的 IP 範圍。這些 IP 範圍值取決於集羣所在的平臺

確定平臺內部的 IP 範圍
阿里雲(AKS)

注:這裏先提下阿里雲,因爲其設置與其他平臺不同

  1. 查看集羣信息,獲取 Pod 網絡 CIDR 和 Service CIDR,如 172.20.0.0/16 和 172.21.0.0/20

  2. 點擊 Istio 管理,點擊更新,找到 includeIPRanges 並設置爲 Pod 網絡 CIDR 和 Service CIDR

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 爲例
    
    
  2. 使用 --set global.proxy.includeIPRanges="10.0.0.1/24"

IBM Cloud Kubernetes Service

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

Google Container Engine (GKE)

範圍是不固定的,你需要運行 gcloud container clusters describe 命令來確定要使用的範圍。舉個例子:

$ 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 Container Service(ACS)

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

Minikube, Docker For Desktop, Bare Metal

默認值爲 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 意味着你不能再監視對外部服務的訪問。

總結

本文先介紹了集羣內無法訪問外部服務的現象,然後闡述了該現象的原因,最後提供了三個方法供大家選用。

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

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

方法三直接繞過了 Istio Sidecar 代理,使你的服務可以直接訪問任意的外部服務。 但是,以這種方式配置代理需要了解集羣提供商相關知識和配置。 與方法一類似,你也將失去對外部服務訪問的監控,並且無法將 Istio 功能應用於外部服務的流量

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

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

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章