App ID を利用して、Node.js クラウド・アプリケーションのシングル・サインオンをスケーリングする
IBM App ID サービスで Redis を使用してアプリの認証をスケーリングする
このチュートリアルでは、App ID サービスを利用する際にスケーラブルな Node.js アプリケーションを実装する方法を学びます。この App ID という IBM Cloud サービスを利用すれば、モバイルおよび Web アプリに認証機能を追加して、IBM Cloud 上で実行中の API とバックエンドをセキュリティーで保護することができます。App ID では、スケーラブルなユーザー・レジストリーを使用して e-メール/パスワードで認証を行うことも、ユーザーが各自の Facebook または Google 資格情報を使用してサインインできるよう、ソーシャル・ログインを追加することもできます。App ID を使用すると、ユーザー・プロファイル情報をホストして、その情報を使って魅力あるエクスペリエンスを創り出すことも可能です。
思い通りの動作をするクラウド・スケーリング・アプリの設計には、アプリのコンポーネントとサービスを適切に水平スケーリングする方法を計画することが求められます。このチュートリアルでは、IBM Cloud 内で Getting Started Node.js アプリケーションを作成し、App ID に応じた変更を加えます。このアプリケーションは単一のインスタンスに対しては正常に機能しますが、アプリケーションを 2 つのインスタンスにスケーリングすると、不安定な動作を見せるようになります。この問題の原因を説明した後、問題を解決するための 1 つの実装として、IBM Cloud 内の Redis Cloud サービスの助けを借りた実装方法を説明します。
“最適なスケーリングを実現するには、各プロセスがステートレスになるように設計し、すべてのプロセスで利用できるバッキング・サービスに常にデータを永続化するようにしてください。”
このチュートリアルを最後まで終えるために必要となるもの
- IBM Cloud アカウント
- 以下のソフトウェアがインストールされたワークステーション
- Git (アプリの基本リポジトリーを複製するため)
- IBM Cloud CLI のバージョン 0.6.5 以降
- テキスト・エディター (提供されているソース・コードに簡単な変更を加えるため)
私が作成したデモ・アプリを実行する際は、ユーザー名には clouduser@example.com
、パスワードには IBMcloud123
を入力してサインインしてください。
ステップ 1. 初期アプリをセットアップする
最初のタスクは、IBM Cloud 内で、App ID サービスを利用した単純な Node.js アプリを作成することです。新規にアプリを作成し、App ID サービス・インスタンスを追加して構成した後、そのサービスをアプリにバインドします。
- IBM Cloud にログインし、「Create Resource (リソースの作成)」をクリックします。
- 「Cloud Foundry Apps (Cloud Foundry アプリケーション)」 > 「SDK for Node.js」の順にクリックします。
- アプリに一意の名前を指定します。例えば、
scaleApp ID
の後にハイフンを続け、その後に名前を一意にするために自分のイニシャルと日付を続けます (例:scaleApp ID-tor1803
)。注: このチュートリアルの残りでは、ここで入力した名前で
< your app name >
を置き換えてください。 - 「Create (作成)」をクリックします。アプリの作成が完了するまで待たずに、以降のステップを続行できます。
- IBM Cloud のカタログにアクセスして、「Security (セキュリティー)」サービス・セクションにある「App ID」をクリックします。
- すべてデフォルト設定のまま、「Create (作成)」をクリックします。サービスが作成されて、サービスのダッシュボードが起動するまで待ちます。
- App ID ダッシュボードの左側のメニューで「Users (ユーザー)」をクリックし、Cloud Directory を開きます。
- 「Add User (ユーザーの追加)」をクリックします。
- 「Add User (ユーザーの追加)」ダイアログ・ボックスで、任意のユーザー名およびパスワードと併せて、その他のプロファイル属性を入力します。「Save (保存)」をクリックしてユーザーを追加します。
- 「Apps (アプリケーション)」ダッシュボードに戻り、アプリケーションをクリックしてアプリケーションの概要ページを開きます。
- アプリケーションの概要ページで、「Connections (接続)」の下にある「Create connection (接続を作成)」をクリックします。
- 先ほど作成した App ID サービスのアイコンを選択してから、「Connect (接続)」をクリックします。
- アプリケーションを再ステージングするよう求められたら、「Cancel (キャンセル)」をクリックします。
- サービスに接続すると、アプリの接続パネルに「App ID」サービスのアイコンが表示されます。
初期アプリのセットアップは、これで完了です。
ステップ 2. アプリケーションのコードに変更を加えてデプロイする
このステップでは、App ID を使用するように構成された Getting Started Node.js アプリが格納されている、GitHub プロジェクトを複製します。このアプリのマニフェストにわずかな変更を加えた後、ステップ 1 で作成した Cloud Foundry アプリケーションに変更後のマニフェストをデプロイします。
- ローカル・ワークステーションでコマンド・プロンプトを開き、以下のコマンドを実行します。
git clone https://github.com/ibmecod/bluemix-scaleAppID.git < your app name >
manifest.yml
ファイルを更新して、アプリのhost
プロパティーとname
プロパティーの値を置き換えます。applications: - name: <your app name> host: <your app name> memory: 128M
- コマンド・ラインの作業ディレクトリーをこのプロジェクトのディレクトリーに変更し、以下のように IBM Cloud コマンド・ライン・ツールを使用して IBM Cloud にログインします。
bx login -a https://api.ng.bluemix.net
- 以下のコマンドを実行して、IBM Cloud 内のアプリを更新およびデプロイします。
bx app push
bx app push
コマンドによって出力されるステージング・メッセージを観察します。デプロイが完了すると、アプリが起動し、アプリのインスタンスの状態が「running
」として表示されます。
これで、アプリをテストできる状態になりました。
ステップ 3. IBM Cloud 内でアプリをスケーリングする
このステップでは、アプリの認証をテストします。まずは、アプリを単一のインスタンスでテストし、次に 2 つ目のインスタンスを追加してテストします。
- ブラウザーの新しいウィンドウまたはタブで、アプリの URL
https://<your app name>.mybluemix.net
を開き、「Log in (ログイン)」ボタンをクリックします。 - デフォルトの App ID ログイン画面が表示されます。App ID ログイン・セレクターで選択可能になっている認証オプションを変更することも、App ID ダッシュボードを使用して、色や表示されるロゴをカスタマイズすることもできます。
App ID サービスを構成するときに「Cloud Directory (クラウド・ディレクトリー)」に追加したユーザー名とパスワードを入力して、「Log in (ログイン)」をクリックします。
認証済みユーザー名と認証のレルムを組み合わせた、「Hello, Cloud User!」のような挨拶メッセージが表示されます。このメッセージは、ユーザーがこのアプリ・インスタンスに対して正常に認証されたことを意味します。 - IBM Cloud の「Apps (アプリケーション)」ダッシュボードに戻って、アプリケーションをスケーリングします。アプリの概要を開き、「Instances (インスタンス数)」ダイヤル上にある「+」ボタンをクリックしてインスタンス数を 2 に増やした後、「Save (保存)」をクリックします。
- IBM Cloud によって、2 つ目のインスタンスが起動されます。その後すぐに、インスタンスのヘルスが新しいインスタンス数で更新されます。
- App ID をテストするために使用したブラウザー・セッションを終了し、ブラウザーの新しいウィンドウまたはタブを開きます。
https://<your app name>.mybluemix.net
にアクセスして、「Log in (ログイン)」ボタンをクリックします。App ID ログイン画面に対する認証を行います。 - 単純な挨拶メッセージが表示されるのではなく、おそらくはログイン・パネルが再表示されるはずです。単純な挨拶メッセージが表示される場合もありますが、再読み込みボタンを 1、2 度クリックすると、結局はログイン・パネルが表示されます。多少の実験によって、ログインに成功したことを確認できる場合もあります。その場合、ログインの成功を通知または確認するメッセージは表示されずに、アプリがホーム・ページに戻ります。要するに、アプリケーションの動作が予測不可能になっているということです。
ステップ 4. テストの結果を理解する
この単純なアプリは、単一のインスタンスのときには認証の問題はありませんでしたが、インスタンスを 2 つに増やすと問題が発生しました。それは、このアプリが「The Twelve-Factor App (アプリの開発に必要な 12 要素)」の方法論に従っていないためです。具体的には、要素 6 の「アプリケーションを 1 つもしくは複数のステートレスなプロセスとして実行する」に違反しています。
この意図しない失敗は、IBM Cloud 内の Node.js アプリが OpenID Connect と組み合わせて使用されていたセッション・インターフェースを実装する際に、express-session
ミドルウェアと Passport を使用していることが原因です。express-session サービスではデフォルトで、セッション・データをインメモリーで保持するため、2 つのインスタンスのうち、どちらか一方しかセッション・データを使用することができません。HTTP セッションで express-session サービスを利用しようとするアプリケーションにはすべて、この問題が実際に影響します。したがって、ここで説明するソリューションは他のすべてのアプリケーションにも有効です。
以下の図に、アプリのインスタンス 0 でしかセッションのデータを使用できない様子を示します。

