AWSのS3で画像を配信

内容

画像の保存にAWSのS3を使用する

基礎知識

  • S3
    →安価で耐久性の高いAWSクラウドストレージサービス
    →静的コンテンツの配信やバッチ連携用のファイル置き場、ログ等の出力先、静的ウェブホスティング等で利用する
  • git-secrets
    AWSが公開しているツールで、commitしようとしたコードをチェックし、パスワードだと推定されるような文字列が含まれている場合は、警告を出して処理が中断してくれる機能
  • CloudFront
    →高速にコンテンツを配信するサービス(CDNのサービス)
    →CloudFrontがあることで、高速化され、S3の負荷が軽減される
    →高速(ユーザーから最も近いエッジサーバーから画像を配信する)で、効率的(エッジサーバーでコンテンツのキャッシングを行うので、オリジンサーバーに負荷をかけずに配信できる)
  • CDN(Content Delivery Network)
    →オリジンサーバー(元となる画像を配信するサーバー)上にあるコンテンツを、世界中100箇所以上にあるエッジロケーションにコピーし、そこから配信を行う

S3に画像を保存

手順1 バケットの作成

手順2 セキュリティ対策

  • IAMのユーザー→作成済みのIAMユーザーをクリック
  • 概要のユーザーARNをコピー
  • バケットポリシーを設定するため、S3のバケットを開き、作成したバケットをクリック
  • アクセス許可→バケットポリシーの編集→以下のように編集

バケットポリシー

{
   "Version": "2012-10-17",
   "Id": "Policy1544152951996",
   "Statement": [
       {
           "Sid": "Stmt1544152948221",
           "Effect": "Allow",
           "Principal": {
               "AWS": "①"
           },
           "Action": "s3:*",
           "Resource": "arn:aws:s3:::②"
       }
   ]
}
  • 「①」の箇所に、先ほどコピーした「ユーザーのARN」を入力
  • 「②」の箇所に、作成したバケット名を入力

手順3 本番環境からS3に画像を保存

  • S3を使用するために必要なGemfileを導入(Gemfileの一番下に以下を追記)

Gemfile

gem "aws-sdk-s3", require: false

ターミナル

% bundle install
  • 画像の保存先を指定するために、production.rbを編集
    ※現状では画像の保存先が「local」に設定されており、アプリケーション内に画像を保存することを意味しているので、S3に保存されるように設定を変更

config/environments/production.rb.rb

# 元々
config.active_storage.service = :local

# 以下のように変更
config.active_storage.service = :amazon
  • storage.ymlに以下のコードを追記

config/storage.yml

test:
# 省略

lacal:
# 省略

