分布式应用运行时 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:万物皆可 API

接下来,就借助官方的入门指南体验 Dapr 的。Dapr 提供了 多种入门指南[15],这里我选了其中的 hello-kubernetes[16],但实际操作可能与官方有些许差异,也正式这些差异能让(坑)我对 Dapr 有更多的了解。

环境

安装 Dapr CLI

Dapr CLI 是操作 Dapr 的工具,对可以用来安装、管理 Dapr 实例,以及进行 debug。参考官方的 安装文档[17],我使用的是 macOS 选择 homebrew 来安装。

brew install dapr-cli

目前最新的版本是 1.9.1。

dapr version
CLI version: 1.9.1
Runtime version: n/a

创建 Kubernetes 集群

使用 k3s v1.23.8+k3s2 作为实验环境集群。

export INSTALL_K3S_VERSION=v1.23.8+k3s2
curl -sfL https://get.k3s.io | sh -s - --disable traefik --disable servicelb  --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config

安装 Dapr

执行下面的命令将 Dapr 安装到集群中。

dapr init --kubernetes  --wait

检查组件是否正常运行。在 Kubernetes 环境下,我们的很多命令都要使用 --kubernetes 或者 -k 参数。

dapr status -k
  NAME                   NAMESPACE    HEALTHY  STATUS   REPLICAS  VERSION  AGE  CREATED
  dapr-dashboard         dapr-system  True     Running  1         0.11.0   47s  2023-02-11 08:30.25
  dapr-sentry            dapr-system  True     Running  1         1.9.6    47s  2023-02-11 08:30.25
  dapr-sidecar-injector  dapr-system  True     Running  1         1.9.6    47s  2023-02-11 08:30.25
  dapr-operator          dapr-system  True     Running  1         1.9.6    47s  2023-02-11 08:30.25
  dapr-placement-server  dapr-system  True     Running  1         1.9.6    47s  2023-02-11 08:30.25

示例应用

环境部署好之后,我们来看下要用的示例应用。

git clone https://github.com/dapr/quickstarts
cd quickstarts/tutorials/hello-kubernetes

示例中包含了 2 个应用 pythonapp 和 nodeapp,以及 Redis。

  • nodeapp 提供 HTTP 端点来创建和查询订单,订单信息保存在 Redis 中
  • pythonapp 会持续访问 nodeapp 的 HTTP 端点来创建订单

用到了 Dapr 的两个功能:服务调用和状态存储。

创建应用命名空间

应用将部署在 dpar-test 命名空间下。

kubectl create namespace dapr-test

状态存储

状态存储使用 Redis,先部署 Redis 到命名空间 store 下。简单起见,只使用单 master 节点,并设置密码 changeme

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install redis bitnami/redis --namespace store --create-namespace 
  --set replica.replicaCount=0 
  --set auth.password=changeme

创建组件

由于 Redis 设置了密码,需要为 Dapr 提供访问 Redis 的密码,通过 Secret 来传递。Secret 保存在 dapr-test 下。

kubectl create secret generic redis -n dapr-test --from-literal=redis-password=changeme

根据 Redis store 规范[18] 在 dapr-test 下创建组件 statetore

  • 组件类型 type 为 state.redis
  • 版本 version=v1
  • 访问地址 redisHost=redis-master.store:6379
  • Redis 的访问密码从秘钥 redis 的键 redis-password 获取
  • auth.secretStore 指定秘钥存储的类型是 Kubernetes[19]
kubectl apply -n dapr-test -f - apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
  metadata:
  - name: redisHost
    value: redis-master.store:6379
  - name: redisPassword
    secretKeyRef:
      name: redis
      key: redis-password
auth:
  secretStore: kubernetes
EOF

访问状态存储

通过 Dapr API 访问状态存储[20],请求格式:POST http://localhost:/v1.0/state/

下面截取了 nodeapp 中的部分代码,stateStoreName 就是上面创建的