こんにちは。よっしーです(^^)
今日は、SvelteKitでのノードサーバーについて解説しています。
背景
SvelteKitでのノードサーバーについて調査する機会がありましたので、その時の内容を備忘として記事に残しました。
ノードサーバー
スタンドアロン Node サーバーを生成するには、adapter-node を使用します。
使い方
npm i -D @sveltejs/adapter-node でインストールし、アダプターを svelte.config.js に追加します。
svelte.config.js
import adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: adapter()
}
};
コード解説
上記のコードは SvelteKit プロジェクトの設定を定義するものであり、特に Node.js 環境へのデプロイを指定しています。このコードの主な要素と目的を解説します:
import adapter from '@sveltejs/adapter-node';
@sveltejs/adapter-node
パッケージからアダプターをインポートしています。- これは SvelteKit アプリケーションを Node.js 環境で実行するためのアダプターです。
export default { ... }
- SvelteKit の設定オブジェクトをデフォルトエクスポートとしてエクスポートしています。
kit: { ... }
- SvelteKit 固有の設定を含むオブジェクトです。
adapter: adapter()
- 使用するアダプターを指定しています。
- この場合、Node.js アダプターを使用し、デフォルト設定でインスタンス化しています。
この設定ファイルの役割:
- SvelteKit プロジェクトを Node.js 環境にデプロイするよう指定しています。
@sveltejs/adapter-node
を使用することで、アプリケーションは Node.js サーバーとして実行可能になります。
注意点と考慮事項:
- Node.js 環境:
- この設定は、アプリケーションが Node.js サーバーとして実行されることを意味します。
- 例えば、Express.js のようなサーバーフレームワークの上で動作します。
- アダプターオプション:
- 現在の設定ではデフォルトオプションを使用していますが、必要に応じてオプションを追加できます。
- 例:
adapter: adapter({ out: 'build', precompress: false })
- 他の設定オプション:
- 必要に応じて、
kit
オブジェクト内に他の設定(例:paths
、prerender
、csp
など)を追加できます。
- 環境考慮:
- Node.js アダプターは、サーバーサイドレンダリング(SSR)をサポートしています。
- 静的サイトホスティングには適していないため、動的なサーバー環境が必要です。
- パフォーマンスとスケーリング:
- Node.js 環境でのデプロイは、適切なスケーリング戦略が必要になる場合があります。
- 負荷分散やコンテナ化などの考慮が必要かもしれません。
- セキュリティ:
- Node.js サーバーとして実行されるため、適切なセキュリティ対策(例:HTTPS の使用、適切なヘッダーの設定)を行うことが重要です。
この設定は、SvelteKit アプリケーションを Node.js 環境で実行するための基本的な設定です。実際のプロジェクトでは、特定の要件やデプロイ環境に応じて、さらに詳細な設定やカスタマイズが必要になる場合があります。
デプロイ
まず、npm run build
でアプリをビルドします。これにより、アダプターオプションで指定された出力ディレクトリ(デフォルトはbuild
)に本番用サーバーが作成されます。
アプリケーションを実行するには、出力ディレクトリ、プロジェクトのpackage.json
、そしてnode_modules
内の本番依存関係が必要です。本番依存関係は、package.json
とpackage-lock.json
をコピーし、その後npm ci --omit dev
を実行することで生成できます(アプリに依存関係がない場合、このステップはスキップできます)。その後、以下のコマンドでアプリを起動できます:
node build
開発依存関係はRollupを使用してアプリにバンドルされます。特定のパッケージをバンドルするか外部化するかを制御するには、package.json
のdevDependencies
またはdependencies
にそれぞれ配置してください。
レスポンスの圧縮
通常、サーバーからのレスポンスを圧縮することが望ましいでしょう。SSLやロードバランシングのために、すでにリバースプロキシの背後にサーバーをデプロイしている場合、その層で圧縮も処理する方が一般的にパフォーマンスが向上します。これは、Node.jsがシングルスレッドであるためです。
しかし、カスタムサーバーを構築していて、そこに圧縮ミドルウェアを追加したい場合は、@polka/compression
の使用をお勧めします。これは、SvelteKitがレスポンスをストリーミングするためです。より一般的なcompression
パッケージはストリーミングをサポートしておらず、使用すると誤動作を引き起こす可能性があります。
環境変数
開発版とプレビュー版では、SvelteKit は .env ファイル (または Vite によって決定される .env.local、または .env.[mode]) から環境変数を読み取ります。 本番環境では、.env ファイルは自動的に読み込まれません。読み込まれるようにするには、プロジェクトに dotenv をインストールします…
npm install dotenv
…ビルドされたアプリを実行する前にこれを呼び出します。
node -r dotenv/config build
Node.js v20.6 以降を使用している場合は、代わりに –env-file フラグを使用できます。
node --env-file=.env build
PORT, HOST and SOCKET_PATH
デフォルトでは、サーバーはポート3000を使用して0.0.0.0
で接続を受け付けます。これらはPORT
とHOST
環境変数を使用してカスタマイズできます:
HOST=127.0.0.1 PORT=4000 node build
あるいは、サーバーを指定されたソケットパスで接続を受け付けるように設定することもできます。これをSOCKET_PATH
環境変数を使って行う場合、HOST
とPORT
環境変数は無視されます。
SOCKET_PATH=/tmp/socket node build
ORIGIN, PROTOCOL_HEADER, HOST_HEADER, and PORT_HEADER
HTTPはSvelteKitに現在リクエストされているURLを確実に知る方法を提供していません。SvelteKitにアプリがどこでサービスされているかを伝える最も簡単な方法は、ORIGIN
環境変数を設定することです:
ORIGIN=https://my.site node build
# または、ローカルでのプレビューやテスト用に
ORIGIN=http://localhost:3000 node build
これにより、/stuff
パス名へのリクエストが正しくhttps://my.site/stuff
に解決されます。あるいは、リクエストのプロトコルとホストをSvelteKitに伝えるヘッダーを指定することもできます。これらからSvelteKitは元のURLを構築できます:
PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build
x-forwarded-proto
とx-forwarded-host
は、リバースプロキシ(ロードバランサーやCDNなど)を使用している場合に、元のプロトコルとホストを転送する事実上の標準ヘッダーです。これらの変数は、サーバーが信頼できるリバースプロキシの背後にある場合にのみ設定すべきです。そうでない場合、クライアントがこれらのヘッダーを偽装する可能性があります。
プロキシを非標準のポートでホストしていて、リバースプロキシがx-forwarded-port
をサポートしている場合は、PORT_HEADER=x-forwarded-port
も設定できます。
adapter-node
がデプロイメントのURLを正しく判断できない場合、フォームアクションを使用する際に次のエラーが発生する可能性があります:
Cross-site POST form submissions are forbidden
ADDRESS_HEADER and XFF_DEPTH
フックとエンドポイントに渡されるRequestEvent
オブジェクトには、クライアントのIPアドレスを返すevent.getClientAddress()
関数が含まれています。デフォルトでは、これは接続元のremoteAddress
です。サーバーが1つ以上のプロキシ(ロードバランサーなど)の背後にある場合、この値にはクライアントのIPアドレスではなく、最も内側のプロキシのIPアドレスが含まれるため、アドレスを読み取るためのADDRESS_HEADER
を指定する必要があります:
ADDRESS_HEADER=True-Client-IP node build
ヘッダーは簡単に偽装できます。PROTOCOL_HEADER
やHOST_HEADER
と同様に、これらを設定する前に何をしているか理解しておく必要があります。
ADDRESS_HEADER
がX-Forwarded-For
の場合、ヘッダー値にはカンマ区切りのIPアドレスリストが含まれます。XFF_DEPTH
環境変数は、サーバーの前にある信頼できるプロキシの数を指定する必要があります。例えば、3つの信頼できるプロキシがある場合、プロキシ3は元の接続と最初の2つのプロキシのアドレスを転送します:
<クライアントアドレス>, <プロキシ1アドレス>, <プロキシ2アドレス>
一部のガイドでは左端のアドレスを読み取るように指示していますが、これでは偽装に対して脆弱になります:
<偽装アドレス>, <クライアントアドレス>, <プロキシ1アドレス>, <プロキシ2アドレス>
代わりに、信頼できるプロキシの数を考慮して、右から読み取ります。この場合、XFF_DEPTH=3
を使用します。
左端のアドレスを読み取る必要がある場合(偽装を気にしない場合)- 例えば、ジオロケーションサービスを提供する場合、IPアドレスが信頼できることよりも実在することの方が重要な場合があります。その場合、アプリ内でx-forwarded-for
ヘッダーを検査することで実現できます。
BODY_SIZE_LIMIT
受け入れる最大リクエストボディサイズをバイト単位で指定します。これはストリーミング中も含まれます。ボディサイズはキロバイト(K
)、メガバイト(M
)、ギガバイト(G
)の単位接尾辞を使って指定することもできます。例えば、512K
や1M
のように指定できます。デフォルトは512kbです。この設定は値をInfinity
(アダプターの古いバージョンでは0)に設定することで無効にできます。より高度な制御が必要な場合は、handle
内でカスタムチェックを実装することができます。
SHUTDOWN_TIMEOUT
SIGTERM
またはSIGINT
シグナルを受信した後、残っている接続を強制的に閉じるまでの待機時間を秒単位で指定します。デフォルトは30
秒です。内部的には、アダプターがcloseAllConnections
を呼び出します。詳細については、グレースフルシャットダウンのセクションを参照してください。
この設定は、アプリケーションが適切にシャットダウンするための重要な役割を果たします。これにより、進行中のリクエストを完了させる時間を確保しつつ、新しい接続を受け付けないようにすることができます。30秒のデフォルト値は多くの場合に適していますが、アプリケーションの特性に応じて調整が必要な場合もあります。
IDLE_TIMEOUT
systemdのソケットアクティベーションを使用する場合、IDLE_TIMEOUT
はリクエストを受信しない状態が続いた後、アプリを自動的にスリープ状態にするまでの秒数を指定します。設定されていない場合、アプリは継続的に実行されます。詳細については、ソケットアクティベーションのセクションを参照してください。
この設定は、システムリソースの効率的な利用に役立ちます。長時間アイドル状態のアプリケーションをスリープさせることで、サーバーのリソースを解放し、他のプロセスに割り当てることができます。必要に応じて、新しいリクエストが来たときにアプリケーションが自動的に再起動します。
ただし、IDLE_TIMEOUT
の値を設定する際は、アプリケーションの特性や期待されるトラフィックパターンを考慮する必要があります。頻繁なリクエストが予想される場合は、この値を長めに設定するか、完全に無効にすることも検討すべきです。一方、トラフィックが散発的な場合は、適切なタイムアウト値を設定することでリソースを節約できます。
おわりに
今日は、 SvelteKitでのノードサーバーについて解説しました。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント