GitHub Actions で突然デプロイが失敗するようになった
こんにちは、那須です。前回、以下のようなデプロイ自動化の記事を書きました。
何事もなくデプロイ作業ができていたのですが、ある日突然動かなくなりました。その時の様子を備忘録も兼ねて書き残しておこうと思います。
ちなみに、今回の事象で得た知識は以下の通りです。デプロイの過程で変更や削除ができないファイルが作成される場合は何かしら手を打つ必要があります。
・Docker コンテナは特にユーザを気にせずに実行すると root で実行される
・Docker コンテナで作成されたファイルの所有者は当然 root になる
・__pycache__ はコンパイルした結果のキャッシュなので削除可能
・デプロイしかしてないのでそもそもキャッシュを作成しなくても大丈夫
事象
GitHub Actions で何気なくデプロイ実行すると、以下のエラーで止まってしまいました。
Cleaning the repository
/usr/bin/git clean -ffdx
warning: failed to remove path/__pycache__/aaa.pyc: Permission denied
Warning: Unable to clean or reset the repository. The repository will be recreated instead.
Deleting the contents of 'path'
Error: Command failed: rm -rf "path"
rm: `path/__pycache__/aaa.pyc' を削除できません: Permission denied
止まったステップの内容はこちらです。普通にブランチにチェックアウトしているだけです。
- name: Check out branch
uses: actions/checkout@v2
with:
ref: ${{ github.ref }}
エラーが出始めた環境はデプロイ用 EC2 インスタンスにセルフホストランナーを入れた環境です。これまで何度もデプロイできていたのに突然このエラーのせいでデプロイできなくなったので、本当に不思議に思っていました。
調査の流れと原因
そもそも突然 path/__pycache__/aaa.pyc が Permission denied で削除できなくなったのがよくわからなかったので、実際に権限を確認してみました。すると削除できなかったファイルだけ所有者が root になっていました。
ここで 1 つ気づきました。デプロイ用 EC2 インスタンスの上に Docker コンテナがあって、その Docker コンテナの中で DB マイグレーションを実行しています。これまでソースコード側と DB 側のバージョンは同じでした。ただエラーの前日に DB 側のバージョンを上げたので、その時にとある python のコードが以下のようなコマンドで実行されていました。
python manage.py upgrade
この結果作成されたバイトコードのキャッシュ(実際にはこの中で import されているモジュールのキャッシュ)が、今回削除できなかったファイルです。
当然 Docker コンテナ上で実行されているので、削除できなかったファイルの所有者も root で権限も rw-r--r-- です。GitHub Actions のユーザは root ではないので、これが原因で削除できない状態になっていることがわかりました。
対処法
Docker コンテナの実行ユーザを root 以外にするのが筋だとは思うのですが、他の作業への影響がはっきりしなかったので Docker コンテナに対する変更は保留しました。代わりに __pycache__ ディレクトリとその中のファイルを生成する必要がないことが調べてみてわかったので、これを作成しないように変更することで対処しました。
具体的には、python 実行時に -B をつけることでバイトコードのキャッシュを作成しないことがわかったので、以下のようなコマンドを Docker コンテナで実行するようにしています。
python -B manage.py upgrade
実際に DB マイグレーションを実行してバージョンを変更しても、__pycache__ ディレクトリは作成されませんでした。これで無事に再び自動デプロイが動くようになったので一安心です。
さいごに
最初にも書きましたが、大事なポイントをまとめると以下になります。
・Docker コンテナは特にユーザを気にせずに実行すると root で実行される
・Docker コンテナで作成されたファイルの所有者は当然 root になる
・__pycache__ はコンパイルした結果のキャッシュなので削除可能
・デプロイしかしてないのでそもそもキャッシュを作成しなくても大丈夫
やっぱり障害対応が一番勉強にはなりますね。発生しないのが一番ですが現実はそう甘くはないので、今後も障害が発生しても極力影響がないように仕組みを作ろうと思います。
最後の最後に dotD の紹介ページをご案内します。私はエンジニアなので同じエンジニアの方が増えると単純に嬉しいですが、他にもたくさんの職種で募集しています。入社して 1 ヶ月経っていろんなことがわかってきましたので、dotD に興味がある方はぜひご連絡ください。仕事の内容や会社の雰囲気など、いろんなお話ができると思います。