48
Streamline your dev env with Docker Giacomo Bagnoli, #RUBYDAY2014

Streamline your development environment with docker

Embed Size (px)

DESCRIPTION

These days applications are getting more and more complex. It's becoming quite difficult to keep track of all the different components an application needs in order to function (a database, a message queueing system, a web server, a document store, a search engine, you name it.). How many times we heard 'it worked on my machine'?. In this talk we are going to explore Docker, what it is, how it works and how much it can benefit in keeping the development environment consistent. We are going to talk about Dockerfiles, best practices, tools like fig and vagrant, and finally show an example of how it applies to a ruby on rails application.

Citation preview

Page 1: Streamline your development environment with docker

Streamline your dev env with DockerGiacomo Bagnoli, #RUBYDAY2014

Page 2: Streamline your development environment with docker

Giacomo BagnoliCurrently Backend Engineer at Gild

Previously at Amazon and Asidev

Twitter: @gbagnoliGithub: gbagnoliabout.me/gbagnoli

WHOAMI

Page 3: Streamline your development environment with docker

Nothing in particular. It's not* broken, so let's fix it.

* conditions apply

WHAT'S WRONG WITH MY DEV ENVIRONMENT?

Page 4: Streamline your development environment with docker

Your developmentenvironment is probably a beautiful, uniquesnowflake

WHAT'S WRONG WITH MY DEV ENVIRONMENT?

Photo credits: https://www.flickr.com/photos/amagill/4223790595/

Page 5: Streamline your development environment with docker

Open source platform

Docker Engine

Container management runtimePackaging tools for images

Docker Hub

WHAT'S DOCKER, ANYWAY?!?

Page 6: Streamline your development environment with docker

Operating system-level virtualization*Runs multiple isolated linux systemsOn a single host, with a single kernelNo /sbin/init, no device emulation

Think them as chroot on steroids

Isolation provided by linux cgroups and namespaces

Resource limiting and prioritization via cgroups

Resource usage accounting via cgroups

* not a virtualization method, containers != VM

CONTAINERS, UH?

Page 7: Streamline your development environment with docker

Portable, read-only layers.

Images are composed at run-time toform the container root FS using anunion filesystem.

Processes tricked to see the filesystemas R/W.

The writable layer is discarded if thecontainer is not committed.

The read-only and portable propertiesare important as they enable sharing(via the docker hub).

DOCKER IMAGES (0)

Page 8: Streamline your development environment with docker

Docker images form a Direct Acyclic Graph.

Each layer is cached (possibly) and reused by other images.

This means that if multiple images derive from debian:wheezy,that particular image is shared by all of them (thus downloadedonce).

Images are pushed/pull to/from the docker hub.

DOCKER IMAGES (1)

Page 9: Streamline your development environment with docker

$ docker pull ubuntu:14.04$ docker pull ubuntu:12.04$ docker pull redis:2.8.13$ docker pull debian:wheezy$ docker pull mongo:2.6.4

$ docker imagesREPOSITORY   TAG           IMAGE ID            CREATED             VIRTUAL SIZEredis        2.8.13        dd52dc9c8f76        9 minutes ago       98.44 MBmongo        2.6.4         dd1f260c0731        12 minutes ago      391.2 MBdebian       wheezy        9cdcc6025135        18 hours ago        85.19 MBubuntu       14.04         96864a7d2df3        2 days ago          204.4 MBubuntu       12.04         ec966722cde4        2 days ago          103.8 MB  

DOCKER IMAGES (2)

Page 10: Streamline your development environment with docker

$ docker images ‐‐tree└─511136ea3c5a Virtual Size: 0 B  └─b37448882294 Virtual Size: 85.19 MB    └─9cdcc6025135 Virtual Size: 85.19 MB Tags: debian:wheezy      ├─e365f7cdb352 Virtual Size: 85.52 MB      │ └─b15940870e43 Virtual Size: 85.52 MB

      │             └─22ad4fc6b16f Virtual Size: 98.44 MB      │               └─bd1e22dd175d Virtual Size: 98.44 MB      │                 └─3b1ce200fdad Virtual Size: 98.44 MB      │                   └─dd52dc9c8f76 Virtual Size: 98.44 MB Tags: redis:2.8.13      └─49fd1ae472a8 Virtual Size: 85.52 MB        └─6c203838fd07 Virtual Size: 99.62 MB

                      └─b1cd74f30329 Virtual Size: 391.2 MB                        └─9d0a3438646f Virtual Size: 391.2 MB                          └─dd1f260c0731 Virtual Size: 391.2 MB Tags: mongo:2.6.4  

