Hands on Docker - Launch your own LEMP or LAMP stack - SunshinePHP

@danaluther
Hands on Docker:
Launch Your Own LEMP or LAMP Stack
Dana Luther
https://git.io/JvUy5
https://joind.in/talk/d1bc3
@danaluther
What are we going to do today, Brain?
Set up standard LAMP and LEMP stacks using Docker stack
Modify the stack to mimic production
Run codeception test suites against our application
Learn how to swap out php versions
Add and remove additional services
@danaluther
Where are you on your Docker
journey?
Brand new to this
rodeo…
Docker containers are
my jam!
I am Docker.
@danaluther
Everyone set up and ready?
Docker Desktop installed and running
Checked out the GIT repo
Pulled the docker images
Anyone using Windows? Set to use LCOW?
@danaluther
Moving beyond containers…
@danaluther
Moving beyond containers…
Image Container Service Stack
@danaluther
Moving beyond containers…
Image Container Service Stack App
@danaluther
Moving beyond containers…
Image Container Service Stack App
@danaluther
Release the swarm!
> docker swarm init
@danaluther
Basic LAMP Stack
docker-compose.yml
php:7.4-apache
mysql
01_LAMP
@danaluther
Basic LAMP Stack
version: "3.7"
services:
php:
# https: //hub.docker.com/_/php
image: php:7.4-apache
ports:
- 80:80
volumes:
- ./src:/var/ www/html
db:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd
secrets:
- db_pwd
secrets:
db_pwd:
file: ./root_db_password.txt
docker-compose.yml
01_LAMP
SIDEBAR:
@danaluther
https://docs.docker.com/compose/compose-file/
Docker compose file reference
@danaluther
Basic LAMP Stack
version: "3.7"
services:
php:
# https: //hub.docker.com/_/php
image: php:7.4-apache
ports:
- 80:80
volumes:
- ./src:/var/ www/html
db:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd
secrets:
- db_pwd
secrets:
db_pwd:
file: ./root_db_password.txt
docker-compose.yml
01_LAMP
SIDEBAR:
@danaluther
What’s in a secret?
@danaluther
Basic LAMP Stack
> docker stack deploy -c docker-compose.yml hod
Make sure you are on the 01_LAMP branch of the workshop repo
01_LAMP
@danaluther
Basic LAMP Stack
> docker stack deploy -c docker-compose.yml hod
Make sure you are on the 01_LAMP branch of the workshop repo
01_LAMP
@danaluther
Verify the services have all launched
> docker service ls
01_LAMP
@danaluther
Verify the services have all launched
> docker service ls
01_LAMP
SIDEBAR:
@danaluther
Stuck and not sure what the
problem is?
> docker stack ps hod --no-trunc
@danaluther
Verify the services have all launched
> docker service ls
01_LAMP
@danaluther
Verify the services have all launched
> docker service ls
01_LAMP
⚠ Common “Gotcha”
@danaluther
No public port for MySQL unless you *want* to expose it externally.
@danaluther01_LAMP
@danaluther01_LAMP
SIDEBAR:
@danaluther
What’s in a stock PHP image?
> docker run --rm php:7.4-fpm php-fpm -m
SIDEBAR:
@danaluther
What’s in a stock PHP image?
@danaluther
Creating our custom PHP image
Add the mysqli and PDO_mysql
extensions
Use build args in the Dockerfile
ARG PHP_TARGET=7.4-apache
FROM php:$PHP_TARGET
RUN docker-php-ext-install 
-j$(nproc) mysqli pdo_mysql
02_LAMP
@danaluther
Enabling Buildkit
https://docs.docker.com/develop/develop-images/build_enhancements/
@danaluther
Creating our custom PHP image
> docker image build -f Dockerfile-php-mysql 
-t dhluther/php:7.4-apache-mysql .
From the /images/ directory:
02_LAMP
@danaluther
Creating our custom PHP image
> docker image build -f Dockerfile-php-mysql 
-t dhluther/php:7.4-apache-mysql .
From the /images/ directory:
02_LAMP
@danaluther
Update our LAMP stack
version: "3.7"
services:
php:
# Custom PHP Image - see /images/Readme.md
image: dhluther/php:7.4-apache-mysql
ports:
- 80:80
volumes:
- ./src:/var/ www/html
db:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd
secrets:
- db_pwd
secrets:
db_pwd:
file: ./root_db_password.txt
02_LAMP
@danaluther
Re-deploy the LAMP stack
> docker stack deploy -c docker-compose.yml hod
@danaluther
Re-deploy the LAMP stack
> docker stack deploy -c docker-compose.yml hod
@danaluther02_LAMP
@danaluther
<?
/**
* mysql-check.php
*/
try
{
$db = new PDO('mysql:host=db', 'root', 'sup3rs3cr3tp4ssw0rd');
echo 'MySQL Version: ',
$db ->getAttribute(PDO ::ATTR_CLIENT_VERSION);
} catch (Exception $e)
{
echo 'Aw shucks pardner, ', $e ->getMessage();
}
02_LAMP
@danaluther
Checking the service logs
02_LAMP
> docker service logs hod_php
@danaluther
Checking the service logs
02_LAMP
> docker service logs hod_php
@danaluther
Take the stack down
02_LAMP
> docker stack rm hod
@danaluther
Basic LEMP Stack
docker-compose.yml
php:7.4-fpm
mysql
nginx
03_LEMP
@danaluther
Basic LEMP Stack
03_LEMP
version: "3.7"
services:
php:
image: dhluther/php:7.4-fpm-mysql
volumes:
- ./src:/var/ www/html
…db…
web:
image: nginx
ports:
- 80:80
- 443:443
volumes:
- ./src:/var/ www/html
configs:
- source: nginx-conf
target: /etc/nginx/conf.d/default.conf
secrets:
db_pwd:
file: ./root_db_password.txt
configs:
nginx-conf:
file: ./nginx/default.conf
@danaluther
Creating our custom PHP-FPM image
> docker image build -f Dockerfile-php-mysql 
-t dhluther/php:7.4-fpm-mysql . 
--build-arg PHP_TARGET=7.4-fpm
From the /images/ directory:
03_LEMP
@danaluther03_LEMP
@danaluther
Deploy the LEMP stack
> docker stack deploy -c docker-compose.yml hod
03_LEMP
@danaluther03_LEMP
@danaluther03_LEMP
@danaluther03_LEMP
⚠ Common “Gotcha”
@danaluther
NO PERSISTENT DATA!
03_LEMP
⚠ Common “Gotcha”
@danaluther
db:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd
secrets:
- db_pwd
NO PERSISTENT DATA!
03_LEMP
⚠ Common “Gotcha”
@danaluther
PERSISTENT DATA!
04_LEMP
⚠ Common “Gotcha”
@danaluther
db:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd
volumes:
- ./data:/var/lib/mysql
secrets:
- db_pwd
PERSISTENT DATA!
04_LEMP
@danaluther
Fresh deploy the Stack
04_LEMP
> docker stack rm hod
> docker stack deploy -c docker-compose.yml hod
> docker network ls
@danaluther
Quick Break - Stretch!
@danaluther
The stack is standard, but are we?
@danaluther
The stack is standard, but are we?
Customized config files:
php.ini and php.conf
my.conf
nginx.conf
@danaluther
The stack is standard, but are we?
Customized config files:
php.ini and php.conf
my.conf
nginx.conf
Customized images:
my/php
my/mysql
my/nginx
@danaluther
Basic LEMP Stack (flashback)
03_LEMP
version: "3.7"
services:
php:
image: dhluther/php:7.4-fpm-mysql
volumes:
- ./src:/var/ www/html
…db…
web:
image: nginx
ports:
- 80:80
- 443:443
volumes:
- ./src:/var/ www/html
configs:
- source: nginx-conf
target: /etc/nginx/conf.d/default.conf
secrets:
db_pwd:
file: ./root_db_password.txt
configs:
nginx-conf:
file: ./nginx/default.conf
@danaluther
What’s in the default nginx.conf?
> docker run --rm nginx cat /etc/nginx/nginx.conf
@danaluther
@danaluther05_LEMP
# Max file size for uploads
client_max_body_size 20m;
# Sets the cache for 1k items for 1 minute
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 60s;
open_file_cache_min_uses 5;
open_file_cache_errors off;
# GZIP compression settings, text/html is automatically compressed
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_comp_level 5;
gzip_buffers 16 8k;
gzip_min_length 350;
gzip_proxied any;
gzip_types application/atom+xml application/rss+xml application/x-javascript application/javascript application/json text/plain text/css text/x-
component text/x-cross-domain-policy text/javascript;
# disable auto-index across the board by default
autoindex off;
# disable server side includes by default
ssi off;
@danaluther
Update NGiNX configs
05_LEMP
version: "3.7"
services:
php:
image: dhluther/php:7.4-fpm-mysql
volumes:
- ./src:/var/ www/html
…db…
web:
…
configs:
- source: nginx-conf
target: /etc/nginx/conf.d/default.conf
- source: nginx-base—conf
target: /etc/nginx/nginx.conf
secrets:
db_pwd:
file: ./root_db_password.txt
configs:
nginx-conf:
file: ./nginx/default.conf
nginx-base-conf:
file: ./nginx/nginx.conf
@danaluther
What’s in our PHP config?
@danaluther
What might you need to customize?
Timezone setting
Error reporting
Output caching and buffering
Listen directives
Ping/Status directives
Timeouts and extension limitations
@danaluther
Create custom .ini and .conf files
tz.ini — /usr/local/etc/php/conf.d/
custom.ini — /usr/local/etc/php/conf.d/
www.mysite.conf — /usr/local/etc/php-fpm.d/
05_LEMP
@danaluther
Update php image Dockerfile
05_LEMP
@danaluther
Build the php image
> docker image build -f Dockerfile-php-mysql 
-t dhluther/php:7.4-fpm-mysql-c 
--target=customized . 
--build-arg PHP_TARGET=7.4-fpm
05_LEMP
@danaluther
Build the php image
05_LEMP
@danaluther
Update compose with new php image
php:
# Custom PHP Image - see /images/Readme.md
image: dhluther/php:7.4-fpm-mysql-c
volumes:
- ./src:/var/www/html
@danaluther
Deploy stack updates
> docker stack deploy -c docker-compose.yml hod
@danaluther
Deploy stack updates
> docker stack deploy -c docker-compose.yml hod
@danaluther
Verify ini files loaded
@danaluther
Quick Break - Get some water!
@danaluther
Great - we match production.
But what about testing??
@danaluther
Add xdebug and blackfire probe
07_LEMP
@danaluther
(the full xdebug install commands)
07_LEMP
RUN yes | pecl install xdebug 
&& echo "zend_extension=$(find /usr/local/lib/php/extensions/ -name xdebug.so)" 
> $PHP_INI_DIR/conf.d/xdebug.ini 
&& echo "xdebug.remote_enable=on" >> $PHP_INI_DIR/conf.d/xdebug.ini 
&& echo "xdebug.remote_autostart=off" >> $PHP_INI_DIR/conf.d/xdebug.ini
EXPOSE 8080 80 9000 9001
@danaluther
Build the debug image
07_LEMP
> docker image build -f Dockerfile-php-mysql 
-t dhluther/php:7.4-debug 
--target=debug . 
--build-arg PHP_TARGET=7.4-fpm
@danaluther
Build the debug image
07_LEMP
> docker image build -f Dockerfile-php-mysql 
-t dhluther/php:7.4-debug 
--target=debug . 
--build-arg PHP_TARGET=7.4-fpm
@danaluther
Build the debug image
07_LEMP
> docker image build -f Dockerfile-php-mysql 
-t dhluther/php:7.4-debug 
--target=debug . 
--build-arg PHP_TARGET=7.4-fpm
SIDEBAR:
@danaluther
https://blackfire.io/docs/integrations/docker
blackfire:
image: blackfire/blackfire
ports:
- "8707:8707"
environment:
# Tokens for your account
- BLACKFIRE_CLIENT_ID=<insert id here>
- BLACKFIRE_CLIENT_TOKEN=<insert token here>
# Tokens for your environment
- BLACKFIRE_SERVER_ID=<insert id here>
- BLACKFIRE_SERVER_TOKEN=<insert token here>
- BLACKFIRE_LOG_LEVEL=4
@danaluther
Deploy stack updates
> docker stack deploy -c docker-compose.yml hod
07_LEMP
@danaluther
Deploy stack updates
> docker stack deploy -c docker-compose.yml hod
07_LEMP
@danaluther07_LEMP
@danaluther07_LEMP
@danaluther07_LEMP
SIDEBAR:
@danaluther
Support the tools you use
https://www.patreon.com/derickr
@danaluther
What and how do we want to test?
Use composer to require testing framework
@danaluther
Update compose with composer
composer:
image: composer:latest
command: ['bash','-c',"sleep infinity"]
volumes:
- ./src:/var/ www/html
08_LEMP
@danaluther
Deploy stack updates
> docker stack deploy -c docker-compose.yml hod
08_LEMP
@danaluther
Deploy stack updates
> docker stack deploy -c docker-compose.yml hod
08_LEMP
@danaluther
Initialize composer
> docker exec -it $(docker ps -lq -f name=hod_composer) 
bash
08_LEMP
@danaluther
Initialize composer
> docker exec -it $(docker ps -lq -f name=hod_composer) 
bash
08_LEMP
SIDEBAR:
@danaluther
> docker ps -lq -f name=stack_service
The command to rule them all …
> docker exec -it 36fce0da4a8f bash
> docker exec -it $(docker ps -lq -f name=hod_php) 
bash
SIDEBAR:
@danaluther
> docker ps -lq -f name=stack_service
The command to rule them all …
> docker exec -it 36fce0da4a8f bash
> docker exec -it $(docker ps -lq -f name=hod_php) 
bash
@danaluther
Initialize composer
> docker exec -it $(docker ps -lq -f name=hod_composer) 
bash
08_LEMP
SIDEBAR:
@danaluther
Don’t want to mess with the
composer install today?
Use the “with_vendor” branch alternates
I got you!
09_LEMP_with_vendor
10_LEMP_with_vendor
@danaluther
Require codeception
bash-5.0# composer require codeception/codeception --dev
08_LEMP
@danaluther
Alternately … composer install
bash-5.0# composer install
09_LEMP
@danaluther
Confirm codeception installation
09_LEMP
@danaluther
Access via a php container
> docker exec -it $(docker ps -lq -f name=hod_php) 
bash
09_LEMP
@danaluther
Verify that you can run unit tests
10_LEMP
@danaluther
(What did we just run?)
require '/var/ www/html/DemoQuickCalculator.php';
class testSampleTest extends CodeceptionTestUnit
{
/**
* @var UnitTester
*/
protected $tester;
// tests
public function testBasicMath()
{
$this ->assertEquals(4, DemoQuickCalculator ::alwaysEquals4());
}
}
@danaluther
(What did we just run?)
/**
* Class DemoQuickCalculator
* Quick and dirty class to show that we can run unit tests
*/
class DemoQuickCalculator
{
/**
* @return int
*/
public static function alwaysEquals4(): int
{
return 2+2;
}
}
@danaluther
Make it fail …
/**
* Class DemoQuickCalculator
* Quick and dirty class to show that we can run unit tests
*/
class DemoQuickCalculator
{
/**
* @return int
*/
public static function alwaysEquals4(): int
{
return 2+3;
}
}
@danaluther
Run the test again …
10_LEMP
@danaluther
Quick Break - Stretch!
@danaluther
Enable headless browsers for testing
@danaluther
Enable headless browsers for testing
Update docker-compose.yml with Chrome and Firefox images
@danaluther
Enable headless browsers for testing
Update docker-compose.yml with Chrome and Firefox images
Default to 0 containers - scale the service up/down as needed
@danaluther
Enable headless browsers for testing
Update docker-compose.yml with Chrome and Firefox images
Default to 0 containers - scale the service up/down as needed
Take advantage of YAML anchors in the .yml to keep the compose file dry
SIDEBAR:
@danaluther
What are YAML anchors?
Additional Resources
https://nickjanetakis.com/blog/docker-tip-82-using-yaml-anchors-and-x-properties-in-docker-compose
https://confluence.atlassian.com/bitbucket/yaml-anchors-960154027.html
Anchor &
Alias *
Override <<:
@danaluther
Setting up x-defaults
x-defaults:
network: &network
networks:
- net
selenium-services: &selenium-svc
environment:
# Required to avoid container startup hanging sometimes in
# some environments
JAVA_OPTS: -Djava.security.egd=file:/dev/./urandom
ports:
- "4444:4444"
deploy:
replicas: 0
restart_policy:
condition: on-failure
<<: *network
10_LEMP
SIDEBAR:
@danaluther
The deploy block
selenium-services: &selenium-svc
…
deploy:
replicas: 0
restart_policy:
condition: on-failure
@danaluther
Implementing x-default network
php:
# Custom PHP Image - see /images/Readme.md
image: dhluther/php:7.4-debug
volumes:
- ./src:/var/ www/html
<<: *network
…
networks:
net:
10_LEMP
@danaluther
Setting up Chrome and Firefox
#Used for Acceptance Tests for Firefox
firefox:
image: selenium/standalone-firefox-debug:2.53.0
<<: *selenium-svc
#Used for Acceptance Tests for Chrome
chrome:
image: selenium/standalone-chrome-debug
<<: *selenium-svc
ports:
- "4443:4444"
10_LEMP
@danaluther
Deploy and Scale services
10_LEMP
@danaluther
What if we forget to scale up chrome?
10_LEMP
@danaluther
So how do we swap out php
versions?
@danaluther
Build the 7.3-fpm image
> docker image build -f Dockerfile-php-mysql 
-t dhluther/php:7.3-fpm-mysql-c 
--target=customized . 
--build-arg PHP_TARGET=7.3-fpm
10_LEMP
@danaluther
Build the 7.3-fpm image
> docker image build -f Dockerfile-php-mysql 
-t dhluther/php:7.3-fpm-mysql-c 
--target=customized . 
--build-arg PHP_TARGET=7.3-fpm
10_LEMP
> docker image build -f Dockerfile-php-mysql 
-t dhluther/php:7.3-debug 
--target=debug . 
--build-arg PHP_TARGET=7.3-fpm
@danaluther
Build the 7.3-fpm image
10_LEMP
@danaluther
Update the service
> docker service update 
--image=dhluther/php:7.3-fpm-mysql-c 
hod_php
@danaluther
Update the service
> docker service update 
--image=dhluther/php:7.3-fpm-mysql-c 
hod_php
POPQUIZ!
@danaluther
Does anyone remember how to get
into the container to run our unit
test?
POPQUIZ!
@danaluther
Does anyone remember how to get
into the container to run our unit
test?
> docker exec -it $(docker ps -lq -f name=hod_php) 
bash
@danaluther
Run our unit test
@danaluther
@danaluther
Verify the MySQL page
@danaluther
Roll back the service
> docker service rollback hod_php
SIDEBAR:
@danaluther
Updates and rollbacks happen
sequentially when affecting a
service with more than one
container running.
SIDEBAR:
@danaluther
Update a service…
SIDEBAR:
@danaluther
Rollback a service…
@danaluther
But what about additional services?
@danaluther
Add new services to our stack
phpMyAdmin — https://www.phpmyadmin.net/
Redis — https://redis.io/
@danaluther
Define the phpMyAdmin service
phpmyadmin:
image: phpmyadmin/phpmyadmin
environment:
- PMA_ARBITRARY=1
deploy:
replicas: 1
restart_policy:
condition: on-failure
ports:
- 8081:80
<<: *network
volumes:
- /sessions
11_LEMP
@danaluther
Deploy stack updates
> docker stack deploy -c docker-compose.yml hod
11_LEMP
@danaluther
localhost:8081
@danaluther
localhost:8081
@danaluther
Define the redis service
redis:
image: redis:latest
volumes:
- ./data_redis:/data
deploy:
placement:
constraints: [node.role == manager]
<<: *network
12_LEMP
@danaluther
Deploy stack updates
> docker stack deploy -c docker-compose.yml hod
12_LEMP
@danaluther
Verify redis is running
@danaluther
Can I use a GUI to help me?
@danalutherhttps://portainer.io
@danaluther
Deploy the portainer stack
> docker stack deploy -c docker-compose-portainer.yml 
hod_port
12_LEMP
@danaluther
@danaluther
@danaluther
@danaluther
@danaluther
@danaluther
@danaluther
@danaluther
Shut it down!
> docker stack rm hod
> docker swarm leave --force
> docker stack rm hod_port
@danaluther
Questions??
🤔
?
? ?
?
https://www.linkedin.com/in/danaluther
dluther@envisageinternational.com
https://git.io/JvUy5
https://joind.in/talk/d1bc3
1 of 151