amazon:
  service: S3
  region: リージョン名(例:ap-northeast-1
  bucket: バケット名
  access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
  secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
  • .envファイルで環境変数を設定
  • これでAWSでgit pullし、本番環境で画像投稿すれば、S3のバケットに画像が保存されるようになっている

参考(誤操作で秘密情報をpushしないよう対策)

git-secretsを設定して、セキュリティー対策

  • ターミナルから、Homebrewを経由してgit-secretsを導入

ターミナル

% cd ~/ #ホームディレクトリに移動
% brew install git-secrets
  • git-secretsが導入できたら、設定を適用したいアプリケーションのディレクトリに移動して、git-secretsを有効化

ターミナル

% cd アプリケーション名 #開発中のアプリに移動
% git secrets --install
  • 続いて、どのようなコードのcommitを防ぐのかを設定→「Access key ID」「Secret access key」等、アップロードしたくないAWS関連の秘密情報を一括で設定

ターミナル

% git secrets --register-aws --global
  • 実際にどのような設定がされているか確認

ターミナル

% git secrets --list
  • これで、「git secrets --install」を行なったリポジトリでは「git commit」コマンドを実行した際にAWSの秘密情報を含んでいないかチェックされるようになった

GitHub Desktopを利用している場合

  • GitHub Desktopがアプリケーションのディレクトリに存在しているか確認
  • 以下のコマンドを実行してGitHub Desktopにgit-secretsを適用

ターミナル

% sudo cp /usr/local/bin/git-secrets /Applications/GitHub\ Desktop.app/Contents/Resources/app/git/bin/git-secrets

# 上記コマンドで「No such file or directory」のエラーがでる場合はGitHub Desktopのバージョンが古い可能性があるので、以下のコマンドを実行
% sudo cp /usr/local/bin/git-secrets /Applications/GitHub\ Desktop.app/Contents/Resources/git/bin/git-secrets

※パスワードは自身のPCにログインする際のパスワード
- 今後作成する全てのリポジトリに、git-secretsが適用されるようにする

ターミナル

% git secrets --install ~/.git-templates/git-secrets
% git config --global init.templatedir '~/.git-templates/git-secrets'

CloudFrontを利用して画像を高速配信

手順1 CloudFrontを設定

  • CloudFrontのディストリビューションの作成をクリック
  • オリジン→オリジンドメインはS3で作成したキャッシュさせたいバケット、オリジンパスは今回は空(指定したディレクトリのみキャッシュさせたい場合は指定)、名前はそのまま、S3バケットアクセスは今回はNoのまま(Cloud Frontのみにアクセスさせたい場合は「Yes」を選択)、オリジンシールドはNoのまま
  • Default Cache Behavior→特にいじる必要なし
  • 設定→すべてのエッジロケーションを使用するが設定されていればOK→ディストリビューションを作成

手順2 CloudFrontではなく独自ドメインで画像が配信されるように設定

  • 作成したCloudFrontのIDをクリック→一般の設定の編集をクリック
  • 代替ドメイン名(CNAME)→項目を追加→今回は「static.ドメイン名」というサブドメイン名で配信
  • SSL証明書を取得するために「証明書をリクエスト」をクリック
  • ドメイン名の追加→「*.myapp.com」「myapp.com」等のように、サブドメイン全てを許容するものとドメイン本体を追加しておく→「次へ」
  • DNSの検証→「次へ」
  • タグの追加は特に必要ないので「確認」
  • 確認して「確定とリクエスト」
  • ドメイン名横の三角ボタンを押し、「Route 53でのレコードの作成」→「作成」をクリックして「続行」
  • SSL証明書」が発行されるまで待ち、発行されたら準備完了
  • CloudFrontの画面に戻る→カスタムSSL証明書→*付きのドメイン名を選択→他はそのままで「変更を保存」

手順3 Route 53で独自ドメインとCloudFrontの紐付け

  • Route 53のホストゾーンでドメイン名を選択→レコードを作成→レコード名「static」を追加、レコードタイプは「CNAME」、値はCloudFrontのドメイン名を記述→ルーティングポリシーはシンプルで→「レコードを作成」

手順4 Rials側で設定

AWS通信

内容

今回は通信について基礎知識のみまとめる

基礎知識

  • プロトコル
    →コンピュータ同士がネットワークを利用して通信するために決められた約束事
    →メーカーやOSが違うコンピュータ同士が通信するためには同じ仕様でやり取りする必要がある、同じプロトコルを使用するという同意があるからこそ様々なコンピュータ同士が通信できている
  • TCP/IP
    TCP・IPを中心として、インターネットを構築する上で必要なプロトコル群の総称(インターネットを運用するために開発された)
    →上から、アプリケーション同士が会話するアプリケーション層(HTTP、DNSSSHSMTP)、データの転送を制御するトランスポート層TCPUDP)、IPアドレスを管理し経路選択するネットワーク層(IP、ICMP、ARP)、直接接続された機器同士で通信するネットワークインターフェース層(Ethernet、PPP)、の4階層のモデルがある
  • HTTP(Hyper Text Transfer Protocol)
    →インターネットでHTML等のコンテンツの送受信に用いられる通信の約束事
    →クライアントがHTTPリクエストを送り、それに対してサーバーがHTTPレスポンスを返す、そのリクエスト・レスポンスの書き方がHTTPの正体
  • TCP(Transmission Control Protocol)
    →信頼性のある通信を提供、信頼性のある通信を実現する必要がある場合に使用する
    →信頼性を保つために、送信するパケットの順序制御や再送制御を行う
  • UDP(User Datagram Protocol)
    →信頼性のない通信 、高速性やリアルタイム性を重視する通信で使用する
    →送信するだけで、パケットが届いたかは保証しない
    →動画や電話等、即時性が必要な通信や総パケット数が少ない通信(DNS等)に向いている
  • IP
    →IPの役割は、IPアドレス、終点コンピュータまでのパケット配送(ルーティング)、パケットの分割・再構築処理の3つ
    IPアドレス:ネットワーク上で通信を行う宛先を識別するのに使われる
    →ルーティング:宛先IPアドレスのコンピュータまでパケットを届ける
    →パケットの分割・再構築処理:各ネットワークインターフェースの最大転送単位より小さくなるようにパケットを分割して送信し、終点コンピュータで再構築する

