リリースした時はきれいだったコードも、運用に曝されることで汚くなっていったりしますよね。
ChatOpsな今のプロジェクトでは、「とあるイベントが発生した際に、運用担当者が迅速に反応できるようSlackに通知してほしい」という要望が出てきたりしています。以前であれば、メール通知してたところでしょうかね。
運用的関心事は移ろいやすい
かつてJavaでAOPが流行りだした頃に、ロギングだったり権限チェックだったりの処理をメソッドにweavingしたInterceptorに追いやるという、所謂「横断的関心事」をビジネスロジックの本質から除外するという手法がありましたね。
自分が運用的関心事と言っているのは何のことかというと、
アプリケーション本体の正常な動作にとっては無関係だけど、運用的には知っておきたい事柄
のことを指します。
弊社も最近は組織的にSlackが導入になったということもあって、ユーザーがアプリケーション内で作成した何かの詳細情報だったり、それに付随する情報はSlackでキャッチアップできるようになるというニーズはこれからも増えてくるんじゃないかなぁと感じてるとこです。
ただ、このような運用的関心事を得るためにアプリケーション内にイベントをたくさん仕込んだり、表示する内容を別途クエリ投げて引っ張ってきたり、内容を整形したりでそれなりの実装が入りがちですよね。そしてその要件も移ろいやすかったりするものです。
なので、アプリの本質とは別軸で運用的関心事を知る良い方法がないかなーとは前々から思っていたわけです。
AWS Lambda on VPC を使って運用的関心事を知る
つい先日、AWS LambdaがVPC対応されました。
[New] LambdaファンクションからのVPC内リソースへのアクセス
VPC対応のおいしいとこはやはりVPC内のリソースにアクセスできることに尽きるので、これはワンチャンあるぞと思って今回作った構成はこちら。EC2はECSでDockerのクラスタになってるけど、今回の本題からは逸れるので省略。
EC2の役割
- 任意のタイミングでSNSへのイベントメッセージを発火する
- 余計なロジックを埋め込まずに、イベントの発火だけに専念する
要件によりますが、Lambdaのスケジューリングで定期的にDBをポーリングすることで、このイベントの埋め込みすら無しにすることもできると思います。イベント発火さえも除外できたら運用的関心事の分離としては完璧な気がします。
SNSの役割
- サーバサイドアプリ側から、運用的関心事のイベントメッセージを受け取る
- イベント受信用のSNS Topicを用意する
- イベント用Topicは、VPC上に作られたLambdaに対してSubscribeする
Lambdaの役割
- SNSからイベントメッセージを受け取る(何かのidだけが入ったシンプルな内容)
- idを元にEC2 SlaveにSELECT発行してレコードを取得
- DBから取得した内容を元に、Slack用のメッセージを作成
- SlackのIncoming WebHookのAPIを実行
NAT Gatewayの役割
- **Lambda on VPCはインターネットにOutboundすることができない(重要)**
- LambdaからSlackのAPIにアクセスするために、NAT Gatewayを使ってSlackへのルーティングを追加し、APIを実行できるようにする
api.slack.com
hooks.slack.com
はどうやらCloudFrontっぽいので、CFのIPレンジ調べてルートテーブルに追加するのがとりあえず良さそう。ただIPレンジは時間とともに変わってくるはずなのでどうするかは課題。
Lambda on VPCの課題
Internal DNSの名前解決に失敗する
内部DNSの解決にかなりの頻度で失敗します。例えばRDSやElastiCacheへアクセスしようとなった場合に、Route53で独自の内部DNSを貼ったりすると思いますがかなりの頻度で失敗します。 公式ドキュメントにも現状では内部DNSの参照は推奨されていないと記述されています。
NATが必要
先述の通り、NATが無いとインターネットに出られません。最近はNAT Gatewayがあるので敷居は下がってますが、NATのキャパシティを考慮して数を増やしたりとかも検討しないといけないので、特定のIPで絞ったりという対応はやはり必要になるでしょう。
S3のVPCエンドポイントは取り扱い注意
VPC LambdaからS3にアクセスしたいというケースもあるでしょう。その場合は昨年リリースされたVPCエンドポイントを利用するのが良いです。
VPC エンドポイント - Amazon Virtual Private Cloud
VPCエンドポイントはルートテーブルに追加することができます。VPCのプライベートサブネットからS3にアクセスされるために使われる手法ですが、Lambdaでも利用できます。
ただし、ルーティングレベルでS3のバケットを絞ったりすることはできません。例えば東京リージョンであれば、 com.amazonaws.ap-northeast-1.s3
へのエンドポイントとなります。VPCエンドポイントではポリシーを設定することでバケットへのアクセスを制御できますが、下手にポリシーで絞ったりすると同一サブネットにあるインスタンスからAccess Deniedになったりするのでかなり扱いには注意です。
十分なIPが必要
VPC Lambdaでは当然VPC内のENIを消費します。そのため大規模で使うとなるとサブネットに十分余裕がある状態を作っておく必要があります。そういう意味では、Lambdaを使ってサーバレスでゴリゴリやっていく場合は初期のサブネット設計がかなり重要になってくるといった感じです。
所感
面白い仕組みではあるなーと思いつつも、現時点では正直使いにくさをかなり感じますね。最初発表されたとき歓喜でしたが、社会の厳しさを知った感じです。 まだまだ厳しい仕組みですが、まあ引き出しの中にでも入れといてもらえれば幸いです。