Rails6 (Unicorn) + MariaDB + Nginx on Docker (M1 preview 버전)의 환경 구축

0. 소개



여기에서는 게시물의 환경을 구축하기 위한 파일 구성과 작업 순서에 대해서, 요점 좁혀 기록하고 있습니다. 상세를 이해하고 싶은 분은, 참고 문헌을 이하에 기재하므로 그쪽을 참조해 주세요.

【참고 문헌】
(1) 원래 Docker 컨테이너에 대해
【연재】 세계 제일 이해가 깊은 컨테이너 & Docker 입문

(2) 본 기사에 나타낸 Dockerfile이나 docker-compose.yml의 내용에 대해서
Docker + Rails + Puma + Nginx + MySQL
【Rails】Rails 6.0 x Docker x MySQL로 환경 구축
Docker로 Nginx+unicorn+rails+Mysql 개발 환경을 만들어 보았다

(3) nginx와 unicorn의 설정 파일 내용에 대해
(배포편 ①) 세계 제일 정중한 AWS 해설. EC2를 사용하여 Rails 앱을 AWS에 올 때까지

1. 파일 구성



test 폴더 안에서 작업을 해 나가기로 해서, 최초로 준비하는 파일의 구성은 이하와 같다.
test ─── docker-compose.yml
      ├─ Dockerfile
      ├─ Gemfile
      ├─ Gemfile.lock
      └─ Nginx ─── Dockerfile
                └─  nginx.conf

이어서 하나씩 파일의 내용을 살펴보겠습니다.

1. docker-compose.yml



db, unicorn, nginx의 순서로 컨테이너를 작성·기동해, 연계하도록 기술. (※ 기동 순서를 제어하는 ​​dependes_on은 기동의 완료까지는 보증하지 않으므로, 사전에 기동이 완료해 주었으면 하는 서비스가 일어나고 있는지 확인하려면 다른 스크립트를 써야 합니다.)

docker-compose.yml
version: '3.5'
services:
  db:
    image: mariadb:10.3
    ports: 
      - '3306:3306'
    environment:
      MYSQL_DATABASE: docker_development
      MYSQL_ROOT_PASSWORD: password
      MYSQL_USER: root
      MYSQL_PASSWORD: password
    volumes: #dbコンテナの/var/lib/mysqlディレクトリをmysql-dataボリュームにマウント
      - mysql-data:/var/lib/mysql

  unicorn:
    build:
      context: .
      dockerfile: Dockerfile
    command: bundle exec unicorn -c /myproject/config/unicorn.conf.rb 
    tty: true #pry-byebugを使えるようにする
    stdin_open: true
    depends_on:
      - db 
    ports:
      - "3000:3000"
    volumes:
      - .:/myproject:cached 
      #unicornコンテナの/myproject/tmp/socketsディレクトリをtmp-dataボリュームにマウント
      #ソケット通信用ファイルをnginxコンテナと共有するため
      - tmp-data:/myproject/tmp/sockets
      #unicornコンテナの/myproject/publicディレクトリをpublic-dataボリュームにマウント
      #画像データなどをnginxと共有するため
      - public-data:/myproject/public

  nginx:
    build:
      context: ./nginx
      dockerfile: Dockerfile
    ports:
      - 80:80
    restart: always #restartポリシーをalwaysにすると、ホストOSやDockerデーモン起動時にコンテナが自動的に起動する。
    volumes:
      #nginxコンテナの/myproject/tmp/socketsディレクトリをtmp-dataボリュームにマウント
      #ソケット通信用ファイルをunicornコンテナと共有するため
      - tmp-data:/myproject/tmp/sockets
      #nginxコンテナの/myproject/publicディレクトリをpublic-dataボリュームにマウント
      #画像データなどをunicornと共有するため
      - public-data:/myproject/public
    depends_on:
      - unicorn

# ホストOS上にpublic-data, tmp-data, mysql-dataという名前のデータ保存領域を作成
volumes:
  public-data:
  tmp-data:
  mysql-data:
    driver: local

2. Dockerfile



Rails6을 실행하는 컨테이너 이미지의 레시피. Rails6.0부터는 Webpacker를 사용한 JS의 관리가 디폴트가 되고 있어 Webpacker를 동작시키기 위해서는 Node.js와 yarn이 필요. 그래서 루비 이미지를 가져온 후 Node.js와 yarn을 설치합니다. 그 후, Host측에 있는 Gemfile과 Gemfile.lock 파일을 컨테이너내에 카피해, bundler gem을 인스톨 해, bundle install 커멘드로 필요한 gem을 모두 인스톨.