AWSで無料のSSL証明書を取得

内容

AWSで無料のSSL証明書を取得する

SSL証明書の取得

  • AWSサービスの中から、Certificate Managerを選択
  • 証明書のプロビジョニングの「今すぐ始める」をクリック
  • パブリック証明書のリクエストを選んで「証明書のリクエスト」をクリック
  • webサイトで利用予定のドメインを指定して「次へ」をクリック
  • DNSの検証で「次へ」をクリック
  • タグの作成は任意なので、不要であれば空欄のまま「確認」をクリック
  • 内容に誤りがなければ「確定とリクエスト」をクリック
  • ドメイン横の三角ボタンをクリックしプルダウンを表示
  • ネームサーバーをRoute 53としている場合は、「Route 53でのレコードの作成」をクリックし、「作成」をクリック→成功と表示されればOK
  • 「続行」で完了

SSL証明書をELBに設定

いくつか方法があるが今回はELBに設定する、その中でもALBを使用する
- AWSサービスの中から、EC2を選択
- 「ロードバランサー」→「ロードバランサーの作成」を選択
- Application Load Balancerを選択
- 任意の名前を入力 - VPCを選択し、アベイラビリティーゾーンでは2つ以上(1つはWebサーバーのあるゾーン)、サブネットを選択
- 新しいセキュリティグループを作成→セキュリティグループ名を設定し、ポートは80と443でどこからでもアクセス出来るように、0.0.0.0/0, ::/0 を許可する→作成したセキュリティグループを選択
- リスナーの追加でHTTPとHTTPSを選べるので、HTTPSを選択
- Create target group→インスタンスを選択、ターゲットグループに任意の名前を設定、バージョンはHTTP1で「Next」
プロトコルはHTTP、ポートは80のデフォルト設定でOK→EC2インスタンスとELBはHTTP通信のため
- 「インスタンス」の項目で追加するwebサーバにチェックを入れ、「Include as pending below」をクリックすると、ターゲットにwebサーバのインスタンスが追加される→作成→作成したターゲットを選択
- Default SSL certificateで先程作成した証明書を選択
- 最後に設定が問題ないことを確認し「作成」
- ターゲットグループの一覧からターゲットを確認すると、LB作成前はunusedになっていたが、LB作成後はhealthyになり、アクセスできる状態になった

ロードバランサードメインを紐付ける

EC2インスタンスのポートを設定

  • EC2にはALBからのアクセスのみ許可する場合は、セキュリティグループのインバウンドルールを編集する

以上で「https://〜」のドメインでアクセスすると鍵がかかった状態になっている
証明書情報を見ると、Amazon認証局になり有効期限が来年になっていることが確認できる

AWSにDockerの環境を構築

内容

AWSのEC2にDockerの環境を構築する

手順1 EC2インスタンスssh接続

まずキーを他人に不正利用されないように、自分で読み込みだけ可能な設定にしてから接続
自分には読み込みだけじゃなく全権限を与えたい場合は下記の数字を700にする(キーはむしろ変更したくないので読み込みだけ許可の400がいい)
ターミナル

% chmod 400 キーの名前.pem
% ssh -i キーの名前.pem ec2-user@パブリックIPアドレス

手順2 AWS(EC2)にDockerをインストール

AWSにDockerをインストールするにはOSはLinux環境が必要となる
下記コマンドを実行
ターミナル

# Dockerをインストール
[ec2-user@ip-〜]$ sudo yum install -y docker

# Dockerを起動
[ec2-user@ip-〜]$ sudo service docker start

# ec2-userをdockerグループに入れる事でdockerコマンドが利用可能になる
[ec2-user@ip-〜]$ sudo usermod -a -G docker ec2-user

# 一度exitでログアウトし再度sshでログイン
# dockerコマンドが利用できるか、docker起動確認の為にinfoコマンドを使い確認
[ec2-user@ip-〜]$ docker info

# EC2起動時にDockerを自動で立ち上げる場合は下記コマンド
[ec2-user@ip-〜]$ sudo chkconfig docker on

手順3 Docker-composeのインストール

AWSにもDocker-composeをインストール
ターミナル

