本番用Docker Compose作成

This commit is contained in:
CyberRex
2026-05-25 10:01:34 +09:00
parent 40e7953ee5
commit d4918762d2
12 changed files with 183 additions and 7 deletions

6
.dockerignore Normal file
View File

@@ -0,0 +1,6 @@
node_modules
.pnpm-store
dist
.env
*.log
.git

View File

@@ -1,4 +1,5 @@
NODE_ENV=development NODE_ENV=development
HOST=127.0.0.1
PORT=3000 PORT=3000
DATABASE_URL=postgres://certremind:certremind@localhost:5432/certremind DATABASE_URL=postgres://certremind:certremind@localhost:5432/certremind
COOKIE_SECRET=replace-with-a-long-random-string COOKIE_SECRET=replace-with-a-long-random-string

View File

@@ -29,6 +29,8 @@
```text ```text
pnpm install pnpm install
docker compose up -d postgres docker compose up -d postgres
docker compose up -d --build
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
pnpm dev pnpm dev
pnpm monitor:once pnpm monitor:once
pnpm monitor:worker pnpm monitor:worker
@@ -45,10 +47,29 @@ Frontend: http://127.0.0.1:5173/
API: http://127.0.0.1:3000 API: http://127.0.0.1:3000
``` ```
Docker Compose:
```text
本番相当: docker compose up -d --build
開発用: docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
本番 URL: http://127.0.0.1:3000/
開発 URL: http://127.0.0.1:5173/
```
- `docker-compose.yml` は本番相当の標準 Compose として、PostgreSQL、Web/API アプリ、`monitor-worker` を起動する。
- `docker-compose.dev.yml` は開発用 override として、アプリを bind mount し `pnpm dev` で Vite と API を起動する。
- Docker CLI が Compose v2 サブコマンドに対応していない環境では、同じ引数で `docker-compose` を使う。
- Compose のアプリ公開ポートは `127.0.0.1` に限定し、外部 NIC へ公開しない。
- Compose の PostgreSQL はホストに公開せず、`app` / `monitor-worker` から内部ネットワーク経由でのみアクセスする。
- コンテナ内では `HOST=0.0.0.0` を使い、ホスト側への公開範囲は Compose の `ports` で制御する。
## ディレクトリ構成 ## ディレクトリ構成
```text ```text
db/schema.sql db/schema.sql
Dockerfile
docker-compose.yml
docker-compose.dev.yml
README.md README.md
public/push-sw.js public/push-sw.js
src/client src/client
@@ -79,6 +100,7 @@ tests/urlPolicy.test.js
主な項目: 主な項目:
- `NODE_ENV` - `NODE_ENV`
- `HOST`
- `PORT` - `PORT`
- `DATABASE_URL` - `DATABASE_URL`
- `COOKIE_SECRET` - `COOKIE_SECRET`
@@ -89,6 +111,8 @@ tests/urlPolicy.test.js
補足: 補足:
- `HOST` 未設定時は `127.0.0.1` で待ち受ける。Docker Compose ではコンテナ内到達性のため `0.0.0.0` を指定する。
- ホスト実行時の `DATABASE_URL` は通常 `localhost:5432`、Compose 内のサービスでは `postgres:5432` を使う。ただし Compose 起動中の PostgreSQL はホストに公開しない。
- `OPENSSL_PATH` 未設定時は `openssl` を使う。 - `OPENSSL_PATH` 未設定時は `openssl` を使う。
- Windows では Git 付属の `openssl.exe` を自動検出する実装がある。 - Windows では Git 付属の `openssl.exe` を自動検出する実装がある。
- VAPID private key が未設定の場合、Push 実送信は失敗として `delivery_result` に記録される。 - VAPID private key が未設定の場合、Push 実送信は失敗として `delivery_result` に記録される。
@@ -121,6 +145,9 @@ DDL は `db/schema.sql` に保管している。
注意: 注意:
- `docker-compose.yml` は初回 DB 起動時に `db/schema.sql` を読み込む。既存 volume がある場合、schema の変更は自動再適用されない。 - `docker-compose.yml` は初回 DB 起動時に `db/schema.sql` を読み込む。既存 volume がある場合、schema の変更は自動再適用されない。
- `docker-compose.yml` はアプリ本体と監視 worker も起動するため、DB だけを起動したい場合は `docker compose up -d postgres` を使う。
- 開発用 Compose は `docker-compose.dev.yml` を重ねて使い、Vite の公開ポートも `127.0.0.1:5173` に限定する。
- Compose の PostgreSQL へホストから直接接続する前提の作業は行わない。DB 操作が必要な場合はコンテナ内実行や一時的な明示設定を検討する。
## 実装済み API ## 実装済み API
@@ -317,6 +344,7 @@ pnpm monitor:worker
- Webhook / Push の送信失敗は `delivery_result` に記録する。 - Webhook / Push の送信失敗は `delivery_result` に記録する。
- 監視ジョブの開始、終了、失敗は構造化ログで出力する。 - 監視ジョブの開始、終了、失敗は構造化ログで出力する。
- `pnpm monitor:worker` は 1 時間ごとに監視ジョブを実行する長時間起動プロセス。 - `pnpm monitor:worker` は 1 時間ごとに監視ジョブを実行する長時間起動プロセス。
- 本番相当の Docker Compose では `monitor-worker` サービスとして `pnpm monitor:worker` を常駐起動する。
注意: 注意:
@@ -363,6 +391,7 @@ pnpm monitor:worker
- UI の削除操作には確認ダイアログを追加する。 - UI の削除操作には確認ダイアログを追加する。
- 既存の左サイドメニューとレスポンシブ挙動を崩さない。 - 既存の左サイドメニューとレスポンシブ挙動を崩さない。
- フロントエンドの入力チェックを追加しても、サーバー側検証を必ず維持する。 - フロントエンドの入力チェックを追加しても、サーバー側検証を必ず維持する。
- Docker Compose のアプリポートを変更する場合も、ホスト側 bind は `127.0.0.1` に限定する。
- 変更後は少なくとも以下を実行する。 - 変更後は少なくとも以下を実行する。
```text ```text

