Slide 1

Slide 1 text

By Matthew Setter / @webdevwithmatt Deploy PHP Apps with Docker Compose The Essentials

Slide 2

Slide 2 text

Nice to meet you • @settermjd • @webdevwithmatt • Coding since 1996 • Mostly web-based apps • Once wrote VB/Access • Used Docker since 2015 I’m Matthew

Slide 3

Slide 3 text

Want to Get in Touch? @webdevwithmatt https: // webdevwithmatt.com matthew@webdevwithmatt

Slide 4

Slide 4 text

What Will You Learn?

Slide 5

Slide 5 text

How to Deploy PHP Apps Using Docker Compose Picture: Docker Compose logo courtesy of Unixmen / https: // www.unixmen.com/container-docker-compose-ubuntu-16-04/

Slide 6

Slide 6 text

What Will You Learn About Docker? 1 2 Build a local development environment Build a production environment from the development version 3 Build and tag self-contained Docker images

Slide 7

Slide 7 text

What Will You Learn About Docker? 4 5 6 Push the Docker images to a Docker image registry Test the production deployment Deploy to production

Slide 8

Slide 8 text

It's not a deep-dive into Docker Compose (or Docker)

Slide 9

Slide 9 text

source: https://docs.docker.com/get-started/overview/#docker-architecture “ Docker uses a client-server architecture. The Docker client talks to the Docker daemon, which does the heavy lifting of building, running, and distributing your Docker containers. ” The Docker Architecture

Slide 10

Slide 10 text

Can Be Easier Than... Picture: Docker Swarm logo courtesy of Sum Global / https: // sumglobal.com/wp-content/uploads/2017/07/docker-swarm-logo.png Picture: Kubernetes logo courtesy of wikimedia.org / https: // commons.wikimedia.org/wiki/File:Kubernetes_logo.svg

Slide 11

Slide 11 text

Why Deploy With Docker?

Slide 12

Slide 12 text

Why Deploy With Docker? • Can be a simpler • Use unified commands • Deploy to production like development • Share setups • Integrate with CI/CD tools

Slide 13

Slide 13 text

What Do You Need?

Slide 14

Slide 14 text

What Do You Need? • A broad familiarity with Docker • Docker Engine on Linux • Docker Desktop on macOS and Microsoft Windows • Docker Compose V2 • A Docker image registry • A deployment server running Docker

Slide 15

Slide 15 text

The Demo PHP Application

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

The Application’s Composition PHP Webserver Database 8.1 NGINX MariaDB

Slide 19

Slide 19 text

“One process or service per container” source: https://docs.docker.com/config/containers/multi-service_container/

Slide 20

Slide 20 text

The File Structure 1 2 3 4 5 6

Slide 21

Slide 21 text

docker ├── nginx │ ├── default.conf │ └── Dockerfile ├── php │ └── Dockerfile ├── database │ ├── Dockerfile │ └── dump.sql ├── docker-compose.prod.yml ├── docker-compose.dev.yml 
 └── docker-compose.yml The file & directory structure

Slide 22

Slide 22 text

The file & directory structure Sub-divided by service docker ├── nginx │ ├── default.conf │ └── Dockerfile ├── php │ └── Dockerfile ├── database │ ├── Dockerfile │ └── dump.sql ├── docker-compose.prod.yml ├── docker-compose.dev.yml 
 └── docker-compose.yml

Slide 23

Slide 23 text

The file & directory structure A Dockerfiles and supporting files docker ├── nginx │ ├── default.conf │ └── Dockerfile ├── php │ └── Dockerfile ├── database │ ├── Dockerfile │ └── dump.sql ├── docker-compose.prod.yml ├── docker-compose.dev.yml 
 └── docker-compose.yml

Slide 24

Slide 24 text

The file & directory structure A base Docker Compose file docker ├── nginx │ ├── default.conf │ └── Dockerfile ├── php │ └── Dockerfile ├── database │ ├── Dockerfile │ └── dump.sql ├── docker-compose.dev.yml ├── docker-compose.prod.yml └── docker-compose.yml

Slide 25

Slide 25 text

