Pulog

Drupal 9 の環境を Docker を用いて構築する

Drupal Advent Calendar 2020 - Qiita の8日目です。
ものすごくギリギリになっちゃいました、スミマセン。

2022/06/03 更新 また Drupal を触る機会が発生したので、記事の内容を2022年現在のものに刷新しています。

WSL2 上で起動させている Docker で Drupal 環境を構築していこうと思います。

Drupal は公式で Docker イメージを公開しているので、それをベースに作っていこうと思います。

※今回はイメージを 9.3.14-php8.1-apache-bullseye にしていますが Drupal - Official Image | Docker Hub なりで好きなものを選んでもらえればと思います。

資材の永続化の準備

まずは Drupal の資材をホストマシン側に展開し、永続化を行います。

$ docker run --rm drupal:9.3.14-php8.1-apache-bullseye bash -c "composer require drush/drush && tar -cC /opt/drupal --exclude ./vendor --exclude ./web/core --exclude ./web/profiles ." | tar -xC ./

上記のコマンドでカレントディレクトリに composer で管理されるパッケージが格納されている ./vendor/ ディレクトリと drupal 資材の core/ 及び profiles/ ディレクトリを除いてコンテナからホストに展開しています。

また、今回は drupal をコマンドラインで操作するための drush も使用したいため、drush のパッケージ状態が記載された composer.json 及び composer.lock ファイルが欲しかったので tar コマンドで資材を展開する前に composer require drush/drush を実行しています。

これを怠ると docker-compose を立ち上げた際、後ほど記載する Dockerfile で drush をインストールしてもホスト側の drush の記載がない composer.json 及び composer.lock で上書きされてしまうので注意です。

補足として、他に永続化する必要のないディレクトリがある場合は以下コマンドで資材を確認しつつ --exclude 不要なファイルやディレクトリ パラメーターを継ぎ足して実行するなりしてもらえればと思います。

$ docker run --rm drupal:9.3.14-php8.1-apache-bullseye ls -altr /opt/drupal
$ docker run --rm drupal:9.3.14-php8.1-apache-bullseye ls -altr /opt/drupal/web

Dockerfile の設定

FROM drupal:9.3.14-php8.1-apache-bullseye

# Drushインストール
RUN composer require drush/drush \
  && ln -s /opt/drupal/vendor/bin/drush /usr/local/bin/drush \
  && drush --version

# 初回起動時にDrupalをインストールする用のshellを配置
COPY docker-entrypoint.sh /tmp
RUN sed -i 's/\r//g' /tmp/docker-entrypoint.sh

Drupal のイメージをベースに drush コマンドを使用できるようにします。
また、コンテナを初めて立ち上げた時のみ起動するシェルスクリプトファイルをコピーしておきます。

drush に対してシンボリックリンクをしておくことで、ホスト側からも drush コマンドを実行しやすくなります。

※この Dockerfile をビルドして、それを利用すれば最初に行った 資材の永続化の準備composer require drush/drush の記述をせずとも composer.jsoncomposer.lockdrush/drush の記述が含まれた状態になっていると思います……

docker-compose.yml

version: '3.8'

services:
  mysql:
    image: mariadb
    volumes:
      - data:/var/lib/mysql
      - ./dump:/docker-entrypoint-initdb.d
    environment:
      MYSQL_ROOT_PASSWORD: "password"
      MYSQL_DATABASE: "drupal"
    ports:
      - "3306:3306"

  drupal:
    build: ./
    volumes:
      - ./web/themes:/var/www/html/themes
      - ./web/modules:/var/www/html/modules
      - ./web/sites:/var/www/html/sites
      - ./config:/opt/drupal/config
      - ./composer.json:/opt/drupal/composer.json
      - ./composer.lock:/opt/drupal/composer.lock
    entrypoint: "/tmp/docker-entrypoint.sh"
    command: "apache2-foreground"
    environment:
      PROFILE: "minimal"
    ports:
      - "80:80"
    depends_on:
      - mysql

volumes:
  data: {}

永続化のために各種 volumes の指定をしています。

drupal や各種モジュールのバージョンを保持するために composer.json composer.lock を volumes に含めています。

また、各種 module や theme, sites 設定 を Git で管理することを想定してこれらも volumes に含めています。

drupal の environment でプロファイルを指定しています。
ちょうど Drupal Advent Calendarの前日の Drupal9 インストール機能を追いかける | Irologue blog で詳しく紹介してくださっているので、その辺りを見ていただければと思います。

docker-entrypoint.sh

#!/bin/sh

# 初回起動時Drupalインストールを走らせる
if [ ! -d "/tmp/check" ]; then
  mkdir /tmp/check

  mkdir -p /opt/drupal/web/sites/default/files/translations/
  curl https://ftp.drupal.org/files/translations/all/drupal/drupal-${DRUPAL_VERSION}.ja.po \
    -o /opt/drupal/web/sites/default/files/translations/drupal-${DRUPAL_VERSION}.ja.po
  sleep 30s && drush si -y ${PROFILE} \
    --account-name="admin" --account-pass="admin" --account-mail="example@example.com" \
    --db-url="mysql://root:password@mysql:3306/drupal" \
    --site-mail="example@example.com" --site-name="Drupal Test" \
    --locale="ja"
  drush config-set system.site uuid e707291e-7532-46d1-822c-0983cb676f8d
  drush config-set language.entity.ja uuid 0a2ddbab-8662-4d8a-bc0b-e20dd875e041

  if [ -f "/opt/drupal/config/system.site.yml" ]; then
    # config の yml ファイルが出力されているなら取り込む
    drush cim -y
  else
    # 出力されてないなら出力しておく
    drush cex -y
  fi

fi

exec "$@"

コンテナを立ち上げた初回のみ Drupal のインストールを自動的に行なってくれるような shell を書いておきます。

Docker で Drupal を立ち上げる記事はいくつかあったのですが、コンテナ立ち上げたタイミングで Drupal のインストールしている記事はなさそうだったので、今回記事にしてみました。

site:install - Drush 細かい引数は適宜調整してもらうとして、以下の公式ドキュメントに各引数の説明があるので、そちらを参照いただければと思います。

途中で Drupal の configuration management を有効にするために、固定のUUIDをセットしています。
この UUID は各々のプロジェクトごとに変更すると良いと思います。

最後に config/ 配下にファイルがありそうだったら drush の configuration import を実行して構成の同期を行うようにしています。
存在しなければ新規でファイルを出力するために drush の configuration export を実行しています。

この UUID をセットする処理を省くと、以下のようなエラーが出てしまい、正常に config/ 配下に出力されている 設定用の yaml 郡を取り込めないので、セットする必要があります。

The import failed due for the following reasons:                                                                                                          [error]
Site UUID in source storage does not match the target storage.
(ソースのストレージにあるサイトのUUIDが、対象のストレージに一致しません。)

※以前マサカリ頂いてもっとスマートな方法があったような気がするのですが、手持ちの環境で上手く動作せず……引き 続き検証中です。

ソース一式

一応以下で上記の内容を含んだスケルトン的なものを置いているので、ちょっと試したいなと思われましたらDLしていただければ幸いです。

https://github.com/kato83/drupalExample