はじめに
最近、仕事でダークカナリアリリース対応を少しだけお手伝いしたので、その時のメモになります。
本記事では、モバイルアプリ側の対応だけにしか触れていません。具体的には、ダークカナリアリリースのアプリかどうか判別するときに使用するHTTPヘッダの追加方法についてのみ説明します。
ダークカナリアリリースとは
ダークカナリアリリースとは、本番環境下で新機能を検証することを目的としたリリース手法を指します。
- カナリアリリース
- 一部のユーザー・開発者にのみ、新バージョンのアプリケーションをリリースする手法。これにより、新バージョンのアプリで何か問題が発生しても、影響範囲を抑えることができる。
- ダークカナリアリリース
- 本番環境で実施するカナリアリリースを指す。対象者は開発者で、新機能を本番環境下で検証することが目的となる。
ダークカナリアリリースのイメージ
今回実装するダークカナリアリリースのイメージ図です。ダークカナリアリリースの実装形式は多種多様だと思うので、下記の図はあくまで一例として捉えてください。
新バージョンのモバイルアプリのリクエストからは、 x-relase-model: dcr
というカスタムのHTTPヘッダー(x-relase-model
)と値(dcr
)を渡すようにします。
HTTPヘッダーx-relase-model: dcr
がリクエストに含まれている場合は、新バージョンのAPIにリクエストを割り振ります。逆にHTTPヘッダーx-relase-model: dcr
がリクエストに含まれていない場合は、現バージョンのAPIに割り振ることによってダークカナリアリリースを実現します。
HTTPヘッダーの追加方針
ビルド時にカスタムの環境変数RELEASE_MODEL=dcr
がセットされている時のみ、x-relase-model: dcr
をHTTPヘッダーに追加するようにします。なお、モバイルアプリのリリースはJenkins経由で実施する想定です。
モバイルアプリの環境
今回、ダークカナリア対応を実施するモバイルアプリのフレームワーク・ライブラリのバージョンは下記となります。
ionic info
コマンドの実行結果
$ ionic info Ionic: Ionic CLI : 6.11.8 (/usr/local/lib/node_modules/@ionic/cli) Ionic Framework : @ionic/angular 5.6.0 @angular-devkit/build-angular : 0.1101.4 @angular-devkit/schematics : 11.1.4 @angular/cli : 11.1.4 @ionic/angular-toolkit : 3.1.0 Utility: cordova-res : not installed native-run : not installed System: NodeJS : v12.13.1 (/usr/local/bin/node) npm : 5.6.0 OS : macOS Catalina
ビルドスクリプトの作成
該当Ionicプロジェクトのscripts/
配下にdcr.js
というスクリプトを作成します。
scripts/dcr.js
'use strict'; /* * ダークカナリアリリース用のHTTPヘッダーを追加する。 */ const env = process.env, releaseModel = env.RELEASE_MODEL || '', fs = require('fs'); const addReleaseModelHeader = (releaseModel) => { if (releaseModel === '') { return; } const httpInterceptorFilePath = './src/app/http-interceptors/http-header-interceptor.ts'; let httpInterceptorFile = fs.readFileSync(httpInterceptorFilePath, 'utf-8'); httpInterceptorFile = httpInterceptorFile.replace( /\/\* \{RELEASE_MODEL\} \*\//, `.set('x-relase-model', '${releaseModel}')` ); fs.writeFileSync(httpInterceptorFilePath, httpInterceptorFile); }; addReleaseModelHeader(releaseModel);
上記のスクリプト(dcr.js
)は、環境変数(RELEASE_MODEL
)に値が設定されていた場合、TypeScriptファイル(http-header-interceptor.ts
)の/* {RELEASE_MODEL} */
というコメント文を.set('x-relase-model', [RELEASE_MODELの値])
に置き換える処理を行います。
package.json
に上記のスクリプトを実行するnpm-scriptsを追加します。
package.json
"scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e", "dcr": "node ./scripts/dcr.js" },
追加した行は、"dcr": "node ./scripts/dcr.js"
の部分になります。こうすることで、npm run dcr
コマンドでdcr.js
を実行することができます。
インターセプターの作成
続いて、HttpClientによるHTTPリクエスト送信とHTTPレスポンス受信の直前に呼び出されるAngularのインターセプターという機能を追加します。
src/app/http-interceptors/http-header-interceptor.ts
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @Injectable() export class HttpHeaderInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const dcrReq = req.clone({ headers: req.headers /* {RELEASE_MODEL} */ }); return next.handle(dcrReq); } }
上記のインターセプターでは、HTTPヘッダーを設定している箇所に/* {RELEASE_MODEL} */
というコメントを挿入しているため、コメント/* {RELEASE_MODEL} */
をdcr.js
で.set('x-relase-model', [RELEASE_MODELの値])
に置き換えることによって、x-relase-model: [RELEASE_MODELの値]
をHTTPヘッダーに追加することができます。
動作確認
RELEASE_MODEL='dcr' npm run dcr
コマンドを実行すると、http-header-interceptor.ts
の内容が下記画像のように変更され、HTTPヘッダーx-relase-model: dcr
が追加されることがわかります。
実際の運用では上記で作成したソースコードを使って、JenkinsなどのCIでモバイルビルド前に毎回npm run dcr
コマンドを実行し、Jenkinsの実行時に環境変数RELEASE_MODEL=dcr
がセットされた場合のみ、ダークカナリアリリース用のアプリとしてリリースする、といったことを実施するイメージです。