継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

Lambda を使って Elasticsearch のエイリアスとインデックスを更新する

タダです.

Amazon OpenSearch Service のインデックス再設計に関わった際に,次の運用上の懸念がありました.

  • インデックスは月ごとに生成し,生成したインデックスに対してアプリケーションから見た時のエイリアスとして参照用と書き込み用の2つを貼りたい
  • 前月以前のインデックスにも参照用エイリアスを残す形で運用したい

上記の運用を実現するために Lambda で上記の処理を行えないか検証したためその内容をこの記事にまとめます.

Lambda の権限と実行コード

Lambda の権限

Lambda の権限は検証として下記の IAM を付与しています.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:ap-northeast-1:xxx:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:ap-northeast-1:xxx:log-group:/aws/lambda/xxx:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "es:ESHttpPost",
                "es:ESHttpGet",
                "es:ESHttpPut",
                "es:ESHttpDelete"
            ],
            "Resource": "*"
        }
    ]
}

実行コード

コードは Python 3.9 で確認しています.

import boto3
import os
from requests_aws4auth import AWS4Auth
from elasticsearch import Elasticsearch, RequestsHttpConnection
from urllib.request import Request, urlopen
from datetime import datetime, timedelta, timezone
from dateutil.relativedelta import relativedelta

ENDPOINT = os.environ['ES_ENDPOINT']
REGION = os.environ['ES_REGION']
SERVICE = 'es'
CREDINTIAL = boto3.Session().get_credentials()
AWSAUTH = AWS4Auth(CREDINTIAL.access_key, CREDINTIAL.secret_key, REGION, SERVICE, session_token=CREDINTIAL.token)
JST = timezone(timedelta(hours=+9), 'JST')

def lambda_handler(event, context):
    client = Elasticsearch(
        hosts = [{'host': ENDPOINT, 'port': 443}],
        http_auth = AWSAUTH,
        use_ssl = True,
        verify_certs = True,
        connection_class = RequestsHttpConnection
    )

    write_alias_name = "test-writer-alias"
    read_alias_name = "test-reader-alias"
    index_prefix = "test-index-"
    new_index = index_prefix + datetime.now(JST).strftime('%Y%m')
    old_index = index_prefix + datetime.strftime(datetime.today() - relativedelta( months = 1 ), '%Y%m')
   
    client.indices.update_aliases(
            body={
                "actions": [
                    {"remove": {"alias": write_alias_name, "index": old_index}},
                    {"add": {"alias": write_alias_name, "index": new_index}},
                    {"add": {"alias": read_alias_name, "index": new_index}},
                ]
            }
        )

Lambda の実行結果確認

Lambda 実行後の挙動を確認していきます.実行前に予め前月のインデックス test-index-202110エイリアスとして test-reader-ailias(参照用エイリアス)と test-writer-alias(書き込み用エイリアス)を作った状態で確認しています.本記事は2021年の11月に書いているので,インデックスとして test-index-202111エイリアスが書き込み,参照が貼られ,前月にも参照用エイリアスが残った状態にできていたら期待通りです.

Lambda実行前

% curl -XGET 'https://xxx.ap-northeast-1.es.amazonaws.com/_alias?pretty=true'
{
  ".kibana_1" : {
    "aliases" : {
      ".kibana" : { }
    }
  },
  "test-index-202110" : {
    "aliases" : {
      "test-reader-alias" : { }, 
      "test-writer-alias" : { }  
    }
  }
}

Lambda を実行してみたところ期待通りの結果が得られました.

Lambda実行後

% curl -XGET 'https://xxx.ap-northeast-1.es.amazonaws.com/_alias?pretty=true'
{
  ".kibana_1" : {
    "aliases" : {
      ".kibana" : { }
    }
  },
  "test-index-202110" : {
    "aliases" : {
      "test-reader-alias" : { }
    }
  },
  "test-index-202111" : {
    "aliases" : {
      "test-reader-alias" : { },
      "test-writer-alias" : { }
    }
  }

まとめ

Elasticsearch のインデックスとエイリアスの運用上の懸念に対して Lambda を使ったプローチで検証したのをまとめました.次は,インデックス再設計するためのインデックス移行について書いていければと思います.

関連記事

sadayoshi-tada.hatenablog.com