How to deploy highly available WordPress in docker swarm behind Caddy v2.3

How to deploy highly available WordPress in docker swarm behind Caddy v2.3

Caddy’s reverse_proxy is capable of serving any FastCGI application, specifically for PHP apps (wordpress:php7.4-fpm-alpine). Caddy proxies requests to a PHP FastCGI server such as php-fpm.

In this post, I am going to show you how to deploy highly available WordPress in our Docker Swarm Cluster Environment using Docker Compose behind Caddyserver. Previously I deployed WordPress behind Traefik.

WordPress is a content management system(CMS) based on PHP and MySQL. WordPress is used by more than 60 million websites, including 33.6% of the top 10 million websites.

Prerequisites

Please make sure you should fulfill the below requirements before proceeding to the actual deployment.

  1. Docker Swarm Cluster with GlusterFS as persistent tool.
  2. Caddy as reverse proxy to expose micro-services to external.
  3. Database stack to host application databases.

Introduction to WordPress

WordPress is open source software you can use to create a beautiful website, blog, or app.

WordPress is used by more than 60 million websites, including 33.6% of the top 10 million websites as of April 2019 and also WordPress is one of the most popular content management system (CMS) solutions in use.

WordPress (WordPress.org) is a content management system (CMS) based on PHP and MySQL. Features include a plugin architecture and a template system, referred as Themes. WordPress is most associated with blogging (its original purpose when first created) but has evolved to support other types of web content including more traditional mailing lists and forums, media galleries, membership sites, learning management systems (LMS), and online stores.

WordPress Features

WordPress combines simplicity for users and publishers with under-the-hood complexity for developers. This makes it flexible while still being easy to use.

There are thousands of plugins that extend what WordPress does, so the actual functionality is nearly limitless.

We are free to use WordPress code, extend it or modify in any way or use it for commercial projects without any licensing fees.

That’s the beauty of free software, free refers not only to price but also the freedom to have complete control over it.

Here are some of the features that we think that you’ll love

  1. Flexibility
  2. Simplicity
  3. Resonsive
  4. High Performance
  5. Manage on the Go
  6. High security

If you want to learn more about WordPress, please go through official URL and Wikipedia.

Persist WordPress Data

Containers are fast to deploy and make efficient use of system resources. Developers get application portability and programmable image management and the operations team gets standard run time units of deployment and management.

With all the known benefits of containers, there is one common misperception that the containers are ephemeral, which means if we restart the container or in case of any issues with it, we lose all the data for that particular container. They are only good for stateless micro-service applications and that it’s not possible to containerize stateful applications.

I am going to use GlusterFS to overcome the ephemeral behavior of Containers.

I already set up a Replicated GlusterFS Volume to have data replicated throughout the cluster if I would like to have some persistent data.

The below diagram explains how the replicated volume works.

GlusterFS Replicated Volume

Volume will be mounted on all the nodes, and when a file is written to the /mnt/ partition, data will be replicated to all the nodes in the Cluster

Note

In case of any one of the nodes fails, the application automatically starts on other node without loosing any data and that’s the beauty of the replicated volume.

Persistent application state or data needs to survive application restarts and outages. We are storing the data or state in GlusterFS and had periodic backups performed on it.

We will use the backup of the volume to spin a new application container anywhere else in case of unexpected issues occur in the current environment.

Create folder in /mnt directory to the persistent WordPress data folder, /var/www/html

cd /mnt
sudo mkdir -p wordpress-caddy

Tip

Please watch the below video for the GlusterFS Replicated Volume Setup.

Prepare WordPress Environment

I am going to use docker-compose to prepare an environment file for deploying WordPress. The compose file is known as YAML ( YAML stands for Yet Another Markup Language) and has extension .yml or .yaml

I am going to create application folders in /opt directory on manager node in our docker swarm cluster to store configuration files, nothing but docker compose files (.yml or .yaml).

Also, I am going to use the caddy overlay network created in the previous Caddy post.

Now it’s time to create a folder, wordpress in /opt directory to place configuration file, i.e, .yml file for WordPress.

Use the below commands to create the folder.

Go to /opt directory by typing cd /opt in Ubuntu console

make a folder, wordpress in /opt with sudo mkdir -p wordpress

Let’s get into wordpress folder by typing cd wordpress

Now create a docker-compose file inside WordPress folder using sudo touch wordpress.yml

Open wordpress.yml docker-compose file with nano editor using sudo nano wordpress.yml and copy and paste the below code in it.

Caddy PHP FastCGI Directory

Caddy’s reverse proxy is capable of serving any FastCGI application, specifically for PHP apps. This directive is actually just a convenient way to use a longer.

It proxies requests to a PHP FastCGI server such as php-fpm. Please go through the below link to learn more about Caddy’s FastCGI directory.

Caddy’s FastCGI

FPM (FastCGI Process Manager) is an alternative PHP FastCGI implementation with some additional features (mostly) useful for heavy-loaded sites.

FPM features include…

  1. Advanced process management with graceful stop/start
  2. Ability to start workers with different uid/gid/chroot/environment, listening on different ports and using different php.ini (replaces safe_mode)
  3. Stdout and stderr logging
  4. Emergency restart in case of accidental opcode cache destruction
  5. Accelerated upload support
  6. Slowlog - logging scripts (not just their names, but their PHP backtraces too, using ptrace and similar things to read remote process execute_data) that are executed unusually slow
  7. Fastcgi_finish_request() - special function to finish request and flush all data while continuing to do something time-consuming (video converting, stats processing etc.)
  8. Dynamic/static child spawning
  9. Basic SAPI status info (similar to Apache mod_status)
  10. php.ini-based config file

I am going to utilize Caddy’s FastCGI directory to deploy WordPress using wordpress:php7.4-fpm-alpine docker container.

