socketwench.github.io/rideTheWhale
No sandboxing
...stays on your system forever
Files, databases, config, binaries
Takes time, disipline to find and remove
Standard commands for delete
No matter the project or tech stack
Dev environment isn't repeatable
Humans are fallible, forgettable, fried
Docker containers repeatable by design
Built from a few simple text files
Dev environment isn't sharable
That's what bespoke environments are
On-boarding, troubleshooting, and recovery time
...should apply to servers too
Download from the internet
Add build files to your repo
Docker is free and open source!
github.com/docker/dockerDocker 1.12+
Includes Virtualbox
Embedded in Docker 1.12+
Virtualbox for earlier versions
No VM needed, uses host's kernel
Use your distro's package manager to install
You rarely build containers yourself.
Online repository of ready-to-use containers
hub.docker.comTypically created by project maintainers
Apache, PHP, MySQL, and many Linux Distros
Great for demos...not great for development.
docker pull image_name
image_name is the name on Docker Hub
$ docker pull debian
Good "base image" for web projects
image_name:tag
Used for different versions or varients
Default to latest
Unique per container, check page on Hub
docker run image_name
$ docker run -i -t debian /bin/bash
root@ffc6d3de3d27:/#
root@ffc6d3de3d27:/# uname -a
Linux ffc6d3de3d27 4.4.15-moby #1 SMP Thu Jul 28 21:30:50 UTC 2016 x86_64 GNU/Linux
Use exit
to quit
$ docker run -d debian /bin/bash -c "while true; do sleep 1; done"
b4d333ad4d0f9c68249a091fdde53d25a09cbc785c5aba2a697b3a3ea04da778
$
Think "detach"
Unique identifier for that container instance
Use the first few characters as a shorthand
docker ps
Think "process"
docker exec container_id /command/to/run
exec
$ docker exec -i -t b4d /bin/bash
root@b4d333ad4d0f:/#
Use exit
to quit
docker kill container_id
Not exactly true, but close
One process = one container
Manage "container sets" with one file
Descriptive, not imperitive
Save in the root directory of your project!
version: '2'
services:
web:
image: php:apache
db:
image: mariadb
docker-compose up -d
docker-compose.yml must be in current directory!
$ docker-compose ps
Name Command State Ports
------------------------------------------------------------------
ridethewhale_db_1 docker-entrypoint.sh mysqld Exit 1
ridethewhale_web_1 apache2-foreground Up 80/tcp
parentDirName_serviceName_index
/home/tess/projects/rideTheWhale
├── docker-compose.yml
└── docroot
Needed to access the container from the host
"portOnHost:portInContainer"
version: '2'
services:
web:
image: php:apache
ports:
- "80:80"
db:
image: mariadb
$ docker-compose kill
$ docker-compose up -d
Creates a persistent directory inside a container
Mounts a directory or file in the container
In the volumes
section...
path/on/host:path/in/container
/home/tess/projects/rideTheWhale
├── docker-compose.yml
└── docroot
└── index.html
version: '2'
services:
web:
image: php:apache
ports:
- "80:80"
volumes:
- ./docroot:/var/www/html
db:
image: mariadb
Use relative paths to Compose file
Use absolute paths in the container
Bake it into the container
Mount configuration files
Use environment variables
In the environment
section...
VAR_NAME=VALUE
Check Hub page for variable names and use
version: '2'
services:
web:
image: php:apache
ports:
- "80:80"
volumes:
- ./docroot:/var/www/html
db:
image: mariadb
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=your_database_name
- MYSQL_USER=your_database_user
- MYSQL_PASSWORD=your_database_password
(Scroll down.)
Private network automatically created per set
Containers preconfigured with the right port open
version: '2'
services:
web:
image: php:apache
ports:
- "80:80"
volumes:
- ./docroot:/var/www/html
db:
image: mariadb
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=your_database_name
- MYSQL_USER=your_database_user
- MYSQL_PASSWORD=your_database_password
(Scroll down.)
$ docker-compose kill && docker-compose up -d
$ mysql -u your_database_user -p -h 127.0.0.1 -P 3306
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 2
Server version: 10.1.16-MariaDB-1~jessie mariadb.org binary distribution
Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]>
Download to volume-mounted directory
Create, set permissions for files directory
Create services.yml and settings.php as normal
Container "source code"
Always named Dockerfile
Imperitive, not descriptive
/home/tess/projects/rideTheWhale
├── .docker
│ ├── web
│ │ └── Dockerfile
│ └── db
├── docker-compose.yml
└── docroot
└── index.html
Common, but only a suggestion
FROM php:apache
MAINTAINER tess@deninet.com
All containers are based on another container
scratch
containerStarting point for all containers
Just an empty *.tar.gz file
Base Linux install (Debian, CentOS, Arch, etc.)
Whatever image you start with
INSTALL
directiveDocker lets the container base image decide!
Debian uses apt-get
CentOS uses yum
Some containers provide special install scripts!
RUN
directiveRUN /command/to/run
FROM php:apache
MAINTAINER tess@deninet.com
RUN apt-get update && apt-get install -yq \
libfreetype6-dev \
libjpeg62-turbo-dev \
libmcrypt-dev \
libpng12-dev \
libicu-dev
RUN docker-php-ext-install gd json intl pdo pdo_mysql mbstring
build: path/to/Dockerfile
Use instead of image
version: '2'
services:
web:
build: .docker/web
ports:
- "80:80"
volumes:
- ./docroot:/var/www/html
db:
image: mariadb
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=your_database_name
- MYSQL_USER=your_database_user
- MYSQL_PASSWORD=your_database_password
(Scroll down.)
We're just adding more to php:apache
docker-compose build optionalServiceName
Must be run before up
!
Copy and paste volume
sections
Used named volumes (Compose 2+)
Use volumes_from
version: '2'
services:
web:
build: .docker/web
ports:
- "80:80"
volumes:
- ./docroot:/var/www/html
drush:
image: drush/drush
volumes_from:
- web
db:
image: mariadb
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=your_database_name
- MYSQL_USER=your_database_user
- MYSQL_PASSWORD=your_database_password
(Scroll down.)
$ docker-compose ps
Name Command State Ports
------------------------------------------------------------------------------------
ridethewhale_db_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp
ridethewhale_drush_1 drush Exit 0
ridethewhale_web_1 apache2-foreground Up 0.0.0.0:80->80/tcp
$ docker-compose ps
Name Command State Ports
------------------------------------------------------------------------------------
ridethewhale_db_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp
ridethewhale_drush_1 drush Exit 0
ridethewhale_web_1 apache2-foreground Up 0.0.0.0:80->80/tcp
Executes a command, then quits
Must be run interactively!
docker-compose run drush drushCommand
Creates a new container instace each time!
...and stays up as long as its process runs
$ docker-compose run drush si \
--uri=http://web \
--root=/var/www/html \
--db-url=mysql://your_database_user:your_database_password@db/your_database_name -y
You are about to DROP all tables in your 'your_database_name' database. Do you want to continue? (y/n): y
Starting Drupal installation. This takes a while. Consider using the --notify global option. [ok]
Installation complete. User name: admin User password: jvBdZnmeMt [ok]
Congratulations, you installed Drupal!
$
No really, don't use them.
They quickly become a big problem.
Create represtative content with UUID Features
Works better with Behat and automation
Use a client on the host to load it
Best for small databases
$ docker-compose exec db /usr/bin/mysql \
-u your_database_user \
-p \
-C your_database_name < /path/to/your_dump.sql
mariadb
container needs the mysql
command!
Increase max_allowed_packet
Import from inside the DB container with a volume
$ docker-compose kill
$ docker-compose rm
Not deleted by default!
$ docker-compose rm -v
Snapshot of a container
Created with docker build
...
...or downloaded from Hub
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ridethewhale_web latest e716ac87fa15 17 minutes ago 496.4 MB
mariadb latest 9a0138c02438 6 days ago 391.6 MB
drush/drush latest e88ee37fadfd 7 weeks ago 691.9 MB
(Scroll right.)
$ docker rmi $(docker images -q)
$ docker kill $(docker ps -q)
$ docker rm -v $(docker ps -q -a)
$ docker rmi $(docker images -q)
"It's the only way to be sure."
"Reset to factory defaults" in Docker prefs
Use Supervisor for an always-running container
Drupal Console, SASS, Grunt, etc.
Use docker-compose exec
for more speed
Lots of options to choose from!
Find the way that fits your style and project
Bare-bones Drupal containers
Drop-in D8 module dev environment
Pulls, installs Drupal dev internally
Full-featured: Redis, Memcached, Solr
Per-project is OK!
It's free and anyone can do it
Hub builds your containers, so you don't have to locally
Drupal Association
TEN7
Marc Drummond, Paul Mitchum
and others
Read the blog series:
deninet.com/tags/docker-scratch
socketwench.github.io/rideTheWhale