Rails On DockerでのAWSデプロイができたので,中身を整理します。


近況報告

やっとですよ!!!!!
 railsをDockerコンテナに入れてデプロイができました。ざっと3週間以上かかった!
就職活動に関しては,wantedlyは相変わらずですが,Greenの反応がよくなった感じがします。コロナからの復調の兆しでしょうか。最近,超福利厚生が良い企業の面接の機会を頂いたのですが,企業研究の不足でダメでした。2日くらい凹みましたが,自分の面接対策に大きな成長をもたらす経験だったと考えて,そこより自身に合った企業をつかむべく頑張っていこうと思います。

今日のお題

 railsを搭載したdockerの備忘録をつくろう

デプロイまでに参考にした資料

☆デプロイの参考☆
ローカル環境構築の勉強に使用
この記事がコンテナ作成に特に参考になった
コンテナ作っては壊しまくってたので整理をたくさんしました

環境

ruby 2.5.1
rails 5.2.3
Mac OS
docker for mac

おおまかな作成フロー

・railsで作成したポートフォリオにdockerfileなど諸々を搭載 ←本項では中身をまとめる
・buildしてローカル環境で動くか確認
・Githubにアップ
・EC2にPull
デプロイに関しては上の☆以上の記事を書ける気がしないので割愛

ディレクトリ構成


$ tree 

├── app-...省略
├── Dockerfile
├── containers(このフォルダは別になくても問題なし) --- nginx
│                                     ├── Dockerfile
│                                     └── nginx.conf
├── Gemfile
├── Gemfile.lock
├── docker-compose.yml
・
省略

 テックキャンプ終わりたてとかのDocker初心者に向けて噛み砕いて説明をしますと,アプリケーションが動く環境を全て箱の中で整備してしまえば(画像の青枠),本番環境とローカル環境の構築をいちいちしなくて付け替えるだけで済んでよくね?って感じです。あとは,各々のパソコンごとに異なるバージョンとかOSとかを気にせず,Dockerで整備した環境をみんなで共有できることも利点です。

コンテナはRailsコンテナとNginxコンテナを立ち上げて,Nginxのlocalhostで受け,pumaと通信してrailsに繋がっています。

Dockerfile

 コンテナの設計図です。コンテナの中に何を組み込んで,どのファイルを読み込み,何の処理を行うかをここに記入します。1つのコンテナにつき1つdockerfileが存在します。今回私がデプロイしたものにはdockerfileが2つあるのでコンテナは2つと考えて良いです。Dockerfileをもとにコンテナは構築されます。

イメージとかビルドとか,テックキャンプでは耳にしない単語ばかり出てくるので最初は仕組みがさっぱりでした笑

 .Dockerfile(railsコンテナ)
FROM ruby:2.5.1 

# リポジトリを更新し依存モジュールをインストール
RUN apt-get update -qq && \
    apt-get install -y build-essential \
                       nodejs \
                       vim ←コンテナ内でvimを用いる際に必要
 ※rails最新版ではwebpackerが必要なので別途インストールする必要がある。
 youtubeで「俺はdockerに勝った」という動画があるので参考にしてください。

# ルート直下にwebappという名前で作業ディレクトリを作成(コンテナ内のアプリケーションディレクトリ)
RUN mkdir /webapp ←ぶっちゃけなくても動く。webappは任意の名前で
WORKDIR /webapp

# ホストのGemfileをコンテナにコピー
ADD Gemfile /webapp/Gemfile
ADD Gemfile.lock /webapp/Gemfile.lock 
※アプリケーションのGemfile.lockの中身を空にしないとエラーになる?

# bundle installの実行
RUN bundle install

# ホストのアプリケーションディレクトリ内をすべてコンテナにコピー
ADD . /webapp
 .Dockerfile(nginxコンテナ)
FROM nginx:1.15.8

