tl;dr
タイトルの通りです.
YAMAP 社内で既に利用されている AWS インフラの構成について, コードで管理した方がいいのかもしれないけど, ガッツリコード化する時間的な余裕が無い...でも...管理はしたいという気持ちが高まって, 色々と思いを巡らしているうちに, 正しいかどうかの議論はひとまず置いといて一つの思いに至りました.
インフラをコード化するのはあくまでも手法の一つであって, 本当に管理したいのは「その変更を行ったのは何故か, 誰か, いつか」ということではないか
ということで, Terraform のコードを書こうかどうか迷っていた手を一旦止めて, いくつかのリソースの変更管理を Github Issue で行うように Lambda ファンクションを書いたのでその振り返りです.
今回, Github Issue で管理することにしたリソースは以下の AWS リソースです.
尚, これらのリソースは Terraform 以外でも Roadworker 等のコード化の為の素晴らしいツールが提供されているので, 積極的にコード化しない手は無いとは思ってはいますが, コード化によって一部のメンバー (ざっくり言うと, インフラ管理者もしくは構成管理ツールに理解のあるメンバー) しか構成の変更出来ないという状況は, スピード感を持ってサービスをリリースしていく状況にとって, 時に弊害となりうる状況があると考えています. そういう状況でも出来るだけ構成管理の見える化を進める為の一つの手法として, うまく運用していければなあと考えています.
施策
やりたいこと
やりたいことシンプルです.
- リソースを操作して発生した差分を記録
- 記録された差分情報を Github Issue に登録
これだけです. 作成された Issue は, 操作した本人または管理者 (自分) が Github Issue にコメントを付けてクローズするという運用を想定しています.
どんなふうにやったか
基本的には以下のような流れを実装しました. 実装は Serverless Framework を利用して関連する AWS リソースも丸っと一緒にデプロイしています. Serverless Framework 最高です.
当初は AWS Config があるではないか!と楽勝な気持ちで挑みましたが, 未対応のリソースもあったり, AWS Config 自体が未経験だった為に手っ取り早い方法を取ってしまいました. ごめんなさい.
Lambda のコードは恥ずかしいので世の中に晒すのは控えておきますが, 上図のような処理の流れを Ruby で実装しました. コードの行数として 200 行程度になりました. また, Lambda へのデプロイは Serverless Framework を用いていて, CloudWatch Events からのイベントをトリガーとする設定の書き方に少し試行錯誤しました. 以下, serverless.yml の一部抜粋です.
functions: handler: handler: handler.main memorySize: 128 timeout: 300 description: "API Gateway Change Resource Recorder." layers: - {Ref: ApiGatewayChangeResourceRecorderLambdaLayer} events: - cloudwatchEvent: event: source: - "aws.apigateway" detail-type: - "AWS API Call via CloudTrail" detail: eventSource: - "apigateway.amazonaws.com" eventName: - "CreateMethod" - "DeleteMethod" - "CreateResource" - "DeleteResource" - schedule: rate: cron(0 * * * ? *)
あと, serverless.yml 内での S3 バケットのライフサイクルの設定についても, CloudFormation 力というか, S3 バケットの細かい設定をどのように落とし込むかを問われましたが, 結局は, 以下のような内容となりました.
RecorderBucket: Type: AWS::S3::Bucket Properties: BucketName: "${self:provider.environment.STORE_BUCKET_NAME}" BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 VersioningConfiguration: Status: Enabled LifecycleConfiguration: Rules: - Id: "${self:provider.environment.STORE_BUCKET_NAME}-LifeCycle-Rule" Status: Enabled NoncurrentVersionExpirationInDays: 30
どんな感じか
API Gateway の Resource の変更を行った場合, 以下のような感じで Issue が作られます.
Issue の中身は以下のような感じです.
また, Route53 にて DNS レコードの構成を変更した場合には, 以下のような感じで Issue が作られます.
Issue の中身は以下のような感じです.
マネジメントコンソール上で Alias レコードを修正した場合に作成される Issue です. 修正の場合, 内部的なオペレーションとして DELETE
と CREATE
が実行されるようです.
尚, Github の Slack インテグレーションが設定されているので, 指定したチャンネルにも通知され見逃しを出来るだけ防いでいます.
この例は自分自身で変更した内容なので, 自分でコメントに変更の理由等を書いてクローズという流れです. 権限を持っている他のエンジニアが変更した場合には, 変更したユーザー名は確認出来るので, そのエンジニアに変更の理由をコメントに書いてもらうか, 自分が聞き取ってコメント欄に書いてクローズとなります.
最後に
そもそも
リソースの変更も頻繁ではないということから仕組み自体が発動した回数は 1 回ですが, Issue が登録されて, 変更の理由について聞き取りを行い close することが出来ました. Issue の通知が飛んできて, 開発者に聞き取りを行ったところ, 最初は「AWS リソース警察が犯人探しっすかw」って言われましたが, 変更理由を聞いたりしているうちに色々とエンジニアチームとのコミュニケーションを取ることが出来たのは良い誤算でした.
懸念点
この仕組の懸念点. いくつかあります.
- 検証等でリソースの変更頻度が高い場合, Issue が作られまくる (対策: 検証環境は除外等の処理を入れる)
- Issue の確認漏れ等が発生する懸念 (対策: Slack と連携, ある程度 Issue が溜まったら Issue 警察を発動させる)
- 仕組み自体が複雑 (対策: 無し, すいません)
- やっぱり, infrastructure as code が良いのでは (対策: 無し, おっしゃる通り!時間に余裕が出来たらコツコツコード化していきたいかも)
そして
この仕組みを実装しながら, リリースしてから, そして, この記事を書きながらたどり着いた思い.
Infrastructure as Code について, 俺は何もわかっていなかった
ということです. ただ, インフラをコード化して, それを Github で管理するというだけではない, もっと大きくて深いものがそこにあるのでは無いかと思った次第です.
ということで
YAMAP ではこんな感じで色々とやっております. 引き続き, 精進して参ります.