From 0ff44141770ad9865f255bb9a4b3fa3420eb7869 Mon Sep 17 00:00:00 2001 From: Joni Collinge Date: Tue, 9 Jun 2020 22:07:36 +0100 Subject: [PATCH] Docker Compose Sample (#211) * add docker-compose sample * add Makefile * add README * small tweaks to README * Update README.md * updated based on feedback * small updates and reword * Add --components-path to work with 0.8.0 changes * Change port of redis to 6380 as 6379 would mostly likely be already in use because of the redis container that dapr spins up during "dapr init" * Add --components-path to the list of options * Change host port instead of container port in README * Change host port instead of container port Co-authored-by: Mark Fussell Co-authored-by: Young Bu Park Co-authored-by: Pruthvidhar R Dhodda <60198385+pruthvidhodda@users.noreply.github.com> --- 10.hello-docker-compose/.gitignore | 2 + 10.hello-docker-compose/README.md | 133 ++++++++++++++++++ .../components/pubsub.yaml | 11 ++ .../components/statestore.yaml | 11 ++ 10.hello-docker-compose/docker-compose.yml | 69 +++++++++ 10.hello-docker-compose/makefile | 17 +++ 10.hello-docker-compose/node/.gitignore | 2 + 10.hello-docker-compose/node/Dockerfile | 6 + 10.hello-docker-compose/node/app.js | 63 +++++++++ 10.hello-docker-compose/node/package.json | 16 +++ 10.hello-docker-compose/python/.gitignore | 1 + 10.hello-docker-compose/python/Dockerfile | 6 + 10.hello-docker-compose/python/app.py | 23 +++ 13 files changed, 360 insertions(+) create mode 100644 10.hello-docker-compose/.gitignore create mode 100644 10.hello-docker-compose/README.md create mode 100644 10.hello-docker-compose/components/pubsub.yaml create mode 100644 10.hello-docker-compose/components/statestore.yaml create mode 100644 10.hello-docker-compose/docker-compose.yml create mode 100644 10.hello-docker-compose/makefile create mode 100644 10.hello-docker-compose/node/.gitignore create mode 100644 10.hello-docker-compose/node/Dockerfile create mode 100644 10.hello-docker-compose/node/app.js create mode 100644 10.hello-docker-compose/node/package.json create mode 100644 10.hello-docker-compose/python/.gitignore create mode 100644 10.hello-docker-compose/python/Dockerfile create mode 100644 10.hello-docker-compose/python/app.py diff --git a/10.hello-docker-compose/.gitignore b/10.hello-docker-compose/.gitignore new file mode 100644 index 00000000..17cdcc9b --- /dev/null +++ b/10.hello-docker-compose/.gitignore @@ -0,0 +1,2 @@ +.dockerfiles +*.override.* \ No newline at end of file diff --git a/10.hello-docker-compose/README.md b/10.hello-docker-compose/README.md new file mode 100644 index 00000000..adfab285 --- /dev/null +++ b/10.hello-docker-compose/README.md @@ -0,0 +1,133 @@ +# Dapr with Docker-Compose + +This sample demonstrates how to get Dapr running locally with Docker Compose. This uses the same applications as the `1.hello-world` sample, please review those docs for further information on the applicaiton architecture. + +## Prerequisites +Clone this repo using `git clone https://github.com/dapr/samples.git` and go to the directory named */10.hello-docker-compose* + +- [Docker](https://docs.docker.com/) +- [Docker Compose](https://docs.docker.com/compose/install/) + +## Reviewing the Docker Compose Definition + +Review the Docker Compose file *./docker-compose.yml* below: + +```yaml +version: '3' +services: + ############################ + # Node app + Dapr sidecar + ############################ + nodeapp: + build: ./node + ports: + - "50002:50002" + depends_on: + - redis + - placement + networks: + - hello-dapr + nodeapp-dapr: + image: "daprio/daprd:edge" + command: ["./daprd", + "-app-id", "nodeapp", + "-app-port", "3000", + "-placement-address", "placement:50006", + "-dapr-grpc-port", "50002", + "-components-path", "/components"] + volumes: + - "./components/:/components" + depends_on: + - nodeapp + network_mode: "service:nodeapp" + ############################ + # Python app + Dapr sidecar + ############################ + pythonapp: + build: ./python + depends_on: + - redis + - placement + networks: + - hello-dapr + pythonapp-dapr: + image: "daprio/daprd:edge" + command: ["./daprd", + "-app-id", "pythonapp", + "-placement-address", "placement:50006", + "-components-path", "/components"] + volumes: + - "./components/:/components" + depends_on: + - pythonapp + network_mode: "service:pythonapp" + ############################ + # Dapr placement service + ############################ + placement: + image: "daprio/dapr" + command: ["./placement", "-port", "50006"] + ports: + - "50006:50006" + networks: + - hello-dapr + ############################ + # Redis state store + ############################ + redis: + image: "redis:alpine" + ports: + - "6380:6379" + networks: + - hello-dapr +networks: + hello-dapr: +``` + +### Services +This Docker Compose defintion has the following containerized services: +- `nodeapp` // The node app +- `nodeapp-dapr` // The node app Dapr sidecar +- `pythonapp` // The python app +- `pythonapp-dapr` // The python app Dapr sidecar +- `placement` // Dapr's placement service +- `redis` // Redis + +### Networking +Each of these services is deployed to the `hello-dapr` Docker network and have their own IP on that network. +The `nodeapp-dapr` and `pythonapp-dapr` services are sharing a network namespace with their associated app service by using [`network_mode`](https://docs.docker.com/compose/compose-file/#network_mode). +This means that the app and the sidecars are able to communicate over their localhost interface. + +> Ports are still bound on the host machine, therefore, we need to ensure we avoid port conflicts. + +### Volumes +In order to get Dapr to load the redis statestore and pubsub components, you need to mount the +`./components` directory to the default working directory. These component definitions have been modified +to talk to redis using a DNS name `redis` rather than localhost. This resolves on the Docker network to +the IP of the container running redis. + +## Deploy the Docker Compose Definition +To deploy the above `docker-compose.yml` you can run: +``` +make run +``` +> If you want to change the Dapr Docker image used in the deployment, you can + set the env var `DAPR_IMAGE` and run `make run`. This generates + a `docker-compose.override.yml` file using your custom image. If you want + to revert to the default Dapr Docker image, you'll need to remove this file. + +altentiavely, you can just use Docker Compose directly: +``` +docker-compose up +``` + +## Clean up + +To tear down the Docker Compose deployment, you can run: +``` +docker-compose down +``` + +## Additional Resources: + +[Overview of Docker Compose](https://docs.docker.com/compose/) diff --git a/10.hello-docker-compose/components/pubsub.yaml b/10.hello-docker-compose/components/pubsub.yaml new file mode 100644 index 00000000..58ea866b --- /dev/null +++ b/10.hello-docker-compose/components/pubsub.yaml @@ -0,0 +1,11 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: pubsub +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis:6379 + - name: redisPassword + value: "" \ No newline at end of file diff --git a/10.hello-docker-compose/components/statestore.yaml b/10.hello-docker-compose/components/statestore.yaml new file mode 100644 index 00000000..29351060 --- /dev/null +++ b/10.hello-docker-compose/components/statestore.yaml @@ -0,0 +1,11 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore +spec: + type: state.redis + metadata: + - name: redisHost + value: redis:6379 + - name: redisPassword + value: "" \ No newline at end of file diff --git a/10.hello-docker-compose/docker-compose.yml b/10.hello-docker-compose/docker-compose.yml new file mode 100644 index 00000000..2adb90d7 --- /dev/null +++ b/10.hello-docker-compose/docker-compose.yml @@ -0,0 +1,69 @@ +version: '3' +services: + ############################ + # Node app + Dapr sidecar + ############################ + nodeapp: + build: ./node + ports: + - "50002:50002" + depends_on: + - redis + - placement + networks: + - hello-dapr + nodeapp-dapr: + image: "daprio/daprd:edge" + command: ["./daprd", + "-app-id", "nodeapp", + "-app-port", "3000", + "-placement-address", "placement:50006", + "-dapr-grpc-port", "50002", + "-components-path", "/components"] + volumes: + - "./components/:/components" + depends_on: + - nodeapp + network_mode: "service:nodeapp" + ############################ + # Python app + Dapr sidecar + ############################ + pythonapp: + build: ./python + depends_on: + - redis + - placement + networks: + - hello-dapr + pythonapp-dapr: + image: "daprio/daprd:edge" + command: ["./daprd", + "-app-id", "pythonapp", + "-placement-address", "placement:50006", + "-components-path", "/components"] + volumes: + - "./components/:/components" + depends_on: + - pythonapp + network_mode: "service:pythonapp" + ############################ + # Dapr placement service + ############################ + placement: + image: "daprio/dapr" + command: ["./placement", "-port", "50006"] + ports: + - "50006:50006" + networks: + - hello-dapr + ############################ + # Redis state store + ############################ + redis: + image: "redis:alpine" + ports: + - "6380:6379" + networks: + - hello-dapr +networks: + hello-dapr: diff --git a/10.hello-docker-compose/makefile b/10.hello-docker-compose/makefile new file mode 100644 index 00000000..642af4f7 --- /dev/null +++ b/10.hello-docker-compose/makefile @@ -0,0 +1,17 @@ +DOCKER_COMPOSE:=docker-compose +DOCKER_COMPOSE_FILE:=docker-compose.yml +DOCKER_COMPOSE_OVERRIDE_FILE:=docker-compose.override.yml + +DAPR_IMAGE:=${DAPR_IMAGE} + +.PHONY: build +build: + $(DOCKER_COMPOSE) build + +.PHONY: run +run: +ifneq ($(DAPR_IMAGE),) + cp $(DOCKER_COMPOSE_FILE) $(DOCKER_COMPOSE_OVERRIDE_FILE) + sed -i 's,daprio\/daprd:edge,$(DAPR_IMAGE),g' $(DOCKER_COMPOSE_OVERRIDE_FILE) +endif + $(DOCKER_COMPOSE) up \ No newline at end of file diff --git a/10.hello-docker-compose/node/.gitignore b/10.hello-docker-compose/node/.gitignore new file mode 100644 index 00000000..cfe9975c --- /dev/null +++ b/10.hello-docker-compose/node/.gitignore @@ -0,0 +1,2 @@ +node_modules +.dockerfiles diff --git a/10.hello-docker-compose/node/Dockerfile b/10.hello-docker-compose/node/Dockerfile new file mode 100644 index 00000000..fc5ffc7c --- /dev/null +++ b/10.hello-docker-compose/node/Dockerfile @@ -0,0 +1,6 @@ +FROM node:8-alpine +WORKDIR /app +COPY . . +RUN npm install +EXPOSE 3000 +CMD [ "node", "app.js" ] diff --git a/10.hello-docker-compose/node/app.js b/10.hello-docker-compose/node/app.js new file mode 100644 index 00000000..adfbcf9e --- /dev/null +++ b/10.hello-docker-compose/node/app.js @@ -0,0 +1,63 @@ +// ------------------------------------------------------------ +// 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 stateStoreName = `statestore`; +const stateUrl = `http://localhost:${daprPort}/v1.0/state/${stateStoreName}`; +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}); + }); +}); + +app.listen(port, () => console.log(`Node App listening on port ${port}!`)); \ No newline at end of file diff --git a/10.hello-docker-compose/node/package.json b/10.hello-docker-compose/node/package.json new file mode 100644 index 00000000..f6f296d3 --- /dev/null +++ b/10.hello-docker-compose/node/package.json @@ -0,0 +1,16 @@ +{ + "name": "node_server", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.18.3", + "express": "^4.16.4", + "isomorphic-fetch": "^2.2.1" + } +} diff --git a/10.hello-docker-compose/python/.gitignore b/10.hello-docker-compose/python/.gitignore new file mode 100644 index 00000000..96a4f9a5 --- /dev/null +++ b/10.hello-docker-compose/python/.gitignore @@ -0,0 +1 @@ +.dockerfiles diff --git a/10.hello-docker-compose/python/Dockerfile b/10.hello-docker-compose/python/Dockerfile new file mode 100644 index 00000000..f1514979 --- /dev/null +++ b/10.hello-docker-compose/python/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.7.1-alpine3.8 +WORKDIR /app +COPY . . +RUN pip install requests +ENTRYPOINT ["python"] +CMD ["app.py"] diff --git a/10.hello-docker-compose/python/app.py b/10.hello-docker-compose/python/app.py new file mode 100644 index 00000000..a36e23b3 --- /dev/null +++ b/10.hello-docker-compose/python/app.py @@ -0,0 +1,23 @@ +# ------------------------------------------------------------ +# 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)