📚

Rails7 のステージング環境(Docker)を楽々に作成してSSL化する

2022/09/04に公開

目的

WEBサーバー(nginx,apacheなど)やアプリケーションサーバー(unicorn,pumaなど)、その他のSSL対応手続きに必要な、面倒なセットアップを大幅にカットあるいは簡略化することで、ローカル環境で実装したRailsとDockerのアプリケーションのステージング環境作成を容易にすること。

前提条件

  • ローカル環境でアプリケーションが正常に動いている (http://localhost:3000)
  • ステージングサーバーの契約済み
  • ステージングサーバーのインフラ設定(ネットワーク、セキュリティなど含む)が完了している
  • ステージングサーバー内にdockerとdocker-composeがインストールされている
  • 独自ドメインを取得済み
  • DNS設定にて、ドメインとパプリックIPを紐付け済み

別に一緒でなくて全然構いませんが、筆者の環境は以下のようになっていますので、参考までに。

  • ドメイン取得 => ムームードメインで購入
  • ステージングサーバー => EC2でインスタンス作成
  • ドメインとパプリックIPを紐付け => Route53で設定済み

バージョン情報

  • 手元の作業PC: Apple M1 Pro
  • Rails: 7.0.2
  • Ruby: 3.1.1
  • PostgreSQL: 14.3
  • Docker: 20.10.17
  • Docker-compose.yml: 2.10.1

ゴール

ブラウザに 独自ドメインでアクセスできること。
今回は、例として、https://www.mysite.work で アクセスできるようにします。
なお、本番環境の設定手順もすぐに別の記事で解説するのでお待ちを。

ディレクトリ構成

プロジェクトルート
├── data (postgresqlのデータマウント用)
├── docker-compose.staging.yml (ステージング用だとわかる命名だと良き)
├── https-portal
└── web
    ├── .env
    ├── Dockerfile.prod (自分は本番と共通にしています)
    ├── Gemfile
    ├── Gemfile.lock
    ├── Rakefile
    ├── app
    ├── bin
    ├── config
    ├── config.ru
    ├── db
    ├── entrypoint.sh
    ├── lib
    ├── log
    ├── public
    ├── storage
    ├── test
    ├── tmp
    └── vendor

「Rails7.0.2 + PostgreSQL14.3 開発環境(docker)を構築する」の構成をベースにしています。
https://zenn.dev/dragonarrow/articles/8503c36d19eb6f

手順1: 各ファイルの作成・編集

.envを作成

秘匿したい情報はこのファイルに記載

DB_STG_DATABASE="xxxxxxxxxx"
DB_STG_HOST="db" # 今回は、docker-composeで立ち上げるdbサービスを利用
DB_STG_USER="xxxxxxxxxx"
DB_STG_PASSWORD="xxxxxxxxxx"

docker-commpose.staging.yml作成

docker-commpose.staging.yml
version: '3'
services:
  db:
    image: postgres:14.3
    ports:
      - 5432:5432
    volumes:
      - ./data:/var/lib/postgresql/data
      # - ./db/initdb.d:/docker-entrypoint-initdb.d
    environment:
      POSTGRES_USER: ${DB_STG_USER}
      POSTGRES_PASSWORD: ${DB_STG_PASSWORD}
    restart: always
    networks:
      dragon-network:
        ipv4_address: 192.168.1.2

  web:
    build: 
      context: web
      dockerfile: Dockerfile.prod
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0' -e production"
    restart: always
    volumes:
      - ./web:/app
    environment:
      DB_PROD_DATABASE: ${DB_STG_DATABASE}
      DB_PROD_HOST: ${DB_STG_HOST}
      DB_PROD_USER: ${DB_STG_USER}
      DB_PROD_PASSWORD: ${DB_STG_PASSWORD}
      EDITOR: vim
      RAILS_ENV: production
    depends_on:
      - db
    networks:
      dragon-network:
        ipv4_address: 192.168.1.3

  https-portal:
    image: steveltn/https-portal:1
    ports:
      - '80:80'
      - '443:443'
    restart: always
    environment:
      DOMAINS: 'admin:bluesky@mysite.work => www.mysite.work, admin:bluesky@www.mysite.work -> http://web:3000'
      STAGE: "staging"
      WORKER_PROCESSES: 2
      CLIENT_MAX_BODY_SIZE: 10M
    volumes:
      - ./https-portal:/var/lib/https-portal
    networks:
      dragon-network:
        ipv4_address: 192.168.1.4

networks:
  dragon-network:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.1.0/24
          gateway: 192.168.1.1

https-portal の詳細に関しては以下をご覧ください
https://github.com/SteveLTN/https-portal

  • 今回のポイント1
    • Basic認証をhttps-portalで設定
      • ユーザー名: admin
      • パスワード: bluesky
  • 今回のポイント2
    • mysite.workにアクセスするとwww.mysite.workにリダイレクトするように設定
  • 注意点1つ
    • サービス名webのポート3000番をdockerネットワークの外部にまで公開しない。ports: 3000:3000などでポートフォワーディングしてしまうとブラウザから、[staging_public_ip]:3000でアクセスできてしまいます。

environments/production.rbの強制SSLをオフにしておく

プロジェクトルート/web/config/environments/production.rb

production.rb
...
	config.force_ssl = false
...

https-portalサービスにて、SSL証明書を発行するときに、httpの通信が発生するため、docker-compose.staging.ymlにて80番ポートが開いていなかったり、force_sslをtrueにしてしまっていると、SSL証明書の発行に失敗してしまう。

Dockerfile.prodを作成

# gemインストールのみに使用
FROM ruby:3.1.1-alpine as builder

ENV ROOT="/app"
ENV LANG=C.UTF-8
ENV TZ=Asia/Tokyo

WORKDIR ${ROOT}

COPY Gemfile Gemfile.lock ${ROOT}

RUN apk add \
    alpine-sdk \
    build-base \
    # sqlite-dev \
    # mysql-client \
    # mysql-dev \
    postgresql-dev \
    postgresql-client \
    tzdata \
    git

# M1のRails(Docker環境)起動時にnokogiriがLoadErrorとなる問題の解決方法
RUN apk add --no-cache gcompat

RUN apk add --no-cache --virtual .build-deps \
    build-base \
    ruby-dev

RUN gem install bundler && bundle install
RUN bundle update webdrivers selenium-webdriver
RUN bundle exec rails assets:precompile RAILS_ENV=production

# マルチステージビルド
FROM ruby:3.1.1-alpine AS runner

ENV ROOT="/app"
ENV LANG=C.UTF-8
ENV TZ=Asia/Tokyo

WORKDIR ${ROOT}

RUN apk update && \
    apk add \
        postgresql-dev \
        tzdata \
        bash \
        gcompat \
        vim

RUN apk add --no-cache nodejs

COPY --from=builder /usr/local/bundle /usr/local/bundle
COPY . ${ROOT}

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
  • ポイント
    • docker-composeでビルドしたときに、アセットのプリコンパイルを行うようにしている
      • これをしないと、jsとcssがステージングに反映されない
  • 注意点
    • EXPOSE 3000などで、外部に公開しない

web/config/database.ymlを編集

web/config/database.yml
production:
  <<: *default
  database: <%= ENV["DB_PROD_DATABASE"] %>
  host: <%= ENV["DB_PROD_HOST"] %>
  username: <%= ENV["DB_PROD_USER"] %>
  password: <%= ENV["DB_PROD_PASSWORD"] %>

(uglifierを使っている人) terserに置き換え

2年前にRails5.2で動かしていた頃は、自分は、uglifierというgemを使って、javascriptのコードを軽量化していたが、Rails7.0で同じように使用すると、precompile時にエラーを吐いていた。ES6構文まではuglifierが使えるけど、ES2020とかになるとterserに置き換えないといけないらしい。以下のようにしてterserに置き換えることで解決した。

Gemfile
- gem 'uglifier'
+ gem 'terser'
web/config/environments/production.rb
- config.assets.js_compressor = :uglifier
+ config.assets.js_compressor = :terser

手順2: アプリケーションをステージングサーバーに転送

それぞれのやり方で転送してください。自分は、fileZillaを愛用しております。

手順3: ステージングで起動

ビルド

$ docker-compose -f docker-compose.staging.yml build

DB作成とマイグレーションファイルの実行

$ docker-compose -f docker-compose.staging.yml run --rm web bundle exec rails db:migrate:reset RAILS_ENV=production

起動

 $ docker-compose -f docker-compose.staging.yml up -d

すると、オレオレ証明書がhttps-portalにより、自動で作成される。
ログで確認する。

docker-compose -f docker-compose.staging.yml logs -f

証明書が発行されたら、ブラウザにて以下2点を確認する。オレオレ証明書なので、ブラウザから警告が出ると思いますが、気にしないで、問題ないです。

  1. https://www.mysite.workにアクセスして、Webページが表示される
  2. https://mysite.workにアクセスしてhttps://www.mysite.workにリダイレクトされる

確認できたら、手順4に入る。

手順4: environments/production.rbの強制SSLをオンにしておく

プロジェクトルート/web/config/environments/production.rb

production.rb
...
	config.force_ssl = true
...

設定を変えたら、立ち上げ直す

$ docker-compose -f docker-compose.staging.yml down
$ docker-compose -f docker-compose.staging.yml up -d --build

終わりに

今回はRailsとDockerで作成したローカル環境を容易にステージングに作成する方法を紹介いたしました。
2年前までは自分は、サーバーにgit cloneして手動でライブラリをインストールして、設定編集したりして、極めて面倒な作業だったので、今では非常にhttps-portalに利便性を感じています。
https-portalは、wordpressで使っている記事はよく見るものの、Railsで使っている記事は、自分が探した限りでは、ただの1件もなかったので、非常に価値のある記事が、公開できたのではないかと思っています。
本番環境作成手順に関してもすぐに別の記事で紹介するので、お楽しみに。

docker + Railsのステージング環境作成にぜひ役に立てば幸いです。

Discussion