More Related Content

What's hot(20)

Containers 101Containers 101
Containers 101
Black Duck by Synopsys2.4K views
Stealthy, Hypervisor-based Malware AnalysisStealthy, Hypervisor-based Malware Analysis
Stealthy, Hypervisor-based Malware Analysis
Tamas K Lengyel13.1K views
Software ContainerizationSoftware Containerization
Software Containerization
Roshan Deniyage799 views
HTTP/3, QUIC and streamingHTTP/3, QUIC and streaming
HTTP/3, QUIC and streaming
Daniel Stenberg1.8K views
Docker containers : introductionDocker containers : introduction
Docker containers : introduction
rinnocente247 views
Docker, LinuX ContainerDocker, LinuX Container
Docker, LinuX Container
Araf Karsh Hamid5.6K views
FD.IO Vector Packet ProcessingFD.IO Vector Packet Processing
FD.IO Vector Packet Processing
Kernel TLV4.4K views
OpenWRT guide and memoOpenWRT guide and memo
OpenWRT guide and memo
家榮 吳3.2K views
Kubernetes networkingKubernetes networking
Kubernetes networking
Sim Janghoon156 views
Intro to containerizationIntro to containerization
Intro to containerization
Balint Pato1.9K views

Similar to Hands on Docker - Launch your own LEMP or LAMP stack - SunshinePHP(20)

