docker-library / drupal

Docker Official Image packaging for Drupal

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.