弁護士ドットコム株式会社 Creators’ blog

弁護士ドットコムがエンジニア・デザイナーのサービス開発事例やデザイン活動を発信する公式ブログです。

AWS OpenSearch のマネージドサービス更新を自動化する方法

ブログタイトル画像。右側に「AWS OpenSearch のマネージドサービス更新を自動化する方法」と記載され、左側に赤ちゃんがノートパソコンを触っており、AWS OpenSearchの設定を行っている様子。 この記事は、弁護士ドットコム Advent Calendar 2024 の 22 日目の記事です。

こんにちは。弁護士ドットコム 技術戦略本部でSREをしています山崎です。

今年の初めに3ヶ月間の育休から復帰しました。長期休暇は社会人として珍しい経験だったため、新鮮な気持ちでした。

背景

復帰後に、子どもが生まれる前と比べて仕事に集中できる時間(工数)が大きく異なることを実感しました。

当社のエンジニアは、チームの働き方に合わせて柔軟にリモートワークを選択できる環境が整っています。

しかし、家で仕事をしていると、子どもがいつ自分を求めてくるか分からない状況があります。 このため、できるだけ作業を自動化・効率化したいと考えるようになりました。

また、運用上の課題として、メンテナンスが早朝作業になることが挙げられます。 早起きや夜間作業が苦手な方も多いのではないでしょうか。

今回は、その一環として取り組んだAWSサービス更新の自動化について紹介します。

マネージドサービス更新

当チームが管理しているAWSリソースには、RDS、OpenSearch、ElastiCacheなどがあります。 これらのリソースに月次でアップデートがないか確認し、必要に応じて更新しています。

この作業は、新機能を利用するためというよりも、セキュリティ向上のための対応です。

AWSコンソールから確認することもあれば、Slackと連携して通知させる仕組みも活用しています。

AWSアップデートのSlack通知

元々はAWSコンソール上から作業していましたが、作業事故防止や手順書作成の工数削減の観点から、AWS CLIを使った作業に移行しました。

例えば、以下のようなコマンドを利用します。

aws opensearch upgrade-domain --domain-name <ドメイン名> --target-version OpenSearch_2.7

しかし、手動実行は手間がかかり、手順書作成や作業ミスのリスクも伴います。

そこで、チーム内で自動化する方針を立てました。

OpenSerchのバージョン

OpenSearchには次の2種類のバージョンがあります。

・バージョン: 例として、OpenSearch 2.15のような形式で、更新頻度は少ないです。

・サービスソフトウェアのバージョン: 例として 、OpenSearch_2_15_R20240904-P4のような形式で、パッチ更新があり、1ヶ月に1回程度の頻度で更新されます。

どちらか片方のみ対応しても不十分なため、今回の自動化では両方のバージョンに対応するようにしました。

OpenSerchマネージドサービス更新の自動化

当社のシステム上、影響が少ないOpenSearchのアップデートを優先的に自動化しました。

検討した実装方法の例として、以下のものが挙げられます。

  • Health -> EventBridge -> Codebuild
  • Health -> EventBridge -> Lambda
  • Health -> EventBridge -> CDK

しかし、AWSサポートに問い合わせたところ、Healthを契機にアップデートを実施するのは難しいとの回答を得ました。

また、完全自動化するとSREチーム以外への周知が必要になるため、以下の構成で実現しました。

自動化の構成図

EventBridgeのスケジュール機能を使うことで、任意のタイミングで実行可能にしています。

アップデート実行時の通知について

Lambdaを使ったアップデート処理には、会社で利用している「Slack」への通知機能を実装しました。

Slack処理はこちら

SLACK_WEBHOOK_URL = os.environ['SLACK_WEBHOOK_URL']

# ログ設定
logger = logging.getLogger()
logger.setLevel(logging.INFO)

my_config = Config(
    region_name='ap-northeast-1'
)

client = boto3.client('opensearch', config=my_config)

def lambda_handler(event, context):
    if 'DomainName' in event:
        domain_name = event['DomainName']
        update_with_version_check(domain_name)
    else:
        print('対象のドメインが存在しません')

def slack(title, details):
    slack_message = {
        'username': "Lambda Execution Result",
        'text': title,
        'attachments': [
            {
                "color": "#36a64f",
                "text": details
            }
        ]
    }
    req = Request(SLACK_WEBHOOK_URL, json.dumps(slack_message).encode('utf-8'))
    try:
        response = urlopen(req)
        response.read()
        logger.info("Message posted to %s", SLACK_WEBHOOK_URL)
    except HTTPError as e:
        logger.error("Request failed: %d %s", e.code, e.reason)
    except URLError as e:
        logger.error("Server connection failed: %s", e.reason)

