WordPress in Docker. Part 1: Dockerization

WordPress in Docker. Part 1: Dockerization

This entry-level guide will tell you why and how to Dockerize your WordPress projects.

This entry-level guide will tell you why and how to Dockerize your WordPress projects.

Docker & WordPress - WordPress Containerization With Docker

Docker & WordPress - WordPress Containerization With Docker

Setting up a local WordPress installation involves multiple steps and sometimes can be very cumbersome. Usually you need to setup a local web server (w.g. Apache), configure the server to be able to execute PHP code, and setup a MySQL database....

Setting up a local WordPress installation involves multiple steps and sometimes can be very cumbersome. Usually you need to setup a local web server (w.g. Apache), configure the server to be able to execute PHP code, and setup a MySQL database. Of course you can use pre-build bundles like MAMP for MacOS or XAMPP for Windows to get all those components on your system. However the most easiest way to setup a local WordPress environment is to use containerization with Docker.

In this tutorial you'll learn how to make use of Docker Images, Containers and Services to setup a complete local WordPress environment. This environment can then be used for testing, programming, or training purposes. E.g. you can use that testing environment to setup a copy of your production WordPress site and test any critical updates of WordPress itself, plugins etc.

Dockerizing a Node.js web application

Deploying a python-django application using docker

Getting started with Docker Compose

Deploying a Node 12 Function to Cloud Run

Docker for front-end developers

Kubernetes Tutorial: How to deploy Gitea using the Google Kubernetes Engine

An Introduction to Kubernetes: Pods, Nodes, Containers, and Clusters

10 docker-compose and docker commands that are useful for active development

Developing WordPress Sites With Docker

Developing WordPress Sites With Docker

I recently set up a new WordPress based website and local Docker-based development environment. The following article by Rob Allen guided me to do that.

Originally published by Rob Allen at dzone.com

I recently set up a new WordPress-based website and local Docker-based development environment. This post documents what I did so that I can do it again next time! As I'm not in the WordPress world, many things are strange to me and I'm indebted to Jenny Wong for pointing me in the right direction on numerous occasions and being very patient with my questions! Thanks Jenny!

Project Organization

There's always ancillary files and directories in a project that aren't part of the actual website, so I have put the WordPress site in a subdirectory called app and then I have room for other stuff. My project's root directory looks like this:

$ tree . --dirsfirst -FL 1
.
├── app/
├── bin/
├── data/
├── docker/
├── README.md
└── docker-compose.yml

This is what each item is for:

  • app/ — The WordPress application files are in this directory.
  • bin/ — Useful command-line scripts.
  • data/ — MySQL dump files go here.
  • docker/ — Files required by the Docker setup are in this directory.
  • README.md — Every project needs a README!
  • docker-compose.yml — Development orchestration config file.

I put everything into git, with a .gitignore file to ignore everything in data along with various other WordPress files/directories that shouldn't be in version control.

Docker

A pair of Docker containers is used to run the site locally for development. I'm slowly getting my feet wet with Docker, so I'm not sure if this is the best way to do things. The docker-compose command allows you to spin up multiple containers in one go and join them together. This is done via the docker-compose.yml file.

Mine looks like this:

docker-compose.yml:

version: '3'
services:
  db:
    image: mysql:5.7
    ports:
      - 127.0.0.1:3306:3306
    command: [
        '--default_authentication_plugin=mysql_native_password',
        '--character-set-server=utf8mb4',
        '--collation-server=utf8mb4_unicode_ci'
    ]
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_ROOT_PASSWORD: 123456
  wp:
    build:
      context: ./docker
      dockerfile: Dockerfile-wp
    ports:
      - 127.0.0.1:80:80
    volumes:
      - ./docker/php.conf.ini:/usr/local/etc/php/conf.d/conf.ini
      - ./app:/var/www/html
    depends_on:
      - db
    links:
      - db
    environment:
      DB_NAME: wordpress
      DB_USER: root
      DB_PASSWORD: 123456
      DB_HOST: db
  WP_DEBUG: 1
      LIVE_URL: https://project1.com
      DEV_URL: http://dev.project1.com