22
Dockerfile Normal file
View File

@@ -0,0 +1,22 @@
FROM node:22-bookworm-slim
WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates openssl \
&& rm -rf /var/lib/apt/lists/* \
&& npm install -g pnpm@11.1.3
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm exec vite build
ENV NODE_ENV=production \
HOST=0.0.0.0 \
PORT=3000
EXPOSE 3000
CMD ["pnpm", "start"]

View File

@@ -25,6 +25,39 @@ Frontend: http://127.0.0.1:5173/
API: http://127.0.0.1:3000 API: http://127.0.0.1:3000
``` ```
## Docker Compose
Production-like startup:
```text
docker compose up -d --build
```
If your Docker CLI does not provide the Compose v2 subcommand, use `docker-compose` with the same arguments.
Production URL:
```text
App: http://127.0.0.1:3000/
```
This starts PostgreSQL, the Web/API application, and the hourly certificate monitor worker. The application container serves the built React assets from `dist` through the Hono server.
Docker-based development startup:
```text
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
```
Development URLs:
```text
Frontend: http://127.0.0.1:5173/
API: http://127.0.0.1:3000
```
The Compose application port mappings bind to `127.0.0.1` only, so the application ports are not published on external network interfaces. PostgreSQL is not published to the host; only the `app` and `monitor-worker` services can reach it through the internal Compose network. Inside containers, the application binds to `0.0.0.0` through `HOST=0.0.0.0`; host exposure is controlled by Compose.
Run the one-shot certificate monitor: Run the one-shot certificate monitor:
```text ```text
@@ -58,6 +91,7 @@ Copy `.env.example` to `.env` for local development.
| Name | Required | Default | Purpose | | Name | Required | Default | Purpose |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `NODE_ENV` | No | `development` | Runtime mode. `production` enables secure cookies. | | `NODE_ENV` | No | `development` | Runtime mode. `production` enables secure cookies. |
| `HOST` | No | `127.0.0.1` | Server listen host. Compose sets this to `0.0.0.0` inside containers. |
| `PORT` | No | `3000` | API server port. | | `PORT` | No | `3000` | API server port. |
| `DATABASE_URL` | No | `postgres://certremind:certremind@localhost:5432/certremind` | PostgreSQL connection string. | | `DATABASE_URL` | No | `postgres://certremind:certremind@localhost:5432/certremind` | PostgreSQL connection string. |
| `COOKIE_SECRET` | Reserved | none | Reserved for future signed-cookie support. | | `COOKIE_SECRET` | Reserved | none | Reserved for future signed-cookie support. |
@@ -66,9 +100,12 @@ Copy `.env.example` to `.env` for local development.
| `VAPID_SUBJECT` | For Push | `mailto:admin@example.com` | VAPID contact subject. | | `VAPID_SUBJECT` | For Push | `mailto:admin@example.com` | VAPID contact subject. |
| `OPENSSL_PATH` | No | `openssl` | OpenSSL executable path. On Windows, the app can also detect Git's bundled `openssl.exe`. | | `OPENSSL_PATH` | No | `openssl` | OpenSSL executable path. On Windows, the app can also detect Git's bundled `openssl.exe`. |
For local host execution, `DATABASE_URL` normally points to `localhost:5432`. For Docker Compose services, it points to the internal service name: `postgres://certremind:certremind@postgres:5432/certremind`.
## Operational Notes ## Operational Notes
- Run `pnpm monitor:worker` as a long-lived Node process for hourly certificate checks. - Run `pnpm monitor:worker` as a long-lived Node process for hourly certificate checks.
- `docker compose up -d --build` runs the monitor worker as the `monitor-worker` service.
- `pnpm monitor:once` remains available for manual checks or external schedulers. - `pnpm monitor:once` remains available for manual checks or external schedulers.
- The monitor limits concurrent external certificate checks and records per-site failures without stopping the whole run. - The monitor limits concurrent external certificate checks and records per-site failures without stopping the whole run.
- Webhook URLs and monitored site URLs must be HTTPS and reject localhost/private IPv4 targets. - Webhook URLs and monitored site URLs must be HTTPS and reject localhost/private IPv4 targets.

View File

@@ -1,6 +1,6 @@
# CertRemind 開発進捗 # CertRemind 開発進捗
最終更新: 2026-05-22 最終更新: 2026-05-25
## 現在の実装状況 ## 現在の実装状況
@@ -10,7 +10,8 @@
- Hono.js ベースの API サーバー構成 - Hono.js ベースの API サーバー構成
- React / Vite ベースのフロントエンド構成 - React / Vite ベースのフロントエンド構成
- PostgreSQL 開発環境用の `docker-compose.yml` - 本番相当 / 開発用に分離した Docker Compose 構成
- Docker Compose での Web/API アプリと監視 worker 起動
- 環境変数サンプル `.env.example` - 環境変数サンプル `.env.example`
- DB 論理定義に基づく `db/schema.sql` - DB 論理定義に基づく `db/schema.sql`
- DB 接続モジュール - DB 接続モジュール
@@ -72,7 +73,8 @@
- API セキュリティ境界の統合テスト - API セキュリティ境界の統合テスト
- 証明書監視処理の単体テスト - 証明書監視処理の単体テスト
- API 未捕捉エラーと監視ジョブの構造化ログ - API 未捕捉エラーと監視ジョブの構造化ログ
- README に開発環境起動手順環境変数一覧を追加 - README に開発環境起動手順、Docker Compose 手順、環境変数一覧を追加
- AGENTS に Docker Compose 構成と loopback 公開方針を追加
- `.gitignore` の整備 - `.gitignore` の整備
## 作成・更新した主なファイル ## 作成・更新した主なファイル
@@ -80,7 +82,10 @@
- `package.json` - `package.json`
- `pnpm-lock.yaml` - `pnpm-lock.yaml`
- `pnpm-workspace.yaml` - `pnpm-workspace.yaml`
- `Dockerfile`
- `.dockerignore`
- `docker-compose.yml` - `docker-compose.yml`
- `docker-compose.dev.yml`
- `.env.example` - `.env.example`
- `.gitignore` - `.gitignore`
- `README.md` - `README.md`
@@ -348,6 +353,8 @@ API 動作確認:
```text ```text
docker compose up -d postgres docker compose up -d postgres
pnpm dev pnpm dev
docker compose up -d --build
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
``` ```
確認 URL: 確認 URL:
@@ -355,6 +362,7 @@ pnpm dev
```text ```text
Frontend: http://127.0.0.1:5173/ Frontend: http://127.0.0.1:5173/
API: http://127.0.0.1:3000 API: http://127.0.0.1:3000
Docker App: http://127.0.0.1:3000/
``` ```
## 既知の注意点 ## 既知の注意点
@@ -362,6 +370,9 @@ API: http://127.0.0.1:3000
- `pnpm` の build script 承認で `esbuild` を承認済み。 - `pnpm` の build script 承認で `esbuild` を承認済み。
- サンドボックス環境では Vite / Vitest の設定解決時に権限付き実行が必要だった。 - サンドボックス環境では Vite / Vitest の設定解決時に権限付き実行が必要だった。
- `docker-compose.yml` は初回 DB 起動時に `db/schema.sql` を読み込む。既存 volume がある場合、schema の変更は自動再適用されない。 - `docker-compose.yml` は初回 DB 起動時に `db/schema.sql` を読み込む。既存 volume がある場合、schema の変更は自動再適用されない。
- Compose のアプリ公開ポートは `127.0.0.1` に限定し、外部 NIC へ公開しない。
- Compose の PostgreSQL はホストに公開せず、`app` / `monitor-worker` から内部ネットワーク経由でのみアクセスする。
- コンテナ内では `HOST=0.0.0.0` で待ち受け、ホスト側への公開範囲は Compose の `ports` で制御する。
- 実装確認用に API 経由でテストユーザーと `https://example.com/` / `https://example.org/` / `https://example.net/` のサイトを登録済み。 - 実装確認用に API 経由でテストユーザーと `https://example.com/` / `https://example.org/` / `https://example.net/` のサイトを登録済み。
- 実装確認用にアラート履歴を手動挿入済み。 - 実装確認用にアラート履歴を手動挿入済み。
- 実装確認用に Webhook と Push 購読情報を API 経由で登録済み。 - 実装確認用に Webhook と Push 購読情報を API 経由で登録済み。

31
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,31 @@
services:
app:
command: ['pnpm', 'dev']
environment:
NODE_ENV: development
HOST: 0.0.0.0
VITE_HOST: 0.0.0.0
PORT: 3000
DATABASE_URL: postgres://certremind:certremind@postgres:5432/certremind
ports:
- '127.0.0.1:5173:5173'
volumes:
- .:/app
- app-node-modules:/app/node_modules
depends_on:
postgres:
condition: service_healthy
monitor-worker:
profiles:
- worker
command: ['pnpm', 'monitor:worker']
environment:
NODE_ENV: development
DATABASE_URL: postgres://certremind:certremind@postgres:5432/certremind
volumes:
- .:/app
- app-node-modules:/app/node_modules
volumes:
app-node-modules:

View File

@@ -6,8 +6,6 @@ services:
POSTGRES_DB: certremind POSTGRES_DB: certremind
POSTGRES_USER: certremind POSTGRES_USER: certremind
POSTGRES_PASSWORD: certremind POSTGRES_PASSWORD: certremind
ports:
- '5432:5432'
volumes: volumes:
- postgres-data:/var/lib/postgresql/data - postgres-data:/var/lib/postgresql/data
- ./db/schema.sql:/docker-entrypoint-initdb.d/001-schema.sql:ro - ./db/schema.sql:/docker-entrypoint-initdb.d/001-schema.sql:ro
@@ -17,5 +15,43 @@ services:
timeout: 5s timeout: 5s
retries: 5 retries: 5
app:
build: .
image: certremind-app:local
container_name: certremind-app
environment:
NODE_ENV: production
HOST: 0.0.0.0
PORT: 3000
DATABASE_URL: postgres://certremind:certremind@postgres:5432/certremind
COOKIE_SECRET: ${COOKIE_SECRET:-replace-with-a-long-random-string}
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-}
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-}
VAPID_SUBJECT: ${VAPID_SUBJECT:-mailto:admin@example.com}
OPENSSL_PATH: ${OPENSSL_PATH:-openssl}
ports:
- '127.0.0.1:3000:3000'
depends_on:
postgres:
condition: service_healthy
monitor-worker:
image: certremind-app:local
container_name: certremind-monitor-worker
command: ['pnpm', 'monitor:worker']
environment:
NODE_ENV: production
DATABASE_URL: postgres://certremind:certremind@postgres:5432/certremind
COOKIE_SECRET: ${COOKIE_SECRET:-replace-with-a-long-random-string}
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-}
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-}
VAPID_SUBJECT: ${VAPID_SUBJECT:-mailto:admin@example.com}
OPENSSL_PATH: ${OPENSSL_PATH:-openssl}
depends_on:
postgres:
condition: service_healthy
app:
condition: service_started
volumes: volumes:
postgres-data: postgres-data:

View File

@@ -6,7 +6,7 @@
"scripts": { "scripts": {
"dev": "concurrently \"pnpm dev:server\" \"pnpm dev:client\"", "dev": "concurrently \"pnpm dev:server\" \"pnpm dev:client\"",
"dev:server": "node --watch src/server/index.js", "dev:server": "node --watch src/server/index.js",
"dev:client": "vite --host 127.0.0.1", "dev:client": "vite",
"start": "node src/server/index.js", "start": "node src/server/index.js",
"monitor:once": "node src/server/jobs/monitorCertificates.js", "monitor:once": "node src/server/jobs/monitorCertificates.js",
"monitor:worker": "node src/server/jobs/monitorWorker.js", "monitor:worker": "node src/server/jobs/monitorWorker.js",

View File

@@ -1,5 +1,6 @@
export const env = { export const env = {
nodeEnv: process.env.NODE_ENV ?? 'development', nodeEnv: process.env.NODE_ENV ?? 'development',
host: process.env.HOST ?? '127.0.0.1',
port: Number.parseInt(process.env.PORT ?? '3000', 10), port: Number.parseInt(process.env.PORT ?? '3000', 10),
databaseUrl: databaseUrl:
process.env.DATABASE_URL ?? 'postgres://certremind:certremind@localhost:5432/certremind', process.env.DATABASE_URL ?? 'postgres://certremind:certremind@localhost:5432/certremind',

View File

@@ -8,7 +8,7 @@ serve(
{ {
fetch: app.fetch, fetch: app.fetch,
port: env.port, port: env.port,
hostname: '127.0.0.1', hostname: env.host,
}, },
(info) => { (info) => {
console.log(`CertRemind API listening on http://${info.address}:${info.port}`); console.log(`CertRemind API listening on http://${info.address}:${info.port}`);

View File

@@ -1,9 +1,11 @@
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
import { env } from 'node:process';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
server: { server: {
host: env.VITE_HOST ?? '127.0.0.1',
port: 5173, port: 5173,
proxy: { proxy: {
'/api': 'http://127.0.0.1:3000', '/api': 'http://127.0.0.1:3000',