Files
container.training/www/htdocs/index.html
Jerome Petazzoni f49835c3d5 RC1
2015-06-08 21:51:17 -07:00

1557 lines
27 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<head>
<base target="_blank">
<title>Docker Orchestration Workshop</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style type="text/css">
@import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz);
@import url(https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic);
@import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700,400italic);
body { font-family: 'Droid Serif'; font-size: 150%; }
h1, h2, h3 {
font-family: 'Yanone Kaffeesatz';
font-weight: normal;
}
a {
text-decoration: none;
color: blue;
}
.remark-code, .remark-inline-code { font-family: 'Ubuntu Mono'; }
.red { color: #fa0000; }
.gray { color: #ccc; }
.small { font-size: 70%; }
.big { font-size: 140%; }
.underline { text-decoration: underline; }
.footnote {
position: absolute;
bottom: 3em;
}
.pic {
vertical-align: middle;
text-align: center;
padding: 0 0 0 0 !important;
}
img {
max-width: 100%;
max-height: 450px;
}
.title {
vertical-align: middle;
text-align: center;
}
.title {
font-size: 2em;
}
.title .remark-slide-number {
font-size: 0.5em;
}
.quote {
background: #eee;
border-left: 10px solid #ccc;
margin: 1.5em 10px;
padding: 0.5em 10px;
quotes: "\201C""\201D""\2018""\2019";
font-style: italic;
}
.quote:before {
color: #ccc;
content: open-quote;
font-size: 4em;
line-height: 0.1em;
margin-right: 0.25em;
vertical-align: -0.4em;
}
.quote p {
display: inline;
}
.icon img {
height: 1em;
}
.exercise {
background-color: #eee;
background-image: url("keyboard.png");
background-size: 1.4em;
background-repeat: no-repeat;
background-position: 0.2em 0.2em;
border: 2px dotted black;
}
.exercise::before {
content: "Exercise:";
margin-left: 1.8em;
}
li p { line-height: 1.25em; }
</style>
</head>
<body>
<textarea id="source">
class: title
# Docker <br/> Orchestration <br/> Workshop
---
<!-- grep '^# ' index.html | grep -v '<br' | tr '#' '-'^C -->
## Outline (1/2)
- Pre-requirements
- VM environment
- Our sample application
- Running the whole app on a single node
- Finding bottlenecks
- Scaling workers on a single node
- Scaling HTTP on a single node
- Connecting to containers on other hosts
- Abstracting connection details
---
## Outline (2/2)
- Backups
- Logs
- Security upgrades
- Network traffic analysis
- Introducing Swarm
- Setting up our Swarm cluster
- Running on Swarm
- Network plumbing on Swarm
- Last words
---
# Pre-requirements
- Computer with network connection and SSH client
<br/>(on Windows, get [putty](http://www.putty.org/))
- GitHub account
- Docker Hub account
- Basic Docker knowledge
.exercise[
- This is the stuff you're supposed to do!
- Create [GitHub](https://github.com/) and
[Docker Hub](https://hub.docker.com) accounts now if needed
- Go to [view.dckr.info](http://view.dckr.info) to view those slides
]
---
# VM environment
- Each person gets 5 VMs
- They are *your* VMs
- They'll be up until tomorrow
- You have a little card with login+password+IP addresses
- You can automatically SSH from one VM to another
.exercise[
- Log into the first VM
- Check that you can SSH (without password) to `node2`
- Check the version of docker with `docker version`
]
Note: from now on, unless instructed, all commands have
to be done from the first VM, `node1`.
---
## Versions
- Docker 1.6 (1.7 will be released in a few days)
- Compose 0.3 RC
- Swarm 0.3 RC
---
# Our sample application
- Let's look at the general layout of the
[source code](https://github.com/jpetazzo/orchestration-workshop)
- Each directory = 1 microservice
- `rng` = web service generating random bytes
- `hasher` = web service computing hash of POSTed data
- `worker` = background process using `rng` and `hasher`
- `webui` = web interface to watch progress
.exercise[
- Fork the repository on GitHub
- Clone your fork on `node1`
]
---
## What's this application?
- It is a DockerCoin miner! 💰🐳📦🚢
- No, you can't buy coffee with DockerCoins
- How DockerCoins works:
- `worker` asks to `rng` to give it random bytes
- `worker` feeds those random bytes into `hasher`
- each hash starting with `0` is a DockerCoin
- DockerCoins are stored in `redis`
- you can see the progress with the `webui`
Next: we will inspect components independently.
---
## Running components independently
.exercise[
- Go to the `dockercoins` directory (in the cloned repo)
- Run `docker-compose up rng`
<br/>(Docker will pull `python` and build the microservice)
]
.icon[![Warning](warning.png)] The container log says
`Running on http://0.0.0.0:80/`
<br/>but that is port 80 *in the container*.
On the host it is 8001.
This is mapped in The `docker-compose.yml` file:
```
rng:
ports:
- "8001:80"
```
---
## Getting random bytes of data
.exercise[
- Open a second terminal and connect to the same VM
- Check that the service is alive:
<br/>`curl localhost:8001`
- Get 10 bytes of random data:
<br/>`curl localhost:8001/10`
<br/>(the output might confuse your terminal, since this is binary data)
- Test the performance on one big request:
<br/>`curl -o/dev/null localhost:8001/10000000`
<br/>(should take ~1s, and show speed of ~10 MB/s)
]
Next: we'll see how it behaves with many small requests.
---
## Concurrent requests
.exercise[
- Test 100 requests of 1000 bytes each:
<br/>`ab -n 100 localhost:8001/1000`
- Test 100 requests, 10 requests in parallel:
<br/>`ab -n 100 -c 10 localhost:8001/1000`
<br/>(look how the latency has increased!)
- Try with 100 requests in parallel:
<br/>`ab -n 100 -c 100 localhost:8001/1000`
]
Take note of the number of requests/s.
---
## Save some random data and stop the generator
Before testing the hasher, let's save some random
data that we will feed to the hasher later.
.exercise[
- Run `curl localhost:8001/1000000 > /tmp/random`
]
Now we can stop the generator.
.exercise[
- In the shell where you did `docker-compose up rng`,
<br/>stop it by hitting `^C`
]
---
## Running the hasher
.exercise[
- Run `docker-compose up hasher`
<br/>(it will pull `ruby` and do the build)
]
.icon[![Warning](warning.png)] Again, pay attention to the port mapping!
The container log says that it's listening on port 80,
but it's mapped to port 8002 on the host.
You can see the mapping in `docker-compose.yml`.
---
## Testing the hasher
.exercise[
- Run `curl localhost:8002`
<br/>(it will say it's alive)
- Posting binary data requires some extra flags:
```
curl \
-H "Content-type: application/octet-stream" \
--data-binary @/tmp/random \
localhost:8002
```
- Compute the hash locally to verify that it works fine:
<br/>`sha256sum /tmp/random`
<br/>(it should display the same hash)
]
---
## Benchmarking the hasher
The invocation of `ab` will be slightly more complex as well.
.exercise[
- Execute 100 requests in a row:
```
ab -n 100 -T application/octet-stream \
-p /tmp/random localhost:8002/
```
- Execute 100 requests with 10 requests in parallel:
```
ab -c 10 -n 100 -T application/octet-stream \
-p /tmp/random localhost:8002/
```
]
Take note of the performance numbers (requests/s).
---
## Benchmarking the hasher on smaller data
Here we hashed 1 meg.
Later we will hash much smaller payloads.
Let's repeat the tests with smaller data.
.exercise[
- Run `truncate --size=10 /tmp/random`
- Repeat the `ab` tests
]
---
## Why do `rng` and `hasher` behave differently?
![Equations on a blackboard](equations.png)
---
# Running the whole app on a single node
.exercise[
- Run `docker-compose up` to start all components
]
- Aggregate output is shown
- Output is verbose because the worker is constantly hitting other services
- Now let's use the little web UI to see realtime progress
.exercise[
- Open http://[yourVMaddr]:8000/ (from a browser)
]
---
## Running in the background
- The logs are very verbose (and won't get better)
- Let's put them in the background for now!
.exercise[
- Stop the app (with `^C`)
- Start it again with `docker-compose up -d`
- Check on the web UI that the app is still making progress
]
---
# Finding bottlenecks
- Let's look at CPU, memory, and I/O usage
.exercise[
- run `top` to see CPU and memory usage
<br/>(you should see idle cycles)
- run `vmstat 3` to see I/O usage (si/so/bi/bo)
<br/>(the 4 numbers should be almost zero,
<br/>except `bo` for logging)
]
We have available resources.
- Why?
- How can we use them?
---
## Measuring performance
- The code doesn't have instrumentation
- Let's use `ab` and `httping` to view latency of microservices
.exercise[
- Start two new SSH connections
- In the first one, let run `httping localhost:8001`
- In the other one, let run `httping localhost:8002`
]
---
# Scaling workers on a single node
- Docker Compose supports scaling
- It doesn't deal with load balancing
- For services that *do not* accept connections, that's OK
- Let's scale `worker` and see what happens!
.exercise[
- In one SSH session, run `docker-compose logs worker`
- In another, run `docker-compose scale worker=4`
- See the impact on CPU load (with top/htop),
<br/>and on compute speed (with web UI)
]
---
# Scaling HTTP on a single node
The plan:
- Scale `rng` to multiple containers
- Put a load balancer in front of it
- Point other services to the load balancer
Note: Compose does not support that kind of scaling yet.
<br/>We will have to do it manually for now.
---
## Scaling `rng`
.exercise[
- Replace the `rng` service with multiple copies of it:
```
rng1:
build: rng
rng2:
build: rng
rng3:
build: rng
```
]
That's all!
---
## Introduction to `jpetazzo/hamba`
- Public image on the Docker Hub
- Load balancer based on HAProxy
- Expects the following arguments:
<br/>`FE-port BE1-addr BE1-port BE2-addr BE2-port ...`
<br/>*or*
<br/>`FE-addr:FE-port BE1-addr BE1-port BE2-addr BE2-port ...`
- FE=frontend (the thing other services connect to)
- BE=backend (the multiple copies of your scaled service)
.small[
Example: listen to port 80 and balance traffic on www1:1234 + www2:2345
```
docker run -d -p 80 jpetazzo/hamba 80 www1 1234 www2 2345
```
]
---
## Add our load balancer to the Compose file
.exercise[
- Add the following section to the Compose file:
```
rng0:
image: jpetazzo/hamba
links:
- rng1
- rng2
- rng3
command: 80 rng1 80 rng2 80 rng3 80
ports:
- "8001:80"
```
]
---
## Point other services to the load balancer
- The only affected service is `worker`
- We have to replace the `rng` link with a link to `rng0`,
but it should still be named `rng` (so we don't change the code)
.exercise[
- Update the `worker` section as follows:
```
worker:
build: worker
links:
- rng0:rng
- hasher
- redis
```
]
---
## Start the whole stack
- The new `rng0` load balancer also ties up port 8001
- We have to stop the old `rng` service first
<br/>(Compose doesn't do it for us)
.exercise[
- Run `docker-compose stop rng`
]
- Now (re-)start the whole stack
.exercise[
- Run `docker-compose up -d`
- Check worker logs with `docker-compose logs worker`
- Check load balancer logs with `docker-compose logs rng0`
]
---
## The good, the bad, the ugly
- The good
We scaled a service, added a load balancer -
<br/>without changing a single line of code
- The bad
We manually copy-pasted sections in `docker-compose.yml`
- The ugly
If we scale up/down, we have to restart everything
---
## Ideas to improve the situation
- Parse `docker-compose.yml` to automatically replace
services with their scaled counterparts
- Replace Docker Links with network namespace sharing
- More on this later
---
# Connecting to containers on other hosts
- We want to scale across multiple nodes
- We will deploy the same stack multiple times
- But we want every stack to use the same Redis
<br/>(in other words: Redis is our only *stateful* service here)
---
## The plan
- Deploy our Redis service separately
- use the same `redis` image
- make sure that Redis server port (6379) is publicly accessible,
using port 6379 on the Docker host
- Update our Docker Compose YAML file
- remove the `redis` section
- in the `links` section, remove `redis`
- instead, put a `redis` entry in `extra_hosts`
---
## Deploy Redis
.exercise[
- Start a new redis container, mapping port 6379 to 6379:
```
docker run -d -p 6379:6379 redis
```
- Check that it's running with `docker ps`
- Note the IP address of this Docker host
- Try to connect to it (from anywhere):
```
telnet ip.ad.dr.ess 6379
```
]
To exit a telnet session: `Ctrl-] c ENTER`
---
## Update `docker-compose.yml` (1/3)
.exercise[
- Comment out `redis`:
```
#redis:
# image: redis
```
]
---
## Update `docker-compose.yml` (2/3)
.exercise[
- Update `worker`:
```
worker:
build: worker
extra_hosts:
redis: A.B.C.D
links:
- rng
- hasher
```
]
(Replace `A.B.C.D` with the IP address noted earlier)
---
## Update `docker-compose.yml` (3/3)
.exercise[
- Update `webui`:
```
webui:
build: webui
extra_hosts:
redis: A.B.C.D
ports:
- "8000:80"
volumes:
- "webui/files/:/files/"
```
]
(Replace `A.B.C.D` with the IP address noted earlier)
---
## Start the stack on another machine
- We will set the `DOCKER_HOST` variable
- `docker-compose` will detect and use it
- Our Docker hosts are listening on port 55555
.exercise[
- Set the environment variable:
<br/>`export DOCKER_HOST=tcp://X.Y.Z:55555`
- Start the stack:
<br/>`docker-compose up -d`
- Check that it's running:
<br/>`docker-compose ps`
]
---
## Scale!
.exercise[
- Open the Web UI
- Deploy one instance of the stack on each node
]
---
## Cleanup
- Let's remove what we did
.exercise[
- You can use the following scriptlet:
```
for N in $(seq 1 5); do
export DOCKER_HOST=tcp://node$N:55555
docker ps -qa | xargs docker rm -f
done
unset DOCKER_HOST
```
]
---
# Abstracting connection details
- What if we can't/won't run Redis on its default port?
- What if we want to be able to move it more easily?
--
- We will use an ambassador
- Redis will run at an arbitrary location (host+port)
- The ambassador will be part of the scaled stack
- The ambassador will connect to Redis
- The ambassador will "act as" Redis in the stack
---
## Start redis
- This time, we will let Docker pick the port for Redis
.exercise[
- Run redis with a random public port:
<br/>`docker run -d -P --name myredis redis`
- Check which port was allocated:
<br/>`docker port myredis 6379`
]
- Note this IP address and port
---
## Update `docker-compose.yml`
.exercise[
- Restore `links` as they were before in `webui` and `worker`
- Replace `redis` with an ambassador using `jpetazzo/hamba`:
```
redis:
image: jpetazzo/hamba
command: 6379 52.26.10.3 32785
```
]
---
## Start the new stack
.exercise[
- Run `docker-compose up -d`
- Go to the web UI
- Start the stack on another node as previously,
<br/>and confirm on the web UI that it's picking up
]
---
## Discussion
- `jpetazzo/hamba` is stack and stupid
- It could be replaced with something dynamic:
- looking up the host+port in consul/etcd/zk
- reconfiguring itself when consul/etcd/zk is updated
- dealing with failover
---
# Backups
- Redis is still running (with name `myredis`)
- We want to enable backups without touching it
- We will use a special backup container:
- sharing the same volumes
- linked to it (to connect to it easily)
- possibly containing our backup tools
- This works because the `redis` container image
<br/>stores its data on a volume
---
## Starting the backup container
.exercise[
- Start the container:
```
docker run --link myredis:redis \
--volumes-from myredis \
-v /tmp/myredis:/output \
-ti ubuntu
```
- Look in `/data` in the container.
<br/>(It should be empty.)
]
- We need to tell Redis to perform a data dump
---
## Connecting to Redis
.exercise[
- `apt-get install telnet`
- `telnet redis 6379`
- issue `SAVE` then `QUIT`
- Look at `/data` again
]
- There should be a dump file now
---
## Getting the dump out of the container
- We could use many things:
- s3cmd to copy to S3
- SSH to copy to a remote host
- gzip/bzip/etc before copying
- We'll just copy it to the Docker host
.exercise[
- Copy the file from `/data` to `/output`
- Exit the container
- Look into `/tmp/myredis` (on the host)
]
---
# Logs
- Sorry, this part won't be hands-on
- Two (and a half) strategies:
- log to plain files on volumes
- log to stdout with the syslog driver
- log to stdout with the JSON driver
- The last one doesn't really count
<br/>(but it's the default)
---
## Logging to plain files on volumes
- Start a container with `-v /logs`
- Make sure that all log files are in `/logs`
- To check logs, run e.g.
```
docker run --volumes-from ... ubuntu sh -c \
"grep WARN /logs/*.log"
```
- Or just go interactive:
```
docker run --volumes-from ... -ti ubuntu
```
- You can (should) start a log shipper that way
---
## Logging to syslog
- All containers should write to stdout/stderr
- Change Docker start options to add `--log-driver syslog`
<br>(On Ubuntu, tweak `DOCKER_OPTS` in `/etc/default/docker`)
- When you do that, you can't use `docker logs` anymore
---
## Logging to JSON files
- That's the default option
- All containers should write to stdout/stderr
- You can use `docker logs`
- But those local JSON files are, well, local
- ... And they will eventually use up all the space
---
# Security upgrades
- This section is not hands-on
- Public Service Announcement
- We'll discuss:
- how to upgrade the Docker daemon
- how to upgrade container images
---
## Upgrading the Docker daemon
- Stop all containers cleanly
<br/>(`docker ps -q | xargs docker stop`)
- Stop the Docker daemon
- Upgrade the Docker daemon
- Start the Docker daemon
- Start all containers
- This is like upgrading your Linux kernel,
<br/>but it will get better
---
## Upgrading container images
- When a vulnerability is announced:
- if it affects your base images,
<br/>make sure they are fixed first
- if it affects downloaded packages,
<br/>make sure they are fixed first
- re-pull base images
- rebuild
- restart containres
(The procedure is simple and plain, just follow it!)
---
# Network traffic analysis
- We still have `myredis` running
- We will use *shared network namespaces*
<br/>to perform network analysis
- Two containers sharing the same network namespace...
- have the same IP addresses
- have the same network interfaces
- `eth0` is therefore the same in both containers
---
## Install and start `ngrep`
.exercise[
- Start a container with the same network namespace:
<br/>`docker run --net container:myredis -ti ubuntu`
- Install ngrep:
<br/>`apt-get update && apt-get install -y ngrep`
- Run ngrep:
<br/>`ngrep -tpd eth0 -Wbyline . tcp`
]
---
# Introducing Swarm
![Swarm Logo](swarm.png)
---
## Overview
- Swarm consolidates multiple Docker hosts into a single one
- Swarm "looks like" a Docker daemon, but it dispatches (schedules)
your containers on multiple daemons
- Swarm talks the Docker API front and back
- Swarm is open source and written in Go (like Docker)
- Swarm was started by two of the original Docker authors
<br/>([@aluzzardi](https://twitter.com/aluzzardi) and [@vieux](https://twitter.com/vieux))
- Swarm is not stable yet (version 0.3 right now)
---
# Setting up our Swarm cluster
- This is usually done by **Docker Machine**
<br/>( or by custom deployment scripts)
- We will do a simplified version here (without TLS),
<br/>to give you an idea of what's involved
- Components involved:
- service discovery mechanism
<br/>(we'll use Docker's hosted system)
- swarm agent
<br/>(runs on each node, registers it with service discovery)
- swarm manager
<br/>(runs on `node1`, exposes Docker API)
---
## Service discovery
- Possible backends:
- dynamic, self-hosted (zk, etcd, consul)
- static (command-line or file)
- hosted by Docker (token)
- We will use the token mechanism
.exercise[
- Run `docker run swarm create`
- Save the output carefully: it's your token
<br/>(it's the unique identifier for your cluster)
]
---
## Swarm agent
- Used only for dynamic discovery (zk, etcd, consul, token)
- Must run on each node
- Every 20s (by default), tells to the discovery system:
</br>"Hello, there is a Swarm node at A.B.C.D:EFGH"
- The node continues to work even if the agent dies
---
## Join the cluster
.exercise[
- Connect to `node2`
- Start the swarm agent:
<br/>`docker run -d swarm join \`
<br/>` --advertise A.B.C.D:55555 token://XXX`
<br/>.small[(`A.B.C.D` is the IP address of `node2`, `XXX` is the token generated earlier)]
- Check that the node registered successfully:
<br/>`docker swarm list token://XXX`
- Repeat on nodes 3, 4, 5
]
Note: the Docker daemon on your VMs listens on port 55555
---
## Swarm manager
- Today: must run on the "master" node
- Later: can run on multiple nodes, with master election
.exercise[
- Connect to `node1`
- Start the swarm manager:
<br/>`docker run -d -p 10000:2375 swarm manage token://XXX`
]
- Remember to replace XXX with your token!
- The Swarm manager listens on port 2375
- We're telling Docker to expose that on port 10000
---
## First contact with Swarm
- We must setup our CLI to talk to the Swarm master
.exercise[
- From any machine, set the environment variable:
<br/>`export DOCKER_HOST=tcp://node1:10000`
- Check the output of `docker version` and `docker info`
]
- Remember to set the environment variable if you open another SSH session!
- With Docker Machine, you would do a command like:
<br/>`eval $(docker-machine env my-swarm-master)`
---
# Running on Swarm
- Swarm doesn't support builds (yet)
- .icon[![Explosion](explosion.gif)] Our version of Compose will crash on builds
- Try it!
.exercise[
- Run `docker-compose build` and see the traceback!
]
---
## Building with Swarm
- Let's step back and think for a minute ...
- What should `docker build` do on Swarm?
- build on one machine
- build everywhere ($$$)
- After the build, what should `docker run` do?
- run where we built (how do we know where it is?)
- run on any machine that has the image
- What do, what do‽
---
## The plan
- Build locally
- Tag images
- Upload them to the hub
- Update the Compose file to use those images
*That's the purpose of the `build-tag-push.py` script!*
---
## Build, Tag, And Push
.exercise[
- Look at `build-tag-push.py`
- Run it!
]
- Run it in the directory containing `docker-compose.yml`
<br/>(suggestion: `../build-tag-push.py`)
- It will create a new `docker-compose.yml` file
<br/>(named `docker-compose.yml-XXX` where `XXX` is a timestamp)
---
## Can we run this now?
Let's try!
.exercise[
- Run `docker-compose -f docker-compose.yml-XXX up`
]
--
It won't work, because Compose and Swarm do not collaborate
to establish *placement constraints*.
So, what do‽
---
# Network plumbing on Swarm
- We will share *network namespaces* (as seen before)
- Other available options:
- injecting service addresses in environment variables
- implementing service discovery in the application
- using an overlay network like Weave or Pipework
---
## Another use of network namespaces
- Two (or more) containers can share a network stack
- They will have the same IP address
- They will be able to connect over `localhost`
- Other containers can be added later
---
## Connecting over localhost
.exercise[
- Start a container running redis:
<br/>`docker run -d --name myredis redis`
- Start another container in the same network namespace:
<br/>`docker run -ti --net container:myredis ubuntu`
- In the 2nd container, install telnet:
<br/>`apt-get update && apt-get install telnet`
- In the 2nd container, connect to redis on localhost:
<br/>`telnet localhost 6379`
]
Some Redis commands: `"SET key value"` `"GET key"`
---
## Same IP address
- Let's confirm that our containers share
the same IP address
.exercise[
- Run a couple of times:
<br/>`docker run ubuntu ip addr ls`
- Now run a couple of times:
<br/>`docker run --net container:myredis ubuntu ip addr ls`
]
---
## Our plan for service discovery
- Replace all `links` with static `/etc/hosts` entries
- Those entries will map to `127.0.0.X`
<br/>(with different `X` for each service)
- Example: `redis` will point to `127.0.0.2`
<br/>(instead of a container address)
- Start all services; scale them if we want
<br/>(at this point, they will all fail to connect)
- Start ambassadors in the services' namespace;
<br/>each ambassador will listen on the right `127.0.0.X`
.icon[![Warning](warning.png)] Services should try to reconnect!
---
## .icon[![Warning](warning.png)] Work in progress
- Ideally, we would use `--add-host`
(and Docker Compose counterpart, `extra_hosts`) to populate
`/etc/hosts`
- Unfortunately, this does not work yet
<br/>(See [Swarm issue #908](https://github.com/docker/swarm/issues/908)
for details)
- We'll populate `/etc/hosts` manually instead
<br/>(with `docker exec`)
---
## Our tools
- `unlink-services.py`
- replaces all `links` with `extra_hosts` entries
- `connect-services.py`
- scans running containers
- generate commands to patch `/etc/hosts`
- generate commands to start ambassadors
---
## Putting it together
.exercise[
- Generate the new Compose YAML file:
<br/>`../unlink-services.py docker-compose.yml-XXX deploy.yml`
- Start our services:
<br/>`docker-compose -f deploy.yml up -d`
<br/>`docker-compose -f deploy.yml scale worker=8`
<br/>`docker-compose -f deploy.yml scale rng=4`
- Generate plumbing commands:
<br/>`../connect-services.py deploy.yml`
- Execute plumbing commands:
<br/>`../connect-services.py deploy.yml | sh`
]
---
## Notes
- If you want to scale up or down, you have to re-do
the whole plumbing
- This is not a design issue; just an implementation detail
of the `connect-services.py` script
- Possible improvements:
- get list of containers using a single API call
- use labels to tag ambassador containers
- update script to do fine-grained updates of `/etc/hosts`
- update script to add/remove ambassadors carefully
<br/>(instead of destroying+recreating them all)
---
# Last words
- Kubernetes
- Mesos
- Powerstrip
- Weave
---
class: title
# Thanks! <br/> Questions?
### [@jpetazzo](https://twitter.com/jpetazzo) <br/> [@docker](https://twitter.com/docker)
</textarea>
<script src="https://gnab.github.io/remark/downloads/remark-0.5.9.min.js" type="text/javascript">
</script>
<script type="text/javascript">
var slideshow = remark.create();
</script>
</body>
</html>