溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Istio無(wú)頭服務(wù)問(wèn)題怎么解決

發(fā)布時(shí)間:2022-01-11 17:50:19 來(lái)源:億速云 閱讀:121 作者:iii 欄目:云計(jì)算

這篇文章主要介紹“Istio無(wú)頭服務(wù)問(wèn)題怎么解決”,在日常操作中,相信很多人在Istio無(wú)頭服務(wù)問(wèn)題怎么解決問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Istio無(wú)頭服務(wù)問(wèn)題怎么解決”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

什么是『無(wú)頭服務(wù)』?

『無(wú)頭服務(wù)』即 Kubernetes 中的 Headless Service。Service 是 Kubernetes 對(duì)后端一組提供相同服務(wù)的 Pod 的邏輯抽象和訪問(wèn)入口。Kubernetes 會(huì)根據(jù)調(diào)度算法為 Pod 分配一個(gè)運(yùn)行節(jié)點(diǎn),并隨機(jī)分配一個(gè) IP 地址;在很多情況下,我們還會(huì)對(duì) Pod 進(jìn)行水平伸縮,啟動(dòng)多個(gè) Pod 來(lái)提供相同的服務(wù)。在有多個(gè) Pod 并且 Pod IP 地址不固定的情況下,客戶端很難通過(guò) Pod 的 IP 地址來(lái)直接進(jìn)行訪問(wèn)。為了解決這個(gè)問(wèn)題,Kubernetes 采用 Service 資源來(lái)表示提供相同服務(wù)的一組 Pod。

在缺省情況下,Kubernetes 會(huì)為 Service 分配一個(gè) Cluster IP,不管后端的 Pod IP 如何變化,Service 的 Cluster IP 始終是固定的。因此客戶端可以通過(guò)這個(gè) Cluster IP 來(lái)訪問(wèn)這一組 Pod 提供的服務(wù),而無(wú)需再關(guān)注后端的各個(gè)真實(shí)的 Pod IP。我們可以將 Service 看做放在一組 Pod 前的一個(gè)負(fù)載均衡器,而 Cluster IP 就是該負(fù)載均衡器的地址,這個(gè)負(fù)載均衡器會(huì)關(guān)注后端這組 Pod 的變化,并把發(fā)向 Cluster IP 的請(qǐng)求轉(zhuǎn)發(fā)到后端的 Pod 上。(備注:這只是對(duì) Service 的一個(gè)簡(jiǎn)化描述,如果對(duì) Service 的內(nèi)部實(shí)現(xiàn)感興趣,可以參考這篇文章 如何為服務(wù)網(wǎng)格選擇入口網(wǎng)關(guān)?)

對(duì)于無(wú)狀態(tài)的應(yīng)用來(lái)說(shuō),客戶端并不在意其連接的是哪一個(gè) Pod,采用 Service 是沒有問(wèn)題的。但在某些特殊情況下,并不能這樣做。例如,如果后端的這一組 Pod 是有狀態(tài)的,需要由客戶端根據(jù)某種應(yīng)用相關(guān)的算法來(lái)選擇哪一個(gè) Pod 提供服務(wù);或者客戶端需要連接所有的后端 Pod,這時(shí)我們就不能在這一組 Pod 前放一個(gè)負(fù)載均衡器了。這種情況下,我們需要采用 Headless Service,即無(wú)頭服務(wù)(該命名把多個(gè) Pod 前面的負(fù)載均衡器比作服務(wù)的頭,很形象是不是?)。在定義 Headless Service,我們需要把 Service 的 Cluster IP 顯示設(shè)置為 None,這樣 Kubernetes DNS 在解析該 Service 時(shí)會(huì)直接返回其后端的多個(gè) Pod IP,而不是 Service 的 Cluster IP。

假設(shè)從客戶端訪問(wèn)一個(gè) Redis 集群,分別采用帶 Cluster IP 的普通 Service 和 Headless Service 進(jìn)行訪問(wèn)的過(guò)程如下圖所示:

Istio無(wú)頭服務(wù)問(wèn)題怎么解決

Istio 中『無(wú)頭服務(wù)』的 mTLS 故障

由于 Headless Service 的特殊性,Istio 中對(duì) Headless Service 的處理和普通 Service 有所不同,在應(yīng)用遷移到 Isito 的過(guò)程中也常常遇到由于 Headless Service 導(dǎo)致的一些問(wèn)題。下面我們就以一個(gè)由于 Headless Service 的 mTLS 故障導(dǎo)致的典型案例進(jìn)行說(shuō)明。