DOCKER IMAGES (3)

Page 11: Streamline your development environment with docker

Ubuntu 14.04 image has no ruby at all. Repos have ruby 1.9.

Let's create an image with 2.1 as default.

Dockerfile:

FROM ubuntu:14.04MAINTAINER Giacomo Bagnoli <[email protected]>

RUN echo "deb http://ppa.launchpad.net/brightbox/ruby‐ng/ubuntu trusty main" > \    /etc/apt/sources.list.d/ruby‐ng.listRUN apt‐key adv ‐‐keyserver hkp://keyserver.ubuntu.com:80 ‐‐recv‐keys C3173AA6RUN apt‐get updateRUN apt‐get install ‐y ruby2.1

Build!

$ docker build ‐‐rm ‐t rubyday/ruby:2.1 .

LET'S BUILD A RUBY2 IMAGE

Page 12: Streamline your development environment with docker

Each directive in the Dockerfile adds a layer

$ docker images ‐‐tree

└─96864a7d2df3 Virtual Size: 204.4 MB Tags: ubuntu:14.04  └─8f1b6341c5be Virtual Size: 204.4 MB                                 # MAINTAINER    └─d323cc59da91 Virtual Size: 204.4 MB                               # RUN      └─724a6664d97a Virtual Size: 204.4 MB                             # RUN        └─8614dab05fbe Virtual Size: 224.8 MB                           # RUN          └─d7ae4a198781 Virtual Size: 257.2 MB Tags: rubyday/ruby:2.1  # RUN  

$ docker run ‐t rubyday/ruby:2.1 ruby ‐vruby 2.1.2p95 (2014‐05‐08 revision 45877) [x86_64‐linux‐gnu]  

Woah, 53Mb. apt‐get update adds 20Mb to the image.

RUBY2 IMAGE

Page 13: Streamline your development environment with docker

Let's remove apt-get files by adding another RUN statement

diff ‐‐git a/ruby2/Dockerfile b/ruby2/Dockerfileindex dd37dcb..2b9c105 100644‐‐‐ a/ruby2/Dockerfile+++ b/ruby2/Dockerfile@@ ‐8,3 +8,4 @@RUN echo "deb http://ppa.launchpad.net/brightbox/ruby‐ng/ubuntu trusty main" > / RUN apt‐key adv ‐‐keyserver hkp://keyserver.ubuntu.com:80 ‐‐recv‐keys C3173AA6 RUN apt‐get update RUN apt‐get install ‐y ruby2.1+RUN rm ‐rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb  

I TRIED...

Page 14: Streamline your development environment with docker

... GRUMPY CAT SAYS

Page 15: Streamline your development environment with docker

$ docker images ‐‐tree

└─96864a7d2df3 Virtual Size: 204.4 MB Tags: ubuntu:14.04  └─8f1b6341c5be Virtual Size: 204.4 MB                                   # MAINTAINER    └─d323cc59da91 Virtual Size: 204.4 MB                                 # RUN      └─724a6664d97a Virtual Size: 204.4 MB                               # RUN        └─8614dab05fbe Virtual Size: 224.8 MB                             # RUN          └─d7ae4a198781 Virtual Size: 257.2 MB                           # RUN            └─b8bb3ce3008e Virtual Size: 257.2 MB Tags: rubyday/ruby:2.1  # RUN  

Remember that every directive adds a layer. Layers are read only.

LAYERS..

Page 16: Streamline your development environment with docker

Let's rewrite the Dockerfile

FROM ubuntu:14.04MAINTAINER Giacomo Bagnoli <[email protected]>

RUN \ echo "deb http://ppa.launchpad.net/brightbox/ruby‐ng/ubuntu trusty main" \    > /etc/apt/sources.list.d/ruby‐ng.list && \    apt‐key adv ‐‐keyserver hkp://keyserver.ubuntu.com:80 ‐‐recv‐keys C3173AA6 && \    apt‐get update && \    apt‐get install ‐y ruby2.1 && \    rm ‐rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb  

LET'S TRY ONCE MORE

Page 17: Streamline your development environment with docker

Build:

$ docker build ‐‐rm ‐t rubyday/ruby:2.1 .  

