【Docker】Windows11 + WSL2 + Docker で Rails6.1 + MariaDB の開発環境を構築する

 Rails Guide をやるために Rails6.1 の環境を作ろうとしたら思いの外苦戦したので備忘を含めて記事にします。

 なぜRailsのバージョンが6.1なのかというと、パーフェクトRuby on Rails のバージョンが6.0.3なのでなるべく6系に合わせたかったというのと、6.1がLTSバージョンだからです。

結論だけ知りたい人向け

 結論に飛ぶか下のリンクを参考にしてください。

Docker-sample/Rails_6_1-MariaDB at main · tsuneken5/Docker-sample
Contribute to tsuneken5/Docker-sample development by creating an account on GitHub.

環境

  • ホストOS:Windows 11 23H2
  • ゲストOS:Ubuntu 20.04.5
  • Docker version:20.10.12
  • Docker Compose version:2.10.2
  • Ruby:3.0.5
  • Ruby on Rails:6.1
  • MariaDB:10.11

基本パターン

 まずは普通にイメージを作ってコンテナを作成していきます。

ディレクトリ構成

$ tree --dirsfirst
.
├── src
│   ├── Gemfile
│   └── Gemfile.lock
├── .env
├── Dockerfile
└── docker-compose.yml

.env

UID=1000
GID=1000
USERNAME=docker
GROUPNAME=docker

Gemfile

source 'https://rubygems.org'
gem 'rails', '6.1.0'

 Gemfile.lockは空ファイルになります。

Dockerfile

FROM ruby:3.0.5

# Node.jsをインストール
RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs

# yarnパッケージ管理ツールをインストール
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
  && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
  && apt-get update -qq && apt-get install -y yarn

COPY ./src /app

ARG USERNAME=docker
ARG GROUPNAME=docker
ARG UID=1000
ARG GID=1000
RUN groupadd -g ${GID} ${GROUPNAME}
RUN useradd -u ${UID} -g ${GID} -s /bin/bash -m ${USERNAME}
RUN gpasswd -a ${USERNAME} sudo

USER ${USERNAME}

WORKDIR /home/${USERNAME}/app
COPY --chown=${USERNAME}:${GROUPNAME} ./src /home/${USERNAME}/app

RUN bundle config --local set path 'vendor/bundle' \
  && bundle install

EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]

13行目 ~ 21行目

 WSL2特有の問題だったと思うのですが、ユーザーを作らずにそのままコンテナでコマンドを実行すると、 root ユーザーでログインしてしまいます。その場合、 rails newrails generate で作成されるファイルの所有者がすべて root になってしまって編集できなくなります。それを避けるために一般ユーザーを作成して、ログインユーザーを一般ユーザーに切り替える必要があります。

24行目

 上記で作成したユーザの権限でディレクトリをコピーしないと、 bundle install 実行時に permission denied でエラーになってしまいます。

docker-compose.yml

version: '3'
services:
  db:
    image: mariadb:10.11
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: password
      TZ: "Asia/Tokyo"

  web:
    build:
      context: .
      args:
        - UID=${UID}
        - GID=${GID}
        - USERNAME=${USERNAME}
        - GROUPNAME = ${GROUPNAME}
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    user: "${UID}:${GID}"
    volumes:
      - ./src:/home/${USERNAME}/app
    ports:
      - "3000:3000"
    depends_on:
      - db
    environment:
      DATABASE_PASSWORD: password
      TZ: "Asia/Tokyo"

volumes:
  mysql-data:
    driver: local

 基本的には他の記事と同じだと思いますが、タイムゾーンを東京に変更しています。

イメージのビルド

$ docker-compose build

プロジェクトの作成

$ docker-compose run --rm web rails new . --force --database=mysql

 プロジェクト作成時のログに、警告が山ほど出てきます。大丈夫なのかと言われれれば大丈夫ではありません。とりあえず今回は先に進めます。

イメージの再ビルド

$ docker-compose build

 今の状態だとイメージに bundle install ができていないので、再度イメージをビルドしてやります。

データベースの設定・作成

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: <%= ENV.fetch("DATABASE_PASSWORD") %>
  host: db

 データベースのコンフィグで passwordhost を変更します。

$ docker-compose run --rm web  rails db:create

コンテナの起動

$ docker-compose up

