@@ -19,9 +19,9 @@ lede: "Next.jsやNuxt.jsなどのサーバーサイドレンダリング必須
1919
2020Next.jsやNuxt.jsなどのサーバーサイドレンダリング必須なフレームワークであれば、Node.jsと一緒にコンテナ化するか、Vercelなどにデプロイする方法があります。こちらはJavaScriptのウェブアプリケーションなので実行環境を用意する必要があります。
2121
22- 一方、SPAとして作成したVue.jsなど、現代のフレームワークで作成したフロントエンドは、ビルドすると静的HTMLとJavaScriptコードになります。ただし、ファイルが存在指定なパスへのリクエストがあった場合にindex.htmlの内容をフォールバックとして返す必要があります 。
22+ 一方、SPAとして作成したVue.jsなど、現代のフレームワークで作成したフロントエンドは、ビルドすると静的HTMLとJavaScriptコードになります。ただし、物理的なファイルが存在しないパスへのリクエストがあった場合にindex.htmlの内容をフォールバックとして返す必要があるため、動作させるにはそのあたりが設定可能なウェブサーバーを使う必要があります。index.htmlをロードするとブラウザ上でJavaScriptのコードが動作しますが、そのコードが内部で持っているURLのパス情報をみて適切なページを表示したり、それでも存在なければJavaScriptがエラー画面を出力します 。
2323
24- [ Webフロントエンド設計ガイドラインのSPAのホスティング] ( https://future-architect.github.io/arch-guidelines/documents/forWebFrontend/web_frontend_guidelines.html#spaのホスティング ) では、いくつか紹介しています 。
24+ フューチャー作のガイドラインの [ Webフロントエンド設計ガイドラインのSPAのホスティング] ( https://future-architect.github.io/arch-guidelines/documents/forWebFrontend/web_frontend_guidelines.html#spaのホスティング ) では、いくつかホスティング方法を紹介しています 。
2525
26261 . CloudFront+S3
27272 . LB+S3
@@ -31,17 +31,17 @@ Next.jsやNuxt.jsなどのサーバーサイドレンダリング必須なフレ
3131
3232* classmethodブログ: [ Application Load Balancer のリスナールールでトランスフォームを構成し、ターゲットにルーティングする前にホストヘッダーや URL パスを書き換えれるようになりました] ( https://dev.classmethod.jp/articles/application-load-balancer-url-header-rewrite/ )
3333
34- これ以外には、ウェブアプリケーション側に配信機能を持たせてしま鵜というのも過去に技術ブログで紹介しました。比較的高速なGoとかRustならありでしょう 。
34+ これ以外には、ウェブアプリケーション側に配信機能を持たせてしまうというのも過去に技術ブログで紹介しました。比較的高速なGoとかRustならこれもありでしょう 。
3535
3636* [ Go 1.16のembedとchiとSingle Page Application] ( https://future-architect.github.io/articles/20210408/ )
3737
38- 今回はDockerイメージを作る、3番目の方法を試そうと思います 。
38+ 今回はガイドラインではSPA用によいとしている1. 3のうち、Dockerイメージを作った3番目の方法を全力で試そうと思います 。
3939
4040# なぜDockerにするか
4141
42- S3とかオブジェクトストレージにおいて配信というのがお手軽ですが、コンテナにまとめておくことでデプロイ時にまとめてフロントエンド資材を入れ替えたりがしやすいのがメリットと考えています 。また、CloudFrontはインターネット公開するサービスには良いのですが、社内システムでは使えません。また、ビルド済みフロントエンドを軽量なサーバーで配信すればリソース消費は少なくて済みます。開発時もフロントエンドを触らない人にとってはありがたいのではないでしょうか
42+ S3とかオブジェクトストレージにおいて配信というのがお手軽ですが、コンテナにまとめておくことでデプロイ時にまとめてフロントエンド資材を入れ替えたり、戻したりがしやすいのがメリットと考えています 。また、CloudFrontはインターネット公開するサービスには良いのですが、社内システムでは使えません。また、ビルド済みフロントエンドを軽量なサーバーで配信すればリソース消費は少なくて済みます。開発時もフロントエンドを触らない人がローカルで動作検証するにはありがたいでしょう。
4343
44- せっかく作るのであればセキュリティを意識したコンテナを目指します。近年、ランサムウェアが流行っています。静的なHTML/JSでアプリを作りフロントエンドを配信するだけのコンテナにすることで攻撃面をかなり狭くできます。ですが不正なプログラムを配る踏み台にはされる可能性があるため、次の項目にもチャレンジしてみます 。
44+ せっかく作るのであればセキュリティを意識したコンテナを目指します。近年、ランサムウェアが流行っています。静的なHTML/JSでアプリを作りフロントエンドを配信するだけのコンテナにしてバックエンドをプライベートネットワークの後ろ側に隠すことで攻撃面をかなり狭くできます。ですがHTMLなどが買い替えられると不正なプログラムを配る踏み台にされる可能性があるため、そうならないために次の項目にもチャレンジしてみます 。
4545
4646* シェルがないDistroless
4747* フロントエンドのartifactは読み込み専用で実行ユーザーでは書き換えられない
@@ -56,7 +56,6 @@ Need to install the following packages:
56565757Ok to proceed? (y) y
5858
59-
6059> npx
6160> " create-vite"
6261
@@ -70,7 +69,7 @@ Ok to proceed? (y) y
7069:
7170```
7271
73- シングルページアプリケーションで正しく動作することをテストするために 、vue-routerを入れてページをいくつか足します。
72+ シングルページアプリケーションとして正しく動作することをテストするために 、vue-routerを入れてページをいくつか足します。
7473
7574``` ts router/index.ts
7675import { createRouter , createWebHistory } from ' vue-router'
@@ -178,14 +177,13 @@ h1 { margin-bottom: 1rem }
178177
179178## nginxの設定
180179
181- Vueアプリができたところで次はサーバーです。Rust製の[ static-web-server] ( https://crates.io/crates/static-web-server ) とか安全そうだし良さそうだなとも思ったのですが、[ APIサーバーへのリクエストをプロキシするような設定がなく、今後も入らなそう] ( https://github.com/static-web-server/static-web-server/issues/489 ) ということもあり見送りました。本番デプロイだけならALBがやってくれるはずなのでstatic -web-serverでも良いかと思います。まあありきたりですがnginxにします 。
180+ Vueアプリができたところで次はサーバーです。Rust製の[static-web-server](https://crates.io/crates/static-web-server)とか安全そうだし良さそうだなとも思ったのですが、[APIサーバーへのリクエストをプロキシするような設定がなく、今後も入らなそう](https://github.com/static-web-server/static-web-server/issues/489)ということもあり見送りました。このプロキシ機能があればウェブフロントエンドとバックエンドが同じドメイン(ポート番号も含めて)動作するので、CORSを機にする必要がなくなります。もちろん、作ったイメージを本番デプロイするだけならALBがやってくれるはずなのでstatic -web-serverでも良いかと思います。ここはありきたりですがnginxにしておきます。なお、今回はイメージサイズは60MBほどになりました。static-web-serverはシングルバイナリで4MBほどらしいので小ささを極めたい場合はstatic-web-serverで試すと良いでしょう 。
182181
183182設定ファイルとしては、SPAで必要なフォールバックを入れたのと、ログは` /var/log ` とかではなく、コンソールに出力するようにしています。
184183
185- 設定ファイル上に` user www-data; ` と書けばユーザーが設定できます。ただし設定しなくても ` nobody ` ユーザーで動作します。最初は非ルートでやるぞ !と設定していたのですが、` nobody ` で十分です 。
184+ 設定ファイル上に` user www-data; ` と書けばユーザーが設定できます。ただし設定しなくてもワーカーは ` nobody ` ユーザーで動作します。最初はセキュリティ強化のためにサーバーは非ルートユーザーで動かすぞ !と設定していたのですが、ワーカーさえルートでなければ実用上は問題ないため、無視底の ` nobody ` で十分だと判断しました。なお、別ユーザーで動かすと、特権ポートの1024以下は使えないため、80番ポートでサーバーを動かすことはできなくなります 。
186185
187186``` sh nginx.conf
188- # nginx.conf
189187
190188worker_processes auto;
191189
@@ -234,20 +232,18 @@ http {
234232
235233## Dockerfile
236234
237- Dockerfileは以下の通りです。最初Geminiに雛形をざっと作ってもらいましたが、いろいろ細かいところを後から修正しました。その際に心がけたポイントは以下の通りです
235+ Dockerfileは以下の通りです。最初Geminiに雛形をざっと作ってもらいましたが、いろいろ細かいところを後から修正しました。その際に心がけたポイントは以下の通りです。
238236
2392371 . ビルドステージ
240- * bindマウント、cacheマウントを駆使してキャッシュフレンドリーな高速ビルド
238+ * bindマウント、cacheマウントを駆使してキャッシュフレンドリーな高速ビルド(生成AIはいつもやってくれない)
2412392 . nginxの設定
242240 * こちらもbindマウント、cacheマウントで効率化
243- * 最終イメージのDistrolessはシェルがなくてmkdirとかもできないので、こちらのステージですべての必要なフォルダを作ったり、ユーザーやグループの設定を引っこ抜いたり、ディレクトリの権限設定をお子なり必要ライブラリをコピーしたりも含めて全て行なっています
241+ * 最終イメージのDistrolessはシェルがなくてmkdirとかもできないので、こちらのステージですべての必要なフォルダを作ったり、ユーザーやグループの設定を引っこ抜いたり、ディレクトリの権限設定を行ったり、nginxの動作に必要ライブラリをコピーしたりも含めて全て行なっています
2442423 . 実行イメージ
245243 * Debianの新しいバージョンのtrixie(13)が使いたい→まだベータ扱いなのでいったん保留
246244 * ビルド済みのHTML/JSを持ってきたり、nginxの設定を持ってきたり
247245 * 実行ユーザー(nobodyから書き換えられないユーザーでHTML/JSを配置
248246
249- 最初は実行イメージは別ユーザー、と思ったのですが、nginx自体、ワーカーを作ってそちらが実質的な処理を行うと言う個性になっており、それはnobodyで動きます。そのため、仮にプロセスが乗っ取られても被害はないかな、ということでやっています。
250-
251247``` Dockerfile Dockerfile
252248# syntax=docker/dockerfile:1
253249
@@ -321,6 +317,8 @@ EXPOSE 80
321317ENTRYPOINT ["/usr/sbin/nginx" , "-g" , "daemon off;" ]
322318```
323319
320+ 実行するには次のようにします。
321+
324322``` sh
325323# ビルド
326324$ docker build -t vue-spa .
@@ -331,10 +329,13 @@ $ docker run --rm -it -p 8080:80 vue-spa
331329
332330## デバッグ実行
333331
334- 実行イメージを` gcr.io/distroless/base-debian12 ` から` gcr.io/distroless/base-debian12:debug ` にして、
332+ 設定を変えてみたい場合のデバッグ方法も紹介しておきます。Distrolessではセキュリティのためにシェルがイメージに含まれていませんが、デバッグ用のイメージが提供されています。
333+
334+ * ランタイムをイメージを` gcr.io/distroless/base-debian12 ` から` gcr.io/distroless/base-debian12:debug ` に変更
335+ * ` docker run --rm -it -p 8080:80 --entrypoint=sh vue-spa ` で、shをnginxの代わりに実行
335336
336337# まとめ
337338
338- Dockerfileは新旧の書き方がウェブには混在しているため、機会をみてはbind/cacheを使ったDockerfileをブログに書くようにしています。今回はSPAの静的HTMLのコンテナを考察して作ってみました。ビルドの効率と実行効率、 セキュリティ、どれも妥協しないDockerfileを作りました。
339+ Dockerfileは新旧の書き方がウェブには混在しているため、機会をみてはbind/cacheを使ったモダンなDockerfileの記法をブログに書くように日頃からしていました。今回はVue連載ということで、Vue製のSPAの静的HTMLのコンテナを作ってみました。単に作るだけでは世の中の有象無象の記事と変わらないので、最新の記法を使ったビルドの効率、実行効率、 セキュリティ、どれも妥協しないDockerfileを作りました。Vue以外の方にも参考にしてもらえる記事になったと思います 。
339340
340341明日は松本朝香さんです。
0 commit comments