code

2022年3月8日 星期二

Cloud Native - Gateways

 問題:

client要怎麼知道你的各個服務的endpoints是什麼?或是更新後的endpoints?

API Gateway 主要是routing traffic到該去的service endpoints / expose API / tasks (SSL)。此外API gateway可以有多個layer topology,某些前面的gateway可以做traffic offloading / SSL,下一層的gatewau可以做身份認證或是真的導向service endpoints。


Routing

API gateway扮演reverse proxy角色,將requests route到後端service (這也就是reverse proxy定義)。


這個pattern的問題在於,gateway變成single point of failure + bottleneck! 或許可以使用雲gateway服務。


Aggregation

gateway也可以扮演aggregator角色,將多個backend service的response集合起來變成一個返回client端的response:


這個pattern的風險除了一樣bottleneck之外,另外就是不要讓gateway以及這些services之間形成高耦合狀態,還有aggregation也會對gateway帶來額外的CPU load。好處是在client端的,只需要一個request就可以使用多個service。要是aggregation的load或是邏輯太大,會讓gateway變成monolithic,此時最好(以microservices觀點)可以拆出另一個aggregation service:



Offloading

gateway可以把某些不屬於個別service該做的load接手過去做,例如SSL termination,額外的好處是讓SSL certificate這樣的安全性資產可以分散。

這類的load最好是所有service共有的額外項目才放到gateway去做,稱為cross-cutting concerns,例如:

- 安全性認證與授權

- Rate limiting / retry policy / circuit breaking (?!)

- Caching

- Compression

- SSL offloading

- logging & monitoring


實作gateway

最受歡迎的:NGINX / HAProxy / Envoy,都提供了reverse proxy / load balancing / SSL / routing,雲端服務的話有 Azure Application Gateway / Azure Frontdoor / Amazon API Gateway。


Egress gateway

上面講的是ingress gateway,將外部流量往內導向該去的地方。

相對的有一個egress gateway會將內部流量導離自己的private network,來存取外部資源,e.g. 使用一個egress gateway來對某些outbound流量做安全性的blocking動作(可以阻擋不安全往外流量)。總之當你需要monitor outbound requests時,可以用Istio這類的egress gateway。



2022年3月7日 星期一

Cloud Native - Service Communication

 At high level

1. external communication (North-South traffic) - 不在同一個K8s cluster之中的service之間的通訊,使用K8s 的exgress controller

2. internal communication (East-West traffic) - 在同一個K8s cluster之中的service之間的通訊,使用K8s 的ingress controller


Protocols

HTTP是最常使用但不見得是最好效能者,優化效能的話,可以透過proxy轉換protocol:



1. Websockets - 適合雙向長時間,透過單一TCP socket建立全雙工低overhead的連線。首先透過HTTP request要求建立WebSocket連線。低延遲也是一個特點。

2. HTTP/2 - binary data protocol,特別適合低延遲高效率的串流通訊。

3. gRPC - 建立在HTTP/2上的lightweight transport protocol,在微服務社群中逐漸受到歡迎,提供包括身份驗證 / 雙向streaming / flow control / blocking bindings / cancellation & timeouts。


Messaging Protocols

cloud native天生使用event-driven / message-based approach,所以我們來看一下兩個主要的protocols:

1. MQTT (Message Queue Telemetry Transport)

通常用在IoT / 機器間 的binary protocol,例如在low-bandwidth環境下的sensor跟gateway的溝通。非常lightweight的publisher/subscriber的scheme。


2. AMQP (Advanced Message Queuing Protocol)

MQTT的rich feature set版本,所以相對不是那麼lightweight跟快速,但是也被許多vendor驗證過是很可靠的,所以在不同vendor的service間溝通可以使用AMQP。


Serialization Considerations

data serialization也是影響效能的一個可能。

1. JSON - 最常使用,但是有較大的memory footprint,所以也會花費計算在serialization / deserialization較大。JSON serializer的選擇就很關乎效能。

2. Protobuf - 由於是binary format,所以效能較佳,幾乎所有現代語言都有相對應的generator。message schema會預先定義在某個proto file,所以每個service預先會有此proto file才知道如何serialize/deserialize data。


