SvelteKitのDocker化:マルチステージビルドでイメージサイズを1GBから100MBに削減

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

なぜSvelteKitには標準的なDocker化プロセスが必要なのか?

SvelteKitは、現代のWebアプリケーションにおいて非常に強力なフレームワークです。しかし、静的ファイルのみをホストする純粋なSPA(ReactやVueなど)とは異なり, SvelteKitはデフォルトでサーバーサイドレンダリング(SSR)モードで動作します。これは、サーバー側でコードを実行するために、本格的なNode.js環境が必要であることを意味します。

よくある間違いは、「素朴な」Dockerfileを使用することです。単にFROM nodeとしてnpm installを実行するだけでは、イメージサイズが1GBを超えてしまうことがよくあります。肥大化したイメージはCI/CDプロセスを遅延させ、レジストリの容量を圧迫し、コンテナの起動を遅くします。

実際、node_modulesフォルダをそのまま Dockerにコピーしたために、WindowsとLinuxの間でライブラリの競合が発生したプロジェクトを何度も見てきました。さらに悪いことに、adapter-autoを使用していると、デプロイ時にコンテナがどのコマンドでアプリケーションを実行すべきか判断できなくなることもあります。以下に、これらの「落とし穴」を回避する方法を説明します。

ステップ1:Adapter-Nodeへの切り替え

デフォルトでは、SvelteKitは@sveltejs/adapter-autoを使用します。DockerやVPSで安定して動作させるには、@sveltejs/adapter-nodeに切り替えましょう。これにより、アプリケーションが純粋なNode.jsサーバーとしてビルドされ、管理が非常に容易になります。

アダプターをインストールします:

npm install -D @sveltejs/adapter-node

次に、svelte.config.jsファイルを更新します:

import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	preprocess: vitePreprocess(),
	kit: {
		adapter: adapter({ out: 'build' })
	}
};

export default config;

ステップ2:.dockerignoreによるクリーンアップ

Dockerが無駄なファイルをスキャンして時間を浪費しないようにしましょう。node_modulesを除外することで、ビルド時間を数分から数秒に短縮できます。プロジェクトのルートディレクトリに.dockerignoreファイルを作成します:

node_modules
.svelte-kit
build
.env
.env.*
!.env.example
npm-debug.log
.git
.DS_Store

ステップ3:最適化されたDockerfileの作成(マルチステージビルド)

マルチステージビルドの技術は、キッチンで様々な道具を使って料理を作り、最終的に出来上がった料理だけを清潔なテーブルに運ぶようなものです。ビルドにはフル機能のイメージを使用し、その結果(アーティファクト)だけを超軽量なalpineイメージに移します。

# ステージ1: ビルドステージ
FROM node:20-alpine AS builder
WORKDIR /app
# packageファイルを先にコピーしてDockerレイヤーキャッシュを活用する
COPY package*.json ./
RUN npm city
COPY . .
RUN npm run build

# ステージ2: 実行ステージ
FROM node:20-alpine AS runner
WORKDIR /app
# 実際に必要なものだけをコピーする
COPY --from=builder /app/build ./build
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/package-lock.json ./package-lock.json

# 本番環境用の依存関係のみをインストールし、devDependenciesを除外する
RUN npm ci --prod

ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000

CMD ["node", "build"]

結果はどうでしょうか?イメージサイズは1GBから約120〜150MBにまで削減されます。npm ci --prodを使用することで、ViteやSvelte-checkといった肥大化したライブラリを実行環境から排除できます。

ステップ4:Nginx – 必要な保護レイヤー

Node.js単体でも動作しますが、前段にNginxを配置するのが賢明な選択です。NginxはGzip圧縮をより効率的に処理し、SSLの設定も容易にします。nginx/default.confファイルを作成しましょう:

server {
    listen 80;
    server_name localhost;

    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml;

    location / {
        proxy_pass http://sveltekit_app: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;
    }
}

ステップ5:Docker Composeによるデプロイ

アプリケーションとNginxを連携させるには、Docker Composeを使用します。これが複数のコンテナを同時に管理する最も簡単な方法です。

services:
  sveltekit_app:
    build: .
    container_name: sveltekit_container
    restart: always

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - sveltekit_app
    restart: always

運用と確認

docker-compose up -d --buildコマンドでシステムを起動します。コンテナが起動したら、<a href="https://itfromzero.com/ja/docker-ja/ctop-%e3%82%bf%e3%83%bc%e3%83%9f%e3%83%8a%e3%83%ab%e3%81%a7docker%e3%82%b3%e3%83%b3%e3%83%86%e3%83%8a%e3%82%92%e3%83%aa%e3%82%a2%e3%83%ab%e3%82%bf%e3%82%a4%e3%83%a0%e7%9b%a3%e8%a6%96-docker.html">docker stats</a>を実行してみてください。驚くべきことに、標準的なSvelteKitアプリケーションのRAM消費量は通常わずか50〜70MB程度です。これは他の重量級フレームワークと比較して非常に優れた数値です。

重要な教訓:安価なVPS(DigitalOceanの5ドルプランなど)にデプロイする場合は、必ずコンテナのRAM制限を設定してください。これにより、万が一コンテナでメモリリークが発生しても、サーバー全体がクラッシュするのを防ぐことができます。Docker化の成功を祈っています!

Share: