商用サーバレスサービスとしてAWS Lambdaがリリースされてから8年以上が経過しました。その間、MicrosoftからはAzure Functions、GoogleからはGoogle Cloud Functionsがリリースされ、3大クラウドベンダーそれぞれがサーバレスをサービスとして展開しています。本連載では、4回にわたって、この3大クラウドベンダーのサーバレスサービスを比較していきます。連載1回目は、そもそもサーバレスサービスとはどのようなものなのか、3大クラウドベンダーのサーバレスのそれぞれの特徴を概観するとともに、本連載で実行させる関数を、ローカルで実行させるところまで紹介します。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
本連載のサンプルコードをGitHubで公開しています。こちらからダウンロードしてみてください。
3大クラウドベンダーのサーバレスサービスを比べていく本連載。第1回は「そもそもサーバレスとはどういうものなのか?」から話を始めます。
商用サーバレスサービスであるAWS Lambdaがリリースされてから8年以上が経過していますので、この「サーバレス」という単語にそろそろ慣れてきた開発者も多いと思います。しかし、サーバレスという単語そのものは、いまだに誤解を招く用語です。この単語を文字通り解釈すると、サーバが存在しないようなイメージを抱いてしまいます。しかし、サーバレスサービスでは、当然ながらサーバが存在しています。この「レス」が指すものは、サーバの存在そのものではなく、サーバの管理に対してです。
物理的に存在するものであれ、仮想化されたものであれ、Webアプリケーションを実行するためには、サーバが必要です。そのサーバ内には、当然OSは必要ですし、ランタイムなどのアプリケーションの実行環境も必要です。そして本来であれば、サーバマシンに加えて、これらOSなどのWebアプリケーション環境を手動で構築し、管理する必要があります(図1の左側)。
これらの環境構築、管理をなくし、単にソースコードをデプロイするだけでアプリケーションが実行できてしまうサービスが、サーバレスサービスです。つまり、サーバレスというのは、サーバ「管理」レスのことだといえます(図1の右側)。
このサーバレスサービスと似たサービスとして、いわゆるPaaS(Platform as a Service)というものがあります。有名なものに、HerokuやNetlifyなどがあります。PaaSも、サーバレス同様に、Webアプリケーションの実行環境を自動で用意してくれており、管理が自動化されています。開発者は、アプリケーションコードをデプロイするだけで、実行できるようになっています。
これら、PaaSとサーバレスサービスとの決定的な違いは、アプリケーションを常時起動しておくかどうかです。PaaSが従来のWebアプリケーション実行環境に近く、デプロイしたアプリケーションは常時起動された状態であり、そこにユーザーはアクセスして利用していくことになります。
一方、サーバレスサービスの場合は、ユーザーがアクセスしてきた時点でアプリケーションが起動し、処理が終了すると、アプリケーションも終了します。イメージ的には、アクセスの一瞬だけコードが実行されるようなものです。そのため、アプリケーション内では、状態を保持することはしません。状態を保持しないため、アクセス数に応じていくらでもアプリケーションを起動することができ、自動でスケールしていくという特徴があります。
また、サービス料金についても、コードが実行されている一瞬について課金されるという従量課金制度が採用されているのも特徴です。そのため、常時サーバを稼働させる必要があるPaaSに比べて、場合によっては費用が抑えられます。
サーバレスサービスは、このような仕組みのため、デプロイするソースコードも、例えば、JavaScriptの関数一つのみ、のようなイメージとなります。関数を一つだけ定義し、ユーザーのアクセスに呼応して、その関数が実行される、という流れです。もちろん、関数内から別の関数などを呼び出すことで、そこそこの規模のアプリケーションを構築することも可能です。
このようなサービスであることから、当初、サーバレスサービスは、FaaS(Function as a Service)と呼ばれていました。また、次回から実際に3大サーバレスサービスを利用していきますが、そのどれもが、サーバレスサービスで実行されるコードのことを、「関数(function)」と表現しています。
では、その3大サーバレスに話を移していきます。
3大サーバレスというのは、3大クラウドベンダーであるAmazon、Microsoft、Googleがそれぞれ提供しているサーバレスサービスです。それぞれの名称は、AmazonがAWS Lambda、MicrosoftがAzure Functions、GoogleがGoogle Cloud Functionsです。リリース年は、AWS Lambdaが2014年、Azure Functionsが2016年、Google Cloud Functionsが2018年です。
このリリース年だけを見ると、Amazonが最古参であり、Googleが新参者のように思えますが、実は、Googleが2008年にプレビューリリースしたGoogle App Engineが、サーバレスの仕組みとなっています。ただし、このGoogle App Engineが正式リリースされた時点では、サーバレスとはならず、PaaSの形態となっていましたので、Googleはサーバレスの幻の元祖のような扱いになっています。
このような3大サーバレスには、共通する特徴があります。それぞれ以下に列挙していきます。
関数の開発言語として、JavaScript、Python、Java、C#がサポートされています。当然、それらの実行環境(ランタイム)として、Node.js、Python、JVM、.NETが用意されています。これらの言語のうち、JavaScript(Node.js)が、まず選択する言語となっています。
前節で、サーバレスはユーザーのアクセスによって起動すると説明しましたが、必ずしもユーザーのアクセスとは限りません。何かのきっかけによって関数が実行される仕組みとなっています。このきっかけのことを、トリガーといいます。そして、3大サーバレスサービスのどれもが、HTTPトリガーをサポートしています。つまり、特定のURLにアクセスすることで、関数が実行されるようになっています。
HTTP以外のトリガーも利用できますが、それらは、AWS LambdaならばAWSの各サービスから発せられるものとなります。Azure FunctionsもGoogle Cloud Functionsも同様に、Azure、Google Cloudそれぞれの内部サービスから発せられるメッセージをトリガーとして利用できます。そのため、それぞれのサーバレスサービスによってさまざまなトリガーが存在することになります。
同様に、同じクラウドベンダー内のサービスならば、データベースへのデータの書き込みなど、関数内でそれらのサービスを利用することも可能です。
ソースコードのデプロイは、zipファイルとしてアップロードします。その他のデプロイの方法に関しては、AWS、Azure、Google Cloudそれぞれによって違いがあります。
関数は、その他のクラウドサービスと同様に、管理コンソール画面上で作成します。管理コンソールが充実しており、インラインエディタも含まれています。そのため、JavaScriptなど、言語によっては管理コンソール上でコーディングが可能なものもあります。
AWS、Azure、Google Cloudともに、Visual Studio Code(以降、VSCode)用の拡張機能がリリースされており、VSCode上でコーディングからデプロイ、場合によってはローカルテストが可能となっています。
このような共通の特徴があるため、3大サーバレスを比較する本連載においては、以下の環境で各サーバレスサービスを紹介していくことにします。
開発言語としては、3大サーバレスともにまず選択する言語であるJavaScriptを、本連載でも利用します。それに伴い、ランタイムもNode.jsを利用します。
トリガーとしては、3大サーバレス共通でサポートされているHTTPトリガーを利用します。すなわち、特定のURLにアクセスして関数を実行するようにします。
開発ツールとしてはVSCodeを利用し、各クラウドベンダーの拡張機能をインストールして利用していきます。
関数の作成や確認には、管理コンソールも利用しますが、コードのデプロイにはVSCodeの拡張機能を利用していきます。
3大サーバレスの共通特徴を簡単に確認できたところで、ここからは、差分でそれぞれのサーバレスサービスの特徴を簡単に補足していきます。
先に挙げた言語の他に、GoとRubyがサポートされています。デプロイ方法としては、zipファイル以外に、コンテナイメージでのデプロイもサポートしています。
先に挙げた言語の他に、TypeScriptとPowerShellがサポートされています。デプロイ方法としては、zipファイル以外に、GitHubやBitbucket、Azure Reposと連携した継続的なデプロイもサポートしています。
先に挙げた言語の他に、Go、Ruby、PHPがサポートされています。デプロイ方法としては、zipファイル以外に、Cloud Storageからのデプロイも可能です。さらに、GitHubやBitbucketと連携した継続的デプロイもサポートしています。
概論はここまでにして、ここからは実際にサーバレスを利用していくための準備を行っていきます。
本連載で実行するサンプルの実行結果は、リスト1のようなものです。
{ "name": "しんちゃん", "comment": "こんにちは" }
サンプルのURLにアクセスすると、リスト1のJSONデータがレスポンスとして返ってきます。一方、このURLの続きに、例えば「?msg=Hello」とクエリを追加すると、リスト2のようにクエリの内容がレスポンスJSONデータに追加されます。
{ "name": "しんちゃん", "comment": "こんにちは", "msg": "Hello" }
もちろん、このクエリデータは複数追加することができます。例えば「?msg=Hello¬e=World」のようなクエリだと、リスト3のようなJSONデータが返ってきます。
{ "name": "しんちゃん", "comment": "こんにちは", "msg": "Hello", "note": "World" }
このように動作する関数を、3大サーバレスそれぞれで作成していくことになりますが、その前に、まずローカルで作成して実行するところまで紹介します。もちろんローカルでも、サーバレスのランタイムに合わせてNode.js上で動作するものを作成します。その上で、Node.js上で動作するコードが、同じNode.jsランタイムで動作する3大サーバレスでどのように変わっていくのかを味わっていただこうと思います。
それでは、ローカルで動作するコードを作成していきましょう。そのためには、まず環境が必要です。開発環境は、先述の通りVSCodeを利用していきますので、インストールしていない方は、こちらからダウンロードして、インストールしておいてください。メニューなど表示の日本語化に関しては、こちらの記事を参考にしてください。
次に、Node.jsをインストールしておく必要があります。Node.js環境がない方は、こちらから推奨版をダウンロードの上、インストールしておいてください。インストールが完了した方、すでにNode.jsがインストールされている方は、以下のコマンドを実行して、利用できるNode.jsのバージョンを確認しておいてください。
node -v
原稿執筆時点での推奨版は、18.13となっています。
なお、3大サーバレス全てが、ランタイムとしてNode.js 18をサポートしていますが、Azure FunctionsとGoogle Cloud Functionsは、原稿執筆時点では16を推奨しています。ただ、本連載で作成するアプリケーションは16でも18でも問題なく動作しますので、ローカルは最新の18を利用することにします。
次に、コーディングをしていきます。任意のフォルダを作成し、その中にリスト4のindex.jsを作成してください。
import http from "node:http"; // (1) import url from "node:url"; // (2) import {jsonData} from "./data.js"; // (3) const server = http.createServer( // (4) (request, response) => { // (5) const parsedUrl = url.parse(request.url, true); // (6) const responseData = {...jsonData, ...parsedUrl.query}; // (7) response.writeHead(200, {"Content-Type": "application/json; charset=utf-8"}); // (8) response.write(JSON.stringify(responseData)); // (9) response.end(); // (10) } ); server.listen(8000); // (11)
リスト4は典型的なnodeサーバを用意するコードです。ご存じの方も多いと思いますが、簡単に補足しておきます。
(1)でインポートしたhttpオブジェクトのcreateServer()メソッドによって、HTTPサーバオブジェクトを生成します。それが(4)です。ここでは変数名をserverとしています。そのHTTPサーバオブジェクトserverのlisten()メソッドを実行することで、サーバが起動状態となり、引数で渡されたポート番号を監視します。それが(11)であり、ここではポート8000を監視するようにします。
このcreateServer()メソッドを実行する際、引数としてサーバ処理が記述されたコールバック関数を渡します。それが(5)のアロー関数です。このコールバック関数の第1引数がリクエストオブジェクト、第2引数がレスポンスオブジェクトです。
第2引数のレスポンスオブジェクトを利用して、レスポンスを生成します。まず(8)のように、writeHead()メソッドを利用してヘッダ情報を設定します。第1引数がステータスコードであり、(8)では成功を表す200としています。第2引数以降は省略可能ですが、ヘッダ情報を設定したい場合は、(8)のようにオブジェクトを渡します。本サンプルのレスポンスの内容はJSONデータですので、(8)ではContent-TypeをJSONに設定しています。
その後、(9)のようにwrite()メソッドを使って、HTTPボディーを出力します。最終的に出力が全て終了したら、(10)のようにend()メソッドを実行します。
先述のように、本サンプルでは、レスポンスの内容はJSONデータです。しかも、クエリデータが存在する場合は、そのJSONデータにクエリデータが追加されたものを返す必要があります。そこで、まず、クエリ情報を追加する前のJSONデータを別ファイルとして用意します。それが、リスト4の(3)でインポートしているdata.jsです。index.jsと同一階層にこのファイルを作成し、リスト5の内容を記述してください。
export const jsonData = { name: "しんちゃん", comment: "こんにちは" };
ソースコードの内容は問題ないでしょう。リスト1と同内容のオブジェクト変数を用意して、エクスポートしているだけです。
さて、このjsonDataに対して、クエリデータがある場合は値を追加しなければなりません。そのため、まず、クエリデータをオブジェクト形式で取得します。リスト4の(6)では、(2)でインポートしたurlオブジェクトのparseメソッドを利用しています。これによりURLが解析され、そのうちクエリ部分がオブジェクト形式に変換され、queryプロパティとして用意されます。最終的に、jsonDataとこのqueryプロパティを、それぞれスプレッド演算子で展開したものをまとめて一つのオブジェクトとします。それが(7)です。 このようにして用意されたresponseDataが、レスポンスとして返すJSONデータオブジェクトとなります。(9)では、このresponseDataをJSON.stringify()でJSONデータにしています。
これでソースコードは準備できました。いよいよ実行しますが、このままではエラーとなります。というのは、リスト4の(1)〜(3)およびリスト5のように、import/export構文、すなわち、ESモジュールを利用する場合、そのための設定が必要だからです。これは、package.jsonに設定します。index.jsと同一階層に、リスト6のpackage.jsonファイルを作成してください。
{ "type": "module" }
リスト6にあるように、typeプロパティとしてmoduleを指定しないと、ESモジュール機能は利用できません。
これで、実行環境が整いました。index.jsが存在するフォルダ上で、以下のコマンドを実行してください。
node index.js
特にエラーなど表示されなければ、プロンプトが返らずに待ち状態となります。その状態で、ブラウザから以下のURLにアクセスしてください。
リスト1のJSONデータが表示されれば成功です。さらに、クエリ文字列を付け加えて、例えば以下のURLにアクセスしてください。
http://localhost:8000/?msg=Hello¬e=World
リスト3のJSONデータが表示されれば成功です。
一通り確認が終わったら、Nodeサーバを終了させましょう。これには、先のプロンプトが返らないコマンドラインツール上で[ctrl]+[C]を入力してください。プロンプトが返れば、サーバが終了しています。
今回は、サーバレスとは何かということ、3大サーバレスの特徴を紹介し、本連載で実行するサンプルアプリケーションをローカルで実行するところまでを紹介しました。
次回は、実際に3大サーバレスの最古参であるAWS Lambdaを利用し、今回作成したサンプルをLambda上で動作させてみます。
WINGSプロジェクト
有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティー(代表山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手掛ける。2021年10月時点での登録メンバーは55人で、現在も執筆メンバーを募集中。興味のある方は、どしどし応募頂きたい。著書、記事多数。
・サーバーサイド技術の学び舎 - WINGS(https://wings.msn.to/)
・RSS(https://wings.msn.to/contents/rss.php)
・Twitter: @yyamada(https://twitter.com/yyamada)
・Facebook(https://www.facebook.com/WINGSProject)
Copyright © ITmedia, Inc. All Rights Reserved.