SvelteKitアプリをLinodeサーバーにデプロイする方法(SSR対応)

Ubuntu環境でSvelteKitアプリをServer-Side Rendering (SSR) 対応でLinodeサーバーにデプロイする手順を解説します。Node.jsやNginxを活用して、安全でスケーラブルな環境を簡単に構築する方法を学べます。

SvelteKitのNode.jsアダプタを設定

Node.jsアダプタのインストール

SvelteKitをNode.js環境で動作させるために、@sveltejs/adapter-node をインストールします。

以下、アダプタの説明になります。

@sveltejs/adapter-node は、Node.js サーバー環境での使用を目的として設計されています。以下のような特徴があります:

  • 独自のNode.jsサーバーを提供: アプリケーションをNode.js上で直接動作させるため、pm2systemdなどでプロセス管理が簡単にできます。
  • フレキシブルな設定: カスタムミドルウェアの統合や、特定のポート・ホストでの動作設定が可能です。

PM2との相性の良さ

PM2 を使用することで、以下の利点があります:

  • プロセスの自動管理(再起動やクラスターモード)。
  • ログ管理やパフォーマンス監視。
  • サーバー再起動後の自動起動。

adapter-node は、Node.jsサーバーとして動作するため、PM2とシームレスに連携できます。一方、adapter-auto では、他のアダプター(例: adapter-static)が選択される場合があり、PM2のようなプロセス管理ツールとの併用が困難になる可能性があります。

ではアダプタをインストールします。

npm install @sveltejs/adapter-node --save-dev

ではこれさくっとを理解したところでsvelte.config.jsを更新します。

import adapter from '@sveltejs/adapter-node';

/** @type {import('@sveltejs/kit').Config} */
const config = {
    kit: {
        adapter: adapter({
            out: 'build', // 出力先ディレクトリ
            precompress: false, // gzip 圧縮の有効化
            envPrefix: 'SVELTEKIT_' // 環境変数のプレフィックス
        })
    }
};

export default config;

Linodeサーバーのセットアップ

  • Linodeにログインし、新しいインスタンスを作成します。
    • イメージ: Ubuntu 24.04 (または最新バージョン)
    • サイズ: 必要に応じて選択
    • リージョン: 最寄りのリージョンを選択

作成後、インスタンスのIPアドレスrootパスワードをメモします。
プロビジョニングが終わると下記の黄色のアイコンが緑色になり、Runningに更新されることからセットアップが完了したことがわかります。
ここでSSHアクセスのためにIPアドレスの部分をメモっておきます。

SSHでサーバーにアクセス

ssh root@<YOUR_SERVER_IP>

初回接続時には、次のようなプロンプトが表示されます:

Are you sure you want to continue connecting (yes/no)? yes

yes と入力して進めます。その後、サーバーのパスワードを入力します。

サーバーのアップデート

apt update && apt upgrade -y

ユーザーの権限を設定

セキュリティのため、アプリケーションディレクトリをroot以外のユーザーで操作するのが推奨されます。

ユーザーを作成する場合
新しいユーザーを作成します。

adduser appuser

作成したディレクトリの所有権を変更します。

chown -R appuser:appuser /var/www/my-app

アクセスを確認するため、appuserでログインまたは切り替えます。

su - appuser
cd /var/www/my-app

ファイアウォールの設定を確認

ファイアウォールでポート 3000 を開放します。

ファイアウォールルールを確認

ufw status

UFWを有効化 UFWを有効にするには以下のコマンドを実行します:

ufw enable

必要なポートを許可 サーバーで必要なポートを許可します。

HTTP (80番ポート) と HTTPS (443番ポート) を許可:

ufw allow 80
ufw allow 443

ポート 3000 を開放

ufw allow 3000
ufw reload

SSHアクセス(22番ポート)を許可: SSHでサーバーを管理する場合、必ずSSHポートを許可しておきます。

ufw allow 22

最終確認

root@localhost:/var/www/kaihatsublog# ufw status
Status: active

To                         Action      From
--                         ------      ----
3000                       ALLOW       Anywhere
80                         ALLOW       Anywhere
443                        ALLOW       Anywhere
22                         ALLOW       Anywhere
3000 (v6)                  ALLOW       Anywhere (v6)
80 (v6)                    ALLOW       Anywhere (v6)
443 (v6)                   ALLOW       Anywhere (v6)
22 (v6)                    ALLOW       Anywhere (v6)

