Istio的部署模型介紹
部署模型
當配置一個生產級別的Istio時,需要解決一些問題:如網格是單集群使用,還是跨集群使用?所有的服務會放到一個完全可達的網絡中,還是需要網關來連接跨網絡的服務?使用一個控制面(可能會跨集群共享一個控制面),還是使用多個控制面來實現高可用(HA)?所有的集群都連接到一個多集群服務網格,還是聯合成一個多網格形態。
所有這些問題,以及他外因素,都代表了Istio部署的各個配置維度。
- 單集群或多集群
- 單網絡或多網絡
- 單控制面或多控制面
- 單網格或多網格
上述條件可以任意組合(雖然有些組合相比其他更加普遍,有些則不那么受歡迎,如單集群中的多網格場景)。
在一個涉及多集群的生產環境中,可以混合使用部署模型。例如,可以使用多個控制面來做到HA。在一個3集群環境中,可以將兩個集群共享一個控制面,然后給第三個集群在不同的網絡中添加另外一個控制面。然后配置三個集群共享各自的控制面,這樣所有的集群就可以使用2個控制面來做到HA。
實際使用中,需要根據隔離性,性能,以及HA要求來選擇合適的部署模型。本章將描述部署Istio時的各種選擇和考量。
集群模式
該模式下,應用的負載會運行在多個集群中。為了隔離,性能或高可用,可以限定集群的可用zone和region。
取決於具體要求,生產系統可以跨多個zone或region的多個集群運行,利用雲負載均衡器來處理諸如本地性,zonal 或regional故障轉移之類的事情。
大多數場景下,集群表示配置和終端發現的邊界。例如,每個kubernetes集群都有一個API Server來管理集群的配置,以及提供服務終端的信息,如pod的啟停等。由於kubernetes的這種配置行為是基於單個集群的,因此會將潛在的錯誤(如配置錯誤)限制在其所在的集群中。
使用Istio可以在任意個集群上配置一個服務網格。
單集群
在最簡單的場景中,可以在單個集群中部署單Istio網格。一個集群通常會運行在一個獨立的網絡中,但具體取決於基礎設施提供商。包含一個網絡的單集群模型會包含一個控制面,這就是istio最簡單的部署模型:
單集群的部署比較簡單,但同時也缺少一些特性,如故障隔離和轉移。如果需要高可用,則應該使用多集群模式。
多集群
一個網格可以包含多個集群。使用多集群部署可以在一個網格中提供如下功能。
- 故障隔離和轉移:當
cluster-1
宕機后,使用cluster-2
- 位置感知路由和故障轉移:發送請求到最近的服務
- 多種控制面模型:支持不同級別的可用性。
- 團隊和項目隔離:每個團隊運行各自的集群
多集群部署提供了更大的隔離性和可靠性,但同時也增加了復雜性。如果系統需要高可用,則集群可能會跨多個zone和region。
可以通過金絲雀配置來在單個集群中修改或使用新的二進制版本,這樣配置變更僅會影響一小部分的用戶流量。如果一個集群出現問題,可以臨時把流量路由到臨近的集群(直到問題解決)。
此外,可以根據網絡和雲提供商支持的選項配置集群間的通信。例如,如果兩個集群使用了相同的底層網絡,那么就可以通過簡單的防火牆規則實現集群間的通信。
網絡模型
很多生產系統需要多個網絡或子網來實現隔離和高可用。Istio支持將一個服務網格部署到多種類型的網絡拓撲中。通過這種方式選擇符合現有網絡拓撲的網絡模型。
單網絡
在最簡單場景下,服務網格會運行在一個完全連接的網絡上,在單網絡模型下,所有的負載示例能夠在沒有Istio網格的情況下實現互聯。
單個網絡使Istio能夠在網格中以統一的方式配置服務使用者,並具有直接處理工作負載實例的能力。
多網絡
多網絡提供了如下新功能:
- 為service endpoints提供了可交叉的IP或VIP
- 跨邊界
- 容錯功能
- 擴展網絡地址
- 符合網絡分段的標准
這種模式下,不同網絡中的負載實例只能通過一個或多個Istio網關進行互聯。Istio使用分區服務發現來為使用者提供service endpoints的不同視圖。視圖取決於使用者的網絡。
控制面模型
Istio網格使用控制面來配置網格內負載實例之間的通信。通過復制控制面,工作負載可以連接到任何控制面來獲取配置。
最簡單的場景下,可在單集群中運行網格的控制面:
像這樣具有自己的本地控制平面的集群被稱為主集群。
多集群部署可以共享相同的控制面實例。這種情況下,控制面實例可以位於一個或多個主集群中。沒有自己的控制面的集群稱為遠端集群。
一個完全由遠程集群組成的服務網格可以通過外部控制面進行控制,而不需要通過運行在主集群中的控制面進行控制。通過這種方式將istio在管理上進行了隔離,即將控制面和數據面服務進行了完全分割。
雲提供商的
Managed Control Plane
實際上就是一個外部控制面。
為了高可用,控制面可能需要跨多集群,zone和region來部署。下圖中的服務網格在每個region中都使用了一個控制面。
使用上述模型具有如下優勢:
- 提升可用性:如果一個控制面不可用,停機的范圍將僅限於該控制平面
- 配置隔離:可以修改一個集群,zone或region中的配置,而不會影響另外一個控制面的配置。
可以通過故障轉移來提升控制面的可靠性。當一個控制面實例變為不可用時,負載實例可以連接到其他可用的控制面實例上。集群,zone和region中都可能發生故障遷移。下圖可以看到,當左側的控制面出問題后,由右側的控制面托管了Envoy代理(虛線)。
以下列表按可用性對控制平面的部署進行了排序:
- 每個region一個集群(低可用性)
- 每個region多集群
- 一個zone一個集群
- 一個zone多個集群
- 每個集群一個控制面(高可用性)
身份和信任模型
當在一個服務網格中創建負載實例時,istio會賦予該負載一個身份。
CA( certificate authority)會為網格中使用的身份創建和頒發證書,后續就可以使用CA為該身份創建和頒發證書時使用的公鑰對消息發送端進行校驗。trust bundle是Istio網格使用的所有CA公鑰的集合。任何人都可以使用網格的trust bundle校驗來自網格的服務。
網格中的信任
在單istio網格中,istio保證每個負載實例都有一個標識其身份的證書,使用trust bundle來識別網格和聯邦網格中的所有身份。CA僅負責為這些身份創建和簽發證書。該模型允許網格中的負載實例互聯。下圖展示了一個具有 certificate authority的服務網格。
網格之間的信任
如果一個網格中的服務需要調用另外一個網格中的服務,此時需要在兩個網格之間使用聯邦身份。為了實現聯邦身份的信任,需要交換網格的trust buncles(可以通過手動或自動方式,如SPIFFE Trust Domain Federation來交換trust bundles)
網格模型
Istio支持在一個網格或聯邦網格(也被稱為多網格)中部署所有的應用。
單網格
單網格是部署Istio的最簡單的方式。在一個網格中,服務名是唯一的。例如,一個foo
命名空間中,只能存在一個名為mysvc
的服務。此外,負載實例共享同一個身份service account,因為該資源在命名空間內是唯一的。
單網格可以部署在一個或多個集群中,以及一個或多個網絡上。在一個網格中,命名空間用於tenancy(見下)。
多網格
多網格是網格聯邦的結果。
多網格提供了如下功能:
- 組織邊界:業務線
- 重用服務名稱或命名空間:例如
default
命名空間可以用於多種用途 - 強隔離:將測試負載和生產負載進行隔離。
可以使用中間網格來連接網格聯邦。當使用聯邦時,每個網格都可以暴露一組所有參與的網格都能識別的服務和身份。
為了避免服務名沖突,可以給每個網格分配一個全局唯一的網格ID,來保證每個服務的fully qualified domain name (FQDN)是有效的。
當聯邦中兩個網格沒有使用相同的信任域時,必須對這兩個網格的身份和trust bundles進行聯邦。查看Multiple Trust Domains。
租戶模式
在Istio中,租戶是一個共享用戶組,共享一組已部署的工作負載的訪問權限和特權。通常需要從網絡配置和策略層面來為不同的租戶隔離負載實例。
可以配置租戶模式來滿足組織的隔離需求:
- 安全
- 策略
- 容量
- 成本
- 性能
Istio支持兩種類型的租戶:
Namespace tenancy
在一個網格中,Istio使用命名空間作為租戶的單位。Istio也可以運行在沒有實現命名空間租戶的環境中。在實現命名空間租戶的環境中,可以保證僅允許一個團隊將負載部署在一個給定的命名空間或一組命名空間中。默認情況下,多個租戶命名空間中的服務都可以互聯。
為了提升隔離性,可以選擇暴露到其他命名空間中的服務。通過授權策略來暴露服務或限制訪問。
當使用多集群時,每個集群中的相同名稱的命名空間被看作是相同的命名空間。例如,cluster-1
中的foo
命名空間中的Service B
,以及cluster-2
中的foo
命名空間中的Service B
被認為是相同的服務,且Istio會在服務發現時合並endpoints,並在這些endpoints間執行負載均衡。下圖展示了具有相同命名空間的兩個集群
Cluster tenancy
Istio支持以集群作為租戶的單位。這種情況下,可以給每個團隊指定特定的集群或一組集群來部署負載,並授權團隊成員。可以給成員分配不同的角色,如:
- 集群管理員
- 開發人員
為了在Istio中使用集群租戶,需要將每個集群作為獨立的網格。此外,可以使用Istio將一組集群作為單租戶。這樣每個團隊就可以擁有一個或多個集群,而不是將所有的集群配置為單網格。為了連接不同團隊的網格,可以將這些將這些網格聯邦為多網格。下圖展示了使用兩個集群和命名空間來隔離服務網格。
由於網格由不同的團隊或組織進行操作,因此服務命名很少會不同。例如,cluster-1
的foo
命名空間中的mysvc
和cluster-2
的foo
命名空間中的mysvc
服務並不是相同的服務。最常見的示例是Kubernetes中的場景,許多團隊將工作負載部署到default
命名空間。
當每個團隊都有各自的網格后,就可以使用多網格模型跨網格進行通信。
性能和可靠性
Istio使用豐富的路由,負載均衡,服務到服務的認證,監控等簡化了部署服務的網絡,且不需要修改應用代碼。Istio力爭使用最少的資源開銷來提供這些便利,以及在增加最小的延遲下支撐更大規模的網格和更高的請求率。
Istio數據面組件,Envoy代理會處理流經系統的數據。Istio的控制面組件,Pilot,Galley和Citadel來配置數據面。數據面和控制面都有明顯的性能問題。
1.7的性能摘要
Istio負載測試網格包含1000個服務和2000個sidecar,每秒70000個網格范圍的請求。在對Istio1.7測試之后,得出如下結果:
- 在每秒有1000個請求經過代理時,Envoy代理會使用0.5 vCPU 和50 MB內存
- 如果部署時使用了Mixer,在每秒有1000個網格范圍的請求時,
istio-telemetry
服務會使用0.6 vCPU - Pilot使用1 vCPU 和1.5 GB內存
- Envoy代理對90%的延遲增加了3.12毫秒。
控制面性能
Pilot會根據用戶的配置文件和系統的當前狀態配置sidecar代理。在Kubernetes環境中,CRD和deployment構成了配置和系統狀態。Istio配置對象,如gateway和virtual service等提供了用戶可編輯的配置。為了生成代理的配置,Pilot處理來自Kubernetes環境和用戶配置的組合配置以及系統狀態。
控制平面支持成千上萬的服務,這些服務分布在成千上萬個Pod中,並使用類似數量的由用戶編寫的virtual services和其他配置對象。Pilot的CPU和內存會隨着配置數據的變化和系統狀態而變化。CPU的使用包含如下因素:
- deployment變化率
- 配置變化率
- 連接到Pilot的代理數目
但這部分是支持水平擴展的。
當啟用命名空間租戶時,使用1vCPU和1.5GB內存的單個Pilot可以支持1000個服務,2000個sidecar。可以通過增加Pilot的數量來降低配置所有代理的時間。
數據面性能
數據面性能依賴很多因素,如:
- 客戶端連接數
- 目標請求率
- 請求大小和響應大小
- 代理worker的線程數
- 協議
- CPU cores
- 代理過濾器的數目和類型,特別是Mixer的過濾器
延遲,吞吐量以及代理的CPU和內存消耗均根據上述因素進行衡量。
CPU和內存
由於sidecar代理會在數據路徑上做一些額外的工作,因此會消耗CPU和內存。如Istio 1.1中,在每秒1000個請求的情況下,一個代理會消耗0.6 vCPU。
代理的內存消耗取決於代理保存的總配置狀態。大量listeners,clusters和routes可能會增加內存消耗。Istio 1.1引入了命名空間隔離來限制發送到一個代理的配置。在一個大的命名空間中,一個代理大概會消耗50 MB的內存。
由於代理通常不會緩存通過的數據,因此請求率不會影響內存消耗。
命名空間的隔離是通過sidecar資源實現的。如何使用可以參見istio-namespace-isolation-tricks。
延遲
由於Istio在數據路徑上注入了sidecar代理,因此延遲是一個需要重點考慮的因素。Istio會將身份驗證和Mixer過濾器添加到代理,每個額外的過濾器都會增加代理內部的路徑長度,並影響到延遲。
Envoy代理會收在客戶端接收到響應之后采集原始的遙測數據。對采集請求的遙測數據的時間不會計算在總的完成請求所需要的時間內。但由於worker忙於處理請求,因此worker可能不會立即處理下一個請求。這種處理會增加請求在隊列中等待的時間,並影響到平均值和尾部延遲。實際的尾部延遲取決於流量狀況。
Istio 1.7的延遲
在網格中,請求會通過客戶端的代理,然后到達服務端。1.7的默認配置中(即,telemetry V2),這兩個代理在基准數據平面延遲的90百分位和99百分位延遲上分別增加了大約3.12ms 和3.13ms。通過Istio benchmarks的http/1.1
協議得出如上結論,使用兩個代理worker,並啟用mutual TLS,通過16個客戶端連接來發送每秒1000個請求,每個請求1KB。
在即將發布的Istio版本中,將把Istio策略和Istio遙測功能作為TelemetryV2添加到代理中。通過這種方式來減少流經系統的數據量,從而減少CPU使用量和延遲。
P90 latency vs client connections without jitter
P99 latency vs client connections without jitter
P90 latency vs client connections with jitter
P99 latency vs client connections with jitter
baseline
客戶端Pod直接調用服務端Pod,不經過sidecarnone_both
使用Istio代理,但不配置Istio過濾器v2-stats-wasm_both
客戶端和服務端的sidecar都配置似乎用telemetry v2v8
v2-stats-nullvm_both
客戶端和服務端的sidecar默認都配置似乎用telemetry v2nullvm
v2-sd-full-nullvm_both
使用配置的 telemetry v2nullvm
暴露Stackdriver metrics,訪問日志v2-sd-nologging-nullvm_both
與上面系統,但不暴露訪問日志
Benchmarking 工具
Istio使用如下工具來進行benchmarking:
fortio.org
- 恆定吞吐量的負載測試工具blueperf
- 真實的雲原生應用程序isotope
- 具有可配置拓撲的合成應用程序
Pods 和Services
作為網格的一部分,kubernetes的pod和service必須滿足如下要求:
- 關聯的Service:一個pod必須對應至少一個kubernetes service,即使pod沒有暴露任何端口。如果一個pod對應多個kubernetes services,那么這些services無法為不同的協議使用相同的端口號,如HTTP和TCP。
- 應用的UID:確保pods不能以用戶ID (UID)值為1337的身份運行應用程序
NET_ADMIN
和NET_RAW
capabilities:如果集群強制使用pod安全策略,則必須給pod添加NET_ADMIN
和NET_RAW
capabilities。如果使用了Istio CNI 插件,則可以不遵守該要求。- Deployments 使用 app 和version labels:建議Deployments 使用
app
和version
labels。app
和version
labels會給istio采集的metrics和遙測數據添加上下文信息app
label:每個deployment應該包含不同的app
label。app
label用於在分布式跟蹤中添加上下文信息。version
label:指定特定deployment對應的應用版本。
- 命名service端口:可以選擇使用命名服務端口來指定協議。更多細節參見Protocol Selection。
要求的pod capabilities
如果集群使用了pod安全策略,除非使用了Istio CNI插件,否則pod必須允許NET_ADMIN
和NET_RAW
capabilities。Envoy代理的initialization容器會使用這些capabilities。
為了校驗pod是否允許NET_ADMIN
和NET_RAW
capabilities,需要校驗pod對應的service account是否可以使用pod安全策略來允許NET_ADMIN
和NET_RAW
capabilities。如果沒有在pod的deployment中指定service account,那么pod會使用其所在命名空間的default
service account。
使用如下命令可以列出一個service accout的capabilities,替換<your namespace>
和<your service account>
# for psp in $(kubectl get psp -o jsonpath="{range .items[*]}{@.metadata.name}{'\n'}{end}"); do if [ $(kubectl auth can-i use psp/$psp --as=system:serviceaccount:<your namespace>:<your service account>) = yes ]; then kubectl get psp/$psp --no-headers -o=custom-columns=NAME:.metadata.name,CAPS:.spec.allowedCapabilities; fi; done
例如可以使用如下命令來校驗default
命名空間中的default
service account。
# for psp in $(kubectl get psp -o jsonpath="{range .items[*]}{@.metadata.name}{'\n'}{end}"); do if [ $(kubectl auth can-i use psp/$psp --as=system:serviceaccount:default:default) = yes ]; then kubectl get psp/$psp --no-headers -o=custom-columns=NAME:.metadata.name,CAPS:.spec.allowedCapabilities; fi; done
如果在capabilities列表中看到 NET_ADMIN
和 NET_ADMIN
或*
,則說明該pod允許運行Isti init容器,否則需要配置權限。