91s

プログラミング関係、日記などを書きます

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)

自分の場合、sshで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が動かないときに確認すること

  • 結論
    • database.ymlにrubyを埋め込む時は<%= =%>で囲うのを忘れないようにする!!
    • ymlの中にruby埋め込んだら使われずとも実行されるので全ての環境でcredentials:editで該当項目を設定しておくこと

それは、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の設定をした時と同様、

  1. ipアドレス 確認」で検索してグローバルIPアドレスをメモ
  2. AWSコンソールで接続したいインスタンスのセキュリティグループを確認
  3. インバウンドルールに、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
  • 解決法
    • 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もまだ対応していない(テストビルド版では対応している)
    • 対処

      • docker-compose.ymlのmysqlコンテナのコマンドに追記
        • command: mysqld --character-set-server=utf8mb4 --default-authentication-plugin=mysql_native_password
        • (--default-authentication-plugin=mysql_native_password が追記部分)
    • 参考:https://qiita.com/ucan-lab/items/3ae911b7e13287a5b917

  • Ruby2.7

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 # ←追加


で解消しました。