アップデート実行時に以下の情報が通知されます。

  • 対象ドメイン
  • アップデート前のバージョン
  • アップデート後のバージョン

Slack通知の例

通知の例を以下に示します。

  • バージョン更新がある場合: 対象のバージョンを含むメッセージ
  • バージョン更新がない場合: 「更新なし」のメッセージ

Slackを利用していない場合は、他の通知方法を適宜検討してください。

アップデート処理の内容

バージョンアップされると、ソフトウェアバージョンも最新となる。 深く考える必要はないですが、バージョン更新された場合ソフトウェアバージョンの更新は必要ないです。

アップデート処理はこちら

# バージョン確認と更新処理
def update_with_version_check(domain_name):
    try:
        # バージョン確認
        response = invoke_opensearch_compatible_versions(domain_name)
        target_versions = response.get('CompatibleVersions', [{}])[0].get('TargetVersions')
        version = response.get('CompatibleVersions', [{}])[0].get('SourceVersion')
  
        if target_versions:
            target_version = target_versions[0]
            # バージョンがあるときバージョンアップ
            upgrade_opensearch_version(domain_name, target_version)
            print(f"OpenSearchのバージョンを {target_version} にアップグレードしています")
            message = f"OpenSearch 自動更新実行. 対象ドメイン: {domain_name} "
            message1 = f"Version: ['{version}'] -> {target_versions}"
            slack(message, message1)
        else:
            # バージョンがないとき、サービスソフトウェアの更新を実行
            get_update_status(domain_name)
            print(f"サービスソフトウェアの更新を確認しています")
    except Exception as e:
        print(f"エラーが発生しました: {e}")

# バージョン確認
def invoke_opensearch_compatible_versions(domain_name):
    client = boto3.client('opensearch')
    response = client.get_compatible_versions(DomainName=domain_name)
    return response

# バージョンアップグレード
def upgrade_opensearch_version(domain_name, new_version):
    client = boto3.client('opensearch')
    response = client.upgrade_domain(
        DomainName=domain_name,
        TargetVersion=new_version
    )
    print('Upgrading domain to ' + new_version + '...')

# 更新対象のドメインのサービスソフトウェア更新
def get_update_status(domain_name):
    try:
        response = client.describe_domain(DomainName=domain_name)
        sso = response['DomainStatus']['ServiceSoftwareOptions']
        if sso['UpdateStatus'] == 'ELIGIBLE':
            print('ドメイン [' + domain_name + '] は、バージョン ' +
            sso['CurrentVersion'] + ' からバージョン ' + sso['NewVersion'] + ' にサービスソフトウェアを更新することが可能です')
            update_domain(domain_name)
            message = f"OpenSearch 自動更新実行. 対象ドメイン: {domain_name} "
            message1 = f'SoftwareVersion: [' + sso['CurrentVersion'] + '] -> [' + sso['NewVersion'] + ']'
            slack(message, message1)
        else:
            print('現在、ドメインは更新の対象ではありません。')
            message = f"OpenSearch 自動更新実行. 対象ドメイン: {domain_name} "
            message1 = f"現在、ドメインは更新の対象ではありません"
            slack(message, message1)
    except ClientError as e:
        if e.response['Error']['Code'] == 'ResourceNotFoundException':
            print('指定されたドメインが見つかりませんでした。')
        else:
            print('エラーが発生しました: {}'.format(e))

# サービスソフトウェア更新
def update_domain(domain_name):
    response = client.start_service_software_update(DomainName=domain_name)
    print('ドメイン [' + domain_name + '] をバージョン ' +
    response['ServiceSoftwareOptions']['NewVersion'] + ' に更新しています...')

実行にはEventBridgeを採用しました。

定期実行として、月1回や3ヶ月に1回の頻度で設定するのが適切だと考えています。

自動化して感じたこと

SREに限らず、手動作業を自動化できることは大きな喜びです。

たとえ数分で終わる作業でも、毎日繰り返していると面倒に感じてしまうものです。

自動化を進める際には、エラー時の影響が少ない作業や、人が監視しなくてもよいタスクを選定する必要があります。

今回、早朝の手動作業が1つ減ったことで、非常に快適になりました。

おわりに

今回は毎月実施していた手作業を自動化した事例として、OpenSearchのサービス更新自動化について紹介しました。

RDSなど他のリソースも同様に自動化したい気持ちはありますが、バグや切り戻しを考慮すると、慎重な対応が必要だと感じています。

引き続き改善を進め、より洗練された解決策を追求し続けています。

この内容が少しでも参考になれば幸いです。