Idempotency (冪等性)

service對任意的request每次都能產出一樣結果的話,稱為idempotency,一個可能發生不idempotency的場景是,某個API因為網路不穩而重新送了兩次request,造成service端重複process了同樣的data兩次,若是不符合idempotency的API有可能會造成錯誤。常見的解決方法是加上unique request ID,所以已經process過的ID就不會再process,避免了non-idempotency的可能,也稱為de-duping。


Request/Response

synchronous pattern就沒什麼好說的,一來一往等待:


asynchronous pattern的話,就要確保request方知道目前的response是對哪一個request的回應,這可以用message queue + correlation id (CID) 來達成:



publisher/subscriber

cloud native app中最常見的async communication pattern,多個subscriber訂閱某個topic,當publisher push message時,中間角色的broker對所有subscriber廣播出去。

- pub / sub 是完全獨立解耦合的

- event driven design


注意事項

1. async communication不保證接收到訊息的順序,所以需要注意idempotency issue

2. 如果care接收到訊息的順序,可以善用messaging system的內建ordering functionality或是使用priority queue pattern


要選擇Pub/Sub or Request/Response?

case by case依照application需求而定,例如以下為同一個cloud native app採用兩種approach:


data flow是 S1收到client async request後,處理完後又對S2 / S3也發出request。S3處理完後,向S4 / S5發出request。

Request/Response - S3要等待S4/S5處理完才能respond to S1,有可能因為fail而timeout,但是client端可能要很久之後才發現fail,而且必須處理idempotency,免得客戶多發了很多次request造成資料異常。換言之,request/response對每個參與的service來說是高度耦合的。


Pub/Sub - 較低耦合度,透過中間pub/sub系統例如 Redis/RabbitMQ/Apache Kafka。


Synchronous vs Asynchronous

所有的service instance基本上就是一個process,彼此間的溝通就是IPC (interprocess communication)機制,用synchronous communication有主要三個問題:

1. scale up時會因為等待response而被block住,容易產生資源瓶頸

2. response延遲時間是喔死有節點的延遲加總,所以其中一個慢,會讓client端的整體response latency高。

3. 錯誤累積,這類似2. 的問題。


所以cloud native app基本上是採用asynchronous communication。


2022年3月5日 星期六

Cloud Native - API Design & Versioning

API Versioning

服務間依賴API溝通,但API versioning是一件不容易的事,有以下三種常見的approaches:

1. the knot - API版本永遠不更改(single version),所以每次API更動,所有的clients都要更動

2. point-to-point - API提供者提供不同版本的API,client自己採用需要的版本,但是對API提供者來說維護成本高。

3. compatible versioning - API版本永遠不更改(single version),但是向下相容,也是對API提供者較高的開發跟維護成本。

compatible versioning根據研究,會有較高的效率。

REST規格本身並沒有提供versioning規範,一般採用subdomain或是url path方式來區分不同version,例如

api-v1.test.com/users

./api/v2/users

另一種方式是mime-based approach,透過對header的resource version/type來標記API version,例如 (Accept: application/vnd.test.users.v2+json),但是API url path並不會改變。


Backward Compatibility

對於採用compatible versioning approach來說,



上圖中,如果Service B v2.0要達成向下相容,代表他要能跟Service A v1.0能完全溝通,因為service A並不會有任何異動。要注意以下best practice來達成:

1. 新的API提供default values或是optional values,如果無法達成則應該要開新的API uri

2. 絕對不要重新命名或是移除現有的參數

3. 標記沒在使用的API endpoint obsolete

4. 同時測試新舊version API,傳舊的message data給兩者。

5. forward compatibility: 設計API時考慮未來相容性的話,implementation要能ignore新的參數,並不會出現任何錯誤!


Semantic Versioning

versioning的命名應該要有語意,讓人們能了解版本的差異 (major.minor.patch):

major - 當不相容的定義出現時,要增加major version

minor - 當向下相容的功能出現時,要增加minor version

patch - 當向下相容的bug fixes