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 <mfussell@microsoft.com>
Co-authored-by: Young Bu Park <youngp@microsoft.com>
Co-authored-by: Pruthvidhar R Dhodda <60198385+pruthvidhodda@users.noreply.github.com>
This commit is contained in:
Joni Collinge 2020-06-09 22:07:36 +01:00 committed by GitHub
parent ae4c4e6281
commit 0ff4414177
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 360 additions and 0 deletions

2
10.hello-docker-compose/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.dockerfiles
*.override.*

View File

@ -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/)

View File

@ -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: ""

View File

@ -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: ""

View File

@ -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:

View File

@ -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

View File

@ -0,0 +1,2 @@
node_modules
.dockerfiles

View File

@ -0,0 +1,6 @@
FROM node:8-alpine
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD [ "node", "app.js" ]

View File

@ -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}!`));

View File

@ -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"
}
}

View File

@ -0,0 +1 @@
.dockerfiles

View File

@ -0,0 +1,6 @@
FROM python:3.7.1-alpine3.8
WORKDIR /app
COPY . .
RUN pip install requests
ENTRYPOINT ["python"]
CMD ["app.py"]

View File

@ -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)