Ride the Whale! – Docker for Drupalists – "What's wrong with MAMP?"



Ride the Whale! – Docker for Drupalists – "What's wrong with MAMP?"

0 1


rideTheWhale


On Github socketwench / rideTheWhale

Ride the Whale!

Docker for Drupalists

socketwench.github.io/rideTheWhale

So you might be wondering who is this weird person in front of you. And yes, I do make that face when I get to go on a business trip.
My name is Tess Flynn, otherwise known as socketwench.
That's wench, not wrench.
I'm the module maintainer for Flag, Flag Friend, and Examples.
And I work as a Drupal Developer for FFW, the largest Drupal consultancy in the world.

"What's wrong with MAMP?"

Problem #1

No Sandboxing.

What happens outside the repo...

...stays on your system forever

Like pulling weeds

Files, configuration, old binaries

Requires discipline and time to find, delete

Hacks

Bash aliases, symlinks, multiple network ports

Works around assumed OS best practices

Problem #2

NOM NOM NOM Resources

Multiple VMs = Duplication

And you'll probably need more than one

Docker De-duplication by design

Files are also de-duplicated

UnionFS only uses the space needed

Even across containers

Problem #3

"Build tool? You mean this Word doc?"

Instructions aren't enough

Because humans are fallible

Each system is unique

Because it was built by hand

Eats productivity, harder to on-ramp, creates weird bugs

Time to learn a build tool

Puppet, Chef, Ansible are powerful

They also have a steeper learning curve

Docker builds in building

Consistent dev environments each time

No need to install anything else

Built on Linux APIs

Enterprise ready tech

Maintained by Red Hat, Google, IBM and others

Getting Docker

Do I need to pay for it?

Docker is free and open source!

github.com/docker/docker

Docker Toolbox

Easiest way to install on Mac and Windows

Installing Toolbox

docker.com/products/docker-toolbox

Includes Virtualbox

Docker Machine

Provides a Docker Host on a VM

Replaces boot2docker

Docker On Linux

Docker's native environment

No need for a VM

Installing on Linux

Use your distro's package manager

Soon, no more Virtualbox!

Docker for Mac & Windows Beta

Quick, pull,and run

Starts default docker machine

Sets $DOCKER_HOST environment variable

Without Quickstart

In ~/.bashrc

docker-machine start default
eval $(docker-machine env default)

May slow down terminal startup

Docker works like Vagrant

You rarely build a container from scratch

Online registry of pre-made containers

hub.docker.com

Official containers

Vetted and regularly updated

MySQL, PHP, Apache, many Linux Distros

Official Drupal Container

hub.docker.com/_/drupal

Good for demos...but limited for development

Downloading a Container

$ docker pull image_name

Where image_name is the name on Docker Hub

Getting started

$ docker pull debian

Running a Container

docker run image_name command

Listing containers

$ docker ps
CONTAINER ID       IMAGE        COMMAND            CREATED             STATUS
$

 

Listing containers

$ docker ps
CONTAINER ID       IMAGE        COMMAND            CREATED             STATUS
$

 

Listing containers

$ docker ps
CONTAINER ID       IMAGE        COMMAND            CREATED             STATUS
$

 

"A container is just a process."

Not exactly true, but close

Listing all containers

$ docker ps -a
CONTAINER ID       IMAGE        COMMAND            CREATED             STATUS
0c6142a4488a       debian       "/bin/bash"        8 seconds ago       Exited

A server ina text file

Ingredients

The starting container

Who maintains this file

Other commands & config

What to run when ready

Basis of Docker's build system

Directory and filename

<your_project_root>
  └── Dockerfile

Always create a project directory!

Each line has two parts

DIRECTIVE parameter

docs.docker.com

The first two directives

FROM debian:wheezy
MAINTAINER your_email@example.com

 

The first two directives

FROM debian:wheezy
MAINTAINER your_email@example.com

 

Turtles all the way down

All containers are build on top of another container

"scratch" container

Parent of all Docker containers

Just an empty *.tar.gz file

FROM directive

FROM name:version

Version is optional, defaults to "latest"

Using your Dockerfile

docker build /path/to/Dockerfile

$ docker build .

Sending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:wheezy
 ---> 4c82789231a5
Step 2 : MAINTAINER your_email@example.com
 ---> Running in 908f461e93e4
 ---> c9c74cdf1e1d
Removing intermediate container 908f461e93e4
Successfully built c9c74cdf1e1d

$

Images

VMs are like Pudding

It's one big blob of stuff

Containers are like Cake

They have layers!

It's not just where you're FROM

Each line in the Dockerfile is like a commit

$ docker build .