# docker-composeをインストール
[ec2-user@ip-〜]$ sudo curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose

# 下記コマンドで実行できるようにアクセス権を変更
[ec2-user@ip-〜]$ sudo chmod +x /usr/local/bin/docker-compose

# 下記コマンドで実行できるかを確認
[ec2-user@ip-〜]$ docker-compose --version

手順4 AWSGitHubを連携

以下コマンドを実行
ターミナル

# gitをインストール
[ec2-user@ip-〜]$ sudo yum install -y git

# キーを作成(質問は全てエンター)
# -t 暗号化方式を指定
# -b 暗号化強度を指定
[ec2-user@ip-〜]$ ssh-keygen -t rsa -b 4096

# 作成した公開鍵を表示し、コピー
[ec2-user@ip-〜]$ cat ~/.ssh/id_rsa.pub

こちらのサイトにアクセス
右上の「new SSH key」をクリック
titleを入力し、keyのところに先ほどコピーした文字列を貼り付ける(ssh-rsa~から)
以下コマンドを実行し、途中yesと入力したら、AWSgithubの認証が成立
ターミナル

[ec2-user@ip-〜]$ ssh -T git@github.com

以後、AWSインスタンス側から自身のgithubに対し、pull等ができるようになる

手順5 コンテナの構築

構築方法は以下の2通り
- Dockerfileを作成し環境を構築
- docker pullでRails等のイメージを取得してコンテナを作成する方法
今回はDockerfileを作成しつつ環境を構築する方法で進める
※ここから自分のファイル名をmyappに置き換える

1. Dockerfileの修正

ここはローカル用とAWS用で同じコードを使う
前回作成したファイルの記述から変更
Dockerfile

FROM ruby:2.6.5
# これがないとAWSでyarnが使用できない
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
RUN apt-get update -qq && apt-get install -y \
    build-essential \
    libpq-dev \
    nodejs \
    yarn \
    vim
WORKDIR /myapp
ADD Gemfile /run-share/Gemfile
ADD Gemfile.lock /run-share/Gemfile.lock
RUN gem install bundler
RUN bundle install
ADD . /myapp
RUN mkdir -p tmp/sockets
RUN mkdir -p tmp/pids

NginxのDockerfileは変更なし
nginx_docker/Dockerfile

