Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Building a Wordpress production image with a custom theme. #567

Closed
darkl0rd opened this issue Feb 23, 2021 · 9 comments · Fixed by docker-library/docs#2057
Closed

Building a Wordpress production image with a custom theme. #567

darkl0rd opened this issue Feb 23, 2021 · 9 comments · Fixed by docker-library/docs#2057
Labels
question Usability question, not directly related to an error with the image

Comments

@darkl0rd
Copy link

I am currently attempting to promote my image including custom theme to Production using a custom Dockerfile, like so:

FROM wordpress:latest

COPY wp-content/ /usr/src/wordpress/wp-content

The resulting image will have the wp-content directory in the "distribution" directory, which is copied to /var/www/html on initialisation when the right conditions are met.
The right conditions being that the following files do not exist in /var/www/html:

  • index.php
  • wp-includes/version.php

The first time, this works as expected as the target volume mounted on /var/www/html will be empty still. However, any subsequent updates will detect that /var/www/html has content in it and therefore initialisation will be skipped. With it, any changes made to my custom theme are not applied.

The simplest solution would be to not map a volume on /var/www/html, so that initialisation takes place every time - as each startup would effectively end up with an empty directory. However, the downside of this is that I end up with tons of dangling volumes, as the provided Wordpress image has a VOLUME directive set for this directory, which results in Docker creating an unnamed Volume to map on the directory. This is the approach I'm currently taking, together with a cleanup script running on the hosts to remove the volumes.

This is obviously less than ideal. Why is /var/www/html a Volume in the first place? Considering that in a local development environment the necessary content is bind mounted and in a production image the data is copied to /usr/src/wordpress.

@wglambert wglambert added the question Usability question, not directly related to an error with the image label Feb 23, 2021
@wglambert
Copy link

What about bind-mounting the /var/www/html/wp-content/themes/ folder?
https://github.com/docker-library/docs/tree/master/wordpress#include-pre-installed-themes--plugins

An approach to using a directory that's not a VOLUME: #232 (comment)

Other discussions on removing inherent volumes in images docker-library/mongo#306, redis/docker-library-redis#140, docker-library/postgres#404
Specifically the discussion over in docker-library/mysql#255: "Why is there volume for data in the first place?"

@tianon
Copy link
Member

tianon commented Feb 23, 2021

With #557, you can theoretically run completely stateless as long as you've got the appropriate environment variables and don't try to add anything that goes into wp-content like image uploads -- that should even allow running with --read-only (assuming you point the webserver at /usr/src/wordpress instead of /var/www/html), although I admit I haven't tested that use case.

@darkl0rd
Copy link
Author

Thanks for all the feedback and suggestions, however the question as to why this 'VOLUME' directive is there in the first place is still unclear to me?

Running with a bind mounted directory, you don't use the volume. Running a production image, you have the data copied in /usr/src/wordpress, which is copied to /var/www/html on init - to me it seems, at this point the VOLUME is just in the way. If someone still wants to mount something on /var/www/html, they can do it; you don't need to VOLUME directive for that.

The only thing the VOLUME directive does is:
a) make it clear this is where application data is stored.
b) explicitly decouple the state of the specified directory.

In case of b, if you want to persist, you can mount a volume regardless. If you don't mount a volume, you'll end up with an unnamed volume (exactly my problem), for which I can not come up with a use case.

@yosifkit
Copy link
Member

The volume is the only reliable way to let users know where the mutable state is stored.

WordPress was not designed for containers and, as such, considers all of its installation mutable state (and applies security updates automatically). So we designed the WordPress image to reflect that; the initial start will fill the volume (whether a bind-mount or named/unnamed docker volume) with the install and WordPress would then take care of updates. You can even update the container around WordPress to get newer PHP without it effecting the WordPress install.

In the early days of docker, volumes did not exist without a contianer and so tools like fig/docker-compose used tricks to keep volumes for new containers and still does so for named or unnamed volumes.

@darkl0rd
Copy link
Author

I understand, however if the only reason is to point out where the "mutable state" is, is it worth the problems it introduces? As I described in my (which I think are the common use-cases) use-cases:

  • You either bind mount your dev tree onto /var/www/html
  • You extend the image through a Dockerfile and copy your content to /usr/src/wordpress, which in turn will, as it stands populate /var/www/html

If you do not mount a volume on /var/www/html, an unnamed volume will be mounted on it by Docker - which results in pollution.

In case of a named volume, the volume is useless - since when you add the data to /usr/src/wordpress, it's copied to /var/www/html (your volume), meaning any new versions of your extended image (changes to themes etc.) are not copied to /var/www/html anymore.

So, what's a real use-case for wanting to persist /var/www/html, considering that it will be populated by the content of /usr/src/wordpress when it's empty?

Can we add a separate folder, let's say /usr/src/wordpress/themes or /themes (and plugins) that is always copied to /var/www/html upon startup to the default image? I can easily add this in my image, but I think this might benefit others as well.

@oxodesign
Copy link

@darkl0rd do you mind sharing your custom Dockerfile with the script that copies data from /usr/src/wordpress to /var/www/html on startup?

Thanks in advance

@darkl0rd
Copy link
Author

darkl0rd commented Oct 7, 2021

Dockerfile

FROM wordpress:5.8.1

# Production image #
# 1. Embed content
# 2. Add custom entrypoint to re-ensure init of /var/www/html
# 3. Drop wp-admin

# Cleanout built-in themes and plugins.
RUN  rm -R $(ls -1 -d /usr/src/wordpress/wp-content/plugins/*) \
  && rm -R $(ls -1 -d /usr/src/wordpress/wp-content/themes/*)

# Inject our Plugins, themes, etc. into the image.
COPY wp-content/ /usr/src/wordpress/wp-content
COPY .htaccess /usr/src/wordpress/.htaccess
COPY docker/docker-entrypoint-override.sh /usr/local/bin

HEALTHCHECK --interval=30s --timeout=10s --retries=3 CMD curl -o /dev/null -s --fail http://localhost/health
ENTRYPOINT ["/usr/local/bin/docker-entrypoint-override.sh"]
CMD ["apache2-foreground"]

docker-entrypoint-override.sh

#!/bin/bash

echo "Entrypoint override"
if mount | grep -q /var/www/html/wp-content
then
  # entrypoint from Wordpress initializes /var/www/html; ignores wp-content directory
  # If this is bind-mounted, it's a "development area".
  echo "/var/www/html/wp-content is mounted, skipping"
else
  # This is done because a volume is mapped on /var/www/html - otherwise on each startup the tree would get populated in an unnamed volume
  # resulting in many dangling volumes.

  echo "Re-initializing /var/www/html ..."
  rm -fr /var/www/html/*
fi

echo "Executing built-in entrypoint"
exec /usr/local/bin/docker-entrypoint.sh "$@"

@ILCAI
Copy link

ILCAI commented Oct 14, 2021

I have one questions to you.

Isn't one big (or the) feature of "using docker for a WordPress project" to reflect tested changes in code (new features) directly in a production website (live)?

  • I user wordpress:latest as base from a Dockerfile, together with another service called database in a docker-compose.yml
  • I add functionality to a theme (cause the theme and/or some custom plugins will be the only thing you will track with git in most cases).
  • I commit and merge my changes (e.g.) to a master branch
  • A build is triggered on a build-server building a new image (containing the new features => COPY themes/theme /var/www/html/wp-content/themes/custom-theme)
  • on the production server the new image gets pulled, the container restarted

But if the updated theme file does not get copied anymore ... whats the point of using docker? Just for having a WordPress with an initial theme-setting running in a container? Or am I missing something?

@wglambert you asked "What about bind-mounting the /var/www/html/wp-content/themes/ folder?" Yes, I could do that, but how do I get my theme code into there? I need to apply my changes at build-time. Bind-mounting would need another step to checkout a theme from a repository and copy it to the bind-mounted directory when I want to update a website ...

@darkl0rd
Copy link
Author

@ILCAI I am not sure whether your question was directed at me? In case it was - this is exactly what I am requesting and achieving with my customisations as I see no way to do exactly that with the 'stock' image (see my earlier post and script customisations) - now it's of course entirely possible I'm not using the image correctly or missing something, which is why I raised this question. If I am, please enlighten me, because I would prefer using the community provided image rather than having to roll my own.

Developers mount /var/www/html/wp-content.
Production images have their content copied into /var/www/html/wp-content.

The question I had, at least with my workflow, is what's the use-case for the VOLUME instruction in the Dockerfile? If you bind mount a directory on '/var/www/html/wp-content' it's not used (you can mount anything on everything regardless of a VOLUME instruction). The only thing the VOLUME instruction here does is create a unnamed docker volume when you do not pass in a volume mapping - which is actually annoying in the case where you build production images which have their content copied into them (as each deployment will result in a new unnamed volume).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Usability question, not directly related to an error with the image
Projects
None yet
6 participants