Sending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:wheezy
 ---> 4c82789231a5
Step 2 : MAINTAINER your_email@example.com
 ---> Running in 908f461e93e4
 ---> c9c74cdf1e1d
Removing intermediate container 908f461e93e4
Successfully built c9c74cdf1e1d

$

Image History

$ docker history c9c74cdf1e1d

IMAGE          CREATED        CREATED BY                                      SIZE                COMMENT
c9c74cdf1e1d   42 hours ago   /bin/sh -c #(nop) MAINTAINER your_email@examp   0 B
4c82789231a5   10 days ago    /bin/sh -c #(nop) CMD ["/bin/bash"]             0 B
<missing>      10 days ago    /bin/sh -c #(nop) ADD file:add5fc8cb18678647f   84.93 MB

(You'll almost never need this.)

You can't run an image

Running an image creates a new container!

Container data is ephemeral

Exists as long as container exists

Must be commited to an image

Listing images

$ docker images
REPOSITORY   TAG        IMAGE ID       CREATED           VIRTUAL SIZE
                        64b0d7e8eef9   1 minutes ago     85.02 MB
debian       wheezy     60c52dbe9d91   4 weeks ago       85.02 MB
debian       latest     9a61b6b1315e   4 weeks ago       125.2 MB

Intermediate containers?

We didn't need it any longer

So we threw them away

Base image

It's just an image

Usually whatever image you start with

Starting a Container from an Image

docker run imagehash /path/to/command
$ docker run -i -t c9c /bin/bash
root@71724ddf264b:/# cat /etc/issue
Debian GNU/Linux 7 \n \l

 

 

Installing Software

No INSTALL directive

It lets the container decide!

Base image decides the tool

Debian and Ubuntu use apt-get

Cent OS uses yum

Base image decides the tool

Debian and Ubuntu use apt-get

Cent OS uses yum

Base image decides the tool

Debian and Ubuntu use apt-get

Cent OS uses yum

RUN directive

RUN some_command -with arguments

Runs the specified command in the container

LAMP Tutorials

linode.com/docs/websites/lamp

Installing Apache

FROM debian:wheezy
MAINTAINER your_email@example.com

RUN apt-get update
RUN apt-get install -y apache2

Updating our image

Just use docker build!

$ docker build .
Sending build context to Docker daemon 2.048 kB
Sending build context to Docker daemon
Step 0 : FROM debian:wheezy
 ---> 60c52dbe9d91
Step 1 : MAINTAINER your_email@example.com
 ---> Using cache
 ---> 64b0d7e8eef9
Step 2 : RUN apt-get update
 ---> Running in 8652b461d629
Get:1 http://security.debian.org wheezy/updates Release.gpg [1554 B]
...
Fetched 8438 kB in 7s (1094 kB/s)
Reading package lists...
 ---> eefa2fcb15e7
Removing intermediate container 8652b461d629
Step 3 : RUN apt-get install -yq apache2
 ---> Running in ef474ef3a976
Reading package lists...
Building dependency tree...
Reading state information...
…
Setting up libswitch-perl (2.16-2) ...
 ---> aa084388ac30
Removing intermediate container ef474ef3a976
Successfully built aa084388ac30
(scroll down for more)
$docker images
REPOSITORY   TAG        IMAGE ID       CREATED           VIRTUAL SIZE
                        aa084388ac30   2 minutes ago     145 MB
debian       wheezy     60c52dbe9d91   4 weeks ago       85.02 MB
debian       latest     9a61b6b1315e   4 weeks ago       125.2 MB

RUN isn't interactive

Software can require input on installation

Will cause docker build to hang or fail

Keywords

"headless", "scripted", or "unattended installation"

Non-interactive apt-get

-yq switch

"Yes to all questions, use Quiet installation."

Foreground processes,background containers

Starting Apache

apachectl command

Starting Apache

apachectl command

As a run command?

$ docker run -i -t aa0 apachectl start
apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.7 for ServerName

$
$ docker ps -a
CONTAINER ID   IMAGE   COMMAND             CREATED         STATUS
9d0eab0934fe   aa0     "apachectl start"   2 minutes ago   Exited (0) 2 minutes ago
(Scroll right.)

Why'd it stop?

apachectl started apache as a background process

Then returned 0

Remember

A container runs as long as it's process does

Running Apache in the Foreground

apachectl -D FOREGROUND

Apache switch, not Docker

Running a container in the background

docker run -d

'd' for 'daemon'

Getting what we want

$ docker run -d aa0 apachectl -D FOREGROUND
86414f5548dcecb83d80dec4ec9351a7697b23d11664738f8668167a127ec70e

In a new terminal session:

$ docker ps
CONTAINER ID   IMAGE   COMMAND                CREATED          STATUS
86414f5548dc   aa0     "apachectl -D FOREGR   36 seconds ago   Up 34 seconds
(Scroll right.)

Stopping a Background Container

$ docker kill 864
864

$

Default Run Commands

CMD Directive

CMD [“apachectl”, “-D”, “FOREGROUND”]

Entrypoint

Actual executable run in container

docker run command passed as a parameter

Default Entrypoint

/bin/sh -c /your/run/command

May be overridden by your base image!

Specifying an entrypoint

CMD [“-D”, “FOREGROUND”]
ENTRYPOINT [“apachectl”]

Use CMD to pass parameters

Custom Entrypoints

The ENTRYPOINT can be any command, even a script!

Avoid too much automation in your containers

Our Dockerfile so far

FROM debian:latest
MAINTAINER your_email@example.com

RUN apt-get update
RUN apt-get install -yq apache2

CMD ["-D", "FOREGROUND"]
ENTRYPOINT ["apachectl"]

Updating an existing container

docker build your_container_id
$ docker build .

Sending build context to Docker daemon 2.048 kB
Sending build context to Docker daemon
Step 0 : FROM debian:latest
 ---> 60c52dbe9d91
Step 1 : MAINTAINER your_email@example.com
 ---> Using cache
 ---> 64b0d7e8eef9
Step 2 : RUN apt-get update
 ---> Using cache
 ---> eefa2fcb15e7
Step 3 : RUN apt-get install -y apache2
 ---> Using cache
 ---> aa084388ac30
Step 4 : CMD -D FOREGROUND
 ---> Running in 32b9f66cf1e3
 ---> 239ae06b7d68
Removing intermediate container 32b9f66cf1e3
Step 5 : ENTRYPOINT apachectl
 ---> Running in 6ac785739695
 ---> 72b3cac68438
Removing intermediate container 6ac785739695
Successfully built 72b3cac68438
(Scroll down for more.)

Look, Ma, no run command!

$ docker run -d 72b
988bf0c8cdfdf88c42e3673a65597f78693c07b14070cd535962b13b36884560

$ docker ps
CONTAINER ID        IMAGE         COMMAND                CREATED             STATUS
988bf0c8cdfd        72b           "apachectl -D FOREGR   4 seconds ago       Up 3 seconds

There's no place like 127.0.0.1

Opening Ports

EXPOSE port_number

Typically the third-to-last line in your Dockerfile

Getting the Machine IP

$ docker-machine ip
192.168.59.103

/etc/hosts

192.168.59.103	docker.dev

Machine IP may change on restart!

EXPOSE alone isn't Enough

Contains must be run with a port mapping!

Mapping Ports on run

docker run -d -p x:y your_image_id

x is the port on your machine

y is the port in the container

$ docker run -d -p 80:80 802
2f4126401306277841aa173794e17703caaa38c8a30709747e4bd0577ba3a44d

Container sets

Lets you build multiple containers at once

Included out-of-the-box, replaces Docker Fig

Descriptive, rather than instructive

3 YAML Rules

Indentation is significant!

Section headers end in a full colon (:)

List items start with a hyphen (-)

"Up"ing a container

docker-compose up -d

-d for daemon

$ cd /your/project/directory

$ docker-compose up -d
Creating dockerthingy_web_1

$ docker ps
CONTAINER ID   COMMAND                STATUS       NAMES
245fbb0bf255   "apachectl -D FOREGR   Up 18 secs   dockerthingy_web_1
$ cd /your/project/directory

$ docker-compose up -d
Creating dockerthingy_web_1

$ docker ps
CONTAINER ID   COMMAND                STATUS       NAMES
245fbb0bf255   "apachectl -D FOREGR   Up 18 secs   dockerthingy_web_1

Compose container names

parentDir_serviceName_index
/home/tess/dockerthingy
  ├── docker-compose.yml
  └── Dockerfile

List only your services

$ docker-compose ps
Name                 Command                   State   Ports
-----------------------------------------------------------------------
dockerthingy_web_1   apachectl -D FOREGROUND   Up      80/tcp, 9000/tcp

Overriding Dockerfiles

version: '2'
  services:
    web:
      build: .
      ports:
        - “80:80”

Compose always overrides Dockerfiles

Adding Containers from Hub

image: name_on_hub

Use it instead of build

Directories for container sets

<your_project_dir>
  ├── .docker
  │   ├── db
  │   └── web
  │       └── Dockerfile
  ├── docker-compose.yml
  └── docroot
      └── index.html

Just one possibility, do what works for you

Database container

version: '2'
services:
  web:
    build: .docker/web
    ports:
      - "80:80"
  db:
    image: mysql
      ports:
       - "3306:3306"

Stopping & deleting

$ docker-compose kill
$ docker-compose rm

Acts only on containers in docker-compose.yml.

Where to learn more

docs.docker.com/compose/compose-file

Files and Configuration

Copying in your Dockerfile

COPY source/file/path container/file/path

Directories and wildcards accepted

FROM debian:latest
MAINTAINER your_email@example.com

RUN apt-get update
RUN apt-get install -yq apache2

COPY docroot/index.html /var/www/html

CMD ["-D", "FOREGROUND"]
ENTRYPOINT ["apachectl"]

Updating container sets

docker-compose build optional_service_name
$ docker-compose up -d

$ curl docker.dev
<html>
<body>
<h1>Behold, my amazing page!</h1>
<p>Okay, yeah, it sucks. Sorry.</p>
</body>
</html>

Volumes

Mounts one or more directories into a container

- local/file/path:/path/on/container
version: '2'
services:
  web:
    build: .docker/web
    ports:
      - "80:80"
    volumes:
      - ./docroot:/var/www/html
  db:
    image: mysql
    ports:
      - "3306:3306"

Getting config into containers

Bake it into the container

Mount config files via volumes

Set environment variables

Setting environment variables

environment section

- ENV_VAR_NAME=VALUE
version: '2'
services:
  web:
    build: .docker/web
    ports:
      - "80:80"
    volumes:
      - ./docroot:/var/www/html
  db:
    image: mysql
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=drupal8
      - MYSQL_USER=drupal
      - MYSQL_PASSWORD=thisisawesome
(Scroll down.)

Now for Drupal!

Download, extract to docroot/

Create files directory and settings.php as normal

But there's a problem...

There's no PHP on our web container!

We could update it, but it'd be more to maintain.

But there's a problem...

There's no PHP on our web container!

We could update it, but it'd be more to maintain.

Replacing our web container

FROM php:apache
MAINTAINER your_email@example.com

RUN apt-get update && apt-get install -y \
libfreetype6-dev \
libjpeg62-turbo-dev \
libmcrypt-dev \
libpng12-dev \
libicu-dev

RUN docker-php-ext-install gd json intl pdo pdo_mysql mbstring opcache

Kill, build, up

You may need to docker-compose rm

Network connections created on build

Never get too attached!

Always be willing to jump containers

What about Drush?

Problems with local install

Different versions & configuration of PHP

Drush versions may be significant

There's a container for that

docker pull drush/drush

"Stealing" volumes

volumes_from mounts all volumes from another container

Volumes not unmounted from other containers

services:
  web:
    build: .docker/web
    ports:
      - "80:80"
    volumes:
      - ./docroot:/var/www/html
  db:
    image: mysql
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=drupal8
      - MYSQL_USER=drupal
      - MYSQL_PASSWORD=thisisawesome
  drush:
    image: drush/drush
    volumes_from:
      - web
(Scroll down.)

Running a container in a set

docker-compose run service_name

Beware! Creates a (tiny) new container each time

$ docker-compose run drush --root=/var/www/html --uri=http://web/ status
Drupal version                  :  8.1.3
Site URI                        :  http://web/
Database driver                 :  mysql
Database hostname               :  db
Database port                   :  3306
Database username               :  drupal
Database name                   :  drupal8
Drupal bootstrap                :  Successful
PHP OS                          :  Linux
Install profile                 :  standard
Drupal root                     :  /var/www/html
Drupal Settings File            :  sites/default/settings.php
Site path                       :  sites/default
File directory path             :  sites/default/files
Temporary file directory path   :  /tmp

Where to go from here

Persistent CLI container with multiple tools

Helper scripts to make things easy

Docker from Scratch, Part 6 on deninet.com

Ready-made container sets

Drude

Multi-project Drupal site development

github.com/blinkreaction/drude

A MAMP Replacement

Requires a system-wide one time setup

Includes the Drude Shell Helper (dsh)

Drude Advantages

Mac and Windows performance enhancements

Multiple simultaneous sites

Documentation

Dropwhale

Drop in D8 module development

github.com/socketwench/dropwhale

Dropwhale is for Modules

Self-contained in a .docker/ directory

Installs Drupal 8 dev, Drush, Console, XDebug

Dropwhale is Hackable

Lightweight containers, simple Dockerfiles

Helper scripts named for verbs (start, build, drush)

Others

Bowline

bradjonesllc/docker-drupal

Build your own!

Thank you!

socketwench.github.io/rideTheWhale

Ride the Whale! Docker for Drupalists socketwench.github.io/rideTheWhale