上一篇Dapr的運行時環境已經安裝完成。今天我們在Standalone模式部署第一個Dapr程序。
程序來自Dapr官方的quickstarts教程里的Hello World,我們用目前的最新版本v1.0.0-rc.2。
$ git clone -b v1.0.0-rc.2 https://github.com/dapr/quickstarts.git
$ cd quickstarts
hello-world下面是Standalone版的,進去看看。
程序很簡單,一個Node的程序,提供3個Endpoint(在StateStore里保存,取得,刪除發過來的OrderId)。
還有一個Python的程序,每秒發1個HTTP Request給Node程序。
下面開始部署。Standalone模式下首先要先安裝Node和Python的運行環境。不然dapr run的時候會報錯。
Node的運行環境:
$ sudo apt update
$ sudo apt install nodejs
$ sudo apt install npm
$ sudo npm install
$ npm list
里面有express和body-parser的話就可以用Dapr啟動node的程序了。
$ dapr run --app-id nodeapp --app-port 3000 --dapr-http-port 3500 node app.js
ℹ️ Starting Dapr with id nodeapp. HTTP Port: 3500. gRPC Port: 43509
INFO[0000] starting Dapr Runtime -- version 1.0.0-rc.2 -- commit 196483d app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] log level set to: info app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] metrics server started on :37057/ app_id=nodeapp instance=PC57-064 scope=dapr.metrics type=log ver=1.0.0-rc.2
INFO[0000] standalone mode configured app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] app id: nodeapp app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] mTLS is disabled. Skipping certificate request and tls validation app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] local service entry announced: nodeapp -> 172.17.183.23:44399 app_id=nodeapp instance=PC57-064 scope=dapr.contrib type=log ver=1.0.0-rc.2
INFO[0000] Initialized name resolution to standalone app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] component loaded. name: pubsub, type: pubsub.redis app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] waiting for all outstanding components to be processed app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] component loaded. name: statestore, type: state.redis app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] all outstanding components processed app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] enabled gRPC tracing middleware app_id=nodeapp instance=PC57-064 scope=dapr.runtime.grpc.api type=log ver=1.0.0-rc.2
INFO[0000] enabled gRPC metrics middleware app_id=nodeapp instance=PC57-064 scope=dapr.runtime.grpc.api type=log ver=1.0.0-rc.2
INFO[0000] API gRPC server is running on port 43509 app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] enabled metrics http middleware app_id=nodeapp instance=PC57-064 scope=dapr.runtime.http type=log ver=1.0.0-rc.2
INFO[0000] enabled tracing http middleware app_id=nodeapp instance=PC57-064 scope=dapr.runtime.http type=log ver=1.0.0-rc.2
INFO[0000] http server is running on port 3500 app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] enabled gRPC tracing middleware app_id=nodeapp instance=PC57-064 scope=dapr.runtime.grpc.internal type=log ver=1.0.0-rc.2
INFO[0000] enabled gRPC metrics middleware app_id=nodeapp instance=PC57-064 scope=dapr.runtime.grpc.internal type=log ver=1.0.0-rc.2
INFO[0000] internal gRPC server is running on port 44399 app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] application host: 127.0.0.1. application protocol: http. waiting on port 3000. This will block until the app is listening on that port. app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
== APP == Node App listening on port 3000!
INFO[0000] application discovered on port 3000 app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] application configuration loaded app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s app_id=nodeapp instance=PC57-064 scope=dapr.runtime.actor type=log ver=1.0.0-rc.2
INFO[0000] dapr initialized. Status: Running. Init Elapsed 418.37829999999997ms app_id=nodeapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] placement tables updated, version: 0 app_id=nodeapp instance=PC57-064 scope=dapr.runtime.actor.internal.placement type=log ver=1.0.0-rc.2
ℹ️ Updating metadata for app command: node app.js
✅ You're up and running! Both Dapr and your app logs will appear here.
log顯示Node程序已經跑起來了。APP自己的HTTP Endpoint端口是3000,Dapr(Sidecar)的HTTP端口是3500。
現在再開另一個窗口,用Dapr CLI執行:
$ dapr invoke --app-id nodeapp --method neworder --data '{"data": { "orderId": "42" } }'
✅ App invoked successfully
成功了。再看看原來的窗口,多了下面兩條:
== APP == Got a new order! Order ID: 42
== APP == Successfully persisted state.
顯示持久化成功。
用Node程序的GET API確認一下:
$ curl http://localhost:3000/order
{"orderId":42}
用Dapr的API確認一下:
$ curl http://localhost:3500/v1.0/invoke/nodeapp/method/order
{"orderId":"42"}
我們看看數據存在了哪里。用docker ps看看redis跑在哪個Container里:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a51c086c8bbe daprio/dapr "./placement" 31 minutes ago Up 31 minutes 0.0.0.0:50005->50005/tcp dapr_placement
fdca40421094 redis "docker-entrypoint.s…" 31 minutes ago Up 31 minutes 0.0.0.0:6379->6379/tcp dapr_redis
9dc60b00db4d openzipkin/zipkin "start-zipkin" 31 minutes ago Up 31 minutes (healthy) 9410/tcp, 0.0.0.0:9411->9411/tcp dapr_zipkin
Container名是dapr_redis。進去看看:
$ docker exec -it dapr_redis redis-cli
127.0.0.1:6379>
看看有什么Key:
127.0.0.1:6379> keys *
1) "nodeapp||order"
127.0.0.1:6379> type nodeapp||order
hash
127.0.0.1:6379> hgetall nodeapp||order
1) "data"
2) "{\"orderId\":\"42\"}"
3) "version"
4) "3"
OK,剛才的orderId(42)找到了。
除了用Dapr CLI保存OrderId,我們還可以用Dapr的API。
$ curl -XPOST -d @sample.json -H "Content-Type:application/json" http://localhost:3500/v1.0/invoke/nodeapp/method/neworder
其中sample.json的內容如下。調用Dapr的invoke API可以起到Dapr CLI(dapr invoke)一樣的效果。
{"data":{"orderId":"42"}}
當然我們也可以用Node程序自己的Endpoint,這樣不通過Dapr Sidecar。
$ curl -XPOST -d @sample.json -H "Content-Type:application/json" http://localhost:3000/neworder
你也許會問,為什么Redis直接就能用了呢?答案就是Dapr Runtime安裝的時候,自動就跑起來了Redis的服務用的Docker Container(配置文件在~/.dapr/components/下面的statestore.yaml和pubsub.yaml),所以基於Redis的statestore和pubsub是開箱即用的。
OK。接下來部署Python的程序。Python也得先裝運行環境:
$ sudo apt install python3-pip
用Dapr CLI啟動Python程序:
$ dapr run --app-id pythonapp --dapr-http-port 3501 python3 app.py
ℹ️ Starting Dapr with id pythonapp. HTTP Port: 3501. gRPC Port: 34093
ℹ️ Checking if Dapr sidecar is listening on HTTP port 3501
INFO[0000] starting Dapr Runtime -- version 1.0.0-rc.2 -- commit 196483d app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] log level set to: info app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] metrics server started on :37435/ app_id=pythonapp instance=PC57-064 scope=dapr.metrics type=log ver=1.0.0-rc.2
INFO[0000] standalone mode configured app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] app id: pythonapp app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] mTLS is disabled. Skipping certificate request and tls validation app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] local service entry announced: pythonapp -> 172.17.183.23:44129 app_id=pythonapp instance=PC57-064 scope=dapr.contrib type=log ver=1.0.0-rc.2
INFO[0000] Initialized name resolution to standalone app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] component loaded. name: pubsub, type: pubsub.redis app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] waiting for all outstanding components to be processed app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] component loaded. name: statestore, type: state.redis app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] all outstanding components processed app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] enabled gRPC tracing middleware app_id=pythonapp instance=PC57-064 scope=dapr.runtime.grpc.api type=log ver=1.0.0-rc.2
INFO[0000] enabled gRPC metrics middleware app_id=pythonapp instance=PC57-064 scope=dapr.runtime.grpc.api type=log ver=1.0.0-rc.2
INFO[0000] API gRPC server is running on port 34093 app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] enabled metrics http middleware app_id=pythonapp instance=PC57-064 scope=dapr.runtime.http type=log ver=1.0.0-rc.2
INFO[0000] enabled tracing http middleware app_id=pythonapp instance=PC57-064 scope=dapr.runtime.http type=log ver=1.0.0-rc.2
INFO[0000] http server is running on port 3501 app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] enabled gRPC tracing middleware app_id=pythonapp instance=PC57-064 scope=dapr.runtime.grpc.internal type=log ver=1.0.0-rc.2
INFO[0000] enabled gRPC metrics middleware app_id=pythonapp instance=PC57-064 scope=dapr.runtime.grpc.internal type=log ver=1.0.0-rc.2
INFO[0000] internal gRPC server is running on port 44129 app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s app_id=pythonapp instance=PC57-064 scope=dapr.runtime.actor type=log ver=1.0.0-rc.2
WARN[0000] failed to read from bindings: app channel not initialized app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] dapr initialized. Status: Running. Init Elapsed 4.716900000000001ms app_id=pythonapp instance=PC57-064 scope=dapr.runtime type=log ver=1.0.0-rc.2
INFO[0000] placement tables updated, version: 0 app_id=pythonapp instance=PC57-064 scope=dapr.runtime.actor.internal.placement type=log ver=1.0.0-rc.2
ℹ️ Checking if Dapr sidecar is listening on GRPC port 34093
ℹ️ Dapr sidecar is up and running.
ℹ️ Updating metadata for app command: python3 app.py
✅ You're up and running! Both Dapr and your app logs will appear here.
啟動成功了。因為Python自己不提供服務,所以--app-port不用指定。--dapr-http-port是3501,這是自己的Sidecar用的端口,不能跟別人的重了。再看看剛才Node的窗口,不停的有新的Request過來,就是Python程序來的每隔一秒的Request。
最后看一下dapr list的結果:
$ dapr list
APP ID HTTP PORT GRPC PORT APP PORT COMMAND AGE CREATED PID
nodeapp 3500 35485 3000 node app.js 41m 2020-12-27 00:54.54 18395
pythonapp 40175 33349 0 python3 app.py 1m 2020-12-27 01:36.27 31185
要結束這兩個APP,在各自窗口里Ctrl+c就可以。如果在別的窗口,可以用Dapr CLI的命令:
$ dapr stop --app-id nodeapp
$ dapr stop --app-id pythonapp
至此在Standalone模式下的第一個Dapr程序部署成功!下一篇講講在Kubernetes模式下部署的方法。