RailsのECS環境構築で詰まったこと
はじめに
Railsのstaging環境をECS上に構築するにあたり詰まったことをまとめました。
随時更新していきます。
前提
環境:Rails 6.0(nginxなし, unicornのみ), Ruby 2.7, ECS, CircleCI
ECS
[ipアドレス:ポート]にブラウザからアクセスしてもタイムアウトする
セキュリティグループのインバウンドルールに、
タイプ:カスタムTCP
ポート範囲:32768 - 61000
ソース:マイIP or 任意の場所(アクセス元となりうるIPアドレス)
という設定を追加するとブラウザからのアクセスができるようになるはずです。
ポート範囲は、ポートマッピングでホストポートを0に設定した場合(動的ポートマッピングを使用している場合)は上記の範囲で、それ以外は設定した値で
[ipアドレス:ポート]にブラウザからアクセスしてもアクセス拒否される
自分の場合にはなるのですが、動的ポートマッピングを使用している為、デプロイ毎にポートが変更される為、アクセスしたポートが古いものとなっている可能性があるので確認してみてください
サーバー起動には成功しているのにブラウザからアクセスできない
ターゲットグループのヘルスチェックを確認してみると原因がわかるかもしれません
サービスを更新したのにPENDINGのまま進まない(EC2)
docker container ls -a
を実行すると、終了したコンテナが多数(10個以上)、
docker image ls -a
を実行すると、手動デバッグしていた頃の不要イメージの数々が見つかりました。
その結果を元に、不要なイメージ・コンテナ等を削除した後に再度サービスのアップデートをすると無事RUNNINGになりました。
Rails
assets:precompileをどこでするか
Dockerfileの最下部に追加
rails db:migrateをどこでするか
ECSのタスク定義のコンテナ追加で、サーバー起動と同じイメージを使用してrails db:migrateだけをするコンテナを立てると簡単です。
CircleCI
aws-ecs/deploy-service-updateで失敗する
- familyに書かれたサービスがECS上で設定されていないのかもしれません
aws-ecs/deploy-service-updateの中のcontainer-image-name-updatesで複数のコンテナを指定したい
container-image-name-updates: 'container=コンテナ名,image-and-tag=イメージ名:タグ,container=コンテナ名,image-and-tag=イメージ名:タグ'
のように、container・image-and-tagを1セットとして列挙すると複数指定できる
参考:https://circleci.com/orbs/registry/orb/circleci/aws-ecs
複数のECSのサービスの更新をしたい
ステージング環境と本番環境の両方のデプロイを行いたいときに、どうやってworkflowを書けばいいか迷っていましたが、難しく考えずに
- aws-ecs/deploy-service-update:
を複数書けばいいだけでした。
複数指定した場合、CircleCI側で勝手に番号が振られ、
aws-ecs/deploy-service-update-1
aws-ecs/deploy-service-update-2
のように区別されていました
Rails6 staging環境でunicornが動かないときに確認すること
- 結論
それは、unicornの謎エラー、「master failed to start, check stderr log for details」と格闘していたときのこと...
credentialsではこのように設定しており、
db: host: db_host username: root password: password
こうやって取得しようとしたら....
staging: <<: *default database: db_staging host: Rails.application.credentials.db.host username: Rails.application.credentials.db.username password: Rails.application.credentials.db.password
サーバー側で以下のエラー発生
Unknown MySQL server host 'Rails.application.credentials.db.host' (-2) (Mysql2::Error::ConnectionError)
ネット記事を参考に以下のように修正
staging: <<: *default database: db_staging host: Rails.application.credentials.db[:host] username: Rails.application.credentials.db[:username] password: Rails.application.credentials.db[:password]
しかしこれでもなお動かない。。
Unknown MySQL server host 'Rails.application.credentials.db[:host]'
rails cの中で確認した時は動いたのに... あれ...?
よく見たら上の方に
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
こんな表記があるぞ...?? erbでもないのになんで囲っているんだろう...??
... ... ... ......あ!!!!
このファイル.rbじゃない、.ymlだ!!!!!!
staging: <<: *default database: db_staging host: <%= Rails.application.credentials.db[:host] %> username: <%= Rails.application.credentials.db[:username] %> password: <%= Rails.application.credentials.db[:password] %>
これでとりあえず取得することはできるっぽい。
その後、試しにrspecを実行したところ、以下のエラーが出た。
Failure/Error: require File.expand_path('../config/environment', __dir__) NoMethodError: Cannot load database configuration: undefined method `[]' for nil:NilClass
あれ、credentials使ったの、staging環境だけなのに... と思ったのですが、調べたところどうやら呼ばれなくても実行されるみたいです。
なので、test, staging, developmentの環境で
rails credentials:edit --environment ●●
(●●は各環境)
でstagingと同様に設定を行いました(値は仮のもので問題ないです)
ここまでして無事にローカル環境で動きました。
この後、CircleCIでまた少し詰まったのですがrailsとはあまり関係ないので別記事にしようと思います。
最後までお読みいただき、ありがとうございます。
【リモートワーク】家でEC2にSSH接続できないときに確認すること
リモートワークをしていてEC2に接続できない... となってしまっていたのでメモ
会社でEC2の設定をした時と同様、
- 「ipアドレス 確認」で検索してグローバルIPアドレスをメモ
- AWSコンソールで接続したいインスタンスのセキュリティグループを確認
- インバウンドルールに、typeにSSHを指定してソースはカスタムにしてアドレス入力欄に1 でメモしたIPアドレスを入力→保存
この手順で設定したらssh接続がTime outとなって接続に失敗していました。
ですが、インバウンドルールでソースをマイIPにすることでAWSが自動的にIPを検出してくれてそのまま保存すれば無事接続できました。
よく見たら普通に公式*1
にも書いてあるので、過去の自分がどこかでカスタムにしないといけない的な記事を見たんでしょうね...笑
原因としては、家のWifiが貧弱なのでそもそも固定IPではない可能性が高いんですよね。
実際、マイIPで自動取得されたipアドレスと検索で出てくるipアドレスがまるで違ったのでなるほどなーって感じです。
-------------------
追記:固定IPを使える設定で回線を契約していないと接続するたびにIPアドレスが変わるので、繋がらなくなったなーと思ったら再設定が必要です。
-------------------
【RSpec】FactoryBotで KeyError: Trait not registered: が出た時に確認すること
RSpecでFactoryBotを使っていたときに遭遇したエラー。
KeyError: Trait not registered:
結論から言うと凡ミスだったのですが場合によっては気づきにくいのでアウトプットして定着させておこうと思ってこの記事を書いています。
前提として、
spec/factories/product.rb
# frozen_string_literal: true FactoryBot.define do factory :product, class: Product do supplier price buy_count { 0 } end end
みたいな感じでfactoryを定義していました。
このfactoryをspec側で
create(:product, supplier: supplier, price: 1000)
のように使おうとすると
KeyError: Trait not registered: "price"
というエラーがでました。
定義しているのにおっかしいなーと思ってあれやこれやしている内に、「あれ、priceって関連先モデルがあるわけじゃないぞ」ということに気づいたわけです。
ということで、
price → price {}
とすると期待通りの動作をしました。
ちなみに、 Rails6 Ruby2.7 の環境です。(バージョンと今回の問題は多分関係ないです。)
activeadmin導入時、/adminにアクセスしたとき/admin/loginではなく/loginにリダイレクトされる不具合の修正法
完全に個人用メモにはなりますが修正にかなりの時間がかかったので、同じ現象で困っている人の参考になると幸いです。
- 前提
- Rails6, Ruby 2.7.0
- activeadmin 2.6.0
- admin_userの他に通常のuserも存在している
- 起きたこと
- /adminにアクセスしたとき/loginにリダイレクトされる
- 理想は/admin/login
- /adminにアクセスしたとき/loginにリダイレクトされる
- 解決法
- lib/custom_failure.rbを以下のように変更
# frozen_string_literal: true class CustomFailure < Devise::FailureApp def redirect_url return new_admin_user_session_path if request.original_fullpath == admin_root_path # 追加 new_user_session_path end end
このように、元々/loginが返っていたのを分岐させて/adminから呼ばれたときはadminのログインパスを返すように変更しました。
以上です。最後までご覧いただき、ありがとうございました。
Rails6, Ruby2,7, mysql8 でdocker環境を作成した時に詰まった箇所 メモ
- Mysql8
- 認証のデフォルトがcaching_sha2_passwordになっている
- mysql2がまだ対応していない
- Sequel Proもまだ対応していない(テストビルド版では対応している)
対処
- 認証のデフォルトがcaching_sha2_passwordになっている
- Ruby2.7
- 言語仕様変更により、deprecatedのwarningが多くのgemで出現
- 対処
- Dockerfileに以下を追記
- ENV RUBYOPT -W:no-deprecated -W:no-experimental
- Dockerfileに以下を追記
- 参考:https://tmtms.hatenablog.com/entry/ruby27-release
unicornが起動できない(TypeError: no implicit conversion of nil into String)
- 前提
- fog-aws、carrierwaveを使用しています。
staging環境でunicornの再起動をしようとしたら以下のエラーが出ました
[fog][DEPRECATION] Fog::Storage::AWS is deprecated, please use Fog::AWS::Storage. [fog][WARNING] Unrecognized arguments: region, aws_access_key_id, aws_secret_access_key bundler: failed to load command: unicorn_rails (/home/(・・略・・)/vendor/bundler/ruby/2.5.0/bin/unicorn_rails) TypeError: no implicit conversion of nil into String ...
fog-awsのエラーかと思いましたがその辺は触っていないので、carrierwave側で何かあるのかと思い確認してみると、aws_access_key、aws_secret_access_keyが読み込めていなかったようなので、以下のように変更
carrierwave.rb ・・略・・ # aws_access_key_id: Rails.application.credentials.aws[:access_key_id], ←コメントアウト # aws_secret_access_key: Rails.application.credentials.aws[:aws_secret_access_key], ← コメントアウト ・・略・・ use_iam_profile: true # ←追加
で解消しました。