$ docker imagesREPOSITORY           TAG        IMAGE ID            CREATED             VIRTUAL SIZErubyday/ruby         2.1        b337a5c538f3        About a minute ago  236.9 MB  

└─96864a7d2df3 Virtual Size: 204.4 MB Tags: ubuntu:14.04  └─86ae939e2da3 Virtual Size: 204.4 MB                         # MAINTAINER    └─b337a5c538f3 Virtual Size: 236.9 MB Tags: gild/ruby:2.1   # RUN  

yay!

REBUILD

Page 18: Streamline your development environment with docker

PAT ME ON THE BACK

Page 19: Streamline your development environment with docker

We probably want ‐dev packages and bundle

Let's update the Dockerfile

FROM ubuntu:14.04

MAINTAINER Giacomo Bagnoli RUN \ echo "deb http://ppa.launchpad.net/brightbox/ruby‐ng/ubuntu trusty main" \    > /etc/apt/sources.list.d/ruby‐ng.listRUN apt‐key adv ‐‐keyserver hkp://keyserver.ubuntu.com:80 ‐‐recv‐keys C3173AA6RUN apt‐get update && \    apt‐get install ‐y build‐essential && \    apt‐get install ‐y ruby2.1 ruby2.1‐dev && \    update‐alternatives ‐‐set ruby /usr/bin/ruby2.1 && \    update‐alternatives ‐‐set gem /usr/bin/gem2.1 && \    rm ‐rf /var/lib/apt/lists/* /var/cache/apt/archives/*.debRUN gem install bundle  

... ONE MORE THING

Page 20: Streamline your development environment with docker

Let's try creating a Dockerfile for a rails app.

The app is a random simple TODO listapplication found on Github.

It's a rails4 application that uses SQL, nothingfancy.

Let's assume we are developing this apptargeting postgresql.

Github url: https://github.com/gbagnoli/todo‐rails4‐angularjs

TIME FOR RAILS

Page 21: Streamline your development environment with docker

In docker, we can access service(s) running in other container(s) via linking.

Linking a container to another will setup some environment variables in it, allowing the container todiscover and connect to the service.

We will use this feature to access postgres from our app container.

A WORD ON LINKING

Page 22: Streamline your development environment with docker

FROM rubyday/ruby:2.1MAINTAINER Giacomo Bagnoli <[email protected]>

RUN adduser todo ‐‐home /opt/todo ‐‐shell /bin/bash ‐‐disabled‐password ‐‐gecos ""RUN apt‐get update && \    apt‐get install ‐y libpq‐dev nodejs && \    rm ‐rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb

ADD Gemfile /opt/todo/ADD Gemfile.lock /opt/todo/RUN chown ‐R todo:todo /opt/todoRUN su ‐c "D=/opt/todo/bundle; mkdir $D && bundle install ‐‐deployment ‐‐path $D"\  ‐s /bin/bash ‐l todo

WORKDIR /opt/todoEXPOSE 3000ADD . /opt/todoRUN chown ‐R todo:todo /opt/todo

USER todoENTRYPOINT ["/bin/bash", "/opt/todo/bin/docker_entrypoint.sh"]CMD ["bundle", "exec", "rails", "server"]    

THE DOCKERFILE

Page 23: Streamline your development environment with docker

FROM rubyday/ruby:2.1

MAINTAINER Giacomo Bagnoli <[email protected]>  

DOCKERFILE EXPLAINED (0)

Page 24: Streamline your development environment with docker

RUN adduser todo ‐‐home /opt/todo ‐‐shell /bin/bash ‐‐disabled‐password ‐‐gecos ""

RUN apt‐get update && \    apt‐get install ‐y libpq‐dev nodejs && \    rm ‐rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb  

DOCKERFILE EXPLAINED (1)

Page 25: Streamline your development environment with docker

ADD Gemfile /opt/todo/ADD Gemfile.lock /opt/todo/RUN chown ‐R todo:todo /opt/todoRUN su ‐c "D=/opt/todo/bundle; mkdir $D && bundle install ‐‐deployment ‐‐path $D"\  ‐s /bin/bash ‐l todo  

DOCKERFILE EXPLAINED (2)

Page 26: Streamline your development environment with docker

WORKDIR /opt/todoEXPOSE 3000ADD . /opt/todoRUN chown ‐R todo:todo /opt/todo  

DOCKERFILE EXPLAINED (3)

