オンプレミスで動くアプリケーションを SaaS へ移行させる際のよもやま話
~後編 : マルチアカウントにおける CI/CD
Author : 福地 信哉 (株式会社網屋)
前回の記事 では、弊社のオンプレ版のアプリケーションをいかにして AWS 上のコンポーネントに分解し移殖しようと試みたかということをお話ししました。
加えて SaaS 版では、オンプレ版と異なり新たに必要になるアプリケーションロジック外の大きな要素が存在します。それは、上記のように構成されるアプリケーションを AWS 上に展開し管理するということです。私達はこの件についても試行錯誤を経ることになりました。
結論としては、AWS CodePipeline + AWS Organizations によって対マルチアカウントの CI/CD パイプラインを構築する運びになりました。
本節ではその経緯をお伝えします。マルチアカウントの運用管理にご興味のある方々の一助となれれば幸いです。
なぜ AWS CodePipeline が必要になったのか
リソースを AWS 上にデプロイする方法として、当初漠然と想定していたのは、社内サーバー上にある種のパイプラインを構築するということでした。具体的には、オンプレミス版のビルド・テストを行っている弊社の社内サーバーで、同様に SaaS 版のビルド・テストも行い、AWS CLI、AWS Copiot 等を用いて AWS 上にデプロイを行うというフローです。
しかし、この方法は早々に大きく 2 つの問題点に突き当たることになりました。
第 1 に、マルチアカウントと社内ネットワークへのアクセスの問題です。
前回の記事で述べたように、今回のアプリケーション構成の重要なポイントは、ユーザー企業 1 つに対して AWS アカウント 1 つを割り当てるマルチアカウント構成を採用することでした。したがって、ユーザー企業が増える度に動的に AWS アカウントを作成し、内部にリソースを流し込む必要が生じます。これを以下では「企業アカウントのデプロイ」と呼ぶことにします。
企業アカウントのデプロイでは、ユーザー企業を新規追加するリクエストが管理アカウントの Web サービスに投げられ、それによってパイプラインがキックされることになります。しかし、Web サービスから社内サーバーへのアクセス許可は、セキュリティ上の観点から可能な限り控えたいという要望がありました。
第 2 に、プログラムのバージョン管理の問題です。
ビルドアーティファクト群を管理し、最新のテストに通ったアーティファクトと数的に同一のものを本番環境にデプロイしたいという欲求がありました。しかし、そのために社内サーバー上でビルドアーティファクト群を自前で管理するのは手間がかかります。
これら 2 つの問題点を解決するのが、CodePipeline を利用しパイプラインを社内ではなく AWS 上に構築するという選択でした。
CodePipeline を使用することによって、第 1 に、マルチアカウントと社内ネットワークへのアクセスの問題が解消されます。パイプラインは AWS 内で完結し、社内へのアクセスは不要になるからです。
第 2 に、プログラムのバージョン管理の問題が解消されます。CodeBuild でテストにパスしたビルドアーティファクトの S3 パスを CloudFormation テンプレートに渡すことで、テストに成功したアーティファクトと数的に同一のものを、ビルドし直すことなく簡単にデプロイすることができます (*1)。これにより、テスト環境と本番環境のプログラムが全く同一のビルドに基づくという状況を簡単に実現できます。
AWS CodePipeline + AWS Organizations によるマルチアカウント CI/CD パイプラインのアーキテクチャ
要点は下記の通りです。
- CI/CD の道具立てを集中配備し環境の統括を行う Root アカウントを持つ
- 開発用 / テスト用 / 本番用の環境別に、 AWS CodeCommit のブランチ、CodePipeline のパイプライン、そして Organizations の OU を区分する
- デプロイプロバイダとして AWS CloudFormation StackSets を使用し、OU 内のアカウント群に並列デプロイを行う
- アプリケーション更新時には CodeCommit へのコミットにより、企業ユーザー追加時には管理側 Web サービスにより、パイプラインがトリガーされる
これらの点について、「環境の隔離」および「環境の統括・連携」という 2 つの観点から説明します。
環境を隔離する
上のアーキテクチャでは、 次の 3 つの意味で環境の隔離を行っています。
- パイプラインのコントロールプレーンとデータプレーンの隔離
- 開発 / 検証 / 本番環境の隔離
- 管理側 / 企業側アカウントの隔離、また企業アカウント間の隔離
3 点目については既に触れたので、ここでは 1. と 2. について説明します。
1. コントロールプレーンとデータプレーンの隔離
第 1 に、パイプラインオーケストレータのコントロールプレーン (デプロイを行う側) とデータプレーン (デプロイが行われる側) をアカウントとして分離しています。
コントロールプレーンのアカウントには CodePipeline 本体を配置し、Organizations の Root アカウントにも設定しています。Root アカウントは、本製品で使用する全てのアカウントを眺望し、またデプロイを統括的に管理するという目的を持っています。
コントロールプレーンとデータプレーンをアカウントとして切り分けることは、機能上の分離と同時に、権限の分離という意味も持ちます。十分な権限を持つユーザーであれば、Root アカウント内からは、パイプライン自体や OU 自体を破壊することができてしまいます。こうした強力な操作が可能な領域を Root アカウントとして切り出し限定することにより、セキュリティ上の管理が明確になります。
2. 開発 / 検証 / 本番環境の隔離
第 2 に、 開発 / 検証 / 本番環境を隔離します。それぞれの環境を Organizations の OU (Organizational Unit) として区分します。各 OU は同一の CloudFormation テンプレート群で構築し、本番環境で発生しうる問題に開発・検証段階で気づけるようにします。
パイプラインは、開発フェーズを通じて 1 本のものを使う こともできますし、フェーズごとに分離する 設計もあります。
私達は後者の選択肢を採り、デプロイ対象の環境ごとにパイプラインを対応付け、各開発フェーズの下で閉じたパイプラインを使い分けることにしました。これにより、例えば開発パイプラインでしばらく先のバージョンのテストをしたり、後述するような企業アカウントのデプロイを行うことができるようになるからです。
開発段階では、CodeCommit の develop ブランチへのコミットをトリガーとして、ビルド・テスト・および開発環境へのデプロイを行います。
検証段階に入ると、develop ブランチから test ブランチを切ります。これをトリガーとして、同様に検証環境へのデプロイを行います。
検証段階の最後の承認ステージで手動承認すると、test ブランチから release ブランチが切られます。
その際は、最後の E2E テストにパスしたビルドアーティファクトの Amazon S3 パスを CloudFormation テンプレートに詰めてコミットします。それによって、本番用のパイプラインでは、改めてビルドを行うことはなく、検証用パイプラインと数的に同一のビルドアーティファクトをデプロイします。
OU を活用して環境の統括と連携を行う
以上のようにしてひとたび環境群を隔離すると、次に問題になるのは、それらをどのように統括し連携させるかということです。
ここでは、パイプラインから企業アカウント群にデプロイするという部分に話題を絞ってお話しします。
アカウントを分離する以上、アカウントをまたいだやり取りを許す権限の設定が煩瑣になると思われがちです。しかし、Organizations、CloudFormation StackSets、AWS IAM というサービスをシームレスに連携させることにより、それが容易に行えるということを見ていただければと思います。
企業アカウント群へのデプロイを行うための準備をします。
まず、コントロールプレーンのアカウントを Organizations の Root アカウントに設定すると上で述べましたが、同じく Organizations の OU として企業アカウント群をグループ化します。これを簡単に「企業 OU」と呼びます。
次に、CloudFormation StackSets のデプロイターゲットとして企業 OU を指定します。
StackSets とは 1 枚の CloudFormation テンプレートを用いてマルチアカウントにデプロイを行う機能です。これを使うと、テンプレートの統一化と並列実行によるデプロイの高速化という恩恵を受けることができます。
StackSets のアクセス許可モデルとして、サービスマネージドモデルを使用します。これを使うと、開発者がデプロイターゲットに StackSets 用の IAM ロールを作成する必要がありません。デプロイターゲットとして指定された時点で、そのアカウントには自動で StackSets の Service-linked Role が作成されます。
準備は以上です。
あとは、企業アカウント新規追加のリクエストが来た時に AWS アカウントを作成し、それを 企業 OU に追加してパイプラインをキックするだけで、企業アカウント群へのデプロイが行われます ! (*2)
このように、OU を活用し Organizations と StackSets を連携させることによって、分離したアカウント群を統括・連携させることができます (*3)。
一般に、アカウント A からアカウント B にアカウント間でアクセスを行う場合、アカウント A 内の RoleA とアカウント B 内の RoleB を作成し、RoleA が RoleB を assume する (いわば被る) ということが推奨されています。
こうした関係を取り結ぶためには、RoleA の側で AssumeRole アクションを許可し、RoleB の側で RoleA を信頼させる必要があります。なおかついつも通り、RoleA と RoleB のそれぞれに必要十分な権限を割り当てることも心がけなければなりません。
こうした作業はしばしば time consuming です。しかし、Organizations と StackSets の統合機能を使えば、こうした作業が自動化されます。
他にも、IAM ポリシーやリソースポリシーの Condition key として aws:PrincipalOrgPaths を指定することで、OU に対してまとめてアクセス許可を行うことができます。これを使うと、アカウントを生成するたびにそのアカウントからのアクセス許可を付け加えるといったことが不要になります。
このように、CloudFormation、Organizations、IAM といったサービスを相互に連携させることによって、マルチアカウントにおける権限関係のしがらみを取り払うことができます。
まとめ
今回は弊社がマルチアカウント構成の CI/CD パイプラインをどう構築するに至ったかという経緯をお話してきました。ポイントは次の通りです。
アプリケーション更新時のみならず、Web リクエストを受けて AWS アカウントを動的に作成する「企業アカウントのデプロイ」も行う場合
- CodePipeline を使用することで、社内へのアクセスが不要かつバージョン管理の容易な対マルチアカウント CI/CD パイプラインを構築できる
- 各種環境を隔離することで、アカウントの役割の明確化や権限の分離を実現できる
- CloudFormation StackSets、Organizations、IAM を相互に連携させることで、隔離された環境を容易に統括し疎通させることができる
ここまで読んでいただきありがとうございました。マルチアカウントの運用管理という、まだ情報の多くはない分野において、本記事が少しでも皆さんのお役に立てれば幸いです (*4)。
*1 : AWS CLI の aws cloudformation package コマンドを使いました。詳細はユーザーガイド S3 バケットへのローカルアーティファクトのアップロード をご参照ください。
*2 : 環境ごとにパイプラインを分けているのは、ここでパイプラインをキックするためです。本記事執筆時現在、1 本のパイプラインを途中のステージからスタートさせることはできません。
*3 : 詳しくはユーザーガイド AWS CloudFormation StackSets と AWS Organizations をご参照ください。
*4 : 本稿の内容の多くは [AWS re:Invent 2021 - Automating cross-account CI/CD pipelines
] に負っています。ぜひ併せてご参照ください。
筆者プロフィール
福地 信哉
株式会社網屋 開発部
大学院で哲学を研究していました。そこで先輩に競技プログラミングのことを教わったのが、プログラミングを始めたきっかけです。そのままエンジニアとして就職し、2 年目になります。
趣味はシューゲイザーを聴きながら眠りにつくことです !
AWS を無料でお試しいただけます