4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS ルーティングテーブルの書き換えによるAZまたぎでのフェイルオーバ

Last updated at Posted at 2021-01-20

オンプレミスとawsをダイレクトコネクト接続するにあたり、ルータのインスタンスを作成しました。awsではVRRPなどを使って冗長化することができませんが、lambdaを使ってawsのルーティングテーブルを書き換えることで、似たようなことが実現できます。

この内容は、以下のブログを参考にしています。
Making Application Failover Seamless by Failing Over Your Private Virtual IP Across Availability Zones

以下のように、AZを超えた切り替えを目指します。

image.png
cloudwatchが検知する、インスタンスの"stopping"イベントをトリガーにして切り替えることにしました。cloudwatchの設定で、ステータスチェック(ping)に失敗すると、インスタンスを再起動するようにしてあります。

image.png
lambdaは以下のように、cloudwatchをトリガにして起動する関数を設定します。
image.png

RouterFailover_Fn

import json, boto3
from boto3.session import Session

def get_instance(response, instance_id):
  for reservation in response["Reservations"]:
    for instance in reservation["Instances"]:
      if instance["InstanceId"] == instance_id:
        return instance

def get_iface(instance):
  if instance.get("NetworkInterfaces", False):
      return instance["NetworkInterfaces"][0]


def lambda_handler(event, context):
    host1eni = 'eni-XXXXXXXXXXXXXX'   #ルータ1のeniをセット
    host2eni = 'eni-YYYYYYYYYYYYYY'   #ルータ2のeniをセット
    vpcid = 'vpc-qqqqqqqqq' #vpcをセット
    region = 'ap-northeast-1' #リージョンをセット
    print(event)
    my_instance_id = event['detail']['instance-id']
    session = boto3.session.Session()
    ec2client = session.client('ec2', region_name = region)
    response = ec2client.describe_instances()
    instance = get_instance(response, my_instance_id)
    iface = get_iface(instance)
    print(iface['NetworkInterfaceId'])
    ifid = iface['NetworkInterfaceId']
    if ifid == host1eni:
        altifid = host2eni
    elif ifid == host2eni:
        altifid = host1eni
    else:
        return
    route_table = ec2client.describe_route_tables(Filters=[{'Name': 'vpc-id','Values': [vpcid,]},])['RouteTables']
    for iter in route_table: 
        for each_route in  iter['Routes']: 
            try:
                if each_route['NetworkInterfaceId'] == ifid:
                    try:
                        print (iter['RouteTableId'] + " Route Deleting started for "+str(each_route['DestinationCidrBlock'])+" with eniid "+ifid )
                        ec2client.delete_route(DestinationCidrBlock=each_route['DestinationCidrBlock'],RouteTableId=iter['RouteTableId'])
                        print (iter['RouteTableId'] + " Route Deleted for "+str(each_route['DestinationCidrBlock'])+" with eniid "+ifid )
                    except Exception as e:
                        print (e)
                    try:
                        print (iter['RouteTableId'] + " Route creating started for "+str(each_route['DestinationCidrBlock'])+" with eniid "+altifid)
                        ec2client.create_route(DestinationCidrBlock=each_route['DestinationCidrBlock'],NetworkInterfaceId=altifid,RouteTableId=iter['RouteTableId'])
                        print (iter['RouteTableId'] + " Route created for "+str(each_route['DestinationCidrBlock'])+" with eniid "+altifid)
                    except Exception as e:
                        print (e)
            except:
                continue

ルータ1のインスタンスを停止させると、対象のeniが書き換わります。
書き換わる前
image.png
書き換わった後
image.png

#最後に
awsのロードバランサはIPレベルでの切り替えができないので、ルータを切り替えたいような
場合に便利なソリューションです。
参考元のブログのように、cloudwatchの検知対象を広げれば、プロセスやサービスレベルのダウンなどのイベントでの切り替えが可能になります。
高度なHAクラスタの仕組みを導入しなくとも、少ないコードだけで実現できるのもリーズナブルで良いのではないでしょうか。

4
4
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
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?