歴史地図アプリの構成
React + TypeScript + Vite + MapLibre GL のSPA。歴史的国境データ(GeoJSON)を表示するアプリ。
データはpublic/data/以下にGeoJSONを置く構成で、.gitignoreに含めているためリポジトリには入っていない。
ちなみに、生成するスクリプトはあるが、GEMINIを利用しないといけない。
しかし、APIキーのレート制限が入ってしまったので、ローカルで生成済みのデータを持ち込むことにした。
インフラ構成
自宅のProxmox上にLXCコンテナとしてk3sクラスタを構築している。マスター1台+ノード1台の最小構成。
外部公開はNginx Proxy Manager(NPM)でポートフォワーディングしており、DuckDNSのドメインにSSL終端している。
インターネット
↓
Nginx Proxy Manager(SSL終端)
↓
k3s NodePort
↓
Pod
問題:データファイルをどう持ち込むか
public/data/がgitignoreされているため、コンテナ内でgit cloneしてもデータが存在しない。
選択肢はいくつかあったが、今回はk3sのhostPathボリュームでマウントする方針にした。
だるいファイル転送
データファイルをk3sノードに転送するのが一番面倒だった。
- Proxmoxのファイルアップロード → UIの制限でNG
- ngrok経由 → Tailscale環境のためlocalhostの名前解決失敗
- 結局TailscaleのIPでProxmoxホストに転送 →
pct pushでLXCコンテナへ
# ProxmoxホストからLXCへ
pct push <CTID> /path/to/data.tar.gz /tmp/data.tar.gz
# k3sマスターで解凍
mkdir -p /opt/history-map-data
tar -xzf /tmp/data.tar.gz -C /opt/history-map-data
融通の効かないViteとふわふわClaude君の罠
npm run previewはデフォルトで許可ホストを制限する。Nginx Proxy Manager経由でアクセスするとBlocked requestが出る。
環境変数で全許可とかできたらよかったけど、結論だけ言うとできなかった。少なくともClaude君の指示では何をどうしても駄目だったので、最終的にvite.config.tsをデプロイ時に動的に書き換えることで回避した。
cat > /app/vite.config.ts << 'EOF'
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
preview: {
allowedHosts: ['your-domain.example.com'],
},
})
EOF
最終的なYAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: history-map
spec:
replicas: 1
selector:
matchLabels:
app: history-map
template:
metadata:
labels:
app: history-map
spec:
nodeName: k3s-master
containers:
- name: history-map
image: node:20-alpine
workingDir: /app
command: ["sh", "-c"]
args:
- |
apk add --no-cache git
git clone https://github.com/wasuken/history-map-app.git /app --depth=1
cat > /app/vite.config.ts << 'EOF'
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
preview: {
allowedHosts: ['your-domain.example.com'],
},
})
EOF
mkdir -p /app/public/data
cp -r /data/historical /app/public/data/historical
cp -r /data/modern /app/public/data/modern
cp /data/translation-cache.json /app/public/data/translation-cache.json
npm install
npx vite build
npm run preview -- --host 0.0.0.0 --port 3000
ports:
- containerPort: 3000
volumeMounts:
- name: map-data
mountPath: /data
volumes:
- name: map-data
hostPath:
path: /opt/history-map-data
type: Directory
---
apiVersion: v1
kind: Service
metadata:
name: history-map-service
spec:
selector:
app: history-map
ports:
- port: 80
targetPort: 3000
nodePort: 30080
type: NodePort
nodeName: k3s-masterを指定しているのはhostPathがPodの動くノード上に存在する必要があるため。ProxmoxのNPM(Nginx Proxy Manager)から
このNodePortに向けてプロキシを設定している。
まとめ
本番運用するなら素直にDockerfileでビルドしてイメージに焼いた方がいいとかあるだろうが、今回は雑に動かすことを優先した。