故障現(xiàn)象:運(yùn)維同學(xué)反饋從帶 Envoy Sidecar 的 Pod 中訪問(wèn) Redis 服務(wù)器,但在沒有安裝 Sidecar 的 Pod 中可以正常訪問(wèn)該 Redis 服務(wù)器。

遇到無(wú)法進(jìn)行出向訪問(wèn)的問(wèn)題,我們可以首先通過(guò) Envoy 的管理接口來(lái)查看 Envoy 的訪問(wèn)日志。在客戶端 Pod 中運(yùn)行下面的命令查看 Envoy 日志:

kubectl logs -f redis-client-6d4c6c975f-bm5w6 -c istio-proxy

日志中對(duì) Redis 的訪問(wèn)記錄如下,其中 UR,URX 是 Response Flag,表示 upstream connection failure,即連接上游失敗。

[2020-09-12T13:38:23.077Z] "- - -" 0 UF,URX "-" "-" 0 0 1001 - "-" "-" "-" "-" "10.1.1.24:6379" outbound|6379||redis.default.svc.cluster.local - 10.1.1.24:6379 10.1.1.25:45940 - -

我們可以通過(guò) Envoy 管理接口導(dǎo)出其 xDS 配置,以進(jìn)一步分析其失敗原因。

kubectl exec redis-client-6d4c6c975f-bm5w6 -c istio-proxy curl http://127.0.0.1:15000/config_dump

由于是出向訪問(wèn)錯(cuò)誤,因此我們主要關(guān)注客戶端中該出向訪問(wèn)的 Cluster 的配置。在導(dǎo)出的 xDS 配置中,可以看到 Redis Cluster 的配置,如下面的 yaml 片段所示(為了方便讀者查看,去掉了該 yaml 中一些無(wú)關(guān)的內(nèi)容):

{
     "version_info": "2020-09-13T00:33:43Z/5",
     "cluster": {
      "@type": "type.googleapis.com/envoy.api.v2.Cluster",
      "name": "outbound|6379||redis.default.svc.cluster.local",
      "type": "ORIGINAL_DST",
      "connect_timeout": "1s",
      "lb_policy": "CLUSTER_PROVIDED",
      "circuit_breakers": {
        ...
      },

      # mTLS 相關(guān)設(shè)置
      "transport_socket": {
       "name": "envoy.transport_sockets.tls",
       "typed_config": {
        "@type": "type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext",
        "common_tls_context": {
         "alpn_protocols": [
          "istio-peer-exchange",
          "istio"
         ],

         # 訪問(wèn) Redis 使用的客戶端證書
         "tls_certificate_sds_secret_configs": [
          {
           "name": "default",
           "sds_config": {
            "api_config_source": {
             "api_type": "GRPC",
             "grpc_services": [
              {
                "envoy_grpc": {
                "cluster_name": "sds-grpc"
               }
              }
             ]
            }
           }
          }
         ],

         "combined_validation_context": {
          "default_validation_context": {
           # 用于驗(yàn)證 Redis 服務(wù)器身份的 spiffe indentity
           "verify_subject_alt_name": [
            "spiffe://cluster.local/ns/default/sa/default"
           ]
          },
          # 用于驗(yàn)證 Redis 服務(wù)器的根證書
          "validation_context_sds_secret_config": {
           "name": "ROOTCA",
           "sds_config": {
            "api_config_source": {
             "api_type": "GRPC",
             "grpc_services": [
              {
               "envoy_grpc": {
                "cluster_name": "sds-grpc"
               }
              }
             ]
            }
           }
          }
         }
        },
        "sni": "outbound_.6379_._.redis.default.svc.cluster.local"
       }
      },
      "filters": [
       {
         ...
       }
      ]
     },
     "last_updated": "2020-09-13T00:33:43.862Z"
    }

在 transport_socket 部分的配置中,我們可以看到 Envoy 中配置了訪問(wèn) Redis Cluster 的 tls 證書信息,包括 Envoy Sidecar 用于訪問(wèn) Redis 使用的客戶端證書,用于驗(yàn)證 Redis 服務(wù)器證書的根證書,以及采用 spiffe 格式表示的,需驗(yàn)證的服務(wù)器端身份信息。 這里的證書相關(guān)內(nèi)容是使用 xDS 協(xié)議中的 SDS(Secret discovery service) 獲取的,由于篇幅原因在本文中不對(duì)此展開進(jìn)行介紹。如果需要了解 Istio 的證書和 SDS 相關(guān)機(jī)制,可以參考這篇文章一文帶你徹底厘清 Isito 中的證書工作機(jī)制。從上述配置可以得知,當(dāng)收到 Redis 客戶端發(fā)起的請(qǐng)求后,客戶端 Pod 中的 Envoy Sidecar 會(huì)使用 mTLS 向 Redis 服務(wù)器發(fā)起請(qǐng)求。

