LoginSignup
14
17

More than 3 years have passed since last update.

最小構成のdocker+Railsアプリ(Nginx/PostgreSQL/Rails/docker-compose)

Last updated at Posted at 2019-09-20

概要

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は共有ディレクトリに配置します。

  1. rbenv等で最新のRubyをインストール。
  2. /minimal/app 配下で bundle init
  3. app配下に share/bundle ディレクトリを作成。
  4. 作成されたGemfileを編集してrailsのgemを有効化。
  5. bundle install --path share/bundle してrailsのgemをインストール。
  6. bundle exec rails new -d postgresql . でrailsアプリを初期化。
  7. 適宜Gemfile等を編集しながらrailsが動くようにする。

※今回は簡単な動作確認のため、以下のコマンドでscaffoldを作って確認した。

$ bundle exec rails g scaffold User name:string

docker-composeの設定

docker-composeの設定ファイル( docker-compose.yml )を作成します。各コンテナの構成の紹介の中で内容に触れます。

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; 以下の部分を変更しました。

nginx.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」で名前解決できます。

config/database.yml
※抜粋
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 時に実行されるスクリプトを作成します。

startup.sh
#!/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コマンドでアクセス出来ることや、ブラウザで操作が出来ることを確認します。

以上です。

補足

  1. 今回の docker-compose.yml では記述の簡便化のためにパスワードやマスターキーをそのまま設定ファイル内の環境変数として書いていますが、これをそのままリポジトリ管理してしまうと非常に危険です。「environment」の代わりに「env_file」を使い、環境変数は全て別ファイルに定義する等、安全策を取りましょう。
  2. 動作確認はCentOS 7(v7.6.1810)でのみ行っています。
  3. 本記事内容はあくまで例であり、記事内容によって発生したいかなる損害に対しても責任は取りかねます。

参考記事

14
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
17