Docker for PHP
Developers
Chris Tankersley
@dragonmantank
php[tek] 2017
php[tek] 2017 1
php[tek] 2017
Secret Docker Wifi
3
●
SSID: phptek_docker
●
Password: phpdocker2017
●
NAS: 172.16.0.40
●
Username: admin
●
Password: secretpassword
php[tek] 2017
What Is Docker?
“Docker is an open platform for developers and sysadmins to build,
ship, and run distributed applications. Consisting of Docker Engine, a
portable, lightweight runtime and packaging tool, and Docker Hub, a
cloud service for sharing applications and automating workflows,
Docker enables apps to be quickly assembled from components and
eliminates the friction between development, QA, and production
environments.”
4
https://www.docker.com/whatisdocker/
php[tek] 2017
What is a Container?
5
php[tek] 2017
Normal Bare-Metal Server
6
CPU RAM HD Network
Operating System
nginx PHP DB
CPU RAM I/O
0
5
10
15
20
25
% Resources
php[tek] 2017
Normal Bare-Metal Server
7
CPU RAM HD Network
Operating System
nginx PHP DB
CPU RAM I/O
0
5
10
15
20
25
30
35
App Resources
% Resources
php[tek] 2017
Virtual Machines
8
CPU RAM HD Network
Operating System
nginx PHP DB
Operating System
nginx PHP DB
Operating System
Hypervisor
CPU RAM I/O
0
10
20
30
40
50
60
70
App Resources
% Resources
php[tek] 2017
Containers
9
CPU RAM HD Network
Operating System
nginxnginx PHP DB PHP DB
CPU RAM I/O
0
5
10
15
20
25
30
35
40
45
App Resources
% Resources
php[tek] 2017
Containers vs VMs
CPU RAM I/O
0
10
20
30
40
50
60
70
App Resources
% Resources
CPU RAM I/O
0
5
10
15
20
25
30
35
40
45
App Resources
% Resources
php[tek] 2017
Containers Are Not New
• LXC (Linux Containers)
• OpenVZ
• Systemd-nspawn
• BSD Jails
• Solaris Zones
• chroot
11
php[tek] 2017
Containers are just walled processes
12
Ubuntu Kernel
/
+ bin/
+ etc/
+ dev/
+ home/
+ usr/
+ var/
+ lib/
+ …
nginx
bash
/
+ bin/
+ etc/
+ dev/
+ home/
+ usr/
+ var/
+ lib/
+ …
php
php[tek] 2017
What is Docker?
13
php[tek] 2017
Docker is an Ecosystem
14
Docker Engine
php[tek] 2017
Docker is an Ecosystem
15
Docker ComposeDocker Machine Docker Swarm
php[tek] 2017
How does it work?
16
Uses a variety of existing
Container technologies
Server Containers
Hyper-V Containers xhyve Virtualization
php[tek] 2017
Sorry OSX < 10.10 and Windows < 10 Users
Docker Toolbox
17
php[tek] 2017
Let’s use Docker
18
php[tek] 2017
Running a container
• `docker run` will run a container
• This will not restart an existing container, just create a new one
• docker run [options] IMAGE [command] [arguments]
• [options ]modify the docker process for this container
• IMAGE is the image to use
• [command] is the command to run inside the container
• [arguments] are arguments for the command
19
php[tek] 2017
Running a simple shell
20
php[tek] 2017
Running a simple shell
21
php[tek] 2017
Running a simple shell
22
php[tek] 2017
What’s Going On?
23
Ubuntu Kernel
/
+ bin/
+ etc/
+ dev/
+ home/
+ usr/
+ var/
+ lib/
+ …
nginx
bash
/
+ bin/
+ etc/
+ dev/
+ home/
+ usr/
+ var/
+ lib/
+ …
php
php[tek] 2017
Running Two Webservers
24
php[tek] 2017
Running Two Webservers
25
php[tek] 2017
Running Two Webservers
26
php[tek] 2017
Running Two Webservers
27
php[tek] 2017
Running Two Webservers
28
php[tek] 2017
Running Two Webservers
29
php[tek] 2017
Running Two Webservers
30
php[tek] 2017
Running Two Webservers
31
php[tek] 2017
Some Notes
• All three containers are 100% self contained
• Docker containers share common ancestors, but keep their own files
• `docker run` parameters:
• --rm – Destroy a container once it exits
• -d – Run in the background (daemon mode)
• -i – Run in interactive mode
• --name – Give the container a name
• -p [local port]:[container port] – Forward the local port to the container port
32
php[tek] 2017
Volumes
33
php[tek] 2017
Modifying a running container
• `docker exec` can run a command inside of an existing container
• Use Volumes to share data
34
php[tek] 2017
Persistent Data with Volumes
• You can designate a volume with –v
• Create a named volume with `volume create`
• Volumes can be shared amongst containers
• Volumes can mount data from the host system
35
php[tek] 2017
Mounting from the host machine
36
php[tek] 2017
Mounting from the host machine
37
php[tek] 2017
Mounting from the host machine
38
php[tek] 2017
Mounting from the host machine
39
php[tek] 2017
Mounting from the host machine
40
php[tek] 2017
Mounting from the host isn’t perfect
• The container now has a window into your host machine
• Permissions can get screwy if you are modifying in the container
• Most things it creates will be root by default, and you probably aren’t root on
the host machine
• Host-mounted volumes are not portable at all
• OSX and Hyper-V VMs have limited pathings to mount
• OSX has poor I/O performance
41
php[tek] 2017
Named Data Volumes
• Creates a space that becomes persistent
• Can be mounted anywhere inside your images
• Have our app containers use the data volume to store data
• Use ‘editor containers’ to go in and modify data when needed
42
php[tek] 2017
vim Tutorial
• vim is a Modal text editor
• ESC will drop you back to default mode
• :new /opt/webconfig/default to create a new file
• In default mode, i will get us into interactive (edit) mode
• :w to save a file
• :q will quit
43
php[tek] 2017
Mounting Data Volumes
44
php[tek] 2017
Mounting Data Volumes
45
php[tek] 2017
Mounting Data Volumes
46
php[tek] 2017
Mounting Data Volumes
47
php[tek] 2017
Mounting Data Volumes
48
php[tek] 2017
Mounting Data Volumes
49
php[tek] 2017
Why go through the hassle?
• Data volumes are portable, depending on the driver
• Data volumes are safer
• Separates the app containers from data
• Production can use a data volume, dev can use a host volume
• Our app containers stay small
• Works directly with other tools
50
php[tek] 2017
Networking
51
php[tek] 2017
Networking
• Docker can create multiple network “pools”
• Each container gets an IP address
• Containers can be attached to multiple networks
• Docker network allow service discovery inside networks
52
php[tek] 2017
Legacy - Docker Links
• Legacy Links work with `--link`
• Only works on the legacy “bridge” network
• Doesn’t support service discovery
• Not worth it to use anymore
53
php[tek] 2017
Docker Networks
• Discreet IP pool for containers
• Containers can be added and removed to the network at whim
• Service discovery though ‘--network-alias’
• Can be set up to work across hosts
54
php[tek] 2017
Create a network
55
php[tek] 2017
Attach to a network
56
php[tek] 2017
Ping the web container
57
php[tek] 2017
Add another web and kill web1
58
php[tek] 2017
BREAK TIME! WOO!
59
php[tek] 2017
Other Helpful Commands
60
php[tek] 2017
Inspect a container
docker inspect [options] CONTAINER_NAME
• Returns a JSON string with data about the container
• Can also query
• docker inspect -f “{{ .NetworkSettings.IPAddress }}” web_server
• Really handy for scripting out things like reverse proxies
61
php[tek] 2017
Work with images
• docker pull IMAGE – Pulls down an image before using
• docker images – Lists all the images that are downloaded
• docker rmi IMAGE – Deletes an image if it’s not being used
62
php[tek] 2017
Containerizing An Application
63
php[tek] 2017
Our Goals
• Not change our workflow (much)
• Run PHP 7, Unit Tests, and webserver
• Deploy “easily”
64
php[tek] 2017
Just try and run it
docker run -d --name d4dapp 
-v C:dragoProjectsdockerfordevs-app:/var/www/ 
-p 8080:80
php:apache
65
php[tek] 2017 66
php[tek] 2017
Checking Logs
• Containers log to stdout/stderr
• Docker aggregates the logs
• Can be viewed with docker logs
67
php[tek] 2017
Oops
68
php[tek] 2017
Custom Images
• PHP images are pretty bare
• Lots of times need to install extensions
69
php[tek] 2017
Dockerfile
• Dockerfile is the configuration steps for an image
• Can be created from scratch, or based on another image
• Allows you to add files, create default volumes, ports, etc
• Can be used privately or pushed to Docker Hub
70
php[tek] 2017
docker/Dockerfile
FROM php:apache
RUN a2enmod rewrite
71
php[tek] 2017
Build it
docker build -t tag_name ./
• This runs through the Dockerfile and generates the image
• We can now use the tag name to run the image
72
php[tek] 2017
Build it
docker build -t d4dapp docker/
73
php[tek] 2017 74
php[tek] 2017
Use the new image
docker run -d --name d4dapp 
-v C:dragoProjectsdockerfordevs-app:/var/www/ 
-p 8080:80
d4dapp
75
php[tek] 2017
Use the new image
76
php[tek] 2017
Slightly better
77
php[tek] 2017
Install Dependencies
78
php[tek] 2017
Running Composer
docker run --rm 
-v c:/Users/drago/.composer:/root/.composer 
-v c:/Users/drago/Projects/workshop:/app 
-v c:/Users/drago/.ssh:/root/.ssh 
composer/composer 
install
79
php[tek] 2017
Better!
80
php[tek] 2017
Look at queues!
81
php[tek] 2017
docker/Dockerfile
FROM php:apache
RUN a2enmod rewrite
&& docker-php-ext-install pdo_mysql
82
php[tek] 2017
Rebuild the image
docker build -t d4dapp docker/
83
php[tek] 2017
Rebuild the container
$ docker rm -f d4dapp
$ docker run -d --name d4dapp 
-v C:dragoProjectsdockerfordevs-app:/var/www/ 
-p 8080:80
d4dapp
84
php[tek] 2017
Progress!
85
php[tek] 2017
Docker Compose
86
php[tek] 2017
What is Docker Compose?
• Multi-container orchestration
• A single config file holds all of your container info
• Works with Docker Swarm and a few other tools, like Rancher
87
php[tek] 2017
Sample docker-compose.yml
version: '2'
volumes:
mysqldata:
driver: local
services:
d4dapp:
build: ./docker/
volumes:
- ./:/var/www/
ports:
- 8080:80
mysqlserver:
image: mysql
environment:
MYSQL_DATABASE: dockerfordevs
MYSQL_ROOT_PASSWORD: 's3curep@assword'
volumes:
- mysqldata:/var/lib/mysql
88
php[tek] 2017
No longer use docker run
$ docker rm –f d4dapp
$ docker-compose up -d
89
php[tek] 2017
Now we have 2 containers
90
php[tek] 2017
Config for DB now points to the service
name
91
<?php
return [
'debug' => true,
'config_cache_enabled' => false,
'db' => [
'driver' => 'Pdo_Mysql',
'hostname' => 'mysqlserver',
'port' => '3306',
'database' => 'dockerfordevs',
'user' => 'root',
'password' => 's3curep@assword',
],
];
php[tek] 2017
Yay!
92
php[tek] 2017
Install our DB Migration Software
docker run --rm 
-v c:/Users/drago/.composer:/root/.composer 
-v c:/Users/drago/Projects/workshop:/app 
-v c:/Users/drago/.ssh:/root/.ssh 
composer/composer 
require robmorgan/phinx
93
php[tek] 2017
Set up phinx
docker run --rm 
-v C:UsersdragoProjectsdockerfordevs-app:/app 
-w /app 
php:cli php vendor/bin/phinx init
94
php[tek] 2017
Run the migration
docker run --rm 
-v C:UsersdragoProjectsdockerfordevs-app:/app 
-w /app 
--network dockerfordevsapp_default 
php:cli php vendor/bin/phinx migrate
95
php[tek] 2017
Oops
96
php[tek] 2017
Let’s use the existing container
docker-compose run --rm 
-v C:UsersdragoProjectsdockerfordevs-app:/app 
-w /app 
d4dapp php vendor/bin/phinx migrate
97
php[tek] 2017
Good…
98
php[tek] 2017
It Lives!
99
php[tek] 2017
Unit Testing
docker run --rm 
-v C:UsersdragoProjectsdockerfordevs-app:/app 
-w /app 
d4dapp php vendor/bin/phpunit -c .
100
php[tek] 2017
Running the tests
php[tek] 2017
Build a service
service:
testrunner:
build: ./docker/
volumes:
- ./:/app
working_dir: /app
command: vendor/bin/phpunit -c .
102
php[tek] 2017
Run the tests with the service
docker-compose run --rm testrunner
103
php[tek] 2017
Running the tests
Docker Machine
ZendCon, October 2016 105
What is Docker Machine?
• A provisioning tool that is used to set up a box with Docker
• Used in Docker Toolbox to create the VM
• Supports:
• EC2
• Azure
• Digital Ocean
• Hyper-V
• OpenStack
• Virtualbox
• VMWare
php[tek] 2017 106
Why use it?
• Makes it very easy to spin up new boxes
• Docker Machine handles all of the dirty stuff for you
• Docker Toolbox users are already using it
• Integrates with Docker Swarm
• It is not necessarily portable
php[tek] 2017 107
Let’s make a machine!
php[tek] 2017 108
Let’s Connect!
php[tek] 2017 109
php[tek] 2017
BREAK TIME AGAIN! WOO!
110
Production Considerations
php[tek] 2017 111
12 Factor Applications
php[tek] 2017 112
1. Codebase
One codebase tracked in revision control, many deploys
php[tek] 2017 113
Repo Tips
• Keep everything in your repository
• Tag releases
• Never move tags
php[tek] 2017 114
2. Dependencies
Explicitly declare and isolate dependencies
php[tek] 2017 115
Dependencies
• Commit both composer.json and composer.lock files
• Commit Dockerfiles to the same repo as the codebase
php[tek] 2017 116
3. Config
Store config in the environment
php[tek] 2017 117
Configuration
• Anything that is environment specific should move to environment
vars
• Makes it much easier to build and deploy code
• Code cares less what external services it is talking to
php[tek] 2017 118
Use Environment Vars
• Can specify them one-by-one
– docker run ­e VAR_NAME=value
• Can specify a file
– docker run ­­env­file=filename
• Can specify in docker-compose.yml
php[tek] 2017 119
4. Backing Services
Treat backing services as attached resources
php[tek] 2017 120
Everything is “external”
• Never talk to local sockets
• Don’t make a determination between “locally” hosted and third party
• Easier to switch environments
• Easier to scale up
php[tek] 2017 121
5. Build, release, run
Strictly separate build and run stages
php[tek] 2017 122
The Workflow
• Build step installs dependencies, compiles files, and generates a Build
Artifact that can be deployed
– Does not contain any deployment configuration
• Release step pushes a Build Artifact into an environment
– Runs DB migrations, anything needed to happen before running
• Run step runs the app fully in the environment
php[tek] 2017 123
Tips
• Build Artifact can be an image
• Builds should be completely reproducible
• Release always take a build artifact, never directly from the repo
• Tag all your builds
• Track all your releases
php[tek] 2017 124
Build Step - Start Small
• Build your application
• Run composer
• Run npm/bower
• Build JS/CSS
• Use the compiled output to build an image with docker build
• Push full image to private registry
php[tek] 2017 125
docker build
• Additional options to look at
• -f, --file – Specify a different filename for the Dockerfile
• --no-cache – Don’t use a cached layer
• --pull – Always pull a new version of the image
php[tek] 2017 126
Sample usage
docker build 
--no-cache 
–f docker/php/phpserver.dockerfile 
–t prod_php /opt/builds/20161010
php[tek] 2017 127
phpserver.dockerfile
FROM php:fpm
RUN docker-php-ext-install pdo pdo_mysql
COPY ./ /var/www
php[tek] 2017 128
6. Processes
Execute the app as one or more stateless processes
php[tek] 2017 129
Built Into Docker
• One Process per container
• Allows tools to scale just what needs to be scaled
• Allows images to be swapped out as needed
php[tek] 2017 130
7. Port Binding
Export services via port binding
php[tek] 2017 131
Built Into Docker (Again)
• Each container gets its own IP and exposes its own ports
• Processes should already be talking over a network
• Can work with service locators that are port-based
php[tek] 2017 132
8. Concurrency
Scale out via the process model
php[tek] 2017 133
How well does your app handle scaling?
php[tek] 2017 134
Built Into Docker (Again) (Again)
• One Process per container
• Scale up just the container that is needed
• App should not care how many instances of each service are running
php[tek] 2017 135
9. Disposability
Maximize robustness with fast startup and graceful shutdown
php[tek] 2017 136
Signals
• Docker starts containers fairly quickly
• Applications should gracefully shut down, not just die
• Docker sends a SIGTERM when shutting down a container
• Your CLI apps may need to handle SIGTERM properly
– Cal Evans, “Signalling PHP”
php[tek] 2017 137
10. Dev/prod Parity
Keep development, staging, and production as similar as possible
php[tek] 2017 138
11. Logs
Treat logs as event streams
php[tek] 2017 139
Logging in Docker
• Various logging options built in
– JSON file (default)
– Fluentd
– Syslog
– Journald
– Gelf
– Splunk
– Aws
– Etwlogs
– Gcplogs php[tek] 2017 140
Push logs remotely
• When possible, push Docker logs to a remote service
– Container logs only exist while the container exists
• Allows logs to be viewed in a single place
• No need to get into actual servers
• Can host yourself, or pay for a SaaS
• ELK stack is very popular
– Docker uses fluentd instead
php[tek] 2017 141
Setting up fluentd
services:
  d4dapp:
    build: ./docker/d4dapp
    depends_on:
      ­ fluentd
    volumes:
      ­ ./:/var/www/
    ports:
      ­ 8080:80
    logging:
      driver: "fluentd"
      options:
        fluentd­address: 127.0.0.1:24224
        tag: apache.access
