概要
docker,Rails,Nginx,PostgreSQLという定番の構成が簡単に出来る記事を目指しました。Rails単独でのアプリ作成経験があることを前提に書いている部分があります。
以下の構成のWebアプリをdocker-composeを使って連携させます。
- ホストOS: CentOS7 (v7.6.1810)
- 仮想化: docker (v19.03.2, build 6a30dfc)
- 仮想化補助: docker-compose (v1.24.1, build 4667896b)
- Webサーバ: nginx (v1.16.1 stable)
- Webアプリ: Ruby On Rails (5.2.3)
- Webアプリ言語: Ruby (v2.6.3)
- DBサーバ: PostgreSQL (v11.5 Debian 11.5-1.pgdg90+1)
事前準備
ディレクトリ・ファイル構成
以下は完成形のディレクトリ・ファイル構成のイメージです。
minimal
┣ nginx
┃ ┣ Dockerfile
┃ ┗ nginx.conf
┣ postgresql
┃ ┣ Dockerfile
┃ ┗ after_init.sh
┣ app
┃ ┣ Dockerfile
┃ ┣ startup.sh
┃ ┣ .dockerignore
┃ ┗ Rails作成のファイル・フォルダ(Gemfile, app, bin...)
┣ docker-compose
┗ docker-comopse.yml
dockerのインストール
Docker公式インストール手順 がありますので最新情報はそちらを参照してください。
過去のバージョンのdockerのアンインストール
$ sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
docker動作に必要なパッケージのインストール
$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2
yum用リポジトリにdockerパッケージの情報を追加
$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yumでdockerをインストール
$ sudo yum install docker-ce docker-ce-cli containerd.io
dockerデーモンの起動と有効化
$ sudo systemctl start docker
$ sudo systemctl enable docker
docker-composeのインストール
こちらも docker-compose公式インストール手順 があります。最新情報は公式ページのLinuxへのインストールの部分を参照してください。
上記の手順ではdocker-composeは /usr/local/bin
にインストールしていますが、ここではminimalディレクトリ直下に置きます。docker-composeは単独で動作する実行ファイルなので、同じディレクトリ内で管理した方がバージョン管理がしやすいからです。
docker-composeのダウンロード
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /minimal/docker-compose
実行権限の付与
$ sudo chmod +x /minimal/docker-compose
RubyおよびRailsのインストール
RubyおよびRailsのインストールに関しては他に詳しい記事がいくらでもあるのでこちらでは詳細は割愛します。
通常のRailsと違う点として、コンテナが終了しても残しておきたいデータをボリューム(Data Volume)としてマウントすることを前提とします。Bundlerでのインストールがコンテナ再作成の度に実行されるのはかなり時間がかかるため、gemは共有ディレクトリに配置します。
- rbenv等で最新のRubyをインストール。
-
/minimal/app
配下でbundle init
。 - app配下に
share/bundle
ディレクトリを作成。 - 作成されたGemfileを編集してrailsのgemを有効化。
-
bundle install --path share/bundle
してrailsのgemをインストール。 -
bundle exec rails new -d postgresql .
でrailsアプリを初期化。 - 適宜Gemfile等を編集しながらrailsが動くようにする。
※今回は簡単な動作確認のため、以下のコマンドでscaffoldを作って確認した。
$ bundle exec rails g scaffold User name:string
docker-composeの設定
docker-composeの設定ファイル( docker-compose.yml
)を作成します。各コンテナの構成の紹介の中で内容に触れます。
version: '3.7'
services:
postgresql:
build:
context: ./postgresql
dockerfile: Dockerfile
environment:
- POSTGRES_PASSWORD=password
expose:
- "5432"
restart: always
network_mode: host
volumes:
- postgresql_data:/var/lib/postgresql/data
app:
build:
context: ./app
dockerfile: Dockerfile
environment:
- PORT=3000
- POSTGRES_PASSWORD=password
- RAILS_MASTER_KEY=0123456789abcdef0123456789abcdef
expose:
- "3000"
restart: always
network_mode: host
volumes:
- app_data:/app/share
depends_on:
- postgresql
nginx:
build:
context: ./nginx
dockerfile: Dockerfile
ports:
- 80:80
restart: always
network_mode: host
depends_on:
- app
volumes:
postgresql_data:
driver: local
app_data:
driver: local
Nginxコンテナの設定
dockerhub上の「Nginx公式イメージ」を使います。
このページの「Complex configuration」セクションを読むと、ユーザ定義の nginx.conf
をコンテナに取り込む方法が紹介されています。これを参考に設定をコンテナイメージに取り込みます。
nginx.confの内容
以下の内容は公式イメージそのままの nginx.conf
にrailsコンテナと連携するための情報を書き加えたものです。
#include /etc/nginx/conf.d/*.conf;
以下の部分を変更しました。
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
#include /etc/nginx/conf.d/*.conf;
upstream web {
server localhost:3000;
}
server {
listen 80;
charset UTF-8;
keepalive_timeout 5;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://web;
}
}
}
変更内容
- include行をコメントアウト
- upstreamセクションでlocalhost:3000を転送先に指定
- serverセクションで80番ポートへの全ての通信をupstreamに転送
ここでupstreamにlocalhost:3000を指定しているのがポイントです。
docker-compose.yml
で各コンテナが「network_mode: host」で接続されています。また、railsコンテナ設定部分で「expose: "3000"」を指定し、dockerコンテナ内部ネットワーク向けのみに3000番ポートを公開しています。 よってnginxコンテナからrailsコンテナに「localhost+ポート番号」で名前解決が出来るのです。
Dockerfileの内容
nginx.confファイルを上書きします。
FROM nginx:stable
MAINTAINER Kou Sirataki<srtkkou@outlook.jp>
# 設定ファイルの上書き
COPY nginx.conf /etc/nginx/nginx.conf
PostgreSQLコンテナの設定
dockerhub上の「PostgreSQL公式イメージ」を使います。
このページの「Initialization Scripts」セクションを読むと、 /docker-entrypoint-initdb.d
配下の *.sql, *.sql.gz, *.sh
ファイルはデータディレクトリ(通常は /var/lib/postgresql/data
)が空の場合には initdb
によるDB初期化後に実行されるとあります。 after_init.sh
に初期化後に実行したいスクリプトを書き込みます。
また、DBのデータがコンテナを再起動する度に消えると困るので、データディレクトリ( /var/lib/postgresql/data
)はボリューム(Data Volume)としてマウントします。
after_init.shの内容
コンテナ以外のサーバからユーザとパスワードを使ったアクセスを許可するため、データディレクトリ配下の pg_hba.conf
を編集し、md5で認証可能にします。
#!/bin/bash
# pg_hba.confの編集
PG_HBA=/var/lib/postgresql/data/pg_hba.conf
# - 現在の設定のコメントアウト
sed -ri 's/^(local*)/#\1/g' $PG_HBA
sed -ri 's/^(host*)/#\1/g' $PG_HBA
# - パスワードアクセスの許可設定挿入
echo 'local all all peer' >> $PG_HBA
echo 'host all all 127.0.0.1/32 md5' >> $PG_HBA
echo 'host all all 172.0.0.0/8 md5' >> $PG_HBA
echo 'host all all ::1/128 md5' >> $PG_HBA
# 設定のリロード
pg_ctl reload
pg_ctl status
Dockerfileの内容
「Initialization Scripts」セクションに書かれている例を参考に、ロケールを日本語に設定します。また、上の after_init.sh
スクリプトを適切な場所にコピーします。
FROM postgres:11
MAINTAINER Kou Sirataki<srtkkou@outlook.jp>
# 日本語ロケールへの変更
RUN localedef -i ja_JP -c -f UTF-8 \
-A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
# スタートアップスクリプトの登録
COPY after_init.sh /docker-entrypoint-initdb.d/after_init.sh
Railsコンテナの設定
Rails等のgemはGemfileで制御したいので、ruby環境のみ設定されたdockerhub上の「Ruby公式イメージ」を使います。
現在のRailsの既定のwebサーバはPumaであり、環境変数PORTからポート番号を決める設定になっています。よって docker-compose.yml
内の「environment」セクションで環境変数として設定しています。
本番環境(RAILS_ENV=production)では暗号化された設定ファイル credentials.yml.enc
を開くために、環境変数RAILS_MASTER_KEYで復号化鍵を設定する必要があります。これも docker-compose.yml
内の「environment」セクションで環境変数として設定しています。上の docker-compose.yml
ファイルでは仮の内容を設定していますが、実際は master.key
の内容を記入してください。
database.ymlの設定(抜粋)
PostgreSQLを使う設定について、参考までに database.yml
の一部を抜粋してお見せします。 docker-compose.yml
のPOSTGRES_PASSWORD環境変数からパスワードを取得することを想定しています。
Nginxの項で説明した通り docker-compose.yml
で各コンテナが「network_mode: host」で接続されており、postgresqlコンテナ設定部分で「expose: "5432"」を指定しているため、railsコンテナからpostgresqlコンテナへは「localhost:5432」で名前解決できます。
※抜粋
production:
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch('RAILS_MAX_THREADS'){ 5 } %>
username: postgres
password: <%= ENV.fetch('POSTGRES_PASSWORD'){ '' } %>
host: localhost
port: 5432
database: app_production
startup.shの設定
docker run
時に実行されるスクリプトを作成します。
#!/bin/bash
# ボリューム/app/share配下の初期化
mkdir -p /app/share/{bundle,log,pids,sockets}
chown -R app:app /app/share
# Gemの更新
gosu app bundle install --path share/bundle --without test development
gosu app bundle clean
# DB初期化
gosu app bundle exec rails db:create
gosu app bundle exec rails db:migrate
# JS/CSSファイルのコンパイル
gosu app bundle exec rails assets:precompile
# アプリケーションの開始
gosu app bundle exec rails s
Dockerfileの設定
FROM ruby:2.6.3
MAINTAINER Kou Sirataki<srtkkou@outlook.jp>
# 日本語ロケールへの変更
RUN apt-get update && \
apt-get install -y \
locales && \
echo "ja_JP.UTF-8 UTF-8" >> /etc/locale.gen && \
locale-gen && \
update-locale LANG=ja_JP.UTF-8
# 環境変数の設定
# - LANG: ロケール
# - RAILS_ENV: Rails実行環境
# - RAILS_SERVE_STATIC_FILES: 静的ファイルの公開設定
ENV LANG="ja_JP.UTF-8"\
RAILS_ENV="production"\
RAILS_SERVE_STATIC_FILES="true"
# 必要なライブラリのインストール
# - gosu: docker内でroot実行
# - git: ソースコード管理
# - sqlite3: Sqlite3データベース
# - libpq-dev: PostgreSQLデータベース用ライブラリ
# - nodejs: サーバサイドJavaScript
# - yarn: JavaScriptパッケージマネージャ
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - && \
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update && \
apt-get install -y \
gosu \
git \
sqlite3 \
libpq-dev \
nodejs \
yarn
# 実行ユーザ、実行ディレクトリの追加
RUN useradd -m app -s /bin/bash -u 5000 && \
mkdir /app && \
chown app:app /app && \
chmod 777 /app
# サービス開始スクリプト
CMD ["/app/startup.sh"]
# /app配下へのソースの配置
WORKDIR /app
COPY --chown=5000:5000 ./ /app/
.dockerignoreの設定
railsアプリのディレクトリをdockerでコピーする際に無視したいファイルを .dockerignore
ファイルに指定できます。マスターキーをコンテナに含めてしまうのはセキュリティ上良くないので、少なくともマスターキーは無視するファイルに含めた方が良いでしょう。
# 環境変数設定ファイルの無視
/.env
# マスターキーの無視
/config/master.key
# Sqliteデータベースの無視
/db/*.sqlite3
/db/*.sqlite3-journal
# share配下の無視
/share/*
!/share/.keep
docker-composeの実行
上記の設定を行った上でdockerコンテナを作成し、動かします。
$ sudo /minimal/docker-compose build
$ sudo /minimal/docker-compose run -d
以下のコマンドでコンテナが正常に動作していることを確認します。
$ sudo docker ps
Nginxを通じてRailsのコンテナにアクセスできることを確認します。curlコマンドでアクセス出来ることや、ブラウザで操作が出来ることを確認します。
以上です。
補足
- 今回の
docker-compose.yml
では記述の簡便化のためにパスワードやマスターキーをそのまま設定ファイル内の環境変数として書いていますが、これをそのままリポジトリ管理してしまうと非常に危険です。「environment」の代わりに「env_file」を使い、環境変数は全て別ファイルに定義する等、安全策を取りましょう。 - 動作確認はCentOS 7(v7.6.1810)でのみ行っています。
- 本記事内容はあくまで例であり、記事内容によって発生したいかなる損害に対しても責任は取りかねます。