前言

我前不久体验了 Dapr 的单机使用,体验不错。不过Dapr 作为一个分布式应用的运行时,当然需要加入集群使用才能完全体验呀。我们使用 GitHub 上的文档 部署。首先我们需要一个 Kubernetes 集群来做实验,选择 Rancher 由于我比较熟悉 Rancher ,同时 UI 操作才能让人感受到工具的便利嘛。之后会使用 RancherHelm 部署 DaprRedis 图表,不过由于 UI 操作的便利,点一下就部署了。

集群的环境部署比较长,不过本来这些工具就是部署之后一劳永逸降低心智负担,当然你有一个 Kubernetes 可以直接实验就更好了。

下面是 架构 图
image.png
可以看到我们已经接触到了 Dapr 多语言的特性了。两个 Pod 包括不同语言的应用:Python 和 Node.js ,他们通过 Dapr Injects 的 sidecar 交互。

那么我们开始吧!

环境

Kubernetes 部署

Docker

  1. $ yum install docker-ce-18.06.2.ce-3.el7 -y

语雀内容

Rancher

  1. $ sudo docker run -d --restart=unless-stopped -p 80:80 -p 443:443 rancher/rancher:latest

语雀内容

K8S By Rancher

语雀内容

你也可以使用其他的工具快速启动 Kubernetes 集群

可选部署 Kubernetes 方法

Dapr

  1. Using Helm(Advanced)

添加 Dapr 源 https://daprio.azurecr.io/helm/v1/repo 到商店

image.png

  1. 启动 Dapr

image.png

  1. 默认配置即可

image.png

  1. 等待一段时间完成

image.png

你可以选择其他方法安装 Dapr

Using Dapr CLI【可选】官方文档

Redis

  1. 同样在 Rancher 的商店中找到 Redis Chart,默认部署即可。(可以自己设定一些选项比如密码和访问端口等方便你接下来的操作)

image.png

  1. 等待一会儿部署完毕

image.png

  1. 使用 Rancher 自带的 kubectl 即可执行命令

image.png

  1. 使用 以下命令获取到 redis 命名空间下设置的密码

kubectl get secret --namespace redis redis -o jsonpath="{.data.redis-password}" | base64 --decode
image.png
可以看到密码是 123456(方便实验,内网环境,生产环境请使用生成密码)

  1. 找到 Redis 的访问端口,我的是 192.168.0.116:32091(这是随机的端口)

image.png

  1. 接下来我们把这些值置换到官方给的模板里的

    Configuration

    这里是明文存储的 secret ,生产环境请参照.secret management .

Configuring Redis for State Persistence and Retrieval

在刚才的 kubectl 命令行中继续操作。

  1. $ vi redis-state.yaml

记得将上文 4 ,5 步骤得到的 HOST 地址 和 Password 密码替换.

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Component
  3. metadata:
  4. name: statestore
  5. spec:
  6. type: state.redis
  7. metadata:
  8. - name: redisHost
  9. value: <HOST>
  10. - name: redisPassword
  11. value: <PASSWORD>

同时这个文件也可以在 samples/2.hello-kubernetes/deploy下找到

我的 Yaml 文件

Apply the configuration

使用 kubectl apply 部署。

  1. $ kubectl apply -f redis-state.yaml
  2. $ kubectl apply -f redis-pubsub.yaml

HelloKubernetes

Download Code

首先下载示例代码

  1. $ git clone https://github.com/dapr/samples.git
  2. $ cd samples/2.hello-kubernetes/node

Node

该示例有多个应用,首先我们看老朋友 Node.js 的代码

Cat app.js

  1. // $ cat app.js
  2. const express = require('express');
  3. const bodyParser = require('body-parser');
  4. require('isomorphic-fetch');
  5. const app = express();
  6. app.use(bodyParser.json());
  7. const daprPort = process.env.DAPR_HTTP_PORT || 3500;
  8. const daprUrl = `http://localhost:${daprPort}/v1.0`;
  9. const port = 3000;
  10. app.get('/order', (_req, res) => {
  11. fetch(`${daprUrl}/state/order`)
  12. .then((response) => {
  13. return response.json();
  14. }).then((order) => {
  15. res.send(order);
  16. });
  17. });
  18. app.post('/neworder', (req, res) => {
  19. const data = req.body.data;
  20. const orderId = data.orderId;
  21. console.log("Got a new order! Order ID: " + orderId);
  22. const state = [{
  23. key: "order",
  24. value: data
  25. }];
  26. fetch(`${daprUrl}/state`, {
  27. method: "POST",
  28. body: JSON.stringify(state),
  29. headers: {
  30. "Content-Type": "application/json"
  31. }
  32. }).then((response) => {
  33. console.log((response.ok) ? "Successfully persisted state" : "Failed to persist state");
  34. });
  35. res.status(200).send();
  36. });
  37. app.listen(port, () => console.log(`Node App listening on port ${port}!`));