php[tek] 2017 142
Setting up fluentd
services:
  fluentd:
    build: docker/fluentd
    depends_on:
      ­ elasticsearch
    volumes:
      ­ ./docker/fluentd/fluent.conf:/fluentd/etc/fluent.conf
    ports:
      ­ 24224:24224
      ­ 24224:24224/udp
php[tek] 2017 143
Setting up fluentd
FROM fluent/fluentd
RUN ["gem", "install", "fluent­plugin­elasticsearch", "­­no­rdoc", "­­no­ri", "­­version", 
"1.9.2"]
php[tek] 2017 144
Setting up fluentd
[See Config File in repo]
php[tek] 2017 145
Setting up ElasticSearch and Kibana
services:
  elasticsearch:
    image: elasticsearch
    expose:
      ­ 9200
    ports:
      ­ 9200:9200
  kibana:
    image: kibana
    depends_on:
      ­ elasticsearch
    ports:
      ­ 5601:5601
php[tek] 2017 146
Viewing Logs
php[tek] 2017 147
Logging notes
• docker logs does not work with external logging, only JSON
• This example can be cleaned up a bit
• Kibana syntax can be a bit odd to work with
php[tek] 2017 148
12. Admin Processes
Run admin/management tasks as one-off processes
php[tek] 2017 149
php[tek] 2017
BREAK TIME AGAIN! WOO!
150
Deployment using Docker Compose
php[tek] 2017 151
Very Good for Small Deployments
• Can be used to augment your dev environment
• Works well with Docker Machine
php[tek] 2017 152
Create a machine
docker-machine create --driver digitalocean 
--digital-ocean-access-token [token] 
sunshinephp2017
php[tek] 2017 153
SunshinePHP 2017 154
Switch to the remote node
• Run docker-machine env sunshinephp2017
& "C:Program
FilesDockerDockerResourcesbindocker-
machine.exe" env sunshinephp2017 | Invoke-Expression
php[tek] 2017 155
Set up docker-compose
• Docker Compose allows multiple config files with -f
• Have a base docker-compose.yml for Production
• Add a secondary one for Development
php[tek] 2017 156
version: '2'
volumes:
mysqldata:
driver: local
services:
nginx:
build:
context: ./
dockerfile: ./nginx.dockerfile
ports:
- 80:80
- 443:443
phpserver:
build:
context: ./
dockerfile: ./phpserver.dockerfile
working_dir: /var/www/public
mysqlserver:
image: mysql
environment:
[redacted]
volumes:
- mysqldata:/var/lib/mysql
php[tek] 2017 157
docker-compose.yml
version: '2'
volumes:
mysqldata:
driver: local
services:
nginx:
image: nginx
volumes:
- ./output_dev:/var/www/public:ro
- ./app/nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./ssl:/etc/nginx/ssl/
phpserver:
build:
context: ./
dockerfile: ./phpserver.dockerfile
working_dir: /var/www/public
volumes:
- ./app:/var/www/
- ./vendor:/var/www/vendor
mysqlserver:
image: mysql
environment:
[redacted]
volumes:
- mysqldata:/var/lib/mysql
php[tek] 2017 158
docker-compose.dev.yml
When doing development
docker-compose 
–f docker-compose.yml 
–f docker-compose.dev.yml 
up -d
php[tek] 2017 159
When doing a deployment
docker-compose up -d
php[tek] 2017 160
Other Alternative – Variable Substitution
• Docker Compose allows variable substitution inside the file
• Wrap variables in ${}
• image: ${DEPLOY_VERSION}_php
php[tek] 2017 161
When doing a deployment
docker-compose up -d
php[tek] 2017 162
Docker Swarm
php[tek] 2017 163
What is Swarm?
• Docker-supplied clustering
• Define a series of services that can be deployed
php[tek] 2017 164
Setup
php[tek] 2017 165
Manager
Node 2Node 1
Register a node as a Manager
docker swarm init ----advertise--addr 172.16.0.245
php[tek] 2017 166
Add nodes
docker swarm join --token [token] 172.16.0.245:2377
php[tek] 2017 167
docker-compose.yml
version: '3'
services:
phpserver:
image: php:apache
ports:
- 80:80
php[tek] 2017 168
Deploy the stack
$ docker stack deploy -c docker-compose.yml myapp
Creating network myapp_default
Creating service myapp_phpserver
php[tek] 2017 169
docker-compose.yml
version: '3'
services:
phpserver:
image: php:apache
ports:
- 80:80
php[tek] 2017 170
Deploy the stack
$ docker stack deploy -c docker-compose.yml myapp
Creating network myapp_default
Creating service myapp_phpserver
php[tek] 2017 171
docker-compose.yml
version: '3'
services:
phpserver:
image: php:apache
ports:
- 80:80
php[tek] 2017 172
docker-compose.yml
version: '3'
services:
phpserver:
image: php:apache
ports:
- 80:80
php[tek] 2017 173
docker stack ps myapp
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
ERROR PORTS
72mud5othsjf myapp_phpserver.1 nginx:latest node2 Running Running 8 minutes ago
wsf3m32u9vcr _ myapp_phpserver.1 php:apache manager1 Shutdown Shutdown 11 minutes ago
xf3wyh289bec myapp_phpserver.2 nginx:latest node1 Running Preparing 20 seconds ago
fehf1vdx4m0r myapp_phpserver.3 nginx:latest manager1 Running Preparing 20 seconds ago
pwnq65e6w7ew myapp_phpserver.4 nginx:latest manager1 Running Preparing 20 seconds ago
roxtanjughq8 myapp_phpserver.5 nginx:latest node2 Running Running 20 seconds ago
php[tek] 2017 174
php[tek] 2017
Thank You!
• Software Engineer for InQuest
• Author of “Docker for Developers”
• https://leanpub.com/dockerfordevs
• Co-Host of “Jerks Talk Games”
• http://jerkstalkgames
• http://ctankersley.com
• chris@ctankersley.com
• @dragonmantank
175

Docker for Developers - php[tek] 2017

Editor's Notes

  • #2 &amp;lt;number&amp;gt;