Redis 客戶端中 Envoy Sidecar 的 mTLS 配置本身看來(lái)并沒有什么問(wèn)題。但我們之前已經(jīng)得知該 Redis 服務(wù)并未安裝 Envoy Sidecar,因此實(shí)際上 Redis 服務(wù)器端只能接收 plain TCP 請(qǐng)求。這就導(dǎo)致了客戶端 Envoy Sidecar 在向 Redis 服務(wù)器創(chuàng)建鏈接時(shí)失敗了。

Redis 客戶端以為是這樣的:

Istio無(wú)頭服務(wù)問(wèn)題怎么解決

但實(shí)際上是這樣的:

Istio無(wú)頭服務(wù)問(wèn)題怎么解決

在服務(wù)器端沒有安裝 Envoy Sidecar,不支持 mTLS 的情況下,按理客戶端的 Envoy 不應(yīng)該采用 mTLS 向服務(wù)器端發(fā)起連接。這是怎么回事呢?我們對(duì)比一下客戶端 Envoy 中的其他 Cluster 中的相關(guān)配置。

一個(gè)訪問(wèn)正常的 Cluster 的 mTLS 相關(guān)配置如下:

   {
     "version_info": "2020-09-13T00:32:39Z/4",
     "cluster": {
      "@type": "type.googleapis.com/envoy.api.v2.Cluster",
      "name": "outbound|8080||awesome-app.default.svc.cluster.local",
      "type": "EDS",
      "eds_cluster_config": {
       "eds_config": {
        "ads": {}
       },
       "service_name": "outbound|8080||awesome-app.default.svc.cluster.local"
      },
      "connect_timeout": "1s",
      "circuit_breakers": {
       ...
      },
      ...

      # mTLS 相關(guān)的配置
      "transport_socket_matches": [
       {
        "name": "tlsMode-istio",
        "match": {
         "tlsMode": "istio"  #對(duì)帶有 "tlsMode": "istio" lable 的 endpoint,啟用 mTLS
        },
        "transport_socket": {
         "name": "envoy.transport_sockets.tls",
         "typed_config": {
          "@type": "type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext",
          "common_tls_context": {
           "alpn_protocols": [
            "istio-peer-exchange",
            "istio",
            "h3"
           ],
           "tls_certificate_sds_secret_configs": [
            {
             "name": "default",
             "sds_config": {
              "api_config_source": {
               "api_type": "GRPC",
               "grpc_services": [
                {
                 "envoy_grpc": {
                  "cluster_name": "sds-grpc"
                 }
                }
               ]
              }
             }
            }
           ],
           "combined_validation_context": {
            "default_validation_context": {},
            "validation_context_sds_secret_config": {
             "name": "ROOTCA",
             "sds_config": {
              "api_config_source": {
               "api_type": "GRPC",
               "grpc_services": [
                {
                 "envoy_grpc": {
                  "cluster_name": "sds-grpc"
                 }
                }
               ]
              }
             }
            }
           }
          },
          "sni": "outbound_.6379_._.redis1.dubbo.svc.cluster.local"
         }
        }
       },
       {
        "name": "tlsMode-disabled",
        "match": {},   # 對(duì)所有其他的 enpoint,不啟用 mTLS,使用 plain TCP 進(jìn)行連接
        "transport_socket": {
         "name": "envoy.transport_sockets.raw_buffer"
        }
       }
      ]
     },
     "last_updated": "2020-09-13T00:32:39.535Z"
    }

從配置中可以看到,一個(gè)正常的 Cluster 中有兩部分 mTLS 相關(guān)的配置:tlsMode-istio 和 tlsMode-disabled。tlsMode-istio 部分和 Redis Cluster 的配置類似,但包含一個(gè)匹配條件(match部分),該條件表示只對(duì)帶有 "tlsMode" : "istio" lable 的 endpoint 啟用 mTLS;對(duì)于不帶有該標(biāo)簽的 endpoint 則會(huì)采用 tlsMode-disabled 部分的配置,使用 raw_buffer,即 plain TCP 進(jìn)行連接。

