こんにちは。蜜蜂の館のリメイク度合いが楽しみなgamenechangeです。今回は、AWS Elastic Beanstalk(以下、EB)に対して、サブモジュールを使用しているプロジェクトを$ eb deploy
する際にハマってしまったことを書かせて頂きます。
以下の内容では、Railsアプリをデプロイすることを前提としています。
$ eb deploy した後にサブモジュールのディレクトリの中身が空っぽになる事象が発生
先日、普段のように Railsアプリを$ eb deploy
し、特にエラーもなくデプロイが終了しました。しかしながら、Webブラウザで動作を確認すると500エラーが出て正常に動作していませんでした。
慌てて原因を調査したところ、サブモジュールを用いる部分の処理が理由で500エラーになっていることが分かりました。具体的に調査すべく$ eb ssh
を行い、当該サブモジュールのディレクトリの中身を確認してみました。
……空っぽでした。ファイルやディレクトリが何も存在していませんでした。
サブモジュールが正しく $ git submodule update できていない
ローカル開発環境では、当該サブモジュールのディレクトリには期待通りのファイルが存在しています。つまり、EB上ではサブモジュールが正しく$ git submodule update
(以下、update)できていないことが分かりました。
確認したこと
サブモジュールが正しくupdateできていないため、まずは.gitmodules
の内容を確認します。さらに.gitmodules
が正しくデプロイされているか(例えば、.ebignore
の中で.gitmodules
を除外ファイルとして指定してしまっていないか)を確かめました*1。
……いずれも問題ありませんでした。
原因はサブモジュールにアクセスするための権限の設定にあった
さらに調査を進め、今度は$ eb ssh
を経てEBの環境内でサブモジュールをupdateしようとしました。すると「アクセス権がない」旨のエラー表示が出て正しくupdateできませんでした。
アクセス権の問題とのことで、改めて.gitmodules
を確認しました。すると以下のように、url:
の箇所に、git@github.com:foobar/hogehoge.git
という書式でサブモジュールのURLが記述されていました。
[submodule "fuga/hogehoge"] path = fuga/hogehoge url = git@github.com:foobar/hogehoge.git
ここで重要なことは「サブモジュールをupdateする主体はEBのCLIを実行したユーザになる」ということです*2。したがって、「$ eb deploy
のコマンドを実行したユーザ*3」がこのリポジトリにアクセスできる権限を持っていなければなりません。
このことに気づき、ローカル開発環境の自分のアカウントの~/.ssh/config
を確かめてみたところ、当該サブモジュールへアクセスするための鍵の指定が適切ではないことが分かりました。そして正しい鍵を設定した後に$ eb deploy
を改めて実行したところ、サブモジュールが正しくupdateされました。一件落着です。
aws-elastic-beanstalk-cli のコードを読んで具体的な動作を確認する
上記の内容の具体的な動作をaws-elastic-beanstalk-cliのコードを読んで確認してみましょう。
まず、ebcli/objects/sourcecontrol.py
の中の do_zip
メソッドの中で、サブモジュールを$ git submodule foreach --recursive
しています。
※ここで、Railsプロジェクト内に存在する.elasticbeanstalk/config.yml
に記載されているglobal: include_git_submodules:
のtrue/false
を判別している箇所があり、ここにfalse
を設定すればsubmoduleをupdateしない挙動にすることが可能です
must_zip_submodules = fileoperations.get_config_setting('global', 'include_git_submodules') if must_zip_submodules: stdout, stderr, exitcode = self._run_cmd(['git', 'submodule', 'foreach', '--recursive']) for index, line in enumerate(stdout.splitlines()): submodule_dir = line.split(' ')[1].strip('\'') os.chdir(os.path.join(project_root, submodule_dir)) self.do_zip_submodule( location, "{0}_{1}".format( location, str(index) ), staged=staged, submodule_dir=submodule_dir )
次に上記のコードにあるdo_zip_submodule
メソッドを見てみます。すると、submoduleを$ git archive
してzipファイルに固めていることが分かります。
# individually zip submodules if there are any stdout, stderr, exitcode = self._run_cmd(['git', 'archive', '-v', '--format=zip', '--prefix', os.path.join(submodule_dir, ''), '-o', sub_location, commit_id])
この一連の「$ git submodule foreach --recursive
を実行して$ git archive
を実行する」という流れは、EBのCLIを実行した時の流れです。したがってこの流れより、「CLI実行ユーザがsubmoduleへのアクセス権限を持つ必要がある」という結論を導くことができます。
図解
これまでの説明を簡単に図解すると下の図のようになります。
$ eb deploy
するユーザ(下図ではenechange_user
)は内部的に$ git submodule
コマンドを実行します。そしてそれにより取得できたファイルを$ git archive
コマンドで zipファイル に固めて、それをEBへデプロイするという流れになります。EB側では$ git submodule
コマンドは実行されません。
ハマりどころ
今回ハマった理由の一つは、たとえsubmoduleのgitリポジトリの権限の設定が不適切であったとしても $ eb deploy
自体は成功することです*4。以下がそのときのターミナルのログです。
$ eb deploy Creating application version archive "app-foo-bar". Uploading: [##################################################] 100% Done... 2020-04-05 23:38:51 INFO Environment update is starting. 2020-04-05 23:39:01 INFO Deploying new version to instance(s). 2020-04-05 23:40:36 INFO New application version was deployed to running EC2 instances. 2020-04-05 23:40:36 INFO Environment update completed successfully.
このログが表示されたため、サブモジュールや、そのサブモジュールをupdateするための権限が不適切であることがエラーの原因だとは直ちに思いつきませんでした。
根本的な原因は、普段と違う環境からデプロイを行ったこと
今回の事象の直接の原因は、リポジトリへのアクセス権限の設定が不適切であったことです。ただ、根本的な原因は、僕が普段と異なる環境(ユーザ)でデプロイを行ったことによるものでした。
ENECHANGEでは先日より原則フルリモートでの勤務に移行しており、そのため普段とは異なる環境でデプロイを行いました。そのときにこの事象が起きました。
昨今の状況を考えると、同様の理由によるデプロイや権限周りの失敗は起きてもおかしくはないと思いますので、今回記事として書かせて頂きました。
ENECHANGE株式会社ではデプロイをシュッと行えるエンジニアを募集しています
ENECHANGE株式会社では、リモートワークの環境でもデプロイをシュッと行えるエンジニアを募集しております。詳しくは採用情報のページをご覧ください。