volumes:
  db_data: # store database into a volume so that we can pause the containers

There are two containers: wp for the Apache/PHP and db for the MySQL.

The db Container

The db container uses the default Docker MySQL container. I picked version 5.7, as that's what's running in my live environment. As this is a single-purpose development container, I just use the MySQL root user and set its password.

I want to persist the MySQL database between invocations of the container, so to do this, I create a volume called db_data and then map the /var/lib/mysqldirectory to that volume. I also expose MySQL on 3306 so that I can connect to it from my desktop.

The wp Container

For the wp container, I start with the default Docker WordPress container and add XDebug and the WP-CLI to it. This is done in the ./docker/Dockerfile-wpfile:

./docker/Dockerfile-wp:

FROM wordpress:php7.3-apache
# Install xdebug
RUN pecl install xdebug && docker-php-ext-enable xdebug
# Install Less for WP-CLI
RUN apt-get update && apt-get -y install less
# Install WP-CLI
RUN curl -s -o /usr/local/bin/wp \
    https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \
    && chmod +x /usr/local/bin/wp

docker-compose will now create a container from our Dockerfile-wp and use that.

I map the docker/php.conf.ini into the container so that the PHP picks up our additional php.ini configuration settings:

docker/php.conf.ini:

upload_max_filesize = 10M
post_max_size = 10M
xdebug.overload_var_dump = 1
xdebug.remote_enable = 1
xdebug.remote_autostart = 0
xdebug.remote_connect_back = 0
xdebug.remote_host = host.docker.internal

Note that I'm on Mac, so the magic host.docker.internal domain name is available so that XDebug works. Incredibly, this hostname isn't available on Linux (yet?), so you'll have to find a workaround if you're on Linux.

For the wp container, I also set some environment variables for the WordPress configuration. I also set LIVE_URL and DEV_URL for use later when restoring a database dump file from live.

Hostname

Set up a hostname pointing at 127.0.0.1 in your /etc/hosts for the dev site. I use dev.live-site.name personally.

127.0.0.1 dev.project1.com

Startup

Start up with:

$ docker-compose up

You can now go to http://dev.project1.com and start work. Put WordPress into app/, and the install process will start and create a database for you.

ctrl+c will stop them.

To rebuild the containers: docker-compose up --force-recreate --build

To delete the db_data volume: docker-compose down -v

The Database

There's a couple of obvious database operations that we need to perform: exporting the data to dump file and restoring a dump file. I've implemented a couple of scripts for these tasks in my bin directory.

Exporting the Database

Firstly, to export the database into a MySQL dump file from the db container:

bin/export-db:

#!/usr/bin/env bash
this_dir=$(cd `dirname $0` && pwd)
file="$this_dir/../data/dump.sql"
# Create dump file
cmd='exec mysqldump "$MYSQL_DATABASE" -uroot -p"$MYSQL_ROOT_PASSWORD"'
docker-compose exec db sh -c "cmd" > $file
# Remove password warning from the file
sed -i '.bak' 1,1d $file && rm "$file.bak"

The container already has the database and password as environment variables, so we can avoid hard-coding them. As mysqldump gets upset about using the password on the command line nowadays, we remove its warning with sed.

Restoring a Database Dump

The master database for my project is the live one and I want to use it on dev, so I have a script that retrieves a dump from live and puts it into the data directory. I then restore it using:

$ bin/restore-db live-dump.sql

This is the restore script:

bin/restore-db:

#!/usr/bin/env bash
file=$1
if [ -z "$file" ]; then
    echo "USAGE: restore-db <filename>"
    exit 1;
