Solving MySQL / MariaDB startup / connection problems when running ZoneMinder in a Docker image

After a few years away from ZoneMinder, I decided to reinstall, but this time using a Docker image. I selected dlandon’s very helpful packaging rather than attempting to create my own image. I created the necessary directories (for the mapped in recording cache and for the MySQL database) and started up the image with the parameters as described in dlandon’s documentation.

However, the image kept refusing to start for me. I would get:

Starting MariaDB database server mysqld,
...fail!

First thing to do was to figure out what the problem was for mysqld. You can’t just run a shell inside the image though, as the image has quit by the time you’re looking at it. So let’s modify the image to start up with a bash shell (while keeping its state, so that we can check the logs, etc.) and poke around inside. I recommend storing all this in a script so you can repeat it! Here’s the script I used to commit a new copy with bash as the entry point, start it up, and then delete it afterwards (while it’s running you can exec bash in it from another terminal if you want to have multiple terminals poking around, which can be helpful):

YOURCONFIGPATH=your config path goes here
YOURCACHEPATH=your cache path goes here
YOURUSERNAME=your user name goes here

# Get the original container
CONTAINERID=`docker ps -a | grep zoneminder | awk '{print $1}'`

# Clone it
docker commit ${CONTAINERID} ${YOURUSERNAME}/test

# Start it with bash as the modified entry point
docker run -it --entrypoint=bash \
--name="Zoneminder2" \
--privileged="true" \
-p 8443:443/tcp \
-p 9000:9000/tcp \
-e TZ="America/Chicago" \
-e SHMEM="30%" \
-e PUID="99" \
-e PGID="100" \
-v "${YOURCONFIGPATH}":"/config":rw \
-v "${YOURCACHEPATH}":"/var/cache/zoneminder":rw \
${YOURUSERNAME}/test

# Clean up afterwards ...
docker images -a | grep "${YOURUSERNAME}/test" | awk '{print $3}' | xargs docker rmi -f
docker ps -a | grep "Zoneminder2" | awk '{print $1}' | xargs docker rm

Now you can poke around inside the container to figure out why mysqld isn’t happy running. You’ll find your key log file here:

/var/log/mysql/error.log

You may want to clean out the log file (it probably has some goop inherited from the original docker build) and re-run the service to get a clean and easy view:

rm /var/log/mysql/error.log
service mysql start

OK, so what you’re probably seeing in the log file is a warning about not being able to write a test file, and some following MariaDB errors about bad permissions (I’m no longer getting them, so this is from memory/Googling others):

[Warning] Can't create test file

This is indicating that MySQL can’t write to the directory you’ve pointed it to.

First, ensure that there are no basic permissions problems. I won’t get too into this, as you should be able to do this — but make sure that wherever you put the database (it’s going to be in ${CONFIG}/mysql) is fully accessible (e.g., the directory path can all be read, the directory itself is writable, etc.)

I’m assuming you have what look to be fine permissions — I sure did.

At this point, you probably have one of three culprits:

  1. If you located the CONFIG outside of the normal spots (let’s say, anywhere outside of /var), then it’s possible you’re running into MariaDB’s own protection. It’s got some settings to prevent writing to /usr, /etc, /home, and so forth. This wasn’t my problem, but I found it while trying to figure out what my problem was. If this is your problem, easiest would be to relocate the directory, but if you can’t, you may find some help in tinkering with the ProtectHome and ProtectSystem settings for MySQL in the configuration (inside the docker image! — which introduces its own long-term complexities).
  2. If you’re running SELinix in your host system, SELinux may be blocking access to your data directory. I’m not, so I can’t give you details, but you may find SELinux and MySQL by Jeremy Smyth to be of use.
  3. For me, the problem was that AppArmor (in my host system) was blocking access. I could see this very clearly (once I started looking) by checking /var/log/syslog and looking for apparmor lines. You can find the fix for this in AppArmor and MySQL by Jeremy Smyth . In brief, the fix is to add two lines to the bottom of /etc/apparmor.d/local/usr.sbin.mysqld (customized for your data location, of course):
/YOURDATAPATH/ r,
/YOURDATAPATH/** rwk,

Et voila, everything started working!