Skip to content

Instantly share code, notes, and snippets.

@pwillis-els
Created June 25, 2021 14:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pwillis-els/b9568db8bbb1e1bba7000e79fbde9ac4 to your computer and use it in GitHub Desktop.
Save pwillis-els/b9568db8bbb1e1bba7000e79fbde9ac4 to your computer and use it in GitHub Desktop.
Best practice for building and running Wordpress + Bedrock + Apache2 + PHP-FPM + MariaDB in an Alpine container with docker-compose
FROM alpine:3.13.5 AS php7
ENV PHP_VER=7.4
ENV RUNTIME_USER=www-data
ENV RUNTIME_GROUP=www-data
# Alpine www-data UID/GID is 82
ARG RUNTIME_UID=82
ENV RUNTIME_UID=$RUNTIME_UID
ARG RUNTIME_GID=82
ENV RUNTIME_GID=$RUNTIME_GID
RUN apk add -u --no-cache aws-cli bash curl ca-certificates jq sudo tini shadow ; \
[ -e /usr/bin/tini ] || ln -sf /sbin/tini /usr/bin/tini
# install the PHP extensions we need
# - https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions)
# - https://wiki.alpinelinux.org/wiki/WordPress
RUN set -eux; \
apk add -u --no-cache \
php7 php7-fpm php7-common php7-session php7-iconv php7-json php7-gd php7-curl php7-xml php7-mysqli php7-imap php7-cgi \
php7-pdo php7-pdo_mysql php7-soap php7-xmlrpc php7-posix php7-mcrypt php7-gettext php7-ldap php7-ctype php7-dom \
php7-mbstring php7-zip php7-pecl-imagick php7-pecl-ssh2 php7-intl php7-phar php7-tokenizer php7-xmlwriter php7-simplexml \
libxml2 fcgi wget curl mysql-client ghostscript gnupg openssl ; \
getent group www-data || addgroup -g ${RUNTIME_GID} -S www-data ; \
getent passwd www-data || adduser -u ${RUNTIME_UID} -D -S -G www-data www-data ; \
mkdir -p /run/php ; \
chown -R ${RUNTIME_USER}:${RUNTIME_GROUP} /run/php ; \
# Create a symlink to this Unix socket so we don't have to write configs against a single version
ln -sf /run/php/php${PHP_VER}-fpm.sock /run/php/php-fpm.sock ; \
ln -sf /run/php/php${PHP_VER}-fpm.pid /run/php/php-fpm.pid
FROM php7 AS php7-apache2
ENV RUNTIME_USER=www-data
ENV RUNTIME_GROUP=www-data
RUN set -eux; \
apk add -u --no-cache \
apache2 apache2-proxy apache2-proxy-html apache2-ctl apache2-ssl apr apr-util \
apr-util-dbd_sqlite3 apr-util-ldap brotli-libs && \
mkdir -p /var/run/apache2 /var/log/apache2 /var/lock/apache2 && \
chown -R ${RUNTIME_USER}:${RUNTIME_GROUP} /var/run/apache2 /var/log/apache2 /var/lock/apache2 && \
# Add www-data to the ssl-cert group to read the /etc/ssl/private/ keys
if getent group ssl-cert ; then usermod -aG ssl-cert www-data ; fi && \
if getent passwd apache ; then usermod -aG www-data apache ; fi
ARG TLS_HOST
ENV TLS_HOST=$TLS_HOST
RUN openssl req \
-new -newkey rsa:4096 -days 3650 -nodes -x509 \
-subj "/C=US/ST=City/L=State/O=O/CN=$TLS_HOST" \
-keyout domain.key -out domain.crt \
&& mv domain.key domain.crt /etc/apache2/
FROM php7 AS php7-bedrock
ENV BEDROCK_DIR=/app
# Download and install composer.phar
RUN set -eux && \
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" && \
php composer-setup.php --install-dir=/usr/local/bin --filename=composer --version=2.0.8 && \
php -r "unlink('composer-setup.php');" && \
# Make sure we can write the composer cache as the user
homedir=`getent passwd ${RUNTIME_USER} | cut -d : -f 6` && \
mkdir -p $homedir/.composer $homedir/.cache/composer ${BEDROCK_DIR} && \
chown -R ${RUNTIME_USER}:${RUNTIME_GROUP} $homedir/.composer $homedir/.cache/composer ${BEDROCK_DIR}
# Change to Bedrock dir to copy files and run composer
USER ${RUNTIME_USER}
WORKDIR ${BEDROCK_DIR}
COPY --chown=${RUNTIME_USER}:${RUNTIME_GROUP} composer.json composer.lock ./
RUN set -eux && \
composer install --no-scripts --no-autoloader && \
homedir=`getent passwd ${RUNTIME_USER} | cut -d : -f 6` && \
rm -rf $homedir/.composer/* $homedir/.cache/composer/*
# Then copy anything else which might have invalidated the cache.
# This way dependencies are cached in the step above.
COPY --chown=${RUNTIME_USER}:${RUNTIME_GROUP} wp-cli.yml plugins.txt phpcs.xml ./
COPY --chown=${RUNTIME_USER}:${RUNTIME_GROUP} web web
COPY --chown=${RUNTIME_USER}:${RUNTIME_GROUP} config config
RUN set -eux ; composer dump-autoload --optimize
FROM php7-apache2 AS production-bedrock
ENV BEDROCK_DIR=/app
ENV PHP_VER=7.4
ENV HTTPD_PREFIX=/etc/apache2
ARG HTTPD_LOGLEVEL=debug
ENV HTTPD_LOGLEVEL=$HTTPD_LOGLEVEL
ARG PHP_FPM_LOGLEVEL=notice
ENV PHP_FPM_LOGLEVEL=$PHP_FPM_LOGLEVEL
# This is the Apache2 docroot, so it must end in '/web'.
# The WP_SITEURL must then set to include '/wp'.
# If both are not done, the site will not load correctly.
ENV PHP_APP_DIR=/app/web
ENV PHP_PORT=9000
ENV RUNTIME_USER=www-data
ENV RUNTIME_GROUP=www-data
# Alpine www-data UID/GID is 82
ARG RUNTIME_UID=82
ENV RUNTIME_UID=$RUNTIME_UID
ARG RUNTIME_GID=82
ENV RUNTIME_GID=$RUNTIME_GID
WORKDIR ${BEDROCK_DIR}
COPY --from=php7-bedrock /usr/local/bin/composer /usr/local/bin/composer
COPY --from=php7-bedrock --chown=${RUNTIME_USER}:${RUNTIME_GROUP} ${BEDROCK_DIR} ${BEDROCK_DIR}
USER root
# This is mostly for calling /usr/sbin/php7.4-fpm and its sub-processes
ENV PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin
# add the user to the tty group so it can write to /dev/pts/0
# (stdout and stderr are all /dev/pts/0 in docker)
RUN set -eux ; usermod -aG tty ${RUNTIME_USER}
# used by Bedrock
ARG WP_ENV=development
ENV WP_ENV=$WP_ENV
RUN rm -rf /etc/apache2/*
# Uncomment these to copy Apache and PHP-FPM configuration into the container
#COPY --chown=${RUNTIME_USER}:${RUNTIME_GROUP} .deploy/etc/ /etc/
#COPY --chown=${RUNTIME_USER}:${RUNTIME_GROUP} .deploy/usr/ /usr/
# Note: this command includes a sanity check of apache and php
RUN set -eux ; \
[ -e /etc/apache2/conf.d/proxy-html.conf ] && sed -i -e 's/libxml2\.so$/libxml2.so.2/' /etc/apache2/conf.d/proxy-html.conf ; \
ln -sf /etc/apache2/envvars /usr/sbin/envvars ; \
[ ! -e /usr/lib/apache2/modules ] && ln -sf /usr/lib/apache2 /usr/lib/apache2/modules ; \
[ ! -e /etc/mime.types ] && ln -sf /etc/apache2/mime.types /etc/mime.types ; \
touch /usr/share/apache2/ask-for-passphrase ; \
apachectl -t ; \
cp -a /etc/php/phpenmod /etc/php/phpdismod /etc/php/phpquery /usr/sbin/ ; \
/etc/php/phpenmod `cd /etc/php/${PHP_VER}/mods-available/; ls *.ini | sed -e 's/\.ini//g'` ; \
[ ! -e /usr/sbin/php-fpm${PHP_VER} ] && [ -e /usr/sbin/php-fpm7 ] && ln -sf /usr/sbin/php-fpm7 /usr/sbin/php-fpm${PHP_VER} ; \
php-fpm${PHP_VER} -t ; \
chown ${RUNTIME_USER}:${RUNTIME_GROUP} ${BEDROCK_DIR}
EXPOSE 8080
EXPOSE 8443
# https://httpd.apache.org/docs/2.4/stopping.html#gracefulstop
#STOPSIGNAL SIGWINCH
STOPSIGNAL SIGQUIT
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
COPY apache-wordpress.sh /usr/local/bin/apache-wordpress.sh
CMD [ "/usr/local/bin/apache-wordpress.sh" ]
# Look up AWS secrets on start-up
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
HEALTHCHECK --interval=10s --timeout=30s --retries=3 CMD curl -iLf http://localhost:8080/ || exit 1
#!/usr/bin/env bash
# Start PHP-FPM and Apache in the Docker container
#
# Set PHP_VER and PHP_APP_DIR in your Docker container's ENV settings.
#
[ "${DEBUG:-0}" = "1" ] && set -x
set -eu
_cmd_start-php () {
echo "$0: Running: /usr/sbin/php-fpm${PHP_VER} in ${PHP_APP_DIR}"
( cd ${PHP_APP_DIR} && /usr/sbin/php-fpm${PHP_VER} -F ) &
}
_cmd_start-apache () {
echo "$0: Running: apache2-foreground &"
apache2-foreground &
}
_cmd_start () {
_cmd_start-php
_cmd_start-apache
}
# default command
CMD="start"
if [ "${1:-}" = "--help" ] ; then
cat <<EOUSAGE
Usage: $0 [CMD]
Runs a command, and waits for any background process to exit.
Commands:
start Start PHP-FPM and Apache
start-php Start PHP-FPM
start-apache Start Apache
EOUSAGE
exit 1
fi
if [ $# -gt 0 ] ; then
CMD="$1"; shift
fi
_cmd_"$CMD"
# Wait for any background process to die
wait -n
version: "3"
services:
mysql:
container_name: wordpress-mysql
image: mariadb/server:10.4
restart: always
volumes:
- wordpress-mysql:/var/lib/mysql
wordpress:
container_name: wordpress
depends_on:
- mysql
restart: always
build:
context: .
dockerfile: alpine.Dockerfile
args:
- BEDROCK_DIR=${BEDROCK_DIR}
- HTTPD_LOGLEVEL=${HTTPD_LOGLEVEL}
- PHP_FPM_LOGLEVEL=${PHP_FPM_LOGLEVEL}
- RUNTIME_GROUP=${RUNTIME_GROUP}
- RUNTIME_USER=${RUNTIME_USER}
- TLS_HOST=${TLS_HOST}
ports:
- 80:8080
- 443:8443
volumes:
wordpress-mysql: {}
#!/bin/sh
[ "${DEBUG:-0}" = "1" ] && set -x
echo "$0: Running as: $(id -un) ($(id -u))"
echo ""
# Set PHP-FPM defaults at runtime
if [ "${ENV:-}" = "production" ] ; then
cp -f /usr/lib/php/${PHP_VER}/php.ini-production /etc/php/${PHP_VER}/fpm/php.ini
fi
# Set PHP-FPM log level at runtime
PHP_FPM_LOGLEVEL="${PHP_FPM_LOGLEVEL:-notice}"
sed -i -e "s/log_level.*/log_level = $PHP_FPM_LOGLEVEL/g" /etc/php/${PHP_VER}/fpm/php-fpm.conf
# Modify the UID and GID of the runtime user (if they changed at runtime)
OLDUID=$(id -u $RUNTIME_USER)
OLDGID=$(id -g $RUNTIME_USER)
if [ ! "$OLDUID" = "$RUNTIME_UID" ] || [ ! "$OLDGID" = "$RUNTIME_GID" ] ; then
usermod -u $RUNTIME_UID $RUNTIME_USER
groupmod -g $RUNTIME_GID $RUNTIME_USER
echo "$0: Please wait, changing filesystem ownership (this will take a while)..."
find /var/ /run/ -uid $OLDUID -exec chown -hR $RUNTIME_USER:$RUNTIME_GID {} \;
chown -hR $RUNTIME_USER:$(id -g $RUNTIME_USER) /app/
fi
chown $RUNTIME_USER:$RUNTIME_GROUP /proc/self/fd/*
exec sudo -H -E -u $RUNTIME_USER -s /bin/bash "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment