【デプロイ】チーム開発でデプロイを担当したまとめ（参考リンク付き）

#この記事の目的
　
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の実装では開発環境、本番環境ともに画像を投稿した場合はS３に保存されるような仕様になっていました。しかし、チーム開発においてChat-Spaceと同じような仕様（画像の保存先を環境ごとに条件分岐をさせない）にすると、デプロイを担当した人しか開発環境で画像投稿ができません（理由はAWSのアクセスキーとシークレットキーがないため。）

## 環境ごとに画像の保存先を変える仕様にする

では、実際に導入した手順について書いていきます。
　
####①S3を導入する
まずはChat-Spaceと同様S３を導入させます。
今回は`carrierwave`のgemを使っています。

```ruby:Gemfile

gem 'carrierwave'
gem 'fog-aws'
```
 

####②image_uploaderで条件分岐のコードをかく

コードはこんな感じになります

```ruby:app/uploader/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に保存するときのキーを渡してあげる

```ruby:config/initializer/carrierwave.rb
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の知識も入っています。

```ruby
      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](https://guides.rubyonrails.org/5_2_release_notes.html)

つまり、要約すると

- 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 のキホン](https://qiita.com/NaokiIshimura/items/2a179f2ab910992c4d39)
[Rails5.2から導入されたcredentials.yml.encを極める](https://qiita.com/yuuuking/items/53a37a2e998972be32b8)

###　個人的に詰まった部分

credentials.ymlで詰まった部分は主に２箇所ありました。

#### ①credentials.ymlの中身がデフォルトでコメントアウトされていることに気がつかなかった
 
ターミナルで```EDITOR=vim bin/rails credentials:edit```と打つと、以下のように出力されます

```ruby:credentaials.yml
# 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で取得した値>
```

この１〜３行目のコメントアウト(#)にまったく気がつかずに３時間くらい頭を抱えていました。笑

####④本番のshared/config/master.ymlにmaster.keyをコピーできなかった
さっき上にあげた２つのリンクでこの方法が紹介されているのですが、まったくできなかったため、```vim ~/.bash_profile```でmaster_keyを無理やり使えるようにしました。（本当はよくない。環境変数にするのが正解っぽいです。）

```terminal:ssh
vim ~/.bash_profile  　　 #書き込み
source ~/.bash_profile　　#読み込み
```

#頻発したエラーとその対応、対策
実はこの一ヶ月間、rubyやrailsのコードが原因で「デプロイができない！」みたいなエラーはなかったです！これは実際に正確なコードを書いて、コメントおじさんをはじめとして、内容の濃いコードレビューをして、ローカル環境で確認してくださっていたから。本当にありがとうございました！

##開発中におきたエラー集（随時追加予定）

実際にデプロイ作業中はエラーが頻発しました。ただ、いつも同じようなエラーだったり、原因は「またお前か…」みたいなことになるので、最終的にはRailsのエラーを解決するのとそんなに難易度は変わりません。
　
ここでは実際におきたエラーをジャンル別に分けて見ていきます。
 
###1.身に覚えのないエラー＝EC2インスタンスの再起動
これは先輩の発表会のときに聞いた言葉です。
>「身に覚えのないエラーはとりあえずEC２インスタンスを再起動すれば大丈夫だから！」

これはマジでした。笑
例えば、よくあったのは```bundle exec cap production deploy```で自動デプロイ中に「bundle installができません！」と怒られたとき。最初はbundlerとかを見てましたが、試しにEC2インスタンスを再起動させたところ、すんなり解決。身に覚えのないエラーに遭遇したら、AWSでEC2を再起動させましょう。

###2.自動デプロイ中のエラー
自動デプロイの最中にエラーがおきた場合は、エラー文を読んであげると解決することが多いです。特に```master.key```が正しいものではないとエラーが起きるので、そこもチェックしてみるといいかな。

####Nginx,Mysql,Unicornの状態を確認する
```terminal:ssh
$ 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を立ち上げる
```
EC２を再起動後は自動的にこれらがSTOP状態になるので、Mginx,MySQL,Unicornを再起動させます。


####Capistrano導入後のディレクトリ構造の変化

Capistranoを導入後はディレクトリの構成が変わります。例えば```config/unicorn.rb```が、```shared/config/unicorn.rb```になったり。unicorn.rbを参照する記述はsharedを参照する記述になっているか確認してみるといいです。

```ruby:unicorn.rb
# 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コマンドで作成する

```terminal
$ sudo touch /tmp/mysql.sock 
```
このエラーは「ローカルのMySQLに繋がない」ので、サーバー(ssh)ではなく、ローカルでこのコマンドを打ちます。
/tmp/mysql.sockの場所はdatabase.ymlに記載されている。


```ruby:config/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による自動デプロイトラブルシューティング(随時追記予定)](https://qiita.com/yuki_uchida/items/c559862fba6f39bd6e73#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E6%88%90%E5%8A%9F%E3%81%97%E3%81%9F%E3%81%AE%E3%81%AB%E5%A4%89%E6%9B%B4%E3%81%8C%E5%8F%8D%E6%98%A0%E3%81%95%E3%82%8C%E3%81%AA%E3%81%84)
↑この記事控えめに行ってヤバイです。だいたいこれで解決できる

##credentials.yml.enc系
[Rails5.2から導入されたcredentials.yml.encを極める](https://qiita.com/yuuuking/items/53a37a2e998972be32b8)
[Rails 5.2の新機能Credentialsでパスワード等を管理する](http://thr3a.hatenablog.com/entry/20180408/1523156956)
[Rails5.2から追加された credentials.yml.enc のキホン](https://qiita.com/NaokiIshimura/items/2a179f2ab910992c4d39)

##Mysql,DB操作系

[MySQLの既存のデータベースの見方MySQLデータベース初心者向け](https://qiita.com/hayabusa3703/items/9893a53c21ddc3c2403a)
[EC2上のMySQLをsequel proを使って表示する](https://qiita.com/naginx/items/9b6fcad470474f8e09e9)
[GitHubにpushしたmigrationファイルは安易に修正してはいけません](https://qiita.com/jnchito/items/3525fd22973477b88411)
[mysqlが起動できない（Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)）](https://qiita.com/carotene4035/items/e00076fe3990b9178cc0)
[mysqlが起動できない](https://qiita.com/hondy12345/items/d32ed749fb49e9da7de6)

##AWS系
[AWSの無料枠について](https://qiita.com/TECHEXP/private/d1cbf077989de78f330c)
[Amazon EC2 インスタンスの IP アドレッシング](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-instance-addressing.html)

##公式リンク系
[Ruby on Rails 5.2 Release Notes](https://guides.rubyonrails.org/5_2_release_notes.html)
[carrierwaveuploader/carrierwave](https://github.com/carrierwaveuploader/carrierwave)
[fog/fog](https://github.com/fog/fog)
[]()