使用Dapr框架在Azure Kubernetes Service(AKS)上进行微服务的状态管理
首先
我打算在今年12月份的Microsoft Azure Tech Advent Calendar 2019上发布一篇关于Dapr框架在AKS(Azure Kubernetes Service)上的使用的投稿。这个框架在2019年10月份发布。
概括回答
2019年10月,Dapr(分散アプリケーションランタイム)作为一个框架被发布,旨在简化微服务应用程序的开发。尽管目前只是α版,但Dapr正在开发中,旨在提供跨编程语言的微服务调用功能、状态管理、服务之间消息传递、资源绑定和分布式服务之间的跟踪等功能。

Dapr以所谓的Sidecar架构运行,并通过应用程序的HTTP/gRPC API调用来使用(在Kubernetes上,在应用程序和同一个POD内作为Sidecar容器运行)。因此,应用程序不需要嵌入特殊的运行时来使用Dapr,可以在应用逻辑被隔离的状态下使用功能。

这次我们将在 Kubernetes 上验证 Dapr 的操作,作为 Azure Tech Advent Calendar 的一部分,我将在 Microsoft Azure 的托管 k8 服务 AKS 上执行。
在 AKS 上运行的容器应用程序中,使用 Dapr 框架来管理应用程序状态的方法,我们将按照以下步骤整理示例命令和代码解释。
-
- 创建 Azure Kubernetes Service (AKS) 集群
-
- 安装 Dapr CLI
-
- 在 AKS 集群上安装 dapr
-
- 构建 Dapr 的 State Store (Redis)
-
- 部署带有 dapr 的 Node.js 应用程序
-
- 部署带有 dapr 的 Python 应用程序
- 在 Node.js 和 Python 应用程序之间进行 dapr 的交互并进行操作确认
最终,AKS 上将实现以下配置。State Store 将使用 Redis。