可以看到这个和我们之前使用的单机的 Node.js 一样,可以参照我的这篇文章

Node Application Explain

语雀内容

Deploy Node Application

虽然代码一样,但是我们部署的方法变了,不是直接使用 dapr 进行部署,而是使用 kubectl(注意我们当前目录还在 samples/2.hello-kubernetes/node

  1. $ kubectl apply -f ../deploy/node.yaml
  2. service/nodeapp created
  3. deployment.apps/nodeapp created

如果你好奇,我们可以看一看 node.yaml 的内容(我把代码块调小了,可以自己滑动查看)

  1. kind: Service
  2. apiVersion: v1
  3. metadata:
  4. name: nodeapp
  5. labels:
  6. app: node
  7. spec:
  8. selector:
  9. app: node
  10. ports:
  11. - protocol: TCP
  12. port: 80
  13. targetPort: 3000
  14. type: LoadBalancer
  15. ---
  16. apiVersion: apps/v1
  17. kind: Deployment
  18. metadata:
  19. name: nodeapp
  20. labels:
  21. app: node
  22. spec:
  23. replicas: 1
  24. selector:
  25. matchLabels:
  26. app: node
  27. template:
  28. metadata:
  29. labels:
  30. app: node
  31. annotations:
  32. dapr.io/enabled: "true"
  33. dapr.io/id: "nodeapp"
  34. dapr.io/port: "3000"
  35. spec:
  36. containers:
  37. - name: node
  38. image: dapriosamples/hello-k8s-node
  39. ports:
  40. - containerPort: 3000
  41. imagePullPolicy: Always
  1. 注意这两个选项, ` dapr.io/enabled: "true"` 告诉 `Dapr` 控制器注入边车<br /> ` dapr.io/id: "nodeapp"` 告诉 `Dapr` 这个部署的唯一 ID(用来 `Dapr` 之间相互通信)

现在我们来查看状态和外部 IP(外部 IP 在 pending 是因为我们使用 Rancher 的原因,4 层负载均衡并不是每一个运营商都支持,不过外部 IP 不影响本次 Demo, 我会在下面做一个 Ingress 访问它

  1. $ kubectl get svc nodeapp
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. nodeapp LoadBalancer 10.43.255.238 <pending> 80:30557/TCP 5m17s

将 NodeApp 导入环境变量【跳过:由于我们没有 EXTERNAL-IP 】

  1. $ export NODE_APP=$(kubectl get svc nodeapp --output 'jsonpath={.status.loadBalancer.ingress[0].ip}')

Python

我们还有一个 Python 的 Application 需要部署。

  1. $cd samples/2.hello-kubernetes/python
  2. $cat app.py

Python 的代码很简单,他把 JSON 消息发到 localhost:3500 (这个端口是可变的,3500Dapr 默认端口) 调用 Node App 的 neworder endpoint 。我们的消息也是很简单的每秒递增一次的消息。

  1. import time
  2. import requests
  3. import os
  4. dapr_port = os.getenv("DAPR_HTTP_PORT", 3500)
  5. dapr_url = "http://localhost:{}/v1.0/invoke/nodeapp/method/neworder".format(dapr_port)
  6. n = 0
  7. while True:
  8. n += 1
  9. message = {"data": {"orderId": n}}
  10. try:
  11. response = requests.post(dapr_url, json=message)
  12. except Exception as e:
  13. print(e)
  14. time.sleep(1)

部署并等待 Pod 进入 Running 状态

  1. $ kubectl apply -f ./deploy/python.yaml
  2. $ kubectl get pods --selector=app=python -w
  3. NAME READY STATUS RESTARTS AGE
  4. pythonapp-b5fd4474d-tk84x 0/2 ContainerCreating 0 9s
  5. pythonapp-b5fd4474d-tk84x 1/2 ErrImagePull 0 38s
  6. pythonapp-b5fd4474d-tk84x 1/2 ImagePullBackOff 0 39s
  7. pythonapp-b5fd4474d-tk84x 2/2 Running 0 3m24s

Observe

我们毕竟使用的是 UI 界面,现在我们可以通过 Rancher UI 检验一下我们的成果。
可以看到 nodeapppythonapp 两个 Pod 都在 active 状态。同时我们点进入更多信息界面可以看到 Pod 中还有 Dapr 的 边车
image.png

我们进入 nodeapp 检查日志
image.png

或者使用命令行

  1. $ kubectl logs --selector=app=node -c node
  2. Got a new order! Order ID: 1
  3. Successfully persisted state
  4. Got a new order! Order ID: 2
  5. Successfully persisted state
  6. Got a new order! Order ID: 3
  7. Successfully persisted state

Persistence

当然,我们不能使用 LoadBalance 或者 外部 IP 并不能阻止我们检查持久性。
我们可以通过 Rancher 添加一个 Ingress 七层负载均衡到 目标容器进行访问。
image.png

然后设置 nodeapp 的端口 3000
image.png

等待一会儿出现分配的地址 http://nodeapp.default.192.168.0.102.xip.io

image.png

浏览器或者命令行访问,会返回最新的 orderID

  1. $ curl http://nodeapp.default.192.168.0.102.xip.io/order
  2. {"orderID":"42"}

Clean

这会清理所有的东西,包括 状态组件。

  1. $ cd samples/2.hello-kubernetes/deploy
  2. $ kubectl delete -f .

image.png

Summary

首先使用 Dapr 的开发感受还需要一步,就是修改代码之后编译为自己的 Docker 然后部署在 Kubernetes 上,这里不详述,留给读者当练手即可。引用官方的 Next Steps 大概导引一下。

Next Steps

Now that you’re successfully working with Dapr, you probably want to update the sample code to fit your scenario. The Node.js and Python apps that make up this sample are deployed from container images hosted on a private Azure Container Registry. To create new images with updated code, you’ll first need to install docker on your machine. Next, follow these steps:

  1. Update Node or Python code as you see fit!
  2. Navigate to the directory of the app you want to build a new image for.
  3. Run docker build -t <YOUR_IMAGE_NAME> .. You can name your image whatever you like. If you’re planning on hosting it on docker hub, then it should start with <YOUR_DOCKERHUB_USERNAME>/.
  4. Once your image has built you can see it on your machines by running docker images.
  5. To publish your docker image to docker hub (or another registry), first login: docker login. Then rundocker publish <YOUR IMAGE NAME>.
  6. Update your .yaml file to reflect the new image name.
  7. Deploy your updated Dapr enabled app: kubectl apply -f <YOUR APP NAME>.yaml.

这一次的 Dapr 体验遇到的一些问题主要是集群本身的网络问题。本身 Dapr 在这个 Demo 中起到的就是通信和存储状态的服务,但是在实际使用中并没有看到非常多的日志可供调试。也就是说依旧需要专业的运维人员进行 Dapr 开发的维护。不过,本来 Dapr 的打算就是制定标准,划开开发人员和运维人员的界限降低心智负担。

Demo 中是两个语言的应用,我在查看日志的时候看了下环境变量,看到了 GRPC 的 50001 端口。同时 Dapr 也提供 各个语言的 SDK。我们看过了两个应用的代码,都很简单,不过可以看到的是对于通信的感知是不大的,或者说对于 Dapr 的感知不大,意味着微服务改为 Dapr 的并不需要改动太多,就是服务的 URL 变动一下。剩余的交给 Dapr 的 Sidecar 就行了。

如果你不选择 Rancher 作为 搭建 Kubernetes 的集群的工具的话,跳转 也有其他的教程指导。没有一些非常明白好看的 UI 了。Rancher 的可视化部署状态在这个 Demo 中起到了很大的作用。

接下来对于 Dapr 会尝试一下 他的事件驱动特性和 Pub/Sub 。这对于分布式应用来说也比较好玩。我非常赞同边车的开发模式,说到 sidecar 很多人都会想到 Istio 和 envoy,我也是一开始因为 ServiceMesh 和 sidecar 才开始关注 Dapr 的。刚看到开源的消息到现在,GitHub stars 已经到 4k 了,不过一天时间涨了 1k 多。微软的能量还是挺大的。

Refer