The file & directory structure Environment-specific Docker Compose files docker ├── nginx │ ├── default.conf │ └── Dockerfile ├── php │ └── Dockerfile ├── database │ ├── Dockerfile │ └── dump.sql ├── docker-compose.prod.yml ├── docker-compose.dev.yml 
 └── docker-compose.yml

Slide 26

Slide 26 text

Create the Core Dockerfiles 1 2 3 4 5 6

Slide 27

Slide 27 text

FROM php:8.1.1-fpm-alpine3.15 as base RUN docker-php-ext-install pdo_mysql RUN if [ "$ENV" = "development" ]; then \ RUN docker-php-ext-install pecl install xdebug \ && docker-php-ext-enable xdebug; \ fi The PHP Dockerfile

Slide 28

Slide 28 text

“With multi-stage builds, you use multiple FROM statements in your Dockerfile.” source: https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds

Slide 29

Slide 29 text

The PHP Dockerfile FROM php:8.1.1-fpm-alpine3.15 as base RUN docker-php-ext-install pdo_mysql RUN if [ "$ENV" = "development" ]; then \ RUN docker-php-ext-install pecl install xdebug \ && docker-php-ext-enable xdebug; \ fi

Slide 30

Slide 30 text

The PHP Dockerfile FROM php:8.1.1-fpm-alpine3.15 as base RUN docker-php-ext-install pdo_mysql RUN if [ "$ENV" = "development" ]; then \ RUN docker-php-ext-install pecl install xdebug \ && docker-php-ext-enable xdebug; \ fi

Slide 31

Slide 31 text

The PHP Dockerfile FROM php:8.1.1-fpm-alpine3.15 as base RUN docker-php-ext-install pdo_mysql RUN if [ "$ENV" = "development" ]; then \ RUN docker-php-ext-install pecl install xdebug \ && docker-php-ext-enable xdebug; \ fi

Slide 32

Slide 32 text

FROM php:8.1.1-fpm-alpine3.15 as composer RUN apk add - - update git RUN curl -- silent - - fail -- location -- retry 3 -- output /tmp/installer.php -- url https: / / raw.githubusercontent.com/composer/getcomposer.org/cb19f2aa3aeaa2006c0cd69a7ef011eb31463067/web/ installer \ && php -r " \ \$signature = '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5'; \ \$hash = hash('sha384', file_get_contents('/tmp/installer.php')); \ if (!hash_equals(\$signature, \$hash)) { \ unlink('/tmp/installer.php'); \ echo 'Integrity check failed, installer is either corrupt or worse.' . PHP_EOL; \ exit(1); \ }" \ && php /tmp/installer.php -- no-ansi - - install-dir=/usr/bin -- filename=composer \ && composer -- ansi -- version -- no-interaction \ && rm -f /tmp/installer.php The PHP Dockerfile

Slide 33

Slide 33 text

FROM php:8.1.1-fpm-alpine3.15 as composer RUN apk add - - update git RUN curl -- silent - - fail -- location -- retry 3 -- output /tmp/installer.php -- url https: / / raw.githubusercontent.com/composer/getcomposer.org/cb19f2aa3aeaa2006c0cd69a7ef011eb31463067/web/ installer \ && php -r " \ \$signature = '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5'; \ \$hash = hash('sha384', file_get_contents('/tmp/installer.php')); \ if (!hash_equals(\$signature, \$hash)) { \ unlink('/tmp/installer.php'); \ echo 'Integrity check failed, installer is either corrupt or worse.' . PHP_EOL; \ exit(1); \ }" \ && php /tmp/installer.php -- no-ansi - - install-dir=/usr/bin -- filename=composer \ && composer -- ansi -- version -- no-interaction \ && rm -f /tmp/installer.php The PHP Dockerfile

Slide 34

Slide 34 text

