🛠️

Go実装のAWS-CLI代替品 awslim - fujiwara-ware 2024 day 17

2024/12/17に公開

この記事は fujiwara-ware advent calendar 2024 の17日目です。

awslim とは

https://github.com/fujiwara/awslim

awslim は Go で実装された AWS CLI のようなものです。といっても、AWS CLI と100%の互換機能を持っているわけではありません。

より正確な表現としては、「AWS SDK Go v2 のクライアント機能をCLI化したもの」というところでしょうか。

なぜ作ったのか

AWS CLI は便利ですが、起動が遅いという大きな不満がありました。Python で実装されていることもありますが、なぜか初期化にとても時間が掛かります。具体的には特にAPI呼び出しも何もしない aws --version でも0.7秒程度掛かります。

手元で使うだけならばそこまで問題でもないのですが、例えば「あるサービスのメトリクス (CloudWatchには存在しないが AWS CLI では取得できる) を定期的に取得する」ような自動化処理を書いた場合、その初期化時間がボトルネックになります。しかもそのような処理は 1 vCPU もないような環境 (0.25 vCPU の ECS タスクやメモリ割り当てが小さい Lambda など) で動かすことも多く、その場合には起動だけで3秒以上掛かることがあります。これはとてもストレスですね。

自分はそのような場合、Go の aws-sdk-go-v2 を使って都度専用のコマンドを書いていました。しかし、毎回同じようなコードを書くのも面倒ですし、毎回ビルドも必要になります。もう少し汎用的に解決できないものか……と考えた結果、awslim ができました。

使い方

awslim は AWS SDK Go v2 の各 AWS サービスに対応するクライアントをそのまま使用し、SDK のメソッドを CLI から呼び出せるようにしています。

コマンドの呼び出し形式は awslim サービス名 メソッド名 [メソッドへの引数] です。例えば、STS の GetCallerIdentity メソッドを呼び出す場合は以下のようになります。

$ awslim sts get-caller-identity

上記のコマンドは以下のような Go のコードと等価です。引数を省略するとデフォルト値として {} が使われます。

client := sts.NewFromConfig(cfg)
out, err := client.GetCallerIdentity(ctx, &sts.GetCallerIdentityInput{})

SDK の呼び出し結果 (ここでは var out *GetCallerIdentityOutput) は JSON にシリアライズされて表示されます。

{
  "Account": "012345678901",
  "Arn": "arn:aws:iam::012345678901:user/fujiwara",
  "UserId": "AIDAJ3OGXXXXXXXXXXXX",
  "ResultMetadata": {}
}

呼び出しに入力が必要なコマンドの場合、引数で JSON 文字列で指定します。例えば、ECS の ListTasks メソッドを呼び出す場合は以下のようになります。

$ awslim ecs list-tasks '{"Cluster":"default"}'

このコマンドは以下の Go のコードと等価です。

client := ecs.NewFromConfig(cfg)
out, err := client.ListTasks(ctx, &ecs.ListTasksInput{
  Cluster: aws.String("default"),
})

入力の JSON をコマンドラインから指定するのはちょっと使いづらいですね。入力の型のトップレベルのキーで、かつ文字列型の値については、コマンドラインオプションで指定できるようになっています。

$ awslim ecs list-tasks --Cluster default  # {"Cluster":"default"} と等価

文字列以外の値 (数値、真偽値、配列など) は JSON で指定する必要があります。JSON ではエスケープが必要な文字が多いので、Jsonnet も使えるようになっています。

$ awslim ec2 describe-instances '{"InstanceIds": ["i-0123456789abcdef0"]}' # JSON
$ awslim ec2 describe-instances '{InstanceIds: ["i-0123456789abcdef0"]}' # Jsonnet

どれぐらい速いのか

README にも書いていますが、awslim は起動が速いのが特徴です。aws --version で0.7秒程度掛かる AWS CLI と比べて、awslim は 0.1 秒程度で起動します。[1]

また、awslim は特定の AWS サービスのみに対応したコマンドを生成できます。例えば ec2, ecs, ecr, lambda だけが使えるコマンドを生成できます。この場合、awslim は更に高速に起動し、メモリフットプリントも小さくなります。自動化処理に使う場合、AWS の全サービスのクライアントが必要になることはまずないでしょう。必要なサービスのみが使えるバイナリを生成することで、サイズを小さくし、起動時間を短縮できます。

特定サービス用バイナリのビルド方法はドキュメントを参照してください。複数の方法がありますが、いずれも簡単にビルドできるようになっています。

例えば Docker を使って、ecs, firehose, s3 だけを使えるバイナリをビルドする場合は以下のようにします。

$ docker run -it -e AWSLIM_GEN=ecs,firehose,s3 ghcr.io/fujiwara/awslim:builder
$ docker cp $(docker ps -lq):/app/awslim .

これでコンテナ上でビルドされた awslim バイナリがローカルにコピーされます。

面白い使い方

SDK のドキュメントへのリンクをみる

awslim は aws-sdk-go-v2 のラッパー的なコマンドであるため、ドキュメントは aws-sdk-go-v2 のものを参照できるようになっています。

$ awslim ec2 describe-instances help
See https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ec2#Client.DescribeInstances

引数に help を指定すると、対応する SDK ドキュメントへのリンクが表示されます。これにより、awslim (aws-sdk-go-v2) で使えるコマンドの詳細な使い方を知ることができます。

Go で SDK を使ったコードを書いている際に、どのような引数があるのか、どのような値を取るのかを知りたい場合にドキュメントへのショートカットとして使えて地味に便利です。

--follow (-f) オプション

AWS API のいくつかは、全てのデータを取得するためにはページネーションを使う必要があります。例えば、S3 の ListObjectsV2 は最大 1000 件のオブジェクトを返しますが、それ以上のオブジェクトがある場合は NextContinuationToken が返されます。この場合、NextContinuationToken の値を ContinuationToken に指定して再度リクエストを送ることで、次のページのオブジェクトを取得できます。

これを手動でやるのは面倒ですが、awslim は --follow-next オプションを使うことで自動的にページネーションを行えます。[レスポンスのトークンが含まれるフィールド名]=[リクエストでトークンを設定するフィールド名] という形式で指定します。

awslim s3 list-objects-v2 でページネーションを行う例は以下のようになります。

$ awslim s3 list-objects-v2 --Bucket my-bucket \
  --follow-next NextContinuationToken=ContinuationToken

トークンのフィールド名が入力と出力で同じ API では、入力フィールド名を省略できます。

$ awslim ecs list-tasks --follow-next NextToken

これを AWS CLI で自動的に行うことはできません。shell script などでなんらかの wrapper を書いてやる必要があります。これは awslim の便利なところですね。

まとめ

awslim は AWS SDK Go v2 のクライアントを CLI 化した簡易的な AWS CLI の代替です。起動が速いのが特徴で、特定の AWS サービス用のバイナリをビルドすることで、さらに高速に、メモリフットプリントを小さくすることができます。

公式の AWS CLI が Go 実装になって起動が速くならないものかとずっと思っているのですが、それが実現されるまでの間の代替として、自動化処理のための軽量な CLI として awslim を使ってみてください。

それでは、明日もお楽しみに!

関連資料

https://sfujiwara.hatenablog.com/entry/2024/05/27/091630

https://speakerdeck.com/fujiwara3/layerx-dot-go-number-1

https://speakerdeck.com/fujiwara3/number-kamakurago

脚注
  1. ただし、バイナリのサイズやメモリ使用量は awslim の方が大きくなっています。 ↩︎

Discussion