問題点

 この状態でもブラウザで「http://localhost:3000/」にアクセスすればトップページにアクセスできます。

 では何が問題なのかというと、今の状態だとJavaScriptが効きません。私はこの問題でRails Guideの「6.5 記事を削除する」でlink_toのmethod: :deleteがGetになる問題が発生し、無駄に時間を使ってしまいました。ちなみにこの問題はググれば解決方法が出てきますが、今回の問題はそれ以前の問題です。

原因

 この問題の原因は、webpackerがコンパイルできてないことにあります。試しにコンパイルするとエラーが出ます。

$ docker-compose run --rm web rails webpacker:compile
(省略)
ERROR in ./app/javascript/packs/application.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: Cannot find package '@babel/plugin-proposal-private-methods' imported from /home/docker/app/babel-virtual-resolve-base.js
...
(省略)

 簡単に言えば「 @babel/plugin-proposal-private-methods パッケージが見つかりません」ってことですね。

修正

 では @babel/plugin-proposal-private-methods をインストールすればいいと思うかもしれませんが、そうではありません。現在、 @babel/plugin-proposal-private-methods は名前が変更されていて、今は @babel/plugin-transform-methods に変更されています。しかもこれだけではなく、 @babel/plugin-proposal-* 系はすべて @babel/plugin-transform-* に変更されています。

 この情報だけだと変更しないといけないパッケージが網羅できていないので、Webpackerの設定ファイル( babel.config.js )を見ていきます。

      [
        '@babel/plugin-proposal-class-properties',
        {
          loose: true
        }
      ],
      [
        '@babel/plugin-proposal-object-rest-spread',
        {
          useBuiltIns: true
        }
      ],
      [
        '@babel/plugin-proposal-private-methods',
        {
          loose: true
        }
      ],
      [
        '@babel/plugin-proposal-private-property-in-object',
        {
          loose: true
        }
      ],

 設定ファイルを見ると @babel/plugin-proposal-class-properties, @babel/plugin-proposal-object-rest-spread, @babel/plugin-proposal-private-methods, @babel/plugin-proposal-private-property-in-object があるので、@babel/plugin-transform-class-properties, @babel/plugin-transform-object-rest-spread, @babel/plugin-transform-private-methods, @babel/plugin-transform-private-property-in-object をインストールして、設定ファイルを書き換える必要があります。

 ついでに @babel/core のバージョンが古いという警告が出るので、@babel/core のバージョンも上げておきます。

パッケージのインストール

$ docker-compose run --rm web yarn add @babel/core@latest --dev
$ docker-compose run --rm web yarn add @babel/plugin-transform-class-properties @babel/plugin-transform-object-rest-spread @babel/plugin-transform-private-methods @babel/plugin-transform-private-property-in-object --dev

babel.config.js

      [
        '@babel/plugin-transform-class-properties',
        {
          loose: true
        }
      ],
      [
        '@babel/plugin-transform-object-rest-spread',
        {
          useBuiltIns: true
        }
      ],
      [
        '@babel/plugin-transform-private-methods',
        {
          loose: true
        }
      ],
      [
        '@babel/plugin-transform-private-property-in-object',
        {
          loose: true
        }
      ],

Webpackerのコンパイル

 この状態でもう一度Webpackerをコンパイルしてやります。

$ docker-compose run --rm web rails webpacker:compile
(省略)
Compiling...
Compilation failed:
Error: error:0308010C:digital envelope routines::unsupported
...
(省略)

 またエラーが出ました。これはNode.js のバージョンと OpenSSL の互換性の問題から発生するようです。

Node.js — Node v17.0.0 (Current)
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

 Node v17.0.0以降では「OpenSSL 1.1.1」に替わって「OpenSSL 3.0」が収録されるようになっています。プロジェクトで使っているあるライブラリが、サポートしていないOpenSSLのバージョンを使っているということでエラーになっているみたいです。Node.jsが暗号化関連の操作(ハッシュ関数など)を行おうとしたとき、その操作がサポートされていないということになるようです。

 これを解決するには環境変数で NODE_OPTIONS: --openssl-legacy-provider を設定して、OpenSSL3をレガシープロパイダーに戻すことで対応します。

docker-compose.yml

 docker-compose.yml を編集して環境変数を設定します。