fi
# Restore database to db container
cmd='exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE"'
docker exec -i $(docker-compose ps -q db) sh -c "$cmd" < $file
# Replace LIVE_URL using WP-CLI in wp container
cmd='wp --allow-root search-replace "$LIVE_URL" "$DEV_URL" --skip-columns=guid'
docker-compose exec wp sh -c "$cmd"

We have two operations in this script.

Firstly, we need to restore the database. This is complicated slightly by docker-compose exec not providing an input TTY, so we have to use docker exec and find the container from the compose name (db) with docker-compose ps -q db. Again, we take advantage of the environment variables so that we don't need to store the database name or password in the script.

Secondly, we need to replace all instances of the live domain name in the data (i.e. fully qualified links in posts and also config data) with the dev domain name. This is done with the WP-CLI command:

$ wp --allow-root search-replace $from $to --skip-columns=guid

We need --allow-root as our container runs with user root. $from and $to are our search and replace terms, and finally, we need to ensure we don't change any GUIDs using --skip-columns=guid. (If ever there should have been a default column to skip, guid was it!)

The end result is that we can restore our database and start working on the next feature.

wp-config.php

WordPress uses a set of constants in the wp-config.php file for configuration. I updated my wp-config.php to look for environment variables in preference:

/app/wp-config.php:

/** The name of the database for WordPress */
define('DB_NAME', $_SERVER['DB_NAME'] ?? $_ENV['DB_NAME'] ?? null);

/** MySQL database username */
define('DB_USER', $_SERVER['DB_USER'] ?? $_ENV['DB_USER'] ?? null);

/** MySQL database password */
define('DB_PASSWORD', $_SERVER['DB_PASSWORD'] ?? $_ENV['DB_PASSWORD'] ?? null);

/** MySQL hostname */
define('DB_HOST', $_SERVER['DB_HOST'] ?? $_ENV['DB_HOST'] ?? null);

// ... later ...

define('WP_DEBUG', (bool) ($ENV['WP_DEBUG'] ?? false));
define('WP_DEBUG_LOG', (bool) ($ENV['WP_DEBUG'] ?? false));

The environment variables we set in docker-compose.yml are used here. My live server uses nginx and configures these using fastcgi_param which puts them into $_SERVER, so I check that superglobal too. There's no need to do that for WP_DEBUG, as I never turned that on on live!

Conclusion

I'm not sure if this is best-practice for WordPress development, but it's working for me so far. I particularly like that all the configuration changes that I need to make for different sites are all in docker-composer.yml which makes it reasonably easy to use this system for future projects too.

Originally published by Rob Allen at dzone.com

========================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Learn More

☞ Docker Mastery: The Complete Toolset From a Docker Captain

☞ Docker and Kubernetes: The Complete Guide

☞ Docker for the Absolute Beginner - Hands On - DevOps

☞ Wordpress for Beginners - Master Wordpress Quickly

☞ The Complete WordPress Website Business Course

☞ Become a WordPress Developer: Unlocking Power With Code

☞ WordPress Theme Development with Bootstrap

☞ Docker Crash Course for busy DevOps and Developers

☞ The Docker for DevOps course: From development to production

☞ Docker for Node.js Projects From a Docker Captain

☞ Docker Certified Associate 2019

☞ Selenium WebDriver with Docker


Learn how to CI/CD with GitHub Actions and Docker

Learn how to CI/CD with GitHub Actions and Docker

In this post, you'll learn how to CI and CD a Node.JS Application Using GitHub Actions

Originally published by Abhinav Dhasmana at https://blog.bitsrc.io

This article will cover the following:

  • Use Docker instead of bare metal deployment
  • Use GitHub actions for continuous integration of your app
  • Use GitHub actions for continuous deployment by pushing the Docker image to a Docker registry (Docker Hub)

Our workflow will look like this

A workflow of a Node.js app deployed using GitHub actions

The complete source code is available on GitHub

Use Docker instead of bare metal deployment

Dockerizing an existing app is easy. All we need is a Dockerfile and an optional .dockerignore file. Below is a Dockerfile for our app.