それでは、これに対するソリューションは何でしょう?「The Twelve-Factor App (アプリの開発に必要な 12 要素)」の要素 6 ではさらに、「永続化する必要のあるすべてのデータは、ステートフルなバッキング・サービス (一般的にはデータベース) に格納しなければならない。」と言っています。GitHub にある express-session のドキュメントに、これに対応したいくつかのバッキング・ストアがリストアップされています。これから、キー・バリュー型のキャッシュを提供する Redis を使用して、この永続化を実装します。このように変更すると、express-session のセッション・データが特定のインスタンスのメモリーから、すべてのインスタンスで使用できる永続ストアに移されることになります。

ステップ 5. Redis を使用してセッションの永続化機能を追加する
IBM Cloud の Redis Cloud サービスは、アプリと同じクラウド環境にホストされているため、最小限の応答時間でこのバッキング・サービスにアクセスすることができます。
- IBM Cloud 内でカタログを表示し、「Data & Analytics (データおよび分析)」カテゴリーから「Redis Cloud」を選択します。
- 「Pricing Plans (料金プラン)」リストから「30MB Free Plan (30MB 無料プラン)」を選択します。
- 「Create (作成)」をクリックして、このサービスを追加します。
- 「RESTAGE (再ステージ)」をクリックして、サービスをアプリに追加するプロセスを完了させます。
- 「Apps (アプリケーション)」ダッシュボードに戻り、アプリケーションをクリックしてアプリケーションの概要ページを開きます。
- アプリケーションの概要ページで、「Connections (接続)」の下にある「Create connection (接続を作成)」をクリックします。
- 先ほど作成した Redis サービスのアイコンを選択してから、「Connect (接続)」をクリックします。
- 「Restage (再ステージ)」をクリックして、サービスをアプリに追加するプロセスを完了させます。
- アプリのソース・コードのローカル・コピーに含まれる package.json で、
dependencies
セクションの 25 行目にコンマを追加し、それに続けて 2 つのエントリーを追加します。"passport": "^0.4.0", "connect-redis" : "^3.3.0", "redis" : "^2.8.0" },
- server.js 内で、
require
セクションの 6 行目の後にredis
とconnect-redis
を追加します。const passport = require("passport"); const redis = require('redis'); const RedisStore = require('connect-redis')(session);
- 112 行目の後に、Redis Cloud サービスの構成詳細を該当する環境から取得して、このサービスに接続するためのコードを追加します。
// additions for App ID support // get configuration for redis backing service and connect to service var redisConfig = appEnv.getService(/Redis.*/) var redisPort = redisConfig.credentials.port; var redisHost = redisConfig.credentials.hostname; var redisPasswd = redisConfig.credentials.password; var redisclient = redis.createClient(redisPort, redisHost, {no_ready_check: true}); redisclient.auth(redisPasswd, function (err) { if (err) { throw err; } }); redisclient.on('connect', function() { console.log('Connected to Redis'); });
- 130 行目近くの
app.use (session (...
ステートメントに、Redis をセッションのストアとして使用するオプションを追加します。app.use(session({ store: new RedisStore({ client: redisclient }), secret: '123456', resave: true, saveUninitialized: true })); app.use(passport.initialize()); app.use(passport.session());
- app.js と package.json をまだ保存していない場合は保存します。
- コマンド・ラインから、以下のコマンドを実行してアプリを更新します。
bx app push
- アプリが起動した後、
recent
オプションを指定した Cloud Foundry のlogs
コマンドを実行します。bx app logs <your app name> --recent
- アプリが起動時に Redis に接続したことを通知するメッセージが出力されていることを確認します。
- manifest.yml ファイルでは指定しているアクティブなインスタンスの数は多くないため、アプリは 2 つのインスタンスを使用して再起動しました。上記の出力例は、インスタンス 1 からのものです。
https://<your app name>.mybluemix.net/
にアクセスしてアプリに戻り、ステップ 3 のログイン・テストを繰り返します。
成功です!アプリは 2 つ (あるいはそれ以上) のインスタンスで正常に機能するようになりました。
まとめ
このチュートリアルでは、IBM Cloud の App ID サービスを利用する単純なアプリを作成し、このアプリを 1 つのインスタンスを実行した状態でテストし、次に 2 つのインスタンスを実行した状態でテストしました。2 つのインスタンスを実行すると、アプリは不安定な動作を見せるようになりました。原因は、Node.js express-session
ミドルウェアがローカルのインメモリー・ストアを使用するようにデフォルト設定されているためです。
この問題を解決するために、IBM Cloud 内の Redis Cloud サービスを使用して、express-session
セッションの永続ストアを追加しました。Redis Cloud の無料プランは、このチュートリアルで使用するには最適ですが、このプランにはライアビリティー/SLA が適用されません。本番で使用するには、Compose for Redis サービスまたは Redis Enterprise Cloud に用意されているいずれかのサービスを利用することを検討してください。