Page 27: Streamline your development environment with docker

USER todoENTRYPOINT ["/bin/bash", "/opt/todo/bin/docker_entrypoint.sh"]CMD ["bundle", "exec", "rails", "server"]  

DOCKERFILE EXPLAINED (4)

Page 28: Streamline your development environment with docker

$ docker pull postgres:9.3$ docker run ‐d ‐‐name postgres ‐t postgres:9.3a5723351c46ce015d585dd49f230ecb376557d0b955f233dbff3bf92f3a6721d$ docker psCONTAINER ID        IMAGE               [...]   PORTS               NAMESa5723351c46c        postgres:9          [...]   5432/tcp            postgres  

This container EXPOSEs port 5432.

Question is, how do we connect to it?

POSTGRES CONTAINER (0)

Page 29: Streamline your development environment with docker

We can't just hardcode its ip address, as it defeats the purpose...

$ docker inspect postgres | grep NetworkSettings ‐A 9    "NetworkSettings": {        "Bridge": "docker0",        "Gateway": "172.17.42.1",        "IPAddress": "172.17.0.4",        "IPPrefixLen": 16,        "PortMapping": null,        "Ports": {            "5432/tcp": null        }    },  

POSTGRES CONTAINER (1)

Page 30: Streamline your development environment with docker

In the Dockerfile, an ENTRYPOINT was specified.

#!/bin/bash

# exit with error if a variable is unbound (not set)set ‐u# exit with error if a command returns a non‐zero statusset ‐e

PGADDR=$DB_PORT_5432_TCP_ADDRPGPORT=$DB_PORT_5432_TCP_PORTPGDBNAME="${DATABASE_NAME:‐todo}"PGUSER="${DATABASE_USER:‐postgres}"

# export database configuration for rails.export DATABASE_URL="postgresql://${PGUSER}@${PGADDR}:${PGPORT}/${PGDBNAME}"

# exec what the user wantsexec "$@"  

THE WRAPPER SCRIPT (0)

Page 31: Streamline your development environment with docker