FROM node:10.16.0-alpine

WORKDIR /source/github-action-example-node

COPY package.json /source/github-action-example-node

RUN cd /source/github-action-example-node && npm i --only=production

COPY . .

EXPOSE 3000
CMD ["sh", "-c", "node server.js"]

It copies our package.json, runs npm install and starts the server. To make sure our file is correct, we can run docker build -t abhinavdhasmana/github-action-example-node . from the root folder. If we run docker images , we will see our latest image. We can also run our container with docker run -d -p 3000:3000 abhinavdhasmana/github-action-example-node. Point the browser to http://localhost:3000/ and text will appear.

What are GitHub Actions and how do they work

‘GitHub Actions’ is an API that can react to any event, GitHub’s or our own events. For example, for every push event on the repository, we want our test cases to run.

For GitHub Actions to work, we need to create a .github/workflows folder. We need to create our workflows inside this folder. Let’s create push.yml. Here is what we want from our workflow:

On every push, perform these actions in the given order

  1. git clone the repo
  2. run npm install
  3. run npm lint
  4. run npm test
  5. build the docker image
  6. login to docker hub
  7. Push the image to docker hub

Since we have to run each of these commands inside a docker we have to declare a Dockerfile for each of these actions and run the command in those containers. This is, of course, very tedious and error-prone. Remember, GitHub Actions are code, so we can just reuse, edit and fork them as we do with any other piece of code.

This is how our push.yml would look like

on: push
name: npm build, lint, test and publish
jobs:
build-and-publish:
name: build and publish
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
- name: npm install
uses: actions/[email protected]
with:
args: install
- name: npm test
uses: actions/[email protected]
with:
args: run test
- name: npm lint
uses: actions/[email protected]
with:
args: run lint
- name: docker build
uses: actions/docker/[email protected]
with:
args: build -t abhinavdhasmana/github-action-example-node .
- name: docker login
uses: actions/docker/[email protected]
env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
- name: docker push
uses: actions/docker/[email protected]
with:
args: push abhinavdhasmana/github-action-example-node

GitHub actions file for npm actions and push to docker hub

Let’s dissect this file

line 1: We want to trigger our workflow when someone pushes the code to our repo

line 3–6: We are defining a job build-and-publish which runs on ubuntu-latest. Each job runs in a fresh instance of a virtual environment. A job can contain one or more steps .

line 8: This is step 1 of our application. Here we want to get our source code. We can write our own code to pull our source code or reuse an open source. The repo link is https://github.com/actions/checkout

line 9-12: This is step 2 of our workflow where we run npm install on our codebase. Again, we use an open source action at https://github.com/actions/npm and pass install as an argument.

line 13–20: These are same as the last step except the argument passed to npm command.

line 21–24: We build a docker image of our code with the help of docker action and tag the image as abhinavdhasmana/github-action-example-node

line 25-29: This one is a little different where we want to login into docker hub. Here we use secrets which are passed as an env variables to our build. We can set these env variables in many ways. To set this up via GitHub, go to Settings-> Secrets and create new secrets

Store secrets in GitHub

line 30-33: We push the image to the docker hub with the tag we created in line 24.

If we commit these changes, GitHub Actions will come into play and start running all the steps in our job. We should see something like this

GitHub Actions running our job

To validate if a new image has been pushed to DockerHub, we should see a new image being pushed in Docker Hub

Docker Hub image

Full source code is available on GitHub.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow me on Facebook | Twitter

Further reading

Docker and Kubernetes: The Complete Guide

Docker Mastery: The Complete Toolset From a Docker Captain

Docker for the Absolute Beginner - Hands On - DevOps

Docker for Absolute Beginners

How to debug Node.js in a Docker container?

Docker Containers for Beginners

Deploy Docker Containers With AWS CodePipeline

Build Docker Images and Host a Docker Image Repository with GitLab

How to create a full stack React/Express/MongoDB app using Docker