Docker로 Ruby on Rails 애플리케이션 구축하기

이재홍 http://www.pyrasis.com 2014.08.02 ~ 2014.09.20

Rails Dockerfile 작성하기

Ruby와 Rails 설치가 끝났으니 예제 Rails 애플리케이션을 생성합니다.

~$ rails new exampleapp --database=postgresql

exampleapp 디렉터리가 생성되었습니다. exampleapp/config 디렉터리 아래에 있는 database.yml 파일을 열고 다음과 같이 수정합니다.

~/exampleapp/config/database.yml

default: &default
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see rails configuration guide
  # http://guides.rubyonrails.org/configuring.html#database-pooling
  pool: 5
  template: template0
  username: postgres
  password: <%= ENV['DB_ENV_POSTGRESQL_PASSWORD'] %>
  host: <%= ENV['POSTGRESQL_HOST'] || 'db' %>
  • template: template0를 설정합니다. PostgreSQL은 데이터베이스를 생성할 때 기존 데이터베이스를 복제합니다. template0는 인코딩을 새로 설정할 수 있습니다. 이 부분을 설정하지 않으면 UTF-8 에러가 발생합니다.
  • username: postgres를 설정합니다.
  • password: 환경 변수의 DB_ENV_POSTGRESQL_PASSWORD를 사용하도록 설정합니다. docker run 명령의 --link 옵션으로 컨테이너를 연결했을 때 연결한 컨테이너의 환경 변수는 <별칭>_ENV_<환경 변수> 형식입니다. 우리는 컨테이너를 연결할 때 별칭을 db로 하고, 데이터베이스 컨테이너에서 환경 변수는 POSTGRESQL_PASSWORD를 사용할 것이기 때문에 DB_ENV_POSTGRESQL _PASSWORD가 됩니다.
  • host: || 연산자를 이용하여 개발 환경과 데이터베이스 컨테이너에서 사용할 데이터베이스 호스트를 각각 설정합니다. 개발을 할 때는 환경 변수의 POSTGRESQL_HOST에 데이터베이스 컨테이너의 IP 주소를 설정합니다. 그리고 데이터베이스 컨테이너를 연결할 때는 별칭을 db로 할 것이므로 db를 설정합니다.

MySQL 사용하기

~$ rails new exampleapp --database=mysql

~/exampleapp/config/database.yml

default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password: <%= ENV['DB_ENV_MYSQL_ROOT_PASSWORD'] %>
  host: <%= ENV['MYSQL_HOST'] || 'db' %>

Unicorn을 사용하기 위해 exampleapp 디렉터리 아래에 있는 Gemfile을 열고 gem ‘unicorn’ 부분의 주석을 해제합니다.

~/exampleapp/Gemfile

gem 'unicorn'

다음 내용을 Dockerfile로 저장합니다.

~/exampleapp/Dockerfile

FROM ubuntu:14.04

RUN apt-get update
RUN apt-get install -y autoconf bison build-essential \
    libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev git
RUN apt-get install -y nginx nodejs curl libpq-dev

RUN git clone https://github.com/sstephenson/rbenv.git /root/.rbenv
RUN git clone https://github.com/sstephenson/ruby-build.git \
    /root/.rbenv/plugins/ruby-build
ENV PATH /root/.rbenv/bin:/root/.rbenv/shims:$PATH

ENV CONFIGURE_OPTS --disable-install-doc
RUN rbenv install 2.1.3
RUN rbenv global 2.1.3
RUN rbenv init -

RUN echo 'gem: --no-rdoc --no-ri' >> /root/.gemrc
RUN gem install bundler
RUN rbenv rehash

RUN echo "daemon off;" >> /etc/nginx/nginx.conf
RUN rm -rf /etc/nginx/sites-enabled/default
ADD exampleapp.conf /etc/nginx/sites-enabled/exampleapp.conf

WORKDIR /tmp
ADD Gemfile Gemfile
ADD Gemfile.lock Gemfile.lock
RUN bundle install

ADD ./ /var/www/exampleapp
WORKDIR /var/www/exampleapp
RUN chmod +x entrypoint.sh

EXPOSE 80

ENTRYPOINT ./entrypoint.sh

