Architecting for the Cloud というホワイトペーパーがあります。2018 年に出たもので、既にアーカイブされているのですが、今でも役に立つプラクティスが盛り込まれています。ここでは、内容の簡単にまとめていき、補足と感想を入れていきます。各セクションの見出しは英語のままにしておきます。
Introduction
ここでは、クラウドへの単純な移行(いわゆる Lift&Shift における Lift)でもクラウドの利点を享受できるが、より利点を最大限に活用していくためにはクラウド固有のアーキテクチャに変化させていく必要があるということが述べられています。
このことは今日では クラウドネイティブ というワードで説明されることがある概念かと思います。CNCF の定義では「クラウドネイティブ技術は、パブリッククラウド、プライベートクラウド、ハイブリッドクラウドなどの近代的でダイナミックな環境において、スケーラブルなアプリケーションを構築および実行するための能力を組織にもたらします。このアプローチの代表例に、コンテナ、サービスメッシュ、マイクロサービス、イミュータブルインフラストラクチャ、および宣言型APIがあります。これらの手法により、回復性、管理力、および可観測性のある疎結合システムが実現します。 」とあり、挙げられているアプローチは今まさに流行ってるものたちです。ただ、このホワイトペーパーでは、そういった要素技術というよりは、アーキテクチャ上のデザインパターンを紹介してくれるものとなります。
Differences Between Traditional and Cloud Computing Environments
ここでは、従来の環境(オンプレミス環境)とクラウドの環境に、どのような潜在的な違いがあるのかについてまとめられています。これらはクラウドネイティブなシステムを構築していく前提、より具体的には、例えばなぜコンテナやマイクロサービスがクラウドとの親和性があるのか、などを説明する足がかりとなります。
このホワイトペーパーでは、以下の 6 点について説明されています。
- IT Assets as Provisioned Resources : クラウドでは IT リソースを、必要なときに必要なだけ利用します。そして、支払いは使用した分のみです。これは、理論上の最大ピークの推定値に基づいてキャパシティプランニングをし、平時には高価なリソースを遊ばせておくしかなかった従来の環境との大きな違いになります。これにより、キャパシティプランニングや構成管理の考え方が変わります。また、一時的に環境を作って、すぐに壊すこともしやすくなるので、例えばテストや検証のために一時的な実環境を使う(そして不要になったらすぐに消して課金を止める)ということが非常にしやすくなり、「とりあえず触って動かしてみる」というマインドが重要になります。
- Global, Available, and Scalable Capacity : クラウドではプロバイダー (AWS) が保有するインフラを使用することができるので、複数拠点にまたがる DR 構成を組みやすく、海外拠点への展開も容易です。また、好きなときに好きなだけリソースを使うことができるため、まずは初期費用をかけずにスモールスタートして、ビジネスの成長とともにインフラを拡大していくことも容易に可能となります。そのため、事前に綿密なプランニングしてからインフラを構築するというよりは、まずは小さく初めてビジネスとともにインフラも変化させていくという考え方もひとつの戦略として有効です。
- Higher-Level Managed Services : AWS では 2023 年現在で 200 種類以上の AWS サービスを提供していて、そのほとんどがいわゆるマネージドサービスです。つまり、AWS 側で運用タスクの一部がマネージ(管理)されるということです。これにより、専門の知識をもつスタッフが少なくても高度なデータベースや機械学習やデータ分析などのサービスを導入してすぐに、使い始めることが可能になります。VUCA 時代と呼ばれて久しい昨今では、こういった初動の速さはビジネスを成功に導く大きな要素の一つといえるでしょう。
- Built-in Security : AWS がマネージする領域はセキュリティにも及びます。現に AWS では様々な コンプライアンス の認定をうけており、評価レポートについては AWS Artifact というサービス上で公開されています。この中には、クレジットカードを扱う際に必要となる PCI DSS であったり、日本政府の認定である ISMAP も含まれます。一方で、AWS では 責任共有モデル というセキュリティモデルを採用しており、あくまでセキュリティはクラウド利用者と責任を共有するものとしています。すなわち、AWS 上で構築したあらゆるシステムが自動でセキュリティが担保されて前述したプログラムの認定基準を満たすわけではなく、そのためにはクラウド利用者側の取り組み(最も基本的なところでは、通信やストレージの暗号化設定など)も必要になるということです。それでも、少なくとも物理レイヤのセキュリティをそれほど意識しなくてもよくなるのは、大きなアドバンテージとなります。
- Architecting for Cost : AWS では使ったぶんだけの支払いとなるので、無駄なコストの発生を抑えやすくなります。また、どのサービスにどれだけのコストが発生しているかが明確なので、それをもとにコスト最適化の観点でもアーキテクチャの変化を起こしていく事が可能となります。
- Operations on AWS : AWS ではあらゆる操作を API から行うため、運用の自動化が容易です。また、好きなときに好きなだけ使える、そして従量課金制という特徴とも相まって、IaC との親和性が高いです。つまり、テスト環境を作るテンプレートを用意しておけば、スッと環境を自動生成させて、用事が済んだら環境を削除するということが全自動で行われます。また、イベント駆動でのスケーリングも自動化することができるので、アクセスの多寡に応じてサーバー台数を調整するということも自動で行えます。DevOps 環境の導入も比較的容易といえるでしょう。
Design Principles
ここがこのホワイトペーパーのメイントピックで、11 の項目について様々な設計上の指針や考え方について説明されています。
Scalability
クラウドでは様々な IT リソースを好きなときに好きなだけ使うことができ、スケーリングには大きく 2 つの戦略がとられます。ひとつが 垂直スケーリング(スケールアップ・スケールダウン)で、個々のリソースのスペックを増減させる手法です。アーキテクチャ上の変更がなく容易に実現することができるため、短期的には多くのユースケースに利用することができますが、早期にスペックの上限に到達しやすく、必ずしもコスト効率や可用性の高いアプローチとは言えません。もうひとつが 水平スケーリング(スケールアウト・スケールイン)で、リソースの台数を増減させる手法です。クラウド環境においては台数の増減は容易に行うことができるため、クラウドの伸縮性を生かしたアーキテクチャを実現することができます。しかしながら、すべてのワークロードが複数のリソースにリクエストを分散できるように設計されているわけではないので、水平スケーリングを導入するためにはシステム側の設計が重要になります。
例えば、セッション情報をサーバー内部に保持しておいて、特定のクライアントとそのセッション情報をもとに通信を行うような、いわゆるステートフルなシステムの場合は水平スケーリングに向いていません。もう少し抽象化すると、特定のサーバーしか知り得ない情報を前提にシステムが構成されている場合は、水平スケーリングに向いていないといえます。水平スケーリング環境においては複数台のサーバーで構成されるため、どのサーバーにリクエストが向いても問題なく処理を継続できる必要があるためです。このような性質をもったシステムをステートレスなシステムということがあります。もしセッション情報を保持したい場合は、それ用の外部データベース(AWS の場合は ElastiCache や DynamoDB)に格納するようにして、個々のサーバー内部に持たせないことが重要です。
なお、ALB には スティッキーセッション の機能があるため、Cookie に基づいたステートフルなコネクションを実現することは一応可能になっています。ただしこの機能を用いる場合は、上述した水平スケーリングの恩恵を受けにくいことには留意しておく必要があります(例えば、スケールアウトして新たなインスタンスが追加されたとしても、既存のクライアントからの通信がそのインスタンスにルーティングされることはなく、インスタンス間の負荷の偏りを生みます)。
Disposable Resources Instead of Fixed Servers
従来の環境では、新しいハードウェアを導入したらそれを大事に使い続ける必要がありましたが、クラウドではその必要はありません。好きなときに好きなだけリソースを追加・削除していけばよいわけです。このような考え方を 使い捨て可能なリソース(Disposable Resources)と呼ぶことにします。
これを実践していくためにはいくつかのコツがあります。ひとつは、IP アドレスを固定を前提にシステムを構築しないことです。先程の水平スケーリングとも関連しますが、クラウドの特徴を生かした構成とする場合、ひとつのサーバーを固定的に使い続けるケースは少なくなります。そうなると、サーバーの台数、および IP アドレスが動的に変更されることを意味します。ロードバランサーを挟むことやメッセージキューを挟んだ非同期アーキテクチャとすることなどがこの解決に繋がります。
また、各サーバーの作成時刻が異なる場合、ソフトウェアのバージョンなどの設定のズレが出てくる可能性も考慮する必要があります。これについてはイミュータブルインフラストラクチャパターンがヒントになります。つまり、一度起動したサーバーはその後更新せずに、アップデートが必要になった際は新たなサーバーに置き換えることで、管理を容易にします。ただしこの運用を実現するためには前述したステートレスの考え方も合わせて必要になります。
この部分について、どのようにサーバー(EC2 インスタンス)をセットアップするかについてもう少し掘り下げます。これにはいくつかの方法が考えられます。ひとつは、EC2 インスタンスの起動はデフォルト設定から開始し、起動時のスクリプト(EC2 の場合はユーザーデータが該当)によって環境を構築します。これを Bootstrapping と呼ぶことにします。この方法は、ユーザーデータのほか、Chef や Puppet といった構成管理ツールを使っても実現できるでしょう。もうひとつは、予め環境構築済みの起動イメージ(EC2 の場合は AMI が該当)を用意しておいて、それを起動するようにします。これを Golden Images と呼ぶことにします。この方法は、Bootstrapping と比較して、起動時間の削減や、起動時のスクリプト実行エラーの低減を期待できます。ただし、更新の必要が生じた際にスクリプトを変更すればよい Bootstrapping と比較して、Golden Images は更に起動イメージの再作成が必要になります。そのため、あまり頻繁に更新されない部分については Golden Images 化しておいて、頻繁に更新される部分や環境ごと(テスト・ステージング・本番など)に異なる設定をしたい部分については Bootstrapping で対応するといったハイブリッド戦略も考えられます。そして、この点についてのもうひとつのアプローチがコンテナ技術の利用となります。
Automation
AWS ではあらゆる操作が API 化されているため、これは各操作がプログラム可能であることを意味し、これまで手動での対応が必要だった部分についても 自動化 することができます。自動化により、作業速度の向上、人為的ミスの低減が期待でき、運用コストの最適化に繋げられます。
たとえばインフラストラクチャをコードとしてテンプレート化しておき、それを用いた環境の自動プロビジョニング、いわゆる IaC も容易に実現することができます。AWS の場合、このような機能を提供するサービスは AWS CloudFormation ですが、Amplify や SAM、CDK のような、CloudFormation を内部的に利用するツールも複数提供されています。これにより、好きなときに好きなだけ開発環境やテスト環境を自動で構築可能で、そのあと環境ごと削除すればそれ以上の料金は発生しなくなります。
インフラ構築の自動化だけではなく、システムのデプロイを自動化するということも推進していくことができます。いわゆる DevOps を実現するひとつのツールとしてパイプラインの構築がありますが、AWS では CodePipeline によってそれを構築することができ、CodeBuild や CodeDeploy などとも組み合わせるおことで、テストやビルドの自動化、そして環境への自動デプロイも実現することができます。
インフラ構築やデプロイの自動化だけではなく、監視についても自動化の考えを適用することができます。AWS では、Amazon CloudWatch に AWS 上のリソースの多くのメトリクス情報が自動で集約されるため、閾値とアクションを定義しておくことで、ある監視項目が閾値を上回った(もしくは下回った)時点で自動でアクション(担当者にメールで通知をするなど)を実行させることができます。これは CloudWatch アラーム機能、と呼ばれます。他の設定例としては、アクションとして Auto Scaling という AWS サービスを指定しておいて、CPU 使用率が閾値を超えた場合に自動で EC2 インスタンスを追加する、といった水平スケーリングの自動化も可能になっています。また、AWS Config というサービスを使うことで、その環境上で発生した何らかの操作(S3 というストレージを誰かが一般公開してしまった等)を検知して自動で通知を送ることだったり、Amazon EventBridge というサービスを利用することで、その環境上で発生した何らかのイベントを検知して(もしくは定期的に)別の AWS サービスに連携する、ということも可能です。
Loose Coupling
クラウドかどうかに依らず、アプリケーションの複雑さが増すにつれ、IT システムの望ましい特性は、より小さく、疎結合のコンポーネントに分割できるかとなっていきます。別の言い方をすると、あるコンポーネントの変更や障害が他のコンポーネントに波及しないようにする必要があるとも言えます。このことに関するキーワードが 疎結合(Loose Coupling)です。
アーキテクチャレベルでは、それぞれのコンポーネントを分離させて、コンポーネント間は厳密に定義された API を介して疎通するというマイクロサービスアーキテクチャの適用が結合度を疎にするという関連で考えられます。
各コンポーネントを独立させる場合、自身の通信相手がどこにいるのかを特定するためのメカニズムが必要になり、これはマイクロサービスの文脈ではサービスディスカバリと呼ばれます。また、システムを EC2 インスタンスにホストするような場合は、ELB が水平スケーリングによって増減する EC2 インスタンスを追跡し、適切な EC2 インスタンスに通信をルーティングする役割を果たします。ELB 自体のエンドポイントは変わらないため、そこ宛に通信することで、ELB はその背後にいて現時点でヘルシーな EC2 インスタンスに通信を転送してくれるわけです。これは、前述した IP アドレスの固定を前提としない話とも関連しています。対向システムと直接通信しようとするのではなく、間に ELB のようなコンポーネントを挟むことで、対向システム(を構成する EC2 インスタンス群)の状況を隠蔽し、システム同士の結合度を緩めることができます。
非同期通信の導入も、結合度を緩めるひとつのアプローチです。つまり、対向システムへの処理の依頼をしたあと、その返答を待たないというアプローチです(処理結果は別途取得します)。具体的な実装パターンとしては、Amazon SQS のようなメッセージキューを間に挟んでシステム間でメッセージの伝達を行います。これによって、SQS のエンドポイントさえ知っていればその背後に対向システムの状況を知っておく必要はなくなり、更に、もしその対向システムに障害が発生しても(メッセージキューにメッセージが残ったままにはなるものの)その他のシステムまで障害の影響が波及するリスクを低減させることができます。
Services, Not Servers
従来の環境では、何らかのサービスを開発して提供するまでに、必然的に物理的なインフラの構築やサーバーの保守管理が必要になっていました。しかし、それらは本来ビジネスの価値を直接生むようなタスクではありません。クラウドでは、そのようなビジネス上の差別化を生まないような重労働(Undifferentiated heavy lifting)を減らせる可能性があります。すなわち、サーバーではなくサービス にフォーカスするということです。
例えば、前述したような自動化は、そのひとつの例になります。AWS では様々なカテゴリのサービスを提供していて、多くのことが自動化されています。例えば、Amazon RDS というデータベースサービスでは、日々のバックアップ機能や、障害発生時のフェイルオーバー作業などが自動化されていて、ひとつの機能としてあらかじめ組み込まれた状態でサービスが提供されています。また、Amazon S3 というストレージサービスでは、デフォルトでは 3 箇所以上の物理的に離れた場所にデータの複製をとってくれます。このように、ある AWS サービスについて、そのような自動化機能にフォーカスを当てたい場合は、そのサービスのことをマネージドサービスというように呼称することがあります。
もし、サーバーそのものがそのビジネス上の価値を生まない場合、つまり、専用の GPU があることでサービス品質が変わるとか、ギリギリまで設定をチューニングすることで競合との差別化につながるとか、が無いような場合は、サーバーレスアーキテクチャパターンの導入も選択肢として挙げられるようになります。これは、サーバーを隠蔽して AWS 側に管理を任せることで、ビジネス上の価値を生むサービスそのものに労力を集中させようとするアプローチです。AWS では、AWS Lambda や DynamoDB など、多くのサーバーレスサービスを提供しています。
Databases
従来の環境では、様々な制約から使用可能なデータベース技術が制限されることがよくありました。一方で、データベースというのは設計思想の違いから多くの種類が存在し、それぞれ、得意な処理や不得意な処理が存在しています。好きなときに好きなだけリソースを活用できるクラウド環境では、目的ごとに構築されたデータベース(Purpose Built Database)を複数種類ひとつのサービスの中で採用するという考え方をすることができます。
たとえば、いわゆるリレーショナルデータベース(RDB)は、データを正規化して保持し、強力な整合性の制御や、複数のデータを迅速かつ効率的に組み合わせて検索する機能に優れています。ひろく一般に利用されているデータベースでもあるため、これまで多くの知見が集積されているカテゴリとも言えます。AWS では、Amazon RDS がこれに該当します。
一方で、いわゆる NoSQL と呼ばれるデータベースでは、より柔軟なデータモデルを使用可能で、パフォーマンスを高めやすいものが多いと言われます。AWS では、キーバリューとして Amazon DynamoDB、グラフとして Amazon Neptune、インメモリとして Amazon ElastiCache や Amazon MemoryDB など、幅広いデータベースサービスから選択することができます。
他にも、大容量のデータ管理と分析用の操作に特化したデータウェアハウスというものもあり、AWS では Amazon Redshift が提供されています。一種類のデータベースエンジンにすべてのことを対応させようとするよりも、目的に応じた使い分けをすることで、より品質が高く、そして、管理のし易いインフラストラクチャとなります。
Managing Increasing Volumes of Data
さまざまなシステムがさまざまなデータを日々創出していますが、それらを構造化して分析用途で用いていくためには多大な労力がこれまでは必要でした。また、そもそも、そのように増え続けるデータそのものを保管しておくためのストレージの維持管理も必要でした。
AWS では、Amazon S3 などを中心とした データレイク 基盤の構築が可能です。データレイクは、すべての構造化データと非構造化データを保存できる一元化されたリポジトリです。データをそのままの形で保存できるため、データを構造化しておく必要がありません。また、ダッシュボードや可視化、ビッグデータ処理、リアルタイム分析、機械学習など、さまざまなタイプの分析を実行し、的確な意思決定に役立てることができます。
Removing Single Points of Failure
オンプレミスの環境でもクラウドの環境でも、どれだけ労力をかけても障害の発生確率はゼロにはすることできません。そのため、あらかじめ障害が発生することを想定した設計(Design for Failure)としておくことが重要です。障害は発生させないのではなく、発生しても影響が出にくくなるような対策を施すということです。
最もシンプルな方法は、構成を冗長化することです。EC2 インスタンスの場合は、前述した水平スケーリングやステートレス化を意識した構成になっていれば、それがそのまま冗長化の対策となります。つまり、インスタンス群のうちの 1 台がハングしてしまったとしても、残りのインスタンスが引き続き処理を担当しつつ、壊れたインスタンスを隔離して新たなインスタンスを再作成すればよいわけです。また、例えば Amazon RDS では障害検知とフェイルオーバーを自動化する機能が備わっていますし、Amazon S3 ではもともと内部的にデータの冗長化が行われます。
EC2 インスタンスの異常検知も、前述した CloudWatch や ELB によって自動化することができます。CloudWatch が収集するメトリクスで異常値があれば、それを契機としてアクションを自動で発報することができますし、ELB にはもともとヘルスチェック機能が備わっているため、背後にいる EC2 インスタンスの障害に気付かせることができます。
データの複製を考えるときは、それが同期レプリケーションなのか、非同期レプリケーションなのかを意識することが重要です。同期レプリケーションでは、プライマリとレプリカにデータが同期的に永続化されるので、強い整合性を持たせることができますが、パフォーマンスは一般に低下します。一方で非同期レプリケーションではパフォーマンスへの影響は軽微ですがレプリケーションラグが生じます。障害対策の文脈では同期をとった複製になりますが、たとえばデータベースへの負荷を分散するといった文脈では、非同期的な複製というアプローチもあります。
必要に応じて、地理的に離れたロケーション(海外など)に気軽にデータを複製できる点がクラウドの魅力です。AWS では約 30 の国や地域(リージョン)でのサービス提供をしており、どのリージョンを使用するかや、リージョンをまたいだデータ転送など、比較的自由に設定することができます。ただし、地理的に離れれば離れるほど、一般にレプリケーションラグも増加することには留意が必要です。
Optimize for Cost
AWS では約 130 回の値下げの実績があり、ただ使っているだけでも利用料金が下がることがありますが、従量課金であることを意識することで、自発的に コスト最適化 にむけたアクションを実施しやすいです。
これまで登場した、たとえば Amazon EC2 や Amazon RDS、Amazon Redshift には複数のリソースタイプ(平たく言うと、スペック)があり、それらを適切に設定することでコストの最適化を目指すことができます。これは、ビジネスの成長フェーズにあわせて柔軟に利用スペックを変更できるということです。CloudWatch などでメトリクス監視をしている場合、どの部分に余剰・無駄がありそうかという部分は比較的容易に分析可能です。また、Compute Optimizer など、そのような余剰・無駄を指摘してくれるための機能も提供されています。
利用している個々のリソースのスペックだけではなく、水平スケーリング時におけるリソース数というのも最適化の対象です。つまり、負荷の上昇に合わせて台数をスケールアウトしていくだけではなく、負荷の低下にあわせて台数をスケールインさせていく部分も忘れずに設定します。もしくは、そのような台数の調整を透過的に行うサーバーレスアーキテクチャを採用することで、よりコストの最適化を進められる可能性があります。
他の方法としては、一部の AWS サービスに存在する購入オプションを適切に選択することでもコストの最適化を図れます。具体的には、例えば Amazon EC2 の場合、長期利用を前提とすることで割引を受けられる Savings Plans や、余剰リソースを格安で利用するための Spot Instance というオプションが提供されています。
Caching
負荷の分散やレイテンシの削減などを目的として、さまざまなレイヤに キャッシュ 機構を導入することも有効です。そしてそれは AWS の場合、それ用の AWS サービスを利用することで容易に実現可能です。
例えばデータベースへの読み取り負荷の軽減を目的として、Amazon ElastiCache が採用されることがあります。このサービスはインメモリデータベースであるため、データの永続化というよりはレスポンスタイムの削減で効果を発揮します。本体のデータベースに格納されている、よく参照されるデータについては ElastiCache 上にキャッシュとしてコピーを残しておくことで、アプリケーションからのリクエストをオフロードすることができ、非常に短いレスポンスタイムで応答することができます。また、Amazon DynamoDB の場合は組み込みのキャッシュ機構(DAX)が備わっています。
他にも、ネットワークの観点では、エンドユーザーの近くにコンテンツのキャッシュを配置しておいて、レスポンスタイムおよびオリジンサーバー(データ本体が格納されているリソース)への負荷低減を図ることができます。AWS の場合は Amazon CloudFront によってこの機能を追加することがきます。
Security
AWS では各 AWS サービスでセキュリティの設定をすることが可能となっており、それを各サービスごとにおこなうことで 多層防御 によるセキュリティの強化が可能になっています。また、システムを直接構成する AWS サービスのセキュリティ設定だけではなく、セキュリティ用の AWS サービスを導入するアプローチも考えられます。一例として、AWS WAF というサービスでは、いわゆるアプリケーションファイアウォールを導入することができます。
一方で、AWS におけるセキュリティは、責任共有モデルによって成り立っています。すなわち、上記のようなセキュリティの設定やセキュリティ系サービスの導入については、最終的には利用者側の責任のもとで設定・導入の判断をしていただくということです。逆に言えば、AWS 側の責任範囲(物理的なレイヤのセキュリティ等)は利用者側で考える必要はありません。
利用者側でセキュリティを考える際に、最小権限の原則を意識することは重要です。これによって操作ミスによるインシデントの発生を抑制したり、万が一セキュリティを突破されたあとの影響範囲を最小限に留めることができます。
また、そもそも自動化の領域を増やして、手動でのオペレーションを減らすというのはセキュリティの観点からも有用です。
最後に、上述したような予防的な統制だけではなく、それでもなおインシデントが発生してしまった時に、それを速やかに検知して対処するといった発見的な統制によるアプローチも重要です。たとえば AWS Config ではその環境上で行われた操作が、事前に取り決めていたルールから逸脱していないかを確認してくれますし、Amazon Inspector では EC2 インスタンス内で脆弱性を含むバージョンのライブラリがインストールされていないかをスキャンしてくれます。
Conclusion
ここでは、クラウドそのものが進化を続けているので、このホワイトペーパーで述べられていること(を 2023 年時点の私の感覚で解釈した内容)は常に最新の情報に更新し続けることが必要であることが述べられています。
現に、このホワイトペーパーは既にアーカイブされていて、今はこれらの内容を 6 つのカテゴリに分割して独立させた AWS Well-Architected フレームワークがその役割を引き継いでいます。大事なのはこれらの文書に書かれている直接的な内容ではなく、なぜそのようなプラクティスが(少なくとも執筆時点で)有効なのかを理解して、その考え方を個々のソリューションのケースに転用していく応用力なのかもしれません。