步骤1. 创建Azure Kubernetes服务(AKS)集群
首先我们需要创建一个kubernetes集群来运行dapr和示例应用程序。在进行后续操作之前,请确保以下工具已经设置在您的工作终端上。
・Azure CLI: 微软 Azure 命令行界面
・kubectl: Kubernetes 控制工具
使用Azure CLI创建用于dapr验证环境的AKS集群。由于kubectl也是前提条件,建议您查看”快速入门:使用Azure CLI部署Azure Kubernetes Service集群”。安装完成后,请执行以下命令。
# Azure への接続
az login
# サブスクリプションの選択
az account set -s <YourSubscriptionId>
# リソースグループの作成
az group create --name myAKSdapr --location japaneast
# AKS クラスタの作成
az aks create --resource-group myAKSdapr \
--name myAKSDaprCluster \
--node-count 2 \
--enable-addons http_application_routing
--enable-rbac \
--generate-ssh-keys
# クラスタの資格情報を取得する
az aks get-credentials --resource-group myAKSdapr --name myAKSDaprCluster
为了保险起见,在创建完集群后,我会使用 kubectl get node 命令确认创建已经成功完成。
$ kubectl get node
NAME STATUS ROLES AGE VERSION
aks-nodepool1-37284717-vmss000000 Ready agent 5m39s v1.13.12
aks-nodepool1-37284717-vmss000001 Ready agent 5m51s v1.13.12
第二步:安装Dapr CLI
这次我是通过 Visual Studio Code 的远程部署连接到 Azure VM(Ubuntu)上进行操作的,因此我要在这个 Ubuntu 环境中安装 Dapr CLI。你可以在“安装 Dapr CLI”中查看在 Mac/Windows 上的步骤。
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
Your system is linux_amd64
Installing Dapr CLI...
Downloading https://github.com/dapr/cli/releases/download/v0.2.0/dapr_linux_amd64.tar.gz ...
dapr installed into /usr/local/bin successfully.
cli version: 0.2.0
runtime version: n/a
To get started with Dapr, please visit https://github.com/dapr/docs/tree/master/getting-started
第三步。在 AKS 集群中安装 dapr。
使用以下命令在 AKS 集群中安装 dapr。
$ dapr init --kubernetes
⌛ Making the jump to hyperspace...
ℹ️ Note: this installation is recommended for testing purposes. For production environments, please use Helm
✅ Deploying the Dapr Operator to your cluster...
✅ Success! Dapr has been installed. To verify, run 'kubectl get pods -w' in your terminal
安装完成后,使用kubectl get pods命令可以确认pod是否正确启动,会显示与dapr相关的pod (dapr-operator, dapr-placement, dapr-sidecar-injector)。
$ kubectl get pods -w
NAME READY STATUS RESTARTS AGE
dapr-operator-68f7dcb454-nv25m 1/1 Running 0 70s
dapr-placement-6d77d54dc6-5bsp4 1/1 Running 0 70s
dapr-sidecar-injector-86d6ccf956-5j56p 1/1 Running 0 70s
第四步:建立 State Store(Redis)
Dapr 可以将 Redis、CosmosDB、DynamoDB、Cassandra 等多种数据服务用作 state 信息的存储,但是这次我们想要使用 Redis。
我们打算使用 helm 来创建 Redis。由于在11月发布了 Helm 3.0.0,所以我们先从设置 Helm3 开始。以下假设使用的是 Linux 作为工作终端,如果使用其他环境,请参考 Helm 的官方文档。Helm 3已经变成了 Tillerless,并且命令结构稍有不同。实际执行的命令如下所示。
$curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 6617 100 6617 0 0 21483 0 --:--:-- --:--:-- --:--:-- 21414
Downloading https://get.helm.sh/helm-v3.0.0-linux-amd64.tar.gz
Preparing to install helm into /usr/local/bin
helm installed into /usr/local/bin/helm
如果已安装了Helm命令,就可以注册图表仓库。
$helm repo add stable https://kubernetes-charts.storage.googleapis.com/
"stable" has been added to your repositories
确认存储库可以被引用。
$helm search repo stable
NAME CHART VERSION APP VERSION DESCRIPTION
stable/acs-engine-autoscaler 2.2.2 2.1.1 DEPRECATED Scales worker nodes within agent pools
stable/aerospike 0.3.1 v4.5.0.5 A Helm chart for Aerospike in Kubernetes
stable/airflow 5.1.0 1.10.4 Airflow is a platform to programmatically autho...
# more
我打算将redis安装到默认的命名空间中。请使用以下命令安装redis。
$ helm install redis stable/redis
可以确认 Redis 已经部署好了。
$ helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
redis default 1 2019-11-28 17:49:09.405349951 +0000 UTC deployed redis-10.0.2 5.0.7
$ kubectl get pods -n redis
NAME READY STATUS RESTARTS AGE
redis-master-0 1/1 Running 0 6m2s
redis-slave-0 1/1 Running 0 6m2s
redis-slave-1 1/1 Running 0 5m21s
如果Redis被成功创建,使用deploy/redis.yaml文件来配置Redis。
请参考以下链接至 GitHub 上的项目,路径为 210_aks_dapr_redis/deploy/:
https://github.com/y10e/azure-sample-cli/tree/master/210_aks_dapr_redis/deploy
请编辑 YOUR_REDIS_HOST_HERE 和 YOUR_REDIS_KEY_HERE 的部分。
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
metadata:
- name: redisHost
value: redis-master:6379
- name: redisPassword
value: <YOUR_REDIS_KEY_HERE>
“YOUR_REDIS_KEY_HERE” 指定了通过下列命令确认的值。
$ kubectl get secret redis -o jsonpath="{.data.redis-password}" | base64 --decode
当您修改文件后,将应用配置。这样一来,Redis的操作就暂时完成了。
$ kubectl apply -f ./deploy/redis.yaml
component.dapr.io/statestore created
顺便提一下,Redis的键也可以保存在Kubernetes的Secrets中。示例在这里介绍过。
在中国,只需要一种选择,将以下内容翻译成中国传统文化:
第五步:部署具有Dapr的Node.js应用程序
从这里开始,我们将推进使用 Dapr 进行应用程序的部署。
同样,我们将使用 sample 的 deploy/node.yaml 文件。通过查看 node.yaml 文件,可以看到在 Deployment 的 annotations 中启用了 Dapr Sidecar 的配置。同时,还指定了应用程序的 ID、端口等信息给 Dapr Sidecar。
annotations:
dapr.io/enabled: "true"
dapr.io/id: "nodeapp"
dapr.io/port: "3000"
部署。
$ kubectl apply -f ./deploy/node.yaml
service/nodeapp created
deployment.apps/nodeapp created
部署完成后,您可以使用 “kubectl get pod” 命令查看以 “nodeapp” 开头的 pod。
$ kubectl get pods --selector=app=node
NAME READY STATUS RESTARTS AGE
nodeapp-5956c68964-wfnsh 2/2 Running 0 41h
$ kubectl exec -it nodeapp-5956c68964-wfnsh /bin/ash
由于示例是基于alpine的容器,因此可以使用ash连接并验证node.js代码。
$ kubectl exec -it nodeapp-5956c68964-wfnsh /bin/ash
Defaulting container name to node.
Use 'kubectl describe pod/nodeapp-5956c68964-wfnsh -n default' to see all of the containers in this pod.
/app # ls
app.js node_modules package-lock.json package.json
/app # cat app.js
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// ------------------------------------------------------------
const express = require('express');
const bodyParser = require('body-parser');
require('isomorphic-fetch');
const app = express();
app.use(bodyParser.json());
const daprPort = process.env.DAPR_HTTP_PORT || 3500;
const stateUrl = `http://localhost:${daprPort}/v1.0/state`;
const port = 3000;
app.get('/order', (_req, res) => {
fetch(`${stateUrl}/order`)
.then((response) => {
if (!response.ok) {
throw "Could not get state.";
}
return response.text();
}).then((orders) => {
res.send(orders);
}).catch((error) => {
console.log(error);
res.status(500).send({message: error});
});
});
app.post('/neworder', (req, res) => {
const data = req.body.data;
const orderId = data.orderId;
console.log("Got a new order! Order ID: " + orderId);
const state = [{
key: "order",
value: data
}];
fetch(stateUrl, {
method: "POST",
body: JSON.stringify(state),
headers: {
"Content-Type": "application/json"
}
}).then((response) => {
if (!response.ok) {
throw "Failed to persist state.";
}
console.log("Successfully persisted state.");
res.status(200).send();
}).catch((error) => {
console.log(error);
res.status(500).send({message: error});
});
});
这个应用将与这里的相同。有两种方法,GET /order 和 POST /neworder,新订单将使用dapr的状态管理API来记录接收到的订单信息(键为order)。订单通过order方法来获取订单的状态。
为了后续操作,您可以在 node.yaml 文件中确认已部署的服务的 IP,然后将其设置为环境变量。
$ kubectl get svc nodeapp
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nodeapp LoadBalancer 10.0.42.100 xx.xxx.xxx.xxx 80:32133/TCP 9m55s
$ export NODE_APP=$(kubectl get svc nodeapp --output 'jsonpath={.status.loadBalancer.ingress[0].ip}')
第6步。将带有Dapr的Python应用部署上去。
这次我们将使用 sample 的 deploy/python.yaml。
$ kubectl apply -f ./deploy/python.yaml
deployment.apps/pythonapp created
在部署后,您可以使用 “kubectl get pod” 命令来确认以 “pythonapp” 开头的 pod。
$ kubectl get pods --selector=app=python
NAME READY STATUS RESTARTS AGE
pythonapp-5d9649fccd-pv8x6 2/2 Running 0 42h
由于Python示例也是基于Alpine容器,因此我们可以使用ash连接并查看应用程序的代码。
kubectl exec -it pythonapp-5d9649fccd-pv8x6 /bin/ash
/app # cat app.py
# ------------------------------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------------------------------
import time
import requests
import os
dapr_port = os.getenv("DAPR_HTTP_PORT", 3500)
dapr_url = "http://localhost:{}/v1.0/invoke/nodeapp/method/neworder".format(dapr_port)
n = 0
while True:
n += 1
message = {"data": {"orderId": n}}
try:
response = requests.post(dapr_url, json=message)
except Exception as e:
print(e)
time.sleep(1)
这个应用程序非常简单。每秒钟,它都会向dapr的边界服务的端点发送下一个HTTP请求进行POST操作。
同时,通过指定Node.js应用程序的ID和neworder方法,它会通过dapr调用Node.js服务。
POST http://localhost:<daprPort>/v1.0/invoke/<appId>/method/<method-name>
步骤7. 在 Node 和 Python 应用程序之间进行与 dapr 的交互进行确认。
我們試試使用以下命令來檢查 Node.js 的日誌。
kubectl logs --selector=app=node -c node
当这样做时,您可以在Python应用程序中调用并确认每秒更新的订单信息日志。
Got a new order! Order ID: 1
Successfully persisted state.
Got a new order! Order ID: 2
Successfully persisted state.
Got a new order! Order ID: 3
Successfully persisted state.
Got a new order! Order ID: 4
Successfully persisted state.
Got a new order! Order ID: 5
Successfully persisted state.
另外,连接到Node.js的GET端点时能够确认最新的订单状态(订单ID)。
$ curl $NODE_APP/order
{"orderId":72}
补充填写
如果在上述的操作验证中无法获取到正常的值,可能是dapr的配置有问题。在这种情况下,我认为通过查看节点应用POD内的daprd日志等信息,可以了解到发生了什么样的错误。
kubectl logs nodeapp-5956c68964-wfnsh -c daprd
最后
在这次中,我们介绍了一个在Azure Kubernetes Service上使用Dapr在语言不同的各个应用程序(POD)之间管理状态信息的应用程序示例。
截至本帖子发布日期(2019年12月1日),Dapr已发布0.2.0版本,但由于几乎没有日语信息,我们希望继续关注并介绍即将发布的1.0.0版本。
参考文献
Dapr 官方网站
https://dapr.io/#
Dapr GitHub
https://github.com/dapr/docs
Dapr 的 GitHub 页面
https://github.com/dapr/docs
路线图
https://github.com/dapr/dapr/wiki/%E8%B7%AF%E7%BA%BF%E5%9B%BE
以下是使用了示例进行投稿的链接:
https://github.com/y10e/azure-sample-cli/tree/master/210_aks_dapr_redis/deploy