プライベートネットワーク内のデバイスにAWS Session Managerを経由してAnsibleでプロビジョニングする
ここしばらくリモートワークが続いてきて困るのが遠隔地にあるデバイスのモジュールのアップデート作業をどう行うか、というところです。 今回は AWS System Manager の Session Manager の機能と Ansible を組み合わせて遠隔地にあるプライベートネットワーク内のデバイスの構成管理の方法を紹介します。
遠隔地にあるプライベートネットワーク内のデバイスにプロビジョニングを行いたい
会社のような遠隔地に配置したデバイスに対して自宅内の開発用マシンから構成管理のスクリプトを実行したいと思います。イメージとしては以下です。
たいていの場合、遠隔地に配置されているデバイスはプライベートネットワーク内にあることが多いと思いますので、そう簡単に Ansible を実行することが(=SSH が)できる環境にありません。
エッジデバイスへの簡単なモジュール更新なら AWS Greengrass を使う方法も検討できますが、 真面目な構成管理には向かないので Ansible を実行できる方法を模索します。
環境情報
上記の構成での環境情報は以下です。
- PC(ローカルマシン)
- Mac Book Pro (Mac OS X
10.15.7
) - Ansible
2.9.9
- AWS CLI
2.1.16
- AWS Session Manager Plugin
- Mac Book Pro (Mac OS X
- Device(Ansible の実行ホスト)
- Jetson AGX Xavier (JetPack
4.4
) - AWS Systems Manager Session Manager
- Jetson AGX Xavier (JetPack
実施手順
AWS System Manager Session Manager を設定する
まず最初に AWS System Manager Session Manager の設定をします。 AWS System Manager Session Manager は踏み台サーバの代替として使われることも多いのですが、仮想サーバ(EC2)へのアクセスだけでなく、設定を行えばオンプレミスサーバにもセッションを張ることができます。オンプレミスサーバと言いいましたが、AWS 提供のモジュールが OS のディストリビューションをサポートしていれば、エッジデバイスにもインストールすることができます。
なお、JetPack は AWS 的にはサポートしていませんが、Ubuntu の兄弟だし動くだろう、とやってみたら動きました。
設定手順については公式ドキュメント を参照しましょう。手厚く書かれています。
おおまかな流れだけ紹介しておくと
- Device(Ansible の実行ホスト)にアタッチする IAM Instance Profile を作成する
- System Manager 自体の設定を行う
- PC(ローカルマシン)にAWS Session Manager Plugin をインストールする
- Device(Ansible の実行ホスト) にモジュールをインストールし、System Manager にインスタンスを登録する
AWS Session Manager でポートフォワーディングのセッションを開く
PC(ローカルマシン)で以下のコマンドを実行します。Session Manager を経由してローカルマシンからデバイスにポートフォワードできるようになります。 --target
オプションには Session Manager に登録したデバイスの Instance ID を指定しましょう。
また、ここではローカルマシンの 9090
ポート を デバイスの 22
ポートに転送しています。
1aws ssm start-session \
2 --target mi-xxxxxxxx \
3 --document-name AWS-StartPortForwardingSession \
4 --parameters '{"portNumber":["22"],"localPortNumber":["9090"]}'
Playbook ファイルを作成し Ansible を実行する
次に Ansible の Playbook ファイル一式を作成します。タスクの内容はどんなものでも構いません。
ここで大事なのはインベントリーの指定方法です。
プロビジョニングターゲットを localhost
の 9090
ポートにしています。
1[jetson]
2localhost
3
4[jetson:vars]
5ansible_port=9090
6ansible_user=XXXXXXX
7ansible_ssh_pass=XXXXXX
8ansible_become_password=XXXXXX
9ansible_python_interpreter=/usr/bin/python3
ここまで来ればポートフォワードを経由してローカルマシンからデバイスに対して ansible-playbook
コマンドが実行できるはずです。
どうしてポートフォワーディグするのか
ここでは、どうしてポートフォワーディングセッションを先に開いておくのかを説明します。
AWS Session Manager の手順に準じてローカルマシンの設定を行うと、 ~/.ssh/config
に ProxyCommand
を記載する設定が紹介されています。
1Host i-_ mi-_
2ProxyCommand sh -c "aws ssm start-session \
3 --target %h \
4 --document-name AWS-StartSSHSession \
5 --parameters 'portNumber=%p'"
この設定 は ssh user@mi-xxxxxxx
のように Instance ID 指定での SSH ログインを実現できるため、とても便利なのです。
しかしここで指定されている AWS-StartSSHSession
は呼び出される度に新規で ローカルマシン -> Session Manager -> デバイスのセッションを開く だけ です。 明示的に aws ssm terminate-session を呼び出すか、セッションがアイドルタイムアウトするまで SSH 接続を維持するためのセッションや session-worker は残り続けます 。
例えば、上記の ProxyCommand
設定が入った状態で、以下のように接続ホストを Instance ID で指定するとしましょう。
すると、比較的長い Ansible タスクでは実行の途中で Connection reset by peer
エラーが発生します。
1[jetson]
2mi-xxxxxxxx
3
4[jetson:vars]
5ansible_user=XXXXXXX
6ansible_ssh_pass=XXXXXX
7ansible_become_password=XXXXXX
8ansible_python_interpreter=/usr/bin/python3
Ansible 実行ホスト側の /var/log/amazon/ssm/errors.log
ファイルを確認すると too many open files
が出力されていることがわかります。また、Ansible 実行中に Ansible 実行ホスト側の sshd
プロセスがどんどん増えていることも確認できます。
1ERROR [NewFileWatcherChannel @ filechannel.go.79] [ssm-session-worker] [xxxxxxx-07679fe2fa5825aa8] \
2filewatcher listener encountered error when start watcher: too many open files
3ERROR [createFileChannelAndExecutePlugin @ main.go.105] [ssm-session-worker] \
4[xxxxxxx-07679fe2fa5825aa8] failed to create channel: too many open files
5ERROR [handleSSHDPortError @ port.go.292] [ssm-session-worker] \
6[xxxxxxx-0590c3ea63794d55f] [DataBackend] [pluginName=Port] \
7Failed to read from port: read tcp 127.0.0.1:41294->127.0.0.1:22: use of closed network connection
まとめ
Session Manager を経由して Ansible コマンドを実行する時はポートフォワーディングのセッションを開いてから、localhost に対して実行すると良い。