Trying to execute the container will throw an error (it's a feature!)

$ docker run ‐‐rm ‐‐name todoapp ‐t rubyday/todo/opt/todo/bin/docker_entrypoint.sh: line 6: DB_PORT_5432_TCP_ADDR: unbound variable  

THE WRAPPER SCRIPT (1)

Page 32: Streamline your development environment with docker

$ docker run ‐‐rm ‐‐link postgres:db ‐‐name todoapp \  ‐t rubyday/todo /bin/bash ‐c 'env'

DB_ENV_PGDATA=/var/lib/postgresql/dataDB_NAME=/todoapp/dbDB_PORT_5432_TCP_ADDR=172.17.0.4DB_PORT=tcp://172.17.0.4:5432DB_ENV_LANG=en_US.utf8DB_PORT_5432_TCP=tcp://172.17.0.4:5432DB_ENV_PG_MAJOR=9.3DB_PORT_5432_TCP_PORT=5432DB_PORT_5432_TCP_PROTO=tcpDB_ENV_PG_VERSION=9.3.5‐1.pgdg70+1DATABASE_URL=postgresql://[email protected]:5432/todo

‐‐link postgres:db link container named postgres with alias db

alias db tells docker to prefix all variables with DB

LINKING!

Page 33: Streamline your development environment with docker

Pretty much standard business

$ docker run ‐‐rm ‐‐link postgres:db ‐t rubyday/todo bundle exec rake db:create$ docker run ‐‐rm ‐‐link postgres:db ‐t rubyday/todo bundle exec rake db:schema:load

$ docker run ‐‐link postgres:db ‐‐name todoapp ‐p 3000:3000 ‐d ‐t rubyday/todo7540f7647309110c53d2349cf7c68d1388e0f43de3d5904396fa2bb4041b6b28

$ docker psCONTAINER ID  IMAGE                [..] PORTS                  NAMES7540f7647309  rubyday/todo:latest  [..] 0.0.0.0:3000‐>3000/tcp todoappa5723351c46c  postgres:9           [..] 5432/tcp               postgres,todoapp/db  

‐p 3000:3000 creates a port forward from the host to the container

START!

Page 34: Streamline your development environment with docker

$ netstat -lnp | grep 3000tcp6 0 0 :::3000 :::* LISTEN 3645/docker-proxy

$ curl -v http://localhost:3000* Connected to localhost (127.0.0.1) port 3000 (#0)> GET / HTTP/1.1> User-Agent: curl/7.35.0> Host: localhost:3000> Accept: */*>< HTTP/1.1 200 OK[...]

Good.

DOES IT WORK?

Page 35: Streamline your development environment with docker

Enters FIG.

fig.yml:

web: build: . links: - db ports: - "3000:3000"db: image: postgres:9.3 ports: - "5432"

TOO MUCH WORK. LET'S AUTOMATE

Page 36: Streamline your development environment with docker

diff --git a/bin/docker_entrypoint.sh b/bin/docker_entrypoint.shindex 0775ece..b69980c 100644--- a/bin/docker_entrypoint.sh+++ b/bin/docker_entrypoint.sh@@ -3,8 +3,8 @@ set -u set -e

-PGADDR=$DB_PORT_5432_TCP_ADDR-PGPORT=$DB_PORT_5432_TCP_PORT+PGADDR=$DB_1_PORT_5432_TCP_ADDR+PGPORT=$DB_1_PORT_5432_TCP_PORT PGDBNAME="${DATABASE_NAME:-todo}" PGUSER="${DATABASE_USER:-postgres}"

..SMALL TWEAK FOR FIG

Page 37: Streamline your development environment with docker

$ fig up ‐d  # familiar, huh?$ fig run web bundle exec rake db:create$ fig run web bundle exec rake db:schema:load

$ netstat ‐lnp | grep 3000tcp6       0      0 :::3000      :::*    LISTEN      24727/docker‐proxy

$ curl ‐v http://localhost:3000* Connected to localhost (127.0.0.1) port 3000 (#0)> GET / HTTP/1.1> User‐Agent: curl/7.35.0> Host: localhost:3000> Accept: */*>< HTTP/1.1 200 OK[...]  

PROFIT!

Page 38: Streamline your development environment with docker

$ fig ps   Name              Command            State        Ports‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐todo_db_1    postgres                   Up      49160‐>5432/tcptodo_web_1   bundle exec rails server   Up      3000‐>3000/tcp

# if we remove the :3000 for the web port in the fig.yml$ fig scale web=2Starting todo_web_2...$ fig ps   Name              Command            State        Ports‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐todo_db_1    postgres                   Up      49172‐>5432/tcptodo_web_2   bundle exec rails server   Up      49174‐>3000/tcptodo_web_1   bundle exec rails server   Up      49173‐>3000/tcp  

MORE FIG COMMANDS

Page 39: Streamline your development environment with docker

Leverage the cache.

$ time docker build ‐t rubyday/todo .# ==> 0m1.384s$ touch app/models/user.rb && time docker build ‐t rubyday/todo .# ==> 0m4.835s

# Move the ADD . statement above bundle, then rebuild from scratch$ touch app/model/user.rb && time docker build ‐t rubyday/todo .# ==> 1m54.277s  

VERY OPINIONATED TIPS

Page 40: Streamline your development environment with docker

Choose your storage driver wisely.devicemapper is slower. AUFS works ok.

BTRFS is ... well... btrfs the future.

VERY OPINIONATED TIPS

Page 41: Streamline your development environment with docker

Always tag your image(s). Always pull supplying a tag. Always use a tag for FROM.Don't rely on :latest tag.

VERY OPINIONATED TIPS

Page 42: Streamline your development environment with docker

If possible, avoid run+commit. Prefer Dockerfiles.Waaaaaay more reproducible.

VERY OPINIONATED TIPS

Page 43: Streamline your development environment with docker

Installing ssh into the container is not clever.NSINIT is your friend (gist)

https://gist.github.com/ubergarm/ed42ebbea293350c30a6

VERY OPINIONATED TIPS

Page 44: Streamline your development environment with docker

One process per container. Don't fork.Don't doublefork either. Stay in foreground.

VERY OPINIONATED TIPS

Page 45: Streamline your development environment with docker

Use a process manager.Both upstart or systemd are good at it.Run containers without the ‐d.

VERY OPINIONATED TIPS

Page 46: Streamline your development environment with docker

THANKS!

That's all. For now.

Page 47: Streamline your development environment with docker

QUESTIONS?

QUESTIONS?

Page 48: Streamline your development environment with docker

These slides were made with applausehttps://github.com/Granze/applauseGo check it out, it's AWESOME!

SHAMELESS PLUG