はじめに
GitHub Actions の runner 上で Ansible を動かし、SSM 経由で EC2 を構成する——という CD を組んでいました。SSM 接続には boto3 が要るので、ちゃんと入れたつもりだったのに、最初のタスクで boto3 が無いと怒られて落ちました。
pipx inject で入れたはずなのに見つからない。原因は、runner にプリインストールされた pipx 環境と、自分で追加した venv が並存して、入れ先と実行先がズレていたことでした。
先に結論:私が動かした runner では、Ansible が ansible-core という pipx venv に入っていました。自分で pipx install ansible すると別の venv ができ、boto3 をそっちに入れても、実際に動く ansible-core 側には入っていない——これが原因でした。
環境
- GitHub Actions(ubuntu-latest / GitHub-hosted runner)
- Ansible(runner にプリインストール)+
amazon.aws.aws_ssm接続プラグイン - pipx
- AWS SSM 経由で EC2 を構成
(runner の中身は更新されるので、以下は私が実行した時点の runnerでの話として読んでください。)
症状
最初のタスクで、こう落ちました。
TASK [deploy_app : Install Amazon Corretto] ...
Failed to import the required Python library (boto3) on runnervm...'s Python
/opt/pipx/venvs/ansible-core/bin/python. ... If the required library is installed,
but Ansible is using the wrong Python interpreter, ...
注目したのは、boto3 を探しているのが /opt/pipx/venvs/ansible-core/bin/python だという点です。エラー文自身が「ライブラリは入っているのに、Ansible が違う Python インタプリタを使っているのでは?」とヒントをくれていました。
仕込んでいた手順(効かなかった)
導入ステップにはこう書いていました。
pipx install --include-deps ansible
pipx inject ansible boto3 botocore
「Ansible を入れて、その venv に boto3 を inject」——自分では筋が通っているつもりでした。
原因:boto3 を入れた venv と、実際に動く venv が違った
決め手は、pipx install の出力にありました。
⚠️ File exists at /opt/pipx_bin/ansible-playbook and points to
/opt/pipx/venvs/ansible-core/bin/ansible-playbook, not
/opt/pipx/venvs/ansible/bin/ansible-playbook. Not modifying.
...
injected package boto3 into venv ansible
整理するとこうでした。
- 私の runner では、Ansible が
ansible-coreという venv に入っていて、ansible-playbookコマンドの実体(symlink)はansible-core側を指していた。 - 自分の
pipx install ansibleは、ansibleという別名の venv を新たに作った。でもansible-playbookの symlink は既に存在するので、pipx はNot modifying(書き換えない)と言って、ansible-coreを指したまま。 -
pipx inject ansible boto3は、ansible(動いていないほう)の venv に boto3 を入れた。
つまり、実際に動く ansible-core には boto3 が無く、boto3 は使われない ansible 側に入っていた。これは GitHub runner 固有というより、pipx の複数 venv と PATH / symlink の組み合わせで起きるズレです。amazon.aws.aws_ssm 接続を controller 側で動かすには、使っている Ansible の Python 環境に AWS SDK for Python(boto3 など)が必要なので、最初のタスクで import に失敗して落ちた、というわけです。「インストールしたのに ImportError」は、たいていこのインタプリタ違いでした。
解決:実際に動く venv に入れる
直し方はシンプルで、boto3 を ansible-core venv のほうに injectします。別名の venv を作る pipx install ansible は、二重 venv の原因になるだけなので外しました。
# runner は ansible を ansible-core venv でプリインストール済み。
# 実際に動くその venv へ boto3 を入れる。
pipx inject ansible-core boto3 botocore
あわせて、amazon.aws.aws_ssm は ansible-core 単体には含まれないため、amazon.aws collection の導入も別途必要です。
ansible-galaxy collection install amazon.aws
これで ansible-playbook が指す ansible-core の Python に boto3 が入り、SSM 接続のタスクが通るようになりました。
「どの venv に入れるか/本当に入ったか」を確かめたいときは、コマンドの指し先を見るのが確実です。
which ansible-playbook
readlink -f "$(which ansible-playbook)" # 実体がどの venv を指すか
ansible-playbook --version
# その venv の python に boto3 が入ったか(パスは環境依存なので一般化)
"$(dirname "$(readlink -f "$(which ansible-playbook)")")/python" \
-c "import boto3; print(boto3.__version__)"
教訓
- 依存は「実際に Ansible が動く Python」に入れる。インストール先と実行先がズレると、入れても使われない。
- 私の runner では Ansible は
ansible-corevenv。自分でpipx install ansibleすると別 venv ができ、ansible-playbookはansible-coreを指したまま(Not modifying警告がサイン)。runner の構成は変わりうるので、指し先は毎回確認する。 -
amazon.aws.aws_ssmは ansible-core 単体に含まれない。collection 導入+controller 側の Python に boto3 が要る。 - 「入れたのに ImportError」は、まずインタプリタ/venv 違いを疑う。
pipx listとreadlink -f $(which ansible-playbook)で確認。
おわりに
最初はネットワークやコレクションの入れ忘れを疑ったのですが、犯人は「boto3 を入れた場所が違った」という、なんともシンプルなものでした😅 Not modifying の警告をちゃんと読んでいれば一発だったな、と反省。同じく「入れたのに見つからない」で固まった人の手がかりになれば嬉しいです🙌
参考
- pipx公式:
inject/list(独立した venv へ依存を追加する仕組み)
https://pipx.pypa.io/stable/docs/ - Ansible公式:
amazon.aws.aws_ssm接続プラグイン(collection 要件・boto3)
https://docs.ansible.com/ansible/latest/collections/amazon/aws/aws_ssm_connection.html - GitHub: runner-images(Ubuntu runner にプリインストールされるソフトウェア)
https://github.com/actions/runner-images