Repro における AWS アカウント分離の取り組み

Development Division/Platform Team/Sys-Infra Unit では、よりセキュアな状態を維持できるように様々なことに取り組んでいます。

背景

Repro ではメインとなる AWS アカウントに以下の環境で利用するリソースを全て作成していました。1

  • production
    • 所謂本番環境
  • staging
    • 本番リリース前に QA を行うための環境
  • dev_staging
    • 開発者の動作確認用
  • test
    • アプリケーションの CI で利用する
  • internal

AWS Well-Architected Tool を使って既存環境をチェックしたところ、まずは AWS アカウントを分離し、各環境のワークロードを分離した方がよいとの結果でした。

SEC01-BP01 アカウントを使用してワークロードを分ける: - AWS Well-Architected Framework

進め方の検討

AWS の SA に聞いたり、他社事例等を調べたりしたのですが Repro と同じような状況でアカウント分離をやりきりました、という事例を見つけることはできませんでした。2 インターネット上の記事を調べると「Workload Isolation は基本だよねー」みたいな雰囲気で、最初から分離して作り始めているのが当たり前という感じでした。そのような雰囲気なので、アカウント分離の進め方に関する記事は見つけづらく、どのような種類のアカウントを作成して分類し管理するかに焦点をあてた記事が多かったと記憶しています。

Repro の事情を列挙すると以下の通りです。

  • 一般的なウェブアプリケーションよりも複雑な構成になっている
  • 扱うミドルウェアが多い
  • 複数の AWS サービスを連携
  • 巨大なモノリシックな Rails アプリケーションから徐々に分散型のアーキテクチャに進化してきた
  • データパイプラインの数が多い
    • Kafka Streams app が30個以上あり、今後も増える可能性が高い
      • Kafka topic からデータを取得して RDS,Cassandra,ElastiCache,Kafka topic 等にデータを書き込む
      • 外部からのデータを加工して様々なデータストアに書き込む
    • AWS Lambda が20個以上ある
      • Step Functions から呼び出されるものや、S3 の PUT Notification から呼び出されるもの等がある
    • AWS Step Functions が4個あり、今後も増える可能性が高い
      • 顧客から提供される大容量データを Repro に取り込むための基盤の一部
      • 内部では Rails アプリケーションで定義した ECS Task や Lambda を呼び出すことがある
    • AWS ECS Cluster が dev_staging 環境に約20個ある3
  • Repro が持っているデータを顧客に提供するためのデータエクスポート基盤がある
  • Terraform による IaC は進められているが、全てを管理できているわけではない
  • 主体的に分離を進めるチームである Sys-Infra Unit4 のメンバーはアプリケーションの実装の詳細をあまり知らない
  • アプリケーションを開発しているチーム(FeatureTeam)はインフラにあまり詳しくないし、インフラの変更をする権限もない
  • 機能開発が止まるため、FeatureTeamの工数をアカウント分離に使うことはできない
  • アカウント分離とは関係なく、機能開発は進むため、新規リソースは増え続ける

以下は、現状の Repro のアーキテクチャをかなり簡略化した図です。

これだけでも、手間のかかることをやろうとしているな、ということが伝わるでしょう。

Repro の事情を考慮しつつ2つの進め方を検討しました。

  • 一気に全てを分離する
    • 👍 うまくいけば、短期間で完了する
    • 👎 切り替え作業中は開発環境を利用できない可能性が高い
    • 👎 失敗したとき、戻すのが大変だし、戻せるかどうかも不明
    • 👎 他の業務に差し障りがありそう
    • 👎 作業にかかる時間を見積るのがとても難しい
    • 👎 動作確認が大変
  • アプリケーションを先に分離して少しずつ進める
    • 👍 小さく始められる
    • 👍 コンポーネント5単位で分離できる
    • 👍 作業単位を細かくできる
      • 個々の作業にかかる時間を見積りやすい
      • 動作確認等を他のチームに依頼しやすい
    • 👍 他の業務と並行可能
    • 👎 技術的には少し難しい
      • 既存アカウントと新アカウントで相互に通信しなければならない
      • コンポーネントごとに分界点を決める必要がある

チームで検討した結果、アプリケーションを先に分離して少しずつ進める方針でやっていくことに決定しました。

具体的な進め方

アプリケーションを先に分離して少しずつ進める方針でやっていくことになったので、具体的な方法を考えました。

1. AWSアカウント分離のためのアカウント設計

Workload Isolation を実現したいので、以下のようにAWSアカウントを分離することにしました。

名前 用途 補足
admin 既存のメインアカウント dev_staging,staging,production など全てのリソースをここに作成している
staging QA 用のリソースのみを作る 新規作成
dev_staging 開発者の動作確認に使用するリソースのみを作る 新規作成
test CI構築用6 新規作成
sandbox 開発者が AWS の挙動を理解するために自由に使う 既存
internal 内部で利用するリソースを置く 新規作成
users IAM User を管理するためのジャンプアカウント 新規作成

これまでは IAM User をメインとなる admin アカウントに作成していましたが、分離完了後は users アカウントから各アカウントへスイッチロールする運用にしていく予定です。なお、残念ながら AWS Organization や IAM Identity Center は諸事情7で使えません。

sandbox アカウントは、AWSアカウント分離を検討する前からありました。以前は直接 IAM User を作成していて、誰がどんな権限を持っているのか管理していませんでしたが、この機会に users アカウントからスイッチロールするように運用を改めました。

2. AWSアカウント分離を実施しやすくするための事前準備

AWSアカウント分離作業を実施しやすくするための作業をいくつか行ないました。

特に Atlantis の導入は事前にやっておいてよかったです。新しい AWS アカウントを追加するのがとても簡単になりました。

3. 着手する順番を検討し、実施していく

