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

add example for how to install modules #230

Open
phohomi opened this issue Dec 1, 2022 · 3 comments
Open

add example for how to install modules #230

phohomi opened this issue Dec 1, 2022 · 3 comments

Comments

@phohomi
Copy link

phohomi commented Dec 1, 2022

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.

@tianon tianon transferred this issue from docker-library/docs Dec 1, 2022
@kjostling
Copy link

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

@phohomi
Copy link
Author

phohomi commented Jan 2, 2024

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:

@kjostling
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants