AWS

AWS

EC2(Elastic Computing Cloud)を筆頭にAmazonが展開するwebサービス。

Amazon EC2(仮想サーバ)、Amazon S3(クラウドストレージ)、Amazon RDS(データベース)

デプロイまでの流れ

インスタンスの作成

elastic IPの取得・インスタンスとの関連付け

sshでサーバーに接続

~/.sshディレクトリで以下を実行。

$ ssh -i ダウンロードした鍵の名前.pem ec2-user@作成したEC2インスタンスと紐付けたElastic IP

最低限の環境を整える

sudo yum update

最低限必要なものをインストール
sudo yum install \
git make gcc-c++ patch \
libyaml-devel libffi-devel libicu-devel \
zlib-devel readline-devel libxml2-devel libxslt-devel \
ImageMagick ImageMagick-devel \

railsを始めるに当たって必要なパッケージをインストール
sudo yum install -y openssl-devel

nodejsをインストール
sudo curl -sL https://rpm.nodesource.com/setup_6.x | sudo bash -
sudo yum install nodejs

Node.js

サーバーサイドJavaScriptのパッケージ

rbenvとruby-buildをインストール

#rbenvのインストール
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv 
#パスを通す
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile 
#rbenvを呼び出すための記述
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
#.bash_profileの読み込み
source .bash_profile
#ruby-buildのインストール
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
#rehashを行う
rbenv rehash  

rubyのインストール

rbenv install 2.5.1
rbenv global 2.5.1
rbenv rehash  #rehashを行う
ruby -v # バージョンを確認

mysqlをインストール

sudo yum install mysql56-server mysql56-devel mysql56

mysqlを起動

service コマンドを利用。Amazon LinuxやCentOSに含まれ、インストールしたソフトウェアの起動を一括して行えるツール。

sudo service mysqld start

mysqld:daemonの略。linux用語でサーバーの意。

rootパスワードの設定

sudo /usr/libexec/mysql56/mysqladmin -u root password '設定したいパスワード'

Using a password on the command line interface can be insecure.

これが出る。passwordが丸見えだからセキュアじゃないよって意味。

アプリケーションサーバーの準備

Githubに接続

GithubにSSH鍵を登録

まずEC2サーバーでキーペアを生成

ssh-keygen -t rsa -b 4096

.ssh]$ ls
authorized_keys  id_rsa  id_rsa.pub

上記の2種類が作成される。この内publicの方の中身を確認。

cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2E***

githubへアクセスし、SSHキーを登録する。
※ssh-rsaからIPまでを含めた全体を登録する。

ssh-rsa AAAAB3NzaC1***== ec2-user@ip-111-11-11-111

Unicorn

アプリケーションサーバ
rails sコマンドの代わりにunicorn_railsコマンドで起動する。

Unicornをインストール

gemfileに以下を記述。

group :production do
  gem 'unicorn', '5.4.1'
end

config/unicorn.rbを新規作成

#サーバ上でのアプリケーションコードが設置されているディレクトリを変数に入れておく
app_path = File.expand_path('../../', __FILE__)

#アプリケーションサーバの性能を決定する
worker_processes 1

#アプリケーションの設置されているディレクトリを指定
working_directory app_path

#Unicornの起動に必要なファイルの設置場所を指定
pid "#{app_path}/tmp/pids/unicorn.pid"

#ポート番号を指定
listen 3000

#エラーのログを記録するファイルを指定
stderr_path "#{app_path}/log/unicorn.stderr.log"

#通常のログを記録するファイルを指定
stdout_path "#{app_path}/log/unicorn.stdout.log"

#Railsアプリケーションの応答を待つ上限時間を設定
timeout 60

#以下は応用的な設定なので説明は割愛

preload_app true
GC.respond_to?(:copy_on_write_friendly=) && GC.copy_on_write_friendly = true

check_client_connection false

run_once = true

before_fork do |server, worker|
  defined?(ActiveRecord::Base) &&
    ActiveRecord::Base.connection.disconnect!

  if run_once
    run_once = false # prevent from firing again
  end

  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exist?(old_pid) && server.pid != old_pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH => e
      logger.error e
    end
  end
end

after_fork do |_server, _worker|
  defined?(ActiveRecord::Base) && ActiveRecord::Base.establish_connection
end

