This document is a record of problems that I have encountered when setting up a Docker container from scratch. The container I was creating was to run Mediawiki with MySQL and Apache.
An extra wrinkle in my case is that I already have such a system running on bare metal. I therefore have to do strange things with host names, which cannot be determined until the container starts. There are actually three wikis, so there are three hostnames.
The directory structure is:
$root | The root of the build context |
$root/conf | Files to be copied into the container |
$root/data | Mutable, peristent files (mediawiki, apache, mysql) |
$root/test | Files that are only used for testing |
$root/data/mysql | The mysql database (moved from /var/lib/mysql) |
$root/data/{dtwt,toc,tfd} | The roots of the three wikis |
$root/data/{www,mysqlBackup} | Different types of backups |
$root/data/{aliases,sites-available,backwww} | Files that have to be copied (and patched) into final destinations by /run.sh |
Here is the start of my Dockerfile
FROM ubuntu:20.04 MAINTAINER Me Myself <me@mydomain.com> ARG DTWT=dtwt.mydomain.com ARG TFD=tfd.mydomain.com ARG TOC=toc.mydomain.com # Debug/dev packages to install ARG EXTRAS='' # Clear apt cache? ('&& apt clean') ARG CLEAN='' ENV PHPVERSION 7.4 # We are installing a few packages that are just for development. They can be # removed in due course. # 1. Stuff that is standard for all wikis with mysql # cron requires procps and a MTA. RUN apt update \ && apt upgrade -f -y \ && DEBIAN_FRONTEND=noninteractive apt install -y \ $EXTRAS \ procps rsyslog curl cron exim4 \ php mysql-server mysql-client mediawiki \ $CLEAN \ && mkdir -p /etc/apache2/ssl /usr/local/sbin /var/run/mysqld \ && echo "exit 0\n" >/usr/sbin/policy-rc.d \ && chmod 755 /var/run/mysqld /usr/sbin/policy-rc.d COPY conf/locale /etc/default/ COPY conf/update-exim4.conf.conf /etc/exim4/ COPY conf/exim4.conf.localmacros /etc/exim4/ # Can't do this until the container starts as we need to know the final # hostname for an exim macro #RUN update-exim4.conf EXPOSE 80 443
The build script passes in various ARGs that determine how the image is built
#! /bin/bash -x # Generally needs to run as root. src=. if [ "$1" == prod ] then export CLEAN='&& apt clean ' export EXTRAS='' export DTWT=dtwt.mydomain.com export TFD=tfd.mydomain.com export TOC=toc.mydomain.com else export CLEAN='' export EXTRAS='telnet vim net-tools' export DTWT=dtwt.docker.mydomain.com export TFD=tfd.docker.mydomain.com export TOC=toc.docker.mydomain.com fi imagename=`basename $PWD| dd conv=lcase 2>/dev/null` EXTRAS="$EXTRAS" \ CLEAN="$CLEAN" \ DTWT="$DTWT" \ TFD="$TFD" \ TOC="$TOC" \ script -c "docker build -t $imagename $src" cp -a conf/sites-available data/
The second part of the Dockerfile deals with my specific needs:
# 2. Stuff that is specific to our wiki COPY conf/run.sh / RUN mkdir -p /etc/letsencrypt/live/$DTWT \ && chmod +x /run.sh # The backup scripts that are run daily or weekly COPY conf/backwww.* /usr/local/sbin/ # The crontab file to run the backups COPY conf/backwww /etc/cron.d/ RUN chmod 750 /usr/local/sbin/backwww* \ && chmod 644 /etc/cron.d/backwww \ && a2dissite 000-default # Ensure that cron can send emails COPY conf/aliases /etc/aliases HEALTHCHECK --interval=60s --timeout=5s --start-period=60s \ CMD curl -f https://$DTWT/ || exit 1 CMD ["/run.sh"]
Finally, the run script that will do the final patching that cannot be done until the container starts and runs the services. Note that the Apache configs contain parametrised host names that have to be fixed by a sed script. Similarlly, we have to patch the MySQL data directory and update some PHP parameters. We also have to fix ownership and permissions on some files and directories: the Docker build does not always get them right.
#!/bin/bash -x export USER=root APACHE=/etc/apache2 cd /data/sites-available for f in mydomain*.conf do # Fixup the hostnames in the apache config sed -e "/DTWT/s/DTWT/$DTWT/" \ -e "/TOC/s/TOC/$TOC/ " \ -e "/TFD/s/TFD/$TFD/" \ $f > $APACHE/sites-available/$f done cp /data/sites-available/options-ssl-apache.conf /etc/letsencrypt/options-ssl-apache.conf # Fix the maximum file upload size for PHP. The wiki max size is in the # wiki configs cd /etc/php/$PHPVERSION for f in */php.ini do sed -i.bak -E -e '/post_max_size *=/s/[0-9]+M/50M/' \ -e '/upload_max_filesize *=/s/[0-9]+M/50M/' $f done # Fix the MySQL data directory sed -i.bak -E -e '/datadir\s*=/s"^.*$"datadir = /data/mysql"' \ /etc/mysql/mysql.conf.d/mysqld.cnf a2enmod ssl a2enmod cgi a2enmod authz_user a2enmod php${PHPVERSION} a2enmod rewrite a2ensite mydomain.com a2ensite mydomain.com-le-ssl.conf # Crontabs to do backups chmod 750 /usr/local/sbin/backwww.* # Fix the From: address in cron-generated emails echo $DTWT > /etc/mailname # For test systems we want some extra packages if [ -n "$EXTRAS" ] then DEBIAN_FRONTEND=noninteractive apt install -y $EXTRAS a2ensite mydomain-test else apt clean fi # Start the services service procps start service rsyslog start echo "ETC_MAILNAME=$DTWT" >> /etc/exim4/exim4.conf.localmacros cp /etc/letsencrypt/live/$DTWT/cert.pem /etc/exim4/exim.crt cp /etc/letsencrypt/live/$DTWT/privkey.pem /etc/exim4/exim.key update-exim4.conf chown -R Debian-exim:adm /var/log/exim4 service exim4 start service cron start cd /data # mysql has different uids on the host and in the container chown -R mysql:mysql mysql chown -R www-data:www-data /data/mediawiki mkdir -p /run/mysqld chmod 755 /run/mysqld service mysql start # defining stop actions in case of SIGTERM or SIGINT graceful_stop() { echo "The container was asked to terminate its processes gracefully..." service mysql stop apachectl -k stop echo "Apache server is now stopped." echo "Asking for exit with code 143 (SIGTERM)..." exit 143 } # trapping SIGTERM and SIGINT termination signals and trigger actions trap 'graceful_stop' SIGTERM SIGINT apache2ctl -DFOREGROUND -k start # In case Apache exits we willl at least do a clean DB shutdowm service mysql stop
Ensure that the CMD that you run never exits. If it does, the container will also exit.
Install rsyslog, and remember to start it.
Cron requires procps and a mail server. ssmtp is the simplest send-only package, though I tend to use exim4 as that is what I am familiar with.
Depending on the security level of the mailhub you are using you may need to set MAILFROM and MAILTO in the crontabs to an address with a fully qualified hostnae. Docker, by default, just uses the container ID, and cron just says "cron". Such mails may bounce.
Exim4 creates a
Include
echo my.host.name > /etc/mailname
Create files /etc/exim4.conf.localmacros and /etc/update-exim4.conf.conf exim4.conf.localmacros should contain:
You do not need SSL certificates if this is a send-only server.MAIN_TLS_ENABLE= ETC__MAILNAME=my.host.name
update-exim4.conf.conf should be something like:
dc_smarthost is only needed if dc_eximconfig_type is either 'smarthost' or 'satellite'.dc_eximconfig_configtype='internet' dc_other_hostnames='my.alternative.host.name' dc_local_interfaces='' dc_readhost='' dc_relay_domains='' dc_minimaldns='false' dc_relay_nets='' dc_smarthost='my.smart.host' CFILEMODE='644' # Must be 'false' so that exim4.conf.localmacros should be read dc_use_split_config='false' dc_hide_mailname='false' dc_mailname_in_oh='true' dc_localdelivery='maildir_home'
You may need to do
I have
# Order is important update-exim4.conf service procps start service rsyslog start service exim4 start service cron start
If running a web-server you can do
at the end of the script to ensure that you get a clean DB shutdown before the container exits.service mysql stop
If you are getting the message '
Either in the Dockerfile or in the run script include
echo "exit 0\n" > /usr/sbin/policy-rc.d chmod 755 /usr/sbin/policy-rc.d
There are some glitches that I have not found a way of eliminating, but they do not seem to matter.
/etc/cron.daily/logrotate: invoke-rc.d: could not determine current runlevelo