add example for how to install modules
phohomi opened this issue · comments
Hi,
Drupals power comes from it's large library of modules, but there is no description of how to install a module. Only how to mount a folder with modules, and it is no longer advised to download and unzip them manually.
A dockerfile based on this official image in which composer require ... is demonstrated, together with a docker file that builds this dockerfile: wouldn't that be something to add to the documentation?
Currently when you search for Drupal and docker, you find a lot of alternative docker images, and very few examples of how to work with the official image in a best practice fashion.
Since composer 2.4 I've added a drupal user to install modules since root is not allowed anymore. Otherwise you just run composer require in a site-specific Dockerfile.
FROM drupal:10.1-php8.1
ARG user="drupal"
ARG uid=1000
RUN set -eux; \
useradd -G www-data -u $uid -d /home/$user $user; \
mkdir -p /home/$user/.composer && chown -R $user:$user /home/$user; \
chown -R $user:$user /opt/drupal;
USER drupal
RUN set -eux; \
composer require drupal/mailgun \
drupal/menu_block;
USER root
Hi Johan,
Thanks for the feedback, I havent yet run into that problem, probably because I'm currently still using 9.5. That is good to know, and an extra reason to have such an example available for people just starting out.
I see now that I made a mistake in my first post: I meant a docker file, and a docker COMPOSE file.
Now that I have a bit more experience with running drupal from a docker container, I can share a bit of what I've puzzled together. The user parts are still missing though.
Here's an example of a docker script based on mine, in this script:
- additional software is installed (unzip)
- a php extension is installed (APCu)
- there are a couple of composer settings which are altered
- there's a compose require command to show how to install drupal modules
- some extra configuration of composer and a composer update command for installing libraries for the popular webform module, based on the documentation at https://www.drupal.org/node/3003140
FROM drupal:VERSION HERE
RUN set -eux; \
apt-get update && apt-get install -y unzip; \
pecl install APCu \
&& docker-php-ext-enable apcu \
&& ....
export COMPOSER_HOME="$(mktemp -d)"; \
composer config allow-plugins.composer/* true --no-interaction; \
composer config allow-plugins.drupal/core* true --no-interaction; \
composer config allow-plugins."wikimedia/composer-merge-plugin" true --no-interaction; \
composer require 'drupal/SOME_MODULE:VERSION \
'drupal/OTHER_MODULE:VERSION' \
...
'wikimedia/composer-merge-plugin' \
--no-interaction; \
composer config --json extra."merge-plugin" '{"include": ["web/modules/contrib/webform/composer.libraries.json"]}' --no-interaction; \
composer update drupal/webform 'drupal/webform-*' --with-dependencies --no-interaction; \
# delete composer cache
rm -rf "$COMPOSER_HOME"
And here's a basic docker-compose file based on mine:
version: "3.5"
services:
mysql:
image: mysql:8.0
container_name: mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
MYSQL_ROOT_PASSWORD: ROOT_PASSWORD
MYSQL_DATABASE: drupal
MYSQL_USER: drupal
MYSQL_PASSWORD: PASSWORD
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
drupal:
build: .
container_name: drupal
depends_on:
- mysql
restart: always
ports:
- "80:80"
volumes:
#- ./files/modules:/var/www/html/modules
- ./files/sites:/var/www/html/sites
#labels: for traefik, but that is out of scope
volumes:
mysql-data:
Thanks! Mine are as follows. Should perhaps also include acpu since its already kind-of big.
FROM drupal:10.1-php8.1
# persistent dependencies
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
ghostscript \
less \
git \
curl \
zip \
; \
rm -rf /var/lib/apt/lists/*
# install the PHP extensions we need
RUN set -eux; \
savedAptMark="$(apt-mark showmanual)"; \
apt-get update; \
apt-get install -y --no-install-recommends \
libmagickwand-dev \
; \
# https://pecl.php.net/package/imagick
pecl install imagick-3.6.0; \
docker-php-ext-enable imagick; \
rm -r /tmp/pear; \
\
rm -rf /tmp/* \
# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
apt-mark auto '.*' > /dev/null; \
apt-mark manual $savedAptMark; \
ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \
| awk '/=>/ { print $3 }' \
| sort -u \
| xargs -r dpkg-query -S \
| cut -d: -f1 \
| sort -u \
| xargs -rt apt-mark manual; \
\
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
rm -rf /var/lib/apt/lists/*
# Server settings
RUN cp /usr/share/zoneinfo/Europe/Stockholm /etc/localtime; \
cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini; \
# https://www.php.net/manual/en/errorfunc.constants.php
{ \
echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \
echo 'display_errors = Off'; \
echo 'display_startup_errors = Off'; \
echo 'log_errors = On'; \
echo 'error_log = /dev/stderr'; \
echo 'log_errors_max_len = 1024'; \
echo 'ignore_repeated_errors = On'; \
echo 'ignore_repeated_source = Off'; \
echo 'html_errors = Off'; \
} > /usr/local/etc/php/conf.d/error-logging.ini; \
# upload settings
{ \
echo 'post_max_size = 64M'; \
echo 'upload_max_filesize = 64M'; \
} > /usr/local/etc/php/conf.d/upload-limits.ini;
COPY docker.settings.local.php web/sites/default/settings.local.php
# Who owns app
ARG user="drupal"
ARG uid=1000
RUN set -eux; \
sed -i '/<\!-- disable ghostscript format types -->/d' /etc/ImageMagick-6/policy.xml; \
sed -i '/<policy domain=\"coder\" rights=\"none\" pattern=\"PS\" \/>/d' /etc/ImageMagick-6/policy.xml; \
sed -i '/<policy domain=\"coder\" rights=\"none\" pattern=\"PS2\" \/>/d' /etc/ImageMagick-6/policy.xml; \
sed -i '/<policy domain=\"coder\" rights=\"none\" pattern=\"PS3\" \/>/d' /etc/ImageMagick-6/policy.xml; \
sed -i '/<policy domain=\"coder\" rights=\"none\" pattern=\"EPS\" \/>/d' /etc/ImageMagick-6/policy.xml; \
sed -i '/<policy domain=\"coder\" rights=\"none\" pattern=\"PDF\" \/>/d' /etc/ImageMagick-6/policy.xml; \
sed -i '/<policy domain=\"coder\" rights=\"none\" pattern=\"XPS\" \/>/d' /etc/ImageMagick-6/policy.xml; \
useradd -G www-data -u $uid -d /home/$user $user; \
mkdir -p /home/$user/.composer && chown -R $user:$user /home/$user; \
cp web/sites/default/default.settings.php web/sites/default/settings.php; \
chmod a-w web/sites/default/settings.local.php; \
mkdir config; \
chown -R $user:$user /opt/drupal; \
[ -d "/opt/drupal/private" ] || mkdir /opt/drupal/private; \
[ -d "/opt/drupal/tmp" ] || mkdir /opt/drupal/tmp; \
chown -R www-data:www-data /opt/drupal/private; \
chown -R www-data:www-data /opt/drupal/tmp; \
sed -i "$(( $(wc -l < web/sites/default/settings.php) - 4 + 1)),\$s/#//g" web/sites/default/settings.php;
And from that I always make a site-specific Docker file with theme and modules that are specific for that site.
FROM my-drupal:10.1
COPY --chown=drupal:drupal ./web/themes/custom web/themes/custom/
COPY --chown=drupal:drupal ./web/modules/custom web/modules/custom/
USER drupal
RUN set -eux; \
composer config --no-plugins allow-plugins.wikimedia/composer-merge-plugin true; \
composer config --no-plugins allow-plugins.drupal/console-extend-plugin true; \
composer require wikimedia/composer-merge-plugin; \
composer config --json extra.merge-plugin '{"include": [ "web/modules/contrib/webform/composer.libraries.json"]}'; \
composer require drupal/bootstrap \
drupal/imagick \
drupal/mailgun \
drupal/menu_block \
drupal/metatag \
drupal/pathauto \
drupal/webform \
drush/drush:"^11.0"; \
composer update drupal/webform --with-dependencies;
USER root
And my docker-compose files only include uploads as a volume, traefik as proxy, shared mysql (with more generous config)
version: "3"
services:
site-www:
image: registry.gitlab.com/path/to/image:latest
restart: always
labels:
- "traefik.enable=true"
- "traefik.http.middlewares.site-www-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.site-www.middlewares=site-www-https-redirect"
- "traefik.http.routers.site-www.entrypoints=web"
- "traefik.http.routers.site-www.rule=Host(`www.domain.com`)"
- "traefik.http.routers.site-www-secure.entrypoints=websecure"
- "traefik.http.routers.site-www-secure.rule=Host(`www.domain.com`)"
- "traefik.http.routers.site-www-secure.tls=true"
- "traefik.http.routers.site-www-secure.tls.certresolver=myresolver"
- "traefik.http.routers.site-www-secure.service=site-www"
- "traefik.http.services.site-www.loadbalancer.server.port=80"
- "traefik.docker.network=web"
environment:
DRUPAL_TRUSTED_HOST_PATTERNS: '^www\.domain\.com$$'
DRUPAL_HASH_SALT: "salt-string"
DRUPAL_DB_HOST: mysql
DRUPAL_DB_NAME: db_name
DRUPAL_DB_USER: db_user
DRUPAL_DB_PASSWORD: db_password
networks:
- web
- mysql
volumes:
- site_data:/opt/drupal/web/sites/default/files
networks:
web:
name: 'web'
external: true
mysql:
name: 'mysql'
external: true
volumes:
site_data:
name: 'site_data'
But this is just how I do it for now, consider it as yet another example.