こんにちは、サイオステクノロジー武井です。今回は、クラウドネイティブってナニソレオイシイノ?ッていう話から、Azureでクラウドネイティブなアプリ開発を実現するために必要なことをお話しようかと思います。今回の内容は、それほど込み入ったものではなく、「クラウドネイティブとは何?」みたいな、クラウドネイティブを始める取っ掛かりみたいなことを書こうかとおもっております。クラウドネイティブっていうとなんか崇高な概念みたいなイメージが有るのかもしれないですが、そうではなくもっと身近に出来るものとして広めたいと思い、一筆したためた次第であります。どうぞよろしくお付き合いください。
クラウドネイティブとは?
クラウドネイティブの言葉の定義って色々あって難しいです。でも、わかりやすくまとめますと以下のようになるかと思います。
クラウドならではの機能を活かすことにより、従来のオンプレミス上の物理マシンや仮想マシンでは実現出来なかったような運用を実現します。例えば簡単にスケールアウト出来たり、OSのセキュリティアップデートなどの管理が不要になったりとか。
クラウドネイティブを推進している団体「Cloud Native Computing Foundation」(CNCF)が、CNCF Cloud Native Definition v1.0にて、クラウドネイティブってこんな定義ですよーって発表しています。
そこには、コンテナ、サービスメッシュ、マイクロサービスなどの文脈が並んでいて、私の所感ですが、これだけ見ると何やらクラウドネイティブってハードルが高そうだなと感じてしまいます。
でもクラウドネイティブってもっと身近なところにも十分活用出来ると思うのです。例えば、ちょっとした規模のモノリシックなアプリケーションでも十分にクラウドネイティブを活用できると思うのです。
そんな感じで「身近なクラウドネイティブ」というのを目指して本記事とを執筆してみたいと思います。
クラウドネイティブを実現するサービス
クラウドネイティブを実現するためのサービスってどういうものがあるのでしょうか?Azureにはめちゃくちゃクラウドネイティブを実現するサービスがありますが、代表的なものは以下になります。
Azure App Service
アプリケーションを実行する基盤です。もちろん仮想マシンでもアプリケーション実行できるじゃんと思います。しかしAzure App Serviceのすごいところは、AzureポータルというAzureの管理画面からZIPアーカイブしたアプリケーションをアップロードするだけでアプリケーションが実行出来てしまうところです。
Azure App Serviceは実際には裏では仮想マシンが動いています。そのうえでLinuxやWindowsなどのOSがインストールされており、ApacheやTomcatのミドルウェアなどが稼働し、JavaやPHPなどランタイムでアプリケーションが実行されます。でも開発者にはOS、ミドルウェア、ランタイムの部分は全く見えないようになっており、そのあたりはカスタマイズすることすら出来ません。そんな開発者にはカスタマイズすることの出来ない基盤に対して、AzureポータルからZIPアーカイブしたアプリケーションをアップロードすると、その裏に動いている仮想マシン上にアプリケーションがデプロイされて動作するというわけです。
カスタマイズ出来る部分が少ないのがデメリットですが、その分OSとかミドルウェアやランタイムのインストールとかセキュリティアップデートなどの管理も不要です。ぜーんぶAzureがやってくれるので、開発者は開発だけに専念できるのです!!
つまり開発者はクラウドの機能を使うことによって面倒なOSのセキュリティアップデートなどから解放されて、そのメリットを享受しているわけであり、クラウドにしか出来ないことですよね。こういうのもクラウドネイティブと言えると思います。
他にもAzure FunctionsやWeb App for Containersなどアプリケーションを実行出来る基盤が色々あります。クラウドネイティブなアプリケーションを開発する際に、この基盤の選定は一番悩むとことろであり、そして、本記事でご紹介する内容もそういった部分がメインとなっております。
Azure Database for MySQL
Azure上でMySQLを実行するサービスです。
通常MySQLを稼働する際は仮想マシン上にMySQLをインストールしていましたが、Azure Database for MySQLのリソースを作成すると、すぐに実行できるMySQLの基盤が出来上がります。開発者が行うのは、MySQLクライアントでMySQLに接続し、データベースやスキーマの作成を行うだけです。面倒なバックアップなども全部Azureが面倒見てくれます。
Application Insights
アプリケーションに障害が発生したりパフォーマンスが遅いなどの問題が発生した場合は、従来の方法だと各サーバーに出力されたテキスト形式のログとニラメッコしながら原因を解析したと思います。
Application Insightsを使うと、そのあたりの処理をかなりスマートにできます!!
開発者が行うことは、開発したアプリケーションにApplication Insightsのエージェントを埋め込むだけです。埋め込むッていう表現はアレなのですが、例えばJavaであればエージェントのパスを-javaagentに指定するだけです。そうすれば、アプリケーションのコードを変更することなく、CPU使用率やメモリ使用量(メトリックスといいます)や、アプリケーションが出力するログをApplication Insightsに送付します。
Application Insightsはそれらの情報を独自のデータベースに貯めて、グラフ化して可視化したり、SQLライクな構文でログを検索出来たりします。
詳細は以下のブログをご参考くださいませm(_ _)m
世界一わかりみの深いAPM 〜Application Insightsでアプリケーションパフォーマンス管理に全集中!!〜
IaaS、CaaS、PaaS、FaaSとは?
Azureでクラウドネイティブなアプリケーションを実行する基盤を語る上で欠かせない文脈が、このIaaS、CaaS、PaaS、FaaSです。下図は、アプリケーションを実行する基盤を大きく分類し、アプリケーション稼働環境の構成を階層化したものです。大抵のパブリッククラウドは、この分類に基づくアプリケーション実行基盤を提供しており、それぞれメリデリがあります。
上手の灰色の部分は、開発者がその運用から解放された部分を表しています。右に行くごとに、つまりIaaS→CaaS→PaaS→FaaSの順に開発者は色んな呪縛から解放されてラクチンになりそうに見えますが、まぁ、メリットばかりではないので、それをこれから説明しようかとおもってますです。
IaaS
Infrastructure as a Serviceの略であり、OSも含むそれより上のレイヤーを全て自分で面倒見なければなりません。
Azureでいえば、IssSを実現するためにはVirtual Machineというリソースを使います。Virtual Machineは、OSが既にインストール済みのサーバーが提供される形態となり、OSより上のレイヤーの構築・運用は、システム管理者に任されております。
IaaSにおけるシステム管理者の責任範囲が下図になります。まずOS(LinuxやWindowsなど)があり、その上でランタイム(JavaやPHPなどの実行言語)が稼働し、そのランタイムによってミドルウェア(ApacheやTomcat)が動作し、そのミドルウェア上で、アプリケーションフレームワーク上で開発したアプリケーションが稼働するという構図になっております。
上図で、色のついている部分がシステム管理者の責任範囲になり、図からもわかりますように、OSからアプリケーションまでのすべてのレイヤーをシステム管理者は面倒を見なければなりません。よって、IaaSにおいて、アプリケーションの稼働までに至る道筋は以下の通りとなります。
- 仮想マシンを作成する。
- JavaやPHPなどのランタイムをインストールする。
- ApacheやTomcatなどのミドルウェアをインストールする。
- SpringやCakePHPなどのフレームワーク上でアプリケーションを開発する。
- アプリケーションをビルドする。
- ビルドしたアプリケーションをミドルウェア上にデプロイする。
長い道のりですね^^;しかしながら、従来のアプリケーション開発においては、何の疑いもなく、上記のステップを踏んでいたのではないでしょうか?
そして運用管理面においても、OSのセキュリティアップデートや、ミドルウェア、アプリケーションフレームワークへの脆弱性対応なども、システム管理者の責任において、実行することとなります。
CaaS
Container as a Serviceの略です。OSの上にDockerなどのコンテナ実行基盤が動作している環境です。開発者はOSの面倒は見なくてもOKです。コンテナの実行基盤もすでにパブリッククラウド上で提供されており、開発者はDockerリポジトリにDockerイメージをPushするだけで、アプリケーションが稼働します。
AzureでCaaSを実現するサービスは以下があります。
- Web App for Containers
- Azure Container Instances
- Azure Kubernetes Service
PaaS
PaaSでは、IaaSにおいて、システム管理者の責任範囲であった「OS」「Runtime」「Middleware」が、パブリッククラウドの責任範囲になります。
PaaSにおけるシステム管理者の責任範囲を図にしました。
グレーの部分がシステム管理者の責任範囲外、それ以外の色がついている部分が責任範囲内になります。このアーキテクチャにおいて、アプリケーションを稼働させるためのプロセスは、パブリッククラウドによって異なる部分はありますが、概ね以下のようになります。
- SpringやCakePHPなどのフレームワーク上でアプリケーションを開発する。
- アプリケーションをビルドする。
- ビルドしたアプリケーションをミドルウェア上にデプロイする。
- ZIPなどの形式で圧縮して、パブリッククラウドの管理画面からアップロードする。
IaaSと比べで、OS、ランタイム、ミドルウェアの管理が省かれ、かなり手順が楽になりました(๑•̀ㅂ•́)و✧
もちろん、運用管理面においても、OSのセキュリティアップデートや、ミドルウェアの脆弱性対応などは、パブリッククラウド側で行ってくれます。PaaSを提供するAzureのサービスは、のAzure App Serviceになります。
しかしながらそれより上のレイヤーは相変わらず、システム管理者の責任で実施することとなります。例えば、アプリケーションフレームワーク(SpringやCakePHP等)で脆弱性が発覚した場合は、脆弱性が解消されている最新版でアプリケーションを修正・再ビルドし、PaaSにアップロードしなければなりません。
それでも、IaaSに比べれば、アプリケーションを稼働させるまでの手間がかなり軽減されました。
FaaS
FaaSにおけるシステム管理者の責任範囲を図にしました。
だいぶグレーの部分が多くなってきました。サーバーレスアーキテクチャでは、OSやランタイム、ミドルウェアはもちろんのこと、アプリケーションフレームワークさえも不要で、実行するコードのみを記載すれば、プログラムが実行出来る仕組みになっております。
例えばAzureのサーバーレスアーキテクチャである「Azure Functions」ではAzureの管理画面からコードを書くだけで、簡単にRest APIが作れたりします。
つまりサーバーレスアーキテクチャでは、IaaSよりもPaaSよりも手間なく簡単にアプリケーションを動かすことが出来る実行環境と言えます。
IaaS、CaaS、PaaS、FaaSのメリデリ
本章では、IaaS、CaaS、PaaS、FaaSのメリット・デメリットをご説明します。IaaS、CaaS、PaaS、FaaSのメリット・デメリットをきちんとおさえた上で、適材適所を図らなければ、無駄に運用コストがかかったり、制約が多すぎて顧客のニーズを満たせないケースが発生します。
そこで本章では、IaaS、CaaS、PaaS、FaaSのメリット・デメリットを表にしてみました。アーキテクチャ選択の際の一助になれば幸いです。
表中の◎、◯、△については、以下のとおりです。
◎・・・とってもよい( ´∀`)
◯・・・普通(・∀・)
△・・・イマイチ(´・ω・`)
IaaS | CaaS |
PaaS | FaaS | |
保守性 | △ | ◯ | ◯ | ◎ |
自由度 | ◎ | ◯ | ◯ | △ |
開発工数 | △ | ◯ | ◯ | ◎ |
費用 | ◯ | ◯ | ◯ | ◎ |
スケーラビリティ | △ | ◯ | ◯ | ◎ |
移植性 | ◎ | ◎+ | ◯ | △ |
以降は表中の各項目について、詳細にご説明します。
保守性
IaaS | CaaS |
PaaS | FaaS | |
保守性 | △ | ◯ | ◯ | ◎ |
保守性とは、サービス開始後、アプリケーション正常稼働の維持のために発生する作業の効率性を定めた指標になります。OSのセキュリティパッチ適用、アプリケーションフレームワークやライブラリの脆弱性対応、バージョンアップ等、その作業内容は多岐にわたり、しかも不定期に発生します。
IaaSは、先にご説明したとおり、OSのレイヤーからシステム管理者が面倒を見なければなりません。OSのセキィリティパッチやアプリケーションの脆弱性対応はシステム管理者自身が実施する必要があります。
CaaSについては、OSのレイヤーやコンテナ実行基盤などはパブリッククラウドの責任範囲ですので、面倒を見る必要はないのですが、コンテナ自体のセキュリティアップデートは実行しなければいけません。
PaaSについては、アプリケーションのレイヤーより下位の層はパブリッククラウドの責任範囲ですので、OS周りの対応は不要ですが、アプリケーションのレイヤーのメンテナンスは、やはりシステム管理者が実施しなければなりません。昨今話題になっているStrutsの脆弱性対応がその最たる例ですし、記憶に新しいと思います。そのたびにシステム管理者は、各部署や顧客と調整を行い、メンテナンス日程を調整、作業を手順化して、場合によっては休日夜中に対応するという、なんとも大変な作業を強いられるのです。
FaaSでは、インフラのレイヤーは全て抽象化されます。つまり、今までシステム管理者を煩わせていたセキュリティパッチ当てやライブラリのバージョンアップなどは一切気にせず、コーディングのみに集中できるのです。
自由度
IaaS | CaaS |
PaaS | FaaS | |
自由度 | ◎ | ◯ | ◯ | △ |
自由度とは、システムを構築する際に選択するランタイム(開発言語)、ネットワーク構成、ストレージなどをどの程度自由に選択できるかという指標になります。
こちらは、もちろんIaaSに軍配が上がります。OSのレイヤーから扱えるので、開発言語は自由にインストールできますし、接続するストレージのプロトコルもCIFS、NFS、WebDav、iSCSIと種別を選びません。オンプレミス上のサーバーとほぼ同じ自由度で構築が可能です。
CaaSについては、コンテナ上でアプリケーションを実行するので、ランタイム(開発言語)やアプリケーションフレームワークは自由なものを選択できます。ただしかしながら、コンテナの実行基盤によっては、capabilityの設定によって、通常のroot権限を持っていないので動作が制限されたりする可能性があります。例えばWeb App for Containersは任意のストレージをマウントすることが出来ません。
対して、PaaSですが、OSのレイヤーは抽象化されており、システム管理者が扱えるのはランタイム(開発言語)より上のレイヤーになりますので、IaaSほどの自由度はありません。実行出来るランタイムは、パブリッククラウドが提供するものに限られ、例えば、node.jsで動作するアプリケーションを実行させたいのであれば、node.jsを提供しているPaaSを利用することになります。
ランタイムより上の層は自由に扱えますので、フレームワークは自由に選定出来ます。PHPであればCakePHPや、JavaであればSpring Bootなどになりますかね。
FaaSは、さらに自由度が下がります。ランタイムはある程度自由に選択は可能ですが、コードの実行形態が「関数」という最小単位であるがゆえに、アプリケーションフレームワークのメリット(例えばRuby on RailsのScaffold的な)を活かした設計や、有用な外部ライブラリを使った実装が出来ません。あくまで、パブリッククラウドベンダーが用意した独自の「関数」という箱の中にコードを入れ、パブリッククラウドベンダーが用意したイベント駆動形式(Web Hook、メッセージキューやストレージからのトリガー)によってのみ実行されます。
様々な制約があるがゆえに、設計や実装におけるパラダイムシフトも必要になります。
開発工数
IaaS | CaaS |
PaaS | FaaS | |
開発工数 | △ | ◯ | ◯ | ◎ |
開発工数は、その名の通り、アプリケーションを完成させるまでに必要な工数になります。この工数にはインフラ周り(OSやミドルウェア、ランタイムのインストール・セットアップ)も含みます。
これはFaaSが有利な状況です。今までご説明してきましたように、サーバーレスアーキテクチャは「関数」と呼ばれる、アプリケーションを実行する最小形態以外のレイヤーはすべて抽象化されており、アプリケーション開発者は関数の中にコードを記述するだけで、様々な処理を実行出来ます。
しかしながら、「開発工数」は削減が出来ても、「学習工数」「設計工数」は予想以上にかかる可能性があるということは意識しておかなければなりません。
先程も申し上げましたように、FaaSは、様々な制約があるがゆえに、大きなパラダイムシフトを強いられます。
その一例を挙げます。今までのWebアプリケーションは、大きな単一プロセスが複数のスレッドを起動し、スレッド単位でHTTPリクエストを処理していました。こうすることで、CGI隆盛期にあったような、HTTPリクエストごとのプロセス起動コストを抑えて、高速なレスポンスを実現します。
しかしながら、サーバーレスアーキテクチャは、イベント駆動(Web Hook、メッセージキューやストレージからのトリガー)によってコンテナを起動して処理を実行し、その後はコンテナを破棄します(正確に言いますと、ある一定のルールに基づきコンテナを再利用しています)。つまり、動作的にはステートレスなんです。
ステートレスであるということは、リレーショナル・データベースで多く採用されているコネクションプーリングが使えません。コネクションプーリングは、最初に一定数データベースに接続を行い、そのコネクションをプーリングして、HTTPリクエスト単位で使い回すという技術です。先程も申し上げましたように、サーバーレスアーキテクチャはステートレスになるので、それぞれのHTTPリクエストは共有できるコンテキストはありません。つまり、コネクションプーリングは実現出来ないのです。だからといって、イベント駆動をトリガーにして、都度データベースに接続するというのも多大なるコストが発生します。もともと、サーバーレスアーキテクチャは、突発的なリクエストに応じてほぼ無限大にスケールすることを前提としているので、このアーキテクチャでは、コネクションを食いつぶしてしまいます。(ちなみにサーバーレスアーキテクチャでリレーショナル・データベースを用いるのはアンチパターンとされているようです)
そこで、サーバーレスアーキテクチャには、コネクションレスなデータベースを用いることが多くあります。AWSではDynamoDB、AzureではCosmosDBがその代表です。DynamoDB、CosmosDBともにスキーマレスなNoSQLデータベースです。従来のリレーショナル・データベースとはアーキテクチャが大きく異なります。と、いうことは、まずサーバーレスアーキテクチャを導入するということは、NoSQLデータベースの学習コストを必要とする場合が多くあります。こういった理路により、サーバーレスアーキテクチャは、それ自身も多少なりの学習コストを必要としますが、サーバーレスアーキテクチャに依存するアーキテクチャを学習するコストも合わせて発生します。
もう一つ例を挙げさせて頂きます。ステートレスであるがゆえにセッション管理にも工夫が必要になります。ステートフルなアプリケーションの場合は、Webアプリケーション内部でセッションを生成しデータストアに保存、クライアント側にはそのセッションを一意に識別するIDをCookieに乗せて発行し、クライアントはリクエストの都度にそのセッションIDをCookieヘッダーに乗せて、Webアプリケーション側に送信することで、ユーザーを一意に識別します。
しかしながら、先程も説明しましたように、サーバーレスアーキテクチャはステートレスなので、それぞれのHTTPリクエストは共有できるコンテキストはありません。つまり、サーバー側でセッションの管理を行うことができず、クライアント側で状態を持つ必要があります。その手法の最たる例は、JSON Web Tokenを用いた方法になります。(JSON Web Tokenの詳細については、本ブログの記事「JSON Web Tokenによる認証」を参照下さい)
Azure Active DirectoryやGoogle、Yahoo!などのOpenID Connect Providerで認証して、JSON Web Token形式のIDトークンを発行してもらい、サーバーレスアーキテクチャの関数内でIDトークンの検証をするというのがまずは思いつく方法です。しかしながら、その方式だと、全ての関数でIDトークンを検証しなくてはならなくなり、冗長なコードになってしまいます。そのような横断的な処理はAPI Gatewayに任せるというのが、ここ最近の潮流になってきております。詳細は、本ブログの記事「AzureのAPI Gateway(API Management)を用いてOpenID Connect Providerより発行されたJWTを検証」をご覧頂ければと思います。
ここでまた、「API Gateway」という新しいキーワードが出てきました。先程のNoSQLデータベースと同様、サーバーレスアーキテクチャが依存するアーキテクチャを学習しなければいけなくなりました(´・ω・`)
説明がいささか長くなりましたが、このような理路で、サーバーレスアーキテクチャは開発工数を削減することは出来るが、サーバーレスアーキテクチャに依存する新しいアーキテクチャを学習するコスト、また新しいパラダイムの到来による新たな設計技法を学ぶコストが発生することがあります。そこは注意しなければいけない点になります。
費用
IaaS | CaaS |
PaaS | FaaS | |
費用 | ◯ | ◯ | ◯ | ◎ |
費用においても、FaaSの勝利です。
仮想マシンを稼働している時間単位で課金されているIaaSやPaaSと違い、サーバーレスアーキテクチャは純粋に関数を実行した時間のみ課金されます。
例えば簡単なハロワ(Hello Worldと表示するプログラム)を例にして、実際に料金を計算してみましょう。ハロワが月に300万回実行されると想定し、その際のIaaSとサーバーレスアーキテクチャの料金を比較してみます。このケースでは、サーバーレスアーキテクチャにはAzure Functionsを利用すると想定します。(2018年9月5日時点での東日本リージョンにおける料金をもとに計算をしております)
■IaaSの場合
D2s_V3シリーズ(CPU2コア、メモリ8GB)を一ヶ月稼働させるとします。
合計:9,821円
仮想マシンは、時間単位の課金なので、どれだけハロワが実行されても、この料金には変更はありません。
■サーバーレスアーキテクチャの場合
Azure Functionsの料金形態は少々複雑で、「リソース使用量」と「実行回数」によって算出した料金を足し合わせたものが最終的な料金となります。
<リソース使用量>
関数単体が消費するメモリ量に、関数の実行時間をかけ合わせ、それに一定のレートをかけ合わせたものになります。
例えば、ハロワのAzure Functionsが一回の実行あたり128MBを消費し、1回あたりの実行時間は2秒かかるとします。
2018年9月5日時点の東日本リージョンでは、1GBを消費する関数を1秒実行すると、0.001792円の料金が発生します。
単位がGBなので、ハロワのAzure Functionsが一回の実行あたりに利用するメモリ128MBをGBに変換します。
128MB /1024MB = 0.125GB ・・・ ハロワ一回あたりのメモリ消費量
これをもとにその月で使用したメモリ量の合計を算出します。
0.125GB(ハロワ一回あたりのメモリ消費量) × 2秒(ハロワ一回あたりの実行時間) × 300万回/月(ハロワ一月あたりの実行回数) = 750,0000GB秒 ・・・一月でハロワが使用したメモリ量の合計
ただし、Azure Functionsには、嬉しいことに400,000GB秒/月の無料実行分がついてきます。それを加味すると、その月で使用したメモリ量の合計は以下のとおりになります。
750,0000GB秒 – 400,000GB秒 = 350,000GB秒・・・一月でハロワが使用したメモリ量の合計(無料実行分を引いたもの)
これに対して、先程ご説明もしましたが、1GBを消費する関数を1秒実行すると、0.001792円の料金が発生します。つまり最終的な料金は以下になります。
350,000GB秒 × 0.001792円/GB秒 = 627.2円
<実行回数>
こちらは単純に実行回数ごとに加算される料金になります。東日本リージョンでは、2018年9月5日現在、100万回実行あたり22.40円です。300万回/月実行するという今回の条件では料金は以下になります。
22.40円(100万回実行したときの料金) × 300万回/100万回 = 67.2円
リソース使用量と実行回数を合わせると、694.4円となり、IaaSの9,821円とはかなり大きい差が開きました。一年間にすると、109,519円も得をしますので、毎年新しいiPhoneに機種変更ができます。うれしみー(^o^)
サーバーレスアーキテクチャは、イベント駆動によるトリガーに応じて必要なときだけコンテナを起動し処理を実行します。処理が終了するとコンテナを破棄しますので、処理をしていないときはリソースを占有していません。このような形でリソースを効率的に利用することにより、低価格を実現しています。
スケーラビリティ
IaaS | CaaS |
PaaS | FaaS | |
スケーラビリティ | △ | ◯ | ◯ | ◎ |
スケーラビリティは、スケールアウト(負荷に応じて、サーバーの台数を増やすことで処理能力を向上させること)の柔軟性、つまり負荷に応じた必要な処理能力の提供可否を示す指標となります。
こちらも、FaaSが有利です。
IaaSでスケールアウトを行うためには、パブリッククラウドベンダーによって多少の設定の差異はあるものの、概ね以下の手順になります。
- ロードバランサーを構築する。
- スケールアウト、スケールインするための指標を設定する。(例えばサーバー全体のCPU使用率の平均値が、80%以上になったらスケールアウト、50%を下回ったらスケールインなど)
- スケールアウトするサーバーの台数の上限値を決める。
様々な設定をしなければいけない上に、当然といえば当然なのですが、スケールアウトの上限に限界があります。例えば、スケールアウトするサーバーの台数を10台と決めますと、どれだけ負荷が上がったとしても、10台以上はスケールアウトしません。つまり、ある一定以上の負荷が発生すると、処理能力は頭打ちということになります。
PaaSにおいても同様です。IaaSと比較しますと、スケールアウトの設定がある程度自動化されていることが多いですが、やはりスケールアウトするサーバーの台数に上限があるのには変わりありません。
しかしながら、サーバーレスアーキテクチャは、特に何の設定を行わなくても、負荷に応じてほぼ無限にスケールアウトします。これは、「イベント駆動によってコンテナを生成」→「処理を実行」→「コンテナを破棄」というライフサイクルを実現することで、IaaSのように処理が発生していないときにもリソースを占有するということはなく、必要なときに必要に応じて必要な量だけリソースを確保するというアーキテクチャがもたらす恩恵といえます。
サーバーレスアーキテクチャは、予測できないような突発的なリクエストが発生するユースケース(イベントプロモーション時のWebサイトへのアクセス等)においては、大きなアドバンテージを発揮します。
移植性
IaaS | CaaS |
PaaS | FaaS | |
移植性 | ◎ | ◎+ | ◯ | △ |
移植性とは、開発したアプリケーションを他のパブリッククラウドサービスやオンプレミスに移行する際、どれだけ容易にそして確実に実施出来るかを示す指標となります。
こちらについては、IaaS、CaaSが有利です。
IaaSにおいては、OSから上のレイヤーは全てシステム管理者が自由に扱えますので、ネットワーク、ストレージ、ランタイム、もちろんアプリケーションも全て、ほぼ忠実に移植が可能です。
CaaSについては、更に移植性が高いです。可搬性の高いコンテナ技術を活用しているので、移行元と移行先でコンテ実行環境が同一であれば、クラウドを超えてのアプリケーションの移植がIaaSに比べて更に容易になります。
PaaSにおいては、OSを含むそれより下のレイヤーは抽象化されていますので、IaaSと比べると、移行に制約が多くなります。例えば、移行先のパブリッククラウドサービスが提供するPaaSに、移行元のアプリケーションが利用するランタイムが提供されていなかったり、仮に提供されていたとしてもバージョンが合わないと、その分、移行は困難になります。場合によってはアプリケーションの大幅な改修が必要になってしまいます。
サーバーレスアーキテクチャは、さらに移植性が低下します。例えば、Azureが提供するサーバーレスアーキテクチャであるAzure Functionsを一例として考えてみます。
Azure Functionsは、それ単体でサービスを提供することはないのではないでしょうか?アプリケーションには必ず、認証・認可・ロギング・データの永続化などが必要になります。
Azure Functionsでデータの永続化を行う場合、本章の「開発工数」の項でもご説明したようにリレーショナル・データベースを使うことは出来ません(向きません)。必然的に、Azureが提供しているコネクションレスなNoSQLデータベース「CosmosDB」を採用することになります。ここで、「CosmosDB」を使わずとも、例えば、AWSが提供している「DynamoDB」を使うという選択も可能です(多分)。しかしながら、Azure FunctionsとCosmosDBはとても相性がよく、Azure FunctionsからCosmosDBを使う場合は、「バインド」という機能を使うことで、CosmosDBへの面倒な接続処理を記述することなく、CosmosDBへの読み書きを簡単に行うことが可能です。その他、Azure FunctionsからCosmosDBを使うことによって、様々な恩恵を享受出来るので、やはりCosmosDB一択となるでしょう。
認証・認可も同様です。例えばAzure FunctionsでRestfullなAPIを作成する場合に発生する横断的な処理(認証・認可・ロギング)は、API Gatewayに委任するというのが昨今の趨勢となっております(API Gatewayについての詳細は、しつこいようですが、私の記載したブログ「AzureのAPI Gateway(API Management)を用いてOpenID Connect Providerより発行されたJWTを検証」をご参照頂き、「いいね!」を忘れないで下さい)。
Azureが提供するAPI Gatewayサービスに「API Management」というものがあります。これまた、とてもAzure Functionsと相性がよく、セットで使うことで、面倒な設定を簡略化出来るケースが多くあります。例えば、API Managementのバックエンドに設置するAPIを設定するときにも、既に作成済みのAzure Functionsをインポートすることで、簡単にAzure FunctionsをAPI Managementのバックエンドに配置することが可能です。つまり、Azure Functions、CosmosDB、API Managementの組み合わせは、カレーと福神漬け、明太子とごはんのように、相性がよく、お互いの依存度がかなり強いサービスと言えます。
この組み合わせ(Azure Functions、CosmosDB、API Management)をAWSにマッピングすると、2018年9月5日現在では、以下のようになるかと思います。
- Azure Functions → AWS lambda
- CosmosDB → DynamoDB
- API Management → API Gateway
ここで、アプリケーションをAzureからAWSに移行することになったと考えます。上記の機能を移行することは、ほぼほぼ困難と言えます。Azure Functions、CosmosDB、API ManagementともにAzureが提供しているマネージドサービスですので、根底にあるアーキテクチャは、AWSのそれと変わらなくても、設定方法はAzure固有のものとなります。それらをAWSに移行するときの手間は想像に難くありません。
IaaS、CaaS、PaaS、FaaSの選定例
本章では、具体的にどのようなケースでIaaS、CaaS、PaaS、FaaSをそれぞれ選択するかをケース別に説明したいと思います。
ケースその1:今後Azureでしか使わないような単純な作りのWebアプリ
例えば、Webアプリケーションがデータベースに接続して結果を返すような単純な作りのWebアプリケーションであり、AWSなど他のパブリッククラウドに移設する予定がないのであれば、PaaSが適切かと思います。ということは、Azureで言えばAzure App Serviceになります。
CaaS、つまりコンテナでは、Dockerイメージを作成するという手間が発生しますし、コンテナ自体のセキュリティも考えなくてはいけなくなりますので、この場合は当てはまらないかと思います。
FaaS、つまりAzure Functionsでも実現可能です。
ケースその2:Apacheに特殊なモジュールが必要なWebアプリ
例えばOpenID Connectのトークンを処理するためのApacheのモジュール「mod_auth_openidc」が必要なWebアプリケーションのばあいはCaaSが適切です。PaaS、つまりAzure App ServiceではOSやミドルウェアのレイヤーをカスタマイズすることが出来ません。その点、コンテナであれば、そのあたりは自由にカスタマイズ可能です。
AzureでCaaSといえば以下のリソースが候補となります。
- Web App for Containers
- Azure Kubernetes Service
- Azure Container Instances
ケースその3:マルチクラウドで展開したいアプリケーション
AzureやAWSなど複数のクラウド(マルチクラウド)で同じアプリケーションを展開したい場合があります。様々な理由があると思いますが、例えば、シングルクラウドですと、そのクラウドに障害が発生した場合サービスを継続出来ません。よって同様の機能を提供するアプリケーションを他のクラウドにも提供し、障害発生時はDNS変更などでそのクラウドに切り替えるといった対応が予想されます。
また、ベンダーロックインという問題もあります。Azure FunctionsやAWSのLambdaなどあまりにもクラウドベンダー特有の機能を使ってしまうと、いざ他のクラウドに移植したい場合の障壁になる場合があります。
こういったケースでは断然CaaSが有利です。コンテナ技術はその特性から可搬性に優れています。移行元と移行先のコンテナのランタイムが同じであれば、アプリケーションの移植は容易です。
例えば、Azure Kubernetes Service(Azureが提供するマネージドKubernetes)から、Amazon Elastic Kubernetes Service(AWSが提供するマネージドKubernetes)は双方ともコンテナランタイムにDockerを用いているので、基本的にはコンテナを移植すればアプリケーションは稼働するはずです。
このようにマルチクラウドでは、複数のクラウドで同一のアプリケーションを動作させるので、まずはコンテナ化したアプリケーションを作成し、それぞれのクラウドのコンテナ実行環境でコンテナ化したアプリケーションを実行するといった方法がとても効率的です。
クラウドネイティブならではの設計
クラウドネイティブを実現する場合、オンプレミスで開発を行うときになされていた従来の設計方法では適用できない場面が多くあります。つまり、クラウドネイティブを導入する場合、クラウドネイティブに特化した設計を行う必要があります。
そんなクラウドネィティブな設計ってどうのようにすればいいのかって思いますが、その指針を示した設計パターン「The Twelve-Factor App」というものがあり、これに従うとクラウドネイティブな設計ができると言われています。「The Twelve-Factor App」とは、Herokuのエンジニアによって提唱された、モダンなアプリケーションを開発するための12のベストプラクティスをまとめたものです。本家サイトより、12の要素を以下に記載します。
コードベース | バージョン管理されている1つのコードベースと複数のデプロイ |
依存関係 | 依存関係を明示的に宣言し分離する |
設定 |
設定を環境変数に格納する |
バックエンドサービス |
バックエンドサービスをアタッチされたリソースとして扱う |
ビルド、リリース、実行 | ビルド、リリース、実行の3つのステージを厳密に分離する |
プロセス |
アプリケーションを1つもしくは複数のステートレスなプロセスとして実行する |
ポートバインディング |
ポートバインディングを通してサービスを公開する |
並行性 |
プロセスモデルによってスケールアウトする |
廃棄容易性 |
高速な起動とグレースフルシャットダウンで堅牢性を最大化する |
開発/本番一致 |
開発、ステージング、本番環境をできるだけ一致させた状態を保つ |
ログ |
ログをイベントストリームとして扱う |
管理プロセス |
管理タスクを1回限りのプロセスとして実行する |
クラウドネイティブならではの設計って具体的にどういったものなのかというのを、上記の「The Twelve-Factor App」からいくつかピックアップしてご説明します。
設定
クラウドは、簡単にインスタンスの生成が可能であることから、用途に応じて複数の環境を立てることが多くあります。例えば、開発用インスタンス、ステージング用インスタンス、本番用インスタンスなどです。
それらの環境はそれぞれ全く同じ環境から生成去れたものであることが望ましいです。例えば、開発用インスタンス、ステージング用インスタンス、本番用インスタンスもそれぞれ全く同じGitリポジトリからの同じブランチからビルド・デプロイされたものということです。
なぜ「開発用インスタンス、ステージング用インスタンス、本番用インスタンスもそれぞれ全く同じGitリポジトリからの同じブランチからビルド・デプロイされたもの」である必要があるかといいますと。これもクラウドならではのリリース方法である「Blue-Greenデプロイ」というのものに由来します。
Blue-Greenデプロイとは、現在動いている環境(旧環境)とは別に、新たにリリースする別の環境(新環境)を用意して、DNSのレコード切り替え等により、旧環境から新環境にエイヤと切り替える手法です。
これは一般的な話で、AzureでBlue-Greenデプロイを実現するためには、App Serviceというサービスの「スワップ」という機能を使います。スワップは事前に構築された2つのApp Serviceをくるっと入れ替える作業になります。この場合、2つのApp Serviceの間で、エンドユーザーがアクセスするためのURLに変化はなく、アプリの中身だけが入れ替わるようなイメージになります。
以下のような例で考えてみます。これは、2つのApp Serviceが稼働しており、それぞれ本番用とステージング用になります。本番用とステージング用にそれぞれデータベースが用意されていて、本番用のApp Serviceからは本番用データベースに接続し、ステージング用のApp Serviceからはステージング用データベースに接続します。
では、Azureで上記のようにBlue-Greenデプロイを行う過程で、どのようにクラウドネイティブな設計が必要になるのかをご説明します。
- システム運用担当はアプリケーションのソースコードをAzure Repos(AzureのマネージドなGitリポジトリ)にPushします。このとき、アプリケーションの設定ファイル内のデータベース接続先はDB_HOSTと言う環境変数から取得するように設定します。
- Azure DevOpsによるCI/CDパイプライン(本記事では手順は割愛)により、ステージングスロットへデプロイします。このときApp Serviceの設定でアプリケーションに環境変数を流し込めるので、DB_HOSTという環境変数にステージング用DBの接続先を設定しておきます。
- システム管理担当は、ステージングスロットにデプロイされたアプリケーションの動作確認をします。
- システム運用担当は、ステージングスロットのApp Serviceと本番スロットのApp Serviceをスワップします。このとき、本番スロットのApp Serviceは、あらかじめ本番スロットに設定された環境変数がアプリケーション内の設定に反映されて本番用データベースに接続するようになります。
- エンドユーザーは新しいアプリケーションにアクセスします。
上記の過程で、ステージングスロットにあるApp Serviceと本番スロットにあるApp Serviceは全く同じものですね。というか、スワップしてリリースするというBlue-Greenデプロイを採用しているので、同じものでなければならないのです。
ステージングと本番でアプリの中身は全く同じものでありながら、DBの接続先など環境固有の違いがあります。それを吸収するために、設定ファイルに環境変数から値を取得出来るようにして、その環境変数をApp Serviceの設定で外部から流し込むようにしました。このような設計こそがクラウドネイティブな設計であり、「The Twelve-Factor App」の「設定」になります。
プロセス
クラウドならではの特性として、必要に応じてインスタンスの増減が気軽に行えるということがあります。例えば、あらかじめ高負荷が発生するとわかっている日程・時間帯に合わせてインスタンス数を増やしておいたり、もしくはCPU使用率やリクエスト数に応じて、インスタンス数の増減なんてのも可能です。それこそクラウドの最大の特性です。
ただし、その一方で、インスタンスの増減によってデメリットが生じる場合もあります。その最たる例は「セッション」の扱いです。
以下の例を見てください。例えばログイン済み状態をセッションで管理してたとします。認証完了後に、App Service内のデータストア内にセッションを保存し、そのセッションIDに対応したCookieをブラウザに発行するとします。ここで、スケールアウトをさせてApp Serviceを一つ増やしたとします。新しく生成されたApp Serviceにアクセスしたユーザーは、そのセッションIDに対応するセッションが新しいApp Serviceに当然ないので、もう一度ログイン画面が表示されてしまいます。
これを解決する一つの手段としては、下図のようにAzure Cache for RedisというマネージドなRedis Cacheにセッションを保存し、複数のApp ServiceからAzure Cache for Redisを参照するようにします。
まとめ
クラウドネイティブというのがどういった特性を持ち、どういった利点があるのかをご理解いただけましたでしょうか?一概にクラウドネイティブと言っても何から手をつけていいのか難しいと思いますが、本記事がクラウドネイティブに一歩踏み出すためのきっかけになれば幸いです。