FROM php:8.1.1-fpm-alpine3.15 as composer RUN apk add - - update git RUN curl -- silent - - fail -- location -- retry 3 -- output /tmp/installer.php -- url https: / / raw.githubusercontent.com/composer/getcomposer.org/cb19f2aa3aeaa2006c0cd69a7ef011eb31463067/web/ installer \ && php -r " \ \$signature = '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5'; \ \$hash = hash('sha384', file_get_contents('/tmp/installer.php')); \ if (!hash_equals(\$signature, \$hash)) { \ unlink('/tmp/installer.php'); \ echo 'Integrity check failed, installer is either corrupt or worse.' . PHP_EOL; \ exit(1); \ }" \ && php /tmp/installer.php -- no-ansi - - install-dir=/usr/bin -- filename=composer \ && composer -- ansi -- version -- no-interaction \ && rm -f /tmp/installer.php The PHP Dockerfile

Slide 35

Slide 35 text

FROM php:8.1.1-fpm-alpine3.15 as dependencies WORKDIR /var/www/html COPY . . COPY -- from=composer /usr/bin/composer /usr/bin/ RUN /usr/bin/composer install \ -- no-dev - - no-ansi -- no-plugins -- no-progress -- no-scripts \ -- classmap-authoritative -- no-interaction \ -- quiet The PHP Dockerfile

Slide 36

Slide 36 text

FROM php:8.1.1-fpm-alpine3.15 as dependencies WORKDIR /var/www/html COPY . . COPY -- from=composer /usr/bin/composer /usr/bin/ RUN /usr/bin/composer install \ -- no-dev - - no-ansi -- no-plugins -- no-progress -- no-scripts \ -- classmap-authoritative -- no-interaction \ -- quiet The PHP Dockerfile

Slide 37

Slide 37 text

FROM php:8.1.1-fpm-alpine3.15 as dependencies WORKDIR /var/www/html COPY . . COPY -- from=composer /usr/bin/composer /usr/bin/ RUN /usr/bin/composer install \ -- no-dev - - no-ansi -- no-plugins -- no-progress -- no-scripts \ -- classmap-authoritative -- no-interaction \ -- quiet The PHP Dockerfile

Slide 38

Slide 38 text

FROM php:8.1.1-fpm-alpine3.15 as dependencies WORKDIR /var/www/html COPY . . COPY -- from=composer /usr/bin/composer /usr/bin/ RUN /usr/bin/composer install \ -- no-dev - - no-ansi -- no-plugins -- no-progress -- no-scripts \ -- classmap-authoritative -- no-interaction \ -- quiet The PHP Dockerfile

Slide 39

Slide 39 text

FROM php:8.1.1-fpm-alpine3.15 as final WORKDIR /var/www/html COPY -- from=dependencies /var/www/html . The PHP Dockerfile

Slide 40

Slide 40 text

FROM php:8.1.1-fpm-alpine3.15 as final WORKDIR /var/www/html COPY -- from=dependencies /var/www/html . The PHP Dockerfile

Slide 41

Slide 41 text

FROM php:8.1.1-fpm-alpine3.15 as final WORKDIR /var/www/html COPY -- from=dependencies /var/www/html . The PHP Dockerfile

Slide 42

Slide 42 text

FROM php:8.1.1-fpm-alpine3.15 as base A Few Dockerfile Tips

Slide 43

Slide 43 text

ARG ALPINE_VERSION=3.15 ARG PHP_VERSION=8.1.1 FROM php:${PHP_VERSION}-fpm-alpine${ALPINE_VERSION} as base The PHP Dockerfile

Slide 44

Slide 44 text

ARG ALPINE_VERSION=3.15 ARG PHP_VERSION=8.1.1 FROM php:${PHP_VERSION}-fpm-alpine${ALPINE_VERSION} as base FROM base as extensions The PHP Dockerfile

Slide 45

Slide 45 text

I 😍 Alpine Linux Picture: Alpine Linux logo courtesy of techcentral.ie / http: // www.techcentral.ie/wp-content/uploads/2017/02/Alpine_Linux_logo_web.jpg

Slide 46

Slide 46 text

• Smaller than other distros • Around 5MB in size • Reduces hosting costs • Reduces transfer costs • Reduces deployment time • Is a Docker best practice I 😍 Alpine Linux Picture: Alpine Linux logo courtesy of techcentral.ie / http: // www.techcentral.ie/wp-content/uploads/2017/02/Alpine_Linux_logo_web.jpg

Slide 47

Slide 47 text

FROM mariadb:latest COPY ./docker/database/dump.sql \ /docker-entrypoint-initdb.d/dump.sql The Database Dockerfile

Slide 48

Slide 48 text

FROM mariadb:latest COPY ./docker/database/dump.sql \ /docker-entrypoint-initdb.d/dump.sql The Database Dockerfile

Slide 49

Slide 49 text

DROP TABLE IF EXISTS records; CREATE TABLE records ( id int unsigned NOT NULL, first_name varchar(200) NOT NULL, last_name varchar(200) NOT NULL, email_address varchar(255) NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; dump.sql

Slide 50

Slide 50 text

LOCK TABLES records WRITE; INSERT INTO records VALUES (2,'Steve','McGarrett','[email protected]'), (3,'Danny','Williams','[email protected]'), (4,'Kamekona','','[email protected]'),(5,'Chin Ho','Kelly','[email protected]'), (6,'Kono','Kalakaua','[email protected]'), (7,'Lou','Grover','[email protected]'), (8,'Duke','Lukela','[email protected]'), (9,'Max','Bergman','[email protected]'), (10,'Jerry','Ortega','[email protected]'), (11,'Adam','Noshimuri','[email protected]'); UNLOCK TABLES; dump.sql

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

The Docker Compose Configuration 1 2 3 4 5 6

Slide 53

Slide 53 text

docker-compose.yml

Slide 54

Slide 54 text

volumes: database_data: driver: local A Persistable Volume

Slide 55

Slide 55 text

services: nginx: restart: unless-stopped depends_on: - php healthcheck: test: ["CMD", "curl", "-f", "http: // localhost:8000/api/ping"] interval: 60s timeout: 3s retries: 3 The NGINX Service Configuration

Slide 56

Slide 56 text

services: nginx: restart: unless-stopped depends_on: - php healthcheck: test: ["CMD", "curl", "-f", "http: // localhost:8000/api/ping"] interval: 60s timeout: 3s retries: 3 The NGINX Service Configuration

Slide 57

Slide 57 text

The NGINX Service Configuration services: nginx: restart: unless-stopped depends_on: - php healthcheck: test: ["CMD", "curl", "-f", "http: // localhost:8000/api/ping"] interval: 60s timeout: 3s retries: 3

Slide 58

Slide 58 text

The restart Directive’s Options Setting Description no Docker does not restart a container for any reason. always Docker will always restart a container if it stops. If it is manually stopped, it is restarted only when Docker daemon restarts or the container itself is manually restarted. on-failure Docker will restart a container if the exit code indicates an on-failure error. unless-stopped Docker will always restart a container — except when the container is stopped (manually or otherwise).

Slide 59

Slide 59 text

The restart Directive’s Options Setting Description no Docker does not restart a container for any reason. always Docker will always restart a container if it stops. If it is manually stopped, it is restarted only when Docker daemon restarts or the container itself is manually restarted. on-failure Docker will restart a container if the exit code indicates an on-failure error. unless-stopped Docker will always restart a container — except when the container is stopped (manually or otherwise).

Slide 60

Slide 60 text

The restart Directive’s Options Setting Description no Docker does not restart a container for any reason. always Docker will always restart a container if it stops. If it is manually stopped, it is restarted only when Docker daemon restarts or the container itself is manually restarted. on-failure Docker will restart a container if the exit code indicates an on-failure error. unless-stopped Docker will always restart a container — except when the container is stopped (manually or otherwise).

Slide 61

Slide 61 text

The restart Directive’s Options Setting Description no Docker does not restart a container for any reason. always Docker will always restart a container if it stops. If it is manually stopped, it is restarted only when Docker daemon restarts or the container itself is manually restarted. on-failure Docker will restart a container if the exit code indicates an on-failure error. unless-stopped Docker will always restart a container — except when the container is stopped (manually or otherwise).

Slide 62

Slide 62 text

The restart Directive’s Options Setting Description no Docker does not restart a container for any reason. always Docker will always restart a container if it stops. If it is manually stopped, it is restarted only when Docker daemon restarts or the container itself is manually restarted. on-failure Docker will restart a container if the exit code indicates an on-failure error. unless-stopped Docker will always restart a container — except when the container is stopped (manually or otherwise).

Slide 63

Slide 63 text

services: nginx: restart: unless-stopped depends_on: - php healthcheck: test: ["CMD", "curl", "-f", "http: // localhost:8000/api/ping"] interval: 60s timeout: 3s retries: 3 The NGINX Service Configuration

Slide 64

Slide 64 text

services: nginx: restart: unless-stopped depends_on: - php healthcheck: test: ["CMD", "curl", "-f", "http: // localhost:8000/api/ping"] interval: 60s timeout: 3s retries: 3 The NGINX Service Configuration

Slide 65

Slide 65 text

php: restart: unless-stopped expose: - 9000 The PHP Service Configuration

Slide 66

Slide 66 text

php: restart: unless-stopped expose: - 9000 The PHP Service Configuration

Slide 67

Slide 67 text

php: restart: unless-stopped expose: - 9000 The PHP Service Configuration

Slide 68

Slide 68 text

php: restart: unless-stopped expose: - 9000 The PHP Service Configuration

Slide 69

Slide 69 text

database: restart: unless-stopped build: ./docker/database/ ports: - 3306:3306 The Database Service Configuration

Slide 70

Slide 70 text

database: restart: unless-stopped build: ./docker/database/ ports: - 3306:3306 The Database Service Configuration

Slide 71

Slide 71 text

database: restart: unless-stopped build: ./docker/database/ ports: - 3306:3306 The Database Service Configuration

Slide 72

Slide 72 text

database: restart: unless-stopped build: ./docker/database/ ports: - 3306:3306 The Database Service Configuration

Slide 73

Slide 73 text

docker-compose.dev.yml

Slide 74

Slide 74 text

services: nginx: image: nginx:latest ports: - 8080:80 volumes: - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf - ./:/var/www/html The NGINX Service Configuration

Slide 75

Slide 75 text

services: nginx: image: nginx:latest ports: - 8080:80 volumes: - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf - ./:/var/www/html The NGINX Service Configuration

Slide 76

Slide 76 text

services: nginx: image: nginx:latest ports: - 8080:80 volumes: - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf - ./:/var/www/html The NGINX Service Configuration

Slide 77

Slide 77 text

services: nginx: image: nginx:latest ports: - 8080:80 volumes: - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf - ./:/var/www/html The NGINX Service Configuration

Slide 78

Slide 78 text

server { # ... other configuration options location / { try_files $uri $uri/ /index.php; } location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param APPLICATION_ENV development; fastcgi_intercept_errors off; fastcgi_buffer_size 16k; fastcgi_buffers 4 16k; } }

Slide 79

Slide 79 text

The PHP Service Configuration php: build: context: ./ dockerfile: docker/php/Dockerfile target: base args: ENV: development volumes: - .:/var/www/html

Slide 80

Slide 80 text

The PHP Service Configuration php: build: context: ./ dockerfile: docker/php/Dockerfile target: base args: ENV: development volumes: - .:/var/www/html

Slide 81

Slide 81 text

The PHP Service Configuration php: build: context: ./ dockerfile: docker/php/Dockerfile target: base args: ENV: development volumes: - .:/var/www/html

Slide 82

Slide 82 text

database: environment: MARIADB_DATABASE: hawaii-five-0 MARIADB_USER: user MARIADB_PASSWORD: password volumes: - ./data/database:/var/lib/mysql/ The Database Service Configuration

Slide 83

Slide 83 text

database: environment: MARIADB_DATABASE: hawaii-five-0 MARIADB_USER: user MARIADB_PASSWORD: password volumes: - ./data/database:/var/lib/mysql/ The Database Service Configuration

Slide 84

Slide 84 text

database: environment: MARIADB_DATABASE: hawaii-five-0 MARIADB_USER: user MARIADB_PASSWORD: password volumes: - ./data/database:/var/lib/mysql/ The Database Service Configuration

Slide 85

Slide 85 text

Deploy the Application Locally

Slide 86

Slide 86 text

docker compose \ -- file docker-compose.yml \ 
 -- file docker-compose.dev.yml \ up -- build Deploy the Application Locally

Slide 87

Slide 87 text

[+] Running 4/4 ⠿ Network docker-essentials-test-build_default Created 0.1s ⠿ Container docker-essentials-test-build_php_1 Created 0.2s ⠿ Container docker-essentials-test-build_nginx_1 Created 0.3s ⠿ Container docker-essentials-test-build_database_1 Created 1.2s Attaching to database_1, nginx_1, php_1 php_1 | [18-Nov-2021 13:19:29] NOTICE: fpm is running, pid 1 php_1 | [18-Nov-2021 13:19:29] NOTICE: ready to handle connections nginx_1 | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/ nginx_1 | 2021/11/18 13:19:30 [notice] 1#1: using the "epoll" event method nginx_1 | 2021/11/18 13:19:30 [notice] 1#1: nginx/1.21.3 nginx_1 | 2021/11/18 13:19:30 [notice] 1#1: built by gcc 8.3.0 (Debian 8.3.0-6) nginx_1 | 2021/11/18 13:19:30 [notice] 1#1: OS: Linux 5.10.47-linuxkit nginx_1 | 2021/11/18 13:19:30 [notice] 1#1: start worker processes nginx_1 | 2021/11/18 13:19:30 [notice] 1#1: start worker process 32 nginx_1 | 2021/11/18 13:19:30 [notice] 1#1: start worker process 33 Docker Compose Up Output

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

docker pull nginx:alpine docker pull php:8.1.1-fpm-alpine3.15 docker pull mariadb:latest Save Some Time With docker pull

Slide 90

Slide 90 text

podman pull docker.io/library/nginx:alpine Be careful when using Podman or Kubernetes

Slide 91

Slide 91 text

docker compose \ 
 -- file docker-compose.yml \ 
 -- file docker-compose.dev.yml \ up -d Deploy the Application Locally

Slide 92

Slide 92 text

Are the Containers Working as Expected?

Slide 93

Slide 93 text

docker compose ps docker compose \ -- file docker-compose.yml \ 
 -- file docker-compose.dev.yml \ ps NAME COMMAND SERVICE STATUS PORTS docker-essentials_database_1 "docker-entrypoint.s…" database running 0.0.0.0:3306 -> 3306/tcp, 33060/tcp docker-essentials_nginx_1 "/docker-entrypoint.…" nginx running 0.0.0.0:8080 -> 80/tcp, docker-essentials_php_1 "docker-php-entrypoi…" php running 9000/tcp

Slide 94

Slide 94 text

docker compose ps docker compose \ -- file docker-compose.yml \ 
 -- file docker-compose.dev.yml \ ps database NAME COMMAND SERVICE STATUS PORTS docker-essentials_database_1 "docker-entrypoint.s…" database running 0.0.0.0:3306 -> 3306/tcp, 33060/tcp

Slide 95

Slide 95 text

docker compose logs docker compose \ -- file docker-compose.yml \ 
 -- file docker-compose.dev.yml \ logs -- follow database

Slide 96

Slide 96 text

No content

Slide 97

Slide 97 text

Quick Recap • Created an intuitive directory structure • Created Dockerfiles & supporting files for each service • Created a Docker Compose configuration • Deployed the application locally • Did basic testing and debugging

Slide 98

Slide 98 text

Create the Production Files 1 2 3 4 5 6

Slide 99

Slide 99 text

The NGINX Dockerfile

Slide 100

Slide 100 text

FROM nginx:alpine WORKDIR /var/www/html COPY ./docker/nginx/default.prod.conf /etc/nginx/conf.d/default.conf COPY . . RUN apk add -- update nodejs npm \ && npm install RUN NODE_ENV=production npx tailwindcss \ -i src/css/styles.css \ -o public/css/styles.css \ -- minify The NGINX Production Dockerfile

Slide 101

Slide 101 text

FROM nginx:alpine WORKDIR /var/www/html COPY ./docker/nginx/default.prod.conf /etc/nginx/conf.d/default.conf COPY . . RUN apk add -- update nodejs npm \ && npm install RUN NODE_ENV=production npx tailwindcss \ -i src/css/styles.css \ -o public/css/styles.css \ -- minify The NGINX Production Dockerfile

Slide 102

Slide 102 text

FROM nginx:alpine WORKDIR /var/www/html COPY ./docker/nginx/default.prod.conf /etc/nginx/conf.d/default.conf COPY . . RUN apk add -- update nodejs npm \ && npm install RUN NODE_ENV=production npx tailwindcss \ -i src/css/styles.css \ -o public/css/styles.css \ -- minify The NGINX Production Dockerfile

Slide 103

Slide 103 text

FROM nginx:alpine WORKDIR /var/www/html COPY ./docker/nginx/default.prod.conf /etc/nginx/conf.d/default.conf COPY . . RUN apk add -- update nodejs npm \ && npm install RUN NODE_ENV=production npx tailwindcss \ -i src/css/styles.css \ -o public/css/styles.css \ -- minify The NGINX Production Dockerfile

Slide 104

Slide 104 text

FROM nginx:alpine WORKDIR /var/www/html COPY ./docker/nginx/default.prod.conf /etc/nginx/conf.d/default.conf COPY . . RUN apk add -- update nodejs npm \ && npm install RUN NODE_ENV=production npx tailwindcss \ -i src/css/styles.css \ -o public/css/styles.css \ -- minify The NGINX Production Dockerfile

Slide 105

Slide 105 text

FROM nginx:alpine WORKDIR /var/www/html COPY ./docker/nginx/default.prod.conf /etc/nginx/conf.d/default.conf COPY . . RUN apk add -- update nodejs npm \ && npm install RUN NODE_ENV=production npx tailwindcss \ -i src/css/styles.css \ -o public/css/styles.css \ -- minify The NGINX Production Dockerfile

Slide 106

Slide 106 text

Create the Production Docker Compose Configuration

Slide 107

Slide 107 text

volumes: database_data: driver: local The Production Docker Compose File

Slide 108

Slide 108 text

services: nginx: image: webdevwithmatt/webserver-alpine:latest ports: - 80:80 The Production Docker Compose File

Slide 109

Slide 109 text

services: nginx: image: webdevwithmatt/webserver-alpine:latest ports: - 80:80 The Production Docker Compose File

Slide 110

Slide 110 text

php: image: webdevwithmatt/php-runtime-alpine:latest The Production Docker Compose File

Slide 111

Slide 111 text

database: image: webdevwithmatt/database-alpine:latest environment: - MARIADB_DATABASE - MARIADB_USER - MARIADB_PASSWORD The Production Docker Compose File

Slide 112

Slide 112 text

database: image: webdevwithmatt/database-alpine:latest environment: - MARIADB_DATABASE - MARIADB_USER - MARIADB_PASSWORD The Production Docker Compose File

Slide 113

Slide 113 text

Build & Push the Docker Images 1 2 3 4 5 6

Slide 114

Slide 114 text

docker build \ -- file ./docker/nginx/Dockerfile \ -- tag webdevwithmatt/webserver-alpine:latest . docker build \ -- file ./docker/php/Dockerfile \ -- tag webdevwithmatt/php-runtime-alpine:latest . docker build \ -- file ./docker/database/Dockerfile \ -- tag webdevwithmatt/database-alpine:latest . Build the Docker Images

Slide 115

Slide 115 text

docker build \ -- file ./docker/php/Dockerfile \ -- tag webdevwithmatt/php-runtime-alpine:latest \ . Build the Docker Images

Slide 116

Slide 116 text

docker build \ -- file ./docker/php/Dockerfile \ -- tag webdevwithmatt/php-runtime-alpine:latest \ . Build the Docker Images

Slide 117

Slide 117 text

Check the Images

Slide 118

Slide 118 text

$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE webdevwithmatt/webserver-alpine latest 8c6f620e0709 9 seconds ago 432MB webdevwithmatt/php-runtime-alpine latest 6544148b5c30 9 seconds ago 495MB webdevwithmatt/database-alpine latest 6544148b5c30 9 seconds ago 495MB Check the Docker Images

Slide 119

Slide 119 text

docker images -- filter=reference='registry*/*/*:latest' Filter Docker Images

Slide 120

Slide 120 text

Push the Images

Slide 121

Slide 121 text

docker login - - username -- password docker push webdevwithmatt/webserver-alpine:latest docker push webdevwithmatt/php-runtime-alpine:latest docker push webdevwithmatt/database-alpine:latest Push the Docker Images

Slide 122

Slide 122 text

Test the Deployment

Slide 123

Slide 123 text

Set Environment Variables

Slide 124

Slide 124 text

export MARIADB_DATABASE=project_db export MARIADB_USER=user export MARIADB_PASSWORD=password Set Environment Variables

Slide 125

Slide 125 text

docker compose \ -- file docker-compose.yml \ -- file docker-compose.prod.yml \ up -d Test the Deployment

Slide 126

Slide 126 text

docker compose \ -- file docker-compose.yml \ -- file docker-compose.prod.yml \ ps Test the Deployment

Slide 127

Slide 127 text

docker compose \ -- file docker-compose.yml \ -- file docker-compose.prod.yml \ down Test the Deployment

Slide 128

Slide 128 text

Deploy to Production 1 2 3 4 5 6

Slide 129

Slide 129 text

Deploy to Production • Set DOCKER_HOST • Use -H or --host options with Docker commands • Use a Docker Context

Slide 130

Slide 130 text

Deploy to Production DOCKER_HOST=ssh: / / webdevwithmatt@ docker compose up export DOCKER_HOST=ssh: // webdevwithmatt@ && \ docker compose up docker -H ssh: // webdevwithmatt@ compose up docker - - host ssh: / / webdevwithmatt@ compose up

Slide 131

Slide 131 text

source: https://docs.docker.com/engine/context/working-with-contexts/ A single Docker CLI can have multiple contexts. Each context contains all of the endpoint and security information required to manage a different cluster or node. The docker context command makes it easy to configure these contexts and switch between them.

Slide 132

Slide 132 text

Docker Contexts • Create a context • Set it as the default context • Run Docker commands

Slide 133

Slide 133 text

Docker Contexts docker context create remote \ -docker "host=ssh: / / deploy@" docker context use remote

Slide 134

Slide 134 text

docker context ls NAME TYPE DESCRIPTION DOCKER ENDPOINT default moby Current DOCKER_HOST based configuration unix: // / var/run/docker.sock remote * moby ssh: // webdevwithmatt@ Verify the Context

Slide 135

Slide 135 text

Deploy to Production docker compose \ -- file docker-compose.yml \ -- file docker-compose.prod.yml \ up -d

Slide 136

Slide 136 text

Did It Work?

Slide 137

Slide 137 text

docker compose \ -- file docker-compose.yml \ -- file docker-compose.prod.yml \ ps Check That it Worked

Slide 138

Slide 138 text

Final Recap

Slide 139

Slide 139 text

Final Recap 1. Built the application image 2. Stored the image in an accessible registry 3. Set the Docker daemon to communicate with 4. Built a deployment configuration 5. Deployed the application to a remote host

Slide 140

Slide 140 text

What About a Build Pipeline?

Slide 141

Slide 141 text

What About a Build Pipeline? Picture: Github Actions courtesy of dev.to / https: // res.cloudinary.com/practicaldev/image/fetch/s -- vBB9KCW4 -- /c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https: // dev-to-uploads.s3.amazonaws.com/i/twfkvzk83m0cpjwz26js.png 
 Picture: circleci logo courtesy of freelogovectors.net / https: // cdn.freelogovectors.net/wp-content/uploads/2020/11/circle-ci-logo.png Picture: TeamCity logo courtesy of freebiesupply.com / https: // cdn.freebiesupply.com/logos/large/2x/teamcity-icon-logo-png-transparent.png

Slide 142

Slide 142 text

Want to Dive Deeper? https: // deploywithdockercompose.com

Slide 143

Slide 143 text

Want to Dive Deeper? • It goes into greater depth • It's packed with tips and tricks • It's free... • ...in return for your email address Get the book! https: / / deploywithdockercompose.com

Slide 144

Slide 144 text

Thank You

Slide 145

Slide 145 text

Questions?