worker(ワーカー)

リクエストを処理するunicornのプロセスのこと。適宜増える。
unicornはmaster1つと複数のworkerで構成される。

Unicornの設定

設定項目詳細
worker_processesリクエストを受け付けレスポンスを生成するworker(ワーカー)の数を決めます。
working_directoryUnicornがRailsのコードを動かす際、ルーティングなど実際に参照するファイルを探すディレクトリを指定します。
pidUnicornは、起動する際にプロセスidが書かれたファイルを生成します。その場所を指定します。
listenどのポート番号のリクエストを受け付けることにするかを決定します。今回は、3000番ポートを指定しています。

ディレクトリの作成

#mkdirコマンドで新たにディレクトリを作成
[ec2-user@]$ sudo mkdir /var/www/
#作成したwwwディレクトリの権限をec2-userに変更
[ec2-user@]$ sudo chown ec2-user /var/www/

作成したディクトリのownerはrootになるので、ec2-userへ変更しておく。

/var/www/に移動してgit cloneする。

[ec2-user@]$ cd /var/www/
[ec2-user@ www]$ git clone https://github.com/user名/レポジトリ名.git

$ git clone https://github.com/user名/レポジトリ名.git

rail環境の再構築

まずローカル環境のrubyとbundlerのバージョンを確認。

ruby -v
bundler -v

サーバーでもruby -vを確認。違っていればバージョンを入れ直す。
続いてbundlerをバージョンを指定してインストール

gem install bundler -v 2.0.2

i18nをrails 5.2.2以下で使っている場合に注意がでる。
以下のように変更すればOK

config/environments/production.rb
config.i18n.fallbacks = true
↓
config.i18n.fallbacks = [I18n.default_locale]
Release v1.1.0 · ruby-i18n/i18n
BREAKINGCHANGE:FallbacksFallbacksnowexcludedefaultlocale-#415,possiblyfixes#413+#338PleasecheckyourRailsappforthisline:config.i18n.fallbacks=trueThissettingisno...

環境変数

データベースのパスワードなどはセキュアに扱うためサーバー内の環境変数で扱います。
環境変数は /etc/environment というファイルに保存することで、サーバ全体に適用される。

secret_key_base

Cookieの暗号化に用いられる文字列。
Railsアプリケーションを動作させる際は必ず用意する。

config/secrets.yml と config/database.ymlで扱う。

以下のコマンドでsecret_keyを生成できる。

rake secret

環境変数を編集。

sudo vim /etc/environment
DATABASE_PASSWORD='MySQLのrootユーザーのパスワード'
SECRET_KEY_BASE='作成したsecret_key_base'

を書き込んで保存して終了。
一旦サーバーからexitしてログインしなおすと環境変数として適応される。

portの解放

AWSのセキュリティーグループからインバウンドの編集へ。
カスタムTCP, port 3000として解放。

railsを起動する。

projectの階層で
unicorn_rails -c config/unicorn.rb -E production -D

-c config/unicorn.rb は設定ファイルの指定、 
-E productionは環境を「本番モードとして動作させる」。
-Dは「Daemon(デーモン)」の略で、プログラムを起動させつつターミナルで別のコマンドを打てるようにするオプション。

データベースのアクセスで失敗するのでローカルプロジェクトを以下のように編集する。
終わったらpushしておく。

config/database.yml

production:
  <<: *default
  database: ~~~(個別に違う)
  username: root
  password: <%= ENV['DATABASE_PASSWORD'] %>
  socket: /var/lib/mysql/mysql.sock

gitから差分を更新する。(pullする)

git pull origin master

production用のDBを作成する。

rails db:create RAILS_ENV=production
rails db:migrate RAILS_ENV=production

ユニコーンを起動する。

unicorn_rails -c config/unicorn.rb -E production -D

アセットファイルをコンパイル

開発中はアクセス毎にアセットファイル(画像・CSS・JSファイルの総称)を自動的にコンパイル(圧縮)するが、
本番モードのときにはパフォーマンスのためアクセス毎には実行されないようになっている。

このためコンパイルしないとレイアウトが崩れる。
以下を実行してコンパイルしてrailsを再起動する。

rails assets:precompile

unicornの再起動

unicornのプロセスを探す。

ps aux | grep unicorn

unicron_rails masterのプロセスIDを確認。
killコマンドで終了させる。

