# 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}