分布式應用運行時 Dapr:萬物皆可 API
?Dapr[1] 分布式應用運行時 Distributed Application Runtime 的首字母縮寫。有關多運行時,可以看下 Bilgin Ibryam 的 Multi-Runtime Microservices Architecture[2],不想看英文的可以看下我之前的翻譯。
Dapr 是一個分布式系統工具包,通過提供 API 實現應用程序與外圍組件的解耦合,讓開發人員更加聚焦于業務邏輯的研發。解耦也是與傳統 SDK 的很大區別,能力不再是通過應用程序中加入庫的方式提供,而是通過應用附近的邊車(sidecar)運行時提供(sidecar 不是廣為人知的服務網格 sidecar - pod 中的容器,而是廣泛使用在系統軟件設計中的一種模式,比如操作系統的 initd、日志采集組件,甚至是 Java 中的多線程。)。因此這里說的 Dapr sidecar 可能是個獨立的進程,也可能是 pod 中的一個容器。
在 Dapr 中我們可以看到很多常見 SDK 的能力:
- 如 SpringCloud、Netflix OSS 的 服務調用[3],以及超時、熔斷、重試等 彈性策略[4]
- 如 Spring Data KeyValue 一樣提供 狀態存儲[5] 的抽象,簡化各種持久存儲的訪問
- 如 Kafka、NATS、MQTT 等消息代理,提供 發布/訂閱[6] 抽象供服務通過消息進行通信
- 如 Kafka、MQTT、RabbitMQ 提供以事件觸發應用的抽象:綁定[7]
- 如 Redis 一樣的 分布式鎖[8]
- 如 Consul、Kubernetes 等的 名稱解析[9]
- ...
以上能力都是通過 HTTP 和 gRPC API 暴露給應用,這些 API 在 Dapr 中被叫做 構建塊[10](building blocks),并且也 僅提供抽象,也就是說你可以隨意替換底層實現(Dapr 中也叫做 組件[11])而無需修改任何應用代碼。
比如你的應用需要在存儲中保存狀態,在開發時可以使用 內存[12] 作為存儲組件,其他環境中可以使用 Mysql[13]、Redis[14] 等持久化組件。
接下來,就借助官方的入門指南體驗 Dapr 的。Dapr 提供了 多種入門指南[15],這里我選了其中的 hello-kubernetes[16],但實際操作可能與官方有些許差異,也正式這些差異能讓(坑)我對 Dapr 有更多的了解。
環境
安裝 Dapr CLI
Dapr CLI 是操作 Dapr 的工具,對可以用來安裝、管理 Dapr 實例,以及進行 debug。參考官方的 安裝文檔[17],我使用的是 macOS 選擇 homebrew 來安裝。
目前最新的版本是 1.9.1。
創建 Kubernetes 集群
使用 k3s v1.23.8+k3s2 作為實驗環境集群。
安裝 Dapr
執行下面的命令將 Dapr 安裝到集群中。
檢查組件是否正常運行。在 Kubernetes 環境下,我們的很多命令都要使用 --kubernetes? 或者 -k 參數。
示例應用
環境部署好之后,我們來看下要用的示例應用。
示例中包含了 2 個應用 pythonapp? 和 nodeapp,以及 Redis。
- nodeapp 提供 HTTP 端點來創建和查詢訂單,訂單信息保存在 Redis 中
- pythonapp? 會持續訪問 nodeapp 的 HTTP 端點來創建訂單
用到了 Dapr 的兩個功能:服務調用和狀態存儲。
創建應用命名空間
應用將部署在 dpar-test 命名空間下。
狀態存儲
狀態存儲使用 Redis,先部署 Redis 到命名空間 store? 下。簡單起見,只使用單 master 節點,并設置密碼 changeme。
創建組件
由于 Redis 設置了密碼,需要為 Dapr 提供訪問 Redis 的密碼,通過 Secret 來傳遞。Secret 保存在 dapr-test 下。
根據 Redis store 規范[18] 在 dapr-test? 下創建組件 statetore:
- 組件類型 type 為 state.redis
- 版本 versinotallow=v1
- 訪問地址 redisHost=redis-master.store:6379
- Redis 的訪問密碼從秘鑰 redis 的鍵 redis-password 獲取
- auth.secretStore 指定秘鑰存儲的類型是 `Kubernetes`[19]
訪問狀態存儲
通過 Dapr API 訪問狀態存儲[20],請求格式:POST http://localhost:<daprPort>/v1.0/state/<storename>。
下面截取了 nodeapp? 中的部分代碼,stateStoreName? 就是上面創建的 statestore?。應用和組件位于同一命名空間下,直接只用 statestore?;否則,就要代碼組件所在的命名空間 storeName.storeNamespace?(由于代碼中硬編碼了組件名 statestore,所以在同命名空間下創建組件)。
服務調用
調用方 pythonapp 的代碼。
- 通過 sidecar daprd 的地址 localhost 和端口 3500 訪問 HTTP API。
- 在請求頭中通過 dapr-app-id 指定目標應用 id nodeapp。應用 id 是通過 Kubernetes 注解 dapr.io/app-id 來設置的,更多注解可參考 文檔[21]。
- 目標方法名通過請求路徑來指定:/neworder
部署應用
檢查 node 容器的日志,可以接收到了來自 pythonapp 的請求,并成功持久化存儲了訂單。
Debug
原本官方的指南是將 Redis 和應用部署在同一個命名空間中,加上 nodeapp 中硬編碼了存儲組件名。而我實驗的時候講 Redis 部署在了另一個空間下,檢查 node 容器日志時看到的是:
daprd 容器中,只有下面的日志。
通過為 nodeapp? 的 pod 添加注解 dapr.io/log-level="debug"? 讓 daprd 容器輸出 debug 日志。
更多 Debug 方式,參考官方的 Troubleshooting 文檔[22]。
總結
Dapr 提供了與傳統 SDK 方式完成不同的方法來實現系統集成,讓開發者可以專注于業務邏輯,而無需考慮底層的實現;對組織來說,應用變得更加便攜,可以使用不同的云環境。
但是 Dapr 本身無法跨云跨集群,社區正在考慮與服務網格集成來實現混合多云環境下的服務調用,大家可以期待一下。
參考資料
[1] Dapr: https://dapr.io
[2] Multi-Runtime Microservices Architecture: https://www.infoq.com/articles/multi-runtime-microservice-architecture/
[3] 服務調用: https://docs.dapr.io/developing-applications/building-blocks/service-invocation/
[4] 彈性策略: https://docs.dapr.io/operations/resiliency/policies/
[5] 狀態存儲: https://docs.dapr.io/developing-applications/building-blocks/state-management/
[6] 發布/訂閱: https://docs.dapr.io/developing-applications/building-blocks/pubsub/
[7] 綁定: https://docs.dapr.io/developing-applications/building-blocks/bindings/
[8] 分布式鎖: https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/distributed-lock-api-overview/
[9] 名稱解析: https://docs.dapr.io/reference/components-reference/supported-name-resolution/
[10] 構建塊: https://docs.dapr.io/concepts/building-blocks-concept/
[11] 組件: https://docs.dapr.io/concepts/components-concept/
[12] 內存: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-inmemory/
[13] Mysql: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-mysql/
[14] Redis: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-redis/
[15] 多種入門指南: https://github.com/dapr/quickstarts/tree/master/tutorials
[16] hello-kubernetes: https://github.com/dapr/quickstarts/tree/master/tutorials/hello-kubernetes
[17] 安裝文檔: https://docs.dapr.io/getting-started/install-dapr-cli/
[18] Redis store 規范: https://docs.dapr.io/reference/components-reference/supported-state-stores/
[19] Kubernetes?: https://docs.dapr.io/reference/components-reference/supported-secret-stores/kubernetes-secret-store/
[20] Dapr API 訪問狀態存儲: https://docs.dapr.io/reference/api/state_api/#save-state
[21] 文檔: https://docs.dapr.io/reference/arguments-annotations-overview/
[22] Troubleshooting 文檔: https://docs.dapr.io/operations/troubleshooting/