kill -9 プロセスID

unicornを再起動する。
この時、頭にRAILS_SERVE_STATIC_FILES=1を指定する。
これはコンパイルされたアセットをrailsに渡す指定。

$ RAILS_SERVE_STATIC_FILES=1 unicorn_rails -c config/unicorn.rb -E production -D

アクセス時のログは↓に記録されていくらしいが、なぜかblank。
stderrは書き込みがあるのになぜ?

log/unicorn.stdout.log 

一応ここまででIP:3000にアクセスすれば動くはず。

NGINX

エンジンエックス。

sudo yum install nginx

設定ファイルを書き換える。

sudo vim /etc/nginx/conf.d/rails.conf
upstream app_server {
  # Unicornと連携させるための設定。アプリケーション名を自身のアプリ名に書き換えることに注意。今回であればおそらくchat-space
  server unix:/var/www/<アプリケーション名>/tmp/sockets/unicorn.sock;
}

# {}で囲った部分をブロックと呼ぶ。サーバの設定ができる
server {
  # このプログラムが接続を受け付けるポート番号
  listen 80;
  # 接続を受け付けるリクエストURL ここに書いていないURLではアクセスできない
  server_name <Elastic IP>;

# 接続が来た際のrootディレクトリ
  root /var/www/<アプリケーション名>/public;

# assetsファイル(CSSやJavaScriptのファイルなど)にアクセスが来た際に適用される設定
  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  try_files $uri/index.html $uri @unicorn;

  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://app_server;
  }

  error_page 500 502 503 504 /500.html;
}

POSTメソッドでもエラーが出ないようにするために、下記のコマンドも実行し、
権限を変更。

cd /var/lib
sudo chmod -R 775 nginx  
(デフォルトではnginxは770に指定されていた)

unicorn.rbを編集

project/confiig/unicorn.rbを編集する。

listen 3000

↓以下のように修正

listen "#{app_path}/tmp/sockets/unicorn.sock"

これでデプロイ作業は終了!

AWS S3

2段階認証にするためにauthyを導入。

AWS
Identity and Access Managementの「セキュリティステータス」でMFAの管理を選択

S3を使う専用のIAM userを作成し、S3fullAccess権限を付与。
同時に2段階認証へ。

git-secrets

git-secretsは、AWSが提供しているツール。
パスワード、secret_keyなどの秘密にしたい情報を、誤ってコミットされることを防ぐ。
=秘密情報を含むcommitができなくなる。

ローカルにインストール。

brew install git-secrets

projectフォルダに移って以下を入力

git secrets --install

secretsの条件を設定
以下のコマンドでsecret_key, access_keyなど、アップロードしたくないAWS関連の秘密情報を一括で設定

git secrets --register-aws --global

以下のコマンドで設定を確認できる。

git secrets --list

以下のコマンドで今後作成するproject全てにgit-secretsが適用される。
=git管理下に含めた時点でgit-secretsも有効になる。

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

GitHub Desktop経由でgit secretsを利用する

GitHub Desktop経由でgit secretsを利用する場合は追加の設定が必要
以下のコマンドをうつ。

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

S3のパブリックアクセス管理

  • アクセスコントロールポリシー
  • バケットポリシー

バケットポリシーで以下を入力してバケットにuserを許可する。

{
    "Version": "2012-10-17",
    "Id": "Policy1544152951996",
    "Statement": [
        {
            "Sid": "Stmt1544152948221",
            "Effect": "Allow",
            "Principal": {
                "AWS": "************①****************"
            },
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::************②**********"
        }
    ]
}

画像アップロード先を変更

1.fog-awsというgemをインストールする
2.image_uploader.rbを編集する
3.CarrierWaveの設定ファイルを作成する
4.環境変数を設定する

gemfileに下を記述し、bundle install
group :production doではなく、全てなので一番下にでも入れてください。

gem 'fog-aws'

app/uploads/image_uploader.rbファイルを以下のように変更。
storage :file → storage :fog

class ImageUploader < CarrierWave::Uploader::Base
  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick

  storage :fog

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

CarrierWaveの設定ファイルを新規作成。
/config/initializers直下に、carrierwave.rbを作成

require 'carrierwave/storage/abstract'
require 'carrierwave/storage/file'
require 'carrierwave/storage/fog'

