はじめに
Webアプリケーションではsessionを用いることが多いと思いますが、前回の記事で触れたようにexpress-session
のデフォルトの実装を用いる場合はメモリにsessionの情報を保持するため、アプリケーションの再起動等で情報は失われてしまいますし、アプリケーションのインスタンスが複数ある場合などに問題が起きるため、プロダクションでは外部のデータストアを用いることになると思います。この用途にはRedisが使われることが多いようですが、IBM Cloudでは(2022年1月現在)RedisにLiteプランがないため、最小構成でもそこそこのコストがかかることになり、実験には向かないようです。そこで今回はIBM Cloudantをsessionのストアとして用いることを検討してみます。
IBM Cloudant
IBM CloudantはJSON document databaseです。IBM Cloudのコンソールから検索するなどしてトップページを開き、Liteプランのインスタンスを1つプロビジョンします。
プロビジョンが完了したら管理画面に入れるようになります。サービス資格情報から接続用の資格情報を作成しておきます。
Session情報を保存するデータベースとしてsessions
を作成しておきます。管理画面のLaunch Dashboard
をクリックしてCloudantの管理画面を開き、Create Database
のボタンからデータベースを作成できます。
Node.jsでの構成
まずは connect-cloudant-store
をインストールします。
$ npm install connect-cloudant-store --save
前回のApp IDのcredentialsと同様、Cloudantのcredentialsもローカルではlocal.json
から、クラウド環境では環境変数から指定するように構成します。まずlocal.json
にcloudant
というキーで先ほどの資格情報をペーストしておきます。クラウド環境用にcustom-environment-variables.json
へも追加しておきます。
{
"appid": {
"__name": "APPID_CREDENTIALS",
"__format": "json"
},
"cloudant": {
"__name": "CLOUDANT_CREDENTIALS",
"__format": "json"
},
"url": "APPLICATION_URL"
}
早速設定した資格情報を使ってsession storeを構成してみます。app.js
を以下のように変更します。
const CloudantStore = require('connect-cloudant-store')(session);
const Cloudant = require('@cloudant/cloudant');
const cloudant = Cloudant({
url: config.get('cloudant.url'),
plugins: {
iamauth: {
iamApiKey: config.get('cloudant.apikey')
}
}
});
const store = new CloudantStore({client: cloudant});
app.use(session({
secret: '123456',
resave: true,
saveUninitialized: true,
store
}));
CloudantStore
のパラメータではIAMベースの認証を行うオプションがないので、先にCloudant
クライアントを作成してそれを渡すようにしました。次にsessionをちゃんと保持できているかの確認を容易に行うため、APIにカウンターをつけてみます。
router.get('/user', function(req, res, next) {
const count = (req.session.count || 0)+1;
req.session.count = count;
res.json({
login: !!req.user,
given_name: req.user?.given_name,
count
});
});
そのカウンターをUIで表示する部分も追加しておきます。
<p>
Hello {user.given_name || "Unknown User"}!
Count: {user.count}
</p>
これでUIをリロードすると、以下のようにCount: 1
が表示され、リロードするたびに増えていくのがわかると思います。
Cloudantの管理画面から中身を見るとちゃんとcount: 1
が保持されているのがわかります。
また、カウントを増やした状態で一度アプリケーションを再起動してもカウントが維持されていることがわかります。
ここまでで基本的な動作は確認できたので、sessionのTTLを60秒にセットしてexpireの挙動を確認します。
const store = new CloudantStore({
client: cloudant,
ttl: 60
});
これでアプリケーションを再起動すると、1分以内にリロードしているうちはログイン状態も保持されカウントもアップしていきますが、1分以上経ってからリロードするとログイン状態も解除され、カウントも1に戻ることがわかります。
IBM Cloud Code Engineでの動作確認
Cloudantをsession storeに使ったアプリケーションがCode Engine上で正しく動くかどうかを確認します。ttl
を3600(1時間)にセットしてコードをGitHubにpushしておきます。
まずCloudantのcredentialsを前回のApp IDのcredentialsと同様、プロジェクトの「シークレット及びconfigmap」からシークレットとして保存しておきます。
次にpushしたコードから新しいイメージをビルドしておく必要があるので、Code Engineのコンソールからプロジェクトを選択し、「イメージ・ビルド」から該当するビルドを選択し、「ビルドの実行依頼」をクリックしてしばらく待つと新しいイメージが作成されます。最後にアプリケーションの構成から「編集して新規リビジョンを作成」をクリックし、新しいイメージを使ったリビジョンを作成します。この時「環境変数」タブからCloudantのcredentialsを環境変数に取り込むように設定します。
リビジョンを作成してしばらく待つとデプロイが完了し、アプリケーションが使えるようになります。トップページにアクセスしてカウントが表示されていること、またログインした状態にしてしばらく放置します。数分経つとCode Engineのインスタンスは自動的に停止されます。
停止されたことが確認できたら、再度アプリケーションをリロードしてみます。この時はインスタンスが停止されているのでロードにしばらく時間がかかりますが、アプリケーションが起動してトップページが表示されると、ログイン状態やカウントが維持されているのがわかります。
注意点
IBM CloudantのLiteプランは、1秒当たりの読み書きの回数に制限があるので、プロダクションでの利用を検討する場合は、Standardプランや他のストアの利用を検討した方が良いです。
また、@cloudant/cloudant
モジュールはdeprecatedなのでconnect-cloudant-store
自体をプロダクションで使い続けるのはあまり得策ではないかもしれません。現状は後継であるclouding-node-sdk
を使ったsession storeの実装はないようなので、もしプロダクションで使う場合は自分で実装するなどの代替手段を取る必要がありそうです。
まとめ
IBM Cloudantをsession storeとして使えることが確認できました。Liteプランなので一部制限はありますが、開発段階では問題なく利用できると思います。