はじめに
前編で Azure App Service on Linux 上の Angular 製 Web アプリケーションが動くようになりました。
後編は、Angular アプリに Bootstrap を組み込んでみます。もうしばらくお付き合いいただければ。
といっても、かっこいいアプリはひとっっっつも作りませんし、作れません。志は低く、ボタンに Bootstrap のスタイルが当たるかどうかだけを確認します。
さっさと必要な情報だけ知りたい人はこちら。
まずはローカルから
ローカルで Angular アプリに Bootstrap を追加します。
Bootstrap のセットアップ
ググったら、ng-bootstrap
というやつを使うそうです。
流れで、angular/localize
というのも追加します。
難しいことはよくわかりませんが、詳しくはココとかココに書いてあります。
$ npm install --save @ng-bootstrap/ng-bootstrap
$ npm run ng add @angular/localize
HTML をいじる
src/app/app.component.html
を編集します。
上述の Bootstrap のボタンのサンプルをほぼそのまま使います。
<style> /* https://www.techiediaries.com/css-centering/ */ .center { display: flex; justify-content: center; height: 100vh; margin: 10pt; } .center button{ align-self: center; margin: 1pt; } </style> <div class="center"> <button type="button" class="btn btn-primary">Primary</button> <button type="button" class="btn btn-secondary">Secondary</button> <button type="button" class="btn btn-success">Success</button> <button type="button" class="btn btn-danger">Danger</button> <button type="button" class="btn btn-warning">Warning</button> <button type="button" class="btn btn-info">Info</button> <button type="button" class="btn btn-light">Light</button> <button type="button" class="btn btn-dark">Dark</button> <button type="button" class="btn btn-link">Link</button> </div>
angular.json
に CSS ファイルを追加します。結構深い場所です。
{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "sampleapp": { "projectType": "application", // : "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "dist/sampleapp", // : "styles": [ "src/styles.css", "node_modules/bootstrap/dist/css/bootstrap.min.css" // <-- コレ ],
npm start
で起動してみると。。。
いい感じですね。Bootstrap っぽいです。
App Service on Linux へデプロイ
この状態で App Service on Linux にデプロイして、ブラウザーで表示してみると。。。
ゑ?
デフォルトのボタンが並んでいて、Bootstrap らしさが皆無になりました。
何が起こっているんでしょう。
CSS を確認してみる
まずはローカルから。
Web ブラウザーの開発者ツールで、このページの構成を確認してみると、追加した bootstrap.min.css
がブラウザーに読み込まれていますね。
続いて、App Service on Linux の方を。
node_modules/bootstrap
がありません!
なるほど、これでは Bootstrap らしさがでるはずがありません。
なぜ CSS が読み込まれないのでしょう?
デプロイ ファイルを確認してみる
Web SSH で App Service on Linux 上のファイルを確認してみます。
当然、あります。
# /home/site/wwwroot
にデプロイ ファイルが展開され、ここがプロジェクトのルートになります。
もう少し色々見てみると、
おや?
プロジェクト ルートの node_modules
が /node_modules
(ルートの直下) へのシンボリック リンクになっています。
このシンボリック リンクが何やら怪しそうです。
黒幕発見
まずこのシンボリック リンクが何者なのか、説明しますが、完全に App Service on Linux の内部処理の話です。
興味がない or さっさと解決したい人はこちらへ。
ビルド エンジン Oryx
App Service on Linux には、Oryx というビルド エンジンが組み込まれています。
これはデプロイ後に、アプリケーションの依存モジュールをインストールしたり、ビルド処理を実行したりします。
また起動時には各種ランタイム向けのスタートアップ スクリプトを生成します。
Oryx による Node.js アプリの起動処理
Node.js ランタイムを設定した App Service on Linux に Node.js アプリケーションをデプロイすると、以下のことが一気に行われます。
- npm install
- npm build
- node_modules ディレクトリを tar.gz 化
/home/site/wwwroot/node_modules
->/home/site/wwwroot/node_modules.tar.gz
次にアプリを起動した時には、/opt/startup/startup.sh
(<- これも Oryx が作る) により下記の処理が行われます。
/home/site/wwwroot/node_modules.tar.gz
を/node_modules
へ展開/home/site/wwwroot/node_modules
があれば、mv
で/home/site/wwwroot/__del_node_modules
へ移動/node_modules
へのシンボリック リンクを/home/site/wwwroot/node_modules
に作成- スタートアップ コマンド、もしくは、npm start を実行
アプリ自体は /home/site/wwwroot
に展開されるので、アプリが見る node_modules
の実態は全て /node_modules
にあります。
よりディープなお話 (読み飛ばし可)
なぜこんな(一見面倒そうな)方法を使っているのでしょうか?
App Service on Linux では、/home 配下はネットワーク越しにマウントされた別のファイル サーバーにあります。
これは SSH でログインして mount
コマンドからも確認できます。
ということは、/home/site/wwwroot
に node_modules
とその中のモジュールをそのまま展開すると、モジュールのロードのために、ネットワーク/ファイル I/O が大量に発生します。
さらに、/home
以下は、利用しているプラン サイズに応じた最大容量が決まっています。
一方で、シンボリック リンク方式を使うと、/home 以下にある依存モジュール関連の実ファイルは node_modules.tar.gz
のみとなり(これもアプリケーション実行後は使いません)、モジュールのロードは、シンボリック リンクを介して、ローカルディスク上の /node_modules
から行われます。
というわけで、シンボリック リンク方式を使うと、下記のメリットがあるのです。
- /home の容量を節約できる
- ネットワーク越しの I/O が減る
もちろんこの挙動を変えて、/home/site/wwwroot/node_modules
に直接展開することも可能ですが、容量や I/O のことを考えると、あまりお勧めはしません。
Angular の振る舞い
一方、Angular は、既定の振る舞いとして、シンボリック リンクがあれば、それより先は追いかけないようです。
つまり、先ほど追加した Bootstrap の CSS ファイルは /home/site/wwwroot/node_modules
にあると思わせて、シンボリック リンクを介して、/node_modules/bootstrap/dist/css/bootstrap.min.css
にあるので、(シンボリック リンクを追いかけない) Angular の既定の設定では読み込まれません。
この App Service on Linux と Angular、それぞれの既定の設定の相性で、CSS ファイルが正常に読み込まれていませんでした。
解決編
angular.json に、"preserveSymlinks": true
と一行追加してください。
Angular の設定を App Service on Linux に寄せます。
{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "sampleapp": { "projectType": "application", // : "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "preserveSymlinks": true // <-- コレ
これで、/home/site/wwwroot/node_modules
のシンボリック リンクも Web ブラウザーから読み込むことが可能になります。
App Service on Linux 上のファイルを上のように編集すると、、、
はい、できました(ふぅ、、、)。
今回、Angular を試した時に、たまたまシンボリック リンクの影響を発見しました。
他のフレームワークでもシンボリック リンクの扱いが実ファイルと異なる動作の場合、App Service on Linux のこの動作の影響を受ける可能性あるので、ご注意いただければ。
ちなみに
2020 年 2 月現在、Oryx が生成する /opt/startup/statup.sh
が /node_modules
内に循環参照を作る、という予想外の動作をします。
これが発生すると、Angular アプリケーションの画面は真っ白になります。。。
発生条件や回避方法が気になる方は、下記 issue をご覧あれ。
ではでは。