CarrierWave.configure do |config|
  config.storage = :fog
  config.fog_provider = 'fog/aws'
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: Rails.application.secrets.aws_access_key_id,
    aws_secret_access_key: Rails.application.secrets.aws_secret_access_key,
    region: 'リージョンを入力' #例 'ap-northeast-1'
  }

  config.fog_directory  = 'ここにバケット名を入れます'
  config.asset_host = 'https://s3-ここにリージョン名を入れます(※例 ap-northeast-1).amazonaws.com/ここにバケット名を入れます'
end

application.secrets.aws_secret_access_keyとidは、一旦secrets.ymlに設定してそこから参照させるようにする。

.gitignoreに追加

secrets.ymlにデリケートな情報を持たせるので、このファイルを.ignoreに追加する。

config/secrets.yml

.gitignoreに記載した変更は、一度gitの監視下に置かれてしまったファイルには適用されない。
.gitignoreに記載した変更を反映させるためには、config/secrets.ymlをgitの監視から外す必要がある。
次のコマンドで、config/secrets.ymlをgitの監視から外す。

git rm --cached config/secrets.yml

環境変数を指定

$ vim ~/.bash_profile

export AWS_SECRET_ACCESS_KEY='ここにCSVファイルに乗っている値をコピー'
export AWS_ACCESS_KEY_ID='ここにCSVファイルに乗っている値をコピー'

$ source ~/.bash_profile

ローカル環境とサーバーの両方で上を行う。

secrets.ymlに登録

config/secrets.ymlを以下のように編集。

development:
  secret_key_base: ~~~~~~~~
  aws_access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
  aws_secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>

test:
  secret_key_base: ~~~~~~~~

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  aws_access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
  aws_secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>

環境変数を呼べるように変更。

全体の流れとしてはログインユーザーにしか参照できない環境変数にAWSのキーを登録。
→ railsがyml内にそれを呼び出し
→ それをもってAWSと通信する。

この状態ならGIT上には環境変数はどうやっても乗らないのでセキュア。
この理論ならsecrets.yml自体もセキュアなのでgitであげちゃっても大丈夫そうだけどどうなんだろう?

capistranoの記述を変更

自動デプロイで利用するためには、明示的に環境変数を指定する。

config/deploy.rbに以下を追記

set :default_env, {
  rbenv_root: "/usr/local/rbenv",
  path: "/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH",
  AWS_ACCESS_KEY_ID: ENV["AWS_ACCESS_KEY_ID"],
  AWS_SECRET_ACCESS_KEY: ENV["AWS_SECRET_ACCESS_KEY"]
}

さらに先ほど.gitignoreにsecrets.ymlを追加したため、デプロイしてもgitにpushされないため、明示的にproduction環境にアップロードしてあげる必要がある。

# secrets.yml用のシンボリックリンクを追加
set :linked_files, %w{ config/secrets.yml }

# 元々記述されていた after 「'deploy:publishing', 'deploy:restart'」以下を削除して、次のように書き換え

after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    invoke 'unicorn:restart'
  end

  desc 'upload secrets.yml'
  task :upload do
    on roles(:app) do |host|
      if test "[ ! -d #{shared_path}/config ]"
        execute "mkdir -p #{shared_path}/config"
      end
      upload!('config/secrets.yml', "#{shared_path}/config/secrets.yml")
    end
  end
  before :starting, 'deploy:upload'
  after :finishing, 'deploy:cleanup'
end

set :linked_filesで指定されたファイルは、元々のディレクトリを参照する代わりに、shared/元々のディレクトリ構成を参照するようになりる。
今回の例で言うと、config/secrets.ymlを参照する代わりに、shared/config/secrets.ymlを参照するようになる。

ロードバランサ

ELB

Elastic Load Balancing

CLB(Classic Load Balancer)、ALB(Application Load Balancer)、NLB(Network Load Banancer)の3種類

ELBを使う時の注意点

ロードバランサによって最初に振り分けられたサーバと別のサーバに振り分けられると、そこで一連の流れが途切れてしまいまた最初からやり直しが必要だったりする。

  • ELBを作成した後に”維持設定の編集”をクリック
  • “ロードバランサーによって生成された Cookie の維持を有効化”を選択
  • 設定情報を持つ秒数を入力

コメント