【SvelteKit 入門】アダプター設定・ホスティング・コンテナ運用


なぜ序盤にデプロイの解説かというと

**新規プロジェクトを作成し外部アクセス可能な状態に持っていく**

という一連の作業を先にやってしまい、自分のアプリケーションを稼働させる感覚を掴んだ方が全体の理解が深まるというのが私の持論だからです。

:::note warn
GitHub連携など デプロイまでの検証が済んだ後、公開すべきでない実装に入る場合は連携を切ることをお忘れなく。
:::

---

シリーズまとめ（随時追加・更新）

| | |
|:-|:-|
| [【SvelteKit 入門】はじめに](https://qiita.com/jwnr/items/430cd160e90fdfa42336) |  |
| [【SvelteKit 入門】作業の前に](https://qiita.com/jwnr/items/9bb7583b5e3d9f75289c) |  |
| 【SvelteKit 入門】アダプター設定・ホスティング・コンテナ運用 | now reading |
| [【SvelteKit 入門】ルーティング](https://qiita.com/jwnr/items/8932978ca2f50f102e3d) |  |
| [【SvelteKit 入門】データハンドリング(+page.js)](https://qiita.com/jwnr/items/7934196e9ba2e602e063) |  |
| [SvelteKit + microCMS でブログ構築](https://qiita.com/jwnr/items/c7a153f2d1555cc789fe) |  |


------


この記事では実際にファイルを編集していくので、適当なプロジェクトを作って下さい。

```
## テンプレートはデモアプリ
? Which Svelte app template?
>   SvelteKit demo app
    Skeleton project
    Library skeleton project

## 続く選択項目
何でもかまいませんが、全て `no` にしておくと以降の参考画像とズレがなくなると思います。
```


# 1. アダプター設定


公式ドキュメントの該当部分はこちら
https://kit.svelte.dev/docs/adapters
[日本語ドキュメントならこっち](https://kit.svelte.jp/docs/adapters)


## デプロイ用の設定「アダプター」

- SvelteKit は適用されているアダプターに従いビルドを行う
- デフォルトでは`adapter-auto`がセットされている
- デプロイ先に合わせて各種`adapter-○○`が用意されている
- 各アダプターは npm パッケージとしてインストール
- 既存パッケージを使わず、自分でカスタマイズする事も可能

ジョイントパーツを付け換えるイメージ。
最初に付いてるパーツ`adapter-auto`からの換装を解説します。


## アダプターの選択肢
公式のリポジトリに、用意されているアダプターが見やすく一覧になってる部分があります。
[GitHub 公式リポジトリ README.md](https://github.com/sveltejs/kit#documentation)
有名どころのホスティングサービス向けは用意されていますね。
※サービス名ではないアダプターについては後ほど補足


## ファイル編集
編集するファイルは２つあります。

#### ① package.json

![](https://storage.googleapis.com/zenn-user-upload/77189f9025b9-20221008.png)
*`package.json` の `devDependencies` 内*

ここに使用する`adapter-○○`がないといけません。`adapter-auto`は既に入ってますね。
この`adapter-auto`を書き換える or 下の行に追記 どちらかをしてください。

![](https://storage.googleapis.com/zenn-user-upload/535beb722945-20221008.png)
*Vercel用アダプターに書き換えた例*

ファイルを編集して保存したら、再度
```
npm install
```
を実行して下さい。

:::note info
ちなみにこの一連の作業が Node 環境におけるパッケージのインストールであり、「npm i -D xxx」でも同じことができます。
**`npm install` もしくは `npm i`**
→ package.json に記載されたパッケージが全てインストールされてる状態にする
**`npm install xxx` もしくは `npm i xxx`**
→ パッケージを指定してインストール。`-D` オプションで追加対象をdevDependenciesに。
:::

#### ② svelte.config.js

![](https://storage.googleapis.com/zenn-user-upload/39251e08ff0e-20221008.png)

ここもデフォルトは`adapter-auto`になっていると思うので、使用するものに合わせて書き換えます。ここは追記ではなく書き換えです。（残すならコメントアウト）

ちなみに下の `adapter: adapter()` 部分はこのままでokです。
詳細な設定をする場合はオプションをJSON形式で入れることになります。


## アダプター設定まとめ
以上がアダプター設定の流れです。要するに

- 使う`adapter-○○`のパッケージがインストール済で
- `svelte.config.js`でそれを`import` & `config.kit.adapter` に設定

これがクリアされていればジョイントパーツの付け換えは完了です。
ホスティングサービスに慣れてる方は、もうデプロイしてみて下さい。



## 【補足】`adapter-auto`はダメなの？

**実はたいてい`adapter-auto`のままでいけます**

しかしデプロイ先でエラーが出た時の原因切り分けや、アプリケーションの構成に合わせてアダプターを変更する必要が出てきた場合等、**理解しておけば何かと便利なので早い段階で触っておいて欲しかった** という私個人の押し付けが発動したせいです。すみません。

ホスティングサービス向け **以外の** アダプターについて少し解説

|  |  |
|--|--|
| atapter-auto | 自動判別。大手ホスティングサービスなら無設定でいける可能性が高い |
| adapter-static | 静的ファイル群で出力。<br>`nginx`や`GitHub Pages`といった（非Nodeの）WEBサーバーでも運用可能<br>サーバー側でjsが動くようなレンダリングをしないという事であって、ブラウザ駆動のjsは動く。SPAも可。 |
| adapter-node | Node環境向け。<br>コンテナでNode.jsメージに入れて稼働、PM2でクラスター化して稼働、などなど |
| 自分でカスタマイズ | `adapter`をimportするのではなく、JSONで設定を記述 |

アダプターの使い分けにおいては **実装するレンダリングの種類** も少し関わってきますので、SvelteKit の理解が深まったら色々検証してみてください。





# 2. ホスティングサービスにデプロイ

SvelteKit を動かす環境は色々と考えられますが、まずはシンプルなホスティングサービスを利用するのが良いかと思います。GitHub と連携さえすれば、push するだけでビルド&デプロイを回してくれる環境は非常に快適です。

`Vercel`, `Cloudflare Pages`, `Netlify` の3サービスで設定時の要所のみまとめました。

:::note info
少なくともこの3サービスでは`adapter-auto`のままデプロイが可能です。
:::

## 準備
- プロジェクトをGitHub管理
- Vercel / Cloudflare / Netlify いずれかのアカウント
- ↑ サービスに向けてGitHubの権限許可

この部分は解説しませんので、自力解決でお願いします。

では各サービスでの作業を簡単に。


## Vercel

`FRAMEWORK PRESET` で `SvelteKit` を選択する **だけ**。
（しかも多分最初から自動で選択されてる）

![](https://storage.googleapis.com/zenn-user-upload/e95c6b00ff58-20220921.jpeg)



## Cloudflare Pages

`フレームワーク プリセット` で `SvelteKit` を選択。
続いて `環境変数(アドバンスド)` を展開します。

![](https://storage.googleapis.com/zenn-user-upload/9d98bb8b8ad9-20220921.jpeg)

`＋変数を追加する` をクリックし、`NODE_VERSION` = `16` に設定

![](https://storage.googleapis.com/zenn-user-upload/834024461879-20220921.jpeg)
*ちなみに17でもいけます。18は未対応(2022年10月時点)*


## Netlify

プリセットの選択すら無い。値はデフォルトから変更不要。
`Base direcroty` はブランクのままでok。

![](https://storage.googleapis.com/zenn-user-upload/e0b212079d29-20221002.jpeg)
*`Build command` は自動で判別したのだろうか*


## 比較

サービスごとでビルド環境等に若干違いはありますが、設定し直さなくても`adapter-auto`が柔軟に調整してくれているようです。当然専用のアダプターに変えても正常に稼働します。

|| Vercel | Cloudflare Pages | Netlify |
|:--:|:---:|:---:|:---:|
|出力ディレクトリ<br>（初期値）|/public|/.svelte-kit/cloudflare|/build|
|Nodeバージョン<br>（初期値）|16<br>※設定で12,14,16が選択可|12<br>※環境変数で17まで指定可|16<br>※環境変数で18まで指定可|
|ビルド速度|◎|▲ &emsp; → &emsp; ◎<br>（ &ensp; 初回 &ensp; →  2回目〜）|○|
|アクセス速度|◎|◎|○|

※`Vercel`と`Netlify`はデフォルトのNodeバージョンが`16`以上なのでそのままいけますが、`Cloudflare Pages`はデフォルトが`12`なので、環境変数で`16`以上の値にすることで対応できます。


# 3. コンテナ運用

`docker-compose`は無し、`Dockerfile`からのイメージビルド→立ち上げ の流れで行きます。


## 準備

コンテナ稼働で必要な`SvelteKit`側の設定は
**アダプターを `adapter-node` に変更する**
これだけです。


## 起動条件の確認

各段階で必要なファイル・ディレクトリは以下のようになってます

:::note warn
普遍的なものではなく
- SvelteKitプロジェクト下
- アダプターは `adapter-node` でデフォルト設定

という条件です
:::

| 実行コマンド &emsp;&emsp; | 必要 &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp; | 出力される(影響を受ける) &emsp; |
|:--|:--|:--|
| npm install | package.json | .svelte-kit/<br>node_modules/<br>package-lock.json |
| npm ci | package.json<br>package-lock.json | .svelte-kit/<br>node_modules/ |
| npm run build | node_modules/<br>src/<br>static/<br>package.json<br>svelte.config.js<br>vite.config.js | .svelte-kit/<br>build/ |
| SvelteKit 起動 | build/<br>node_modules/<br>package.json | ---- |

これらを踏まえ、マルチステージビルドにすることでイメージサイズを何割か小さくできます。



## イメージ作成

```Dockerfile:Dockerfile
#### 前半はビルド用ステージ
FROM node:18-slim AS STAGE_NAME_OF_BUILD
WORKDIR    /DIR_FOR_BUILD
COPY       . .
RUN        npm install && npm run build

#### 後半は起動用ステージ
FROM node:18-slim
WORKDIR    /DIR_FOR_RUN
COPY       --from=STAGE_NAME_OF_BUILD /DIR_FOR_BUILD/package.json .
COPY       --from=STAGE_NAME_OF_BUILD /DIR_FOR_BUILD/build ./build
RUN        npm install --omit=dev
                       # ↑ --produnction よりこちらが推奨になった模様
ENV        PORT PORT_NUMBER
                # ↑ ポートを変更したい場合は指定。3000のままで良いなら不要
CMD        ["node","build"]
```

大文字スネークケースの部分は好きなものに変更してください。

##### 全体
`Docker`ファイルが複雑になりすぎない、程々のパターンにしました。これでも十分イメージサイズは削減できます。
イメージは私が`slim`派なだけで、`alpine`でも可。Googleの`distroless`を使う場合はステージングがもっと複雑になるので今回は見送りました。
マルチステージの場合、最後のステージに持ってくるファイルがイメージサイズに直結します。`.dockerignore`でビルドステージへのコピーを減らしてもイメージサイズには影響ありません。

##### パッケージのインストール
`npm install`と`npm ci`の使い分けについては長くなるのでここでは割愛します。
依存関係がシビアな場合は`npm ci`に変えてください。

##### NODE_ENV
`ENV NODE_ENV production`等の記述は、プロジェクト内で環境変数を扱う段階になったら追記してください。少なくとも「環境変数？なにそれ」という段階では記述無しで大丈夫です。

##### EXPOSE
`EXPOSE`でポート番号を入れている資料もありますが不要です。
（起動時の`-p 外部ポート:内部ポート`オプションは必要です）



## コンテナ起動
`Cloud Run`等のCaaS利用だったり、昨今のコンテナ環境は様々だと思いますが… 
**マシン上で直接`podman`を使う** というなかなかニッチなケースの例を載せておきます。
```shell
# SvelteKit のディレクトリに上記 Dockerfile を配置
# そのディレクトリで以下のコマンド
# --squash-all はお好みで
podman build -t イメージ名:タグ --squash-all .

# コンテナ起動
podman run -d --init -p 受け付けたいポート:3000 イメージ名
```
ちなみに`podman`の場合、`Dockerfile`ではなく`Containerfile`というファイル名でもいけます。


## 運用時の構成

接続のSSL化やテスト環境との振り分けなどを考えると、リバースプロキシを挟むのが現実的です。

**外からのhttps接続を`nginx`で受け、内部はhttp接続で`SvelteKit`のコンテナに回す** という構成が最もシンプルかと思います。私は`podman`ユーザーですが、`nginx`-`SvelteKit`の通信は同一podで接続するパターン、ネットワークを作成しIPを振って接続するパターン、どちらも問題ありませんでした。

ここで格好良くアーキテクチャ図とともにサンプルコードを記載できれば良いのですが、オレオレ設定も非常に多くお見せするには散らかり過ぎているので見送らせていただきます。









# 【補足】"デプロイ"という表現

この記事ではデプロイを結構広い意味で使いましたがご了承ください。
例えば Vercel を利用する場合、

1. GitHub と連携 & 設定
2. `push`
3. Vercel が自動でビルド & **デプロイ**

というフローが流れていきます。
つまり厳密に言えば、デプロイは Vercel 内部で行われている工程の一つなわけですが・・・
この **外部アクセスが可能な状態まで持っていくこと** をデプロイと呼びました。



# まとめ

デモアプリはサーバー上で動いてますか？
これで今後 SvelteKit で何か作ってみた際、ローカルに限らず動作検証ができますね。

本記事の内容（特にコンテナの`Dockerfile`まわり）について、何かお気づきの点があれば気軽にコメントください。