feat(deploy): 添加 Knative 部署配置
- 修改 Next.js 配置启用 standalone 输出模式 - 添加前后端多阶段 Dockerfile - 添加 Knative Service 配置(支持缩容到 0) - 添加 Secrets 和 ConfigMap 模板 - 添加构建和部署脚本 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { NextConfig } from 'next';
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: 'standalone',
|
||||
reactStrictMode: true,
|
||||
transpilePackages: ['@seclusion/shared'],
|
||||
};
|
||||
|
||||
81
deploy/knative/api/Dockerfile
Normal file
81
deploy/knative/api/Dockerfile
Normal file
@@ -0,0 +1,81 @@
|
||||
# ================================================
|
||||
# Seclusion API - 后端多阶段 Dockerfile
|
||||
# ================================================
|
||||
|
||||
# Stage 1: 安装依赖
|
||||
FROM node:20-alpine AS deps
|
||||
WORKDIR /app
|
||||
|
||||
# 安装 pnpm
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
|
||||
# 复制 workspace 配置文件
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/api/package.json ./apps/api/
|
||||
COPY packages/shared/package.json ./packages/shared/
|
||||
|
||||
# 安装依赖
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# ================================================
|
||||
# Stage 2: 构建
|
||||
FROM node:20-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
|
||||
# 复制依赖
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY --from=deps /app/apps/api/node_modules ./apps/api/node_modules
|
||||
COPY --from=deps /app/packages/shared/node_modules ./packages/shared/node_modules
|
||||
|
||||
# 复制源代码
|
||||
COPY packages/shared ./packages/shared
|
||||
COPY apps/api ./apps/api
|
||||
COPY pnpm-workspace.yaml package.json tsconfig.json ./
|
||||
|
||||
# 构建 shared 包
|
||||
RUN pnpm --filter @seclusion/shared build
|
||||
|
||||
# 生成 Prisma Client
|
||||
RUN pnpm --filter @seclusion/api db:generate
|
||||
|
||||
# 构建 API
|
||||
RUN pnpm --filter @seclusion/api build
|
||||
|
||||
# ================================================
|
||||
# Stage 3: 生产镜像
|
||||
FROM node:20-alpine AS runner
|
||||
WORKDIR /app
|
||||
|
||||
# 安装 dumb-init 用于优雅关闭
|
||||
RUN apk add --no-cache dumb-init
|
||||
|
||||
# 创建非 root 用户
|
||||
RUN addgroup --system --gid 1001 nodejs && \
|
||||
adduser --system --uid 1001 nestjs
|
||||
|
||||
# 复制 Prisma schema(支持运行时迁移)
|
||||
COPY --from=builder /app/apps/api/prisma ./prisma
|
||||
|
||||
# 复制构建产物和依赖
|
||||
COPY --from=builder /app/apps/api/dist ./dist
|
||||
COPY --from=builder /app/apps/api/node_modules ./node_modules
|
||||
COPY --from=builder /app/apps/api/package.json ./package.json
|
||||
|
||||
# 复制 shared 包(运行时需要)
|
||||
COPY --from=builder /app/packages/shared/dist ./node_modules/@seclusion/shared/dist
|
||||
COPY --from=builder /app/packages/shared/package.json ./node_modules/@seclusion/shared/package.json
|
||||
|
||||
# 设置用户
|
||||
USER nestjs
|
||||
|
||||
# 环境变量
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=4000
|
||||
|
||||
EXPOSE 4000
|
||||
|
||||
# 使用 dumb-init 启动
|
||||
ENTRYPOINT ["dumb-init", "--"]
|
||||
CMD ["node", "dist/main.js"]
|
||||
88
deploy/knative/api/service.yaml
Normal file
88
deploy/knative/api/service.yaml
Normal file
@@ -0,0 +1,88 @@
|
||||
# ================================================
|
||||
# Seclusion API - Knative Service
|
||||
# ================================================
|
||||
|
||||
apiVersion: serving.knative.dev/v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: seclusion-api
|
||||
namespace: default
|
||||
labels:
|
||||
app: seclusion-api
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
# 允许缩容到 0
|
||||
autoscaling.knative.dev/min-scale: "0"
|
||||
autoscaling.knative.dev/max-scale: "10"
|
||||
# 并发设置
|
||||
autoscaling.knative.dev/target: "100"
|
||||
# 缩容延迟(秒)
|
||||
autoscaling.knative.dev/scale-down-delay: "30s"
|
||||
spec:
|
||||
# Harbor 私有仓库认证(如需要请取消注释)
|
||||
# imagePullSecrets:
|
||||
# - name: harbor-registry-secret
|
||||
containerConcurrency: 100
|
||||
timeoutSeconds: 300
|
||||
containers:
|
||||
- name: api
|
||||
# 镜像地址(部署时替换)
|
||||
image: harbor.example.com/seclusion/api:latest
|
||||
ports:
|
||||
- containerPort: 4000
|
||||
protocol: TCP
|
||||
# 环境变量 - 从 ConfigMap 注入
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: seclusion-api-config
|
||||
# 环境变量 - 从 Secret 注入
|
||||
env:
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: seclusion-api-secrets
|
||||
key: DATABASE_URL
|
||||
- name: REDIS_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: seclusion-api-secrets
|
||||
key: REDIS_URL
|
||||
- name: JWT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: seclusion-api-secrets
|
||||
key: JWT_SECRET
|
||||
- name: ENCRYPTION_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: seclusion-api-secrets
|
||||
key: ENCRYPTION_KEY
|
||||
# 资源限制
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "1000m"
|
||||
# 就绪探针
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/docs
|
||||
port: 4000
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
# 存活探针
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/docs
|
||||
port: 4000
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
17
deploy/knative/configmaps/api-config.yaml
Normal file
17
deploy/knative/configmaps/api-config.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
# ================================================
|
||||
# Seclusion API - ConfigMap
|
||||
# ================================================
|
||||
# 非敏感配置项
|
||||
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: seclusion-api-config
|
||||
namespace: default
|
||||
labels:
|
||||
app: seclusion-api
|
||||
data:
|
||||
NODE_ENV: "production"
|
||||
PORT: "4000"
|
||||
JWT_EXPIRES_IN: "7d"
|
||||
ENABLE_ENCRYPTION: "true"
|
||||
33
deploy/knative/secrets/api-secrets.yaml.example
Normal file
33
deploy/knative/secrets/api-secrets.yaml.example
Normal file
@@ -0,0 +1,33 @@
|
||||
# ================================================
|
||||
# Seclusion API - Kubernetes Secret 模板
|
||||
# ================================================
|
||||
# 使用方法:
|
||||
# 1. 复制此文件为 api-secrets.yaml
|
||||
# 2. 将 <BASE64_ENCODED_VALUE> 替换为 Base64 编码后的真实值
|
||||
# 3. 使用 echo -n "your-value" | base64 生成 Base64 值
|
||||
# ================================================
|
||||
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: seclusion-api-secrets
|
||||
namespace: default
|
||||
labels:
|
||||
app: seclusion-api
|
||||
type: Opaque
|
||||
data:
|
||||
# PostgreSQL 连接字符串
|
||||
# 示例: postgresql://user:password@host:5432/database?schema=public
|
||||
DATABASE_URL: <BASE64_ENCODED_VALUE>
|
||||
|
||||
# Redis 连接字符串
|
||||
# 示例: redis://user:password@host:6379
|
||||
REDIS_URL: <BASE64_ENCODED_VALUE>
|
||||
|
||||
# JWT 签名密钥 (建议 32 字符以上的随机字符串)
|
||||
# 生成: openssl rand -base64 32
|
||||
JWT_SECRET: <BASE64_ENCODED_VALUE>
|
||||
|
||||
# AES-256-GCM 加密密钥 (必须是 32 字节的 Base64 编码)
|
||||
# 生成: openssl rand -base64 32
|
||||
ENCRYPTION_KEY: <BASE64_ENCODED_VALUE>
|
||||
20
deploy/knative/secrets/web-secrets.yaml.example
Normal file
20
deploy/knative/secrets/web-secrets.yaml.example
Normal file
@@ -0,0 +1,20 @@
|
||||
# ================================================
|
||||
# Seclusion Web - Kubernetes Secret 模板
|
||||
# ================================================
|
||||
# 使用方法:
|
||||
# 1. 复制此文件为 web-secrets.yaml
|
||||
# 2. 将 <BASE64_ENCODED_VALUE> 替换为 Base64 编码后的真实值
|
||||
# ================================================
|
||||
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: seclusion-web-secrets
|
||||
namespace: default
|
||||
labels:
|
||||
app: seclusion-web
|
||||
type: Opaque
|
||||
data:
|
||||
# AES-256-GCM 加密密钥 (必须与后端 ENCRYPTION_KEY 相同)
|
||||
# 生成: openssl rand -base64 32
|
||||
NEXT_PUBLIC_ENCRYPTION_KEY: <BASE64_ENCODED_VALUE>
|
||||
82
deploy/knative/web/Dockerfile
Normal file
82
deploy/knative/web/Dockerfile
Normal file
@@ -0,0 +1,82 @@
|
||||
# ================================================
|
||||
# Seclusion Web - 前端多阶段 Dockerfile
|
||||
# ================================================
|
||||
|
||||
# Stage 1: 安装依赖
|
||||
FROM node:20-alpine AS deps
|
||||
WORKDIR /app
|
||||
|
||||
# 安装 pnpm
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
|
||||
# 复制 workspace 配置文件
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/web/package.json ./apps/web/
|
||||
COPY packages/shared/package.json ./packages/shared/
|
||||
|
||||
# 安装依赖
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# ================================================
|
||||
# Stage 2: 构建
|
||||
FROM node:20-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
|
||||
# 构建时环境变量(通过 ARG 注入)
|
||||
ARG NEXT_PUBLIC_API_URL
|
||||
ARG NEXT_PUBLIC_ENCRYPTION_KEY
|
||||
ARG NEXT_PUBLIC_ENABLE_ENCRYPTION=true
|
||||
|
||||
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
||||
ENV NEXT_PUBLIC_ENCRYPTION_KEY=${NEXT_PUBLIC_ENCRYPTION_KEY}
|
||||
ENV NEXT_PUBLIC_ENABLE_ENCRYPTION=${NEXT_PUBLIC_ENABLE_ENCRYPTION}
|
||||
|
||||
# 复制依赖
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY --from=deps /app/apps/web/node_modules ./apps/web/node_modules
|
||||
COPY --from=deps /app/packages/shared/node_modules ./packages/shared/node_modules
|
||||
|
||||
# 复制源代码
|
||||
COPY packages/shared ./packages/shared
|
||||
COPY apps/web ./apps/web
|
||||
COPY pnpm-workspace.yaml package.json tsconfig.json ./
|
||||
|
||||
# 构建 shared 包
|
||||
RUN pnpm --filter @seclusion/shared build
|
||||
|
||||
# 构建 Web
|
||||
RUN pnpm --filter @seclusion/web build
|
||||
|
||||
# ================================================
|
||||
# Stage 3: 生产镜像
|
||||
FROM node:20-alpine AS runner
|
||||
WORKDIR /app
|
||||
|
||||
# 安装 dumb-init 用于优雅关闭
|
||||
RUN apk add --no-cache dumb-init
|
||||
|
||||
# 创建非 root 用户
|
||||
RUN addgroup --system --gid 1001 nodejs && \
|
||||
adduser --system --uid 1001 nextjs
|
||||
|
||||
# 设置环境变量
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME="0.0.0.0"
|
||||
|
||||
# 复制 standalone 构建产物
|
||||
COPY --from=builder /app/apps/web/public ./public
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
|
||||
|
||||
# 设置用户
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
# 使用 dumb-init 启动
|
||||
ENTRYPOINT ["dumb-init", "--"]
|
||||
CMD ["node", "apps/web/server.js"]
|
||||
74
deploy/knative/web/service.yaml
Normal file
74
deploy/knative/web/service.yaml
Normal file
@@ -0,0 +1,74 @@
|
||||
# ================================================
|
||||
# Seclusion Web - Knative Service
|
||||
# ================================================
|
||||
|
||||
apiVersion: serving.knative.dev/v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: seclusion-web
|
||||
namespace: default
|
||||
labels:
|
||||
app: seclusion-web
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
# 允许缩容到 0
|
||||
autoscaling.knative.dev/min-scale: "0"
|
||||
autoscaling.knative.dev/max-scale: "10"
|
||||
# 并发设置
|
||||
autoscaling.knative.dev/target: "100"
|
||||
# 缩容延迟(秒)
|
||||
autoscaling.knative.dev/scale-down-delay: "30s"
|
||||
spec:
|
||||
# Harbor 私有仓库认证(如需要请取消注释)
|
||||
# imagePullSecrets:
|
||||
# - name: harbor-registry-secret
|
||||
containerConcurrency: 100
|
||||
timeoutSeconds: 300
|
||||
containers:
|
||||
- name: web
|
||||
# 镜像地址(部署时替换)
|
||||
image: harbor.example.com/seclusion/web:latest
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
protocol: TCP
|
||||
env:
|
||||
# API 地址(指向后端 Knative Service 内部地址)
|
||||
- name: NEXT_PUBLIC_API_URL
|
||||
value: "http://seclusion-api.default.svc.cluster.local"
|
||||
# 加密密钥(与后端相同)
|
||||
- name: NEXT_PUBLIC_ENCRYPTION_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: seclusion-web-secrets
|
||||
key: NEXT_PUBLIC_ENCRYPTION_KEY
|
||||
- name: NEXT_PUBLIC_ENABLE_ENCRYPTION
|
||||
value: "true"
|
||||
# 资源限制
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
# 就绪探针
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3000
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
# 存活探针
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
141
deploy/scripts/build.sh
Executable file
141
deploy/scripts/build.sh
Executable file
@@ -0,0 +1,141 @@
|
||||
#!/bin/bash
|
||||
# ================================================
|
||||
# Seclusion - Docker 镜像构建脚本
|
||||
# ================================================
|
||||
# 使用方法:
|
||||
# ./build.sh api # 仅构建后端
|
||||
# ./build.sh web # 仅构建前端
|
||||
# ./build.sh all # 构建全部
|
||||
# ./build.sh all --push # 构建并推送
|
||||
#
|
||||
# 必需环境变量:
|
||||
# HARBOR_REGISTRY # Harbor 仓库地址 (如: harbor.example.com/seclusion)
|
||||
# TAG # 镜像标签 (如: v1.0.0)
|
||||
#
|
||||
# 前端构建必需环境变量:
|
||||
# NEXT_PUBLIC_API_URL # 后端 API 地址
|
||||
# NEXT_PUBLIC_ENCRYPTION_KEY # 加密密钥
|
||||
# ================================================
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 项目根目录
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# 默认值
|
||||
HARBOR_REGISTRY="${HARBOR_REGISTRY:-harbor.example.com/seclusion}"
|
||||
TAG="${TAG:-latest}"
|
||||
PUSH=false
|
||||
|
||||
# 解析参数
|
||||
TARGET=""
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
api|web|all)
|
||||
TARGET=$arg
|
||||
;;
|
||||
--push)
|
||||
PUSH=true
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}未知参数: $arg${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$TARGET" ]; then
|
||||
echo -e "${YELLOW}使用方法: $0 <api|web|all> [--push]${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}Seclusion Docker 镜像构建${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "仓库地址: ${YELLOW}$HARBOR_REGISTRY${NC}"
|
||||
echo -e "镜像标签: ${YELLOW}$TAG${NC}"
|
||||
echo -e "推送镜像: ${YELLOW}$PUSH${NC}"
|
||||
echo ""
|
||||
|
||||
# 切换到项目根目录
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# 构建后端
|
||||
build_api() {
|
||||
echo -e "${GREEN}[1/2] 构建后端镜像...${NC}"
|
||||
|
||||
docker build \
|
||||
-f deploy/knative/api/Dockerfile \
|
||||
-t "$HARBOR_REGISTRY/api:$TAG" \
|
||||
.
|
||||
|
||||
echo -e "${GREEN}✓ 后端镜像构建完成: $HARBOR_REGISTRY/api:$TAG${NC}"
|
||||
|
||||
if [ "$PUSH" = true ]; then
|
||||
echo -e "${YELLOW}推送后端镜像...${NC}"
|
||||
docker push "$HARBOR_REGISTRY/api:$TAG"
|
||||
echo -e "${GREEN}✓ 后端镜像推送完成${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 构建前端
|
||||
build_web() {
|
||||
echo -e "${GREEN}[2/2] 构建前端镜像...${NC}"
|
||||
|
||||
# 检查必需的环境变量
|
||||
if [ -z "$NEXT_PUBLIC_API_URL" ]; then
|
||||
echo -e "${RED}错误: 缺少 NEXT_PUBLIC_API_URL 环境变量${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$NEXT_PUBLIC_ENCRYPTION_KEY" ]; then
|
||||
echo -e "${RED}错误: 缺少 NEXT_PUBLIC_ENCRYPTION_KEY 环境变量${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker build \
|
||||
-f deploy/knative/web/Dockerfile \
|
||||
--build-arg NEXT_PUBLIC_API_URL="$NEXT_PUBLIC_API_URL" \
|
||||
--build-arg NEXT_PUBLIC_ENCRYPTION_KEY="$NEXT_PUBLIC_ENCRYPTION_KEY" \
|
||||
--build-arg NEXT_PUBLIC_ENABLE_ENCRYPTION="${NEXT_PUBLIC_ENABLE_ENCRYPTION:-true}" \
|
||||
-t "$HARBOR_REGISTRY/web:$TAG" \
|
||||
.
|
||||
|
||||
echo -e "${GREEN}✓ 前端镜像构建完成: $HARBOR_REGISTRY/web:$TAG${NC}"
|
||||
|
||||
if [ "$PUSH" = true ]; then
|
||||
echo -e "${YELLOW}推送前端镜像...${NC}"
|
||||
docker push "$HARBOR_REGISTRY/web:$TAG"
|
||||
echo -e "${GREEN}✓ 前端镜像推送完成${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 执行构建
|
||||
case $TARGET in
|
||||
api)
|
||||
build_api
|
||||
;;
|
||||
web)
|
||||
build_web
|
||||
;;
|
||||
all)
|
||||
build_api
|
||||
build_web
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}构建完成!${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
|
||||
if [ "$PUSH" = false ]; then
|
||||
echo -e "${YELLOW}提示: 使用 --push 参数可自动推送镜像到 Harbor${NC}"
|
||||
fi
|
||||
203
deploy/scripts/deploy.sh
Executable file
203
deploy/scripts/deploy.sh
Executable file
@@ -0,0 +1,203 @@
|
||||
#!/bin/bash
|
||||
# ================================================
|
||||
# Seclusion - Knative 部署脚本
|
||||
# ================================================
|
||||
# 使用方法:
|
||||
# ./deploy.sh deploy # 部署服务
|
||||
# ./deploy.sh migrate # 运行数据库迁移
|
||||
# ./deploy.sh status # 查看服务状态
|
||||
# ./deploy.sh logs <api|web> # 查看服务日志
|
||||
# ./deploy.sh delete # 删除服务
|
||||
#
|
||||
# 必需环境变量:
|
||||
# HARBOR_REGISTRY # Harbor 仓库地址
|
||||
# TAG # 镜像标签
|
||||
# ================================================
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 项目根目录
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
KNATIVE_DIR="$(cd "$SCRIPT_DIR/../knative" && pwd)"
|
||||
|
||||
# 默认值
|
||||
HARBOR_REGISTRY="${HARBOR_REGISTRY:-harbor.example.com/seclusion}"
|
||||
TAG="${TAG:-latest}"
|
||||
NAMESPACE="${NAMESPACE:-default}"
|
||||
|
||||
# 打印标题
|
||||
print_header() {
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}$1${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
}
|
||||
|
||||
# 部署服务
|
||||
deploy() {
|
||||
print_header "Seclusion Knative 部署"
|
||||
|
||||
echo -e "${YELLOW}[1/4] 应用 ConfigMap...${NC}"
|
||||
kubectl apply -f "$KNATIVE_DIR/configmaps/api-config.yaml" -n "$NAMESPACE"
|
||||
|
||||
echo -e "${YELLOW}[2/4] 应用 Secrets...${NC}"
|
||||
if [ -f "$KNATIVE_DIR/secrets/api-secrets.yaml" ]; then
|
||||
kubectl apply -f "$KNATIVE_DIR/secrets/api-secrets.yaml" -n "$NAMESPACE"
|
||||
else
|
||||
echo -e "${RED}警告: api-secrets.yaml 不存在,请从 .example 复制并填入真实值${NC}"
|
||||
fi
|
||||
|
||||
if [ -f "$KNATIVE_DIR/secrets/web-secrets.yaml" ]; then
|
||||
kubectl apply -f "$KNATIVE_DIR/secrets/web-secrets.yaml" -n "$NAMESPACE"
|
||||
else
|
||||
echo -e "${RED}警告: web-secrets.yaml 不存在,请从 .example 复制并填入真实值${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}[3/4] 部署后端服务...${NC}"
|
||||
# 替换镜像地址
|
||||
sed "s|image: harbor.example.com/seclusion/api:latest|image: $HARBOR_REGISTRY/api:$TAG|g" \
|
||||
"$KNATIVE_DIR/api/service.yaml" | kubectl apply -f - -n "$NAMESPACE"
|
||||
|
||||
echo -e "${YELLOW}[4/4] 部署前端服务...${NC}"
|
||||
sed "s|image: harbor.example.com/seclusion/web:latest|image: $HARBOR_REGISTRY/web:$TAG|g" \
|
||||
"$KNATIVE_DIR/web/service.yaml" | kubectl apply -f - -n "$NAMESPACE"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ 部署完成!${NC}"
|
||||
echo ""
|
||||
|
||||
# 等待服务就绪
|
||||
echo -e "${YELLOW}等待服务就绪...${NC}"
|
||||
kubectl wait --for=condition=Ready ksvc/seclusion-api -n "$NAMESPACE" --timeout=300s || true
|
||||
kubectl wait --for=condition=Ready ksvc/seclusion-web -n "$NAMESPACE" --timeout=300s || true
|
||||
|
||||
status
|
||||
}
|
||||
|
||||
# 运行数据库迁移
|
||||
migrate() {
|
||||
print_header "运行数据库迁移"
|
||||
|
||||
echo -e "${YELLOW}创建迁移 Job...${NC}"
|
||||
|
||||
# 获取当前 API 镜像
|
||||
API_IMAGE="$HARBOR_REGISTRY/api:$TAG"
|
||||
|
||||
# 创建一次性迁移 Job
|
||||
kubectl run seclusion-migrate \
|
||||
--image="$API_IMAGE" \
|
||||
--restart=Never \
|
||||
--rm \
|
||||
-i \
|
||||
--env="DATABASE_URL=$(kubectl get secret seclusion-api-secrets -n $NAMESPACE -o jsonpath='{.data.DATABASE_URL}' | base64 -d)" \
|
||||
-n "$NAMESPACE" \
|
||||
-- npx prisma migrate deploy
|
||||
|
||||
echo -e "${GREEN}✓ 数据库迁移完成!${NC}"
|
||||
}
|
||||
|
||||
# 查看服务状态
|
||||
status() {
|
||||
print_header "服务状态"
|
||||
|
||||
echo -e "${BLUE}Knative Services:${NC}"
|
||||
kubectl get ksvc -n "$NAMESPACE" -l app=seclusion-api -o wide 2>/dev/null || echo "后端服务未部署"
|
||||
kubectl get ksvc -n "$NAMESPACE" -l app=seclusion-web -o wide 2>/dev/null || echo "前端服务未部署"
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}服务 URL:${NC}"
|
||||
API_URL=$(kubectl get ksvc seclusion-api -n "$NAMESPACE" -o jsonpath='{.status.url}' 2>/dev/null || echo "未获取到")
|
||||
WEB_URL=$(kubectl get ksvc seclusion-web -n "$NAMESPACE" -o jsonpath='{.status.url}' 2>/dev/null || echo "未获取到")
|
||||
echo -e "后端 API: ${GREEN}$API_URL${NC}"
|
||||
echo -e "前端 Web: ${GREEN}$WEB_URL${NC}"
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}Pod 状态:${NC}"
|
||||
kubectl get pods -n "$NAMESPACE" -l serving.knative.dev/service=seclusion-api 2>/dev/null || true
|
||||
kubectl get pods -n "$NAMESPACE" -l serving.knative.dev/service=seclusion-web 2>/dev/null || true
|
||||
}
|
||||
|
||||
# 查看日志
|
||||
logs() {
|
||||
local SERVICE=$1
|
||||
|
||||
if [ -z "$SERVICE" ]; then
|
||||
echo -e "${RED}请指定服务: api 或 web${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_header "查看 $SERVICE 日志"
|
||||
|
||||
case $SERVICE in
|
||||
api)
|
||||
kubectl logs -f -l serving.knative.dev/service=seclusion-api -n "$NAMESPACE" --all-containers
|
||||
;;
|
||||
web)
|
||||
kubectl logs -f -l serving.knative.dev/service=seclusion-web -n "$NAMESPACE" --all-containers
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}未知服务: $SERVICE${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 删除服务
|
||||
delete() {
|
||||
print_header "删除 Seclusion 服务"
|
||||
|
||||
echo -e "${YELLOW}确认删除所有 Seclusion 服务? [y/N]${NC}"
|
||||
read -r confirm
|
||||
|
||||
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
|
||||
echo "取消删除"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}删除 Knative Services...${NC}"
|
||||
kubectl delete ksvc seclusion-api seclusion-web -n "$NAMESPACE" --ignore-not-found
|
||||
|
||||
echo -e "${YELLOW}删除 ConfigMaps...${NC}"
|
||||
kubectl delete configmap seclusion-api-config -n "$NAMESPACE" --ignore-not-found
|
||||
|
||||
echo -e "${YELLOW}删除 Secrets...${NC}"
|
||||
kubectl delete secret seclusion-api-secrets seclusion-web-secrets -n "$NAMESPACE" --ignore-not-found
|
||||
|
||||
echo -e "${GREEN}✓ 删除完成!${NC}"
|
||||
}
|
||||
|
||||
# 主入口
|
||||
case "${1:-}" in
|
||||
deploy)
|
||||
deploy
|
||||
;;
|
||||
migrate)
|
||||
migrate
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
logs)
|
||||
logs "$2"
|
||||
;;
|
||||
delete)
|
||||
delete
|
||||
;;
|
||||
*)
|
||||
echo -e "${YELLOW}使用方法: $0 <deploy|migrate|status|logs|delete>${NC}"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " deploy 部署 Knative 服务"
|
||||
echo " migrate 运行数据库迁移"
|
||||
echo " status 查看服务状态"
|
||||
echo " logs <api|web> 查看服务日志"
|
||||
echo " delete 删除所有服务"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user