Open6

Docker / Rails / Next.js

てりてり

docker compose up で ArgumentError 発生

ArgumentError の意味

  • Argument = 引数
  • ArgumentError:引数の数があっていないとき、もしくは期待される値ではないときに発生する

compose.yaml / Dockerfile / Gemfile

compose.yaml
services:
  db:
    image: postgres:16.4
    environment:
      POSTGRES_DB: app_development
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
  backend:
    build:
      context: ./backend
    command: bash -c "bundle exec rails s -b '0.0.0.0'"
    volumes:
      - ./backend:/app
    ports:
      - "3000:3000"
    depends_on:
      - db
    tty: true
    stdin_open: true
volumes:
  postgres_data:
Dockerfile
FROM ruby:3.3.5
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client

WORKDIR /app

COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

RUN gem install bundler
RUN bundle install

COPY . /app

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]
Gemfile
source "https://rubygems.org"

ruby "3.3.5"
gem "rails", "~> 7.2.1", ">= 7.2.1.2"

# postgreSQL
gem "pg", "~> 1.1"

# default gem
gem "bootsnap", require: false
gem "puma", ">= 5.0"
gem "tzinfo-data", platforms: %i[ windows jruby ]

# CORS
gem "rack-cors"

group :development, :test do
end

エラー

