LoginSignup
1
0

More than 5 years have passed since last update.

LambdaでElastiCacheサーバのprimaryとreplicaサーバのIPを取得してみよう

Posted at

Introduction

概要

AWSのLambda上で、ElastiCache(Redis)サーバのうち、primaryとreplicaのそれぞれのIPアドレスを取得してみます。

前提

  • Redis用ElastiCacheを使用する
  • クラスターモードはOFFである(つまりクラスターは使用しない)
  • Multi AZによる自動フェイルオーバーを有効にしている
  • 負荷分散の目的で、primaryサーバと、複数のreplicaサーバが存在する

動機(一つ目)

primaryサーバに障害が発生したとき、自動フェイルオーバーでreplicaサーバが自動的にprimaryサーバに昇格するのはとても便利!
しかも、primaryエンドポイントの名前にアクセスする限りは、ユーザはフェイルオーバーを意識する必要が無い (自動的にprimaryサーバを見てくれる)。

でも、replicaサーバにはそんな仕組みが無いので、primaryに昇格したサーバや、障害で切り離されたサーバを意識する必要があります。

動機(二つ目)

EC2から短期間に大量の名前問い合わせを行うと、DNSがロックアウト状態になってエラーを返すようになってしまうようです
(条件等は未確認だが、事実、現象として発生しました)。

したがって、上記のような状態が発生する環境下では、クライアントが都度問い合わせを行うのではなく、ある程度まとめて問い合わせを行うことでDNSへのアクセスを減らす必要があります。

対応

上記の二つの動機を満たすため、ElastiCacheのprimaryとreplicaサーバの状態を随時更新し、また、名前解決を行うlambdaを用意することにしたのです。

Lambda

言語

今回はPython3系を使用しました (Python 3.6)。

ロール

ElastiCacheの情報を読み込む必要がありますので、Lambdaには、AmazonElastiCacheReadOnlyAccess権限を含むロールを持たせる必要があります。

プログラム

lambda_function.py
import boto3
import socket

# 対象となるElastiCacheのクラスター名
CLUSTER_NAME='your-redis-cluster-name'

def get_elasticache_info():
    # boto3のElastiCacheクライアントを取得
    cache_client = boto3.client('elasticache')

    # describeReplicationGroupsコマンドの発行
    info = cache_client.describe_replication_groups(ReplicationGroupId=CLUSTER_NAME)
    if 'ReplicationGroups' in info:
        if 'NodeGroups' in info['ReplicationGroups'][0]:
            # コマンドの結果を解析
            return parse_node_groups(info['ReplicationGroups'][0]['NodeGroups'][0])
    raise Exception('Unexpected AWS result')

def parse_node_groups(group):
    if 'NodeGroupMembers' not in group:
        raise Exception('No Node Group Members')

    primary_node_end_point = None
    replica_node_end_point = []

    # describeReplicationGroupsコマンド内のNodeGroupMembersを解析    
    for cluster in group['NodeGroupMembers']:
        if 'ReadEndpoint' not in cluster or 'CurrentRole' not in cluster:
            raise Exception('Unexpected node group member')

        # クラスターのendpointを取得し、名前解決を行う
        end_point = lookup(cluster['ReadEndpoint'])
        current_role = cluster['CurrentRole']

        # クラスターの種類(primary/replica)毎に情報を保存      
        if 'primary' == current_role:
            primary_node_end_point = end_point
        elif 'replica' == current_role:
            replica_node_end_point.append(end_point)
        else:
            raise Exception('Unknown role')

    if primary_node_end_point is None:
        raise Exception('No primary node')

    return [
        primary_node_end_point,
        replica_node_end_point
    ]

def lookup(endpoint):
    if 'Address' not in endpoint:
        raise Exception('No address in endpoint')
    address = endpoint['Address']

    # 名前解決
    return socket.gethostbyname(address)

def lambda_handler(event, context):
    servers = get_elasticache_info()

    return servers    

このlambdaを呼び出すと、[primaryサーバのIPアドレス, [replicaサーバのIPアドレス, ....]]の結果を返します。

利用方法

API Gatewayと結びつけて、定期的にプログラムから呼び出すとか、そんな感じで(雑)。

TO-DO

  • フェイルオーバーの途中に呼び出したらどうなるか調べる
  • 名前解決のretryとかいる?
  • 直接関係ないけど、cloudwatchでフェイルオーバーやreplicaサーバの死亡を検知する方法も調べとく
  • 例外のエラーメッセージが適当(ごめんなさい)
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0