ENECHANGE Developer Blog

ENECHANGE開発者ブログ

AWS Elastic Beanstalk にサブモジュール込みでデプロイする際は、CLI実行ユーザにサブモジュールにアクセスできる権限が必要になる

こんにちは。蜜蜂の館のリメイク度合いが楽しみな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コマンドは実行されません。

f:id:gamenechange:20200408164253p:plain

ハマりどころ

今回ハマった理由の一つは、たとえ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株式会社では、リモートワークの環境でもデプロイをシュッと行えるエンジニアを募集しております。詳しくは採用情報のページをご覧ください。

(参考)git submodule と git archive についてのドキュメント

*1:結果的に、.gitmodulesのデプロイの成否は今回の件とは関係がないことが分かりました

*2:EBのユーザが主体ではない

*3:すなわち、ローカル開発環境における僕です

*4:$ eb deploy を行うために必要な権限はAWSの権限であるため、gitリポジトリの権限とは別になります