[Rails,AWS,Docker]EC2でデプロイする

備忘録兼ねての記事です。
OS: amazon-linux2

# 前提
EC2インスタンスを作成していて、ターミナル上でssh接続している
（インスタンスやRDSデータベースの作成はこの記事では割愛しますが、たくさんの記事で書かれているのでそこまで難しくないと思います）



# ローカル環境のRailsアプリをDockerコンテナ化

webサーバーとしてnginxを配置します。
appサーバーはpumaを設置します。
```
docker-compose.yml

Dockerfile

nginx_docker
  ├── Dockerfile
  └── nginx.conf

config
  └── puma.rb
```

```movie-review/.Dockerfile
FROM ruby:3.1.0

RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
    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 yarn
RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash -
RUN apt-get update -qq && apt-get install -y curl build-essential libpq-dev nodejs imagemagick chromium-driver 
ENV APP_PATH /myapp

RUN mkdir $APP_PATH
WORKDIR $APP_PATH
COPY Gemfile $APP_PATH/Gemfile
COPY Gemfile.lock $APP_PATH/Gemfile.lock
RUN bundle install

COPY . $APP_PATH
RUN yarn install

EXPOSE 3000

USER root

VOLUME /myapp/public
VOLUME /myapp/tmp

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
CMD /bin/bash -c "rm -f tmp/pids/server.pid && bundle exec rails s"
```

```movie-review/nginx_docker/Dockerfile.
# nginx:1.22.1 イメージをdocker hubからインストールします。
FROM nginx:1.22.1

# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*

# Nginxの設定ファイルをコンテナにコピー
ADD nginx.conf /etc/nginx/conf.d/movie-review.conf

# ビルド完了後にNginxを起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
```

次はpumaの設定ファイルです。
```bind "unix://#{app_root}/tmp/sockets/puma.sock"```の部分は、```nginx.conf```の```server```と一致するようにしなければなりません。

```config/puma.rb
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count

worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"

environment ENV.fetch("RAILS_ENV") { "development" }

pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }

plugin :tmp_restart

app_root = File.expand_path("..", __dir__)
# nginx.confのserverと一致させる。
bind "unix://#{app_root}/tmp/sockets/puma.sock"

stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true
```

# DockerコンテナをAWSにアップロードします。
作成したAWSインスタンスにローカルdockerコンテナをアップロードしていきます。
ngixやpuma、RDSなど接続しなくてはいけないものが多々あります。

# EC2にdockerをインストールする
今回はamazon-linux2を使っています。amazon-linux2023だと下記のコマンドで動かないので、別のコマンドにする必要があります。（一応過去の自分の記事でも書いています）
下記はsudoが必要です。
```
$ amazon-linux-extras install docker -y
systemctl enable docker.service
systemctl start docker.service
curl -SL "https://github.com/docker/compose/releases/download/v2.6.0/docker-compose-$(uname -s|tr [A-Z] [a-z])-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
```

ついでにgitも入れます
```
$ sudo yum install git
```

他にもインストールするものがある場合は、```$ sudo yum install ○○```で入れられます。

## database.ymlの修正
usernameやpasswordをベタ打ちは良くないので、環境変数を使っています。
```config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV['DB_USER'] %>
  password: <%= ENV['MYSQL_PASSWORD'] %>
  host: <%= ENV['DB_HOST'] %>

development:
  <<: *default
  database: happylunch_development

test:
  <<: *default
  database: happylunch_test

production:
  <<: *default
  database: happylunch_production
  username: root
  password: <%= ENV['happylunch__DATABASE_PASSWORD'] %>
```

# nginx.confの修正
```nginx.conf
upstream myapp {
  server unix:///myapp/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name localhost;

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

  root /myapp/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 @myapp;
  keepalive_timeout 5;

  location @myapp {
    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://myapp;
  }
}
```

## docker-compose.ymlの修正

```docker-compose.yml
version: "3"
services:
  db:
    image: mysql:8.0.31
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: password
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - 3306:3306
    platform: linux/x86_64/v8
  rails:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/myapp
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp

    environment:
      TZ: Asia/Tokyo
      RAILS_ENV: development
      DB_HOST: db
      DB_USER: root
      MYSQL_PASSWORD: password
    ports:
      - "3000:3000"
    stdin_open: true
    tty:
      true
    depends_on:
      - db
  web:
    build:
      context: nginx_docker
    volumes:
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
    ports:
      - 80:80
    depends_on:
      - rails  

volumes:
  public-data:
  mysql-data:
  tmp-data:
```
# 環境変数を定義する

次に環境変数の値を**リモート**のdatabase.ymlに書き込みます。
```
$ cd /movie-review/config
$ vi database.yml
```
マスターユーザー名とマスターパスワードはRDSの```設定```、エンドポイントは```接続とセキュリティ```に書かれています（パスワードは伏せ字ですが、忘れてしまった場合はパスワードを変更できます）
```config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: ******（マスターユーザー名）
  password: ******（マスターパスワード）
  host: ********ap-northeast-1.rds.amazonaws.com（エンドポイント）
```

書き換えられたらesc→:wqで保存します。

次にDockerコマンドです。権限がない場合はsudoをつけます。(例：```sudo docker-compose build```）

```
$ cd /movie-review

$ docker-compose build
//コンテナを作成します。

$ docker-compose up
//コンテナを起動します。
```

下記のように表示されていれば問題なく起動しています。
```
movie-review-rails-1  | * Listening on unix:///myapp/tmp/sockets/puma.sock
movie-review-rails-1  | Use Ctrl-C to stop
```
別のターミナルを開きます。
```
$ cd /movie-review

$ docker-compose exec rails bash
//コンテナの中に入ります。

# bin/rails assets:precompile
//プリコンパイルを実施

# bin/rails db:create
//データベースを作成

# bin/rails db:migrate
//マイグレーションを実行

# bin/rails db:seed
//（必要であれば）
```


以上で、```http://固定IP（パブリックIP）```にアクセスした場合、正しく表示されるはずです。（EC2インスタンスのパブリック IPv4 アドレスをコピーしてブラウザの検索で貼り付ければできます）


<br>

参考：

https://zenn.dev/hiro1111/articles/d0ac7c4ebfcf13

https://zenn.dev/hiro1111/articles/07d01356149b5e

https://qiita.com/eighty8/items/0288ab9c127ddb683315

https://qiita.com/Yusuke_Hoirta/items/959f91a61ac1e607c0b1