Caddyfile - WordPress

The Caddyfile is a convenient Caddy configuration format for humans.

Caddyfile is easy to write, easy to understand, and expressive enough for most use cases.

Production-ready Caddyfile for WordPress with PHP FastCGI Process Manager (FPM).

Learn more about Caddyfile here to get familiar with it.

{
    email you@example.com
    cert_issuer acme
    experimental_http3
}

wordpress.example.com {
    log {
        output file /var/log/caddy/wordpress.log {
            roll_size 20mb
            roll_keep 2
            roll_keep_for 6h
        }
        format console
        level error
    }
    @disallowed {
        path /xmlrpc.php
        path *.sql
        path /wp-content/uploads/*.php
    }

    rewrite @disallowed '/index.php'

    root * /var/www/html
    php_fastcgi wordpress:9000
    file_server
    encode gzip zstd
}

Please go to Caddy Post to get more insight to deploy it in the docker swarm cluster.

WordPress Docker Compose

Here is the docker-compose file for WordPress. I am going to utilize the MariaDB stack as a back-end database for it that was deployed earlier.

version: "3.7"
 
services:
  wordpress:
    image: wordpress:php7.4-fpm-alpine
    depends_on:
      - maindb
    volumes:
      - /mnt/wordpress-caddy:/var/www/html
    secrets:
      - wordpress_db_host
      - wordpress_db_user
      - wordpress_db_name
      - mysql_db_password
    environment:
      - WORDPRESS_DB_HOST_FILE=/run/secrets/wordpress_db_host
      - WORDPRESS_DB_USER_FILE=/run/secrets/wordpress_db_user
      - WORDPRESS_DB_NAME_FILE=/run/secrets/wordpress_db_name
      - WORDPRESS_DB_PASSWORD_FILE=/run/secrets/mysql_db_password
    networks:
      - caddy
    ports:
      - "9000:9000"
    deploy:
      placement:
        constraints: [node.role == worker]
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure
secrets:
  wordpress_db_host:
    file: ./wordpress_db_host.txt
  wordpress_db_user:
    file: ./wordpress_db_user.txt
  wordpress_db_name:
    file: ./wordpress_db_name.txt
  mysql_db_password:
    file: ./mysql_db_password.txt 
volumes:
  wordpress-caddy:
    driver: "local"
networks:
  caddy:
    external: true

Don’t forget to map WordPress data directory /mnt/wordpress-caddy:/var/www/html in Caddy configuration caddy.yml. Here is Caddy Deployment Post for more info. I used wordpress:php7.4-fpm-alpine docker image to deploy WordPress behind Caddy. Also caddy docker overlay network for the application to be accessible externally.

Please find the full docker-compose file for WordPress including caddy server configuration.

version: "3.7"

services:
  caddy:
    image: tuneitme/caddy
    ports:
      - "80:80"
      - "443:443"
    networks:
      - caddy
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - /mnt/caddydata:/data
      - /mnt/caddyconfig:/config
      - /mnt/caddylogs:/var/log/caddy
      - /mnt/wordpress-caddy:/var/www/html
    environment:
      ACME_AGREE: 'true'
    deploy:
      placement:
        constraints:
          - node.role == manager
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

  wordpress:
    image: wordpress:php7.4-fpm-alpine
    depends_on:
      - maindb
    volumes:
      - /mnt/wordpress-caddy:/var/www/html
    secrets:
      - wordpress_db_host
      - wordpress_db_user
      - wordpress_db_name
      - mysql_db_password
    environment:
      - WORDPRESS_DB_HOST_FILE=/run/secrets/wordpress_db_host
      - WORDPRESS_DB_USER_FILE=/run/secrets/wordpress_db_user
      - WORDPRESS_DB_NAME_FILE=/run/secrets/wordpress_db_name
      - WORDPRESS_DB_PASSWORD_FILE=/run/secrets/mysql_db_password
    networks:
      - caddy
    ports:
      - "9000:9000"
    deploy:
      placement:
        constraints: [node.role == worker]
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure
secrets:
  wordpress_db_host:
    file: ./wordpress_db_host.txt
  wordpress_db_user:
    file: ./wordpress_db_user.txt
  wordpress_db_name:
    file: ./wordpress_db_name.txt
  mysql_db_password:
    file: ./mysql_db_password.txt 
volumes:
  caddydata:
    driver: "local"
  caddyconfig:
    driver: "local"
  caddylogs:
    driver: "local"
  wordpress-caddy:
    driver: "local"
networks:
  caddy:
    external: true

Deploy WordPress using Docker Compose

Now it’s time to deploy our docker-compose file above, wordpress.yml using the below command

docker stack deploy --compose-file wordpress.yml wp

In the above command, you have to replace wordpress.yml with your docker-compose file name and rock with whatever name you want to call this particular application

As mentioned earlier I named my docker-compose as wordpress.yml and named my application stack as wp

With docker compose in docker swarm what ever we are deploying is called as docker stack and it has multiple services in it as per the requirement.

Access / Install WordPress

Now type http://wordpress.example.com in the browser of choice, it will automatically be redirected to https://wordpress.example.com/wp-admin/install.php ( Be sure to replace example.com in the example above with your actual domain name).

Warning

Make sure that you have DNS entry for your application (wordpress.example.com) in your DNS Management Application.

Please find below images for your reference.

WordPress Installation - Language Selection WordPress Installation - Basic Information WordPress Installation Success WordPress Login Screen Caddy WordPress Site

Deployment of WordPress behind Caddy in our Docker Swarm successful using PHP FPM Docker Image that is useful for heavy-loaded sites.

Stay tuned for more application deployments in our Docker Swarm Cluster behind Caddy 🙄

Let me know your feedback or thoughts by commenting on it below.