サーバレスでNAT Gatewayの起動・停止を管理してみた【押忍!ソフト道場】
今回はAWSにおける、サーバレスを用いたNAT Gatewayの費用削減のノウハウを展開いたします。
ソフト道場の「SIerが目利きする。今日から使えるAWSレシピ」 第9回
- 2018年04月12日公開
はじめに
こんにちは。NTTテクノクロスの渡邉です。
今回はAWSにおける、サーバレスを用いたNAT Gatewayの費用削減のノウハウを展開いたします。
サーバレスとは
本稿ではサーバレスを、以下の特徴を持つシステム、およびアーキテクチャと定義します。
- イベントドリブンで駆動するアーキテクチャで構築される
- VM・ネットワーク管理をクラウド事業者に委ねる(PaaS or SaaSを含む)ことで、運用負荷が低いシステム
- 実行に必要なコンピューティングリソースは自動で割り当てられ、必要に応じて自動でスケールされる
サーバレスで構成されるシステムは、適切に設計することで、コンピューティングリソースの占有時間を少なく利用できるため、IaaSに比べて安価に利用できる場合が多い、というメリットもあります。
AWSにおいては「Lambdaとマネージドサービスで作られる、EC2を利用しないシステム」と捉えても良いでしょう。
余談ですが、今年度から社内研修として「AWSサーバレスアーキテクチャ研修」を開催できることが決まりました!
半年前のブログで語った野望がようやく叶いました。何でも言ってみるものですね(笑)
今年秋の開催を目標に、講義資料を鋭意作成中です。
検証環境でよくある「NAT Gateway費用問題」
話が逸れましたが、本題に入りましょう。
前提として、次のような検証環境を用意するケースを想定します。
- 製品調査(技術調査)のため、検証環境をAWS上に構築する
- セキュリティを考慮し、検証用サーバはインターネットと直接に接しないNW上に配置する
- 作成リソース、実行基盤の管理は必要最小限とする
上記の要件の場合、例えば以下の構成を思いつくのではないでしょうか?
検証サーバをDMZ配下におき、インターネットへのアクセスはNAT Gatewayを用いる、シンプルな構成ですね。
ダイアグラムはこれで良いとして、大まかにコスト計算をしてみましょう。
要件整理・コスト計算
AWSのコスト計算の前に、インスタンスタイプや起動時間、リージョンなども考える必要がありそうです。
もう少し要件を整理してみましょう。
- 検証用サーバはm4.large, 踏み台サーバはt2.nanoを利用する。
- 検証は営業時間内のみで実施される。
- よって、サーバを起動させる必要のある時間は、22(営業日)×8(時間)=176時間
- 利用料金は日本リージョンで計算(2018/3月末時点のもの。)
- EBS・NW費用などは、検証イメージが固まった段階で再び見積もる。
利用イメージが固まってきましたね。それでは、費用を見積もってみましょう。
No. | サーバ | インスタンスタイプ | A.1ヶ月付けっぱなし | B.営業時間のみ起動(EC2) |
---|---|---|---|---|
1 | 踏み台サーバ | t2.nano | $ 5.57 | $ 0.76 |
2 | 検証用サーバ | m4.large | $ 94.43 | $ 20.64 |
3 | NAT Gateway | - | $ 45.39 | $ 45.39 |
- | 合計金額 | - | $ 145.39 | $ 66.79 |
A.案では、検証用環境としては思ったより費用がかかるかも?といったところ。
B.案では月々$66.79となり、A.案の半額以下で利用できそうです。コスト管理の重要性が分かりますね。
もしも、NAT Gatewayが停止できたなら
ここで気になるのは、NAT Gatewayの利用料金です。
2018/03現在、NAT Gatewayは起動・停止相当の操作が出来ないため、起動している限り一ヶ月分の利用料金が必要です。
NAT運用の手間から解放されると捉えれば、かなりお値打ちです。が、今回のような小さい構成の場合、 「サーバ代が約$21なのに、NATは倍の費用がかかるの?」 とコスト部門のメンバに質問されてしまうかもしれません。
仮にNAT Gatewayの起動・停止が出来れば、どの程度コストカットできるのでしょうか。計算してみます。
No. | サーバ | インスタンスタイプ | A.1ヶ月付けっぱなし | B.営業時間のみ起動(EC2) | C.営業時間のみ起動(EC2 & NAT) |
---|---|---|---|---|---|
1 | 踏み台サーバ | t2.nano | $ 5.57 | $ 0.76 | $ 0.76 |
2 | 検証用サーバ | m4.large | $ 94.43 | $ 20.64 | $ 20.64 |
3 | NAT Gateway | - | $ 45.39 | $ 45.39 | $ 9.92 |
- | 合計金額 | - | $ 145.39 | $ 66.79 | $31.32 |
NATの起動時間を管理することで、更にコストを削減できますね!(パターンC)
全体費用が数百ドルを超えるような、大規模な商用システムの運用費としては、$35は大した金額ではないかもしれません。
ただ、今回のような「ちょっと使ってみる」環境として捉えた時、予算全体の4~5割を占める額です。
起動・停止相当を実現してコストダウン出来るかどうか、少し考えてみましょう。
NAT Gatewayの起動・停止をどのように実現するか
NAT Gatewayの主な利用目的としては、
- DMZ越しにインターネット接続がしたい
- 特定のIPアドレスでインターネット接続がしたい
- NATサーバの運用管理から解放されたい
といったところでしょうか。 基本的な要件としては、NATサーバが同じIPアドレスを保持できれば、サーバの種類は問いません。
(= NAT Gatewayを一旦リリースしても良い。)
したがって、
・ NAT Gatewayの起動 = 特定のIPを持つNAT Gatewayの作成+VPC設定(ルートテーブル)
・ NAT Gatewayの停止 = 特定のIPを持つNAT Gatewayの削除+VPC設定(ルートテーブル)
と定義することで、擬似的に起動・停止の実現が可能です。
手作業で毎日作成・削除する、というのは当然避けたいのでバッチ化したいですよね。
せっかくAWSで運用しているので、ここはサーバレスで実現してみましょう。
サーバレスで起動・停止を実現する
前述の構成図に必要なバッチを追加しました。
今回は実行基盤を作り込まずにAPIを叩くのが目的なので、AWS BatchではなくLambdaで実行します。
CloudWatch Eventsを始業時刻、終業時刻に設定し、それぞれバッチ(Lambda)を起動しています。
Lambdaを用いることで、Cron実行サーバ自体の管理や、Cron同士の排他制御から解放されるのも大きなメリットです。
1.起動バッチの作成
それでは、2つのバッチを設定していきましょう。
前提として、Lambdaに関しては、Python3.6で作成するものとします。
一から作成することもできますが、AWSが用意した構成を利用すれば、CloudWatch Events & Lambdaの構成を簡単に作成できます。
マネジメントコンソールのサービス欄から「lambda」を選択し、関数を作成しましょう。
lambda関数の作成で選択できる「設計図」から「lambda-canary-python3」を選択してください。
設定値は、以下の項目を更新します。
No. | 設定項目 | 設定値 |
---|---|---|
1 | CloudWatch Eventsのスケジュール式 | cron(0 0 ? * MON-FRI *) |
2 | IamRole | AmazonVPCFullAccess, AWSLambdaBasicExecutionRole を設定したロール |
3 | 環境変数 | EIP_ID:<設定するEIPのアロケーションID>、SUBNET_ID:<サブネットID> |
4 | 実行時間 | 180秒 |
CloudWatch Eventsのスケジュール式は、JSTではなくUTCで記載することに注意してください。
NAT Gatewayの構築に120秒程度かかるので、実行時間はそれより大きい値を設定する必要があります。
今回は余裕を見て、180秒としましょう。
Lambdaのソースコードは、以下のイメージで作成します。
- 指定したSubnet、EIP情報を持つNAT Gatewayの作成
- 作成完了をget_waiterで待つ ( ≒作成成功の確認 )
- 作成したNAT Gatewayへのルーティング設定を追加する
今回は、下記のサンプルコードを設定してください。
import os
import boto3
eip = os.environ['EIP_ID']
subnet = os.environ['SUBNET_ID']
client = boto3.client('ec2')
def start_natgw(Eip,Subnet):
response = client.create_nat_gateway(
AllocationId=eip,
SubnetId=Subnet
)
natid = response['NatGateway']['NatGatewayId']
client.get_waiter('nat_gateway_available').wait(NatGatewayIds=[natid])
return(natid)
def atatch_natgw(natgw, Subnet):
filters = [ { 'Name': 'association.subnet-id', 'Values': [Subnet] } ]
response = client.describe_route_tables(Filters = filters)
rtb = response['RouteTables'][0]['Associations'][0]['RouteTableId']
response = client.create_route(
DestinationCidrBlock = '0.0.0.0/0',
NatGatewayId = natgw,
RouteTableId = rtb
)
def lambda_handler(event, context):
natgw = start_natgw(eip, Subnet)
atatch_natgw(natgw, Subnet)
2.停止バッチの作成
次に停止バッチを作成しましょう。
起動バッチと同じく、関数を設計図「lambda-canary-python3」から作成し、以下の設定を更新します。
No. | 設定項目 | 設定値 |
---|---|---|
1 | CloudWatch Eventsのスケジュール式 | cron(30 8 ? * MON-FRI *) |
2 | IamRole | AmazonVPCFullAccess, AWSLambdaBasicExecutionRole を設定したロール |
3 | 環境変数 | SUBNET_ID:<サブネットID> |
Lambdaのソースコードは、以下のイメージで作成します。
- 作成したNAT Gatewayへのルーティング設定を削除
- 指定したSubnetのNAT Gatewayの削除
今回は、下記のサンプルコードを設定してください。
import os
import boto3
Subnet = os.environ['SUBNET_ID']
client = boto3.client('ec2')
def stop_natgw(Subnet):
filters = [ { 'Name': 'subnet-id', 'Values': [Subnet] },
{ 'Name': 'state', 'Values': ['available'] } ]
response = client.describe_nat_gateways(Filters=filters)
natgw = response['NatGateways'][0]['NatGatewayId']
client.delete_nat_gateway(NatGatewayId=natgw)
def detach_natgw(Subnet):
filters = [ { 'Name': 'association.subnet-id', 'Values': [Subnet] } ]
response = client.describe_route_tables(Filters = filters)
rtb = response['RouteTables'][0]['Associations'][0]['RouteTableId']
response = client.delete_route(
DestinationCidrBlock = '0.0.0.0/0',
RouteTableId = rtb
)
def lambda_handler(event, context):
detach_natgw(Subnet)
stop_natgw(Subnet)
これで起動・停止システムが完成しました! 簡単ですね。
サーバレス化でかかる諸費用
機能を実現したので満足してしまいそうですが、今回追加したサーバレスシステムに必要なコストもきちんと計算しましょう。
サーバレスでNATの起動管理をした場合のコスト削減結果は、最終的に下記の通りです!
No. | 項目 | 料金 | 料金の考え方 |
---|---|---|---|
1 | lambda起動時間(NAT起動) | $0.0054912 | 実行時間(概算:120000 ms)× 実行回数(営業日:22) × 128MBでの費用レート($/ms) |
2 | lambda起動時間(NAT停止) | $0.00002288 | 実行時間(概算:500ms)× 実行回数(営業日:22) × 128MBでの費用レート($/ms) |
3 | EIP保持費用 | $6.44 | 営業日(22日) × 16.5(営業日外の時間) + 休日(8日) × 24(時間) |
Lambdaを活用することで、結果としてパターンBに比べ43%($29.03)の費用削減が出来ました! Lambda実行料金(No.1、No.2)については、おそらく無料枠の範囲で利用できるでしょう。 仮に支払うとしても、合計1円以下ですね。
ちなみに今回は紹介しませんが、NATのIPを固定する必要が無い場合は、更に$6程度のコストカットも可能です。 ユースケースに応じてlambdaをアレンジしてください。
おわりに
上記のようにサーバレスアーキテクチャを用いることで、オペレータの運用コストをかけずに、AWS費用を簡単に削減できました。
サーバレスは気になるけれど、まだ開発システムには組み込めないな......という方も、まずは運用ツールとして導入するのはいかがでしょうか。
AWS基盤上での商用メール配信システムや画像解析処理システム構築経験を持つ。最近ではAWS関連の支援業務も兼務。 部署名にクラウドが付いてなくても頑張ります。 自家製チャーシューやほうれん草のおひたし、レトルトを用いない麻婆豆腐など自炊も随時勉強中。