Elastic Beanstalkのインスタンスやボリュームのタグをデプロイ時に更新


なぜ

  • .ebextensionsで環境プロパティからインスタンスやボリュームにカスタムタグを追加している。管理ルールが変わりタグ更新が必要になったため、何気なく更新したらデプロイに失敗した。目的の環境プロパティをタグ以外の処で参照していた。
  • シンプルに.ebextensionsを修正する何のことでもないことだが、保守担当に説明すると承認が必要のこと。これは時間がかかりそうだ。

遠回り

改善策でサーバ構成もプログラムも遠回りしてはいけない。根本原因をつぶすのがベストだ。しかし、
承認とリリースまで1~2ヵ月はかかりそうなことと、以前からLambdaのイベントトリガーを試してみたかったことがあり今回は遠回りをする。

Lambda

  • LambdaのRoleは事前登録しておく
Python3.8
import json
import boto3

def lambda_handler(event, context):
    evt = str(event)
    print(evt) #json parseできない文字列が飛んでくるため

    # eb-app, new-tag, old-tag //環境変数の利用をお勧め
    apps = [
        ["app1", "newapp1", "oldapp1"],
        ["app2", "newapp2", "oldapp2"],
        ["app3", "newapp3", "oldapp3"],
        ...
        ...
    ]
    newTag = ''
    oldTag = ''
    client = boto3.resource('ec2', region_name='ap-northeast-1')

    if evt.find('EbUpdateEnvironmentCron') > 0:
        #from cloudwatch cron EBインスタンスはすべてチェックしたい時
        #EbUpdateEnvironmentCron: イベント名

        for app in apps:
            newTag = app[1]
            oldTag = app[2]

            update_ec2tag(client, oldTag, newTag)

    else:
        #from cloudtail event
        #更新されたEB環境に絞る        

        for app in apps:
            if evt.find(app[0]) > -1:
                newTag = app[1]
                oldTag = app[2]
                break

        if len(newTag) > 1:
            update_ec2tag(client, oldTag, newTag)
        else:
            print('target not ')

    return {
        'statusCode': 200,
        'body': json.dumps('OK')
    }


#ec2タグ更新
def update_ec2tag(client, oldTag, newTag):
    print(str(newTag))

    newTags = [{'Key': 'Project', 'Value': newTag}]

    instances = client.instances.filter(
        Filters = [{'Name': 'tag:Project', 'Values': [oldTag]}]
    )

    for instance in instances:
        hasTag = 0
        for tag in instance.tags:
            if tag['Key'] == 'Project': #調整対象タグ
                hasTag += 1
            elif tag['Key'] == 'elasticbeanstalk:environment-id': #Elastic Beanstalkのインスタンのみにしたい
                hasTag += 1

        if hasTag == 2:
            client.create_tags(
                Resources=[instance.instance_id],
                Tags=newTags
            )

            #print(instance.instance_id)
            update_voltag(instance, newTags)

#volumeタグ更新
def update_voltag(instance, ptags):
    instId = instance.instance_id
    print(instId)

    for vol in instance.volumes.all():
        print('Updating tags for {}'.format(vol.id))
        vol.create_tags(Tags=ptags)

CloudWatch Events Rule

  • 名前: EbUpdateEnvironment -> Lambdaのトリガーに追加
  • イベントパターン
{
  "source": [
    "aws.elasticbeanstalk"
  ],
  "detail-type": [
    "AWS API Call via CloudTrail"
  ],
  "detail": {
    "eventSource": [
      "elasticbeanstalk.amazonaws.com"
    ],
    "eventName": [
      "UpdateEnvironment"
    ]
  }
}

おまけ

イベントトリガーをSQSにすると

def lambda_handler(event, context):
    for record in event['Records']:
        pval = record["body"] #ここにElastic Beanstalkのアプリ名
        print(str(pval))

イベント発行
例え、zabbixで環境更新を検知したとしてアクションで設定
Elastic Beanstalkのデプロイ -> zabbixでアイテムで更新検知&アクション(SQSキュー生成) -> Lambda実行
zabbixアイテム例:https://qiita.com/mkawanee/items/66fb507ab9bd53638f58

/usr/bin/aws sqs send-message --queue-url https://sqs.ap-northeast-1.amazonaws.com/12345678/infra-modify-tag \
--region ap-northeast-1 --profile awsX --message-body {EVENT.NAME}

参照

追記

CloudWatch EventでEBの環境名を拾って判断しているが、APIからはEnvironmentIdを使うため、環境名が入らない。EnvironmentNameをパラメータに追加すると、「'errorCode': 'InvalidParameterValueException'」になる。
とにかくログに環境名があればよいのでDescriptionに入れてみたらOK!

C#
var req = new UpdateEnvironmentRequest
{
    EnvironmentId = "e-xxxxxx",
    Description = "env-name-xxx",
    OptionSettings = {options}
};