FROM nginx:1.21.3
RUN rm -f /etc/nginx/conf.d/*
ADD nginx.conf /etc/nginx/conf.d/run-share.conf
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf

2. database.ymlの修正

下記を追加して、本番環境は環境変数から値を読み込むようにする
database.yml

production:
  <<: *default
  database: <%= ENV['DB_DATABASE'] %>
  adapter: mysql2
  encoding: utf8
  charset: utf8
  collation: utf8_general_ci
  host: <%= ENV['DB_HOST'] %>
  username: <%= ENV['DB_USERNAME'] %>
  password: <%= ENV['DB_PASSWORD'] %>

DB_HOSTはRDSのエンドポイント
AWSのRDSダッシュボードのデーターベースをクリックし、一覧から該当のRDSを選択すると確認できる

3. nginx設定ファイルの修正

以下の部分を本番環境用に変更
nginx.conf

server {
  listen 80;
# =========ローカルと本番切り替え===========
  server_name 固定IP;
  # server_name localhost;
# ======================================

4. .envファイルに環境変数を設定

2.の環境変数を設定する

5. docker-compose.ymlの修正

dbコンテナは本番環境ではRDS使用で不要のためコメントアウト(appコンテナのdepends_onもまとめて)
commandのbundle exec pumaに-e production を追記して、本番環境でサーバーを立ち上げる

手順6 githubからclone

再度sshログインして、githubからclone(URLはリポジトリを表示したときのURL)
ターミナル

# ルート直下に移動(homeディレクトリでコンテナを展開するとpumaとnginxの連携でエラーが出る場合がある)
[ec2-user@ip-〜]$ cd /
[ec2-user@ip-〜]$ sudo git clone https://github.com/〇〇〇〇〇〇/〇〇〇〇〇〇
# ディレクトリがあるか確認
[ec2-user@ip-〜]$ ls

手順7 .gitignoreに記載したファイルを転送

現在.gitignoreに記載したファイル(.env master.key)は含まれていないので、ssh通信を用いてローカルからAWSに直接ファイルを転送
ターミナル

# 一旦ログアウトして、ローカルのmyappファイルに移動
[ec2-user@ip-〜]$ exit

# scpコマンドを用いて、AWSに.envを転送
# 権限の関係で一旦、ホームディレクトリに転送
% sudo scp -i ~/.ssh/myapp_key.pem .env ec2-user@固定IP:/home/ec2-user/
% sudo scp -i ~/.ssh/myapp_key.pem config/master.key ec2-user@固定IP:/home/ec2-user

再度sshログインし、ファイルが転送できているか確認
ターミナル

[ec2-user@ip-〜]$ cd
[ec2-user@ip-〜]$ ls -a

ホームディレクトリに移動して.envとmaster.keyが存在することを確認し、.envとmaster.keyを移動させる
ターミナル

[ec2-user@ip-〜]$ sudo mv .env /myapp
[ec2-user@ip-〜]$ sudo mv master.key /myapp/config
[ec2-user@ip-〜]$ ls -a  /myapp/
[ec2-user@ip-〜]$ ls -a  /myapp/config

手順8 コンテナを起動

まずはイメージをビルド
※この時Dockerのサービスが停止していてdockerコマンドが使えなくなっている可能性があるので、その際はsudo service docker startで再度Dockerを起動させる
ターミナル

[ec2-user@ip-〜]$ cd /myapp
[ec2-user@ip-〜]$ docker-compose build

サーバー起動前の準備(プリコンパイル
ターミナル

[ec2-user@ip-〜]$ docker-compose run app rails assets:precompile RAILS_ENV=production

コンテナを起動
※ここでbundler: failed to load command: pumaというエラーが表示され、appサーバーが起動されない場合がある
これが正解かわからないが、mkdir tmp/socketsmkdir tmp/pidsで自分でディレクトリを作成すると解決できた
ターミナル

[ec2-user@ip-〜]$ docker-compose up -d

データベースの作成、マイグレーションファイルの読み込み
ターミナル

[ec2-user@ip-〜]$ docker-compose exec app rails db:create db:migrate RAILS_ENV=production

RDSのデーターベース確認方法

以下のコマンドで実行
ターミナル

mysql -u RDSのユーザー名 -h RDSのエンドポイント -p
//パスワードを入力してログイン

Dockerを用いたローカル環境にWebサーバー(nginx)を設置

内容

前回既存のRailsアプリにDockerを導入したが、Webサーバーも設置することにしたのでまとめる

現状

ここにwebコンテナ(nginx)を設置する(ちなみにappサーバーはpumaを設置)

手順1 必要なファイルを作成

既存アプリケーションのルートディレクトリにnginx_dockerフォルダを作成し、その配下にDockerfileとnginx.confファイルを作成

手順2 Dockerfileの編集

app用とnginx用のDockerfileを編集(app用は前回のそのまま)
※ここからアプリ名をmyappとして記述している
Dockerfile

FROM ruby:2.6.5
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
RUN apt-get update -qq && apt-get install -y \
    build-essential \
    libpq-dev \
    nodejs \
    yarn
WORKDIR /myapp
COPY Gemfile ./Gemfile
COPY Gemfile.lock ./Gemfile.lock
RUN gem install bundler
RUN bundle install
COPY . /myapp
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]

nginx_docker/Dockerfile

FROM nginx:1.21.3

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

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

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

手順3 nginxの設定ファイル

nginx.conf

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

server {
  listen 80;
  # ドメインもしくはIPを指定
  server_name localhost [or example.com or 192.168.xx.xx];

  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;
  }
}

手順4 docker-compose.ymlの編集

前回railsコンテナをwebと定義していたがそれをappに変更し、nginxコンテナをwebと定義する
docker-compose.yml

version: '3'
services:
  # Nginxコンテナ定義
  web:
    build:
      context: ./nginx_docker
    volumes:
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
    ports:
      - 80:80
    depends_on:
      - app
  # Railsコンテナ定義
  app:
    build: .
    command: bundle exec puma -C config/puma.rb
    environment:
      - MYSQL_HOST=${MYSQL_HOST}
      - MYSQL_USERNAME=${MYSQL_USERNAME}
      - MYSQL_PASSWORD=${MYSQL_ROOT_PASSWORD}
    volumes:
      - .:/myapp
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
      - log-data:/myapp/log
    depends_on:
      - db
    tty: true
    stdin_open: true
  # MySQLコンテナ定義
  db:
    image: mysql:5.6.51
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
    ports:
      - "4306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
volumes:
  public-data:
  tmp-data:
  log-data:
  mysql-data:

手順5 puma.rbの編集

puma.rbの最下部に以下の記述を追加
bind "unix://#{app_root}/tmp/sockets/puma.sock"の部分は、nginx.confのserverと一致するようにする
config/puma.rb

app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"

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

手順6 database.ymlの編集

Railsはデフォルトで、localhost上でDBが動作するようになっているので、hostを先ほど作成したdbに変更
また、ここにおける「MYSQL_USER」と「MYSQL_PASSWORD」はDBファイルで定義した環境変数名を設定
database.yml

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch('MYSQL_USER') { 'root' } %>
  password: <%= ENV.fetch('MYSQL_PASSWORD') { 'password' } %>
  host: db

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

手順7 イメージを作成

docker-compose.ymlで、web・app・dbのbuildに指定されているDockerfileを元にイメージを作成
ターミナル

% docker-compose build

手順8 コンテナを起動

以下のコマンドでコンテナを立ち上げ、コンテナが起動しているのかを確認
ターミナル

% docker-compose up -d
% docker-compose ps

手順9 データベースの作成、マイグレーションファイルの読み込み

最後に以下のコマンドでデータベースの作成とマイグレーションファイルの読み込みをする
ターミナル

% docker-compose exec app rails db:create db:migrate

コンテナの停止・削除をしたい時

以下のコマンドを実行
ターミナル

# 停止したい時
% docker-compose stop

# 停止・削除したい時
% docker-compose down

# volumeも一緒に削除したい時(MySQLのデータもすべて消えるので注意!)
% docker-compose down -v

docker-composeによる各種ファイルの変更時

内容

docker-composeによるコンテナ構成後、各種ファイルを変更した時の反映方法についてまとめる

docker-compose.ymlの変更時

  • イメージ:再構築の必要なし
  • コンテナ:再構築の必要あり
    →docker-compose up -d

Dockerfileの変更時

  • イメージ:再構築の必要あり
    →docker-compose build
  • コンテナ:再構築の必要あり
    →docker-compose up -d

ソースコード等の変更時

  • イメージ:再構築の必要あり
    →docker-compose build
  • コンテナ:再構築の必要あり
    →docker-compose up -d

既存のRailsアプリにDockerを導入

内容

すでにRailsアプリを作成している段階でDockerも導入したいと考えたので、アプリ作成後からDockerを導入する方法についてまとめる

バージョン

基礎知識

  • Dockerfileとは
    →Docker imageの設計図で、DockerfileからDocker imageを作る
    →INSTRUCTION argumentsの形で書いていく
  • FROM(instruction)
    →ベースとなるイメージを決定
    →DockerfileはFROMから書き始める
  • RUN(instruction)
    Linuxコマンドを実行
    →RUNを使うことで好きなようにカスタマイズ
    →RUN毎にImage Layerが作られる(RUNをずらっと書いていくとLayerの数が膨大になりimageが大きくなってしまう)
  • imageのLayer数を最小限にするには?
    →Layerを作るのはRUN、COPY、ADDの3つ
    →コマンドを&&で繋げる
    →バックスラッシュ(\)で改行する
  • $apt-get
    →$apt-get update:新しいパッケージリストを取得、$apt-get install をインストール
  • CMD(instruction)
    →コンテナのデフォルトのコマンドを記述
    →CMD["executable", "params1", "params2"]
    →原則Dockerfileの最後に記述
    →RUNはLayerを作る、CMDは作らない
  • COPYとADD(instruction)
    →単純にファイルやフォルダをコピーする場合はCOPYを使う
    →tarの圧縮ファイルをコピーして解凍したい時はADDを使う(大きいファイルの時)
  • CMDとENTRYPOINT(instruction)
    →ENTRYPOINTでもデフォルトのコマンドを指定することができるが、run時に上書きできない
    →ENTRYPOINTがある場合は、CMDは["params1", "params2"]の形を取る
    →コンテナをコマンドのように使いたい時に使う
  • ENV(instruction)
    環境変数を設定する
  • WORKDIR(instruction)
    →Docker instructionの実行ディレクトリを変更する
  • docker compose
    →複数のコンテナを簡単にrunできるDockerのツール
    →docker runコマンドが長くなる時や、複数のコンテナをまとめて起動する時に使う

手順1 DockerHubの登録とDockerのインストール

  • DockerHubのHPでサインアップ
  • 無料の枠を選択
  • メールが送られてくるのでログイン
  • 「Download the desktop application」でDocker Desktopをダウンロード
  • Docker.dmgがダウンロードできたらダブルクリックし、インストール→アプリケーションフォルダに移動

手順2 Dockerfileを作成

既存アプリケーションのルートディレクトリにDockerfileを作成し、下記のように記述
※今回はアプリのディレクトリ名をsample_appとする
Dockerfile

FROM ruby:2.6.5
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
RUN apt-get update -qq && apt-get install -y \
    build-essential \
    libpq-dev \
    nodejs \
    yarn
WORKDIR /sample_app
COPY Gemfile ./Gemfile
COPY Gemfile.lock ./Gemfile.lock
RUN gem install bundler
RUN bundle install
COPY . /sample_app
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]

手順3 docker-compose.ymlを作成

手順2同様にルートディレクトリにdocker-compose.ymlを作成し、下記のように記述
docker-compose.yml

version: '3' #docker-composeのバージョン
services:
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/sample_app
    ports:
      - 3000:3000
    depends_on:
      - db
    tty: true
    stdin_open: true
  db:
    image: mysql:5.6.51 #既存アプリとあわせる。ターミナルに[$ mysql --version]で確認
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} #あなたのパスワード
      MYSQL_DATABASE: root
    ports:
      - "4306:3306"
    volumes:
      - ./mysql-confd:/etc/mysql/conf.d
volumes:
  mysql-data:

手順4 config/database.ymlの編集

configディレクトリにあるdatabase.ymlを下記のように編集
config/database.yml

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: <%= ENV.fetch("DATABASE_PASSWORD") %> #あなたのパスワード
  socket: /tmp/mysql.sock
  host: db

development:
  <<: *default
  database: sample_app_development

test:
  <<: *default
  database: sample_app_test

手順5 .envファイルに環境変数を設定し、.gitignoreに.envを追加

手順6 entrypoint.shを作成

手順2、3同様にルートディレクトリにentrypoint.shを作成し、下記のように記述
entrypoint.sh

#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /sample_app/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

手順7 Dockerイメージの作成

ターミナルで下記のコマンドを実行し、Dockerイメージを作成
ターミナル

sample_app% docker-compose build

手順8 Dockerコンテナを起動

ターミナルで下記のコマンドを実行し、Dockerコンテナを起動 ターミナル

sample_app% docker-compose up -d

手順9 コンテナ内にデータベースを作成する

ターミナルで下記のコマンドを実行し、コンテナ内にデータベースを作成 ターミナル

sample_app% docker-compose run web rails db:create

手順10 作成したデータベースのマイグレーションを実行

ターミナルで下記のコマンドを実行し、データベースのマイグレーションを実行 ターミナル

sample_app% docker-compose run web rails db:migrate

手順11 Gemのインストール(これいらないかも)

ターミナルで下記のコマンドを実行し、Gemをインストール ターミナル

sample_app% docker-compose exec web bundle install

以上で導入完了
localhost:3000に接続するとアプリケーションが表示される(rails sはいらない)

エラー1

手順9のコマンド実行時、以下のエラーが出現
ターミナル

Creating sample_app_web_run ... done
warning Integrity check: System parameters don't match                                                    
error Integrity check failed                                                                              
error Found 1 errors.                                                                                     


========================================
  Your Yarn packages are out of date!
  Please run `yarn install --check-files` to update.
========================================


To disable this check, please change `check_yarn_integrity`
to `false` in your webpacker config file (config/webpacker.yml).


yarn check v1.22.5
info Visit https://yarnpkg.com/en/docs/cli/check for documentation about this command.


ERROR: 1

yarnのパッケージの中に古いものが存在していることが原因のようなので、以下のコマンドを実行してyarnをアップグレードしてみた
ターミナル

% yarn upgrade

しかし同じようなエラーが出たので、再度エラーを確認
ターミナル

To disable this check, please change `check_yarn_integrity`
to `false` in your webpacker config file (config/webpacker.yml).

上記のようなエラーが出ていたため、「config/webpacker.yml」ファイルのcheck_yarn_integrity箇所をfalseに変更すれば良いと解釈して、実際に変更してみる
config/webpacker.yml

# Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
check_yarn_integrity: false

上記で解決した