feat(deploy): single-image docker-compose and kubernetes install.sh

- multi-stage root Dockerfile (opsx-loki pattern): frontend dist +
  go binary + gh-ost in one alpine image, non-root user, HEALTHCHECK,
  entrypoint warning for missing SECRET_KEY; gin now serves the SPA
  from DIST_DIR (asset/db-logos/favicon passthrough, /api returns JSON
  404, SPA fallback to index.html) - one origin, no CORS surface
- docker-compose.yml rewritten: single service, named data volume,
  no-new-privileges + cap_drop ALL, /health healthcheck, resource
  limits, log rotation; SECRET_KEY required (openssl rand -hex 32),
  JWT_SECRET optional (auto-generated and persisted in prod)
- kubernetes/: install.sh one-command deploy (idempotent secret
  reuse, --nodeport/--ingress flags, rollout wait, backup tips) plus
  namespace/secret/pvc/deployment(single replica + Recreate, probes,
  resource limits, non-root)/service/nodeport/ingress manifests and
  README (sqlite single-writer constraints, snapshot backup)
- docs: README quick-start now has 3 deployment paths; ARCHITECTURE
  ch.14 expanded

single-image runtime verified locally (SPA/assets/api/login coexist
on :9002); full image build left to server (docker hub unreachable
from this machine)
This commit is contained in:
Logan.Li committed 2026-10-26 20:33:26 +08:00
1 parent 13fb3aceb3
commit 0b16488029
15 files changed
+683 -34

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
# 平台本体 —— 单副本 + Recreate(SQLite 单写者,滚动更新会双写互踩,参照 opsx-loki 同款约束)
# 镜像: 把 image 改为你的仓库地址,或用本仓库根目录 Dockerfile 自行构建推送
apiVersion: apps/v1
kind: Deployment
metadata:
name: opsxsql
namespace: opsxsql
labels:
app.kubernetes.io/name: opsxsql
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: opsxsql
template:
metadata:
labels:
app.kubernetes.io/name: opsxsql
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: opsxsql
image: opsxsql:latest # TODO: 替换为实际镜像仓库地址
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 9000
env:
- name: PORT
value: "9000"
- name: DB_PATH
value: /app/data/sqlite3.db
- name: DEBUG
value: "false"
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: opsxsql-secrets
key: SECRET_KEY
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: opsxsql-secrets
key: JWT_SECRET
volumeMounts:
- name: data
mountPath: /app/data
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 20
periodSeconds: 30
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: "2"
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
capabilities:
drop: ["ALL"]
volumes:
- name: data
persistentVolumeClaim:
claimName: opsxsql-data