Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Deploy PHP Apps with Docker Compose - The Essentials

Deploy PHP Apps with Docker Compose - The Essentials

Learn the six essentials steps to deploying applications with Docker Compose.

Are you tired of hearing how "simple" it is to deploy apps with Docker Compose, because your experience is more one of frustration? Have you read countless blog posts and forum threads that promised to teach you how to deploy apps with Docker Compose, only for one or more essential steps to be missing, outdated, or broken?

Some years back, I was in exactly this position. In my quest to deploy apps that I'd developed locally with Docker Compose, I’d read the documentation along with countless blog posts, yet was left more frustrated than anything else.

So, I decided to sit down and figure out the simplest approach to deploying apps with Docker Compose and then to document my findings. This talk is the result of what I learned.

If the slides take your interest, you can download a copy of the book that I based the talk on, called Deploy With Docker Compose, from https://deploywithdockercompose.com.

587223eccffb4d264fd18bb9d54cd1d9?s=128

Web Dev with Matt

November 19, 2021
Tweet

Other Decks in Programming

Transcript

  1. By Matthew Setter / @webdevwithmatt Deploy PHP Apps with Docker

    Compose The Essentials
  2. Nice to meet you • @settermjd • @webdevwithmatt • Coding

    since 1996 • Mostly web-based apps • Once wrote VB/Access • Used Docker since 2015 I’m Matthew
  3. Want to Get in Touch? @webdevwithmatt https: // webdevwithmatt.com matthew@webdevwithmatt

  4. What Will You Learn?

  5. 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/
  6. 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
  7. 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
  8. It's not a deep-dive into Docker Compose (or Docker)

  9. 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
  10. 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
  11. Why Deploy With Docker?

  12. Why Deploy With Docker? • Can be a simpler •

    Use unified commands • Deploy to production like development • Share setups • Integrate with CI/CD tools
  13. What Do You Need?

  14. 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
  15. The Demo PHP Application

  16. None
  17. None
  18. The Application’s Composition PHP Webserver Database 8.1 NGINX MariaDB

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

  20. The File Structure 1 2 3 4 5 6

  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. Create the Core Dockerfiles 1 2 3 4 5 6

  27. 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
  28. “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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. FROM php:8.1.1-fpm-alpine3.15 as final WORKDIR /var/www/html COPY -- from=dependencies /var/www/html

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

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

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

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

    Dockerfile
  44. 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
  45. 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
  46. • 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
  47. FROM mariadb:latest COPY ./docker/database/dump.sql \ /docker-entrypoint-initdb.d/dump.sql The Database Dockerfile

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

  49. 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
  50. LOCK TABLES records WRITE; INSERT INTO records VALUES (2,'Steve','McGarrett','steve.mcgarrett@hawaii-50.org'), (3,'Danny','Williams','danny.williams@hawaii-50.org'),

    (4,'Kamekona','','kamekona@hawaii-50.org'),(5,'Chin Ho','Kelly','chin.ho.kelly@hawaii-50.org'), (6,'Kono','Kalakaua','kono.kalakaua@hawaii-50.org'), (7,'Lou','Grover','lou.grover@hawaii-50.org'), (8,'Duke','Lukela','duke.lukela@hawaii-50.org'), (9,'Max','Bergman','max.bergman@hawaii-50.org'), (10,'Jerry','Ortega','jerry.ortega@hawaii-50.org'), (11,'Adam','Noshimuri','adam.noshimuri@hawaii-50.org'); UNLOCK TABLES; dump.sql
  51. None
  52. The Docker Compose Configuration 1 2 3 4 5 6

  53. docker-compose.yml

  54. volumes: database_data: driver: local A Persistable Volume

  55. 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
  56. 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
  57. 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
  58. 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).
  59. 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).
  60. 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).
  61. 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).
  62. 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).
  63. 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
  64. 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
  65. php: restart: unless-stopped expose: - 9000 The PHP Service Configuration

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

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

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

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

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

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

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

    Service Configuration
  73. docker-compose.dev.yml

  74. 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
  75. 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
  76. 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
  77. 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
  78. 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; } }
  79. The PHP Service Configuration php: build: context: ./ dockerfile: docker/php/Dockerfile

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

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

    target: base args: ENV: development volumes: - .:/var/www/html
  82. database: environment: MARIADB_DATABASE: hawaii-five-0 MARIADB_USER: user MARIADB_PASSWORD: password volumes: -

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

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

    ./data/database:/var/lib/mysql/ The Database Service Configuration
  85. Deploy the Application Locally

  86. docker compose \ -- file docker-compose.yml \ 
 -- file

    docker-compose.dev.yml \ up -- build Deploy the Application Locally
  87. [+] 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
  88. None
  89. docker pull nginx:alpine docker pull php:8.1.1-fpm-alpine3.15 docker pull mariadb:latest Save

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

  91. docker compose \ 
 -- file docker-compose.yml \ 
 --

    file docker-compose.dev.yml \ up -d Deploy the Application Locally
  92. Are the Containers Working as Expected?

  93. 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
  94. 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
  95. docker compose logs docker compose \ -- file docker-compose.yml \

    
 -- file docker-compose.dev.yml \ logs -- follow database
  96. None
  97. 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
  98. Create the Production Files 1 2 3 4 5 6

  99. The NGINX Dockerfile

  100. 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
  101. 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
  102. 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
  103. 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
  104. 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
  105. 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
  106. Create the Production Docker Compose Configuration

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

  108. services: nginx: image: webdevwithmatt/webserver-alpine:latest ports: - 80:80 The Production Docker

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

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

  111. database: image: webdevwithmatt/database-alpine:latest environment: - MARIADB_DATABASE - MARIADB_USER - MARIADB_PASSWORD

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

    The Production Docker Compose File
  113. Build & Push the Docker Images 1 2 3 4

    5 6
  114. 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
  115. docker build \ -- file ./docker/php/Dockerfile \ -- tag webdevwithmatt/php-runtime-alpine:latest

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

    \ . Build the Docker Images
  117. Check the Images

  118. $ 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
  119. docker images -- filter=reference='registry*/*/*:latest' Filter Docker Images

  120. Push the Images

  121. docker login - - username <username> -- password <password> docker

    push webdevwithmatt/webserver-alpine:latest docker push webdevwithmatt/php-runtime-alpine:latest docker push webdevwithmatt/database-alpine:latest Push the Docker Images
  122. Test the Deployment

  123. Set Environment Variables

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

  125. docker compose \ -- file docker-compose.yml \ -- file docker-compose.prod.yml

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

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

    \ down Test the Deployment
  128. Deploy to Production 1 2 3 4 5 6

  129. Deploy to Production • Set DOCKER_HOST • Use -H or

    --host options with Docker commands • Use a Docker Context
  130. Deploy to Production DOCKER_HOST=ssh: / / webdevwithmatt@<prod host> docker compose

    up export DOCKER_HOST=ssh: // webdevwithmatt@<prod host> && \ docker compose up docker -H ssh: // webdevwithmatt@<prod host> compose up docker - - host ssh: / / webdevwithmatt@<prod host> compose up
  131. 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.
  132. Docker Contexts • Create a context • Set it as

    the default context • Run Docker commands
  133. Docker Contexts docker context create remote \ -docker "host=ssh: /

    / deploy@<your_digitalocean_droplets_ip>" docker context use remote
  134. docker context ls NAME TYPE DESCRIPTION DOCKER ENDPOINT default moby

    Current DOCKER_HOST based configuration unix: // / var/run/docker.sock remote * moby ssh: // webdevwithmatt@<prod host> Verify the Context
  135. Deploy to Production docker compose \ -- file docker-compose.yml \

    -- file docker-compose.prod.yml \ up -d
  136. Did It Work?

  137. docker compose \ -- file docker-compose.yml \ -- file docker-compose.prod.yml

    \ ps Check That it Worked
  138. Final Recap

  139. 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
  140. What About a Build Pipeline?

  141. 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
  142. Want to Dive Deeper? https: // deploywithdockercompose.com

  143. 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
  144. Thank You

  145. Questions?