はじめに
皆さん、こんにちは。今回は久々にRails(どちらかと言うとDockerだけど、その中でRailsに特化したということで)の記事を執筆しようと思います。
今回の記事の執筆の経緯ですが直近、会社の開発環境をリプレースする仕事を行なっていました。従来、AWSのEC2インスタンス上で直接Railsを動かしていたのですが、会社のプロジェクトが増えた事もあり、開発環境で複数のプロジェクトに対応できるように、これまた複数のRails環境を構築する事も増えてきました。そのような背景があり、複数のプロジェクトに対応することになっても、対応できるようにコンテナ型仮想化で独立したRails環境を作る
必要が出てきました。
という事で、Rails環境を動かすコンテナ群を設定するためのdocker-compose.yml、Dockerfileのベターな設定(?)を共有できたらと思います。
コンテナ型仮想化について
こちらを参照→Dockerチュートリアル 開発者向け
docker-compose.ymlの設定
docker-compose.ymlの設定は以下のようにしました。
version: '3'
volumes:
pids:
services:
web:
build:
context: .
dockerfile: Dockerfile
command: bundle exec rails s -p 3000 -b 0.0.0.0
volumes:
- .:/usr/local/rails
- pids:/usr/local/rails/tmp/pids
links: [db, redis]
environment:
- DEVELOPMENT_HOST=db
- TEST_HOST=db
- REDIS_URL=redis://redis:6379
- REDIS_PORT_6379_TCP_ADDR=redis
ports:
- 3000:3000
stdin_open: true
tty: true
db:
image: mysql:5.7
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --innodb-large-prefix=true --innodb-file-format=Barracuda
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
ports:
- 3306:3306
redis:
image: redis:latest
ports:
- 6379:6379
一つ一つ、docker-compose.ymlの設定を確認していきましょう。
docker-compose.ymlの設定を確認してみる
今回はdocker-composeの記述の説明は省略し、Railsアプリを開発するためにどのような設定を行なったのか
という事を重点に置いて説明していきます。
build:
context: .
dockerfile: Dockerfile
command: bundle exec rails s -p 3000 -b 0.0.0.0
と言いながら、いきなりdocker-compose.ymlの記述の説明です。実行するRailsコンテナのコンテナイメージをカスタムして構築するようにDockerfileで構築するように指定しています。更に、コンテナ起動時に渡す起動コマンドとしてbundle経由でのRailsサーバーの起動コマンドを渡しています。
volumes
volumes:
pids:
volumes:
- .:/usr/local/rails
- pids:/usr/local/rails/tmp/pids
次にvolumesの説明です。ホスト配下のディレクトリとコンテナ配下のディレクトリをマウントしています。これにより、ホストのファイルを変更した時に即座にRailsコンテナに変更が反映されるようにしています(勿論、再起動しなければ反映されないconfigの設定などは置き換えた後にrestartかけるようにします)。
本来は、Railsアプリの直下のディレクトリ全部マウントしたいところですが、tmp/pidsをマウントしてしまうとコンテナの方でpidファイルが残り続けてしまいます。そうなると、Railsサーバー再起動時にpidファイルの存在を認識してしまいpidファイルを消さない限りRailsサーバーが起動できない問題が発生したため、
コンテナ専用の空のData Volumeを作成し、tmp/pidsにマウントすることでコンテナ直下のtmp/pidsの内容をクリアにします。
DB, Redisのリンク
links: [db, redis]
environment:
- DEVELOPMENT_HOST=db
- TEST_HOST=db
- REDIS_URL=redis://redis:6379
- REDIS_PORT_6379_TCP_ADDR=redis
ports:
- 3000:3000
この辺りは単純に環境変数をあらかじめ渡してあげて接続するDBやRedisを認識しているだけです。
binding.pryなどのデバッグ系ツールを使うために、stdin_open,ttyを有効にしておく
stdin_open: true
tty: true
基本的にdocker-composeで動かすときはdocker-compose up -d
でコンテナ一式をデーモンモードで起動しています。その際、デバッグ系のツールを利用して、値や実行の流れを確認したい時にコンテナへ標準入力ができるようにしておく必要があります。
上記のコマンドで、標準入力の有効化、標準入力のデバイスを有効にするために、ttyをtrueにしています。
また、デーモンモードで動いているコンテナにアタッチするにはdocker ps などでコンテナIDを取得し、コンテナにattachする必要があります。
% docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cfbc22800121 web bundle exec rails s -b 0.0.0.0 27 hours ago Up 2 hours 0.0.0.0:3000->3000/tcp
% docker attach cfbc22800121
以上で、docker-composeの設定は終わりです。続いて、Dockerfileの設定です。
Dockerfileの設定
rails専用のカスタムイメージを構築するために、Dockerfileスクリプトを記述していきます。
全体の内容は以下のようになります。
FROM ruby:*****
RUN apt update
# 必要なライブラリをインストール
RUN apt install -y build-essential libpq-dev apt-transport-https apt-utils libprotobuf-dev libprotoc-dev protobuf-compiler
# 日本語環境を整える
ENV LANG C.UTF-8
# システム時刻をJSTに変更
RUN rm -f /etc/localtime
RUN ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
# phantomjsをインストール
RUN apt install -y chrpath libssl-dev libxft-dev
RUN apt install -y libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev
ADD https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 phantomjs-2.1.1-linux-x86_64.tar.bz2
RUN tar xvjf phantomjs-2.1.1-linux-x86_64.tar.bz2 -C /usr/local/share/
RUN ln -sf /usr/local/share/phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin
# 最新のnpmをインストール
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash -
RUN apt install -y nodejs
RUN apt install -y mysql-client
# Chrome & ChromeDriverインストール
RUN apt-get install -y unzip && \
CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \
wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -P ~/ && \
unzip ~/chromedriver_linux64.zip -d ~/ && \
rm ~/chromedriver_linux64.zip && \
chown root:root ~/chromedriver && \
chmod 755 ~/chromedriver && \
mv ~/chromedriver /usr/bin/chromedriver && \
sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -' && \
sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \
apt-get update && apt-get install -y google-chrome-stable
# 日本語フォント設定
RUN mkdir /noto
ADD https://noto-website.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip /noto
WORKDIR /noto
RUN unzip NotoSansCJKjp-hinted.zip && \
mkdir -p /usr/share/fonts/noto && \
cp *.otf /usr/share/fonts/noto && \
chmod 644 -R /usr/share/fonts/noto/ && \
/usr/bin/fc-cache -fv
WORKDIR /
RUN rm -rf /noto
RUN mkdir -p /usr/local/rails
# bundle installのキャッシュ(詳細は後述)
WORKDIR /usr/local/rails
ADD Gemfile Gemfile
ADD Gemfile.lock Gemfile.lock
ADD package.json package.json
ADD config/database.yml config/database.yml
RUN bundle install
Dockerfileの中でやっている事は、ほぼコメントに残しているのですが、一つ一つ解説していきます。
Rails実行前のインストール作業
FROM ruby:*****
RUN apt update
# 必要なライブラリをインストール
RUN apt install -y build-essential libpq-dev apt-transport-https apt-utils libprotobuf-dev libprotoc-dev protobuf-compiler
この部分はデフォルトでrubyが入ったコンテナイメージ(*****はversionを指定する)をRailsが動くようにしたいのでパッケージを入れます。コンテナのOSはDebianなため、aptリポジトリからパッケージをダウンロードします。
日本語環境やシステム時刻の設定
# 日本語環境を整える
ENV LANG C.UTF-8
# システム時刻をJSTに変更
RUN rm -f /etc/localtime
RUN ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
rails c
の日本語入力、またRSpec
における時刻によるテスト事例を正しく検証するために、LANG環境変数をC.UTF-8に指定し、更に、コンテナの/etc/localtimeにホストのJSTのバイナリファイルのシンボリックリンクを作ってあげます。
ブラウザパッケージのインストール
# phantomjsをインストール
RUN apt install -y chrpath libssl-dev libxft-dev
RUN apt install -y libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev
ADD https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 phantomjs-2.1.1-linux-x86_64.tar.bz2
RUN tar xvjf phantomjs-2.1.1-linux-x86_64.tar.bz2 -C /usr/local/share/
RUN ln -sf /usr/local/share/phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin
# 最新のnpmをインストール
RUN curl -sL https://deb.nodesource.com/setup_9.x | bash -
RUN apt install -y nodejs
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN apt install -y mysql-client
# Chrome & ChromeDriverインストール
RUN apt-get install -y unzip && \
CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \
wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -P ~/ && \
unzip ~/chromedriver_linux64.zip -d ~/ && \
rm ~/chromedriver_linux64.zip && \
chown root:root ~/chromedriver && \
chmod 755 ~/chromedriver && \
mv ~/chromedriver /usr/bin/chromedriver && \
sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -' && \
sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \
apt-get update && apt-get install -y google-chrome-stable
# 日本語フォント設定
RUN mkdir /noto
ADD https://noto-website.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip /noto
WORKDIR /noto
RUN unzip NotoSansCJKjp-hinted.zip && \
mkdir -p /usr/share/fonts/noto && \
cp *.otf /usr/share/fonts/noto && \
chmod 644 -R /usr/share/fonts/noto/ && \
/usr/bin/fc-cache -fv
WORKDIR /
RUN rm -rf /noto
この部分では、Feature Spec、System Specのテストを実行できるようにHeadless BrowserのPhantomjs、およびGoogle Chromeを入れています。まだ、System Specの方はテストシナリオを作成していませんが、今後のサポートのためにあらかじめ入れておきます。
RUN mkdir -p /usr/local/rails
# bundle installのキャッシュ(詳細は後述)
WORKDIR /usr/local/rails
ADD Gemfile Gemfile
ADD Gemfile.lock Gemfile.lock
ADD package.json package.json
ADD config/database.yml config/database.yml
RUN bundle install
最後にRailsを起動プロセスとしているのでRailsが起動できるようにbundle installが行えるまでの下準備を設定しておきます。
このスクリプトでコンテナイメージを作成し、コンテナ群を起動します。
% docker-compose build
% docker-compose up -d
その後は
migrateやnpm run buildなどはホスト経由で行うようにします。コンテナイメージを作成するために、及びRailsサーバーを起動するのに必要でないものは後で行うところがミソです。
% docker exec web_1 bundle exec rake db:create
% docker exec web_1 bundle exec rake db:migrate
% docker exec web_1 bundle exec rake db:seed
% docker exec web_1 npm install
% docker exec web_1 npm run build
まとめ
ちょっとRailsコンテナの作り方に慣れすぎたせいでこんな記事を書いてみました。
が、他のプラットフォームやフレームワークでのベストプラクティスな環境も作っていきたいと思うので、またどこかで、違う言語でのベストプラクティスなコンテナ構築の記事も書いていきたいですね。それでは。
参考文献
今回はなし