Drupalcamp London 2018
Dockerize your Drupal environments
Fran García-Linares
March 2018
About me
fran.garcia@amazeelabs.com
Fran García-Linares, Drupal web developer
fjgarlin@gmail.com
https://www.drupal.org/u/fjgarlin
We are
HIRING
Agenda (45min)
1
2
3
4
5
Intro (5 min)
Docker set up (20~25 min)
Real-life example (5 min)
Q & A
Pros and cons (5 min)
Intro
Environments
Depends on:
- Size of project
- Size of company
- Level of knowledge/skills of the team
Options:
- Production
- Development > production
- Local > development > production
- Local > development > staging > production
Options for "local"
- (M/W/L)AMP
- Vagrant
- Cloud + SFTP
- Valet (laravel)
- PHP built-in server
- ...
-
Docker
- amazee.io
- docker4drupal
- Lagoon
- Custom docker images
With docker you can get an identical set up to the production* server... i-d-e-n-t-i-c-a-l
* docker in production? why not :-)
Do I need to know docker to use docker?
My day to day
I work in maintenance and support... how does docker help me with this?
- Quick set up / Task switching
- Normal day: 3~6 different projects
- Normal week: 15~20 different projects
- Issue / bug replication
- Exact set up
- Exact content
- Exact files
- ...
- Computer resources
- Common set up across the team
Docker set up
useful command aliases
- dsql
- dfiles
- dssh
- dsshroot
- dup
- dstop
- ddown
- dps
function dsql() {
drush sql-sync $1 default
}
function dfiles() {
drush rsync $1:%files default:%files
}
alias ddown='docker-compose down'
alias dps='docker ps --format '\''table {{.Names}}\t{{.Status}}'\'''
alias dssh='docker-compose exec --user drupal drupal bash'
alias dsshroot='docker-compose exec --user root drupal bash'
alias dstop='docker-compose stop'
alias dstopall='docker stop $(docker ps -a -q)'
alias dup='docker-compose up -d'
Docker set up
important drush files
aliases.drushrc.php
drushrc.php
settings.php
// Append this if the server variables are set up in all environments.
// You can safely commit settings.php to git.
if(getenv('ENV_DB_NAME')) {
$databases['default']['default'] = array(
'driver' => 'mysql',
'database' => getenv('ENV_DB_NAME'),
'username' => getenv('ENV_DB_USERNAME'),
'password' => getenv('ENV_DB_PASSWORD'),
'host' => getenv('ENV_DB_HOST'),
'port' => getenv('ENV_DB_PORT'),
'prefix' => '',
);
}
### amazee.io Base URL
if (getenv('ENV_BASE_URL')) {
$base_url = getenv('ENV_BASE_URL');
}
if (getenv('ENV_SITE_URL')) {
$options['uri'] = 'http://' . getenv('ENV_SITE_URL');
}
$command_specific['rsync'] = array('verbose' => TRUE);
$aliases['local'] = array(
'root' => '/path/to/drupal/root',
'uri' => 'yoursite.localhost',
'path-aliases' => array(
'%dump-dir' => '/tmp',
),
);
$aliases['dev'] = array (
'uri' => 'yoursite.dev',
'root' => '/path/to/drupal/root',
'remote-user' => 'ssh-username',
'remote-host' => 'ssh-host',
'ssh-options' => '-p 2222', // To change the default port on remote server
'path-aliases' => array(
'%dump-dir' => '/tmp',
),
'source-command-specific' => array (
...
),
// No need to modify the following settings
'command-specific' => array (
...
),
);
$aliases['prod'] = array (
// This is the full site alias name from which we inherit its config.
'parent' => '@yoursite.dev',
'uri' => 'yoursite.com',
'root' => '/path/to/drupal/root',
'remote-user' => 'ssh-user',
'remote-host' => 'ssh-host',
);
Docker set up
syncing DB and persistence in docker
Syncing DB
$ drush sa
@local
@dev
@prod
$ dsql @prod
$ drush sql-sync @prod @local // NOT THE OTHER WAY AROUND!! dsql...
DB persistence in docker... aka volumes
volumes:
- .:/var/www/drupal/public_html
ports:
- "3306"
Add these lines (or similar) somewhere in your docker-compose.yml file:
Docker set up
syncing files or use stage_file_proxy
Syncing files
$ drush sa
@local
@dev
@prod
$ dfiles @prod
$ drush rsync @prod:%files @local:%files // NOT THE OTHER WAY AROUND!! dfiles...
stage_file_proxy
// settings.php
$conf['stage_file_proxy_origin'] = "http://prodsite.url";
Enable the module on local, make sure that sites/default/files is writable and then:
Docker set up
syncing SOLR (Search API)
Syncing SOLR configuration
// docker-compose.yml
...
services:
...
solr:
image: solr
ports:
- "8983:8983"
volumes:
- ./docker/solr/conf:/etc/solr/conf/drupal/conf
...
Then re-index, that's it!
Docker set up
other integrations
redis
// docker-compose.yml
...
services:
drupal:
...
environment:
...
ENV_REDIS_HOST: redis
ENV_REDIS_PORT: '6379'
...
links:
- redis
redis:
image: redis:alpine
network_mode: bridge
emails
// docker-compose.yml
...
services:
...
mailhog:
image: mailhog/mailhog
...
varnish
memcached
node
blackfire
...
Docker set up
settings
settings.php (committed to git)
// Base URL
if (getenv('AMAZEEIO_SITE_URL')) {
$base_url = 'http://' . getenv('AMAZEEIO_SITE_URL');
}
// AMAZEE.IO Redis settings
if (getenv('AMAZEEIO_REDIS_HOST') && getenv('AMAZEEIO_REDIS_PORT')) {
$conf['redis_client_interface'] = 'Predis';
$conf['redis_client_host'] = getenv('AMAZEEIO_REDIS_HOST');
$conf['redis_client_port'] = getenv('AMAZEEIO_REDIS_PORT');
}
// AMAZEE.IO Database connection
if(getenv('AMAZEEIO_DB_NAME')){
$databases['default']['default'] = array(
...
);
}
// AMAZEE.IO SOLR connection
if(getenv('AMAZEEIO_SOLR_HOST') && getenv('AMAZEEIO_SOLR_PORT')){
// Override search API server settings fetched from default configuration.
$conf['search_api_override_mode'] = 'load';
$conf['search_api_override_servers'] = array(
'solr' => array(
'name' => 'Amazee.io Solr - Environment:' . getenv('AMAZEEIO_SITE_ENVIRONMENT'),
'options' => array(
'host' => getenv('AMAZEEIO_SOLR_HOST'),
'port' => getenv('AMAZEEIO_SOLR_PORT'),
'path' => '/solr/'.getenv('AMAZEEIO_SITENAME').'/',
'excerpt' => 0,
...
'http_method' => 'POST',
),
),
);
}
settings.all.php
// Settings for all environments.
if (file_exists(__DIR__ . '/settings.all.php')) {
include __DIR__ . '/settings.all.php';
}
// Environment specific settings files.
if(getenv('AMAZEEIO_SITE_ENVIRONMENT')){
if (file_exists(__DIR__ . '/settings.' . getenv('AMAZEEIO_SITE_ENVIRONMENT') . '.php')) {
include __DIR__ . '/settings.' . getenv('AMAZEEIO_SITE_ENVIRONMENT') . '.php';
}
}
// Last: server's specific settings files.
// NOTE: Include this in .gitignore
if (file_exists(__DIR__ . '/settings.local.php')) {
include __DIR__ . '/settings.local.php';
}
settings.development.php
// Error messages to display: All messages
$conf['error_level'] = 2;
// Google Analytics.
$conf['googleanalytics_account'] = 'UA-XXXXXXXX-Y';
//Theme Debug
$conf['theme_debug'] = TRUE;
$conf['preprocess_css'] = FALSE;
$conf['preprocess_js'] = FALSE;
$conf['cache'] = 0;
$conf['cache_lifetime'] = 0;
// Stage File Proxy, so that files are loaded from Production
$conf['stage_file_proxy_origin'] = "http://prod.url";
// Test keys for other services
// ie: Salesforce, Mandrill, etc.
Docker set up
D7 vs D8 (CMI)
D7
Typical workflow:
- git clone repo folder
- cd folder
- git submodule update --init
- git submodules sync
- dup
- dssh
- dsql @prod
- mkdir sites/default/files
- chmod -R 777 sites/default/files
- drush -y en stage_file_proxy
- drush cc all
- drush uli
D8
Typical workflow:
- git clone repo folder
- cd folder
- dup
- dssh
- composer install
- dsql @prod OR drush -y cim
- dfiles @prod
- drush cr
- drush uli
We'll see a "live" demo in 2 slides :-)
Docker set up
docker-compose.yml
This file is almost all you need to work with a dockerized environment
version: '2'
services:
drupal:
# Choose the URL and hostname for this Docker Container
hostname: &hostname changeme.docker.amazee.io
environment:
WEBROOT: web
APC: 1
VIRTUAL_HOST: *hostname
image: amazeeio/drupal:php70-basic
volumes:
- .:/var/www/drupal/public_html
volumes_from:
- container:amazeeio-ssh-agent
container_name: *hostname
network_mode: bridge
ports:
- "3306"
AMAZEE.IO
version: '2'
services:
cli:
build:
context: .
dockerfile: Dockerfile.builder
image: builder
...
nginx:
networks:
- amazeeio-network
- default
build:
context: .
dockerfile: Dockerfile.nginx
...
php:
build:
context: .
dockerfile: Dockerfile.php
...
mariadb:
image: amazeeio/centos7-mariadb10-drupal
labels:
lagoon.type: mariadb
redis:
image: amazeeio/redis
labels:
lagoon.type: redis
solr:
image: amazeeio/solr:6.6-drupal
labels:
lagoon.type: solr
ports:
- "8983"
varnish:
image: amazeeio/varnish-drupal
networks:
- amazeeio-network
- default
...
networks:
amazeeio-network:
external: true
LAGOON
version: "2"
services:
mariadb:
image: wodby/mariadb:10.2-3.0.2
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: drupal
...
php:
image: wodby/drupal:8-7.1-3.3.2
# image: wodby/drupal:7-7.0-3.3.2
environment:
PHP_SENDMAIL_PATH: /usr/sbin/sendmail -t -i -S mailhog:1025
DB_HOST: mariadb
DB_USER: drupal
...
nginx:
image: wodby/drupal-nginx:8-1.13-3.0.2
# image: wodby/drupal-nginx:7-1.13-3.0.2
depends_on:
- php
environment:
...
volumes:
- codebase:/var/www/html
# apache:
# varnish:
# redis:
# pma:
# solr:
mailhog:
# nodejs:
# node:
# memcached:
# rsyslog:
# blackfire:
traefik:
...
volumes:
codebase:
DOCKER4DRUPAL
Real-life example
demo time
D7
Typical workflow (from scratch):
- git clone repo folder
- cd folder
- git submodule update --init
- git submodules sync
- dup
- dssh
- dsql @prod
- mkdir sites/default/files
- chmod -R 777 sites/default/files
- drush -y en stage_file_proxy
- drush cc all
- drush uli
Pros & Cons
Pros
Cons
- Exact copy of prod
- Really fast
- Computer resources
- Multiple containers won't require too much
- Portable
- ...
- Initial configuration (easier than alternatives)
-
Volatility?
- Volumes
- Redundancy
- Learning?
Docker in production for Drupal...
a much more advanced talk: https://www.youtube.com/watch?v=3RnZPrjvoqo
Q & A
https://slides.com/fjgarlin
Dockerize your Drupal environments // Drupalcamp London 2018
By Fran García-Linares
Dockerize your Drupal environments // Drupalcamp London 2018
- 2,854