#この記事の目的
TECH::EXPERTの最終課題である「チーム開発でメルカリのコピーサイトを作る」ときにデプロイを担当させてもらいました。しかし、他のチームメンバーにはほとんど技術的なことだったり、エラー時の対応などをシェアしていなかったので、この記事の目的は他のメンバーにこの一ヶ月デプロイ担当として一体何をやっていたのか、チーム開発の中のデプロイ担当の役割を自分なりにお伝えする記事です。
※Capistrano,Unicorn,nginxの導入に関してはTECH::EXPERTのカリキュラムで一度やっているので、省きます。
#デプロイの際に使ったサービス、言語など
チーム開発の際に使っていた言語とサービスのversionは以下の通りです
services or lang | version |
---|---|
Ruby | 2.5.1 |
Rails | 5.2.3 |
Bundler | 3.11.0 |
Mysql | 14.14 |
unicorn | 5.4.1 |
Capistrano | 3.11.0 |
Nginx | 1.14.1 |
#S3導入後、環境ごとに画像の保存先を分岐させる
Chat-Spaceの実装では開発環境、本番環境ともに画像を投稿した場合はS3に保存されるような仕様になっていました。しかし、チーム開発においてChat-Spaceと同じような仕様(画像の保存先を環境ごとに条件分岐をさせない)にすると、デプロイを担当した人しか開発環境で画像投稿ができません(理由はAWSのアクセスキーとシークレットキーがないため。)
環境ごとに画像の保存先を変える仕様にする
では、実際に導入した手順について書いていきます。
####①S3を導入する
まずはChat-Spaceと同様S3を導入させます。
今回はcarrierwave
のgemを使っています。
gem 'carrierwave'
gem 'fog-aws'
####②image_uploaderで条件分岐のコードをかく
コードはこんな感じになります
# Choose what kind of storage to use for this uploader:
if Rails.env.development? || Rails.env.test?
storage :file
else
storage :fog
end
developmentまたはtest環境のときは
storage :file
に保存して、
それ以外、つまりproduction(本番)環境のときは
storage :fog
に保存しています。
####③carrierwave.rbにS3に保存するときのキーを渡してあげる
require 'carrierwave/storage/abstract'
require 'carrierwave/storage/file'
require 'carrierwave/storage/fog'
CarrierWave.configure do |config|
if Rails.env.development? || Rails.env.test? #開発とテストは今まで通りに
config.storage = :file
elsif Rails.env.production? #本番はS3に保存する
config.storage = :fog
config.fog_provider = 'fog/aws'
config.fog_credentials = {
provider: 'AWS', #AWSのアクセスキーとシークレットキーを環境変数で定義する
aws_access_key_id: Rails.application.credentials.aws[:access_key_id], #credentails.ymlに鍵の本体があります
aws_secret_access_key: Rails.application.credentials.aws[:secret_access_key], #credentails.ymlに鍵の本体があります
region: #'AWSで設定した地域(おそらく'ap-northeast-1') '
}
config.fog_directory = #'S3のバケット名'
config.asset_host = 'https://s3-ap-northeast-1.amazonaws.com/tech54-mercari-a'
end
end
これで開発環境のときは、今まで通りに。本番はS3に保存されるようになったと思います。
####考えられるエラーの原因
エラーが出たり、画像が期待する場所に保存されない、デプロイができないなどの原因はおそらく以下のうちのどれかが考えられます。
- 条件分岐のコードが正しくかけていない
- バケットの権限は正しく付与されているか
- credentials.ymlのmaster.keyの鍵は鍵穴に一致する鍵か
- credentials.ymlの中身とそれを使っている部分(今回であれば下のコードの
.aws[:access_key_id]
とaws[:secret_access_key]
の部分)
ちなみにこのコードにはcredentials.ymlの知識も入っています。
aws_access_key_id: Rails.application.credentials.aws[:access_key_id],
aws_secret_access_key: Rails.application.credentials.aws[:secret_access_key],
ここの部分です。
この流れで次は、Rails5.2から導入されたcredentials.ymlについて解説してみます。
もうここまできたら、あとはcredentials.ymlを使いこなすだけです。
(ただこのcredentials.yml)にかなり苦戦させられました…
#credentials.yml.encでパスワードを管理する
##結論から
config/credentials.yml.encプロダクションアプリの秘密を保存するためのファイルを追加しました。config/master.keyファイル内のキーまたはRAILS_MASTER_KEY環境変数で暗号化されたリポジトリに、サードパーティサービスの認証資格情報を直接保存できます。これは、最終的にRails.application.secretsはRails 5.1で導入された暗号化された秘密を置き換えます。さらに、Rails 5.2 はCredentialsの基礎となるAPIを開くので、他の暗号化された設定、キー、およびファイルを簡単に扱うことができます。これについては、Securing Rails Applications ガイドを読んでください。引用元:Ruby on Rails 5.2 Release Notes
つまり、要約すると
- Rails 5.2 versionから登場したよ!sercret.ymlの代わりだよ!
- credentials.yml.encに秘密鍵などを保存できるよ!
- しかも、credentials.yml.encは暗号化されてるからgithubに上がってもなにかいてあるかわからないよ!
- しかもしかも、credentials.yml.encはmaster.keyがないと編集したり、中身を見ることができないよ!
- 極め付けはmaster.keyはデフォルトでgitignoreに入っているからgithubには上がらないよ!
つまりmaster.keyを意図的にgithubなどで公開しない限り、秘密は守られるってことです。
##実際にS3のアクセスキーとシークレットキーをcredentials.yml.encに入れて保存してみる
この記事でcredentails.ymlについて書こうと思ったのですが、自分の理解がまだ不十分なところもあります。ミスリードを避けるために、お世話になったcredentails.ymlの記事を貼っておきます。実際に実装するときは以下の記事を参考にするとスムーズにいくと思います。
Rails5.2から追加された credentials.yml.enc のキホン
Rails5.2から導入されたcredentials.yml.encを極める
### 個人的に詰まった部分
credentials.ymlで詰まった部分は主に2箇所ありました。
①credentials.ymlの中身がデフォルトでコメントアウトされていることに気がつかなかった
ターミナルでEDITOR=vim bin/rails credentials:edit
と打つと、以下のように出力されます
# aws:
# access_key_id: 123
# secret_access_key: 345
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: <rake secretで取得した値>
この1〜3行目のコメントアウト(#)にまったく気がつかずに3時間くらい頭を抱えていました。笑
####④本番のshared/config/master.ymlにmaster.keyをコピーできなかった
さっき上にあげた2つのリンクでこの方法が紹介されているのですが、まったくできなかったため、vim ~/.bash_profile
でmaster_keyを無理やり使えるようにしました。(本当はよくない。環境変数にするのが正解っぽいです。)
vim ~/.bash_profile #書き込み
source ~/.bash_profile #読み込み
#頻発したエラーとその対応、対策
実はこの一ヶ月間、rubyやrailsのコードが原因で「デプロイができない!」みたいなエラーはなかったです!これは実際に正確なコードを書いて、コメントおじさんをはじめとして、内容の濃いコードレビューをして、ローカル環境で確認してくださっていたから。本当にありがとうございました!
##開発中におきたエラー集(随時追加予定)
実際にデプロイ作業中はエラーが頻発しました。ただ、いつも同じようなエラーだったり、原因は「またお前か…」みたいなことになるので、最終的にはRailsのエラーを解決するのとそんなに難易度は変わりません。
ここでは実際におきたエラーをジャンル別に分けて見ていきます。
###1.身に覚えのないエラー=EC2インスタンスの再起動
これは先輩の発表会のときに聞いた言葉です。
「身に覚えのないエラーはとりあえずEC2インスタンスを再起動すれば大丈夫だから!」
これはマジでした。笑
例えば、よくあったのはbundle exec cap production deploy
で自動デプロイ中に「bundle installができません!」と怒られたとき。最初はbundlerとかを見てましたが、試しにEC2インスタンスを再起動させたところ、すんなり解決。身に覚えのないエラーに遭遇したら、AWSでEC2を再起動させましょう。
###2.自動デプロイ中のエラー
自動デプロイの最中にエラーがおきた場合は、エラー文を読んであげると解決することが多いです。特にmaster.key
が正しいものではないとエラーが起きるので、そこもチェックしてみるといいかな。
####Nginx,Mysql,Unicornの状態を確認する
$ sudo servise nginx status #nginxの状態を確認する
$ sudo service mysql status #mysqlの状態を確認する
$ bundle exec unicorn -c /var/www/freemarket_sample_54a/current/config/unicorn.rb -E production -D #unicornを立ち上げる
EC2を再起動後は自動的にこれらがSTOP状態になるので、Mginx,MySQL,Unicornを再起動させます。
####Capistrano導入後のディレクトリ構造の変化
Capistranoを導入後はディレクトリの構成が変わります。例えばconfig/unicorn.rb
が、shared/config/unicorn.rb
になったり。unicorn.rbを参照する記述はsharedを参照する記述になっているか確認してみるといいです。
# Capistrano導入前。
listen "#{app_path}/tmp/sockets/unicorn.sock"
pid "#{app_path}/tmp/pids/unicorn.pid"
stderr_path "#{app_path}/log/unicorn.stderr.log"
stdout_path "#{app_path}/log/unicorn.stdout.log"
# Capistrano導入後。それぞれ、sharedの中を参照するよう変更
listen "#{app_path}/shared/tmp/sockets/unicorn.sock"
pid "#{app_path}/shared/tmp/pids/unicorn.pid"
stderr_path "#{app_path}/shared/log/unicorn.stderr.log"
stdout_path "#{app_path}/shared/log/unicorn.stdout.log"
###3.MySQL系エラー
####Can`t connect to local Mysql server through socket /tmp/mysql.sock(2)
原因:mysql.sockがないこと
解決策:mysql.sockをtouchコマンドで作成する
$ sudo touch /tmp/mysql.sock
このエラーは「ローカルのMySQLに繋がない」ので、サーバー(ssh)ではなく、ローカルでこのコマンドを打ちます。
/tmp/mysql.sockの場所はdatabase.ymlに記載されている。
default: &default #ローカル環境
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password:
socket: /tmp/mysql.sock
production: #本番環境
<<: *default
database: freemarket_sample_54a_production
username: root
password: <%= ENV['DATABASE_PASSWORD'] %>
socket: /var/lib/mysql/mysql.sock
#デプロイ作業するときに役立ったリンク集
##エラー全般系
Capistranoによる自動デプロイトラブルシューティング(随時追記予定)
↑この記事控えめに行ってヤバイです。だいたいこれで解決できる
##credentials.yml.enc系
Rails5.2から導入されたcredentials.yml.encを極める
Rails 5.2の新機能Credentialsでパスワード等を管理する
Rails5.2から追加された credentials.yml.enc のキホン
##Mysql,DB操作系
MySQLの既存のデータベースの見方MySQLデータベース初心者向け
EC2上のMySQLをsequel proを使って表示する
GitHubにpushしたmigrationファイルは安易に修正してはいけません
mysqlが起動できない(Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2))
mysqlが起動できない
##AWS系
AWSの無料枠について
Amazon EC2 インスタンスの IP アドレッシング
##公式リンク系
Ruby on Rails 5.2 Release Notes
carrierwaveuploader/carrierwave
fog/fog