LoginSignup
26
23

More than 5 years have passed since last update.

dockerでrailsの実行環境(nginx + rails)を作る

Posted at

参考にしたところ

モチベーション

dockerを使ってproductionでrailsアプリケーションを動かします。データベースはdockerのイメージの中に同梱するのではなく、別にあるDBサーバに接続します。

dockerのイメージには、supervisordがnginxとrailsのプロセスを起動します。

RailsのWebアプリケーションは開発は継続しているので、なるべく迅速にgitリポジトリからソーツツリーをクローンしてdeployできるようにしたいです。

dockerはMacで1.8を使っています。

ベースのリポジトリを作る

一気にdockerイメージをスクラッチからビルドしてもいいのですが、その場合、rubyのコンパイルなどが走って時間がかかります。そのため、ベースとなるdockerイメージと、railsのWebアプリケーションと幾つかの設定ファイルだけをインストールするための二つのイメージを作成します。rubyがインストールされたベースとなるイメージとrailsのイメージ、最後にWebアプリケーションの3つのイメージで管理しているサイトrailsをdockerで動かしたい場合の構成はどうするべきかもあります。自分たちはそこまで頻繁にビルドするわけではないことと、ベースシテムはセキュリティホール以外はそんなにアップデートしたくないので、2つにしています。

まずは、ベースリポジトリを作ります。
Dockerファイルは次の通りです。ubuntuの公式リポジトリをベースに作成します。

FROM ubuntu:14.04
# Install basic packages
RUN apt-get update
RUN apt-get install -y software-properties-common
RUN add-apt-repository ppa:chris-lea/node.js
RUN apt-key adv --keyserver keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A
RUN echo deb http://repo.percona.com/apt trusty main >> /etc/apt/sources.list
RUN echo deb-src http://repo.percona.com/apt trusty main >> /etc/apt/sources.list


RUN apt-get update
RUN apt-get install -y wget curl git
RUN apt-get install -y libc6-dev libc6 libc-dev gcc g++ cpp g++ build-essential
RUN apt-get install -y zlib1g-dev libssl-dev libreadline-dev libyaml-dev libxml2-dev libxslt-dev

# supervisor, nginx...
RUN apt-get install -y nginx supervisor
RUN apt-get install -y libssl-dev libperconaserverclient18.1-dev
RUN apt-get install -y nodejs

# Install ruby-build
RUN git clone https://github.com/sstephenson/ruby-build.git .ruby-build
RUN .ruby-build/install.sh
RUN rm -fr .ruby-build

# Install ruby-2.2.2
RUN ruby-build 2.2.2 /usr/local

# Install bundler
RUN gem update --system
RUN gem install bundler --no-rdoc --no-ri
RUN gem install mysql2 --version "=0.3.19" --no-rdoc --no-ri

RUN gem install rails --version "~>4.2.0" --no-rdoc --no-ri
RUN gem install unicorn  --version "~>4.9.0" --no-rdoc --no-ri



ENTRYPOINT ["bash", "-l", "-c"]

インストールしたのは

  • rubyのビルドに必要な環境
  • ruby 2.2.2
  • nginx
  • supervisord
  • nodejs
  • gem
    • rails
    • mysql2
    • unicorn(railsアプリはunicornで動かします)

libperconaserverclient18.1-devはmysqlのクライアントライブラリです。この環境ではMySQL5.6で動かします。ubuntu 14.04では5.5ベースなので、http://repo.percona.com/ のMySQLのライブラリを使っています。

このDockerファイルで

docker build  --no-cache -t webapp-base:0.1 .

でビルドしています。--no-cacheが付いているのは、キャッシュが効いてaptでのインストールが失敗することがあるからです。

Webアプリを含めたイメージを作る

ベースとなるイメージができたので次にWebアプリを含めたイメージを作ります。

gitリポジトリの展開

下準備として、Dockerファイルのあるディレクトリにgitリポジトリをコピーします。今回は、手元にcloneしてあるところからarchiveでソーツツリーをコピーします。

git archive --format=tar HEAD . | tar -C $DOCKER_DIR/webapp/ -x

これで、ソーツツリーがwebappの下に展開されました。

nginxの設定ファイル

nginxの設定ファイルは次のようになります。


http {
  # unicornのproxy設定
  # unixドメインでnginxとunicornを通信する
  upstream unicorn {
    server unix:/tmp/unicorn.sock;
  }

  server {
    listen  80;

    # Railsのassetファイルの参照先設定
    location /assets {
      root /webapp/public;
    }
  
    # webアプリのunicornでのproxy
    location / {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;
      proxy_redirect off;

      proxy_pass http://unicorn;
    }
  }

  error_log /var/log/nginx/error.log;
  access_log /var/log/nginx/access.log;
}

pid /var/run/nginx.pid;
worker_processes 2;
events {
    worker_connections  1024;
    # multi_accept on;
}

Railsのアプリケーションは/webappに配置します。

unicornの設定

unicornの設定ファイルは$GIT_REPOSITORY/conf/unicorn.rbに保存します。

unicorn.rb
# -*- coding: utf-8 -*-
# WEB_CONCURRENCY : nubmer of worker process.
# LOG_DIR : directory name to store log files

worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 15
preload_app true

listen "/tmp/unicorn.sock"
pid File.expand_path("unicorn.pid", "/var/run")

before_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
    Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

stderr_path File.expand_path('unicorn-err.log', ENV['LOG_DIR'] || "log")
stdout_path File.expand_path('unicorn.log', ENV['LOG_DIR'] || "log")


基本は参考にしたサイトのものをそのままですが、ログファイルの保存先はLOG_DIRは環境変数で設定可能にしています。

supervisord

supervisordの設定は次のようになります。

[supervisord]
nodaemon=true

[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true

[program:unicorn]
command=unicorn_rails -c config/unicorn.rb -E production
directory=/webapp
autostart=true
autorestart=true

わりかし普通。

dockerfile

dockerfileは次のようになります。

FROM webapp-base:0.1

RUN mkdir /webapp
WORKDIR /webapp

ADD webapp /webapp

RUN bundle install --without test development
RUN npm install

# react.jsの処理
RUN npm run bundle

# production環境precompile
RUN RAILS_ENV=production rake assets:precompile


WORKDIR /

copy conf/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
copy conf/nginx.conf /etc/nginx/nginx.conf

ENV SECRET_KEY_BASE=秘密

EXPOSE 22 80

CMD ["/usr/bin/supervisord"]

必要なものをDockerイメージにコピー後にgemとnpmのインストールを行っています。そのあと、production環境で動かすので、jsやcssはプレコンパイルしてnginxで配信できるようにしています。

データベースのホスト名はdatabase.yamlでhost: dbserverとしています。

これをベースにdockerイメージをビルドします。

起動

docker imageはour-webappとしてビルドしました。次のように起動します。

docker run -p 80:80 -it --add-host='dbserver:DBサーバの動いているIP' our-webapp

-p 80:80 でホストも80ポートでListenさせています。データベースの接続情報はdatabase.yamlにdbserverというホスト名を設定しているので、--add-host='dbserver:DBサーバの動いているIPで名前解決できるようんしています。

最後に

これで全部出来上がった、と思ったらHTTPSの設定を完全に忘れていました。

26
23
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
26
23