DockerでサーバーをPumaからNginx+unicornに切り替える

# はじめに
PumaでAWSにデプロイしていましたが、今回Nginx+Unicornに環境を切り替えましたので、その忘備録を作りました。dockerでPumaからサーバーを切り替えたい方の参考になればと思います。

### ファイル構成
コンテナはrailsアプリ用、Mysql用、nginx用の３つを作成

```text
.(railｓプロジェクト)
├── Dockerfile
├── docker-compose.yml
├── Gemfile
├── Gemfile.lock
├── config
|   └──datebase.yml
|   └──unicorn.conf.rb
└── nginx
     ├──Dockerfile 
     └──nginx.conf
```

### 1. Dockerfile(rails)

```ruby

# 19.01.20現在最新安定版のイメージを取得
FROM ruby:2.5.3

# dockerizeパッケージダウンロード用環境変数
ENV DOCKERIZE_VERSION v0.6.1

# 必要なパッケージのインストール
RUN apt-get update && \
    apt-get install -y --no-install-recommends\
    mariadb-client  \
    build-essential  \
    wget \
    vim \
    && wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
        && apt-get install -y nodejs

RUN mkdir /app_name
ENV APP_ROOT /app_name
WORKDIR $APP_ROOT

ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock

# エラー回避の為、bundlerのバージョン下げる
RUN gem install bundler:1.17.3
RUN bundle install
ADD . $APP_ROOT
```
今回 bundlerがないよ！とのエラーが発生した為、RUN gem install bundler で取得しました。最初は最新版をダウンロードしてしまった為、もともとプロジェクトで使用していたbundlerと変わってしまい整合性が取れなくなってしまい、バージョンを指定することにしました。


### 2. Dockerfile(Nginx) 作成
```dockerfile

FROM nginx:stable

COPY nginx.conf /etc/nginx/conf.d/myapp.conf

CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
```

### 3. nginx.conf 作成
```nginx

upstream unicorn {
  server unix:/app_name/tmp/sockets/.unicorn.sock fail_timeout=0;
}

server {
  listen 80 default;
  server_name localhost;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;
  root /app_name/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;
  }
}
```

### 4. Gemfile追加
```ruby
gem 'unicorn'
```

### 5. config/unicorn.conf.rb 作成
```ruby
$worker  = 2
$timeout = 30
# 自分のアプリケーションまでのpath
$app_dir = "/app_name"
$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
```
ここでは自分のアプリケーションまでのpathを間違えないこと。今回は $app_dir = "/app_name"で指定

### 6. 3.docker-compose.yml
```yml
version: '3'
services:
  # データベースのサービス定義
  db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    volumes:
      - db-volume:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: root
    ports:
      - '3306:3306'

  # アプリケーションサーバーのサービス定義
  web:
    build: .
    # unicorn.pidを事前に削除
    command: /bin/sh -c "rm -f tmp/pids/unicorn.pid && dockerize -wait tcp://db:3306 -timeout 20s bundle exec unicorn -p 3000 -c /app_name/config/unicorn.conf.rb"
    volumes:
      - .:/app_name
      - bundle:/usr/local/bundle:delegated
      - tmp-data:/app_name/tmp/sockets
      - public-data:/app_name/public
      - /app/vendor
      - /app/tmp
      - /app/log
      - /app/.git
    ports:
      - "3000:3000"
    tty: true
    stdin_open: true
    links:
      - db
    environment:
    - "SELENIUM_DRIVER_URL=http://selenium_chrome:4444/wd/hub"

  # WEBサーバーのサービス定義
  nginx:
    build:
      context: ./nginx
      dockerfile: Dockerfile
    ports:
      - 80:80
    restart: always
    volumes:
      - tmp-data:/app_name/tmp/sockets
      - public-data:/app_name/public
    links:
      - web

  selenium_chrome:
    image: selenium/standalone-chrome-debug
    logging:
      driver: none

volumes:
  public-data:
  tmp-data:
  mysql-data:
  db-volume:
  bundle:
```

pidファイルが残ってしまいエラー発生していた為、事前に **rm -f tmp/pids/unicorn.pid** でpidファイル削除。Pumaだとserver.pidファイルだけど、今回はUnicornなのでunicorn.pidを削除します。

ここでコンテナはrailsアプリ用、Mysql用、nginx用の３つを作成します。自分はrailsアプリ用のコンテナが直ぐに落ちてしまい数日ハマりました。

# さいごに
今回最も苦戦したことはrailsアプリ用のコンテナをどうしても立ち上げることができなかったことです。
またdockerの仕組みの理解がまだまだ曖昧なので、一つひとつ調べながら、それでも何度もdockerコンテナを削除しては作り直しました。その際余計なコンテナやイメージが残ってしまい時間を多く費やしてしまいました。その時にお世話になった記事も記載しておきます。ここまで読んで頂きありがとうございました。


### 今回主に参考にさせていただいた記事

https://qiita.com/E6YOteYPzmFGfOD/items/509dbabeb20bf2487283

https://teratail.com/questions/272021

### docker-compose build/upがエラーで上手くいかなかった時に参考にした主な記事

https://qiita.com/shisama/items/48e2eaf1dc356568b0d7

https://qiita.com/suin/items/19d65e191b96a0079417