查看 Istio 的相關(guān)源代碼,可以得知,當(dāng) Istio webhook 向 Pod 中注入 Envoy Sidecar 時(shí),會(huì)同時(shí)為 Pod 添加一系列 label,其中就包括 "tlsMode" : "istio" 這個(gè) label,如下面的代碼片段所示:

  patchLabels := map[string]string{
		label.TLSMode:                                model.IstioMutualTLSModeLabel,
		model.IstioCanonicalServiceLabelName:         canonicalSvc,
		label.IstioRev:                               revision,
		model.IstioCanonicalServiceRevisionLabelName: canonicalRev,
	}

由于 Pod 在被注入 Envoy Sidecar 的同時(shí)被加上了該標(biāo)簽,客戶端 Enovy Sidecar 在向該 Pod 發(fā)起連接時(shí),根據(jù) endpoint 中的標(biāo)簽匹配到 tlsMode-istio 中的配置,就會(huì)采用 mTLS;而如果一個(gè) Pod 沒有被注入 Envoy Sidecar,自然不會(huì)有該 Label,因此不能滿足前面配置所示的匹配條件,客戶端的 Envoy Sidecar 會(huì)根據(jù) tlsMode-disabled 中的配置,采用 plain TCP 連接該 endpoint。這樣同時(shí)兼容了服務(wù)器端支持和不支持 mTLS 兩種情況。

下圖展示了 Istio 中是如何通過(guò) endpoint 的標(biāo)簽來(lái)兼容 mTLS 和 plain TCP 兩種情況的。

Istio無(wú)頭服務(wù)問(wèn)題怎么解決

通過(guò)和正常 Cluster 的對(duì)比,我們可以看到 Redis Cluster 的配置是有問(wèn)題的,按理 Redis Cluster 的配置也應(yīng)該通過(guò) endpoint 的 tlsMode 標(biāo)簽進(jìn)行判斷,以決定客戶端的 Envoy Sidecar 是通過(guò) mTLS 還是 plain TCP 發(fā)起和 Redis 服務(wù)器的連接。但實(shí)際情況是 Redis Cluster 中只有 mTLS 的配置,導(dǎo)致了前面我們看到的連接失敗故障。

Redis 是一個(gè) Headless Service,通過(guò)在社區(qū)查找相關(guān)資料,發(fā)現(xiàn) Istio 1.6 版本前對(duì) Headless Service 的處理有問(wèn)題,導(dǎo)致了該故障。參見這個(gè) Issue Istio 1.5 prevents all connection attempts to Redis (headless) service #21964。

解決方案

找到了故障原因后,要解決這個(gè)問(wèn)題就很簡(jiǎn)單了。我們可以通過(guò)一個(gè) Destination Rule 禁用 Redis Service 的 mTLS。如下面的 yaml 片段所示:

kind: DestinationRule
metadata:
  name: redis-disable-mtls
spec:
  host: redis.default.svc.cluster.local
  trafficPolicy:
    tls:
      mode: DISABLE

再查看客戶端 Envoy 中的 Redis Cluster 配置,可以看到 mTLS 已經(jīng)被禁用,Cluster 中不再有 mTLS 相關(guān)的證書配置。

    {
     "version_info": "2020-09-13T09:02:28Z/7",
     "cluster": {
      "@type": "type.googleapis.com/envoy.api.v2.Cluster",
      "name": "outbound|6379||redis.dubbo.svc.cluster.local",
      "type": "ORIGINAL_DST",
      "connect_timeout": "1s",
      "lb_policy": "CLUSTER_PROVIDED",
      "circuit_breakers": {
        ...
      },
      "metadata": {
       "filter_metadata": {
        "istio": {
         "config": "/apis/networking.istio.io/v1alpha3/namespaces/dubbo/destination-rule/redis-disable-mtls"
        }
       }
      },
      "filters": [
       {
        "name": "envoy.filters.network.upstream.metadata_exchange",
        "typed_config": {
         "@type": "type.googleapis.com/udpa.type.v1.TypedStruct",
         "type_url": "type.googleapis.com/envoy.tcp.metadataexchange.config.MetadataExchange",
         "value": {
          "protocol": "istio-peer-exchange"
         }
        }
       }
      ]
     },
     "last_updated": "2020-09-13T09:02:28.514Z"
    }

此時(shí)再嘗試從客戶端訪問(wèn) Redis 服務(wù)器,一切正常!

到此,關(guān)于“Istio無(wú)頭服務(wù)問(wèn)題怎么解決”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI