# Rethinking applications design with **Docker**
7th October 2015 — [EBU DevCon](https://tech.ebu.ch/events/devcon15) {.footer}
@@@
## Disclaimer
This is *not* a talk about **microservices**.
This is a talk about **reproducibility**. {.fragment}
## Bonjour,
I am **Thomas**
[thom4.net](https://thom4.net) – [@thom4parisot](https://twitter.com/thom4parisot) {.footer}
@@@
![Pardon my French](../../images/pardon-my-french.jpg)
@@@
## BBC R&D
[github.com/bbcrd](https://github.com/bbcrd)
[bbc.co.uk/rd](http://bbc.co.uk/rd)
@@@
![Full Stack JavaScript](../../images/javascript.png)
[thom4.net/node.js](https://thom4.net/node.js)
@@@
![Sud Web](../../images/sudweb.png)
[sudweb.fr](http://sudweb.fr)
@@@
~~~~
And photography.
# Once upon a time…
(when I was a PHP developer…) {.footer}
@@@
@@@
```bash
$ scp -Cr ./src production:/var/www/my-cool-website
```
@@@
@@@
@@@
## *Multiple* points of failure
- software (typo)
- transfer (incomplete, no gatekeeper)
- execution (runtime failure)
- host system itself
@@@
## Not *reproducible* because hardly *repeatable*
@@@
## What "*install PHP5*" means
```bash
# Ubuntu
$ apt-get install php5 libapache2-mod-php5 php5-curl
$ php --version
> PHP 5.5.9 (cli)
# CentOS 6
$ yum install php curl curl-devel
$ php --version
> PHP 5.4.16 (cli)
```
## There is a ~~startup~~/~~app~~/*solution* for that
@@@
## **Provisionning** systems
Puppet, Chef, Ansible etc.
@@@
@@@
## **Provisionning** is *easy*
@@@
## **Provisionning** is easy*?*
@@@
## Double bill
Learning what you *need* and its *abstraction*.
@@@
## *Deploy target* is **far** away from *dev machine*
And the *build machine*, and the *integration machine*, and…
## Enters **LXC**
(circa 2008)
@@@
## LXC wraps an *app* and an *OS system* in a *rootfs* partition
@@@
```bash
# build.sh
$ lxc-create -t ubuntu -n nodejs-app --release trusty
$ lxc-start -n nodejs-app --daemon
$ cp ./src /var/lib/lxc/nodejs-app/rootfs
$ lxc-attach -n nodejs-app
$$ apt-get install nodejs -y
$$ npm install
^A^D
$ lxc-stop -n nodejs
```
@@@
## From the *host* perspective
```bash
docker@default:~$ ps fx
PID TTY STAT TIME COMMAND
2 ? S 0:00 [kthreadd]
3 ? S 0:01 \_ [ksoftirqd/0]
5 ? S< 0:00 \_ [kworker/0:0H]
7 ? S 0:00 \_ [rcu_sched]
8 ? S 0:00 \_ [rcu_bh]
9 ? S 0:00 \_ [migration/0]
10 ? S< 0:00 \_ [khelper]
987 ? S 0:00 ntpd -d -n -p pool.ntp.org
…
19086 pts/0 Ss+ 0:00 /bin/sh -c npm start
19115 pts/0 Sl+ 0:00 \_ npm
19124 pts/0 S+ 0:00 \_ sh -c static -p ${PORT:-5000} dist/
19125 pts/0 Sl+ 0:00 \_ node /app/node_modules/.bin/static -p 5000 dist/
…
```
@@@
## From the *container* perspective
```bash
app@aa05e05fc3ae:~$ ps fx
PID TTY STAT TIME COMMAND
1 ? Ss+ 0:00 /bin/sh -c npm start
28 ? Sl+ 0:00 npm
37 ? S+ 0:00 \_ sh -c static -p ${PORT:-5000} dist/
38 ? Sl+ 0:00 \_ node /app/node_modules/.bin/static -p 5000 dist/
```
@@@
## One benefit: **self contained**
Container is not aware of its parent host.
@@@
## But *not (trans)portable*
## Enters **Docker**
(circa 2013)
@@@
## Docker wraps *build instructions* and an *OS system* in *layered partitions*
@@@
```dockerfile
# Dockerfile
FROM ubuntu:trusty
RUN apt-get install -y nodejs
COPY package.json /app/package.json
RUN npm install
EXPOSE 3000
CMD ["npm", "start"]
```
@@@
## Multiple **benefits**
- all the benefits of LXC
- simpler instructions than LXC
- portable images (Docker Hub, private registries, tar exports)
- easier to address networking and partition mounting
## **Single** component
@@@
## **Immutable** build
```bash
docker build -t thom4/devcon-nodejs-app
Step 0 : FROM node:4-slim
---> 81876916bca2
Step 1 : WORKDIR /app
---> Using cache
---> 44a4f0b3ef85
Step 2 : COPY package.json ./package.json
---> Using cache
---> 48cf3e569dc8
Step 3 : RUN npm install
...
```
@@@
## Run
```bash
docker run -ti thom4/devcon-nodejs-app
```
@@@
## **Explicit** behaviour
Nothing happened unless *requested*.
@@@
## Port binding w/ *host*
```bash
# docker run -p [net-interface:host-port]:internal-port
docker run -ti -p 0.0.0.0:5000:3000 thom4/devcon-nodejs-app
```
@@@
## Port binding w/ *containers*
```bash
docker run -d --name app thom4/devcon-nodejs-app
docker run -ti --link "app:app_alias" thom4/devcon-nodejs-app
```
~~~~
```
docker run -ti --link "app:app_alias" thom4/devcon-nodejs-app env
docker run -ti --link "app:app_alias" thom4/devcon-nodejs-app ping app_alias
docker run -ti --link "app:app_alias" thom4/devcon-nodejs-app curl http://app:app_alias:3000
```
@@@
## **Runtime** configuration
```bash
docker run -e KEY_VALUE --env-file=.env-file
```
```
# .env-file
NODE_ENV=test
DATABASE_URL=postgresql://host:port/db?pool=25
```
Think [Twelve-Factor](http://12factor.net/). {.footer}
~~~~
```
docker run -ti --rm -p 0.0.0.0:5000:3000 -e MOTD="Hello Devcon" thom4/devcon-nodejs-app
```
@@@
## Multiply!
```bash
docker run -d -P thom4/devcon-nodejs-app
docker run -d -P thom4/devcon-nodejs-app
docker run -d -P thom4/devcon-nodejs-app
docker run -d -P thom4/devcon-nodejs-app
docker run -d -P thom4/devcon-nodejs-app
# …
docker ps
```
~~~~
```
docker inspect -f '{{range $p, $conf := .NetworkSettings.Ports}}{{(index $conf 0).HostPort}}{{end}}' $(docker ps -q)
```
## **Standard streams** containers
@@@
## *Interop* exchange format
There are already plenty of tools. *Language agnostic*.
```bash
echo something | docker run -i
```
~~~~
```bash
echo 'http://www3.ebu.ch/news/2015/09/media-companies-get-personal-at' | docker run -i freebird/feature-processor-render-html
```
@@@
## Infinite combinations
```bash
echo something | docker run -i | awk '{print $n}' | docker run -i
```
~~~~
```bash
echo 'http://www3.ebu.ch/news/2015/09/media-companies-get-personal-at' | docker run -i freebird/feature-processor-render-html | jq -r '."http://freebird.prototyping.bbc.co.uk/ns#html"' | docker run -i freebird/feature-processor-item-body | jq -r '."http://schema.org/text"'
```
## **Orchestrated** components
@@@
## What if I need…
- a *Rails* application (w/ Ruby 2.1.7)
- *ElasticSearch* (1.4)
- *PostgreSQL* (9.3)
- *Redis* (3.0)
- ?
@@@
## **docker-compose** to the rescue
- no more *complicated README*
- easier than doing everything by hand
- acts as a *technical documentation* of needed resources
- spins up a stack from scratch in minutes
- ability to scale up/down individual containers
@@@
## docker-compose.yml
```bash
web:
build: .
links:
- db
- search
- queue
environment:
- DATABASE_URL=postgres://postgres:postgres@db:5432/postgres
- ELASTICSEARCH_URL=http://search:9200
- REDIS_URL=redis://queue:6379
queue:
image: redis:3.0
db:
image: postgres:9.3
- POSTGRES_PASSWORD=postgres
search:
image: elasticsearch:1.4
```
## **Continuous** Integration
@@@
@@@
```bash
docker-compose up -d
docker-compose run web bundle exec rails test
```
## Other (and fun) purposes
@@@
## *Build* systems
- Documentation generators (to run in CI)
- Spawnables CI agents
@@@
## *Training* materials
- Operating system agnostic demonstrators
- Clonable environments
@@@
## Running **system apps** (like Spotify)
https://blog.jessfraz.com/post/docker-containers-on-the-desktop/
(All your sockets are belong to us!) {.footer}
@@@
## Embedded hardware
- ARM builds for Raspberry Pi
- Distributed deploy (w/ [resin.io](https://resin.io))
## **Best** practices
@@@
## The **order** matters
- *first* line which changes invalidates the rest
- so `COPY` instructions as late as possible
@@@
## **Explicit** FROM statement
```
FROM ubuntu:14.04
```
is better than
```
FROM ubuntu
```
~~~~
Remember, we want predictability: next major of Ubuntu should not break next deploy.
@@@
## Use tools you **know**
(or want to learn about)
To monitor, automate, deploy etc.
@@@
## **Simplify** commands
We use *Makefile* for consistency.
```bash
# builds Docker image
make build
# runs tests in containers
make test
# pushed the image to our own registry
make push
```
~~~~
As a bonus what works for a developer works for the CI too.
@@@
## Watch out **CPU architecture**
- Some apps checks for CPU at runtime
- ARM needs different packages and base images
@@@
## How do you address **security**?
- What if someone gains access to your container?
- OpenAM network driver?
@@@
## Manage **outside** of containers
*syslog* driver is a good choice.
```bash
$ cat /etc/default/docker
DOCKER_OPTS="--log-driver=syslog"
```
~~~~
Forward with rsyslog/kibana etc. Plenty of tooling already out there.
@@@
## **Own** your images
We build on top of a *baseimage* and derive to optimised stack images.
```bash
FROM bbcrd/baseimage
…
```
```bash
FROM bbcrd/node:4.x
…
```
~~~~
- can be better for security than some random image found on Docker Hub
- easier to spread custom utilities (image cleanup)
- enforces practices in your org
- tailored to your needs
- you can automate the rebuilds daily
@@@
## Suitable for **development** too
Inject work directory and enable auto-reload.
```bash
docker run -ti \
-v $(pwd)/src:/app/src:ro \
-p 0.0.0.0:5000:3000 \
thom4/devcon-nodejs-app npm run start-dev
```
## Conclusion
@@@
## Reusable as a **whole**
System included.
@@@
## **Isolated** services
No chance to mess up with *system packages*.
@@@
## **Composable** stacks
Legacy apps and modern ones.
Installable at all time. Now and in 5 years (hopefully).
(want to try out *Go*, *Node.js* or *Spark*?) {.footer}
@@@
## Testable **systems**
Dependencies included.
@@@
## **Consistency**
What works **here** works **there**.
~~~~
Running a container should not be that different from your actual way of running
regular applications.
@@@
> In principle, **the work of art has always been reproducible**. Objects made by humans could always be copied by humans. (…) But the **technological reproduction** of artworks is something *new*.
– *Walter Benjamin*, The Work of Art in the age of Mechanical Reproduction (1936)
# Merci !*\**
\* *en français dans le texte* {.footer}