여기서는 우분투 14.04에 apt-get으로 필요한 패키지를 설치하도록 구성하였습니다.

  • FROM으로 ubuntu:14.04를 기반으로 이미지를 생성하도록 설정합니다.
  • apt-get update로 패키지 목록을 최신 상태로 업데이트한 뒤 Git과 Ruby 설치에 필요한 패키지를 설치합니다. 그리고 nginx, nodejs, libpq-dev 패키지도 설치합니다.
    • nodejs는 JavaScript로 작성된 gem을 실행하기 위해 필요합니다.
    • libpq-dev는 PostgreSQL gem을 설치하기 위해 필요합니다.
  • git 명령으로 rbenv를 /root/.rbenv 디렉터리에 받습니다.
  • git 명령으로 ruby-build를 /root/.rbenv/plugins/ruby-build 디렉터리에 받습니다.
  • ENV로 환경 변수 PATH에 rbenv 경로를 추가합니다.
  • rbenv 명령으로 Ruby를 설치합니다.
    • ENV로 환경 변수 CONFIGURE_OPTS–disable-install-doc 옵션을 추가하여 문서를 설치하지 않습니다.
    • rbenv install 명령으로 Ruby 2.1.3 버전을 설치합니다.
    • rbenv global 명령으로 Ruby 2.1.3 버전을 모든 계정에서 사용하도록 설정합니다.
    • rbenv init - 명령을 실행하여 rbenv를 초기화합니다.
  • bundler를 설치합니다.
    • .gemrc 파일에 –no-rdoc, –no-ri 옵션을 추가하여 gem을 설치할 때 문서와 ri(문서 도구)를 설치하지 않습니다.
    • gem 명령으로 bundler를 설치하고 rbenv rehash 명령을 실행합니다.
  • Nginx를 데몬이 아닌 foreground로 실행하도록 설정합니다. Nginx를 데몬 상태로 실행하면 Docker 컨테이너가 바로 정지되므로 주의합니다.
  • /etc/nginx/sites-enabled 디렉터리에 있는 nginx 기본 설정 파일(default)을 삭제하고, exampleapp.conf 파일을 추가합니다.
  • /tmp 디렉터리에 Gemfile, Gemfile.lock 파일을 추가한 뒤 bundle install 명령으로 gem 파일을 설치합니다.
  • Rails 애플리케이션 디렉터리를 /var/www/exampleapp 디렉터리에 추가합니다.
  • entrypoint.sh 파일을 실행할 수 있도록 권한을 설정합니다.
  • EXPOSE에 80을 설정하여 80번 포트에 접속할 수 있도록 합니다.
  • ENTRYPOINT에 ./entrypoint.sh 파일을 설정하여 컨테이너가 시작되었을 때 스크립트 파일을 실행합니다.

Docker 이미지 생성 시간 줄이기
다음과 같이 Rails 애플리케이션 디렉터리(exampleapp)를 추가한 뒤 bundle install 명령을 실행하면 Rails 애플리케이션의 .rb 파일이나 기타 파일이 바뀔 때마다 gem 파일을 다시 설치하게 됩니다. Dockerfile은 ADD로 추가했던 디렉터리 안의 파일이 바뀌면 그 뒤에 오는 명령을 다시 실행하기 때문입니다.

ADD ./ /var/www/exampleapp
WORKDIR /var/www/exampleapp
RUN bash -l -c "bundle install"

다음과 같이 Rails 애플리케이션 디렉터리를 추가하기 전에 Gemfile, Gemfile.lock 파일만 따로 추가하여 bundle install 명령을 실행하면 .rb 파일 등이 바뀌어도 gem 파일을 다시 설치하지 않습니다.

WORKDIR /tmp
ADD Gemfile Gemfile
ADD Gemfile.lock Gemfile.lock
RUN bundle install

ADD ./ /var/www/exampleapp

Dockerfile의 캐시 기능을 활용하기 위해 자주 변경되지 않고, 시간이 오래 걸리는 부분은 따로 빼서 위로 올립니다.

MySQL 사용하기

~/exampleapp/Dockerfile

RUN apt-get install -y nginx nodejs curl libmysqlclient-dev

Unicorn 설정 파일이 필요합니다. 다음 내용을 unicorn.rb 파일로 저장합니다.

~/exampleapp/unicorn.rb

worker_processes 4

listen "/tmp/unicorn.sock", :backlog => 64
  • worker process는 각자 상황에 맞게 설정합니다.
  • /tmp/unicorn.sock에 유닉스 소켓을 생성합니다.

이제 Nginx 설정 파일을 작성합니다. 다음 내용을 exampleapp.conf 파일로 저장합니다.

~/exampleapp/exampleapp.conf

upstream unicorn {
  server unix:/tmp/unicorn.sock;
}

server {
  listen 80;
  server_name _;
  root /var/www/exampleapp/pubic;

  location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;

    if (!-f $request_filename) {
      proxy_pass http://unicorn;
      break;
    }
  }
}
  • upstream 항목에는 Unicorn의 유닉스 소켓 /tmp/unicorn.sock을 설정합니다.
  • server 항목을 설정합니다.
    • listen 80을 설정하여 80번 포트를 사용합니다.
    • Nginx는 정적 파일(html, js, css 등)을 전송할 것이므로 Rails 애플리케이션 디렉터리아래의 public 디렉터리를 설정합니다.
    • Nginx로 들어온 요청이 파일명이 아니면 앞에서 설정한 유닉스 소켓(http://unicorn)으로 보냅니다. 이렇게 설정하면 정적 파일은 Nginx가 전송하고, 나머지 RESTful API는 Rails가 처리하게 됩니다.

다음 내용을 entrypoint.sh로 저장합니다.

~/exampleapp/entrypoint.sh

#!/bin/bash

RAILS_ENV=${RAILS_ENV:-"development"}

bundle exec unicorn -D -c unicorn.rb
nginx
  • 환경 변수 RAILS_ENV는 docker run 명령의 -e 옵션으로 설정한 값이 있으면 그 값을 사용하고 없으면 development를 사용합니다.
  • unicorn-D 옵션을 사용하여 데몬 모드로 실행하고, -c 옵션을 사용하여 설정 파일로 unicorn.rb 파일을 사용합니다.
  • 앞에서 nginx.confdaemon off;로 설정했으므로 Nginx 웹 서버를 foreground로 실행합니다. 여기서 Nginx를 foreground로 실행하지 않으면 docker run -d로 컨테이너를 생성해도 바로 정지되므로 주의합니다.

docker build 명령으로 이미지를 생성합니다.

~/exampleapp$ sudo docker build --tag rails .

저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

30 November 2014