Dockerfile
#既存のプロジェクトのrubyのバージョンを指定
FROM ruby:2.7

#パッケージの取得
RUN 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 --no-install-recommends\
    nodejs  \
    yarn \
    mariadb-client  \
    build-essential

WORKDIR /myproject

COPY Gemfile /myproject/Gemfile
COPY Gemfile.lock /myproject/Gemfile.lock
RUN gem install bundler
RUN bundle install

COPY . /myproject

3. Gemfile



Rals 컨테이너에 설치할 gem 목록 파일. rails new 에서 필요한 gem이 자동 추기되므로 처음에는 ruby와 rails gem만 기재. bundle install 에서 오류 (참조 : Rails의 GPL 오염 문제 요약 (mimemagic) )가 나왔다 (2021년 3월 29일 현재), 그래서 mimemagic에 의존하지 않는 rails 6.0.3.6을 지정하여 설치.

Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.7.2'
gem 'rails', "6.0.3.6"

4. Gemfile.lock



자동 추가되기 때문에 아무것도 기술하지 않는다.

Gemfile.lock
#空ファイル

5. nginx/Dockerfile



nginx 이미지 작성 레시피. 호스트측에서 준비한 nginx 설정 파일을 컨테이너내에 카피해, 컨테이너 기동과 동시에 nginx 서버가 기동하도록 기술.

nginx/Dockerfile
FROM nginx:stable
#デフォルトのnginxファイルを削除して作成したものコンテナ内にコピー
RUN rm -f /etc/nginx/conf.d/*

COPY nginx.conf /etc/nginx/conf.d/myproject.conf

#-c以降の設定ファイルを指定して起動 daemon offでフォアグラウンドで起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf

6. nginx/nginx.conf



/etc/nginx/conf.d/myproject.conf에 복사되는 nginx 구성 파일.

nginx/nginx.conf
upstream unicorn {
  #ユニコーンソケットの設定
  server unix:/myproject/tmp/sockets/.unicorn.sock fail_timeout=0;
}

server {
  #IPとポートの指定
  listen 80 default;
  #サーバーネームの指定
  server_name localhost;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;
  #ドキュメントルートの指定
  root /myproject/public;

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  $uri/index.html $uri @unicorn;
  keepalive_timeout 5;

  location @unicorn {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://unicorn;
  }
}

2. 작업 절차



(1) rails 앱을 작성.

terminal
$ docker-compose run unicorn rails new . --force --database=mysql --webpacker

(2) Gemfile에 unicorn gem을 추가하고 docker-compose build 여기서 필요한 모든 gem이 설치됩니다.

Gemfile
gem 'unicorn'

terminal
$ docker-compose build

(3) unicorn.conf.rb를 아래와 같이 기술.

unicorn.conf.rb
$worker  = 2
  $timeout = 30
  $app_dir = "/myproject" #自分のアプリケーションまでのpath
  $listen  = File.expand_path 'tmp/sockets/.unicorn.sock', $app_dir
  $pid     = File.expand_path 'tmp/pids/unicorn.pid', $app_dir
  $std_log = File.expand_path 'log/unicorn.log', $app_dir
  # set config
  worker_processes  $worker
  working_directory $app_dir
  stderr_path $std_log
  stdout_path $std_log
  timeout $timeout
  listen  $listen
  pid $pid
  # loading booster
  preload_app true
  # before starting processes
  before_fork do |server, worker|
    defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
    old_pid = "#{server.config[:pid]}.oldbin"
    if old_pid != server.pid
      begin
        Process.kill "QUIT", File.read(old_pid).to_i
      rescue Errno::ENOENT, Errno::ESRCH
      end
    end
  end
  # after finishing processes
  after_fork do |server, worker|
    defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
  end

(4) docker-compose.yml 설정과 일치하도록 database.yml을 수정하고 docker-compose run unicorn bundle exec rails db:create 명령으로 DataBase를 만듭니다. 그런 다음 docker-compose build 명령을 db 컨테이너에 반영합니다.

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password
  host: db

development:
  <<: *default
  database: docker_development

terminal
$ docker-compose run unicorn bundle exec rails db:create
$ docker-compose run unicorn bundle exec rails db:migrate

(5) docker-compose up -d 명령으로 컨테이너를 시작합니다. localhost:80에 액세스할 수 있는지 확인.

terminal
$ docker-compose up -d

좋은 웹페이지 즐겨찾기