使用Dapr框架在Azure Kubernetes Service(AKS)上进行微服务的状态管理

首先

我打算在今年12月份的Microsoft Azure Tech Advent Calendar 2019上发布一篇关于Dapr框架在AKS(Azure Kubernetes Service)上的使用的投稿。这个框架在2019年10月份发布。

概括回答

2019年10月,Dapr(分散アプリケーションランタイム)作为一个框架被发布,旨在简化微服务应用程序的开发。尽管目前只是α版,但Dapr正在开发中,旨在提供跨编程语言的微服务调用功能、状态管理、服务之间消息传递、资源绑定和分布式服务之间的跟踪等功能。

dapr_conceptual_model.jpg

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

overview-sidecar.png

这次我们将在 Kubernetes 上验证 Dapr 的操作,作为 Azure Tech Advent Calendar 的一部分,我将在 Microsoft Azure 的托管 k8 服务 AKS 上执行。
在 AKS 上运行的容器应用程序中,使用 Dapr 框架来管理应用程序状态的方法,我们将按照以下步骤整理示例命令和代码解释。

    1. 创建 Azure Kubernetes Service (AKS) 集群

 

    1. 安装 Dapr CLI

 

    1. 在 AKS 集群上安装 dapr

 

    1. 构建 Dapr 的 State Store (Redis)

 

    1. 部署带有 dapr 的 Node.js 应用程序

 

    1. 部署带有 dapr 的 Python 应用程序

 

    在 Node.js 和 Python 应用程序之间进行 dapr 的交互并进行操作确认

最终,AKS 上将实现以下配置。State Store 将使用 Redis。

Architecture_Diagram.png

步骤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

bannerAds