はじめに
テスト的に作られたRailsアプリをDocker化する経験をさせて頂いたので、そこで得られた知見を皆さんに共有させていただきたいと思います。
完全に新しくDockerで環境を構築するのとは似ていますが、多少違いがありましたのでそこら辺の微妙な違いについても説明しています。
この記事で参考になること
- nodejsのマルチステージビルドのやり方
- .envの環境変数をdocker-compose.ymlで読み込み、Dockerfileに変数を渡す
- rails プロジェクトでpostgresqlを使用する方法
- コンテナ間通信のやり方
- 新しい環境をDockerで構築する流れとの違い
※Dockerについて逐一説明する記事ではなくTips的な記事であることを先にご了承ください
さっそく本題に入っていきましょう!
RailsプロジェクトをDocker化
フォルダ構成
普通のRailsプロジェクトのルートに
- Dockerfile
- docker-compose.yml
- .env
.envファイルについて
YARN_VERSION=1.22.15
POSTGRES_PASSWORD=postgres
今回はnodeとyarnをマルチステージビルドでインストールを行ったので、その際にyarnのバージョン情報が必要だったため環境変数にYARN_VERSIONを入れています。
POSTGRES_PASSWORDについてはpostgresqlのイメージを使いたい場合は必ず設定しないといけない項目です。
詳しく知りたい方はポスグレの公式DockerImageのoverviewのhow to useを見てください
追記
レビューしていただいた結果開発環境では.envで変数を渡すのは冗長なのでdocker-compose.ymlに直接書いて大丈夫だということが分かりました。
今回は、せっかく.envで作成したのでやり方を含めて解説していますが、開発環境を構築する際は直接書くようにするとgoodだと思います!
勝又さんもそういっていました。
Dockerfileについて
FROM node:14.18.1-slim AS node
FROM ruby:3.0.2-slim
ARG YARN_VERSION
RUN mkdir -p /opt
COPY --from=node /opt/yarn-v${YARN_VERSION} /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/
RUN ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
&& ln -s /opt/yarn/bin/yarn /usr/local/bin/yarnpkg \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs \
&& ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
&& ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx \
&& apt-get update \
&& apt-get install -y \
g++ make \
libpq-dev \
postgresql-client
WORKDIR /app
COPY Gemfile Gemfile.lock package.json /app/
RUN bundle install && yarn
CMD ["rails", "s", "-b", "0.0.0.0"]
POINT1 マルチステージビルド
node イメージにはrails環境を作る際に不要なパッケージなどが含まれているので、
本当に必要最低限のnodeとyarnをrubyコンテナにコピーしています。
必要なものだけをインストールする際は大体
/usr/local/bin/パッケージ名
/usr/local/lib/
あたりを目をつけてコピーすれば大丈夫です。
/usr/local/includeや/usr/local/shareなどは使い方の参考例が書いてあるファイルだったり、nodejsの実行環境のためのファイルだったりする感じなので、今回はyarnでライブラリを管理したいだけなのでコピーしなくてOKです。
POINT2 docker-compose.ymlから値を受け取る
ARG YARN_VERSION
この部分でdocker-compose.ymlのargs(後述)から渡されてきた変数を受け取り
COPY --from=node /opt/yarn-v${YARN_VERSION} /opt/yarn
Dockerfile内で${変数名}とすることによって変数の中身が展開されます。
POINT3 バージョンの指定
今回採用したnode imageを見てみると14.18.1を使用しています。
最新のnodeバージョンを使用すると
webpacker::manifest::missingentryerror
というエラーがrails s -b 0.0.0.0をした際に出たのあえてバージョンをかなり下げています。
ruby:3.0.2もバージョンが指定されていますが、これは既存のプロジェクトのGemfileに記載されている
rubyのバージョンに合わせる必要があるのでそこも気を付けてください
docker-compose.ymlについて
version: '3'
services:
web:
build:
context: .
args:
YARN_VERSION: ${YARN_VERSION}
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
ports:
- '3000:3000'
volumes:
- '.:/app'
tty: true
stdin_open: true
depends_on:
- db
links:
- db
db:
image: postgres:12
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- 'db-data:/var/lib/postgresql/data'
volumes:
db-data:
POINT1.envから変数を受け取り変数をDockrefileに渡す
build:
context: .
args:
YARN_VERSION: ${YARN_VERSION}
docker-compose.ymlは同階層にある.envの変数を自動的に読み込んでくれます。
つまりdocker-compose.ymlに何か変数を渡したかったら.envにすべて書けばOKです。
.envには秘匿情報・環境(dev,test,production)に依存する変数のみ書くようにしましょう
使い方はDockerfileと同様${変数名}とすれば展開されます。
またDockrefileに変数を渡したいときは
上記のようにargsを指定してやれば先ほどDockerfileのpoint 2で解説した受け取り方で受け取れます。
POINT2コンテナ間通信について
デフォルトで Compose はアプリに対して1つの ネットワーク を作成します。サービス用の各コンテナはデフォルトのネットワークに接続し、そのネットワーク上で他のコンテナと相互に「 接続可能reachable 」になります。そして、コンテナ名と同じホスト名として、お互いが「 発見可能discoverable 」になります。
links:
- db
linksを書かなければホスト名として名前を解決することはできますが実際に接続ができるようにはならないので注意
depends_on:
- db
depends_onを指定することで、DBコンテナを作成・実行してからwebコンテナが立ち上がるようになります。
もしDBコンテナが立ち上がっていないのに、WebコンテナがDBコンテナにアクセスしようとしたらエラーになってしまいます。
database.ymlとGemfile
default: &default
adapter: postgresql
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
encoding: unicode
host: db
user: postgres
port: 5432
password: <%= ENV.fetch("POSTGRES_PASSWORD") %>
development:
<<: *default
database: db_dev
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: dev_test
production:
<<: *default
database: db_prod
adapter:postgresqlに変更
host:今回は外部コンテナにDBがある構成なので、その場合はdocker-compose.ymlのサービス名を記載してあげれば勝手に名前解決してくれます。(めっちゃ便利)
password:docker-compose.ymlで環境変数に設定したPOSTGRES_PASSWORDを展開する
# Use postgres as the database for Active Record
gem 'pg'
postgreを使用するために必要なgemです。自分のプロジェクトにあったバージョンを使用しましょう。
またsqliteなどいらなくなったDB用のgemがあったら削除しましょう。
ファイル編集などの必要な変更は以上です。
必要に応じてdb:createなどを行ってください。
最後に
個人的にマルチステージビルドの部分とバージョンをエラーが出たら逐一動くようになるバージョンに変更する作業が結構大変だったと思います。
しかし一回慣れてしまえばdockerは怖くなくなったので、これから開発を行う時は全部docker化していく。
みなさんが既存のプロジェクトでdockerを使用する際の雰囲気をつかんでいただけたら幸いです。