実践で学ぶRuby on rails 〜仮説千本ノック〜

プログラマーとして独立するため日々スキルアップに励んでいます。優れたプログラマは仮説を立てるのがうまい。そこを目指して仮説を立てては検証する日々です!!

AWSを使ったデプロイ〜S3にファイルをアップロードする〜

S3にバケット(保存先)を用意する

データが保存される場所であるバケットを作成します。

名前とリージョンを決める必要がありますが、

名前は、バケットにアクセスするためのURLとして使われるため、一意である必要があります。

リージョンは、バケットが存在しているサーバーの場所となりますので、EC2と同じ考え方で選択して下さい。

なお、誰がS3にアクセスできるのか、バケットポリシーで設定しますが、まずは、自分で作成したIAMユーザーからのみアクセス許可して、ARNを確認し、バケットポリシーに下記の通り記述しましょう。

{
    "Version": "2012-10-17",
    "Id": "Policy1544152951996",
    "Statement": [
        {
            "Sid": "Stmt1544152948221",
            "Effect": "Allow",
            "Principal": {
                "AWS": "IAMユーザーのARN"
            },
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::バケットの名前"
        }
    ]
}

〇Sid ポリシーを識別するID

〇Version ポリシーの記法を指定する。

〇Effect ここで設定しているポリシーが許可を与えるポリシー(ALLOW)なのか、許可しない(Deny)なのか設定

〇Action *によってS3に関する全ての権限を許可している

〇Resource  S3のうち、どのリソースに関する権限を許可するのか指定。(今回で言えば、一つのS3についてのみ権限を認める)

 

これで、保存先となるS3のバケットは準備できました。

この後の手順は次のようになります。

▫️ローカルのアプリの画像保存先を、アプリ内のpublicフォルダから、S3へ変更し、ローカルからS3へアップロードできるようにする。

 

▫️次に環境変数の設定を変えた上、アプリを本番環境にデプロイすることで、本番環境においても、保存先がS3となります。

 

なぜ、環境変数の設定を変える必要があるかというと、

S3を使用するには、環境変数が必要。そして環境変数は、OSが提供する変数のため、ローカル環境では、自分のPCのOS(.bash_profileファイル)、本番環境では、EC2インスタンスのOS(environmentファイル)がそれぞれ提供する。

そのため、本番環境でS3へ保存するなら、EC2インスタンス内environmentファイルにてで環境変数を定義する必要がある。

 

画像保存先をS3へ変更する

▫️ネット上(S3)にファイルをアップロードできるようにするためのgem「fog-aws」をインストール

開発環境でも本番でも使うので、Gemfileの一番下に記述して、bundleする。

group :development do
  # Access an IRB console on exception pages or by using <%= console %> in views
  gem 'web-console', '~> 2.0'

  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
end

gem 'carrierwave'
gem 'fog-aws'

 

▫️インストールしたfog-awsをアップロード時に使えるようにimage_uploaderを編集する。

class ImageUploader < CarrierWave::Uploader::Base

  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick
  process resize_to_fit: [800, 800]

  storage :fog

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

 

上の設定では、アップロード時にfogを使うよう指定しただけで、結局fogを使って「どこに」アップロードするのかの指定していない。

 

▫️ここで保存先を指定する。

config/initializers直下にcarrierwave.rbを作成して設定する。

idとkeyの値は環境変数に代入している値を呼び出します。

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

環境変数

大元のidとkeyの値は、ローカルにて環境変数に代入していますが、その環境変数をさらにsecrets.yamlにて、別の変数に代入し、上ではその変数を呼び出すことで、大元の値を取得している。

 

.gitignoreによってsecrets.yamlをアップロード対象から外す

すでに、この段階で、ローカルの開発環境でアップロードした画像はS3にアップロードされるはずです。

 

確認するには、ローカル環境で画像を登録してみましょう。

S3のコンソールでバケットの中身を確認し、成功していればuploadsというディレクトリが生成されており、その中に画像が保存されています。

 

