ci: 添加 Gitea Actions CI/CD 和 Knative 部署配置
- 添加 CI workflow(PR 构建检查) - 添加 Deploy workflow(main 分支自动部署) - 添加 Web/API 多阶段 Dockerfile - 添加 Knative Service 配置(自动扩缩容) - 添加 K8s ConfigMap、Secret、Namespace 配置 - 添加 .dockerignore 优化构建 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
46
.dockerignore
Normal file
46
.dockerignore
Normal file
@@ -0,0 +1,46 @@
|
||||
# Dependencies
|
||||
node_modules
|
||||
**/node_modules
|
||||
|
||||
# Build outputs
|
||||
.next
|
||||
dist
|
||||
.turbo
|
||||
|
||||
# Development
|
||||
.env.local
|
||||
*.local
|
||||
|
||||
# IDE
|
||||
.idea
|
||||
.vscode
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# Docker
|
||||
**/Dockerfile*
|
||||
docker-compose*.yml
|
||||
|
||||
# Documentation
|
||||
docs
|
||||
*.md
|
||||
!README.md
|
||||
|
||||
# Tests
|
||||
**/*.test.ts
|
||||
**/*.spec.ts
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
38
.gitea/workflows/ci.yaml
Normal file
38
.gitea/workflows/ci.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
PNPM_VERSION: 9
|
||||
NODE_VERSION: 20
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Generate Prisma Client
|
||||
run: pnpm db:generate
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
101
.gitea/workflows/deploy.yaml
Normal file
101
.gitea/workflows/deploy.yaml
Normal file
@@ -0,0 +1,101 @@
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
PNPM_VERSION: 9
|
||||
NODE_VERSION: 20
|
||||
REGISTRY: gitea.tegical.world
|
||||
IMAGE_PREFIX: tegical/seclusion
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
name: Build and Push Images
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
image_tag: ${{ steps.meta.outputs.tag }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Generate image tag
|
||||
id: meta
|
||||
run: |
|
||||
echo "tag=$(date +%Y%m%d%H%M%S)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Login to Gitea Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- name: Build and push Web image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./apps/web/Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-web:${{ steps.meta.outputs.tag }}
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-web:latest
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Build and push API image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./apps/api/Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-api:${{ steps.meta.outputs.tag }}
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-api:latest
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
deploy:
|
||||
name: Deploy to Knative
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-and-push
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup kubectl
|
||||
uses: azure/setup-kubectl@v4
|
||||
|
||||
- name: Configure kubeconfig
|
||||
run: |
|
||||
mkdir -p ~/.kube
|
||||
echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
|
||||
chmod 600 ~/.kube/config
|
||||
|
||||
- name: Update image tags in manifests
|
||||
run: |
|
||||
IMAGE_TAG=${{ needs.build-and-push.outputs.image_tag }}
|
||||
sed -i "s|IMAGE_TAG_PLACEHOLDER|${IMAGE_TAG}|g" deploy/k8s/*.yaml
|
||||
|
||||
- name: Deploy to Knative
|
||||
run: |
|
||||
kubectl apply -f deploy/k8s/namespace.yaml
|
||||
kubectl apply -f deploy/k8s/configmap.yaml
|
||||
kubectl apply -f deploy/k8s/secret.yaml
|
||||
kubectl apply -f deploy/k8s/web-ksvc.yaml
|
||||
kubectl apply -f deploy/k8s/api-ksvc.yaml
|
||||
|
||||
- name: Wait for services to be ready
|
||||
run: |
|
||||
kubectl wait --for=condition=Ready ksvc/seclusion-web -n seclusion --timeout=300s
|
||||
kubectl wait --for=condition=Ready ksvc/seclusion-api -n seclusion --timeout=300s
|
||||
|
||||
- name: Get service URLs
|
||||
run: |
|
||||
echo "Web URL: $(kubectl get ksvc seclusion-web -n seclusion -o jsonpath='{.status.url}')"
|
||||
echo "API URL: $(kubectl get ksvc seclusion-api -n seclusion -o jsonpath='{.status.url}')"
|
||||
89
apps/api/Dockerfile
Normal file
89
apps/api/Dockerfile
Normal file
@@ -0,0 +1,89 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# ============================================
|
||||
# Base stage: Install dependencies
|
||||
# ============================================
|
||||
FROM node:20-alpine AS base
|
||||
RUN corepack enable && corepack prepare pnpm@9 --activate
|
||||
WORKDIR /app
|
||||
|
||||
# ============================================
|
||||
# Dependencies stage: Install all dependencies
|
||||
# ============================================
|
||||
FROM base AS deps
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/api/package.json ./apps/api/
|
||||
COPY apps/api/prisma ./apps/api/prisma/
|
||||
COPY packages/shared/package.json ./packages/shared/
|
||||
COPY packages/eslint-config/package.json ./packages/eslint-config/
|
||||
COPY packages/typescript-config/package.json ./packages/typescript-config/
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Generate Prisma Client
|
||||
WORKDIR /app/apps/api
|
||||
RUN pnpm db:generate
|
||||
|
||||
# ============================================
|
||||
# Builder stage: Build the application
|
||||
# ============================================
|
||||
FROM base AS builder
|
||||
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 ./packages
|
||||
COPY . .
|
||||
|
||||
# Build shared package first
|
||||
WORKDIR /app/packages/shared
|
||||
RUN pnpm build
|
||||
|
||||
# Build API application
|
||||
WORKDIR /app/apps/api
|
||||
RUN pnpm build
|
||||
|
||||
# ============================================
|
||||
# Production dependencies stage
|
||||
# ============================================
|
||||
FROM base AS prod-deps
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/api/package.json ./apps/api/
|
||||
COPY apps/api/prisma ./apps/api/prisma/
|
||||
COPY packages/shared/package.json ./packages/shared/
|
||||
COPY packages/eslint-config/package.json ./packages/eslint-config/
|
||||
COPY packages/typescript-config/package.json ./packages/typescript-config/
|
||||
RUN pnpm install --frozen-lockfile --prod
|
||||
|
||||
# Generate Prisma Client for production
|
||||
WORKDIR /app/apps/api
|
||||
RUN pnpm db:generate
|
||||
|
||||
# ============================================
|
||||
# Runner stage: Production image
|
||||
# ============================================
|
||||
FROM node:20-alpine AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nestjs
|
||||
|
||||
# Copy production dependencies
|
||||
COPY --from=prod-deps /app/node_modules ./node_modules
|
||||
COPY --from=prod-deps /app/apps/api/node_modules ./apps/api/node_modules
|
||||
COPY --from=prod-deps /app/packages/shared/node_modules ./packages/shared/node_modules
|
||||
|
||||
# Copy built application
|
||||
COPY --from=builder /app/apps/api/dist ./apps/api/dist
|
||||
COPY --from=builder /app/packages/shared/dist ./packages/shared/dist
|
||||
COPY --from=builder /app/packages/shared/package.json ./packages/shared/
|
||||
|
||||
# Copy Prisma schema for migrations (optional)
|
||||
COPY --from=builder /app/apps/api/prisma ./apps/api/prisma
|
||||
|
||||
USER nestjs
|
||||
|
||||
EXPOSE 4000
|
||||
|
||||
WORKDIR /app/apps/api
|
||||
|
||||
CMD ["node", "dist/main"]
|
||||
63
apps/web/Dockerfile
Normal file
63
apps/web/Dockerfile
Normal file
@@ -0,0 +1,63 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# ============================================
|
||||
# Base stage: Install dependencies
|
||||
# ============================================
|
||||
FROM node:20-alpine AS base
|
||||
RUN corepack enable && corepack prepare pnpm@9 --activate
|
||||
WORKDIR /app
|
||||
|
||||
# ============================================
|
||||
# Dependencies stage: Install all dependencies
|
||||
# ============================================
|
||||
FROM base AS deps
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/web/package.json ./apps/web/
|
||||
COPY packages/shared/package.json ./packages/shared/
|
||||
COPY packages/eslint-config/package.json ./packages/eslint-config/
|
||||
COPY packages/typescript-config/package.json ./packages/typescript-config/
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# ============================================
|
||||
# Builder stage: Build the application
|
||||
# ============================================
|
||||
FROM base AS builder
|
||||
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 ./packages
|
||||
COPY . .
|
||||
|
||||
# Build shared package first
|
||||
WORKDIR /app/packages/shared
|
||||
RUN pnpm build
|
||||
|
||||
# Build web application
|
||||
WORKDIR /app/apps/web
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
RUN pnpm build
|
||||
|
||||
# ============================================
|
||||
# Runner stage: Production image
|
||||
# ============================================
|
||||
FROM node:20-alpine AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
# Copy standalone output
|
||||
COPY --from=builder /app/apps/web/.next/standalone ./
|
||||
COPY --from=builder /app/apps/web/.next/static ./apps/web/.next/static
|
||||
COPY --from=builder /app/apps/web/public ./apps/web/public
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME="0.0.0.0"
|
||||
|
||||
CMD ["node", "apps/web/server.js"]
|
||||
270
deploy/k8s/README.md
Normal file
270
deploy/k8s/README.md
Normal file
@@ -0,0 +1,270 @@
|
||||
# Knative 部署指南
|
||||
|
||||
本项目使用 Knative Serving 在 Kubernetes 上部署应用。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
.gitea/workflows/
|
||||
├── ci.yaml # PR 构建检查
|
||||
└── deploy.yaml # 主分支部署
|
||||
|
||||
apps/
|
||||
├── web/Dockerfile # Web 应用 Dockerfile
|
||||
└── api/Dockerfile # API 应用 Dockerfile
|
||||
|
||||
deploy/k8s/
|
||||
├── namespace.yaml # 命名空间
|
||||
├── configmap.yaml # 环境变量配置
|
||||
├── secret.yaml # 敏感信息配置
|
||||
├── web-ksvc.yaml # Web Knative Service
|
||||
└── api-ksvc.yaml # API Knative Service
|
||||
```
|
||||
|
||||
## 前置要求
|
||||
|
||||
1. **Kubernetes 集群**:需要安装 Knative Serving
|
||||
2. **Gitea Container Registry**:镜像仓库地址 `gitea.tegical.world`
|
||||
3. **kubectl**:本地安装 kubectl 用于手动部署
|
||||
|
||||
## Gitea Secrets 配置
|
||||
|
||||
在 Gitea 仓库设置 → Secrets 中配置:
|
||||
|
||||
| Secret 名称 | 说明 | 生成方式 |
|
||||
|------------|------|---------|
|
||||
| `REGISTRY_USERNAME` | Gitea 容器镜像仓库用户名 | - |
|
||||
| `REGISTRY_PASSWORD` | Gitea 容器镜像仓库密码 | Token 或密码 |
|
||||
| `KUBECONFIG` | Base64 编码的 kubeconfig | `cat ~/.kube/config \| base64` |
|
||||
|
||||
## 配置修改
|
||||
|
||||
### 1. ConfigMap (`deploy/k8s/configmap.yaml`)
|
||||
|
||||
```yaml
|
||||
data:
|
||||
# 修改为实际的 API 地址
|
||||
NEXT_PUBLIC_API_URL: "https://api.example.com"
|
||||
|
||||
# 是否启用加密
|
||||
NEXT_PUBLIC_ENABLE_ENCRYPTION: "false"
|
||||
ENABLE_ENCRYPTION: "false"
|
||||
```
|
||||
|
||||
### 2. Secret (`deploy/k8s/secret.yaml`)
|
||||
|
||||
**方式一:直接编辑文件(不推荐)**
|
||||
```yaml
|
||||
stringData:
|
||||
DATABASE_URL: "postgresql://user:pass@host:5432/db"
|
||||
REDIS_URL: "redis://host:6379"
|
||||
JWT_SECRET: "your-secret-here"
|
||||
# ... 其他配置
|
||||
```
|
||||
|
||||
**方式二:使用 kubectl 创建(推荐)**
|
||||
```bash
|
||||
kubectl create secret generic seclusion-secret -n seclusion \
|
||||
--from-literal=DATABASE_URL='postgresql://user:pass@host:5432/db' \
|
||||
--from-literal=REDIS_URL='redis://host:6379' \
|
||||
--from-literal=JWT_SECRET='your-jwt-secret' \
|
||||
--from-literal=JWT_EXPIRES_IN='7d' \
|
||||
--from-literal=JWT_REFRESH_SECRET='your-refresh-secret' \
|
||||
--from-literal=JWT_REFRESH_EXPIRES_IN='30d' \
|
||||
--from-literal=ENCRYPTION_KEY='' \
|
||||
--from-literal=NEXT_PUBLIC_ENCRYPTION_KEY=''
|
||||
```
|
||||
|
||||
### 3. Knative Service 配置
|
||||
|
||||
**自动扩缩容配置** (`deploy/k8s/web-ksvc.yaml` 和 `deploy/k8s/api-ksvc.yaml`):
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
annotations:
|
||||
# 最小实例数(0 允许缩容到零)
|
||||
autoscaling.knative.dev/min-scale: "1"
|
||||
# 最大实例数
|
||||
autoscaling.knative.dev/max-scale: "10"
|
||||
# 每个实例的目标并发请求数
|
||||
autoscaling.knative.dev/target: "100"
|
||||
```
|
||||
|
||||
**资源限制**:
|
||||
```yaml
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
|
||||
## 部署流程
|
||||
|
||||
### 自动部署(推荐)
|
||||
|
||||
1. **创建 PR** → 触发 CI workflow 进行构建检查
|
||||
2. **合并到 main** → 自动构建镜像并部署到 Knative
|
||||
|
||||
### 手动部署
|
||||
|
||||
```bash
|
||||
# 1. 构建镜像
|
||||
docker build -f apps/web/Dockerfile -t gitea.tegical.world/tegical/seclusion-web:latest .
|
||||
docker build -f apps/api/Dockerfile -t gitea.tegical.world/tegical/seclusion-api:latest .
|
||||
|
||||
# 2. 推送镜像
|
||||
docker push gitea.tegical.world/tegical/seclusion-web:latest
|
||||
docker push gitea.tegical.world/tegical/seclusion-api:latest
|
||||
|
||||
# 3. 部署到 Knative
|
||||
kubectl apply -f deploy/k8s/namespace.yaml
|
||||
kubectl apply -f deploy/k8s/configmap.yaml
|
||||
kubectl apply -f deploy/k8s/secret.yaml
|
||||
kubectl apply -f deploy/k8s/web-ksvc.yaml
|
||||
kubectl apply -f deploy/k8s/api-ksvc.yaml
|
||||
|
||||
# 4. 等待服务就绪
|
||||
kubectl wait --for=condition=Ready ksvc/seclusion-web -n seclusion --timeout=300s
|
||||
kubectl wait --for=condition=Ready ksvc/seclusion-api -n seclusion --timeout=300s
|
||||
|
||||
# 5. 查看服务 URL
|
||||
kubectl get ksvc -n seclusion
|
||||
```
|
||||
|
||||
## 查看部署状态
|
||||
|
||||
```bash
|
||||
# 查看 Knative Service 状态
|
||||
kubectl get ksvc -n seclusion
|
||||
|
||||
# 查看 Pod 状态
|
||||
kubectl get pods -n seclusion
|
||||
|
||||
# 查看服务日志
|
||||
kubectl logs -n seclusion -l serving.knative.dev/service=seclusion-web
|
||||
kubectl logs -n seclusion -l serving.knative.dev/service=seclusion-api
|
||||
|
||||
# 查看服务详情
|
||||
kubectl describe ksvc seclusion-web -n seclusion
|
||||
kubectl describe ksvc seclusion-api -n seclusion
|
||||
```
|
||||
|
||||
## 域名配置
|
||||
|
||||
Knative 会自动为每个 Service 分配一个 URL,格式通常为:
|
||||
- `http://seclusion-web.seclusion.{knative-domain}`
|
||||
- `http://seclusion-api.seclusion.{knative-domain}`
|
||||
|
||||
### 配置自定义域名
|
||||
|
||||
创建 `deploy/k8s/domain-mapping.yaml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: serving.knative.dev/v1alpha1
|
||||
kind: DomainMapping
|
||||
metadata:
|
||||
name: seclusion.example.com
|
||||
namespace: seclusion
|
||||
spec:
|
||||
ref:
|
||||
name: seclusion-web
|
||||
kind: Service
|
||||
apiVersion: serving.knative.dev/v1
|
||||
---
|
||||
apiVersion: serving.knative.dev/v1alpha1
|
||||
kind: DomainMapping
|
||||
metadata:
|
||||
name: api.seclusion.example.com
|
||||
namespace: seclusion
|
||||
spec:
|
||||
ref:
|
||||
name: seclusion-api
|
||||
kind: Service
|
||||
apiVersion: serving.knative.dev/v1
|
||||
```
|
||||
|
||||
应用配置:
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/domain-mapping.yaml
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 镜像拉取失败
|
||||
|
||||
```bash
|
||||
# 检查镜像仓库凭证
|
||||
kubectl get secret -n seclusion
|
||||
|
||||
# 如需要私有镜像,创建 pull secret
|
||||
kubectl create secret docker-registry gitea-registry \
|
||||
--docker-server=gitea.tegical.world \
|
||||
--docker-username=<username> \
|
||||
--docker-password=<password> \
|
||||
-n seclusion
|
||||
|
||||
# 在 Knative Service 中引用
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: gitea-registry
|
||||
```
|
||||
|
||||
### 服务无法就绪
|
||||
|
||||
```bash
|
||||
# 查看事件
|
||||
kubectl get events -n seclusion --sort-by='.lastTimestamp'
|
||||
|
||||
# 查看 Revision 状态
|
||||
kubectl get revision -n seclusion
|
||||
|
||||
# 查看详细日志
|
||||
kubectl logs -n seclusion -l serving.knative.dev/service=seclusion-web --tail=100
|
||||
```
|
||||
|
||||
### 数据库连接失败
|
||||
|
||||
确认 `DATABASE_URL` 和 `REDIS_URL` 配置正确,且集群内可访问数据库。
|
||||
|
||||
## 回滚
|
||||
|
||||
```bash
|
||||
# 查看历史 Revision
|
||||
kubectl get revision -n seclusion
|
||||
|
||||
# 回滚到指定 Revision
|
||||
kubectl patch ksvc seclusion-web -n seclusion --type='json' \
|
||||
-p='[{"op": "replace", "path": "/spec/traffic", "value": [{"revisionName": "seclusion-web-00001", "percent": 100}]}]'
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 调整并发限制
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
# 0 表示无限制,建议设置合理值
|
||||
containerConcurrency: 100
|
||||
```
|
||||
|
||||
### 2. 调整扩缩容策略
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
annotations:
|
||||
# 缩容窗口期(秒)
|
||||
autoscaling.knative.dev/scale-down-delay: "30s"
|
||||
# 稳定窗口期(秒)
|
||||
autoscaling.knative.dev/window: "60s"
|
||||
```
|
||||
|
||||
### 3. 启用缓存优化
|
||||
|
||||
在 Dockerfile 中已使用多阶段构建和 BuildKit 缓存优化构建速度。
|
||||
42
deploy/k8s/api-ksvc.yaml
Normal file
42
deploy/k8s/api-ksvc.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
apiVersion: serving.knative.dev/v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: seclusion-api
|
||||
namespace: seclusion
|
||||
labels:
|
||||
app: seclusion-api
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
# 最小实例数(API 服务建议保持至少 1 个实例)
|
||||
autoscaling.knative.dev/min-scale: "1"
|
||||
# 最大实例数
|
||||
autoscaling.knative.dev/max-scale: "10"
|
||||
# 每个实例的并发请求数
|
||||
autoscaling.knative.dev/target: "100"
|
||||
spec:
|
||||
containerConcurrency: 0
|
||||
containers:
|
||||
- name: api
|
||||
image: gitea.tegical.world/tegical/seclusion-api:IMAGE_TAG_PLACEHOLDER
|
||||
ports:
|
||||
- containerPort: 4000
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: seclusion-config
|
||||
- secretRef:
|
||||
name: seclusion-secret
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 4000
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
14
deploy/k8s/configmap.yaml
Normal file
14
deploy/k8s/configmap.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: seclusion-config
|
||||
namespace: seclusion
|
||||
data:
|
||||
# Web 配置
|
||||
NEXT_PUBLIC_API_URL: "https://api.example.com"
|
||||
NEXT_PUBLIC_ENABLE_ENCRYPTION: "false"
|
||||
|
||||
# API 配置
|
||||
NODE_ENV: "production"
|
||||
PORT: "4000"
|
||||
ENABLE_ENCRYPTION: "false"
|
||||
6
deploy/k8s/namespace.yaml
Normal file
6
deploy/k8s/namespace.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: seclusion
|
||||
labels:
|
||||
app.kubernetes.io/name: seclusion
|
||||
27
deploy/k8s/secret.yaml
Normal file
27
deploy/k8s/secret.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
# 注意:此文件仅作为模板,实际部署时需要手动创建 Secret 或使用外部密钥管理
|
||||
# kubectl create secret generic seclusion-secret -n seclusion \
|
||||
# --from-literal=DATABASE_URL='postgresql://...' \
|
||||
# --from-literal=REDIS_URL='redis://...' \
|
||||
# --from-literal=JWT_SECRET='your-jwt-secret' \
|
||||
# --from-literal=ENCRYPTION_KEY='your-encryption-key'
|
||||
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: seclusion-secret
|
||||
namespace: seclusion
|
||||
type: Opaque
|
||||
stringData:
|
||||
# 数据库连接
|
||||
DATABASE_URL: "postgresql://user:password@postgres:5432/seclusion"
|
||||
REDIS_URL: "redis://redis:6379"
|
||||
|
||||
# JWT 配置
|
||||
JWT_SECRET: "change-me-in-production"
|
||||
JWT_EXPIRES_IN: "7d"
|
||||
JWT_REFRESH_SECRET: "change-me-in-production-refresh"
|
||||
JWT_REFRESH_EXPIRES_IN: "30d"
|
||||
|
||||
# 加密密钥(如启用加密)
|
||||
ENCRYPTION_KEY: ""
|
||||
NEXT_PUBLIC_ENCRYPTION_KEY: ""
|
||||
42
deploy/k8s/web-ksvc.yaml
Normal file
42
deploy/k8s/web-ksvc.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
apiVersion: serving.knative.dev/v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: seclusion-web
|
||||
namespace: seclusion
|
||||
labels:
|
||||
app: seclusion-web
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
# 最小实例数(设为 1 避免冷启动,设为 0 允许缩容到零)
|
||||
autoscaling.knative.dev/min-scale: "1"
|
||||
# 最大实例数
|
||||
autoscaling.knative.dev/max-scale: "10"
|
||||
# 每个实例的并发请求数
|
||||
autoscaling.knative.dev/target: "100"
|
||||
spec:
|
||||
containerConcurrency: 0
|
||||
containers:
|
||||
- name: web
|
||||
image: gitea.tegical.world/tegical/seclusion-web:IMAGE_TAG_PLACEHOLDER
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: seclusion-config
|
||||
- secretRef:
|
||||
name: seclusion-secret
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3000
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
Reference in New Issue
Block a user