小さいコンポーネント、失敗しても影響の少ないコンポーネントから実施できるように順番を検討しました。 dev_staging のリソースについては、アプリケーションを先に分離し、新規のアプリケーションについては新しいアカウントにリソースを作っていくことにしました。 アプリケーションを先に分離することで、既存のメインアカウントに作成するリソースを減らし「分離されていない」リソースをこれ以上増やさないようにしました。

  1. test にあるべきリソースを分離する
    • 他のコンポーネントとは独立して存在する
    • 外部との通信がほぼ存在しない
    • Fargate と S3 しか使用していないので構成がシンプル8
  2. internal にあるべきリソースを分離する
    • Atlantis
    • 内部利用の AWS Lambda
  3. dev_staging にあるべきリソースを分離する
    1. @deploy-bot を動作させるためのリソースを準備する
    2. 一部の Kafka Streams Apps
    3. Step Functions
    4. 上記以外のアプリケーション
    5. RDS, Cassandra, Redis など
  4. staging にあるべきリソースを分離する
    • dev_staging の分離で得た知見を活かして進める
  5. admin の整理
    • admin で使っている Terraform のモジュールを新しいものに切り替える
    • admin に残っている不要なリソースを削除する

他チームの開発も並行して進められているので、適宜連携しながら作業を進めています。また、以下の点も実施できるように進めています。

  • 全てのリソースを Terraform で管理する
    • 過去に手動で作成されたままインポートせずに放置されているリソースが存在する
    • Terraform で管理しているリソースにはタグを付与する
      • タグが付与されていないものは Terraform 管理外なので、削除するか説明用タグを付与する
  • Terraform モジュールの整理
    • ステージ9で分岐しているロジックをなくし、同じ構成にする
    • IAM Role 名やリソース名にステージの情報を含めないようにする
    • アプリケーションごとにモジュールを作る10
    • 複数のアプリケーションで使い回している IAM Role や IAM Policy を洗い出して、それぞれ専用のものを使うよう変更する
    • IAM Role に付与する権限を最適化する
    • 使えそうならば ABAC を使う

まとめると、全てのリソースを Terraform で管理し、アプリケーションごとに独立したモジュールを作ることにより、理解しやすくメンテナンスを続けやすい IaC を実現したいと考えています。

ある程度やってみた結果と感想

2024年04月から「1. test にあるべきリソースを分離する」を開始して、記事執筆時点では「3. dev_staging にあるべきリソースを分離する」の途中まで進んでいます。

今のところ、大きなトラブルもなく順調に四半期ごとの計画通りに進められています。

ある機能を分離するときは、以下のように進めるとうまく進められました。 前提として VPC Peering 等でアカウント間に通信経路を確立してあるものとします。

%%{init: {'theme':'dark'}}%%
flowchart TD
  Step1[分離対象の機能で使われている AWS サービスを洗い出す]
  Step2{RDS, ElastiCache, S3 が含まれている?}
  Step1 --> Step2
  Step2 -->|含む| Step4a[RDS, ElastiCache, S3 の使い方を見て分離方法を考える]
  Step2 -->|含まない| Step4b
  Step4a -->|分界点を決める| Step5a{分界点で上手く分離できるか調査する}
  Step4b --> Step6[分離対象の機能で使うリソースを新アカウントに作成する]
  Step5a -->|上手くいく分界点が見つかった| Step6a[やる]
  Step5a -->|上手くいく分界点が見つからない| Step6b{別の機能と同時にやることを検討する}
  Step6b -->|上手くいきそう| Step7a[別の機能と同時にやる]
  Step6b -->|上手くいかなそう| Step7b[この機能は後回しにする]

ほとんどの場合は RDS, ElastiCache, S3 を旧アカウントに残すような分界点を設定するとうまくいきました。

今後の課題

今後の課題は以下の通りです。

  • 未着手の大物 Rails アプリケーションが残っている
  • RDS, S3, ElastiCache などのマネージドなデータストアが残っている
  • 自前運用している Cassandra, Kafka が残っている
  • Terraform のコードで新旧のモジュールが混ざっている
  • 不要になった AWS リソースを削除しきれるか
  • 不要になった IAM Role や IAM Policy を削除しきれるか
  • 新しい Terraform モジュールを使って production 環境のリソースを整理できるか
  • 差し込まれる割り込みタスクと並行してやりきれるか11

まとめ

Repro では、AWS アカウントを分離することでセキュリティと管理の最適化を進めています。リスクを抑えながら移行するために、アプリケーションから少しずつ分離する方法を採用しました。


  1. リソース管理用リポジトリの最も古いコミットは2014-01-04でした。この頃は AWS Well-Architected Framework もありませんでした。
  2. 他社事例で断念したものを見かけた気がしますが、記事執筆中に探しても見つけられませんでした
  3. staging は約30個、production は約50個あります。
  4. 過去アプリケーション開発をしていたメンバーもいるしドキュメントや実装を読むことはできるが。
  5. この記事でのコンポーネントとは、ある機能要件を満たすためのアプリケーションのまとまり、くらいの意味です。また、アプリケーションとは ECS Service , ECS Task, AWS Lambda, AWS StepFunctions の state machine などのことを指します。
  6. Repro で開発している Rails アプリケーションの CI を動かしています
  7. 近年の円安によるコスト削減圧の影響
  8. Rails アプリケーションの CI は、独自の仕組みで分割して Fargate タスクを並列実行し、テストの結果を S3 に書き込んでいます。
  9. dev_staging(開発), staging(QA), production(本番)
  10. 関連するリソースをまとめておくと、理解しやすくメンテナンスしやすくなるだろうという期待があります。
  11. 実際に大きめの差し込みタスクが複数あって、四半期の計画を見直したこともあります。