A colleague of mine has several times asked me about Dapr and what I thought. Dapr finally released a 1.0 in February this year and I figured it was time to give it a look.
Dapr stands for Distributed APplication Runtime and is meant to take care of some of the common problems one encounters with microservices, namely pub/sub and the stateless nature of containerized services.
Installing
First, let’s follow the steps to get Dapr installed: https://docs.dapr.io/getting-started/install-dapr-cli/
$ wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
Your system is linux_amd64
Installing Dapr CLI...
Getting the latest Dapr CLI...
Installing v1.0.1 Dapr CLI...
Downloading https://github.com/dapr/cli/releases/download/v1.0.1/dapr_linux_amd64.tar.gz ...
[sudo] password for builder:
dapr installed into /usr/local/bin successfully.
CLI version: 1.0.1
Runtime version: n/a
Verification
$ dapr
__
____/ /___ _____ _____
/ __ / __ '/ __ \/ ___/
/ /_/ / /_/ / /_/ / /
\__,_/\__,_/ .___/_/
/_/
===============================
Distributed Application Runtime
Usage:
dapr [command]
Available Commands:
completion Generates shell completion scripts
components List all Dapr components. Supported platforms: Kubernetes
configurations List all Dapr configurations. Supported platforms: Kubernetes
dashboard Start Dapr dashboard. Supported platforms: Kubernetes and self-hosted
help Help about any command
init Install Dapr on supported hosting platforms. Supported platforms: Kubernetes and self-hosted
invoke Invoke a method on a given Dapr application. Supported platforms: Self-hosted
list List all Dapr instances. Supported platforms: Kubernetes and self-hosted
logs Get Dapr sidecar logs for an application. Supported platforms: Kubernetes
mtls Check if mTLS is enabled. Supported platforms: Kubernetes
publish Publish a pub-sub event. Supported platforms: Self-hosted
run Run Dapr and (optionally) your application side by side. Supported pbuilder@DESKTOP-JBA79RT:~/Workspaces/dapr$
status Show the health status of Dapr services. Supported platforms: Kubernetes
stop Stop Dapr instances and their associated apps. . Supported platforms: Self-hosted
uninstall Uninstall Dapr runtime. Supported platforms: Kubernetes and self-hosted
upgrade Upgrades a Dapr control plane installation in a cluster. Supported platforms: Kubernetes
Flags:
-h, --help help for dapr
-v, --version version for dapr
Use "dapr [command] --help" for more information about a command.
Initializing Dapr
This should install redis, zipkin and a few other required components
$ dapr init
⌛ Making the jump to hyperspace...
↖ Downloading binaries and setting up components...
Dapr runtime installed to /home/builder/.dapr/bin, you may run the following to add it to your path if you want to run daprd directly:
export PATH=$PATH:/home/builder/.dapr/bin
✅ Downloaded binaries and completed components set up.
ℹ️ daprd binary has been installed to /home/builder/.dapr/bin.
ℹ️ dapr_placement container is running.
ℹ️ dapr_redis container is running.
ℹ️ dapr_zipkin container is running.
ℹ️ Use `docker ps` to check running containers.
✅ Success! Dapr is up and running. To get started, go here: https://aka.ms/dapr-getting-started
Verification
$ dapr --version
CLI version: 1.0.1
Runtime version: 1.0.1
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
19c53c5ab322 daprio/dapr "./placement" About a minute ago Up About a minute 0.0.0.0:50005->50005/tcp dapr_placement
90e7af85b8d5 openzipkin/zipkin "start-zipkin" About a minute ago Up About a minute (healthy) 9410/tcp, 0.0.0.0:9411->9411/tcp dapr_zipkin
aa1343a2ac94 redis "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:6379->6379/tcp dapr_redis
$ ls $HOME/.dapr
bin components config.yaml
First test, a sidecar with no app
$ dapr run --app-id myapp --dapr-http-port 3500
WARNING: no application command found.
ℹ️ Starting Dapr with id myapp. HTTP Port: 3500. gRPC Port: 44935
ℹ️ Checking if Dapr sidecar is listening on HTTP port 3500
INFO[0000] starting Dapr Runtime -- version 1.0.1 -- commit 45bc40d app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] log level set to: info app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] metrics server started on :41371/ app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.metrics type=log ver=1.0.1
INFO[0000] standalone mode configured app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] app id: myapp app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] mTLS is disabled. Skipping certificate request and tls validation app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] local service entry announced: myapp -> 172.20.135.184:33997 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.contrib type=log ver=1.0.1
INFO[0000] Initialized name resolution to standalone app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] component loaded. name: pubsub, type: pubsub.redis/ app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] waiting for all outstanding components to be processed app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] component loaded. name: statestore, type: state.redis/ app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] all outstanding components processed app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] enabled gRPC tracing middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.grpc.api type=log ver=1.0.1
INFO[0000] enabled gRPC metrics middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.grpc.api type=log ver=1.0.1
INFO[0000] API gRPC server is running on port 44935 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] enabled metrics http middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.http type=log ver=1.0.1
INFO[0000] enabled tracing http middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.http type=log ver=1.0.1
INFO[0000] http server is running on port 3500 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] The request body size parameter is: 4 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] enabled gRPC tracing middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.grpc.internal type=log ver=1.0.1
INFO[0000] enabled gRPC metrics middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.grpc.internal type=log ver=1.0.1
INFO[0000] internal gRPC server is running on port 33997 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.actor type=log ver=1.0.1
WARN[0000] failed to read from bindings: app channel not initialized app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] dapr initialized. Status: Running. Init Elapsed 6.5374ms app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] placement tables updated, version: 0 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.actor.internal.placement type=log ver=1.0.1
ℹ️ Checking if Dapr sidecar is listening on GRPC port 44935
ℹ️ Dapr sidecar is up and running.
✅ You're up and running! Dapr logs will appear here.
We should be able to set and retrieve the state (which is stored in redis) using a bash prompt in a separate terminal
builder@DESKTOP-JBA79RT:~$ curl -X POST -H "Content-Type: application/json" -d '[{ "key": "name", "value": "Bruce Wayne"}]' http://localhost:3500/v1.0/state/statestore
builder@DESKTOP-JBA79RT:~$ curl http://localhost:3500/v1.0/state/statestore/name
"Bruce Wayne"builder@DESKTOP-JBA79RT:~$
We can also use the redis cli on the redis container to view the data:
builder@DESKTOP-JBA79RT:~$ docker exec -it dapr_redis redis-cli
127.0.0.1:6379> keys *
1) "myapp||name"
127.0.0.1:6379> hgetall "myapp||name"
1) "data"
2) "\"Bruce Wayne\""
3) "version"
4) "2"
Back in our first terminal, let’s ctrl-C to kill it and come back to our bash prompt:
terminated signal received: shutting down
INFO[0185] stop command issued. Shutting down all operations app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
✅ Exited Dapr successfully
Create secret in a JSON:
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ mkdir dapr-components
$ cat dapr-components/somesecrets.json
{
"MySecret": "Some people just want to watch the world burn"
}
We can go ahead and create a localSecretStore.yaml file:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: my-secret-store
namespace: default
spec:
type: secretstores.local.file
version: v1
metadata:
- name: secretsFile
value: ./somesecrets.json
- name: nestedSeparator
value: ":"
showing file:
$ cat dapr-components/localSecretStore.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: my-secret-store
namespace: default
spec:
type: secretstores.local.file
version: v1
metadata:
- name: secretsFile
value: ./dapr-components/somesecrets.json
- name: nestedSeparator
value: ":"
and then testing
$ curl http://localhost:3500/v1.0/secrets/my-secret-store/MySecret
{"MySecret":"Some people just want to watch the world burn"}
We can update it just to see it working:
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ cat dapr-components/localSecretStore.yaml | grep somesecrets.json
value: ./dapr-components/somesecrets.json
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ dapr run --app-id myapp --dapr-http-port 3500 --components-path ./dapr-components/
WARNING: no application command found.
ℹ️ Starting Dapr with id myapp. HTTP Port: 3500. gRPC Port: 45005
ℹ️ Checking if Dapr sidecar is listening on HTTP port 3500
INFO[0000] starting Dapr Runtime -- version 1.0.1 -- commit 45bc40d app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] log level set to: info app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] metrics server started on :45113/ app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.metrics type=log ver=1.0.1
INFO[0000] standalone mode configured app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] app id: myapp app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] mTLS is disabled. Skipping certificate request and tls validation app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] local service entry announced: myapp -> 172.20.135.184:41583 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.contrib type=log ver=1.0.1
INFO[0000] Initialized name resolution to standalone app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] waiting for all outstanding components to be processed app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] component loaded. name: my-secret-store, type: secretstores.local.file/v1 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] all outstanding components processed app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] enabled gRPC tracing middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.grpc.api type=log ver=1.0.1
INFO[0000] enabled gRPC metrics middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.grpc.api type=log ver=1.0.1
INFO[0000] API gRPC server is running on port 45005 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] enabled metrics http middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.http type=log ver=1.0.1
INFO[0000] enabled tracing http middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.http type=log ver=1.0.1
INFO[0000] http server is running on port 3500 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] The request body size parameter is: 4 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] enabled gRPC tracing middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.grpc.internal type=log ver=1.0.1
INFO[0000] enabled gRPC metrics middleware app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.grpc.internal type=log ver=1.0.1
INFO[0000] internal gRPC server is running on port 41583 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.actor type=log ver=1.0.1
WARN[0000] failed to read from bindings: app channel not initialized app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] dapr initialized. Status: Running. Init Elapsed 2.4697999999999998ms app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
INFO[0000] placement tables updated, version: 0 app_id=myapp instance=DESKTOP-JBA79RT scope=dapr.runtime.actor.internal.placement type=log ver=1.0.1
ℹ️ Checking if Dapr sidecar is listening on GRPC port 45005
ℹ️ Dapr sidecar is up and running.
✅ You're up and running! Dapr logs will appear here.
And in another shell
$ curl http://localhost:3500/v1.0/secrets/my-secret-store/MySecret
{"MySecret":"Superman is better"}
Dapr Pub/Sub quick start
Let’s do one of the simpler quick starts with node and python
builder@DESKTOP-JBA79RT:~/Workspaces$ git clone https://github.com/dapr/quickstarts.git
Cloning into 'quickstarts'...
remote: Enumerating objects: 43, done.
remote: Counting objects: 100% (43/43), done.
remote: Compressing objects: 100% (36/36), done.
remote: Total 2341 (delta 17), reused 12 (delta 6), pack-reused 2298
Receiving objects: 100% (2341/2341), 10.05 MiB | 23.81 MiB/s, done.
Resolving deltas: 100% (1381/1381), done.
builder@DESKTOP-JBA79RT:~/Workspaces$ cd quickstarts/
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts$ cd pub-sub/builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub$ cd node-subscriber/
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub/node-subscriber$
NPM install
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub/node-subscriber$ nvm use 10.22.1
Now using node v10.22.1 (npm v6.14.6)
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub/node-subscriber$ npm install
added 50 packages from 37 contributors and audited 50 packages in 0.678s
found 0 vulnerabilities
Verify it runs
$ dapr run --app-id node-subscriber --app-port 3000 node app.js
ℹ️ Starting Dapr with id node-subscriber. HTTP Port: 39115. gRPC Port: 43107
INFO[0000] starting Dapr Runtime -- version 1.0.1 -- commit 45bc40d app_id=node-subscriber instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
….
Now setup the python3 subscriber
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub/node-subscriber$ cd ../python-subscriber/
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub/python-subscriber$ pip3 install -r requirements.txt
Collecting Click==7.0 (from -r requirements.txt (line 1))
Downloading https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl (81kB)
100% |████████████████████████████████| 81kB 2.3MB/s
Collecting Flask==1.1.1 (from -r requirements.txt (line 2))
Downloading https://files.pythonhosted.org/packages/9b/93/628509b8d5dc749656a9641f4caf13540e2cdec85276964ff8f43bbb1d3b/Flask-1.1.1-py2.py3-none-any.whl (94kB)
100% |████████████████████████████████| 102kB 5.7MB/s
Collecting Flask-Cors==3.0.8 (from -r requirements.txt (line 3))
Downloading https://files.pythonhosted.org/packages/78/38/e68b11daa5d613e3a91e4bf3da76c94ac9ee0d9cd515af9c1ab80d36f709/Flask_Cors-3.0.8-py2.py3-none-any.whl
Collecting itsdangerous==1.1.0 (from -r requirements.txt (line 4))
Using cached https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
Collecting Jinja2==2.10.1 (from -r requirements.txt (line 5))
Downloading https://files.pythonhosted.org/packages/1d/e7/fd8b501e7a6dfe492a433deb7b9d833d39ca74916fa8bc63dd1a4947a671/Jinja2-2.10.1-py2.py3-none-any.whl (124kB)
100% |████████████████████████████████| 133kB 8.0MB/s
Collecting MarkupSafe==1.1.1 (from -r requirements.txt (line 6))
Downloading https://files.pythonhosted.org/packages/b2/5f/23e0023be6bb885d00ffbefad2942bc51a620328ee910f64abe5a8d18dd1/MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl
Collecting six==1.12.0 (from -r requirements.txt (line 7))
Downloading https://files.pythonhosted.org/packages/73/fb/00a976f728d0d1fecfe898238ce23f502a721c0ac0ecfedb80e0d88c64e9/six-1.12.0-py2.py3-none-any.whl
Collecting Werkzeug==0.15.6 (from -r requirements.txt (line 8))
Downloading https://files.pythonhosted.org/packages/b7/61/c0a1adf9ad80db012ed7191af98fa05faa95fa09eceb71bb6fa8b66e6a43/Werkzeug-0.15.6-py2.py3-none-any.whl (328kB)
100% |████████████████████████████████| 337kB 2.0MB/s
Installing collected packages: Click, Werkzeug, itsdangerous, MarkupSafe, Jinja2, Flask, six, Flask-Cors
Successfully installed Click-7.0 Flask-1.1.1 Flask-Cors-3.0.8 Jinja2-2.10.1 MarkupSafe-1.1.1 Werkzeug-0.15.6 itsdangerous-1.1.0 six-1.12.0
And verify that
$ dapr run --app-id python-subscriber --app-port 5000 python3 app.py
ℹ️ Starting Dapr with id python-subscriber. HTTP Port: 39213. gRPC Port: 41375
INFO[0000] starting Dapr Runtime -- version 1.0.1 -- commit 45bc40d app_id=python-subscriber instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
...
Lastly, let’s build and test the react app
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub/python-subscriber$ cd ..
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub$ cd react-form/
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub/react-form$ dapr run --app-id react-form --app-port 8080 npm run buildandstart
ℹ️ Starting Dapr with id react-form. HTTP Port: 44995. gRPC Port: 33951
…
It failed… seems it has an out of date package:
We can ctrl-c to get back
== APP == npm ERR! /home/builder/.npm/_logs/2021-03-26T13_16_26_258Z-debug.log
^Cℹ️
terminated signal received: shutting down
✅ Exited Dapr successfully
✅ Exited App successfully
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub/react-form$
To fix, i just added an update for browserslist in the package.json:
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub/react-form$ git diff package.json
diff --git a/pub-sub/react-form/package.json b/pub-sub/react-form/package.json
index 44a5515..71cf0d2 100644
--- a/pub-sub/react-form/package.json
+++ b/pub-sub/react-form/package.json
@@ -9,7 +9,7 @@
"server": "nodemon server.js",
"dev": "concurrently --kill-others-on-fail \"yarn server\" \"yarn client\"",
"start": "node server.js",
- "buildclient": "cd client && npm install && npm run build",
+ "buildclient": "cd client && npm install && npx browserslist@latest --update-db && npm run build",
"buildandstart": "npm run buildclient && npm install && npm run start"
},
"author": "Ryan Volum",
Now when we run:
$ dapr run --app-id react-form --app-port 8080 npm run buildandstart
ℹ️ Starting Dapr with id react-form. HTTP Port: 36867. gRPC Port: 46197
INFO[0000] starting Dapr Runtime -- version 1.0.1 -- commit 45bc40d app_id=react-form instance=DESKTOP-JBA79RT scope=dapr.runtime type=log ver=1.0.1
…
Now let’s put it all together…
Launch the Python one in a window, the Node subscriber in a window and then another window can use the dapr publish command to publish to a topic:
dapr publish --publish-app-id react-form --pubsub pubsub --topic A --data '{ "message": "This is a test" }'
And if we launch the react app, we can use that to also publish to topics:
This even worked for very large payloads
Testing in Kubernetes (Try #1, with issues)
First, like always, let's create a cluster for our work:
$ cat ~/create_cluster.sh
#!/bin/bash
set +x
sudo apt-get install -y jq
echo "az aks account set --subscription \"Pay-As-You-Go\""
#az account set --subscription "Visual Studio Enterprise Subscription"
az account set --subscription "Pay-As-You-Go"
echo "az group create --name idjaks$1rg --location centralus"
az group create --name idjaks$1rg --location centralus
echo "az ad sp create-for-rbac -n idjaks$1sp --skip-assignment --output json > my_sp.json"
az ad sp create-for-rbac -n idjaks$1sp --skip-assignment --output json > my_sp.json
echo "cat my_sp.json | jq -r .appId"
cat my_sp.json | jq -r .appId
export SP_PASS=`cat my_sp.json | jq -r .password`
export SP_ID=`cat my_sp.json | jq -r .appId`
sleep 10
echo "az aks create --resource-group idjaks$1rg --name idjaks$1 --location centralus --node-count 3 --enable-cluster-autoscaler --min-count 2 --max-count 4 --generate-ssh-keys --network-plugin azure --network-policy azure --service-principal $SP_ID --client-secret $SP_PASS"
az aks create --resource-group idjaks$1rg --name idjaks$1 --location centralus --node-count 3 --enable-cluster-autoscaler --min-count 2 --max-count 4 --generate-ssh-keys --network-plugin azure --network-policy azure --service-principal $SP_ID --client-secret $SP_PASS
az acr create -n idjacr$1cr -g idjaks$1rg --sku Basic --admin-enabled true
echo "=============== login ================"
set -x
sudo az aks install-cli
(rm -f ~/.kube/config || true) && az aks get-credentials -n idjaks$1 -g idjaks$1rg --admin
kubectl get nodes
kubectl get ns
When run, we can see the nodes up and running:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
aks-nodepool1-15452094-vmss000001 Ready agent 3h39m v1.18.14
aks-nodepool1-15452094-vmss000002 Ready agent 3h39m v1.18.14
We can now add the chart and see the active versions
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ helm repo add dapr https://dapr.github.io/helm-charts/
"dapr" has been added to your repositories
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "hashicorp" chart repository
...Successfully got an update from the "dapr" chart repository
...Successfully got an update from the "kedacore" chart repository
...Successfully got an update from the "datadog" chart repository
...Successfully got an update from the "azure-samples" chart repository
...Successfully got an update from the "nginx-stable" chart repository
...Successfully got an update from the "openfaas" chart repository
...Successfully got an update from the "datawire" chart repository
...Successfully got an update from the "jetstack" chart repository
...Successfully got an update from the "bitnami" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈Happy Helming!⎈
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ helm search repo dapr --devel --versions
NAME CHART VERSION APP VERSION DESCRIPTION
dapr/dapr 1.0.1 1.0.1 A Helm chart for Dapr on Kubernetes
dapr/dapr 1.0.0 1.0.0 A Helm chart for Dapr on Kubernetes
dapr/dapr 1.0.0-rc.4 1.0.0-rc.4 A Helm chart for Dapr on Kubernetes
dapr/dapr 1.0.0-rc.3 1.0.0-rc.3 A Helm chart for Dapr on Kubernetes
dapr/dapr 1.0.0-rc.2 1.0.0-rc.2 A Helm chart for Dapr on Kubernetes
dapr/dapr 1.0.0-rc.1 1.0.0-rc.1 A Helm chart for Dapr on Kubernetes
dapr/dapr 0.11.3 0.11.3 A Helm chart for Dapr on Kubernetes
dapr/dapr 0.11.2 0.11.2 A Helm chart for Dapr on Kubernetes
dapr/dapr 0.11.1 0.11.1 A Helm chart for Dapr on Kubernetes
dapr/dapr 0.11.0 0.11.0 A Helm chart for Dapr on Kubernetes
dapr/dapr 0.10.0 0.10.0 A Helm chart for Dapr on Kubernetes
dapr/dapr 0.4.3 0.9.0 A Helm chart for Dapr on Kubernetes
dapr/dapr 0.4.2 0.8.0 A Helm chart for Dapr on Kubernetes
Create a values file:
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ cat << EOF > values.yaml
> global.ha.enabled: true
> EOF
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ cat values.yaml
global.ha.enabled: true
Now we can just launch it:
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ helm install dapr dapr/dapr --version 1.0.1 --namespace dapr-system --create-namespace --values values.yaml --wait
NAME: dapr
LAST DEPLOYED: Wed Mar 24 16:26:12 2021
NAMESPACE: dapr-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Thank you for installing Dapr: High-performance, lightweight serverless runtime for cloud and edge
Your release is named dapr.
To get started with Dapr, we recommend using our quickstarts:
https://github.com/dapr/quickstarts
For more information on running Dapr, visit:
https://dapr.io
We can launch the Dapr dashboard with “dashboard -k”.. In fact, most of the commands for local vs kubernetes are just the difference of adding “-k”.
Sample app
Deploying a sample node app is pretty easy. Here we skip most of the steps of the Hello Kubernetes guide (https://github.com/dapr/quickstarts/tree/v1.0.0/hello-kubernetes) and just create a deployment yaml:
$ cat dapr-node.yaml
kind: Service
apiVersion: v1
metadata:
name: nodeapp
labels:
app: node
spec:
selector:
app: node
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodeapp
labels:
app: node
spec:
replicas: 1
selector:
matchLabels:
app: node
template:
metadata:
labels:
app: node
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "nodeapp"
dapr.io/app-port: "3000"
spec:
containers:
- name: node
image: dapriosamples/hello-k8s-node:latest
ports:
- containerPort: 3000
imagePullPolicy: Always
The Annotation takes care of the side car..
$ kubectl apply -f dapr-node.yaml -n dapr-system
service/nodeapp created
deployment.apps/nodeapp created
$ kubectl get svc -n dapr-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dapr-api ClusterIP 10.0.221.116 <none> 80/TCP 6m45s
dapr-dashboard ClusterIP 10.0.113.208 <none> 8080/TCP 6m45s
dapr-placement-server ClusterIP None <none> 50005/TCP,8201/TCP 6m45s
dapr-sentry ClusterIP 10.0.200.26 <none> 80/TCP 6m45s
dapr-sidecar-injector ClusterIP 10.0.9.59 <none> 443/TCP 6m45s
nodeapp LoadBalancer 10.0.4.23 13.86.7.154 80:31836/TCP 28s
nodeapp-dapr ClusterIP None <none> 80/TCP,50001/TCP,50002/TCP,9090/TCP 27s
We can also expose the Dapr dashboard here as well:
If you update the service.
$ kubectl get svc dapr-dashboard -n dapr-system -o yaml > dashboard.yaml
Change type: ClusterIP to Loadbalancer
$ cat dashboard.yaml | grep "type: Lo"
type: LoadBalancer
$ kubectl apply -f dashboard.yaml
Warning: resource services/dapr-dashboard is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
service/dapr-dashboard configured
Then we can get the IP
$ kubectl get svc -n dapr-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dapr-api ClusterIP 10.0.221.116 <none> 80/TCP 12m
dapr-dashboard LoadBalancer 10.0.113.208 52.154.153.91 8080:30056/TCP 12m
dapr-placement-server ClusterIP None <none> 50005/TCP,8201/TCP 12m
dapr-sentry ClusterIP 10.0.200.26 <none> 80/TCP 12m
dapr-sidecar-injector ClusterIP 10.0.9.59 <none> 443/TCP 12m
nodeapp LoadBalancer 10.0.4.23 13.86.7.154 80:31836/TCP 6m34s
nodeapp-dapr ClusterIP None <none> 80/TCP,50001/TCP,50002/TCP,9090/TCP 6m33s
And now access it:
We can click on the app to get details
Including logs
We glossed over it a bit, but you can see the dockerfile used for this:https://github.com/dapr/quickstarts/blob/v1.0.0/hello-kubernetes/node/app.js
I tested pushing an order and i see express parsed it, but failed to push into redis
Let’s add the redis yaml and try again
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ kubectl apply -f redis.yaml -n dapr-system
component.dapr.io/statestore created
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ cat redis.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
# These settings will work out of the box if you use `helm install
# bitnami/redis`. If you have your own setup, replace
# `redis-master:6379` with your own Redis master address, and the
# Redis password with your own Secret's name. For more information,
# see https://docs.dapr.io/operations/components/component-secrets .
- name: redisHost
value: redis-master:6379
- name: redisPassword
secretKeyRef:
name: redis
key: redis-password
auth:
secretStore: kubernetes
And installing Redis
$ helm install bitnami/redis -n dapr-system --generate-name
NAME: redis-1616622849
LAST DEPLOYED: Wed Mar 24 16:54:11 2021
NAMESPACE: dapr-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
** Please be patient while the chart is being deployed **
Redis(TM) can be accessed via port 6379 on the following DNS names from within your cluster:
redis-1616622849-master.dapr-system.svc.cluster.local for read/write operations
redis-1616622849-slave.dapr-system.svc.cluster.local for read-only operations
To get your password run:
export REDIS_PASSWORD=$(kubectl get secret --namespace dapr-system redis-1616622849 -o jsonpath="{.data.redis-password}" | base64 --decode)
To connect to your Redis(TM) server:
1. Run a Redis(TM) pod that you can use as a client:
kubectl run --namespace dapr-system redis-1616622849-client --rm --tty -i --restart='Never' \
--env REDIS_PASSWORD=$REDIS_PASSWORD \
--image docker.io/bitnami/redis:6.0.12-debian-10-r3 -- bash
2. Connect using the Redis(TM) CLI:
redis-cli -h redis-1616622849-master -a $REDIS_PASSWORD
redis-cli -h redis-1616622849-slave -a $REDIS_PASSWORD
To connect to your database from outside the cluster execute the following commands:
kubectl port-forward --namespace dapr-system svc/redis-1616622849-master 6379:6379 &
redis-cli -h 127.0.0.1 -p 6379 -a $REDIS_PASSWORD
In summary, I tried several combinations of helm values for redis and the statestore yaml, but the quickstart assumed a passwordless redis and it was not working out.
Kubernetes Try #2 (worked)
Creating a fresh cluster ($ ~/create_cluster.sh 17) I’ll first install Dapr.
To keep it simple, i’ll install Dapr into the default namespace
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ dapr init -k -n default
⌛ Making the jump to hyperspace...
ℹ️ Note: To install Dapr using Helm, see here: https://docs.dapr.io/getting-started/install-dapr-kubernetes/#install-with-helm-advanced
✅ Deploying the Dapr control plane to your cluster...
✅ Success! Dapr has been installed to namespace default. To verify, run `dapr status -k' in your terminal. To get started, go here: https://aka.ms/dapr-getting-started
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$
Next we add Redis
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" already exists with the same configuration, skipping
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "kedacore" chart repository
...Successfully got an update from the "hashicorp" chart repository
...Successfully got an update from the "azure-samples" chart repository
...Successfully got an update from the "dapr" chart repository
...Successfully got an update from the "datadog" chart repository
...Successfully got an update from the "nginx-stable" chart repository
...Successfully got an update from the "openfaas" chart repository
...Successfully got an update from the "datawire" chart repository
...Successfully got an update from the "jetstack" chart repository
...Successfully got an update from the "bitnami" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈Happy Helming!⎈
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ helm install redis bitnami/redis
NAME: redis
LAST DEPLOYED: Fri Mar 26 11:53:13 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
** Please be patient while the chart is being deployed **
Redis(TM) can be accessed via port 6379 on the following DNS names from within your cluster:
redis-master.default.svc.cluster.local for read/write operations
redis-slave.default.svc.cluster.local for read-only operations
To get your password run:
export REDIS_PASSWORD=$(kubectl get secret --namespace default redis -o jsonpath="{.data.redis-password}" | base64 --decode)
To connect to your Redis(TM) server:
1. Run a Redis(TM) pod that you can use as a client:
kubectl run --namespace default redis-client --rm --tty -i --restart='Never' \
--env REDIS_PASSWORD=$REDIS_PASSWORD \
--image docker.io/bitnami/redis:6.0.12-debian-10-r3 -- bash
2. Connect using the Redis(TM) CLI:
redis-cli -h redis-master -a $REDIS_PASSWORD
redis-cli -h redis-slave -a $REDIS_PASSWORD
To connect to your database from outside the cluster execute the following commands:
kubectl port-forward --namespace default svc/redis-master 6379:6379 &
redis-cli -h 127.0.0.1 -p 6379 -a $REDIS_PASSWORD
We can see the password was set in a k8s secret by redis:
builder@DESKTOP-JBA79RT:~/Workspaces/dapr$ kubectl get secrets redis -o yaml | grep redis-password
redis-password: eW9RRjdSUnd6ZQ==
f:redis-password: {}
$ echo eW9RRjdSUnd6ZQ== | base64 --decode
yoQF7RRwze
We will need this in the redis step that comes next:
$ cat redis.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: "redisHost"
value: "redis-master:6379"
- name: "redisPassword"
value: "yoQF7RRwze"
We can now apply the whole of pub-sub:
builder@DESKTOP-JBA79RT:~/Workspaces/quickstarts/pub-sub/deploy$ kubectl apply -f .
deployment.apps/node-subscriber created
deployment.apps/python-subscriber created
service/react-form created
deployment.apps/react-form created
component.dapr.io/pubsub created
We can see pods and services:
As well as access the react form through the public ingress
more details:
Summary
Dapr provides an interesting framework for common containerized problems. I only scratched the surface with some demos, however it was enough to see the value it could provide. I would certainly use it for the simple pub/sub problem many apps need to solve, however I’m not sure I would want to leverage its basic secrets management or observability (logs). I find AKV and Vault better for secrets and either Azure Logs or Datadog for logging easier to use.
The parts that were most interesting was dapr in docker only - the ability to build and test a pub/sub app in Node or Python locally without a cluster would be very handy for development.
I likely will circle back on this, especially to see how Dapr could be partnered with something like KNative or OpenFaaS for functions with pub/sub.