S3への保存はできましたが、ここでセキュリティ対策を1つ講じましょう。

 

secrets.ymlには、パスワード系の記述がされるため、gitignoreに記述することによりgitの管理対象から外しましょう(pushしてもリモートリポジトリにアップロードされなくなる)

config/secrets.yml

 

ただし、gitignoreに記載する前に、すでにsecrets.ymlは一度gitの管理対象となっていますので、gitignoreに記述した後でいいので、対象か外すコマンドを実行しましょう。

$ cd ~/projects/アプリ名
# 単にrmだと削除だが、--cachedオプションにより、ファイルは残し、
 gitの管理対象から外すことができる。 $ git rm --cached config/secrets.yml

 

 

本番環境でも保存先がS3となるよう、環境変数を設定する。

ec2インスタンスにログインして、environmentファイルを編集する。(ローカルの.bash_pfofileで行ったのと同じ趣旨)

$ ssh -i [pem鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]
(ダウンロードした鍵を用いて、ec2-userとしてログイン)
$ sudo vim /etc/environment
# iを押してインサートモードに移行し、下記を追記する。既存の記述は消去しない。
AWS_ACCESS_KEY_ID='ここにCSVファイルのAccess key IDの値をコピー'
AWS_SECRET_ACCESS_KEY='ここにCSVファイルのにSecret access keyの値をコピー'
# 編集が終わったらescapeキーを押してから:wqと入力して保存して終了

 

environmentに加えた変更を反映させるため、一旦ログアウトして改めてログインしましょう。

正しく環境変数が設定されているかをenv | grepコマンドで確認しましょう。

# 編集した環境変数を適用するために一旦ログアウトします。
$ exit
$ ssh -i [pem鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]
# 環境変数が適用されているか確認しましょう。
$ env | grep AWS_SECRET_ACCESS_KEY
$ env | grep AWS_ACCESS_KEY_ID

 

さて、環境変数は大元の設定場所(.bash_profileやenvironment)から、secrets.yml、carrierwave.rbへと読み込まれていきます。

secrets.yamlには、本番環境における環境変数の設定を行っていませんので、次のように記述しましょう。

# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!

# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rails secret` to generate a secure secret key.

# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.

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

test:
  secret_key_base: 7362cb8e960adf75f110e17bb4cd1f2d4edc3d・・・・・

# Do not keep production secrets in the repository,
# instead read values from the environment.
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"] %>

 

これでうまく動きそうですが、実はそうではありません。

 

secrets.ymlの設定をしても、このファイルが現状、gitignoreに記述したことにより、Github経由でサーバーにアップロードされない状況にあるため、機能しないのです。

 

ポイントなのは、ローカル環境にあるsecrets.yamlを機能させるには、何らかの方法でサーバー環境と連携をさせたいが、

直接的にファイルをネット上にアップするにはリスクがあるので、gitignoreに記述して、リモートリポジトリには上がらないようにしつつ、capistranoのdeploy.rbにコードを書くことで、secretsの保存場所は変えずに、シンボリックリンクでサーバ上からローカルのsecrets.ymlを呼び出せるよう、deploy.rbを変更する必要があります。(deploy.rbへの変更はgithub経由でサーバにあげたいので、pushします)

 

deploy.rbを次のように編集しましょう

set :linked_files, %w{ config/secrets.yml }

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

after 'deploy:publishing', 'deploy:restart'

#deployした時、restartさせる。 namespace :deploy do task :restart do invoke 'unicorn:restart' end
#サーバーにshared/configディレクトリがないなら作成し、ローカルにあるsecrets.yamlをshared/condigフォルダにアップロードするという、uploadタスクの定義。 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

〇linked_filesはシンボリックリンクの設定。

大元のsecrets.yamlはローカルにあるが、サーバーのshared/config/secrets.ymlに、ローカルのconfig_secrets.ymlへのシンボリックリンクを貼って連携させる。

 

ここまでで、いつくかローカルで設定ファイルを編集しているのでpushした上で、自動デプロイを実行しましょう。

$ bundle exec cap production deploy