Recently uploaded(20)

WalkingWalking
Walking
Ed Sullivan37 views
Pen Testing - Allendevaux.pdfPen Testing - Allendevaux.pdf
Pen Testing - Allendevaux.pdf
SourabhKumar328076 views
google forms survey (1).pptxgoogle forms survey (1).pptx
google forms survey (1).pptx
MollyBrown8613 views
DU_SERIES_Session1.pdfDU_SERIES_Session1.pdf
DU_SERIES_Session1.pdf
RohitRadhakrishnan8711 views
Audience profile.pptxAudience profile.pptx
Audience profile.pptx
MollyBrown8612 views
Technical SEO: How Anomalies Are Your New Best Friend." Technical SEO: How Anomalies Are Your New Best Friend."
Technical SEO: How Anomalies Are Your New Best Friend."
Kristine Schachinger SEO and Online Marketing55 views
 FS Design 2024 V2.pptx FS Design 2024 V2.pptx
FS Design 2024 V2.pptx
paswanlearning7 views
informing ideas.docxinforming ideas.docx
informing ideas.docx
MollyBrown8612 views
DU Series - Day 4.pptxDU Series - Day 4.pptx
DU Series - Day 4.pptx
UiPathCommunity55 views
zotabet.pdfzotabet.pdf
zotabet.pdf
zotabetcasino5 views
Is Entireweb better than GoogleIs Entireweb better than Google
Is Entireweb better than Google
sebastianthomasbejan9 views
AI Powered event-driven translation botAI Powered event-driven translation bot
AI Powered event-driven translation bot
Jimmy Dahlqvist11 views
Serverless cloud architecture patternsServerless cloud architecture patterns
Serverless cloud architecture patterns
Jimmy Dahlqvist10 views
KHNOG 5: APNIC ServicesKHNOG 5: APNIC Services
KHNOG 5: APNIC Services
APNIC381 views

Hands on Docker - Launch your own LEMP or LAMP stack - SunshinePHP