1. 多段階の Dockerfile
FROM node:22-alpine AS base
RUN corepack enable
# Dependencies stage
FROM base AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --prod
# Build stage
FROM base AS build
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
# Production stage
FROM base AS runtime
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package.json ./
ENV NODE_ENV=production
EXPOSE 3000
USER node
CMD ["node", "dist/app.js"]
2. .dockerignore
node_modules
dist
.git
.env
.env.*
*.md
tests
coverage
.vscode
3.ドッカーコンポーズ
services:
app:
build: .
ports:
- "3000:3000"
env_file: .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
timeout: 3s
redis:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
pgdata:
4. GitHub アクション CI/CD
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:17
env:
POSTGRES_DB: test
POSTGRES_PASSWORD: test
ports: [5432:5432]
options: --health-cmd pg_isready --health-interval 10s
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm typecheck
- run: pnpm lint
- run: pnpm test -- --coverage
env:
DATABASE_URL: postgres://postgres:test@localhost:5432/test
- uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy
run: |
ssh ${{ secrets.SERVER }} "cd /app && git pull && docker compose up -d --build"
5. ベース画像の比較
| 画像 | サイズ | ユースケース |
|---|---|---|
| ノード:22 | ~1GB | 開発、ネイティブビルド |
| ノード:22-スリム | ~200MB | 実稼働環境 (ネイティブ DEP なし) |
| ノード:22-アルパイン | ~130MB | 生産量(最小) |
| gcr.io/distroless/nodejs22 | ~50MB | セキュリティが強化された生産 |
次の記事: 本番環境の監視とスケーリング — ピノ、OpenTelemetry、Prometheus。