backend-1  | /usr/local/bundle/ruby/3.3.0/gems/railties-7.2.1.2/lib/rails/application/configuration.rb:519:in `secret_key_base=': Missing `secret_key_base` for 'production' environment, set this string with `bin/rails credentials:edit` (ArgumentError)
backend-1  |
backend-1  |           raise ArgumentError, "Missing `secret_key_base` for '#{Rails.env}' environment, set this string with `bin/rails credentials:edit`"
backend-1  |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
てりてり

secret_key_base って何?

secret_key_base()

The secret_key_base is used as the input secret to the application’s key generator, which in turn is used to create all ActiveSupport::MessageVerifier and ActiveSupport::MessageEncryptor instances, including the ones that sign and encrypt cookies.
In development and test, this is randomly generated and stored in a temporary file in tmp/local_secret.txt.
You can also set ENV["SECRET_KEY_BASE_DUMMY"] to trigger the use of a randomly generated secret_key_base that’s stored in a temporary file. This is useful when precompiling assets for production as part of a build step that otherwise does not need access to the production secrets.
Dockerfile example: RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile.
In all other environments, we look for it first in ENV["SECRET_KEY_BASE"], then credentials.secret_key_base. For most applications, the correct place to store it is in the encrypted credentials file.
参照:secret_key_base - Rails::Application

翻訳:

secret_key_base() は、アプリケーションのキージェネレーターへの入力シークレットとして使用され、これは次にcookieの署名と暗号化を含むすべての ActiveSupportMessageVerifierActiveSupport::MessageEncryptor インスタンスの作成に使用されます。

開発環境とテスト環境では、これはランダムに生成されて tmp/local_secret.txt の一時ファイルに保存されます。

また、ENV["SECRET_KEY_BASE_DUMMY"] を設定することで、一時ファイルに保存されるランダムに生成された secret_key_base の使用をトリガーすることもできます。これは、本番環境のシークレットにアクセスする必要がない場合のビルドステップの一部として、本番環境用のアセットをプリコンパイルする際に便利です。

Dockerfileの例: RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile

他のすべての環境では、最初にENV["SECRET_KEY_BASE"]、次にcredentials.secret_key_baseで探します。ほとんどのアプリケーションでは、暗号化された認証情報ファイルに保存するのが正しい場所です。

てりてり

原因:rails new により Dockerfile が上書きされていた

Rails7.1 から rails new すると、Dockerfileも自動生成されるようになった。
そのため、事前に用意していた Dockerfile が上書きされて、エラーが発生した。

なお、Rails が自動生成する Dockerfile は本番用で、開発環境用は別で用意する必要がある。

自動生成された Dockerfile:

Dockerfile
# syntax = docker/dockerfile:1

# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand:
# docker build -t my-app .
# docker run -d -p 80:80 -p 443:443 --name my-app -e RAILS_MASTER_KEY=<value from config/master.key> my-app

# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
ARG RUBY_VERSION=3.3.5
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base

# Rails app lives here
WORKDIR /rails

# Install base packages
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y curl libjemalloc2 libvips postgresql-client && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Set production environment
ENV RAILS_ENV="production" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle" \
    BUNDLE_WITHOUT="development"

# Throw-away build stage to reduce size of final image
FROM base AS build

# Install packages needed to build gems
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential git libpq-dev pkg-config && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Install application gems
COPY Gemfile Gemfile.lock ./
RUN bundle install && \
    rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
    bundle exec bootsnap precompile --gemfile

# Copy application code
COPY . .

# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/

# Final stage for app image
FROM base

# Copy built artifacts: gems, application
COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
COPY --from=build /rails /rails

# Run and own only the runtime files as a non-root user for security
RUN groupadd --system --gid 1000 rails && \
    useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
    chown -R rails:rails db log storage tmp
USER 1000:1000

# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server"]

てりてり

解決方法について考えてみる

  1. Rails で自動生成(上書き)されたものは削除し、以前の記述に戻す

  2. Rails で自動生成されたものを活用する
    参考:

  3. Rails で自動生成されたものを本番用として残しつつ、開発用のDockerfileを新規作成する
    参考:

  4. マルチステージビルドを活用する
    参考:Dockerのマルチステージビルドで依存先を統一した回

てりてり

今回の進め方

  1. 以前の記述に戻しつつ、3. 開発用と本番用の Dockerfile を用意する形で進める
てりてり

Github Avtions について

Github Actions の内容は以下記事がとてもわかりやすかった。

【初心者向け】【入門】GitHub Actionsとは?書き方、デバッグ設定、runs-onやcheckoutなどの仕組みや構造も含めて徹底解説 #Linux - Qiita

コードの内容理解

RSpec(テスト)、RuboCop, ESLint(コードルール)のチェックを行う。

ci.yml
name: Continuous Integration

on: push

jobs:
  RSpec:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: backend
    services:
      mysql:
        image: postgres:16.4.0
        ports:
          - "5432:5432"
        env:
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 10

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.3.5
          bundler-cache: true

      - name: Cache node modules
        uses: actions/cache@v3
        with:
          path: node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Bundler and gem install
        run: |
          gem install bundler
          bundle install --jobs 4 --retry 3 --path vendor/bundle

      - name: Database create and migrate
        run: |
          cp config/database.yml.ci config/database.yml
          bundle exec rails db:create RAILS_ENV=test
          bundle exec rails db:migrate RAILS_ENV=test

      - name: Run rspec
        run: bundle exec rspec

  RuboCop:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: backend
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
            ruby-version: 3.3.5
            bundler-cache: true

      - name: Bundler and gem install
        run: |
          gem install bundler
          bundle install --jobs 4 --retry 3 --path vendor/bundle

      - name: Run rubocop
        run: bundle exec rubocop

  ESLint:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: frontend
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install packages
        run: npm install

      - name: Run lint
        run: npm run lint
  • on: push:ブランチへの push 時に workflow を実行
  • jobs::処理の内容を記述
    • RSpec::RSpec がオールグリーンであることをチェック
    • RuboCop::RuboCop のルール違反がないことをチェック
    • ESLint::ESLint のルール違反がないかチェック
  • defaults.run.working-directory:作業ディレクトリを指定
  • jobs.<job_id>.steps:具体的に行う処理の内容。上からコマンドを実行。
  • PostgreSQL の記述は「 PostgreSQLサービスコンテナの作成 」を参照
  • POSTGRES_PASSWORD は、Github Secrets に格納

参考:GitHub Actions のワークフロー構文