下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

分布式应用运行时 Dapr:万物皆可 API

作者:匿名     来源: 云计算点击数:402发布时间: 2023-02-11 15:01:15

标签: 分布式应用API

  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.  brew install dapr-cli

  目前最新的版本是 1.9.1。

  复制

  1.  dapr version

  2.  CLI version: 1.9.1

  3.  Runtime version: n/a

  创建 Kubernetes 集群

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

  复制

  1.  export INSTALL_K3S_VERSION=v1.23.8+k3s2

  2.  curl -sfL https://get.k3s.io | sh -s - --disable traefik --disable servicelb --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config

  安装 Dapr

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

  复制

  1.  dapr init --kubernetes --wait

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

  复制

  1.  dapr status -k

  2.  NAME NAMESPACE HEALTHY STATUS REPLICAS VERSION AGE CREATED

  3.  dapr-dashboard dapr-system True Running 1 0.11.0 47s 2023-02-11 08:30.25

  4.  dapr-sentry dapr-system True Running 1 1.9.6 47s 2023-02-11 08:30.25

  5.  dapr-sidecar-injector dapr-system True Running 1 1.9.6 47s 2023-02-11 08:30.25

  6.  dapr-operator dapr-system True Running 1 1.9.6 47s 2023-02-11 08:30.25

  7.  dapr-placement-server dapr-system True Running 1 1.9.6 47s 2023-02-11 08:30.25

  示例应用

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

  复制

  1.  git clone https://github.com/dapr/quickstarts

  2.  cd quickstarts/tutorials/hello-kubernetes

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

  1.nodeapp 提供 HTTP 端点来创建和查询订单,订单信息保存在 Redis 中

  2.pythonapp​ 会持续访问 nodeapp 的 HTTP 端点来创建订单

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

  创建应用命名空间

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

  复制

  1.  kubectl create namespace dapr-test

  状态存储

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

  复制

  1.  helm repo add bitnami https://charts.bitnami.com/bitnami

  2.  helm repo update

  3.  helm install redis bitnami/redis --namespace store --create-namespace \\

  4.  --set replica.replicaCount=0 \\

  5.  --set auth.password=changeme

  创建组件

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

  复制

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

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

  组件类型 type 为 state.redis

  版本 versinotallow=v1

  访问地址 redisHost=redis-master.store:6379

  Redis 的访问密码从秘钥 redis 的键 redis-password 获取

  auth.secretStore 指定秘钥存储的类型是 `Kubernetes`[19]

  复制

  1.  kubectl apply -n dapr-test -f - <<eof< p="">

  2.  apiVersion: dapr.io/v1alpha1

  3.  kind: Component

  4.  metadata:

  5.  name: statestore

  6.  spec:

  7.  type: state.redis

  8.  version: v1

  9.  metadata:

  10.  - name: redisHost

  11.  value: redis-master.store:6379

  12.  - name: redisPassword

  13.  secretKeyRef:

  14.  name: redis

  15.  key: redis-password

  16.  auth:

  17.  secretStore: kubernetes

  18.  EOF

  访问状态存储

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

  下面截取了 nodeapp​ 中的部分代码,stateStoreName​ 就是上面创建的 statestore​。应用和组件位于同一命名空间下,直接只用 statestore​;否则,就要代码组件所在的命名空间 storeName.storeNamespace​(由于代码中硬编码了组件名 statestore,所以在同命名空间下创建组件)。

  复制

  1.  const stateStoreName = `statestore`;

  2.  const stateUrl = `http://localhost:${daprPort}/v1.0/state/${stateStoreName}`;

      3.

  4.  const state = [{

  5.  key: "order",

  6.  value: data

  7.  }];

      8.

  9.  const response = await fetch(stateUrl, {

  10.  method: "POST",

  11.  body: JSON.stringify(state),

  12.  headers: {

  13.  "Content-Type": "application/json"

  14.  }

  15.  });

  服务调用

  调用方 pythonapp 的代码。

  通过 sidecar daprd 的地址 localhost 和端口 3500 访问 HTTP API。

  在请求头中通过 dapr-app-id 指定目标应用 id nodeapp。应用 id 是通过 Kubernetes 注解 dapr.io/app-id 来设置的,更多注解可参考 文档[21]。

  目标方法名通过请求路径来指定:/neworder

  复制

  1.  dapr_port = os.getenv("DAPR_HTTP_PORT", 3500)

  2.  dapr_url = "http://localhost:{}/neworder".format(dapr_port)

      3.

  4.  n = 0

  5.  while True:

  6.  n += 1

  7.  message = {"data": {"orderId": n}}

      8.

  9.  try:

  10.  response = requests.post(dapr_url, jsnotallow=message, timeout=5, headers = {"dapr-app-id": "nodeapp"} )

  11.  if not response.ok:

  12.  print("HTTP %d => %s" % (response.status_code,

  13.  response.content.decode("utf-8")), flush=True)

  14.  except Exception as e:

  15.  print(e, flush=True)

      16.

  17.  time.sleep(1)

  部署应用

  复制

  1.  kubectl apply -n dapr-test -f deploy/node.yaml

  2.  kubectl wait --for=cnotallow=ready pod -n dapr-test -l app=node --timeout=60s

  3.  kubectl apply -n dapr-test -f deploy/python.yaml

  4.  kubectl wait --for=cnotallow=ready pod -n dapr-test -l app=python --timeout=60s

  检查 node 容器的日志,可以接收到了来自 pythonapp 的请求,并成功持久化存储了订单。

  复制

  1.  kubectl logs -f -n dapr-test -l app=node -c node

      2.

  3.  Successfully persisted state for Order ID: 1

  4.  Got a new order! Order ID: 1

  5.  Successfully persisted state for Order ID: 2

  6.  Got a new order! Order ID: 2

  7.  Successfully persisted state for Order ID: 3

  8.  Got a new order! Order ID: 3

  9.  Successfully persisted state for Order ID: 4

  10.  Got a new order! Order ID: 4

  Debug

  原本官方的指南是将 Redis 和应用部署在同一个命名空间中,加上 nodeapp 中硬编码了存储组件名。而我实验的时候讲 Redis 部署在了另一个空间下,检查 node 容器日志时看到的是:

  复制

  1.  Got a new order! Order ID: 1

  2.  Failed to persist state.

  daprd 容器中,只有下面的日志。

  复制

  1.  time="2023-02-11T02:55:38.166259509Z" level=info msg="HTTP API Called: POST /v1.0/state/statestore" app_id=nodeapp instance=nodeapp-857cf6f985-jnmzw scope=dapr.runtime.http-info type=log useragent="node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" ver=1.9.6

  通过为 nodeapp​ 的 pod 添加注解 dapr.io/log-level="debug"​ 让 daprd 容器输出 debug 日志。

  复制

  1.  time="2023-02-11T03:05:07.663028821Z" level=debug msg="{ERR_STATE_STORE_NOT_CONFIGURED state store is not configured}" app_id=nodeapp instance=nodeapp-59b754ff54-c4x4s scope=dapr.runtime.http type=log ver=1.9.6

  更多 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/

  来源: 云原生指北

    >>>>>>点击进入计算专题

赞(14)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程