Docker ComposeでApache+Rails環境を構築


最終的なフォルダ構成

最終的なフォルダ構成
myapp/
    |--docker-compose.yml
    |--apache/
        |--html/
    |--rails/
        |--app/
        |--bin/
        |--config/
        |--db/
        |--lib/
        |--log/
        |--public/
        |--storage/
        |--test/
        |--tmp/
        |--vendor/
        |--config.ru
        |--Dockerfile
        |--entrypoint.sh
        |--Gemfile
        |--Gemfile.lock
        |--Rakefile
    |--README.md
    |--.env
    |--.gitignore
    |--.git

.git

Gitの初期化を行う

bash
git init

.gitignore

  • Gitの管理下に置きたくないファイルやディレクトリを管理するためのファイル
.gitigrore
/.env

.env

  • 環境変数を管理するために使われるファイル
.env
# commons
WORKDIR=app
CONTAINER_PORT=3000
RAILS_PORT=3000
APACHE_PORT=8080

# db
# POSTGRES_PASSWORD=password

docker-compose.yml

まずプロジェクトフォルダを作成し、そこにrailisフォルダ、apacheフォルダとdockercompose.ymlファイルを作成する。

docker-compose.yml
version: '3.8'

services:

  apache:
    image: httpd:alpine
    container_name: apache
    volumes:
      - ./apache/html:/usr/local/apache2/htdocs
    # httpdイメージでは80番ポートを公開している
    ports:
      - "$APACHE_PORT:80"
    depends_on:
      - rails

  # railイメージを構築、railsコンテナを構築、起動
  rails:
    # railsフォルダの中にあるDockerfileをビルド
    build:
      context: ./rails
      # Dockerイメージをビルドする際にDockerfileに渡す値を指定
      args:
        WORKDIR: $WORKDIR
    container_name: rails
    environment:
      # rack-corsで使用する
      ACCESS_CONTROL_ALLOW_ORIGIN: "localhost:$APACHE_PORT"
    # サーバの起動フラッグを削除(起動されていると勘違いしてしまわないように)
    # ポートを$CONTAINER_PORTとしてサーバ起動(Dockerfile内でも起動しているが)
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p $CONTAINER_PORT -b '0.0.0.0'"
    # コンテナ中のappフォルダをローカルのappフォルダに結びつける(コンテナを起動する際にホストマシン上のデータがコンテナにマウントされる)
    volumes:
      - ./rails:/$WORKDIR
    # コンテナのポートとホストのポートを対応付ける
    ports:
      - "$RAILS_PORT:$CONTAINER_PORT"
    # attachに必要,ターミナルの入出力がコンテナのbashとつながる?,attachしないとブラウザは一生読み込み中に
    stdin_open: true
    # これがないとattachしてもすぐ切れてしまう?
    tty: true

CORSの設定

  • CORSとは、異なるオリジン感の通信を許可する仕組み

    1. Gemのインストール
      1. railsディレクトリのGemfileを開く
      2. 26行目付近のgem 'rack-cors'のコメントを外す
      3. Dockerイメージを再ビルド
    2. 設定ファイルの編集

      1. api/config/initializers直下にあるcors.rbが設定ファイル
      2. 下記のように編集

        cors.rb
        # Be sure to restart your server when you modify this file.
        
        # Avoid CORS issues when API is called from the frontend app.
        # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
        
        # Read more: https://github.com/cyu/rack-cors
        
        Rails.application.config.middleware.insert_before 0, Rack::Cors do
          allow do
            # docker-compose.ymlで指定した値かその値がNullの場合は、空文字を入れる(エラーにならないように)
            origins ENV["ACCESS_CONTROL_ALLOW_ORIGIN"] || ""
        
            # 許可したいリソースファイル
            resource '*',
              headers: :any,
              # getのみ許可
              methods: [:get]
          end
        end
        

Dockerfile

railsイメージはDockerfileから作成するためDockerfileとentrypoint.shをrailsフォルダの下に作成する。

現在のフォルダ構成
myapp/
    |--docker-compose.yml
        |--apache/
    |--rails/
      |--Dockerfile
      |--entrypoint.sh

Dockerfile
FROM ruby:2.7.2

RUN apt update -qq && \
    apt install -y build-essential \
    libpq-dev

# Dockerfile内で使用する変数名を指定    
ARG WORKDIR
ENV APP_ROOT /$WORKDIR
RUN mkdir $APP_ROOT
WORKDIR $APP_ROOT
# このファイル(Dockerfile)と同じ階層にあるGemfile等をコンテナにコピー
COPY ./Gemfile $APP_ROOT/Gemfile
COPY ./Gemfile.lock $APP_ROOT/Gemfile.lock
# コンテナのGemfileを参考にGemをインストール
RUN bundle install
# このファイルが含まれているディレクトリをコンテナのルートディレクトリにコピー
COPY . $APP_ROOT

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

CMD ["rails", "server", "-b", "0.0.0.0"]
entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /app/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

GemfileとGemfile.lock

現在のフォルダ構成
myapp/
    |--docker-compose.yml
        |--apache/
    |--rails/
      |--Dockerfile
      |--entrypoint.sh
      |--Gemfile
      |--Gemfile.lock

Gemfile
source 'https://rubygems.org'
gem 'rails', '6.0.0'

GemfileはRubyのライブラリをまとめたもので、Dockerfile内のbundle installではこのファイルを元にgemたちをインストールしている。
Gemfile.lockはGemfileをもとに実際にインストールされたgemの一覧とバージョンが記載されたファイルで、ここでは空のファイルを作成しておく。

イメージの構築、コンテナの作成・起動

ここまででとりあえず必要なファイルが揃ったので、イメージの構築等を行っていく。

rails new —api

rails newはrailsアプリケーションの雛形を作成してくれるコマンドで、railsサーバを立ち上げるのに必要なgem等を作成してくれる。今回railsはAPIサーバとして用いるためAPIモードでアプリを作成するように--apiをオプションとして付けている。

bash
docker-compose run rails rails new . --api --force --skip-bundle

docker-compose runは引数で指定したサービスについて、イメージの構築から、コンテナの作成・起動までを行ってくれるコマンドで、それプラスサービス名の後にコマンドを打つとコンテナ内で実行してくれる。
ここではサービスにDockerfileで指定したrails:を指定しており、プラスアルファのコマンドとしてrails new .を行っている。

--forceは既存のGemfileを上書きするためのオプションである。
後で、イメージを構築する際にbundle installは行われるので、ここでは--skip-bundleとしている。

イメージの構築

bash
docker-compose build

このとき、Dockerfileよりrailsのイメージが作成される。

コンテナの作成・起動

bash
docker-compose up

httpdのイメージはこのときに、Docker Hubからローカルに持ってきている。

localhost:8080でapache/htmlにアクセスできる。localhost:3000でrailsサーバにアクセスできる。

補足

docker-compose buildbundle installは行われるが、volumeとは紐付かないらしく、コンテナを起動する際にエラーが出る恐れがある。そのときは、volumeと紐づけられるrunコマンドを実行する。

$ docker-compose run rails bundle install

または、rails new--skip-bundleを付けなければ問題ない。

Docker Compose + Railsでイメージ内でbundle installしているはずなのにgemが無いとエラーがでる。 - Qiita

参考

DockerインストールからRails + Docker + MySQLで環境構築までの手順
【Mac編】DockerでRuby on Railsの開発環境を作ってみよう
Docker ComposeでNode.jsの環境構築 - Qiita
Rails6・Nuxt.js・PostgreSQLを動かすdocker-compose.ymlファイルを作成する - 独学プログラマ