diff --git a/www/htdocs/dockercoins-multi-node.png b/www/htdocs/dockercoins-multi-node.png
new file mode 100644
index 00000000..da8755ed
Binary files /dev/null and b/www/htdocs/dockercoins-multi-node.png differ
diff --git a/www/htdocs/dockercoins-single-node.png b/www/htdocs/dockercoins-single-node.png
new file mode 100644
index 00000000..4542b914
Binary files /dev/null and b/www/htdocs/dockercoins-single-node.png differ
diff --git a/www/htdocs/index.html b/www/htdocs/index.html
index 3f7e3e51..5fa61d96 100644
--- a/www/htdocs/index.html
+++ b/www/htdocs/index.html
@@ -4150,7 +4150,12 @@ Note: good guy ~~Stevedore~~ Docker will start without K/V
.exercise[
-- Log into `node1`
+- Make sure you're logged into `node1`,
+ with a clean environment:
+
+ ```
+ unset DOCKER_HOST
+ ```
- The first node must be started with the `-bootstrap` flag:
@@ -4160,24 +4165,24 @@ Note: good guy ~~Stevedore~~ Docker will start without K/V
jpetazzo/consul agent -server -bootstrap)
```
-- Find the internal IP address of that node
-
With This One Weird Trick:
-
- ```
- IPADDR=$(ip a ls dev eth0 |
- sed -n 's,.*inet \(.*\)/.*,\1,p')
- ```
-
]
---
## Starting the other Consul nodes
+- Other nodes have to be started with the `-join A.B.C.D`
+ option, where A.B.C.D is the address of an existing node
+
.exercise[
-- The other nodes have to be startd with the `-join IP.AD.DR.ESS` flag:
+- Find the internal IP address of our first node:
+ ```
+ IPADDR=$(ip a ls dev eth0 |
+ sed -n 's,.*inet \(.*\)/.*,\1,p')
+ ```
+- Start the other nodes:
```
for N in 2 3 4 5; do
ssh node$N docker run --name consul_node$N \
@@ -4186,15 +4191,15 @@ Note: good guy ~~Stevedore~~ Docker will start without K/V
done
```
-- With your browser, navigate to any instance on port 8500
-
(in "NODES" you should see the five nodes)
-
]
---
## Check that our Consul cluster is up
+- With your browser, navigate to any instance on port 8500
+
(in "NODES" you should see the five nodes)
+
- Let's run a couple of useful Consul commands
.exercise[
@@ -4313,7 +4318,7 @@ Note: good guy ~~Stevedore~~ Docker will start without K/V
- Check connectivity:
```
docker exec -ti turquoise ping -c1 navy
- docker exec -ti turquiose ping -c1 grass
+ docker exec -ti turquoise ping -c1 grass
```
(First works; second doesn't)
@@ -4336,7 +4341,7 @@ Note: good guy ~~Stevedore~~ Docker will start without K/V
- Check connectivity:
```
docker exec -ti turquoise ping -c1 navy
- docker exec -ti turquiose ping -c1 grass
+ docker exec -ti turquoise ping -c1 grass
```
(Both commands work now)
@@ -4376,7 +4381,7 @@ Note: good guy ~~Stevedore~~ Docker will start without K/V
- Check connectivity:
```
docker exec -ti turquoise ping -c1 navy
- docker exec -ti turquiose ping -c1 grass
+ docker exec -ti turquoise ping -c1 grass
```
(First command fails, second one works)
@@ -4445,11 +4450,78 @@ Let's examine the `docker-compose.yml` file.
---
-FIXME
+## Our first Compose v2 file
+
+```
+version: "2"
+
+services:
+ backend:
+ image: registry:2
+ frontend:
+ image: jpetazzo/hamba
+ command: 5000 backend:5000
+ ports:
+ - "127.0.0.1:5000:5000"
+ depends_on:
+ - backend
+```
+
+- *Backend* is the actual registry.
+- *Frontend* is the ambassador that we deployed earlier.
+
+It communicates with *backend* using an internal network
+and network aliases.
---
-## Converting from Compose file v1 to v2
+## Starting a local registry with Compose
+
+- We will bring up the registry
+
+- Then we will ensure that one *frontend* is running
+ on each node by scaling it to our number of nodes
+
+.exercise[
+
+- Make sure that `COMPOSE_FILE` is not set:
+ ```
+ unset COMPOSE_FILE
+ ```
+
+- Start the registry:
+ ```
+ docker-compose up -d
+ ```
+
+]
+
+---
+
+## "Scaling" the local registry
+
+- This is a particular kind of scaling
+
+- We just want to ensure that one *frontend*
+ is running on every single node of the cluster
+
+.exercise[
+
+- Scale the registry:
+ ```
+ N=1
+ while docker-compose scale frontend=$N; do
+ N=$((N+1))
+ done
+ ```
+
+]
+
+Note: Swarm might do that automatically for us in the future.
+
+---
+
+## Converting the Compose file for DockerCoins
- Services are no longer at the top level,
but under a `services` section
@@ -4503,44 +4575,358 @@ Copy-paste this into `docker-compose.yml`
---
-## Update our Compose file
+## Use images, not builds
- If we try to start the app like that, containers will only
- run on nodes with the appropriate images
+ run on nodes which have the images
-- We need to replace each `build:` section with an `image:` section
+- Like before: we need to replace `build` with `image`
- We can re-use the `build-tag-push.py` script for that
.exercise[
-XXX
-
-- Start the application
-
-- Observe that it's running on multiple nodes
+- Set `DOCKER_REGISTRY` to use our local registry,
+
then build, tag, and push the application:
+ ```
+ export DOCKER_REGISTRY=localhost:5000
+ ../bin/build-tag-push.py
+ ```
]
---
-## Going further
+## Run the application
-Adding load balancers (difficulty: easy)
+- At this point, our app is ready to run
-- Replace each service by:
+- We don't need ambassadors or extra containers
- - multiple copies of itself
+.exercise[
- - a load balancer
+- Start the application:
+ ```
+ docker-compose up -d
+ ```
-- This is what we did with `rng` earlier
+- Observe that it's running on multiple nodes:
+ ```
+ docker ps
+ ```
-- Traffic will follow suboptimal paths
+]
+
+Each container name is prefixed with the node it's running on.
---
-## Going further
+## View the performance graph
+
+- Load up the graph in the browser
+
+.exercise[
+
+- Check the `webui` service address and port:
+ ```
+ docker-compose port webui 80
+ ```
+
+- Open it in your browser
+
+]
+
+---
+
+# Load balancing with overlay networks
+
+- Scaling the `worker` service works out of the box
+ (like before)
+
+.exercise[
+
+- Scale `worker`:
+ ```
+ docker-compose scale worker=10
+ ```
+
+]
+
+We will hit the bottleneck caused by the `rng` service.
+
+How can we scale that service?
+
+---
+
+## The manual method
+
+- Replace `rng` with:
+
+ - multiple copies `rng1`, `rng2`, `rng3`, ...
+
+ - a load balancer taking over the name `rng`,
+
and spreading traffic accross all instances
+
+- You should have a sense of *déjà vu*
+
+- We did that in the beginning of the workshop
+
+- Can we do better?
+
+---
+
+## The scripted method
+
+- We could write a script to automate those steps
+
+--
+
+- *Can we do better?*
+
+--
+
+- In a perfect world, we would like to do:
+ ```
+ docker-compose scale rng=10
+ ```
+
+---
+
+## Naming problem
+
+- Service is called `rng`
+
+- It therefore takes the network name `rng`
+
+- Worker code connects to `rng`
+
+- So `rng` should point to the load balancer
+
+- What do‽
+
+---
+
+## Naming is *per-network*
+
+- Solution: put `rng` on its own network
+
+- That way, it doesn't take the network name `rng`
+
(at least not on the default network)
+
+- Have the load balancer sit on both networks
+
+- Add the name `rng` to the load balancer
+
+---
+
+class: pic
+
+## Original DockerCoins
+
+
+
+---
+
+class: pic
+
+## Load-balanced DockerCoins
+
+
+
+---
+
+## Declaring networks
+
+- Networks (other than the default one)
+ *must* be declared
+ in a top-level `networks` section
+
+.exercise[
+
+- Add the `rng` network to the Dockerfile:
+ ```
+ version: '2'
+
+ networks:
+ rng:
+
+ services:
+ rng:
+ image: ...
+ ...
+ ```
+
+]
+
+That section can be placed anywhere in the file.
+
+---
+
+## Putting the `rng` service in its network
+
+- Services can have a `networks` section
+
+- If they don't: they are placed in the default network
+
+- If they do: they are placed only in the mentioned networks
+
+.exercise[
+
+- Change the `rng` service to put it in its network:
+ ```
+ rng:
+ image: localhost:5000/dockercoins_rng:…
+ networks:
+ rng:
+ ```
+
+]
+
+---
+
+## Adding the load balancer
+
+- The load balancer has to be in both networks:
+
`rng` and `default`
+
+- In the `default` network, it must have the `rng` alias
+
+- We will use the `jpetazzo/hamba` image
+
+.exercise[
+
+- Add the `rng-lb` service to the Compose file:
+ ```
+ rng-lb:
+ image: jpetazzo/hamba
+ command: run
+ networks:
+ rng:
+ default:
+ aliases: [ rng ]
+ ```
+]
+
+---
+
+## Load balancer initial configuration
+
+- We specified `run` as the initial command
+
+- This tells `hamba` to wait for an initial configuration
+
+- The load balancer will not be operational
+
(until we feed it its configuration)
+
+---
+
+## Start the application
+
+.exercise[
+
+- Bring up DockerCoins:
+ ```
+ docker-compose up -d
+ ```
+
+- See that `worker` is complaining:
+ ```
+ docker-compose logs worker
+ ```
+]
+
+Note: the workers didn't need to be restarted.
+
+---
+
+## Configure the load balancer
+
+- Multiple solutions:
+
+ - lookup the IP address of the `rng` backend
+ - use the backend's network name
+ - use the backend's container name (easiest!)
+
+.exercise[
+
+- Configure the load balancer:
+ ```
+ docker run --rm \
+ --volumes-from dockercoins_rng-lb_1 \
+ --net container:dockercoins_rng-lb_1 \
+ jpetazzo/hamba reconfigure 80 dockercoins_rng_1 80
+ ```
+
+]
+
+The application should now be working correctly.
+
+---
+
+## Scale the application
+
+- Use `docker-compose scale` as planned
+
+.exercise[
+
+- Scale `rng`:
+ ```
+ docker-compose scale rng=10
+ ```
+
+]
+
+Of course, the graph doesn't change *yet*.
+
+We need to add the new backends to the load balancer
+configuration first.
+
+---
+
+## Reconfigure the load balancer
+
+- The command is similar to the one before
+
+- We need to pass the list of all backends
+
+.exercise[
+
+- Reconfigure the load balancer:
+ ```
+ docker run --rm \
+ --volumes-from dockercoins_rng-lb_1 \
+ --net container:dockercoins_rng-lb_1 \
+ jpetazzo/hamba reconfigure 80 \
+ $(for N in $(seq 1 10); do
+ echo dockercoins_rng_$N:80
+ done)
+ ```
+
+]
+
+---
+
+## Automating the process
+
+- Nobody loves artisan YAML handy craft
+
+- This can be automated very easily
+
+- To make things easier, we can use a label:
+
+ *each container behind a load balancer will
+ have a `loadbalancer` label giving the name
+ of that loadbalancer*
+
+- This is implemented by two scripts:
+
+ - add-load-balancer-v2.py
+
+ - reconfigure-load-balancers.py
+
+---
+
+# Going further
Deploying a new version (difficulty: easy)
@@ -4596,12 +4982,14 @@ Harder projects:
- Terminate containers and remove them:
```
- docker-compose kill
- docker-compose rm -f
+ docker-compose down
```
]
+Note: `docker-compose down` also deletes the
+networks that had been created for the application.
+
---
class: pic
@@ -4643,7 +5031,7 @@ class: pic
---
-## Storing files into Consul
+## Storing files in Consul
- We will use [Benjamin Wester's consulfs](
https://github.com/bwester/consulfs)
@@ -4780,6 +5168,8 @@ At this point, `ls -l ~/consul` should show `docker` and
]
+.icon[] Go back to node1 after this.
+
---
## A few words on this strategy
@@ -4788,6 +5178,16 @@ At this point, `ls -l ~/consul` should show `docker` and
(to be fair: anyone accessing Consul can wreck
serious havoc to your cluster anyway)
+- ConsulFS doesn't support *all* POSIX operations,
+
so a few things (like `mv`) will not work)
+
+- As a consequence, with Machine 0.6, you cannot
+ run `docker-machine create` directly on top of ConsulFS
+
+---
+
+## What if Consul becomes unavailable?
+
- If Consul becomes unavailable (e.g. loses quorum),
you won't be able to access your credentials
@@ -4797,6 +5197,7 @@ At this point, `ls -l ~/consul` should show `docker` and
- You can still access each Docker Engine over the
local UNIX socket (and repair Consul that way)
+
---
# Highly available Swarm managers