sorta kinda...

主にAWS関連ですが、これに限らずいろいろ勉強したことや思ったことを書いていきます。

最小限の手間で最新の AMI から EC2 インスタンスを起動したい

めんどくさいことは嫌いですがめんどくさい運用はもっと嫌いです、那須です。

AWS ではあまりないと思いますが、なんらかの事情で最新の AMI から EC2 インスタンスを起動しないといけない場合ってありますよね? オペレーションミスで必要なデータやファイルを削除してしまったり上書きしてしまったり。 ハードウェア障害等でかなりの確率で EC2 インスタンスを stop/start すれば復旧するのになぜか復旧できなかったり。 いろんなことが起こりえます。

その時に、最新の AMI ID ってどれだっけ? って探してる余裕があればいいですが、まあそんな精神的余裕はありません。 手順化されていて落ち着いて作業できる人はいいですが、私はまあ無理ですね。

で、どれが最新の AMI なのかをその時に探すよりは、事前に「これが現時点での最新の AMI ID やで!」ってすぐに分かるようにしておけばみんなハッピーになるんじゃないかと思って、その仕組みを考えてみました。

 

前提条件

AMI 作成は、AWS 標準のものではなくてサーバーワークスさんから提供されている Cloud Automator を使います(サーバーワークスさんいつもありがたく使わせていただいてます

 

全体的な流れ

ざっくり書くとこんな感じです。 AMI 作成の直後にその AMI ID をパラメータストアに入れれば 1 と 2 は同じコードに書けたりするんですが、Cloud Automator で AMI 作成対象をタグで制御してるので、どんくさいかもしれませんがこんな感じで分けました。

  1. Cloud Automator で定期的に AMI を作成する
  2. 特定の EC2 インスタンスの最新 AMI ID を Systems Manager のパラメータストアに入れる
  3. AMI ID をパラメータストアから取ってきて EC2 インスタンスを作成する CloudFormation テンプレートを準備する
  4. (いざという時に) CloudFormation テンプレートからスタック作成して EC2 インスタンスを作成(復旧)する

f:id:nasrinjp1:20190527232150p:plain

今回は上記の 2 と 3 の部分にフォーカスして書いていきますね。

 

最新 AMI ID を Systems Manager のパラメータストアに入れる

とりあえず入れるだけなので簡単です。 Cloud Automator がつけてくれるタグを基に、対象の EC2 インスタンスを指定してその最新 AMI ID を取得してパラメータストアに入れましょう。

この例では、Cloud Automator Source Instance Name タグの値が「ほんばんき」になっている AMI 一覧から作成日が一番新しい AMI ID を取得します。 その後、/prod/ami-id パラメータとしてさきほど取ってきた AMI ID を上書きして書き込みます。

これらの動きを Lambda で実装しましょう。

from logging import getLogger, INFO
import boto3
from operator import itemgetter

logger = getLogger()
logger.setLevel(INFO)

def lambda_handler(event, context):
    ec2_client = boto3.client('ec2')
    response = ec2_client.describe_images(
        Filters=[
            {
                'Name': 'tag:Cloud Automator Source Instance Name',
                'Values': [
                    'ほんばんき',
                ]
            },
        ],
        Owners=[
            'self'
        ]
    )
    image_details = sorted(response['Images'], key=itemgetter('CreationDate'), reverse=True)
    ami_id = image_details[0]['ImageId']
    logger.info(f'The Latest AMI ID is {ami_id}.')

    ssm_client = boto3.client('ssm')
    response = ssm_client.put_parameter(
        Name='/prod/ami-id',
        Value=ami_id,
        Type='String',
        Overwrite=True
    )
    logger.info('The Latest AMI ID is put to parameter store successfully.')

 

最新 AMI ID が入ったパラメータから値を取ってきて CloudFormation テンプレートで使う

↓このドキュメントの最後の方にある「AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> タイプ」の項目を参考に適当に作っています。

docs.aws.amazon.com

実際はパラメータを定義して柔軟に対応できるようにするべきですが、テンプレートが長くなるのでいい感じに読み替えてください。

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  AMIID:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /prod/ami-id

Resources:
  EC2:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: EC2Role
      ImageId: !Ref AMIID
      InstanceType: m5.large
      KeyName: honyalara
      SecurityGroupIds: 
        - sg-xxxxxxxxxxxxxxxxx
      SubnetId: subnet-xxxxxxxxxxxxxxxxx
      Tags:
      - Key: Name
        Value: リストアインスタンス

これでスタックを作成すると最新の AMI から EC2 インスタンスが作成されますよ。 ここまでの準備作業はそれなりにかかりますが、運用開始後の突発的な作業項目はかなり減らすことができますね。 何かあっても EC2 のコンソールは見なくてもよくて、CloudFormation からスタック作成するだけです。

 

運用が楽になるように設計しよう

運用は手順書を揃えたり自動化したりしますが、どうせ作るならオペミスが起こりにくい構造にしたいですよね。 今回は AMI にフォーカス当てましたが、他にもいろんな検討ポイントがあると思います。 AWS のサービスをうまく使って楽にかつオペミスが少なくなるような運用を心がけましょう。