tl;dr

最新 Amazon Linux AMI の情報を返す Lambda Function を作ったけど死ぬほど遅かったので、その情報を S3 に置くことにして、さらに全リージョン分その処理を行う Function も作った。

ソースはここ

どうぞご利用ください

$ curl https://s3.amazonaws.com/latest-ami/.json
S3 じゃねーか!

はい。要は S3 に JSON を置いてるだけです。その JSON を自動で更新する仕組みを作ったという話です。理由は後述します。

これを書いてる時点で情報が取得できるリージョンは

です。

$ curl -s https://s3.amazonaws.com/latest-ami/us-west-2.json | jq
{
  "Region": "us-west-2",
  "RetrievedAt": "2016-06-29T20:01:02.197Z",
  "LatestImage": {
    "ImageId": "ami-7172b611",
    "ImageLocation": "amazon/amzn-ami-hvm-2016.03.3.x86_64-gp2",
    "State": "available",
    "OwnerId": "137112412989",
    "CreationDate": "2016-06-22T09:19:44.000Z",
    "Public": true,
    "ProductCodes": [],
    "Architecture": "x86_64",
    "ImageType": "machine",
    "SriovNetSupport": "simple",
    "EnaSupport": true,
    "ImageOwnerAlias": "amazon",
    "Name": "amzn-ami-hvm-2016.03.3.x86_64-gp2",
    "Description": "Amazon Linux AMI 2016.03.3 x86_64 HVM GP2",
    "RootDeviceType": "ebs",
    "RootDeviceName": "/dev/xvda",
    "BlockDeviceMappings": [
      {
        "DeviceName": "/dev/xvda",
        "Ebs": {
          "SnapshotId": "snap-d465048a",
          "VolumeSize": 8,
          "DeleteOnTermination": true,
          "VolumeType": "gp2",
          "Encrypted": false
        }
      }
    ],
    "VirtualizationType": "hvm",
    "Tags": [],
    "Hypervisor": "xen"
  }
}

こんな感じのデータが返ってきます。

使用例

$ aws ec2 run-instances --region $AWS_REGION --image-id `curl -s https://s3.amazonaws.com/latest-ami/${AWS_REGION}.json | jq -r '.LatestImage.ImageId'`

きっかけから実装まで

動機

EC2 Management Console に入って、 Launch って押すと一番上に Amazon Linux AMI が出てくるじゃないですか。

GUI だとこんなに目立つ位置にあるのに、 SDK や CLI から使おうとすると、最新版 Amazon Linux の AMI ID を簡単に取る方法が意外にないんですよね。

Linux AMI の検索 – AWS CLI を使用した AMI の検索を読んでも

コマンドラインパラメータを使用して、関心のある AMI のタイプのみを表示できます。

お客様または Amazon が所有しているパブリックな AMI を検索できます。

ニーズに合った AMI を見つけたら、その ID を書き留めます。

と、ピンポイントで見つける方法は書かれておらず。

じゃあ作ってみるか、と。

DescribeImages の遅さ

最初は Lambda で DescribeImages して、 API Gateway を前段に置けば終わりじゃんwwって思ってたんですよ。

で、 Function を作りました

しかし、動かしてみると、 DescribeImages があまりにも遅い。40秒とか50秒とか普通に掛かってしまいます。

星の数ほどある AMI から検索するわけで、仕方ないっちゃ仕方ないんだけれども、これでは遅すぎて API 的に使うことはできません。

そうだ、 S3 に置こう

そこでみんな大好き S3 です。

S3 に置く版 を作りました。変更点の少なさよ。

latest-ami というバケットを作ってそこに出力することにしました。ヴァージニアだと URL も短くていい感じ!

この Function を定期的に叩く CloudWatch Event を作れば一応それっぽくなるのですが、

  • input のリージョン名をそれぞれ変えて、 Event Target をリージョン数だけ登録する必要がある
  • 新しいリージョンがリリースされたときに手動で Event Target を追加する必要がある

という点が微妙です。そこで、

全リージョンぶん回し

する別の Function を書きました

Lambda Function から別の Lambda Fucntion を起動するときは invoke を使いますが、この時のポイントは InvocationType プロパティです。

ざっくり言うと Function A から Function B を呼ぶとき、

1.A で B の結果を受け取る必要がある → InvocationType: 'RequestResponse'
2.A で B の結果を受け取る必要がない → InvocationType: 'Event'

を指定します。 InvocationType: 'Event' を指定した場合、 A は B を起動したあと、 B の終了を待つことなく終了します。

今回の構図に当てはめると、

  • A: DescribeRegions して B を呼ぶだけ(ただしリージョンの数だけ呼ぶ)
  • B: DescribeImages する必要があるので死ぬほど遅い

ということで 2. がぴったりです。

CloudWatch Event に登録する Event Target はこの Function だけでよくなり、新リージョンのリリース時も対応不要になりました。めでたしめでたし。

現在、この Function を6時間ごとに起動するように設定しています。

雑感

  • 当然ながら定期実行による半静半動な API もどきなので、 CloudWatch Event と Lambda が確実に動いているとしても一定時間の遅延はある。しかしながら Amazon Linux という数ヶ月に1回更新される程度のものに限定しているので、これでもそこそこ実用的なのではないか
  • 今回は全リージョンぶん回しする Function に呼び出し先の Function を決め打ちしているけど、ここもeventから参照するようにすると 全リージョンでほげほげする系 で使い回しできそう
  • Lambda Function から Lambda Function を呼ぶパターンはまだまだ遊び甲斐がありそうだ
  • なんだかんだ言って S3 は庶民の味方だよなあ

元記事はこちら

最新 Amazon Linux AMI の情報を返す API もどきを作った