version: '3'
services:
  db:
    image: mariadb:10.11
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: password
      TZ: "Asia/Tokyo"

  web:
    build:
      context: .
      args:
        - UID=${UID}
        - GID=${GID}
        - USERNAME=${USERNAME}
        - GROUPNAME = ${GROUPNAME}
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    user: "${UID}:${GID}"
    volumes:
      - ./src:/home/${USERNAME}/app
    ports:
      - "3000:3000"
    depends_on:
      - db
    environment:
      NODE_OPTIONS: --openssl-legacy-provider
      DATABASE_PASSWORD: password
      TZ: "Asia/Tokyo"

volumes:
  mysql-data:
    driver: local

Webpackerのコンパイル

$ docker-compose run --rm web rails webpacker:compile

 これでWebpackerがコンパイルできて、JavaScriptが使えるようになりました。

結論

 今までの操作を踏まえて、 Dockerfile や手順を修正します。

.env

UID=1000
GID=1000
USERNAME=docker
GROUPNAME=docker

Gemfile

source 'https://rubygems.org'
gem 'rails', '6.1.0'

 Gemfile は変更なし。

Dockerfile

FROM ruby:3.0.5

# Node.jsをインストール
RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs

# yarnパッケージ管理ツールをインストール
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
  && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
  && apt-get update -qq && apt-get install -y yarn

COPY ./src /app

ARG USERNAME=docker
ARG GROUPNAME=docker
ARG UID=1000
ARG GID=1000
RUN groupadd -g ${GID} ${GROUPNAME}
RUN useradd -u ${UID} -g ${GID} -s /bin/bash -m ${USERNAME}
RUN gpasswd -a ${USERNAME} sudo

USER ${USERNAME}

WORKDIR /home/${USERNAME}/app
COPY --chown=${USERNAME}:${GROUPNAME} ./src /home/${USERNAME}/app

RUN bundle config --local set path 'vendor/bundle' \
  && bundle install

RUN yarn add @babel/core@latest --dev \
  && yarn add @babel/plugin-transform-class-properties @babel/plugin-transform-object-rest-spread @babel/plugin-transform-private-methods @babel/plugin-transform-private-property-in-object --dev

EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]

29行目 ~ 30行目

 @babel/plugin-transform-* をインストールする手順を追加しています。

docker-compose.yml

version: '3'
services:
  db:
    image: mariadb:10.11
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: password
      TZ: "Asia/Tokyo"

  web:
    build:
      context: .
      args:
        - UID=${UID}
        - GID=${GID}
        - USERNAME=${USERNAME}
        - GROUPNAME = ${GROUPNAME}
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    user: "${UID}:${GID}"
    volumes:
      - ./src:/home/${USERNAME}/app
    ports:
      - "3000:3000"
    depends_on:
      - db
    environment:
      NODE_OPTIONS: --openssl-legacy-provider
      DATABASE_PASSWORD: password
      TZ: "Asia/Tokyo"

volumes:
  mysql-data:
    driver: local

31行目

 環境変数 NODE_OPTIONS=--openssl-legacy-provider を設定する手順を追加しています。

イメージのビルド

$ docker-compose build

プロジェクトの作成

$ docker-compose run --rm web rails new . --force --database=mysql

イメージの再ビルド

$ docker-compose build

データベースの設定・作成

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: <%= ENV.fetch("DATABASE_PASSWORD") %>
  host: db
$ docker-compose run --rm web  rails db:create

Webpacker設定の修正

    plugins: [
      'babel-plugin-macros',
      '@babel/plugin-syntax-dynamic-import',
      isTestEnv && 'babel-plugin-dynamic-import-node',
      '@babel/plugin-transform-destructuring',
      [
        '@babel/plugin-transform-class-properties',
        {
          loose: true
        }
      ],
      [
        '@babel/plugin-transform-object-rest-spread',
        {
          useBuiltIns: true
        }
      ],
      [
        '@babel/plugin-transform-private-methods',
        {
          loose: true
        }
      ],
      [
        '@babel/plugin-transform-private-property-in-object',
        {
          loose: true
        }
      ],
      [
        '@babel/plugin-transform-runtime',
        {
          helpers: false
        }
      ],
      [
        '@babel/plugin-transform-regenerator',
        {
          async: false
        }
      ]
    ].filter(Boolean)

 基本パターンにはなかった追加した手順です。@babel/plugin-proposal-*@babel/plugin-transform-* に変更します。

 Webpackerのコンパイルは適宜自動で実行されるのでここでする必要はありません。

コンテナの起動

$ docker-compose up

 ブラウザから http://localhost:3000/ にアクセスして、Railsのトップページが表示されれば成功です。

コメント

タイトルとURLをコピーしました