Node.jsのインストール

  • 最新のLTS版Node.jsをインストールします。
curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
apt install -y nodejs
  • Node.jsのバージョンを確認します。
node -v
npm -v

このようにバージョンが表示されれば次へ進みます。

root@localhost:~# node -v
v18.20.5
root@localhost:~# npm -v
10.8.2

SvelteKitアプリをサーバーにアップロード

SCPまたはGitを使用してアプリをLinodeにアップロードします。今回はGitを使います。(リポは一般に公開しているものを使用しますが、プライベートのリポはセキュリティの設定が必要。)

  • SCPを使用する場合:
scp -r ./my-app root@<YOUR_SERVER_IP>:/var/www/my-app

Gitを使用する場合: サーバー上でGitをインストールし、リポジトリをクローンします。

apt install git
mkdir -p /var/www/kaihatsublog
cd /var/www/kaihatsublog
git clone https://github.com/DanHachijo/KaihatsuBlog.git .

-p オプションは、親ディレクトリが存在しない場合に自動的に作成します。

もしくは下記の方法でリモートリポジトリを設定することで更新したリポジトリを毎回フェッチできるようにする方法がおすすめです。

cd /var/www/kaihatsublog

Gitリモートリポジトリを設定: リモートリポジトリがまだ設定されていない場合、追加します。
git remote add origin https://github.com/DanHachijo/KaihatsuBlog.git

リポジトリを上書きでフェッチ: 現在の内容をリモートリポジトリで上書きします。
git fetch --all
git reset --hard origin/main

必要に応じてコマンドを使用してディレクトリが作成できたことを確認します。

cdChange Directory の略で、現在の作業ディレクトリを移動するためのコマンド。
lslist の略で、指定したディレクトリ内のファイルやディレクトリを一覧表示するコマンド。

依存関係のインストール

アプリケーションディレクトリ内で依存関係をインストールします。package.jsonがあるディレクトリにいることを確認して下記のコマンドを実行。

cd /var/www/my-app
npm install

インストールが終わったらビルドします。

npm run build

こんなエラーがでました。

メモリ不足エラーが発生する場合、以下のようにメモリの上限を増やしてビルドします:

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0xb9c1f0 node::Abort() [node]
 2: 0xaa27ee  [node]
 3: 0xd73b90 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
 4: 0xd73f37 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
 5: 0xf512b5  [node]
 6: 0xf521b8 v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [node]
 7: 0xf626b3  [node]
 8: 0xf63528 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
 9: 0xf3de7e v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]
10: 0xf3f247 v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]
11: 0xf1f7c0 v8::internal::Factory::AllocateRaw(int, v8::internal::AllocationType, v8::internal::AllocationAlignment) [node]
12: 0xf16d8c v8::internal::FactoryBase<v8::internal::Factory>::AllocateRawArray(int, v8::internal::AllocationType) [node]
13: 0xf16f05 v8::internal::FactoryBase<v8::internal::Factory>::NewFixedArrayWithFiller(v8::internal::Handle<v8::internal::Map>, int, v8::internal::Handle<v8::internal::Oddball>, v8::internal::AllocationType) [node]
14: 0x11d124b v8::internal::MaybeHandle<v8::internal::OrderedHashSet> v8::internal::OrderedHashTable<v8::internal::OrderedHashSet, 1>::Allocate<v8::internal::Isolate>(v8::internal::Isolate*, int, v8::internal::AllocationType) [node]
15: 0x1180898 v8::internal::KeyAccumulator::AddKey(v8::internal::Handle<v8::internal::Object>, v8::internal::AddKeyConversion) [node]
16: 0x1180cbe  [node]
17: 0x1185698 v8::internal::KeyAccumulator::CollectOwnPropertyNames(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::Handle<v8::internal::JSObject>) [node]
18: 0x11859ef v8::internal::KeyAccumulator::CollectOwnKeys(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::Handle<v8::internal::JSObject>) [node]
19: 0x1185c83 v8::internal::KeyAccumulator::CollectKeys(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::Handle<v8::internal::JSReceiver>) [node]
20: 0x11865b3 v8::internal::KeyAccumulator::GetKeys(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::KeyCollectionMode, v8::internal::PropertyFilter, v8::internal::GetKeysConversion, bool, bool) [node]
21: 0x115b6ec v8::internal::JSReceiver::DefineProperties(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>) [node]
22: 0x12ef1be v8::internal::Runtime_ObjectCreate(int, unsigned long*, v8::internal::Isolate*) [node]
23: 0x17122f9  [node]
Aborted (core dumped)

安い共有のサーバーを使っているので下記のコマンドでビルドで使うメモリの容量を上げます。

NODE_OPTIONS="--max-old-space-size=8192" npm run build

プロセス管理ツールの設定 (PM2)

PM2のインストール

npm install -g pm2

アプリケーションを起動

以下のコマンドでアプリをPM2で管理します:

pm2 start build/index.js --name my-svelte-app
例:
pm2 start build/index.js --name kaihatsu-blog
ファイルの場所が違う場合は下記
pm2 start .svelte-kit/output/server/index.js --name kaihatsu-blog

下記のスクショのようにプロセスが表示されました。

ちなみに--name my-svelte-app は、PM2(プロセス管理ツール)でアプリケーションを識別するための名前を指定するオプションです。この名前を設定することで、PM2が管理するプロセスを簡単に識別し、操作しやすくなります。

PM2でのプロセス一覧を確認:

pm2 list

Nginxでリバースプロキシを設定

Nginxのインストール

Nginxをインストールします。

apt install nginx

設定ファイルを編集します。

nano /etc/nginx/sites-available/my-svelte-app
例:
nano /etc/nginx/sites-available/kaihatsu-blog

以下の設定を追加します。

server {
    listen 80;
    server_name <YOUR_DOMAIN_OR_IP>;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

YOUR_DOMAIN_OR_IPの箇所にLinodeもしくは自分のデプロイするサーバーのIPアドレスをいれます。また、ここでドメインをいれることができますが、説明のためにいったんIPアドレスをいれておきます。

では、ファイルを保存してnanoエディタを閉じます。

設定を有効化

以下のコマンドで設定を有効化します:

シンボリックリンクを作成して設定を有効にします:

ln -s /etc/nginx/sites-available/kaihatsu-blog /etc/nginx/sites-enabled/

次にnginxをテストしてリスタートします。

nginx -t

#nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
#nginx: configuration file /etc/nginx/nginx.conf test is successful

systemctl restart nginx

この後に、サーバーのIPアドレスを入力してSvelteのアプリが表示されればOKです。

動作確認とトラブルシュート

アクセス確認

ブラウザでサーバーのIPアドレスまたはドメインを入力し、Svelteアプリが表示されるか確認します。

例:

  • http://<YOUR_SERVER_IP>
  • http://yourdomain.com

Svelteアプリが動作しているか確認する方法

PM2のプロセスを確認 アプリが動作中であることを確認します:

pm2 list

アプリ(kaihatsu-blog)がonlineとなっていれば正常です。

アプリを再起動する アプリが動作していない場合、以下のコマンドで起動します:

pm2 restart kaihatsu-blog

強制的に再実行したい場合は、-f オプションを付けて実行します。
pm2 start .svelte-kit/output/server/index.js --name kaihatsu-blog -f

同じアプリが2つ表示されている状況は、意図的でない場合は解消すべきなので注意。

ファイアウォールの設定を確認

ファイアウォールルールを確認

ufw status

3000ポートで稼働しているSvelteアプリを確認

netstat -tuln | grep 3000

もし何も返さないばあいは下記のコマンドで手動でNodeサーバーを起動してみる

HOST=127.0.0.1 PORT=3000 node build/index.js

これでブラウザからアクセスができた場合はPM2 プロセスに環境変数(HOST=127.0.0.1, PORT=3000)が正しく適用されていなかった可能性があります。

PM2 のプロセス環境変数を明示的に設定

PM2 が環境変数を正しく適用していない可能性があります。以下のコマンドで明示的に環境変数を設定しながらアプリを起動してください:

pm2 start build/index.js --name kaihatsu-blog --env production --update-env --node-args="--inspect=0.0.0.0"

では次のステップに進む前にブラウザにサーバーのIPをたたいてSvelteアプリにアクセスできることを確認しておきましょう。

Leave a Comment