# インクルード用のディレクトリ内を削除(多分かぶるの防いでる)
RUN rm -f /etc/nginx/conf.d/*

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

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

FROM
docker hubからイメージを取得します。docker hubの中にあるものであれば何でもpull可能。はじめのコンテナは買いたてのパソコンのように何もインストールされていないので,rubyを動かすために整備してあげます。バージョンを指定しなかった場合,最新がインストールされます。
RUN
実行コマンド。コンテナをビルドする際に行われる処理。ターミナルのコマンドのような感じで処理される。
COPY
ホストマシン(ここではMAC)のディレクトリ内にあるファイルをコピーする。
ADD
ディレクトリ内にあるファイルをコピーする。
・Copyとの違いはリモートのファイルをコピーできる(ADD)かできない(COPY)か,圧縮が解答される(COPY)かされない(ADD)か。
CMD
コンテナ構築あと,実行した際に行われる処理。ターミナルのコマンドのような感じで処理される。
・RUNとの違いは実行のタイミング。build時に更新が行われるか,コンテナが起動した時にコマンドが実行されるか。

.confファイル

nginx自体の設定ファイルを指す。これがないとnginxは起動しない。中身さえよければnginz.confでもdefault.confでも実行に支障はないが,みんなが見て理解できる名称がベター

nginx.conf
# プロキシ先の指定
# Nginxが受け取ったリクエストをバックエンドのpumaに送信
upstream webapp {
  # ソケット通信したいのでpuma.sockを指定
  server unix:///webapp/tmp/sockets/puma.sock;
}

server {
  listen 80;
  # ドメインもしくはIPを指定
  server_name localhost;

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

  # rootの指定。これないとアプリケーション接続した際にパスが迷子になる。
  root /webapp/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 @webapp;
  keepalive_timeout 5;

  # リバースプロキシ関連の設定
  location @webapp {
    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://webapp;
  }
}

server{}にリクエストの受信方法を設定して,全部通ったらupstreamへ進み,ソケット通信が行われる。コンテナ起動するに当たって理解する場所ではないので暗記しないでコピペでおk。ただしディレクトリの名称はしっかり確認。プロキシ関連は勉強中です。

docker-compose.yml

複数コンテナの実行を管理するファイル。コンテナ1つの時はdocker container run .....みたいにして実行していましたが,docker-compose up でファイルにかかれている全てのコンテナを起動できます。

version: '3'
services:
  app:
    build:
      context: .
    env_file:
      - ./environments/db.env
    command: bundle exec puma -C config/puma.rb
    volumes:
      - .:/webapp
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
      - log-data:/webapp/log
    depends_on:
      - db
  db:
    image: mysql:5.7
    env_file:
      - ./environments/db.env
    volumes:
      - db-data:/var/lib/mysql
  web:
    build:
      context: containers/nginx
    volumes:
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
    ports:
      - 80:80
    depends_on:
      - app
volumes:
  public-data:
  tmp-data:
  log-data:
  db-data:

起動した際に参照するファイル,コマンドなどを記述していきます。
depends_on
コンテナの依存関係を示しています。上の場合は,DB⇨app⇨web(nginx)の順で起動します。
volume
コンテナ外部のデータ保存スペース。volumeは実際なくてもコンテナは動く。コンテナを破棄するとその時コンテナ内の情報が消失してしまい,サイド同じ情報を入れようにも時間がかかる。コンテナは何回も潰すけど残しておきたいデータがある場合に用いる。イメージは外付けハードディスク
マウント
dockerの外にあるデータをdocker内で使えるようにすること。
env_file
環境変数にしているパスワードの読み込み口。ファイルに直書きしてもよし,上のようにファイルに書いてもよし。後者の方がセキュリティ的にベター(多分)

db.env(例)
MYSQL_ROOT_PASSWORD=aaaaa
MYSQL_USER=aaaaa
MYSQL_PASSWORD=aaaaa

コマンド

#ビルドする
docker-compose build
#起動する
docker-compose up
#裏で起動する
docker-compose up -d
#コンテナ止める
docker-compose stop
#コンテナ止めて破棄
docker-compose down
#コンテナ止めて破棄&volume削除
docker-compose down --volume
#コンテナの中確認
docker ps
docker images
#image削除
docker image prune
#動いていないもの全て削除
docker system prune

#docker内でrailsのアクション
docker-compose run [アプリケーション名] rails db:createとか
⇨docker-compose内のアプリケーションへの私事です。db:createして

終わりに

いやー実装まで本当に時間がかかりました。特にNginxはプロキシとかサーバーとたさっぱりなので苦労しました。