chore: reconcile with origin/main preferring local tree snapshot
This commit is contained in:
@@ -1 +1,3 @@
|
||||
client/data/mysql/mysql.sock
|
||||
README.md
|
||||
.git/
|
||||
|
||||
@@ -72,11 +72,11 @@ jobs:
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
git add -A
|
||||
if git diff --cached --quiet; then
|
||||
if git diff --quiet; then
|
||||
echo "No changes to publish"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git add -A
|
||||
git commit -m "pages: update evidence feed [skip ci]"
|
||||
git push origin HEAD:gh-pages
|
||||
|
||||
@@ -0,0 +1,624 @@
|
||||
# Gitea CI/CD 快速配置指南
|
||||
|
||||
## 📋 适用场景
|
||||
|
||||
为新项目快速配置基于 Gitea Actions + Argo CD 的完整 GitOps 工作流。
|
||||
|
||||
## ✅ 前置条件
|
||||
|
||||
### 1. 基础设施检查
|
||||
|
||||
```bash
|
||||
# 检查 Gitea Actions Runner
|
||||
ls -la /Users/brunogao/gitea-runners/runner3/.runner
|
||||
# 应显示: labels: ["macos:docker:host"] 或 ["docker-builder"]
|
||||
|
||||
# 检查 Harbor Registry
|
||||
docker login harbor.omniverseai.net
|
||||
# 应成功登录
|
||||
|
||||
# 检查 Argo CD
|
||||
kubectl get application -n argocd
|
||||
# 应看到 apps-bootstrap 和其他应用
|
||||
```
|
||||
|
||||
### 2. 准备信息
|
||||
|
||||
- **项目名称**: 如 `my-app`
|
||||
- **Gitea 仓库**: `http://192.168.1.83:33000/gao/my-app`
|
||||
- **Harbor 镜像**: `harbor.omniverseai.net/ljwx/my-app`
|
||||
- **K8s Namespace**: `my-app-dev` (推荐独立 namespace)
|
||||
- **Gitea Token**: 从 Gitea 设置 → 应用 → 访问令牌生成
|
||||
|
||||
## 🚀 配置步骤
|
||||
|
||||
### Step 1: 项目仓库配置 Gitea Workflow
|
||||
|
||||
#### 1.1 创建 Workflow 文件
|
||||
|
||||
在项目根目录创建 `.gitea/workflows/ci.yaml`:
|
||||
|
||||
```yaml
|
||||
name: CI/CD Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
REGISTRY: harbor.omniverseai.net
|
||||
IMAGE_NAME: ljwx/my-app # 修改为你的项目名
|
||||
|
||||
jobs:
|
||||
# ==================== Job 1: Lint & Test ====================
|
||||
lint-test:
|
||||
runs-on: ubuntu-latest # K8s runner,快速
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --legacy-peer-deps
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
|
||||
- name: Type check
|
||||
run: npm run build:check || npm run build
|
||||
|
||||
# ==================== Job 2: Build & Push ====================
|
||||
build-push:
|
||||
needs: lint-test
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
runs-on: docker-builder # 宿主机 runner,有完整 Docker 环境
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
run: |
|
||||
SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-7)
|
||||
echo "tag=main-${SHORT_SHA}" >> $GITHUB_OUTPUT
|
||||
echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Log in to Harbor
|
||||
run: echo "${{ secrets.HARBOR_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u "${{ secrets.HARBOR_USERNAME }}" --password-stdin
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
run: |
|
||||
docker buildx create --name ci-builder --use --driver docker-container || docker buildx use ci-builder
|
||||
docker buildx inspect --bootstrap
|
||||
|
||||
- name: Build and push Docker image
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.tag }} \
|
||||
--tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
|
||||
--push \
|
||||
--progress plain \
|
||||
.
|
||||
|
||||
- name: Image digest
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.tag }}
|
||||
|
||||
# ==================== Job 3: Update Deploy Repo ====================
|
||||
update-deploy:
|
||||
needs: build-push
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
IMAGE_TAG: main-${{ github.sha }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
run: |
|
||||
SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-7)
|
||||
echo "IMAGE_TAG=main-${SHORT_SHA}" >> $GITHUB_ENV
|
||||
|
||||
- name: Verify tools
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
git --version
|
||||
yq --version
|
||||
|
||||
- name: Update deployment repository
|
||||
run: |
|
||||
# 克隆 ljwx-deploy 仓库 (使用 token 认证)
|
||||
git clone http://gao:${{ secrets.DEPLOY_REPO_TOKEN }}@192.168.1.83:33000/gao/ljwx-deploy.git /tmp/ljwx-deploy
|
||||
cd /tmp/ljwx-deploy
|
||||
|
||||
# 配置 git
|
||||
git config user.name "Gitea Actions"
|
||||
git config user.email "actions@gitea.local"
|
||||
|
||||
# 导航到 overlay 目录(修改为你的项目路径)
|
||||
cd apps/my-app/overlays/my-app-dev
|
||||
|
||||
# 更新镜像 tag
|
||||
yq eval ".images[0].newTag = \"${IMAGE_TAG}\"" -i kustomization.yaml
|
||||
|
||||
# 提交并推送
|
||||
git add kustomization.yaml
|
||||
git commit -m "chore: update my-app to ${IMAGE_TAG}"
|
||||
git push origin main
|
||||
|
||||
# ==================== Job 4: Build PR (仅验证) ====================
|
||||
build-pr:
|
||||
needs: lint-test
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: docker-builder
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
run: |
|
||||
docker buildx create --name ci-builder --use --driver docker-container || docker buildx use ci-builder
|
||||
docker buildx inspect --bootstrap
|
||||
|
||||
- name: Build Docker image (no push)
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--progress plain \
|
||||
.
|
||||
```
|
||||
|
||||
#### 1.2 配置 Gitea Secrets
|
||||
|
||||
**推荐方式: 使用 Organization Secrets (一次配置,所有项目共享)**
|
||||
|
||||
详细配置步骤请参考: **[GITEA-SECRETS-SETUP.md](GITEA-SECRETS-SETUP.md)**
|
||||
|
||||
```bash
|
||||
# Organization 级别配置 (推荐):
|
||||
# http://192.168.1.83:33000/gao/settings/secrets
|
||||
# 添加以下 3 个 Secrets,所有项目自动可用:
|
||||
# - HARBOR_USERNAME: Harbor 用户名
|
||||
# - HARBOR_PASSWORD: Harbor 密码
|
||||
# - DEPLOY_REPO_TOKEN: Gitea 访问令牌 (用于更新 ljwx-deploy 仓库)
|
||||
|
||||
# 或者项目级别配置 (不推荐):
|
||||
# http://192.168.1.83:33000/gao/my-app/settings/secrets
|
||||
# 需要为每个项目单独配置
|
||||
```
|
||||
|
||||
> 💡 **提示**: 如果已经配置了 Organization Secrets,可以跳过此步骤
|
||||
|
||||
#### 1.3 确保 Dockerfile 存在
|
||||
|
||||
在项目根目录创建 `Dockerfile.prod` (或 `Dockerfile`):
|
||||
|
||||
```dockerfile
|
||||
# 第一阶段:构建
|
||||
FROM harbor.omniverseai.net/library/node:20-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci --legacy-peer-deps
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# 第二阶段:生产
|
||||
FROM harbor.omniverseai.net/library/nginx:alpine
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
EXPOSE 80
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget --quiet --tries=1 --spider http://localhost/health || exit 1
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
```
|
||||
|
||||
### Step 2: ljwx-deploy 仓库配置
|
||||
|
||||
#### 2.1 创建 Namespace 配置
|
||||
|
||||
```bash
|
||||
cd /path/to/ljwx-deploy
|
||||
|
||||
# 创建 namespace 配置文件
|
||||
cat > cluster/namespace-my-app-dev.yaml <<EOF
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: my-app-dev
|
||||
labels:
|
||||
name: my-app-dev
|
||||
environment: dev
|
||||
app: my-app
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ResourceQuota
|
||||
metadata:
|
||||
name: my-app-quota
|
||||
namespace: my-app-dev
|
||||
spec:
|
||||
hard:
|
||||
requests.cpu: "500m"
|
||||
requests.memory: "512Mi"
|
||||
limits.cpu: "1"
|
||||
limits.memory: "1Gi"
|
||||
persistentvolumeclaims: "5"
|
||||
services: "5"
|
||||
pods: "10"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: LimitRange
|
||||
metadata:
|
||||
name: my-app-limits
|
||||
namespace: my-app-dev
|
||||
spec:
|
||||
limits:
|
||||
- max:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
min:
|
||||
cpu: "10m"
|
||||
memory: "32Mi"
|
||||
default:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
defaultRequest:
|
||||
cpu: "50m"
|
||||
memory: "64Mi"
|
||||
type: Container
|
||||
EOF
|
||||
|
||||
# 更新 cluster/kustomization.yaml
|
||||
yq eval '.resources += ["namespace-my-app-dev.yaml"]' -i cluster/kustomization.yaml
|
||||
```
|
||||
|
||||
#### 2.2 创建应用部署配置
|
||||
|
||||
```bash
|
||||
# 创建目录结构
|
||||
mkdir -p apps/my-app/{base,overlays/my-app-dev}
|
||||
|
||||
# 创建 base/deployment.yaml
|
||||
cat > apps/my-app/base/deployment.yaml <<EOF
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: my-app
|
||||
labels:
|
||||
app: my-app
|
||||
component: web
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: my-app
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: my-app
|
||||
component: web
|
||||
spec:
|
||||
containers:
|
||||
- name: my-app
|
||||
image: harbor.omniverseai.net/ljwx/my-app:latest
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 80
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "25m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
EOF
|
||||
|
||||
# 创建 base/service.yaml
|
||||
cat > apps/my-app/base/service.yaml <<EOF
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: my-app
|
||||
labels:
|
||||
app: my-app
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: my-app
|
||||
EOF
|
||||
|
||||
# 创建 base/kustomization.yaml
|
||||
cat > apps/my-app/base/kustomization.yaml <<EOF
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
EOF
|
||||
|
||||
# 创建 overlays/my-app-dev/kustomization.yaml
|
||||
cat > apps/my-app/overlays/my-app-dev/kustomization.yaml <<EOF
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: my-app-dev
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
images:
|
||||
- name: harbor.omniverseai.net/ljwx/my-app
|
||||
newTag: "latest" # CI 会自动更新这个 tag
|
||||
|
||||
replicas:
|
||||
- name: my-app
|
||||
count: 1 # dev 环境使用 1 个副本
|
||||
EOF
|
||||
```
|
||||
|
||||
#### 2.3 创建 Argo CD Application
|
||||
|
||||
```bash
|
||||
# 创建 Application 配置
|
||||
cat > argocd-apps/60-my-app-dev.yaml <<EOF
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: my-app-dev
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: http://192.168.1.83:33000/gao/ljwx-deploy.git
|
||||
targetRevision: main
|
||||
path: apps/my-app/overlays/my-app-dev
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: my-app-dev
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
EOF
|
||||
```
|
||||
|
||||
#### 2.4 提交到 Git
|
||||
|
||||
```bash
|
||||
cd /path/to/ljwx-deploy
|
||||
|
||||
# 提交所有变更
|
||||
git add .
|
||||
git commit -m "feat: add my-app deployment configuration"
|
||||
git push origin main
|
||||
|
||||
# 等待 30 秒让 apps-bootstrap 自动创建 Application
|
||||
sleep 30
|
||||
```
|
||||
|
||||
### Step 3: 验证部署
|
||||
|
||||
#### 3.1 验证 Argo CD Application
|
||||
|
||||
```bash
|
||||
# 检查 Application 是否自动创建
|
||||
kubectl get application my-app-dev -n argocd
|
||||
|
||||
# 应该显示:
|
||||
# NAME SYNC STATUS HEALTH STATUS
|
||||
# my-app-dev Synced Healthy
|
||||
```
|
||||
|
||||
#### 3.2 验证 Namespace 和资源
|
||||
|
||||
```bash
|
||||
# 检查 namespace
|
||||
kubectl get namespace my-app-dev
|
||||
|
||||
# 检查资源配额
|
||||
kubectl get resourcequota,limitrange -n my-app-dev
|
||||
|
||||
# 检查部署状态
|
||||
kubectl get all -n my-app-dev
|
||||
```
|
||||
|
||||
#### 3.3 测试完整 CI/CD 流程
|
||||
|
||||
```bash
|
||||
# 在项目仓库中做一个测试提交
|
||||
cd /path/to/my-app
|
||||
echo "# Test CI/CD" >> README.md
|
||||
git add README.md
|
||||
git commit -m "test: verify CI/CD pipeline"
|
||||
git push origin main
|
||||
|
||||
# 观察流程:
|
||||
# 1. Gitea Actions 开始构建 (约 3-5 分钟)
|
||||
# 2. 镜像推送到 Harbor
|
||||
# 3. ljwx-deploy 仓库自动更新
|
||||
# 4. Argo CD 检测到变更并同步 (约 30 秒)
|
||||
# 5. 新 Pod 启动
|
||||
|
||||
# 检查 Gitea Actions
|
||||
# http://192.168.1.83:33000/gao/my-app/actions
|
||||
|
||||
# 检查 ljwx-deploy 最新提交
|
||||
cd /path/to/ljwx-deploy
|
||||
git pull origin main
|
||||
git log -1 --oneline
|
||||
# 应该看到: chore: update my-app to main-xxxxxxx
|
||||
|
||||
# 检查 Pod 镜像更新
|
||||
kubectl describe pod -n my-app-dev -l app=my-app | grep Image:
|
||||
```
|
||||
|
||||
## 🎯 关键配置点
|
||||
|
||||
### 1. Runner 选择
|
||||
|
||||
```yaml
|
||||
# 轻量级任务 (lint, test) → K8s runner
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Docker 构建任务 → 宿主机 runner
|
||||
runs-on: docker-builder # 或 macos
|
||||
```
|
||||
|
||||
### 2. Multi-arch 构建
|
||||
|
||||
```yaml
|
||||
# 必须使用 buildx + atomic push
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \ # 一步完成 build + push
|
||||
.
|
||||
```
|
||||
|
||||
### 3. Kustomize 路径
|
||||
|
||||
```yaml
|
||||
# CI workflow 中的路径要匹配
|
||||
cd apps/my-app/overlays/my-app-dev
|
||||
|
||||
# Argo CD Application 中的路径
|
||||
path: apps/my-app/overlays/my-app-dev
|
||||
```
|
||||
|
||||
### 4. Namespace 独立性
|
||||
|
||||
```yaml
|
||||
# 推荐: 每个应用独立 namespace
|
||||
namespace: my-app-dev
|
||||
|
||||
# 而不是: 共享 namespace
|
||||
namespace: dev # ❌ 避免
|
||||
```
|
||||
|
||||
## 🔧 常见问题排查
|
||||
|
||||
### 问题 1: Build & Push 失败
|
||||
|
||||
**症状**: Docker build 失败或推送失败
|
||||
|
||||
**检查**:
|
||||
```bash
|
||||
# 1. 检查 runner 标签
|
||||
cat /Users/brunogao/gitea-runners/runner3/.runner
|
||||
|
||||
# 2. 检查 Harbor 凭据
|
||||
echo "$HARBOR_PASSWORD" | docker login harbor.omniverseai.net -u "$HARBOR_USERNAME" --password-stdin
|
||||
|
||||
# 3. 检查 buildx builder
|
||||
docker buildx ls
|
||||
```
|
||||
|
||||
**解决**: 使用 `docker-builder` 标签的 runner,确保 buildx 正确设置
|
||||
|
||||
### 问题 2: Update Deploy Repo 不执行
|
||||
|
||||
**症状**: Job 显示成功但 ljwx-deploy 未更新
|
||||
|
||||
**检查**:
|
||||
```bash
|
||||
# 1. 验证 DEPLOY_REPO_TOKEN
|
||||
# Gitea → 设置 → 应用 → 访问令牌
|
||||
|
||||
# 2. 检查路径是否正确
|
||||
ls -la ljwx-deploy/apps/my-app/overlays/my-app-dev/kustomization.yaml
|
||||
|
||||
# 3. 测试 git clone
|
||||
git clone http://gao:YOUR_TOKEN@192.168.1.83:33000/gao/ljwx-deploy.git /tmp/test
|
||||
```
|
||||
|
||||
**解决**:
|
||||
- 确保 token 有 `repo` 权限
|
||||
- 确保路径 `apps/my-app/overlays/my-app-dev` 存在
|
||||
|
||||
### 问题 3: Argo CD 不自动同步
|
||||
|
||||
**症状**: ljwx-deploy 更新了但 K8s 未部署
|
||||
|
||||
**检查**:
|
||||
```bash
|
||||
# 1. 检查 Application 状态
|
||||
kubectl describe application my-app-dev -n argocd
|
||||
|
||||
# 2. 检查 apps-bootstrap
|
||||
kubectl get application apps-bootstrap -n argocd
|
||||
|
||||
# 3. 手动触发同步
|
||||
kubectl patch application my-app-dev -n argocd \
|
||||
--type merge \
|
||||
-p '{"operation":{"initiatedBy":{"username":"admin"},"sync":{"revision":"HEAD"}}}'
|
||||
```
|
||||
|
||||
**解决**:
|
||||
- 确保 Application 文件名匹配 `*-dev.yaml` 模式
|
||||
- 等待 3 分钟(Argo CD 默认同步间隔)
|
||||
|
||||
### 问题 4: Pod CrashLoopBackOff
|
||||
|
||||
**症状**: Pod 启动失败
|
||||
|
||||
**检查**:
|
||||
```bash
|
||||
# 1. 查看 Pod 日志
|
||||
kubectl logs -n my-app-dev -l app=my-app --tail=50
|
||||
|
||||
# 2. 查看 Pod 事件
|
||||
kubectl describe pod -n my-app-dev -l app=my-app
|
||||
|
||||
# 3. 检查资源限制
|
||||
kubectl get limitrange -n my-app-dev -o yaml
|
||||
```
|
||||
|
||||
**解决**:
|
||||
- 确保 Dockerfile 正确配置
|
||||
- 确保 `/health` 端点存在
|
||||
- 调整 resources.requests 满足 LimitRange
|
||||
|
||||
## 📚 参考文档
|
||||
|
||||
- **ljwx-website 成功案例**: `.gitea/workflows/ci.yaml`
|
||||
- **Gitea Actions 文档**: https://docs.gitea.com/usage/actions/overview
|
||||
- **Argo CD App of Apps**: `ARGOCD-APP-MANAGEMENT.md`
|
||||
- **Namespace 策略**: `NAMESPACE-STRATEGY.md`
|
||||
- **GitOps 原则**: `PROMPT_BLUEPRINT_V2.md`
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
完成上述步骤后,你的项目将拥有:
|
||||
|
||||
✅ 完整的 CI/CD 自动化流程
|
||||
✅ Multi-arch Docker 镜像
|
||||
✅ GitOps 声明式部署
|
||||
✅ 独立的 Namespace 资源隔离
|
||||
✅ Argo CD 自动同步
|
||||
✅ 零手动 kubectl 操作
|
||||
|
||||
**记住**: Git commit 即部署,完全自动化!🚀
|
||||
@@ -0,0 +1,393 @@
|
||||
# Gitea Organization Secrets 配置指南
|
||||
|
||||
## 📋 目标
|
||||
|
||||
配置组织级别的 Secrets,实现一次配置、所有项目共享,无需在每个项目中重复配置。
|
||||
|
||||
## 🎯 方案选择
|
||||
|
||||
### 方案对比
|
||||
|
||||
| 方案 | 优点 | 缺点 | 推荐度 |
|
||||
|------|------|------|--------|
|
||||
| **Organization Secrets** | ✅ 一次配置<br>✅ 所有项目共享<br>✅ 集中管理 | ⚠️ 需要 Organization | ⭐⭐⭐⭐⭐ |
|
||||
| **Repository Secrets** | ✅ 项目隔离 | ❌ 每个项目都要配置<br>❌ 维护成本高 | ⭐⭐ |
|
||||
| **Global Runner Secrets** | ✅ 全局可用 | ❌ 不安全<br>❌ 难以管理 | ⭐ |
|
||||
|
||||
**推荐**: 使用 Organization Secrets
|
||||
|
||||
## 🚀 快速配置 (Organization Secrets)
|
||||
|
||||
### Step 1: 检查当前组织结构
|
||||
|
||||
```bash
|
||||
# 检查你的 Gitea 项目所属组织
|
||||
# 访问: http://192.168.1.83:33000/gao
|
||||
|
||||
# 确认所有项目都在 "gao" 组织/用户下:
|
||||
# - ljwx-website
|
||||
# - ljwx-deploy
|
||||
# - backend
|
||||
# - frontend
|
||||
# - quality-dashboard
|
||||
# - (其他项目)
|
||||
```
|
||||
|
||||
### Step 2: 配置 Organization Secrets
|
||||
|
||||
#### 方式 1: Web UI 配置 (推荐)
|
||||
|
||||
1. **访问组织设置**
|
||||
```
|
||||
http://192.168.1.83:33000/gao
|
||||
```
|
||||
|
||||
2. **导航到 Secrets 页面**
|
||||
```
|
||||
组织首页 → Settings → Secrets (或 Actions → Secrets)
|
||||
```
|
||||
|
||||
3. **添加以下 Secrets**
|
||||
|
||||
**Secret 1: HARBOR_USERNAME**
|
||||
```
|
||||
Name: HARBOR_USERNAME
|
||||
Value: admin (或你的 Harbor 用户名)
|
||||
```
|
||||
|
||||
**Secret 2: HARBOR_PASSWORD**
|
||||
```
|
||||
Name: HARBOR_PASSWORD
|
||||
Value: [你的 Harbor 密码]
|
||||
```
|
||||
|
||||
**Secret 3: DEPLOY_REPO_TOKEN**
|
||||
```
|
||||
Name: DEPLOY_REPO_TOKEN
|
||||
Value: [你的 Gitea Personal Access Token]
|
||||
```
|
||||
|
||||
4. **验证配置**
|
||||
- 点击 "Add Secret" 后应该看到 secret 列表
|
||||
- 确认所有 3 个 secrets 都已添加
|
||||
|
||||
#### 方式 2: Gitea API 配置 (批量)
|
||||
|
||||
```bash
|
||||
# 设置变量
|
||||
GITEA_URL="http://192.168.1.83:33000"
|
||||
ORG_NAME="gao"
|
||||
GITEA_TOKEN="your-admin-token" # 需要 admin 或 owner 权限
|
||||
|
||||
# 添加 HARBOR_USERNAME
|
||||
curl -X PUT "${GITEA_URL}/api/v1/orgs/${ORG_NAME}/actions/secrets/HARBOR_USERNAME" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"data": "admin"
|
||||
}'
|
||||
|
||||
# 添加 HARBOR_PASSWORD
|
||||
curl -X PUT "${GITEA_URL}/api/v1/orgs/${ORG_NAME}/actions/secrets/HARBOR_PASSWORD" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"data": "your-harbor-password"
|
||||
}'
|
||||
|
||||
# 添加 DEPLOY_REPO_TOKEN
|
||||
curl -X PUT "${GITEA_URL}/api/v1/orgs/${ORG_NAME}/actions/secrets/DEPLOY_REPO_TOKEN" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"data": "your-deploy-token"
|
||||
}'
|
||||
```
|
||||
|
||||
### Step 3: 生成 DEPLOY_REPO_TOKEN (如果还没有)
|
||||
|
||||
```bash
|
||||
# 1. 访问个人设置
|
||||
http://192.168.1.83:33000/user/settings/applications
|
||||
|
||||
# 2. 生成新的访问令牌
|
||||
Name: gitea-actions-deploy
|
||||
Scopes:
|
||||
- repo (必须)
|
||||
- write:repository (必须)
|
||||
|
||||
# 3. 复制生成的 token
|
||||
# 示例: 7ea68abccfb9de287305ab23b82c8e9a66fb6814
|
||||
|
||||
# 4. 将 token 添加到 Organization Secrets (见 Step 2)
|
||||
```
|
||||
|
||||
### Step 4: 更新项目 Workflow (移除项目级 Secrets)
|
||||
|
||||
现在项目的 `.gitea/workflows/ci.yaml` 可以直接使用 Organization Secrets,无需任何修改:
|
||||
|
||||
```yaml
|
||||
# ✅ 直接使用,无需在项目中配置
|
||||
- name: Log in to Harbor
|
||||
run: echo "${{ secrets.HARBOR_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u "${{ secrets.HARBOR_USERNAME }}" --password-stdin
|
||||
|
||||
- name: Update deployment repository
|
||||
run: |
|
||||
git clone http://gao:${{ secrets.DEPLOY_REPO_TOKEN }}@192.168.1.83:33000/gao/ljwx-deploy.git /tmp/ljwx-deploy
|
||||
```
|
||||
|
||||
**优先级规则**:
|
||||
1. Repository Secrets (项目级) - 最高优先级
|
||||
2. Organization Secrets (组织级) - 如果项目没有配置
|
||||
3. Global Secrets (全局) - 最低优先级
|
||||
|
||||
### Step 5: 清理项目级 Secrets (可选)
|
||||
|
||||
如果之前在项目中配置了 Secrets,现在可以删除:
|
||||
|
||||
```bash
|
||||
# 访问每个项目的 Settings → Secrets
|
||||
http://192.168.1.83:33000/gao/ljwx-website/settings/secrets
|
||||
http://192.168.1.83:33000/gao/backend/settings/secrets
|
||||
http://192.168.1.83:33000/gao/frontend/settings/secrets
|
||||
|
||||
# 删除以下 Secrets (因为已经在 Organization 级别配置):
|
||||
# - HARBOR_USERNAME
|
||||
# - HARBOR_PASSWORD
|
||||
# - DEPLOY_REPO_TOKEN
|
||||
```
|
||||
|
||||
## ✅ 验证配置
|
||||
|
||||
### 测试 1: 检查 Organization Secrets
|
||||
|
||||
```bash
|
||||
# 访问组织 Secrets 页面
|
||||
http://192.168.1.83:33000/gao/settings/secrets
|
||||
|
||||
# 应该看到:
|
||||
# - HARBOR_USERNAME
|
||||
# - HARBOR_PASSWORD
|
||||
# - DEPLOY_REPO_TOKEN
|
||||
```
|
||||
|
||||
### 测试 2: 新项目自动可用
|
||||
|
||||
```bash
|
||||
# 1. 创建新项目
|
||||
http://192.168.1.83:33000/repo/create
|
||||
|
||||
# 2. 添加 workflow 文件 (使用 secrets)
|
||||
mkdir -p .gitea/workflows
|
||||
cat > .gitea/workflows/test.yaml <<'EOF'
|
||||
name: Test Secrets
|
||||
on: push
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Test Harbor login
|
||||
run: |
|
||||
echo "Testing Harbor credentials..."
|
||||
docker login harbor.omniverseai.net -u "${{ secrets.HARBOR_USERNAME }}" --password-stdin <<< "${{ secrets.HARBOR_PASSWORD }}"
|
||||
echo "✅ Login successful!"
|
||||
EOF
|
||||
|
||||
# 3. 提交并推送
|
||||
git add .gitea/workflows/test.yaml
|
||||
git commit -m "test: verify organization secrets"
|
||||
git push origin main
|
||||
|
||||
# 4. 查看 Actions 运行结果
|
||||
# 应该显示 "✅ Login successful!"
|
||||
```
|
||||
|
||||
### 测试 3: 现有项目验证
|
||||
|
||||
```bash
|
||||
# 在 ljwx-website 项目中触发 CI
|
||||
cd /path/to/ljwx-website
|
||||
git commit --allow-empty -m "test: verify org secrets"
|
||||
git push origin main
|
||||
|
||||
# 查看 Actions 日志
|
||||
http://192.168.1.83:33000/gao/ljwx-website/actions
|
||||
|
||||
# 检查 Build & Push job:
|
||||
# ✅ Harbor 登录成功
|
||||
# ✅ 镜像推送成功
|
||||
# ✅ ljwx-deploy 更新成功
|
||||
```
|
||||
|
||||
## 🔐 Secrets 管理最佳实践
|
||||
|
||||
### 1. Secrets 命名规范
|
||||
|
||||
```yaml
|
||||
# ✅ 推荐: 清晰的语义命名
|
||||
HARBOR_USERNAME
|
||||
HARBOR_PASSWORD
|
||||
DEPLOY_REPO_TOKEN
|
||||
DOCKER_REGISTRY_URL
|
||||
|
||||
# ❌ 避免: 模糊的命名
|
||||
SECRET1
|
||||
TOKEN
|
||||
PASSWORD
|
||||
```
|
||||
|
||||
### 2. Secrets 轮换策略
|
||||
|
||||
```bash
|
||||
# 定期更新 Secrets (建议 90 天)
|
||||
|
||||
# 1. 生成新的 DEPLOY_REPO_TOKEN
|
||||
http://192.168.1.83:33000/user/settings/applications
|
||||
|
||||
# 2. 更新 Organization Secret
|
||||
http://192.168.1.83:33000/gao/settings/secrets
|
||||
# 编辑 DEPLOY_REPO_TOKEN → 输入新 token
|
||||
|
||||
# 3. 撤销旧 token
|
||||
http://192.168.1.83:33000/user/settings/applications
|
||||
# 删除旧的 token
|
||||
|
||||
# 4. 验证所有项目仍正常工作
|
||||
# 触发 CI 测试
|
||||
```
|
||||
|
||||
### 3. Secrets 作用域控制
|
||||
|
||||
```yaml
|
||||
# Organization Secrets 适用于:
|
||||
✅ 共享的基础设施凭据 (Harbor, Gitea)
|
||||
✅ 通用的 API tokens
|
||||
✅ 部署凭据
|
||||
|
||||
# Repository Secrets 适用于:
|
||||
✅ 项目特定的凭据
|
||||
✅ 需要隔离的敏感信息
|
||||
✅ 不同环境的配置 (dev/staging/prod)
|
||||
```
|
||||
|
||||
### 4. 安全检查清单
|
||||
|
||||
```bash
|
||||
# ✅ 定期检查
|
||||
- [ ] Secrets 是否有过期时间
|
||||
- [ ] Token 权限是否最小化
|
||||
- [ ] 是否有未使用的 Secrets
|
||||
- [ ] Workflow 日志是否泄露 Secrets
|
||||
- [ ] 是否定期轮换凭据
|
||||
|
||||
# ❌ 避免
|
||||
- [ ] 在 Workflow 中 echo Secrets
|
||||
- [ ] 将 Secrets 写入文件
|
||||
- [ ] 在 PR 中暴露 Secrets
|
||||
- [ ] 使用过于宽泛的权限
|
||||
```
|
||||
|
||||
## 📊 当前项目配置总结
|
||||
|
||||
### 组织级别 (gao)
|
||||
|
||||
配置以下 3 个 Organization Secrets,所有项目自动可用:
|
||||
|
||||
```yaml
|
||||
HARBOR_USERNAME: admin
|
||||
HARBOR_PASSWORD: [Harbor 管理员密码]
|
||||
DEPLOY_REPO_TOKEN: [Gitea Personal Access Token with repo scope]
|
||||
```
|
||||
|
||||
### 项目列表 (自动继承)
|
||||
|
||||
以下项目无需单独配置,自动使用 Organization Secrets:
|
||||
|
||||
```
|
||||
✅ ljwx-website - 已验证
|
||||
✅ ljwx-deploy - 仅被引用,不需要 CI
|
||||
✅ backend - 可配置 CI
|
||||
✅ frontend - 可配置 CI
|
||||
✅ quality-dashboard - 可配置 CI
|
||||
✅ nginx - 可配置 CI
|
||||
✅ [未来新项目] - 自动可用
|
||||
```
|
||||
|
||||
### Workflow 模板 (通用)
|
||||
|
||||
```yaml
|
||||
# 所有项目都使用相同的 secrets 引用
|
||||
secrets.HARBOR_USERNAME
|
||||
secrets.HARBOR_PASSWORD
|
||||
secrets.DEPLOY_REPO_TOKEN
|
||||
|
||||
# 无需在每个项目中重复配置!
|
||||
```
|
||||
|
||||
## 🎯 快速开始新项目
|
||||
|
||||
现在为新项目添加 CI/CD 只需 2 步:
|
||||
|
||||
```bash
|
||||
# Step 1: 创建 workflow 文件
|
||||
mkdir -p .gitea/workflows
|
||||
cp /path/to/ljwx-website/.gitea/workflows/ci.yaml .gitea/workflows/
|
||||
|
||||
# Step 2: 修改项目名称
|
||||
sed -i 's/ljwx\/ljwx-website/ljwx\/my-new-app/g' .gitea/workflows/ci.yaml
|
||||
|
||||
# ✅ 完成!Secrets 自动可用,无需配置
|
||||
```
|
||||
|
||||
## 🔄 迁移现有项目
|
||||
|
||||
如果现有项目已经配置了 Repository Secrets:
|
||||
|
||||
```bash
|
||||
# 1. 验证 Organization Secrets 已配置
|
||||
curl -X GET "http://192.168.1.83:33000/api/v1/orgs/gao/actions/secrets" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}"
|
||||
|
||||
# 2. 删除项目级 Secrets (可选)
|
||||
# http://192.168.1.83:33000/gao/[project]/settings/secrets
|
||||
|
||||
# 3. 测试 workflow
|
||||
git commit --allow-empty -m "test: verify org secrets"
|
||||
git push origin main
|
||||
|
||||
# 4. 检查运行结果
|
||||
# http://192.168.1.83:33000/gao/[project]/actions
|
||||
```
|
||||
|
||||
## 📝 总结
|
||||
|
||||
### 配置前 (每个项目都要配置)
|
||||
|
||||
```
|
||||
ljwx-website → Settings → Secrets → HARBOR_USERNAME, HARBOR_PASSWORD, DEPLOY_REPO_TOKEN
|
||||
backend → Settings → Secrets → HARBOR_USERNAME, HARBOR_PASSWORD, DEPLOY_REPO_TOKEN
|
||||
frontend → Settings → Secrets → HARBOR_USERNAME, HARBOR_PASSWORD, DEPLOY_REPO_TOKEN
|
||||
quality-dash... → Settings → Secrets → HARBOR_USERNAME, HARBOR_PASSWORD, DEPLOY_REPO_TOKEN
|
||||
[每个新项目都要重复配置] ❌
|
||||
```
|
||||
|
||||
### 配置后 (一次配置,全局共享)
|
||||
|
||||
```
|
||||
gao (Organization) → Settings → Secrets → HARBOR_USERNAME, HARBOR_PASSWORD, DEPLOY_REPO_TOKEN
|
||||
↓
|
||||
└─ 所有项目自动继承 ✅
|
||||
- ljwx-website
|
||||
- backend
|
||||
- frontend
|
||||
- quality-dashboard
|
||||
- [未来所有新项目]
|
||||
```
|
||||
|
||||
**效果**:
|
||||
- ✅ 配置时间: 15 分钟 → 2 分钟
|
||||
- ✅ 维护成本: 降低 80%
|
||||
- ✅ 新项目开箱即用
|
||||
- ✅ 集中管理,安全性提升
|
||||
|
||||
🎉 **一次配置,永久受益!**
|
||||
@@ -0,0 +1,89 @@
|
||||
# Bid-MVP Factory Control Planes Implementation Plan (ljwx-deploy)
|
||||
|
||||
## Phase 0 Inventory
|
||||
|
||||
### Existing
|
||||
- GitOps repo with Argo CD apps under `argocd-apps/` and workload manifests under `apps/`.
|
||||
- Service images currently mostly controlled in overlay `kustomization.yaml` `images` blocks.
|
||||
- Existing utility script: `scripts/create-app-secret.sh`.
|
||||
- No `.github/workflows/` currently.
|
||||
|
||||
### Missing (to add)
|
||||
- Evidence control plane:
|
||||
- `evidence/README.md`
|
||||
- `evidence/schema/evidence-record.schema.json`
|
||||
- `evidence/records/*.json`
|
||||
- generated `evidence/index.json`
|
||||
- Evidence scripts:
|
||||
- `scripts/evidence/collect.py`
|
||||
- `scripts/evidence/validate.py`
|
||||
- Pages site source:
|
||||
- `site/index.html`
|
||||
- `site/app.js`
|
||||
- optional `site/styles.css`
|
||||
- GitHub Actions workflows:
|
||||
- nightly evidence collect + gh-pages publish
|
||||
- auto-repair (phase 5)
|
||||
- Release queue and promoter:
|
||||
- `release/queue.yaml`
|
||||
- `scripts/promoter/deploy_promoter.py`
|
||||
- `scripts/promoter/README.md`
|
||||
- k8s CronJob manifests for promoter
|
||||
- Dev image pin files:
|
||||
- `envs/dev/<service>.yaml`
|
||||
- Smoke checks:
|
||||
- `scripts/smoke/run_smoke.py`
|
||||
- k8s smoke runner CronJob or Job manifest
|
||||
- Auto repair loop:
|
||||
- `scripts/repair/recipes.yaml`
|
||||
- `scripts/repair/run_repair.py`
|
||||
|
||||
### Existing promoter/test-runner/evidence/pages status
|
||||
- Promoter: not present.
|
||||
- Test-runner/smoke runner: not present.
|
||||
- Evidence model/feed: not present.
|
||||
- Pages/gh-pages publishing workflow: not present.
|
||||
|
||||
## Phase 2 Scope (Evidence + Pages)
|
||||
- Add evidence schema/model and scripts.
|
||||
- Add nightly workflow that validates records, builds `evidence/index.json`, and publishes dashboard to `gh-pages` branch root.
|
||||
- Keep GitHub as source of truth; no external state required.
|
||||
|
||||
## Phase 3 Scope (Release Queue + Auto Promote)
|
||||
- Add queue state machine (`pending/promoted/failed`) with retry metadata.
|
||||
- Add promoter logic for per-service serialization and supersede semantics.
|
||||
- Verify Harbor digest existence via v2 manifest API (HEAD/GET) to avoid replication timing assumptions.
|
||||
- Pin dev image in `envs/dev/<svc>.yaml` as digest reference.
|
||||
- Write evidence records for promotion attempts/results.
|
||||
|
||||
## Phase 4 Scope (Post-deploy Smoke)
|
||||
- Add smoke runner to check Argo app health/sync (or endpoint readiness fallback).
|
||||
- Write smoke results into evidence record chain.
|
||||
|
||||
## Phase 5 Scope (Auto Repair Loop)
|
||||
- Add repair recipes + runner for format/lint/codegen style fixes.
|
||||
- Add automation notes/scripts for commit→PR→checks→auto-fix→recheck→merge (dev).
|
||||
- Add fail-after-N behavior to open issue with minimal repro/logs.
|
||||
- Add final `TEST.md` end-to-end dry run instructions.
|
||||
|
||||
## Phase-by-Phase Verification Commands (deploy repo)
|
||||
- Phase 0:
|
||||
- `test -f PLAN.md && sed -n '1,120p' PLAN.md`
|
||||
- Phase 2:
|
||||
- `python3 scripts/evidence/validate.py`
|
||||
- `python3 scripts/evidence/collect.py --out evidence/index.json`
|
||||
- `python3 -m json.tool evidence/index.json >/dev/null`
|
||||
- Phase 3:
|
||||
- `python3 scripts/promoter/deploy_promoter.py --help`
|
||||
- `python3 scripts/promoter/deploy_promoter.py --dry-run`
|
||||
- Phase 4:
|
||||
- `python3 scripts/smoke/run_smoke.py --help`
|
||||
- Phase 5:
|
||||
- `python3 scripts/repair/run_repair.py --help`
|
||||
|
||||
## Commit Plan (deploy repo)
|
||||
- `phase0(deploy): inventory + PLAN.md`
|
||||
- `phase2(deploy): evidence model/scripts/pages workflow/site`
|
||||
- `phase3(deploy): release queue + promoter + cronjob + runbook`
|
||||
- `phase4(deploy): smoke runner + cronjob + evidence integration docs`
|
||||
- `phase5(deploy): auto-repair recipes/runner + workflow + TEST.md`
|
||||
@@ -0,0 +1,392 @@
|
||||
# Gitea CI/CD 快速参考卡片
|
||||
|
||||
## 🎯 一次性配置 (只需做一次)
|
||||
|
||||
### 1. Organization Secrets 配置
|
||||
|
||||
```bash
|
||||
# 访问组织设置
|
||||
http://192.168.1.83:33000/gao/settings/secrets
|
||||
|
||||
# 添加 3 个 Secrets:
|
||||
HARBOR_USERNAME = admin
|
||||
HARBOR_PASSWORD = [Harbor 密码]
|
||||
DEPLOY_REPO_TOKEN = [Gitea Token]
|
||||
```
|
||||
|
||||
**详细步骤**: [GITEA-SECRETS-SETUP.md](GITEA-SECRETS-SETUP.md)
|
||||
|
||||
### 2. 生成 DEPLOY_REPO_TOKEN
|
||||
|
||||
```bash
|
||||
# 访问个人设置
|
||||
http://192.168.1.83:33000/user/settings/applications
|
||||
|
||||
# 生成新 Token:
|
||||
Name: gitea-actions-deploy
|
||||
Scopes: repo, write:repository
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 为新项目添加 CI/CD (5 分钟)
|
||||
|
||||
### Step 1: 复制 Workflow 文件
|
||||
|
||||
```bash
|
||||
cd /path/to/new-project
|
||||
|
||||
# 从 ljwx-website 复制模板
|
||||
curl -o .gitea/workflows/ci.yaml \
|
||||
http://192.168.1.83:33000/gao/ljwx-website/raw/branch/main/.gitea/workflows/ci.yaml
|
||||
```
|
||||
|
||||
### Step 2: 修改项目名称
|
||||
|
||||
```bash
|
||||
# 替换镜像名称 (只需修改这一处)
|
||||
sed -i 's/ljwx\/ljwx-website/ljwx\/my-app/g' .gitea/workflows/ci.yaml
|
||||
|
||||
# 或手动修改:
|
||||
vim .gitea/workflows/ci.yaml
|
||||
# 找到: IMAGE_NAME: ljwx/ljwx-website
|
||||
# 改为: IMAGE_NAME: ljwx/my-app
|
||||
```
|
||||
|
||||
### Step 3: 提交并推送
|
||||
|
||||
```bash
|
||||
git add .gitea/workflows/ci.yaml
|
||||
git commit -m "ci: add Gitea Actions workflow"
|
||||
git push origin main
|
||||
|
||||
# ✅ CI 自动运行,无需配置 Secrets!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 为新项目添加 CD (10 分钟)
|
||||
|
||||
### Step 1: 创建 Namespace 配置
|
||||
|
||||
```bash
|
||||
cd /path/to/ljwx-deploy
|
||||
|
||||
# 复制模板并修改
|
||||
cp cluster/namespace-ljwx-website-dev.yaml cluster/namespace-my-app-dev.yaml
|
||||
|
||||
# 替换所有 ljwx-website → my-app
|
||||
sed -i 's/ljwx-website/my-app/g' cluster/namespace-my-app-dev.yaml
|
||||
|
||||
# 更新 cluster/kustomization.yaml
|
||||
yq eval '.resources += ["namespace-my-app-dev.yaml"]' -i cluster/kustomization.yaml
|
||||
```
|
||||
|
||||
### Step 2: 创建应用配置
|
||||
|
||||
```bash
|
||||
# 复制模板
|
||||
cp -r apps/ljwx-website apps/my-app
|
||||
|
||||
# 更新配置
|
||||
sed -i 's/ljwx-website/my-app/g' apps/my-app/base/*.yaml
|
||||
sed -i 's/ljwx-website/my-app/g' apps/my-app/overlays/ljwx-website-dev/kustomization.yaml
|
||||
|
||||
# 重命名 overlay
|
||||
mv apps/my-app/overlays/ljwx-website-dev apps/my-app/overlays/my-app-dev
|
||||
|
||||
# 更新 namespace
|
||||
sed -i 's/namespace: ljwx-website-dev/namespace: my-app-dev/' \
|
||||
apps/my-app/overlays/my-app-dev/kustomization.yaml
|
||||
```
|
||||
|
||||
### Step 3: 创建 Argo CD Application
|
||||
|
||||
```bash
|
||||
# 复制模板
|
||||
cp argocd-apps/60-ljwx-website-dev.yaml argocd-apps/60-my-app-dev.yaml
|
||||
|
||||
# 替换内容
|
||||
sed -i 's/ljwx-website/my-app/g' argocd-apps/60-my-app-dev.yaml
|
||||
```
|
||||
|
||||
### Step 4: 提交并推送
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "feat: add my-app deployment configuration"
|
||||
git push origin main
|
||||
|
||||
# 等待 30 秒
|
||||
sleep 30
|
||||
|
||||
# ✅ Argo CD 自动创建 Application 并部署!
|
||||
kubectl get application my-app-dev -n argocd
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 完整文档索引
|
||||
|
||||
| 文档 | 用途 | 场景 |
|
||||
|------|------|------|
|
||||
| **[GITEA-SECRETS-SETUP.md](GITEA-SECRETS-SETUP.md)** | Organization Secrets 配置 | 首次设置 |
|
||||
| **[GITEA-CICD-SETUP-GUIDE.md](GITEA-CICD-SETUP-GUIDE.md)** | 完整 CI/CD 配置指南 | 新项目配置 |
|
||||
| **[ARGOCD-APP-MANAGEMENT.md](ARGOCD-APP-MANAGEMENT.md)** | Argo CD App of Apps 使用 | CD 管理 |
|
||||
| **[NAMESPACE-STRATEGY.md](NAMESPACE-STRATEGY.md)** | Namespace 策略分析 | 架构设计 |
|
||||
| **[QUICK-REFERENCE.md](QUICK-REFERENCE.md)** | 快速参考卡片 | 日常查阅 |
|
||||
|
||||
---
|
||||
|
||||
## 🔍 常用命令速查
|
||||
|
||||
### CI/CD 调试
|
||||
|
||||
```bash
|
||||
# 查看 Gitea Actions 运行
|
||||
http://192.168.1.83:33000/gao/[project]/actions
|
||||
|
||||
# 查看 Argo CD 应用状态
|
||||
kubectl get application -n argocd
|
||||
|
||||
# 查看 Pod 状态
|
||||
kubectl get pods -n [namespace] -l app=[app-name]
|
||||
|
||||
# 查看 Pod 日志
|
||||
kubectl logs -n [namespace] -l app=[app-name] --tail=50
|
||||
|
||||
# 手动触发 Argo CD 同步
|
||||
kubectl patch application [app-name] -n argocd \
|
||||
--type merge \
|
||||
-p '{"operation":{"initiatedBy":{"username":"admin"},"sync":{"revision":"HEAD"}}}'
|
||||
```
|
||||
|
||||
### 资源检查
|
||||
|
||||
```bash
|
||||
# 查看 namespace 资源配额使用情况
|
||||
kubectl get resourcequota -n [namespace]
|
||||
kubectl describe resourcequota -n [namespace]
|
||||
|
||||
# 查看 Pod 资源使用
|
||||
kubectl top pods -n [namespace]
|
||||
|
||||
# 查看 Node 资源使用
|
||||
kubectl top nodes
|
||||
```
|
||||
|
||||
### Harbor 镜像管理
|
||||
|
||||
```bash
|
||||
# 登录 Harbor
|
||||
docker login harbor.omniverseai.net
|
||||
|
||||
# 查看镜像列表
|
||||
curl -u admin:password \
|
||||
http://harbor.omniverseai.net/api/v2.0/projects/ljwx/repositories
|
||||
|
||||
# 查看镜像 tags
|
||||
docker buildx imagetools inspect harbor.omniverseai.net/ljwx/[app]:latest
|
||||
|
||||
# 手动拉取镜像
|
||||
docker pull harbor.omniverseai.net/ljwx/[app]:main-[sha]
|
||||
```
|
||||
|
||||
### Git 操作
|
||||
|
||||
```bash
|
||||
# 克隆 ljwx-deploy
|
||||
git clone http://192.168.1.83:33000/gao/ljwx-deploy.git
|
||||
|
||||
# 查看最近提交
|
||||
cd ljwx-deploy
|
||||
git log --oneline -10
|
||||
|
||||
# 查看特定应用的变更历史
|
||||
git log --oneline -- apps/ljwx-website/overlays/ljwx-website-dev/kustomization.yaml
|
||||
|
||||
# 回滚到上一个版本
|
||||
git revert HEAD
|
||||
git push origin main
|
||||
# Argo CD 会自动回滚部署
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 快速测试流程
|
||||
|
||||
### 测试完整 GitOps 流程
|
||||
|
||||
```bash
|
||||
# 1. 修改代码
|
||||
cd /path/to/project
|
||||
echo "// Test CI/CD" >> src/App.tsx
|
||||
|
||||
# 2. 提交并推送
|
||||
git add .
|
||||
git commit -m "test: verify GitOps flow"
|
||||
git push origin main
|
||||
|
||||
# 3. 观察 CI (约 3-5 分钟)
|
||||
# http://192.168.1.83:33000/gao/[project]/actions
|
||||
# ✅ Lint & Test → ✅ Build & Push → ✅ Update Deploy
|
||||
|
||||
# 4. 观察 CD (约 30 秒)
|
||||
watch kubectl get pods -n [namespace] -l app=[app-name]
|
||||
# 应看到旧 Pod Terminating,新 Pod Running
|
||||
|
||||
# 5. 验证镜像更新
|
||||
kubectl describe pod -n [namespace] -l app=[app-name] | grep Image:
|
||||
# 应显示新的 image tag: main-[new-sha]
|
||||
|
||||
# 6. 验证健康检查
|
||||
kubectl exec -n [namespace] deployment/[app-name] -- \
|
||||
wget -q -O- http://localhost/health
|
||||
# 应返回: healthy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Workflow 配置关键点
|
||||
|
||||
### Runner 选择
|
||||
|
||||
```yaml
|
||||
# 轻量级任务 (lint, test)
|
||||
runs-on: ubuntu-latest # K8s runner
|
||||
|
||||
# Docker 构建任务
|
||||
runs-on: docker-builder # 宿主机 runner (macos 标签也可用)
|
||||
```
|
||||
|
||||
### Multi-arch 构建
|
||||
|
||||
```yaml
|
||||
# ✅ 正确: 使用 buildx + atomic push
|
||||
- name: Set up Docker Buildx
|
||||
run: |
|
||||
docker buildx create --name ci-builder --use || docker buildx use ci-builder
|
||||
docker buildx inspect --bootstrap
|
||||
|
||||
- name: Build and push
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
.
|
||||
|
||||
# ❌ 错误: 分离的 build 和 push
|
||||
docker build -t image .
|
||||
docker push image # 会导致竞态条件
|
||||
```
|
||||
|
||||
### Kustomize 镜像更新
|
||||
|
||||
```yaml
|
||||
# ✅ 正确: 使用 yq 更新 kustomization.yaml
|
||||
cd apps/my-app/overlays/my-app-dev
|
||||
yq eval ".images[0].newTag = \"${IMAGE_TAG}\"" -i kustomization.yaml
|
||||
|
||||
# ❌ 错误: 尝试更新 Helm values.yaml
|
||||
yq eval ".image.tag = \"${IMAGE_TAG}\"" -i values.yaml # 不存在
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 安全提示
|
||||
|
||||
### Secrets 安全使用
|
||||
|
||||
```yaml
|
||||
# ✅ 安全的 Secrets 使用
|
||||
- run: echo "${{ secrets.PASSWORD }}" | docker login ... --password-stdin
|
||||
|
||||
# ❌ 危险: 泄露 Secrets
|
||||
- run: echo "Password is ${{ secrets.PASSWORD }}" # 会在日志中显示
|
||||
- run: docker login -p ${{ secrets.PASSWORD }} ... # 会在进程列表中显示
|
||||
```
|
||||
|
||||
### Token 权限最小化
|
||||
|
||||
```bash
|
||||
# DEPLOY_REPO_TOKEN 需要的权限:
|
||||
✅ repo # 读取仓库
|
||||
✅ write:repository # 写入仓库
|
||||
|
||||
# 不需要的权限:
|
||||
❌ admin:org # 组织管理
|
||||
❌ delete:packages # 删除包
|
||||
❌ admin:gpg_key # GPG 密钥管理
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 配置时间对比
|
||||
|
||||
### 使用本指南
|
||||
|
||||
```
|
||||
Organization Secrets (一次性): 5 分钟
|
||||
新项目 CI 配置: 2 分钟
|
||||
新项目 CD 配置: 8 分钟
|
||||
---------------------------------
|
||||
总计 (首次): 15 分钟
|
||||
总计 (后续每个项目): 10 分钟
|
||||
```
|
||||
|
||||
### 传统方式
|
||||
|
||||
```
|
||||
每个项目 Secrets 配置: 5 分钟
|
||||
手动编写 Workflow: 30 分钟
|
||||
手动创建 K8s 资源: 20 分钟
|
||||
手动配置 Argo CD: 10 分钟
|
||||
---------------------------------
|
||||
总计 (每个项目): 65 分钟
|
||||
```
|
||||
|
||||
**效率提升: 85%** 🚀
|
||||
|
||||
---
|
||||
|
||||
## 💡 故障排查速查表
|
||||
|
||||
| 问题 | 排查命令 | 常见原因 |
|
||||
|------|----------|----------|
|
||||
| CI 失败 | 查看 Gitea Actions 日志 | Runner 标签错误、Secrets 未配置 |
|
||||
| Build 失败 | `docker buildx ls` | buildx 未初始化 |
|
||||
| Push 失败 | `docker login harbor.omniverseai.net` | Harbor 凭据错误 |
|
||||
| Update Deploy 未执行 | 检查 ljwx-deploy 最新 commit | Token 权限不足、路径错误 |
|
||||
| Argo CD 未同步 | `kubectl get application -n argocd` | Application 文件名不匹配 pattern |
|
||||
| Pod 启动失败 | `kubectl describe pod -n [ns]` | 资源不足、镜像拉取失败 |
|
||||
| Health check 失败 | `kubectl logs -n [ns]` | /health 端点不存在 |
|
||||
|
||||
---
|
||||
|
||||
## 🎓 学习路径
|
||||
|
||||
### 阶段 1: 基础配置 (第 1 天)
|
||||
1. 阅读 [GITEA-SECRETS-SETUP.md](GITEA-SECRETS-SETUP.md)
|
||||
2. 配置 Organization Secrets
|
||||
3. 测试现有项目 (ljwx-website)
|
||||
|
||||
### 阶段 2: 新项目实践 (第 2-3 天)
|
||||
1. 阅读 [GITEA-CICD-SETUP-GUIDE.md](GITEA-CICD-SETUP-GUIDE.md)
|
||||
2. 为一个新项目配置完整 CI/CD
|
||||
3. 测试完整 GitOps 流程
|
||||
|
||||
### 阶段 3: 高级应用 (第 4-5 天)
|
||||
1. 阅读 [ARGOCD-APP-MANAGEMENT.md](ARGOCD-APP-MANAGEMENT.md)
|
||||
2. 理解 App of Apps 模式
|
||||
3. 阅读 [NAMESPACE-STRATEGY.md](NAMESPACE-STRATEGY.md)
|
||||
4. 规划多环境部署 (dev/staging/prod)
|
||||
|
||||
### 阶段 4: 运维优化 (持续)
|
||||
1. 监控 CI/CD 性能
|
||||
2. 优化 Docker 镜像大小
|
||||
3. 调整资源配额
|
||||
4. 实施安全最佳实践
|
||||
|
||||
---
|
||||
|
||||
**🎉 开始使用 Gitea GitOps,享受自动化部署的乐趣!**
|
||||
@@ -0,0 +1,114 @@
|
||||
# ljwx-deploy
|
||||
|
||||
GitOps 配置仓库,使用 ArgoCD 管理 Kubernetes 集群。
|
||||
|
||||
## 目录结构
|
||||
|
||||
- `infra/` - 基础设施组件(Gitea Runner 等)
|
||||
- `argocd-apps/` - ArgoCD Application 定义
|
||||
- `apps/` - 应用配置
|
||||
- `cluster/` - 集群级别配置
|
||||
|
||||
## 组件
|
||||
|
||||
### Gitea Runner (act_runner)
|
||||
|
||||
- 支持 Gitea Actions 的弹性 runner
|
||||
- 自动伸缩: min 1, max 5 (基于 CPU/Memory)
|
||||
- 支持 Docker build/push
|
||||
- Harbor registry: https://harbor.omniverseai.net
|
||||
|
||||
#### 配置说明
|
||||
|
||||
Runner labels:
|
||||
- `ubuntu-latest` - Ubuntu 22.04 环境
|
||||
- `ubuntu-22.04` - 明确指定版本
|
||||
- `docker` - 支持 Docker 操作
|
||||
- `self-hosted` - 自托管标识
|
||||
|
||||
弹性伸缩策略:
|
||||
- 最小副本: 1
|
||||
- 最大副本: 5
|
||||
- CPU 阈值: 70%
|
||||
- Memory 阈值: 80%
|
||||
- 扩容: 30秒内最多翻倍或增加2个
|
||||
- 缩容: 5分钟稳定期,每分钟最多减少50%
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 获取 Gitea Runner Token
|
||||
|
||||
在 Gitea 管理界面:
|
||||
1. 访问: http://192.168.1.83:33000/admin/actions/runners
|
||||
2. 点击"创建新的 Runner"
|
||||
3. 复制生成的 Registration Token
|
||||
|
||||
### 2. 创建 Runner Token Secret
|
||||
|
||||
```bash
|
||||
kubectl -n gitea-runner create secret generic runner-token \
|
||||
--from-literal=token=YOUR_REGISTRATION_TOKEN
|
||||
```
|
||||
|
||||
### 3. 部署 Runner
|
||||
|
||||
ArgoCD 会自动同步并部署 runner。
|
||||
|
||||
### 4. 在仓库中使用
|
||||
|
||||
创建 `.gitea/workflows/ci.yml`:
|
||||
|
||||
```yaml
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest # 或 docker, self-hosted
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: 构建镜像
|
||||
run: |
|
||||
docker build -t myapp:${{ gitea.sha }} .
|
||||
docker push harbor.omniverseai.net/myproject/myapp:${{ gitea.sha }}
|
||||
```
|
||||
|
||||
## 监控
|
||||
|
||||
```bash
|
||||
# 查看 runner pods
|
||||
kubectl -n gitea-runner get pods -w
|
||||
|
||||
# 查看 HPA 状态
|
||||
kubectl -n gitea-runner get hpa
|
||||
|
||||
# 查看 runner 日志
|
||||
kubectl -n gitea-runner logs -l app=gitea-runner -f
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### Runner 未注册
|
||||
|
||||
检查 token 是否正确:
|
||||
```bash
|
||||
kubectl -n gitea-runner get secret runner-token -o jsonpath='{.data.token}' | base64 -d
|
||||
```
|
||||
|
||||
### Runner 无法连接 Gitea
|
||||
|
||||
检查网络连通性:
|
||||
```bash
|
||||
kubectl -n gitea-runner run test --rm -it --image=curlimages/curl -- \
|
||||
curl http://192.168.1.83:33000
|
||||
```
|
||||
|
||||
### Docker 权限问题
|
||||
|
||||
Runner 使用宿主机 Docker socket,确保有权限访问。
|
||||
# Test stability
|
||||
@@ -1,72 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: agent-platform
|
||||
labels:
|
||||
app: agent-platform
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: agent-platform
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: agent-platform
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: regcred
|
||||
containers:
|
||||
- name: agent-platform
|
||||
image: harbor.eu.lingjingwanxiang.cn/ljwx/agent-platform-api:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
name: http
|
||||
env:
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: agent-platform-db
|
||||
key: DATABASE_URL
|
||||
- name: REDIS_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: agent-platform-app
|
||||
key: REDIS_URL
|
||||
- name: ANTHROPIC_AUTH_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: agent-platform-app
|
||||
key: ANTHROPIC_AUTH_TOKEN
|
||||
- name: JWT_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: agent-platform-app
|
||||
key: JWT_SECRET_KEY
|
||||
- name: MILVUS_HOST
|
||||
value: ""
|
||||
- name: MILVUS_PORT
|
||||
value: "19530"
|
||||
- name: APP_ENV
|
||||
value: "prod"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
failureThreshold: 5
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
cpu: "200m"
|
||||
memory: "512Mi"
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: "1Gi"
|
||||
@@ -1,25 +0,0 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: agent-platform
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: dnspod-letsencrypt
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
- agent.lingjingwanxiang.cn
|
||||
secretName: agent-platform-tls
|
||||
rules:
|
||||
- host: agent.lingjingwanxiang.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: agent-platform
|
||||
port:
|
||||
number: 8000
|
||||
@@ -1,7 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: regcred
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
data:
|
||||
.dockerconfigjson: eyJhdXRocyI6eyJoYXJib3IuZXUubGluZ2ppbmd3YW54aWFuZy5jbiI6eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJIYXJib3IxMjM0NSIsImF1dGgiOiJZV1J0YVc0NlNHRnlZbTl5TVRJek5EVT0ifX19
|
||||
@@ -1,17 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: agent-platform-db
|
||||
type: Opaque
|
||||
stringData:
|
||||
DATABASE_URL: postgresql+asyncpg://agent:ChangeMeInProduction@postgres-lb.infra.svc.cluster.local:5432/agent_platform
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: agent-platform-app
|
||||
type: Opaque
|
||||
stringData:
|
||||
REDIS_URL: redis://redis-lb.infra.svc.cluster.local:6379/0
|
||||
ANTHROPIC_AUTH_TOKEN: CHANGE_ME
|
||||
JWT_SECRET_KEY: CHANGE_ME
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: agent-platform
|
||||
resources:
|
||||
- ../../base
|
||||
images:
|
||||
- name: harbor.eu.lingjingwanxiang.cn/ljwx/agent-platform-api
|
||||
newTag: sha-dec280a
|
||||
@@ -4,43 +4,30 @@ metadata:
|
||||
name: backend
|
||||
labels:
|
||||
app: backend
|
||||
app.kubernetes.io/name: backend
|
||||
app.kubernetes.io/part-of: youngth-guard
|
||||
component: api
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: backend
|
||||
app.kubernetes.io/name: backend
|
||||
app.kubernetes.io/part-of: youngth-guard
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: backend
|
||||
app.kubernetes.io/name: backend
|
||||
app.kubernetes.io/part-of: youngth-guard
|
||||
component: api
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: harbor-registry
|
||||
containers:
|
||||
- name: backend
|
||||
image: harbor.omniverseai.net/ljwx/ljwx-shiti:sha-775861d
|
||||
imagePullPolicy: IfNotPresent
|
||||
image: harbor.omniverseai.net/ljwx/backend-template:dev
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
name: http
|
||||
- name: http
|
||||
containerPort: 8000
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: ENVIRONMENT
|
||||
value: "dev"
|
||||
- name: LOG_LEVEL
|
||||
value: "info"
|
||||
- name: DATABASE_URL
|
||||
value: "postgresql+asyncpg://appuser:123456@postgres-infra-postgresql.infra.svc.cluster.local:5432/appdb"
|
||||
- name: REDIS_URL
|
||||
value: "redis://:123456@redis-infra-master.infra.svc.cluster.local:6379/0"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
@@ -55,8 +42,8 @@ spec:
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
|
||||
@@ -4,3 +4,7 @@ kind: Kustomization
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
|
||||
commonLabels:
|
||||
app.kubernetes.io/name: backend
|
||||
app.kubernetes.io/part-of: youngth-guard
|
||||
|
||||
@@ -4,17 +4,13 @@ metadata:
|
||||
name: backend
|
||||
labels:
|
||||
app: backend
|
||||
app.kubernetes.io/name: backend
|
||||
app.kubernetes.io/part-of: youngth-guard
|
||||
component: api
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: backend
|
||||
app.kubernetes.io/name: backend
|
||||
app.kubernetes.io/part-of: youngth-guard
|
||||
ports:
|
||||
- name: http
|
||||
port: 8000
|
||||
- port: 8000
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: backend
|
||||
|
||||
@@ -5,3 +5,11 @@ namespace: dev
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
images:
|
||||
- name: harbor.omniverseai.net/ljwx/backend-template
|
||||
newTag: "dev"
|
||||
|
||||
replicas:
|
||||
- name: backend
|
||||
count: 1
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: frontend
|
||||
labels:
|
||||
app: frontend
|
||||
component: web
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: frontend
|
||||
component: web
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: harbor.omniverseai.net/ljwx/frontend-dashboard:dev
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "200m"
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
|
||||
commonLabels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/part-of: youngth-guard
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: frontend
|
||||
labels:
|
||||
app: frontend
|
||||
component: web
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: frontend
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: dev
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
images:
|
||||
- name: harbor.omniverseai.net/ljwx/frontend-dashboard
|
||||
newTag: "dev"
|
||||
|
||||
replicas:
|
||||
- name: frontend
|
||||
count: 1
|
||||
@@ -1,326 +0,0 @@
|
||||
# ljwx-bookstore GitOps 配置
|
||||
|
||||
智能小说阅读平台的 Kubernetes GitOps 部署配置,使用 Kustomize 管理。
|
||||
|
||||
## 📋 概览
|
||||
|
||||
- **应用名称**: ljwx-bookstore
|
||||
- **代码仓库**: https://github.com/BrunoGaoSZ/ljwx-bookstore
|
||||
- **GitOps 仓库**: https://github.com/BrunoGaoSZ/ljwx-deploy
|
||||
- **命名空间**: bookstore
|
||||
- **配置管理**: Kustomize
|
||||
- **GitOps 控制器**: Argo CD
|
||||
|
||||
## 🚀 快速部署
|
||||
|
||||
### 前置条件
|
||||
|
||||
1. **Kubernetes 集群**: K3s 或其他 Kubernetes 集群
|
||||
2. **Argo CD**: 已安装并配置
|
||||
3. **基础设施**:
|
||||
- MySQL 8.0 (infra namespace)
|
||||
- Redis 7.2.12 (ljwx-health namespace)
|
||||
- Traefik Ingress Controller
|
||||
- cert-manager + Let's Encrypt
|
||||
|
||||
### 创建必需的 Secrets
|
||||
|
||||
```bash
|
||||
# 1. 创建命名空间
|
||||
kubectl create namespace bookstore
|
||||
|
||||
# 2. GHCR 镜像拉取凭证
|
||||
kubectl create secret docker-registry ghcr-secret \
|
||||
--docker-server=ghcr.io \
|
||||
--docker-username=<github-username> \
|
||||
--docker-password=<github-token> \
|
||||
-n bookstore
|
||||
|
||||
# 3. Redis 连接配置
|
||||
kubectl create secret generic redis-secret \
|
||||
--from-literal=host=redis.ljwx-health.svc.cluster.local \
|
||||
--from-literal=port=6379 \
|
||||
--from-literal=password=<redis-password> \
|
||||
-n bookstore
|
||||
```
|
||||
|
||||
### 部署应用
|
||||
|
||||
```bash
|
||||
# 方法 1: 使用 Argo CD Application CRD
|
||||
kubectl apply -f argocd-apps/40-ljwx-bookstore.yaml
|
||||
|
||||
# 方法 2: 使用 Argo CD CLI
|
||||
argocd app create ljwx-bookstore \
|
||||
--repo https://github.com/BrunoGaoSZ/ljwx-deploy.git \
|
||||
--path apps/ljwx-bookstore/overlays/bookstore \
|
||||
--dest-server https://kubernetes.default.svc \
|
||||
--dest-namespace bookstore \
|
||||
--sync-policy automated \
|
||||
--auto-prune \
|
||||
--self-heal
|
||||
|
||||
# 查看部署状态
|
||||
argocd app get ljwx-bookstore
|
||||
argocd app sync ljwx-bookstore
|
||||
```
|
||||
|
||||
## 📁 目录结构
|
||||
|
||||
```
|
||||
apps/ljwx-bookstore/
|
||||
├── base/ # 基础配置
|
||||
│ ├── deployment.yaml # Deployment 定义
|
||||
│ ├── service.yaml # Service 定义
|
||||
│ ├── ingress.yaml # Ingress + Middleware(HTTPS + 根路径重定向)
|
||||
│ ├── ghcr-secret.yaml # GHCR 拉取凭证引用
|
||||
│ ├── redis-secret.yaml # Redis 连接配置引用
|
||||
│ └── kustomization.yaml # Kustomize 基础配置
|
||||
│
|
||||
└── overlays/ # 环境特定配置
|
||||
└── bookstore/ # bookstore 环境
|
||||
└── kustomization.yaml # 镜像版本配置(自动更新)
|
||||
```
|
||||
|
||||
## ⚙️ 配置说明
|
||||
|
||||
### Deployment 配置
|
||||
|
||||
**文件**: `base/deployment.yaml`
|
||||
|
||||
- **副本数**: 2
|
||||
- **镜像**: `ghcr.io/brunogaosz/ljwx-bookstore/bookstore:bookstore-<commit>`
|
||||
- **端口**: 8080
|
||||
- **环境变量**:
|
||||
- `SPRING_DATASOURCE_URL`: MySQL 连接(infra namespace)
|
||||
- `SPRING_DATASOURCE_USERNAME`: bookstore
|
||||
- `SPRING_DATASOURCE_PASSWORD`: 从 mysql-standalone Secret 获取
|
||||
- `SPRING_DATA_REDIS_HOST`: 从 redis-secret 获取
|
||||
- `SPRING_DATA_REDIS_PORT`: 从 redis-secret 获取
|
||||
- `SPRING_DATA_REDIS_PASSWORD`: 从 redis-secret 获取
|
||||
- **健康检查**:
|
||||
- Liveness Probe: `/fiction/actuator/health/liveness`
|
||||
- Readiness Probe: `/fiction/actuator/health/readiness`
|
||||
|
||||
### Service 配置
|
||||
|
||||
**文件**: `base/service.yaml`
|
||||
|
||||
- **类型**: ClusterIP
|
||||
- **端口**: 8080 → 8080
|
||||
|
||||
### Ingress 配置
|
||||
|
||||
**文件**: `base/ingress.yaml`
|
||||
|
||||
- **Ingress Controller**: Traefik
|
||||
- **域名**: bookstore.lingjingwanxiang.cn
|
||||
- **功能**:
|
||||
- ✅ HTTPS 自动重定向
|
||||
- ✅ Let's Encrypt 证书自动签发(cert-manager)
|
||||
- ✅ 根路径(`/`)自动重定向到 `/fiction/index`
|
||||
- **TLS Secret**: bookstore-lingjingwanxiang-tls
|
||||
- **ClusterIssuer**: dnspod-letsencrypt
|
||||
|
||||
**Traefik Middleware**: 实现根路径重定向
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: redirect-root
|
||||
namespace: bookstore
|
||||
spec:
|
||||
redirectRegex:
|
||||
regex: "^https?://[^/]+/$"
|
||||
replacement: "https://bookstore.lingjingwanxiang.cn/fiction/index"
|
||||
permanent: false
|
||||
```
|
||||
|
||||
### Kustomize 配置
|
||||
|
||||
**基础配置** (`base/kustomization.yaml`):
|
||||
```yaml
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress.yaml
|
||||
- ghcr-secret.yaml
|
||||
- redis-secret.yaml
|
||||
```
|
||||
|
||||
**环境配置** (`overlays/bookstore/kustomization.yaml`):
|
||||
```yaml
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
namespace: bookstore
|
||||
|
||||
images:
|
||||
- name: ghcr.io/brunogaosz/ljwx-bookstore/bookstore
|
||||
newName: ghcr.io/brunogaosz/ljwx-bookstore/bookstore
|
||||
newTag: "bookstore-<commit-sha>" # 由 CI 自动更新
|
||||
```
|
||||
|
||||
## 🔄 GitOps 工作流程
|
||||
|
||||
### 自动化部署流程
|
||||
|
||||
1. **代码提交** → GitHub (ljwx-bookstore)
|
||||
2. **GitHub Actions CI**:
|
||||
- 构建 Docker 镜像(多架构:linux/amd64, linux/arm64)
|
||||
- 推送到 GHCR: `ghcr.io/brunogaosz/ljwx-bookstore/bookstore:bookstore-<commit>`
|
||||
3. **GitOps 更新**:
|
||||
- CI 自动创建 PR 到 ljwx-deploy 仓库
|
||||
- 更新 `overlays/bookstore/kustomization.yaml` 中的镜像标签
|
||||
4. **PR 合并** → main 分支
|
||||
5. **Argo CD 同步**:
|
||||
- 检测 Git 仓库变更(轮询间隔:3 分钟)
|
||||
- 自动同步到 Kubernetes 集群
|
||||
6. **滚动更新**:
|
||||
- Kubernetes 执行零停机滚动更新
|
||||
- 新 Pod 启动 → 健康检查通过 → 旧 Pod 终止
|
||||
|
||||
### 手动更新配置
|
||||
|
||||
```bash
|
||||
# 1. 克隆 GitOps 仓库
|
||||
git clone https://github.com/BrunoGaoSZ/ljwx-deploy.git
|
||||
cd ljwx-deploy
|
||||
|
||||
# 2. 修改配置
|
||||
vim apps/ljwx-bookstore/base/deployment.yaml
|
||||
|
||||
# 3. 提交变更
|
||||
git add .
|
||||
git commit -m "feat: update bookstore configuration"
|
||||
git push
|
||||
|
||||
# 4. Argo CD 自动同步(~3 分钟)
|
||||
# 或手动触发同步
|
||||
argocd app sync ljwx-bookstore
|
||||
```
|
||||
|
||||
## 📊 监控和验证
|
||||
|
||||
### 查看部署状态
|
||||
|
||||
```bash
|
||||
# Argo CD 应用状态
|
||||
argocd app get ljwx-bookstore
|
||||
|
||||
# Kubernetes 资源状态
|
||||
kubectl get all -n bookstore
|
||||
|
||||
# Pod 日志
|
||||
kubectl logs -f -n bookstore -l app=bookstore
|
||||
|
||||
# Ingress 状态
|
||||
kubectl get ingress -n bookstore
|
||||
kubectl describe ingress bookstore-ingress -n bookstore
|
||||
```
|
||||
|
||||
### 健康检查
|
||||
|
||||
```bash
|
||||
# 应用健康检查
|
||||
curl https://bookstore.lingjingwanxiang.cn/fiction/actuator/health
|
||||
|
||||
# 预期响应
|
||||
{
|
||||
"status": "UP",
|
||||
"components": {
|
||||
"db": {"status": "UP"},
|
||||
"redis": {"status": "UP"},
|
||||
"diskSpace": {"status": "UP"},
|
||||
"livenessState": {"status": "UP"},
|
||||
"readinessState": {"status": "UP"}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 访问测试
|
||||
|
||||
```bash
|
||||
# 测试根路径重定向
|
||||
curl -I https://bookstore.lingjingwanxiang.cn/
|
||||
# 预期: HTTP 307 重定向到 /fiction/index
|
||||
|
||||
# 访问应用
|
||||
curl -L https://bookstore.lingjingwanxiang.cn/
|
||||
# 预期: HTTP 200,返回首页 HTML
|
||||
```
|
||||
|
||||
## 🔧 故障排查
|
||||
|
||||
### Pod 无法启动
|
||||
|
||||
```bash
|
||||
# 查看 Pod 状态
|
||||
kubectl get pods -n bookstore
|
||||
|
||||
# 查看 Pod 事件
|
||||
kubectl describe pod <pod-name> -n bookstore
|
||||
|
||||
# 查看日志
|
||||
kubectl logs <pod-name> -n bookstore
|
||||
|
||||
# 常见问题:
|
||||
# 1. 镜像拉取失败 → 检查 ghcr-secret
|
||||
# 2. 健康检查失败 → 检查 MySQL/Redis 连接
|
||||
# 3. ConfigMap/Secret 不存在 → 检查依赖资源
|
||||
```
|
||||
|
||||
### Ingress 无法访问
|
||||
|
||||
```bash
|
||||
# 检查 Ingress 资源
|
||||
kubectl get ingress -n bookstore
|
||||
kubectl describe ingress bookstore-ingress -n bookstore
|
||||
|
||||
# 检查 TLS 证书
|
||||
kubectl get certificate -n bookstore
|
||||
kubectl describe certificate -n bookstore
|
||||
|
||||
# 检查 Traefik 日志
|
||||
kubectl logs -n kube-system -l app.kubernetes.io/name=traefik
|
||||
```
|
||||
|
||||
### Argo CD 同步失败
|
||||
|
||||
```bash
|
||||
# 查看同步状态
|
||||
argocd app get ljwx-bookstore
|
||||
|
||||
# 查看同步日志
|
||||
argocd app logs ljwx-bookstore
|
||||
|
||||
# 手动同步
|
||||
argocd app sync ljwx-bookstore --force
|
||||
|
||||
# 常见问题:
|
||||
# 1. Git 仓库无法访问 → 检查网络和认证
|
||||
# 2. Kustomize 配置错误 → 检查 YAML 语法
|
||||
# 3. 资源冲突 → 使用 --force 强制同步
|
||||
```
|
||||
|
||||
## 📖 相关文档
|
||||
|
||||
- **代码仓库**: https://github.com/BrunoGaoSZ/ljwx-bookstore
|
||||
- **应用访问**: https://bookstore.lingjingwanxiang.cn
|
||||
- **Argo CD 指南**: [docs/argocd-migration/README.md](../../docs/argocd-migration/README.md)
|
||||
- **Flyway 迁移**: [docs/flyway-guide/README.md](../../docs/flyway-guide/README.md)
|
||||
|
||||
## 🎯 最佳实践
|
||||
|
||||
1. **永远不要直接修改 Kubernetes 资源** - 所有变更通过 Git 提交
|
||||
2. **使用语义化提交信息** - 便于追踪变更历史
|
||||
3. **环境隔离** - 使用 Kustomize overlays 管理不同环境
|
||||
4. **自动化测试** - CI 流程包含镜像构建和推送验证
|
||||
5. **监控告警** - 配置 Prometheus + Grafana 监控(计划中)
|
||||
|
||||
## 📝 变更历史
|
||||
|
||||
- **2025-12-20**: 配置 HTTPS Ingress + 根路径重定向
|
||||
- **2025-12-19**: 完成 GitOps 自动部署流程
|
||||
- **2025-12-18**: 配置 MySQL 和 Redis 连接
|
||||
- **2025-12-17**: 初始化 Kustomize 配置
|
||||
@@ -0,0 +1,173 @@
|
||||
# ljwx-bookstore DNS 配置指南
|
||||
|
||||
## 域名信息
|
||||
|
||||
- **生产域名**: `book.omniverseai.net`
|
||||
- **DNS提供商**: Cloudflare
|
||||
- **SSL证书**: Let's Encrypt (自动通过cert-manager获取)
|
||||
|
||||
## DNS记录配置
|
||||
|
||||
### 步骤1: 登录 Cloudflare
|
||||
|
||||
1. 访问 https://dash.cloudflare.com/
|
||||
2. 选择域名 `omniverseai.net`
|
||||
3. 进入 DNS 管理页面
|
||||
|
||||
### 步骤2: 添加 A 记录
|
||||
|
||||
需要添加以下DNS记录:
|
||||
|
||||
| 类型 | 名称 | 内容 | 代理状态 | TTL |
|
||||
|------|------|------|----------|-----|
|
||||
| A | book | `<INGRESS_IP>` | 仅DNS(灰色云朵) | Auto |
|
||||
|
||||
**获取 Ingress IP**:
|
||||
```bash
|
||||
kubectl get ingress ljwx-bookstore -n ljwx-bookstore \
|
||||
-o jsonpath='{.status.loadBalancer.ingress[0].ip}'
|
||||
```
|
||||
|
||||
**当前 Ingress IP**:
|
||||
```bash
|
||||
# 从集群中获取
|
||||
192.168.194.177 # 示例,请使用实际IP
|
||||
```
|
||||
|
||||
### 步骤3: Cloudflare 配置建议
|
||||
|
||||
#### SSL/TLS 设置
|
||||
1. 进入 `SSL/TLS` → `概述`
|
||||
2. 加密模式选择: **完全(严格)** 或 **完全**
|
||||
3. 推荐: **完全(严格)** - 确保端到端加密
|
||||
|
||||
#### 代理状态
|
||||
- **开发/测试**: 仅DNS(灰色云朵)- 直连Kubernetes
|
||||
- **生产环境**: 代理(橙色云朵)- 使用Cloudflare CDN和DDoS防护
|
||||
|
||||
#### 其他推荐配置
|
||||
- 启用 **自动HTTPS重写**
|
||||
- 启用 **始终使用HTTPS**
|
||||
- 配置 **最小TLS版本**: TLS 1.2
|
||||
|
||||
## 验证步骤
|
||||
|
||||
### 1. DNS解析验证
|
||||
|
||||
```bash
|
||||
# 检查DNS记录
|
||||
dig book.omniverseai.net +short
|
||||
|
||||
# 或使用nslookup
|
||||
nslookup book.omniverseai.net
|
||||
```
|
||||
|
||||
**期望输出**: 应该返回Ingress Controller的IP地址
|
||||
|
||||
### 2. SSL证书验证
|
||||
|
||||
等待2-5分钟,cert-manager会自动申请证书:
|
||||
|
||||
```bash
|
||||
# 检查Certificate资源
|
||||
kubectl get certificate -n ljwx-bookstore
|
||||
|
||||
# 查看证书详情
|
||||
kubectl describe certificate ljwx-bookstore-tls -n ljwx-bookstore
|
||||
|
||||
# 检查证书Secret
|
||||
kubectl get secret ljwx-bookstore-tls -n ljwx-bookstore
|
||||
```
|
||||
|
||||
**期望状态**: `READY=True`
|
||||
|
||||
### 3. 访问测试
|
||||
|
||||
```bash
|
||||
# HTTP 自动重定向到 HTTPS
|
||||
curl -I http://book.omniverseai.net
|
||||
|
||||
# HTTPS 访问
|
||||
curl -I https://book.omniverseai.net
|
||||
|
||||
# 浏览器访问
|
||||
open https://book.omniverseai.net/fiction/index
|
||||
```
|
||||
|
||||
### 4. SSL证书检查
|
||||
|
||||
```bash
|
||||
# 使用 openssl 检查证书
|
||||
echo | openssl s_client -servername book.omniverseai.net \
|
||||
-connect book.omniverseai.net:443 2>/dev/null | \
|
||||
openssl x509 -noout -dates -subject
|
||||
|
||||
# 在线检查
|
||||
# 访问 https://www.ssllabs.com/ssltest/
|
||||
# 输入: book.omniverseai.net
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### DNS未解析
|
||||
|
||||
```bash
|
||||
# 检查Cloudflare DNS设置
|
||||
# 1. 确认A记录存在
|
||||
# 2. 确认IP地址正确
|
||||
# 3. 清除本地DNS缓存: sudo dscacheutil -flushcache (macOS)
|
||||
```
|
||||
|
||||
### 证书未自动生成
|
||||
|
||||
```bash
|
||||
# 检查 cert-manager 日志
|
||||
kubectl logs -n cert-manager -l app=cert-manager --tail=50
|
||||
|
||||
# 检查 Certificate Events
|
||||
kubectl describe certificate ljwx-bookstore-tls -n ljwx-bookstore
|
||||
|
||||
# 检查 CertificateRequest
|
||||
kubectl get certificaterequest -n ljwx-bookstore
|
||||
kubectl describe certificaterequest -n ljwx-bookstore
|
||||
|
||||
# 常见问题:
|
||||
# 1. Cloudflare API Token权限不足
|
||||
# 2. DNS记录配置错误
|
||||
# 3. ClusterIssuer配置问题
|
||||
```
|
||||
|
||||
### HTTPS访问失败
|
||||
|
||||
```bash
|
||||
# 检查 Ingress 状态
|
||||
kubectl get ingress ljwx-bookstore -n ljwx-bookstore
|
||||
kubectl describe ingress ljwx-bookstore -n ljwx-bookstore
|
||||
|
||||
# 检查 Ingress Controller 日志
|
||||
kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller --tail=50
|
||||
```
|
||||
|
||||
## 生产环境清单
|
||||
|
||||
部署到生产环境前,确认以下事项:
|
||||
|
||||
- [ ] DNS A记录已添加并解析正确
|
||||
- [ ] SSL证书自动获取成功(`kubectl get certificate -n ljwx-bookstore`)
|
||||
- [ ] HTTPS强制重定向已启用
|
||||
- [ ] 健康检查正常工作
|
||||
- [ ] 应用可通过 https://book.omniverseai.net 访问
|
||||
- [ ] SSL证书A级评分(ssllabs.com测试)
|
||||
- [ ] Cloudflare防火墙规则已配置(可选)
|
||||
- [ ] 监控和告警已设置(可选)
|
||||
|
||||
## 相关资源
|
||||
|
||||
- Ingress配置: `ingress.yaml`
|
||||
- cert-manager文档: https://cert-manager.io/docs/
|
||||
- Cloudflare API: https://developers.cloudflare.com/api/
|
||||
- Let's Encrypt文档: https://letsencrypt.org/docs/
|
||||
|
||||
## 更新历史
|
||||
|
||||
- 2026-01-01: 初始配置,添加 book.omniverseai.net 域名
|
||||
@@ -0,0 +1,173 @@
|
||||
# ljwx-bookstore Secret 管理
|
||||
|
||||
## 概述
|
||||
|
||||
为了符合安全最佳实践和GitOps原则,ljwx-bookstore的Secret**不在Git中存储**。
|
||||
|
||||
## Secret 创建方式
|
||||
|
||||
### 自动化脚本(推荐)
|
||||
|
||||
使用提供的脚本从infra namespace自动获取密码并创建Secret:
|
||||
|
||||
```bash
|
||||
# 在 ljwx-deploy 仓库根目录执行
|
||||
./scripts/create-app-secret.sh ljwx-bookstore
|
||||
```
|
||||
|
||||
### 手动创建
|
||||
|
||||
如果自动化脚本不可用,使用以下命令手动创建:
|
||||
|
||||
```bash
|
||||
# 从 infra namespace 获取基础设施密码
|
||||
MYSQL_PASSWORD=$(kubectl get secret infra-credentials -n infra -o jsonpath='{.data.mysql-root-password}' | base64 -d)
|
||||
REDIS_PASSWORD=$(kubectl get secret infra-credentials -n infra -o jsonpath='{.data.redis-password}' | base64 -d)
|
||||
|
||||
# 创建 ljwx-bookstore Secret
|
||||
kubectl create secret generic ljwx-bookstore-secret \
|
||||
--from-literal=DB_HOST='mysql-infra.infra.svc.cluster.local' \
|
||||
--from-literal=DB_PORT='3306' \
|
||||
--from-literal=DB_NAME='org_fiction' \
|
||||
--from-literal=DB_USER='root' \
|
||||
--from-literal=DB_PASSWORD="${MYSQL_PASSWORD}" \
|
||||
--from-literal=REDIS_HOST='redis-infra-master.infra.svc.cluster.local' \
|
||||
--from-literal=REDIS_PORT='6379' \
|
||||
--from-literal=REDIS_PASSWORD="${REDIS_PASSWORD}" \
|
||||
--from-literal=WX_APP_SECRET="placeholder" \
|
||||
--from-literal=WX_MCH_KEY="placeholder" \
|
||||
--from-literal=MAIL_PASSWORD="placeholder" \
|
||||
--from-literal=SMS_TENCENT_APPKEY="placeholder" \
|
||||
--from-literal=SMS_ALIYUN_ACCESS_KEY_SECRET="xxx" \
|
||||
--from-literal=STORAGE_ALIYUN_ACCESS_KEY_ID="placeholder" \
|
||||
--from-literal=STORAGE_ALIYUN_ACCESS_KEY_SECRET="placeholder" \
|
||||
--from-literal=STORAGE_TENCENT_SECRET_ID="placeholder" \
|
||||
--from-literal=STORAGE_TENCENT_SECRET_KEY="placeholder" \
|
||||
--from-literal=AI_CLOUD_API_KEY="" \
|
||||
--namespace=ljwx-bookstore
|
||||
```
|
||||
|
||||
## Secret 架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ infra namespace │
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ infra-credentials Secret │ │ ← 唯一真实来源
|
||||
│ │ - mysql-root-password │ │ (infra团队管理)
|
||||
│ │ - redis-password │ │
|
||||
│ │ - postgres-password │ │
|
||||
│ └─────────────────────────────────┘ │
|
||||
└──────────────┬──────────────────────┘
|
||||
│
|
||||
│ 手动复制密码
|
||||
│ (未来: External Secrets Operator自动同步)
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ ljwx-bookstore namespace │
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ ljwx-bookstore-secret │ │ ← 手动创建(一次性)
|
||||
│ │ - DB_PASSWORD (从infra复制) │ │
|
||||
│ │ - REDIS_PASSWORD (从infra复制) │ │
|
||||
│ └─────────────────────────────────┘ │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 部署流程
|
||||
|
||||
### 新环境部署
|
||||
|
||||
1. **创建 infra namespace 基础设施密码**(infra团队负责)
|
||||
```bash
|
||||
kubectl create secret generic infra-credentials \
|
||||
--from-literal=mysql-root-password='YOUR_PASSWORD' \
|
||||
--from-literal=redis-password='YOUR_PASSWORD' \
|
||||
--namespace=infra
|
||||
```
|
||||
|
||||
2. **创建应用 Secret**
|
||||
```bash
|
||||
./scripts/create-app-secret.sh ljwx-bookstore
|
||||
```
|
||||
|
||||
3. **部署应用**(Argo CD自动同步)
|
||||
```bash
|
||||
argocd app sync ljwx-bookstore
|
||||
```
|
||||
|
||||
### 密码轮换
|
||||
|
||||
当infra团队更新基础设施密码时:
|
||||
|
||||
```bash
|
||||
# 1. infra团队更新 infra-credentials
|
||||
kubectl patch secret infra-credentials -n infra \
|
||||
--type='json' \
|
||||
-p='[{"op": "replace", "path": "/data/mysql-root-password", "value": "BASE64_NEW_PASSWORD"}]'
|
||||
|
||||
# 2. 重新创建应用Secret
|
||||
kubectl delete secret ljwx-bookstore-secret -n ljwx-bookstore
|
||||
./scripts/create-app-secret.sh ljwx-bookstore
|
||||
|
||||
# 3. 重启应用
|
||||
kubectl rollout restart deployment ljwx-bookstore -n ljwx-bookstore
|
||||
```
|
||||
|
||||
## 验证
|
||||
|
||||
```bash
|
||||
# 检查Secret是否存在
|
||||
kubectl get secret ljwx-bookstore-secret -n ljwx-bookstore
|
||||
|
||||
# 验证密码值
|
||||
kubectl get secret ljwx-bookstore-secret -n ljwx-bookstore \
|
||||
-o jsonpath='{.data.DB_PASSWORD}' | base64 -d
|
||||
|
||||
# 应该输出与infra-credentials中相同的密码
|
||||
```
|
||||
|
||||
## 路线图
|
||||
|
||||
**当前状态**(过渡方案):
|
||||
- ✅ Secret不在Git中(符合安全最佳实践)
|
||||
- ✅ 使用脚本从infra统一获取密码(避免手动输入错误)
|
||||
- ⚠️ 需要手动创建一次Secret
|
||||
|
||||
**未来计划**(完全GitOps):
|
||||
- [ ] 部署 External Secrets Operator
|
||||
- [ ] 迁移到 ExternalSecret CRD
|
||||
- [ ] 实现自动密码同步
|
||||
- [ ] 无需任何手动操作
|
||||
|
||||
## 故障排查
|
||||
|
||||
### Secret未找到
|
||||
|
||||
```bash
|
||||
# 检查Secret是否存在
|
||||
kubectl get secret ljwx-bookstore-secret -n ljwx-bookstore
|
||||
|
||||
# 如果不存在,创建它
|
||||
./scripts/create-app-secret.sh ljwx-bookstore
|
||||
```
|
||||
|
||||
### 密码不正确
|
||||
|
||||
```bash
|
||||
# 比较应用Secret和infra Secret
|
||||
echo "infra密码:"
|
||||
kubectl get secret infra-credentials -n infra -o jsonpath='{.data.mysql-root-password}' | base64 -d
|
||||
|
||||
echo ""
|
||||
echo "应用密码:"
|
||||
kubectl get secret ljwx-bookstore-secret -n ljwx-bookstore -o jsonpath='{.data.DB_PASSWORD}' | base64 -d
|
||||
|
||||
# 如果不一致,重新创建
|
||||
kubectl delete secret ljwx-bookstore-secret -n ljwx-bookstore
|
||||
./scripts/create-app-secret.sh ljwx-bookstore
|
||||
```
|
||||
|
||||
## 相关文档
|
||||
|
||||
- 详细架构设计:`/docs/GITOPS_SECRET_MANAGEMENT.md`
|
||||
- infra namespace密码管理:`/infra/secrets/README.md`
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# ljwx-bookstore ConfigMap Configuration
|
||||
# 应用配置和非敏感环境变量
|
||||
# =============================================================================
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ljwx-bookstore-config
|
||||
namespace: ljwx-bookstore
|
||||
labels:
|
||||
app: ljwx-bookstore
|
||||
component: config
|
||||
data:
|
||||
# 应用基础配置
|
||||
SPRING_PROFILES_ACTIVE: "prod"
|
||||
SERVER_PORT: "8844"
|
||||
|
||||
# JVM配置
|
||||
JAVA_OPTS: "-Xms1g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof"
|
||||
|
||||
# 数据库连接池配置(非敏感)
|
||||
HIKARI_MAXIMUM_POOL_SIZE: "20"
|
||||
HIKARI_MINIMUM_IDLE: "5"
|
||||
HIKARI_CONNECTION_TIMEOUT: "20000"
|
||||
HIKARI_IDLE_TIMEOUT: "300000"
|
||||
|
||||
# AI服务配置
|
||||
AI_ENABLED: "true"
|
||||
AI_LOCAL_ENABLED: "true"
|
||||
AI_LOCAL_PROVIDER: "ollama"
|
||||
AI_LOCAL_OLLAMA_URL: "http://ollama.devops.svc.cluster.local:11434"
|
||||
AI_LOCAL_DEFAULT_MODEL: "qwen2.5:7b"
|
||||
AI_LOCAL_FALLBACK_MODEL: "llama3.2:8b"
|
||||
AI_CLOUD_ENABLED: "false"
|
||||
|
||||
# OpenSearch配置
|
||||
AI_OPENSEARCH_HOST: "opensearch.monitoring.svc.cluster.local"
|
||||
AI_OPENSEARCH_PORT: "9200"
|
||||
AI_OPENSEARCH_PARAGRAPH_INDEX: "ai_paragraphs"
|
||||
|
||||
# 对象存储配置
|
||||
HGBOOK_STORAGE_ACTIVE: "aliyun"
|
||||
HGBOOK_STORAGE_ALIYUN_REGION_ID: "cn-zhangjiakou"
|
||||
HGBOOK_STORAGE_ALIYUN_BUCKET_NAME: "hg-tech"
|
||||
|
||||
# 监控配置
|
||||
MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health,info,metrics,prometheus"
|
||||
MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS: "always"
|
||||
MANAGEMENT_METRICS_EXPORT_PROMETHEUS_ENABLED: "true"
|
||||
|
||||
# 日志配置
|
||||
LOGGING_LEVEL_ROOT: "INFO"
|
||||
LOGGING_LEVEL_CN_BOOK_BUS: "DEBUG"
|
||||
LOGGING_PATTERN_CONSOLE: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
|
||||
|
||||
# Flyway配置
|
||||
SPRING_FLYWAY_ENABLED: "true"
|
||||
SPRING_FLYWAY_BASELINE_ON_MIGRATE: "true"
|
||||
SPRING_FLYWAY_LOCATIONS: "classpath:db/migration"
|
||||
|
||||
# MyBatis Plus配置
|
||||
MYBATIS_CONFIGURATION_MAP_UNDERSCORE_TO_CAMEL_CASE: "true"
|
||||
MYBATIS_CONFIGURATION_CACHE_ENABLED: "true"
|
||||
|
||||
# 微信支付配置(非敏感)
|
||||
HGBOOK_WX_APP_ID: "wxe92bc58a93b04a87"
|
||||
HGBOOK_WX_MCH_ID: "1623644809"
|
||||
HGBOOK_WX_NOTIFY_URL: "http://bookstore.192.168.1.83.nip.io/wx/order/pay-notify"
|
||||
|
||||
# 邮件配置(非敏感)
|
||||
HGBOOK_NOTIFY_MAIL_ENABLE: "true"
|
||||
HGBOOK_NOTIFY_MAIL_HOST: "smtp.qq.com"
|
||||
HGBOOK_NOTIFY_MAIL_PORT: "465"
|
||||
HGBOOK_NOTIFY_MAIL_USERNAME: "38455629@qq.com"
|
||||
HGBOOK_NOTIFY_MAIL_SENDFROM: "38455629@qq.com"
|
||||
HGBOOK_NOTIFY_MAIL_SENDTO: "gaojunivas@gmail.com"
|
||||
|
||||
# 短信配置(非敏感)
|
||||
HGBOOK_NOTIFY_SMS_ENABLE: "true"
|
||||
HGBOOK_NOTIFY_SMS_ACTIVE: "tencent"
|
||||
HGBOOK_NOTIFY_SMS_SIGN: "和光文化"
|
||||
HGBOOK_NOTIFY_SMS_TENCENT_APPID: "1400311284"
|
||||
|
||||
# OpenTelemetry配置
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector-opentelemetry-collector.tracing.svc.cluster.local:4317"
|
||||
OTEL_SERVICE_NAME: "ljwx-bookstore"
|
||||
OTEL_TRACES_SAMPLER: "parentbased_traceidratio"
|
||||
OTEL_TRACES_SAMPLER_ARG: "0.1"
|
||||
OTEL_METRICS_EXPORTER: "prometheus"
|
||||
OTEL_LOGS_EXPORTER: "none"
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# Database Initialization Job
|
||||
# 在 infra namespace 的 MySQL 中创建 org_fiction 数据库
|
||||
# =============================================================================
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: ljwx-bookstore-db-init
|
||||
namespace: ljwx-bookstore
|
||||
labels:
|
||||
app: ljwx-bookstore
|
||||
component: db-init
|
||||
annotations:
|
||||
argocd.argoproj.io/hook: PreSync
|
||||
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
||||
argocd.argoproj.io/sync-wave: "-1"
|
||||
spec:
|
||||
ttlSecondsAfterFinished: 300
|
||||
backoffLimit: 3
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-bookstore
|
||||
component: db-init
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: db-init
|
||||
image: mysql:8.0
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
echo "=== Starting Database Initialization ==="
|
||||
echo "Connecting to MySQL at ${DB_HOST}:${DB_PORT}..."
|
||||
|
||||
# 等待 MySQL 就绪
|
||||
until mysql -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASSWORD}" -e "SELECT 1" &>/dev/null; do
|
||||
echo "MySQL not ready, waiting 2 seconds..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "MySQL is ready! Creating database if not exists..."
|
||||
|
||||
# 创建数据库
|
||||
mysql -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASSWORD}" <<EOF
|
||||
CREATE DATABASE IF NOT EXISTS ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%';
|
||||
FLUSH PRIVILEGES;
|
||||
SELECT 'Database ${DB_NAME} created or already exists' AS status;
|
||||
EOF
|
||||
|
||||
echo "=== Database Initialization Completed Successfully ==="
|
||||
|
||||
env:
|
||||
- name: DB_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: DB_HOST
|
||||
- name: DB_PORT
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: DB_PORT
|
||||
- name: DB_NAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: DB_NAME
|
||||
- name: DB_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: DB_USER
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: DB_PASSWORD
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
@@ -1,79 +1,210 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# ljwx-bookstore Deployment Configuration
|
||||
# Spring Boot 应用部署,连接 infra namespace 的 MySQL 和 Redis
|
||||
# =============================================================================
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: bookstore
|
||||
name: ljwx-bookstore
|
||||
namespace: ljwx-bookstore
|
||||
labels:
|
||||
app: bookstore
|
||||
managed-by: argocd
|
||||
project: ljwx-bookstore
|
||||
app: ljwx-bookstore
|
||||
component: backend
|
||||
version: v1.0.0
|
||||
spec:
|
||||
replicas: 2
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 0
|
||||
maxSurge: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: bookstore
|
||||
app: ljwx-bookstore
|
||||
component: backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: bookstore
|
||||
project: ljwx-bookstore
|
||||
version: v0.1.4
|
||||
app: ljwx-bookstore
|
||||
component: backend
|
||||
version: v1.0.0
|
||||
annotations:
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "8844"
|
||||
prometheus.io/path: "/fiction/actuator/prometheus"
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: ghcr-secret
|
||||
# 初始化容器 - 等待数据库就绪
|
||||
initContainers:
|
||||
- name: wait-for-mysql
|
||||
image: busybox:1.35
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
echo "Waiting for MySQL to be ready..."
|
||||
until nc -z mysql-infra.infra.svc.cluster.local 3306; do
|
||||
echo "MySQL not ready, sleeping for 2 seconds..."
|
||||
sleep 2
|
||||
done
|
||||
echo "MySQL is ready!"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
|
||||
# 主容器
|
||||
containers:
|
||||
- name: bookstore
|
||||
image: ghcr.io/brunogaosz/ljwx-bookstore/bookstore:bookstore-latest
|
||||
- name: ljwx-bookstore
|
||||
image: harbor.omniverseai.net/ljwx/ljwx-bookstore:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
- name: http
|
||||
containerPort: 8844
|
||||
protocol: TCP
|
||||
|
||||
# 环境变量 - 从 ConfigMap
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: ljwx-bookstore-config
|
||||
|
||||
# 环境变量 - 从 Secret(敏感信息)
|
||||
env:
|
||||
- name: SPRING_PROFILES_ACTIVE
|
||||
value: "prod"
|
||||
- name: SERVER_PORT
|
||||
value: "8080"
|
||||
# Database configuration from infra Postgres
|
||||
- name: SPRING_DATASOURCE_URL
|
||||
# 数据库配置
|
||||
- name: DB_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: bookstore-db-credentials
|
||||
key: SPRING_DATASOURCE_URL
|
||||
- name: SPRING_DATASOURCE_USERNAME
|
||||
name: ljwx-bookstore-secret
|
||||
key: DB_HOST
|
||||
- name: DB_PORT
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: bookstore-db-credentials
|
||||
key: SPRING_DATASOURCE_USERNAME
|
||||
- name: SPRING_DATASOURCE_PASSWORD
|
||||
name: ljwx-bookstore-secret
|
||||
key: DB_PORT
|
||||
- name: DB_NAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: bookstore-db-credentials
|
||||
key: SPRING_DATASOURCE_PASSWORD
|
||||
# Redis configuration
|
||||
- name: SPRING_DATA_REDIS_HOST
|
||||
value: "redis-service.ljwx-health.svc.cluster.local"
|
||||
- name: SPRING_DATA_REDIS_PORT
|
||||
value: "6379"
|
||||
- name: SPRING_DATA_REDIS_PASSWORD
|
||||
name: ljwx-bookstore-secret
|
||||
key: DB_NAME
|
||||
- name: DB_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: redis-credentials
|
||||
name: ljwx-bookstore-secret
|
||||
key: DB_USER
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: DB_PASSWORD
|
||||
|
||||
# Redis配置
|
||||
- name: REDIS_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: REDIS_HOST
|
||||
- name: REDIS_PORT
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: REDIS_PORT
|
||||
- name: REDIS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: REDIS_PASSWORD
|
||||
- name: SPRING_DATA_REDIS_DATABASE
|
||||
value: "0"
|
||||
|
||||
# 微信支付配置
|
||||
- name: WX_APP_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: WX_APP_SECRET
|
||||
- name: WX_MCH_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: WX_MCH_KEY
|
||||
|
||||
# 邮件配置
|
||||
- name: MAIL_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: MAIL_PASSWORD
|
||||
|
||||
# 短信配置
|
||||
- name: SMS_TENCENT_APPKEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: SMS_TENCENT_APPKEY
|
||||
|
||||
# 对象存储配置
|
||||
- name: STORAGE_ALIYUN_ACCESS_KEY_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: STORAGE_ALIYUN_ACCESS_KEY_ID
|
||||
- name: STORAGE_ALIYUN_ACCESS_KEY_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-bookstore-secret
|
||||
key: STORAGE_ALIYUN_ACCESS_KEY_SECRET
|
||||
|
||||
# 资源配置
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
|
||||
# 健康检查
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /fiction/actuator/health
|
||||
port: 8080
|
||||
port: 8844
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 120
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 10
|
||||
failureThreshold: 3
|
||||
successThreshold: 1
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /fiction/actuator/health
|
||||
port: 8080
|
||||
port: 8844
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "1000m"
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
successThreshold: 1
|
||||
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /fiction/actuator/health
|
||||
port: 8844
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 30
|
||||
successThreshold: 1
|
||||
|
||||
# 生命周期
|
||||
lifecycle:
|
||||
preStop:
|
||||
exec:
|
||||
command: ["/bin/sh", "-c", "sleep 15"]
|
||||
|
||||
# 重启策略
|
||||
restartPolicy: Always
|
||||
terminationGracePeriodSeconds: 60
|
||||
dnsPolicy: ClusterFirst
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: ghcr-secret
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
data:
|
||||
.dockerconfigjson: eyJhdXRocyI6eyJnaGNyLmlvIjp7InVzZXJuYW1lIjoiQnJ1bm9HYW9TWiIsInBhc3N3b3JkIjoiZ2hwX05UVVNwTW9kOURhcXAxeGxCTWliOGxnV09WMUJidTF5MjJ4cyIsImVtYWlsIjoieW91ci1lbWFpbEBleGFtcGxlLmNvbSIsImF1dGgiOiJRbkoxYm05SFlXOVRXanBuYUhCZlRsUlZVM0JOYjJRNVJHRnhjREY0YkVKTmFXSTRiR2RYVDFZeFFtSjFNWGt5TW5oeiJ9fX0=
|
||||
@@ -1,41 +1,57 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# ljwx-bookstore Ingress Configuration
|
||||
# 域名: book.omniverseai.net (HTTP only)
|
||||
# =============================================================================
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: bookstore-ingress
|
||||
namespace: bookstore
|
||||
name: ljwx-bookstore
|
||||
namespace: ljwx-bookstore
|
||||
labels:
|
||||
app: ljwx-bookstore
|
||||
component: backend
|
||||
annotations:
|
||||
# Traefik annotations
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
|
||||
traefik.ingress.kubernetes.io/redirect-to-https: "true"
|
||||
# Redirect root path to /fiction/index
|
||||
traefik.ingress.kubernetes.io/router.middlewares: bookstore-redirect-root@kubernetescrd
|
||||
# cert-manager annotation for automatic TLS certificate
|
||||
cert-manager.io/cluster-issuer: dnspod-letsencrypt
|
||||
# Nginx Ingress 配置
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
|
||||
|
||||
# 禁用SSL重定向 - HTTP only
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
||||
|
||||
# 健康检查
|
||||
nginx.ingress.kubernetes.io/health-check-path: "/fiction/actuator/health"
|
||||
|
||||
# CORS 配置(如果需要)
|
||||
nginx.ingress.kubernetes.io/enable-cors: "true"
|
||||
nginx.ingress.kubernetes.io/cors-allow-origin: "http://book.omniverseai.net"
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
- bookstore.lingjingwanxiang.cn
|
||||
secretName: bookstore-lingjingwanxiang-tls
|
||||
ingressClassName: nginx
|
||||
|
||||
# 路由规则
|
||||
rules:
|
||||
- host: bookstore.lingjingwanxiang.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: bookstore
|
||||
port:
|
||||
number: 8080
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: redirect-root
|
||||
namespace: bookstore
|
||||
spec:
|
||||
redirectRegex:
|
||||
regex: "^https?://[^/]+/$"
|
||||
replacement: "https://bookstore.lingjingwanxiang.cn/fiction/index"
|
||||
permanent: false
|
||||
# 生产域名
|
||||
- host: book.omniverseai.net
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ljwx-bookstore
|
||||
port:
|
||||
number: 80
|
||||
|
||||
# 本地测试域名
|
||||
- host: bookstore.192.168.1.83.nip.io
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ljwx-bookstore
|
||||
port:
|
||||
number: 80
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# Kustomize Base Configuration for ljwx-bookstore
|
||||
# =============================================================================
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: ljwx-bookstore
|
||||
|
||||
resources:
|
||||
- namespace.yaml
|
||||
# Secret 由 infra 团队手动创建,不在 Git 中管理
|
||||
# TODO: 后续迁移到 External Secrets Operator 实现完全 GitOps
|
||||
- configmap.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress.yaml
|
||||
- ghcr-secret.yaml
|
||||
- redis-secret.yaml
|
||||
- db-init-job.yaml
|
||||
|
||||
commonLabels:
|
||||
app: ljwx-bookstore
|
||||
managed-by: kustomize
|
||||
|
||||
# 镜像替换(将由 CD 流程动态更新)
|
||||
images:
|
||||
- name: harbor.omniverseai.net/ljwx/ljwx-bookstore
|
||||
newTag: latest
|
||||
|
||||
# 配置生成器行为
|
||||
generatorOptions:
|
||||
disableNameSuffixHash: true
|
||||
|
||||
# 构建元数据
|
||||
buildMetadata: [originAnnotations, transformerAnnotations]
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# ljwx-bookstore Namespace
|
||||
# =============================================================================
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: ljwx-bookstore
|
||||
labels:
|
||||
name: ljwx-bookstore
|
||||
project: ljwx
|
||||
environment: production
|
||||
managed-by: argocd
|
||||
@@ -1,7 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: redis-credentials
|
||||
type: Opaque
|
||||
stringData:
|
||||
REDIS_PASSWORD: "123456"
|
||||
@@ -1,12 +1,23 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# ljwx-bookstore Service Configuration
|
||||
# =============================================================================
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: bookstore
|
||||
name: ljwx-bookstore
|
||||
namespace: ljwx-bookstore
|
||||
labels:
|
||||
app: ljwx-bookstore
|
||||
component: backend
|
||||
spec:
|
||||
selector:
|
||||
app: bookstore
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: ljwx-bookstore
|
||||
component: backend
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 8844
|
||||
protocol: TCP
|
||||
sessionAffinity: None
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# ljwx-bookstore 通用配置(跨环境共享)
|
||||
|
||||
# 通用标签
|
||||
commonLabels:
|
||||
project: ljwx-bookstore
|
||||
managed-by: argocd
|
||||
app: bookstore
|
||||
|
||||
# 通用注解
|
||||
commonAnnotations:
|
||||
maintained-by: ljwx-team
|
||||
@@ -1,7 +0,0 @@
|
||||
resources:
|
||||
- ../../base
|
||||
namespace: bookstore
|
||||
images:
|
||||
- name: ghcr.io/brunogaosz/ljwx-bookstore/bookstore
|
||||
newName: ghcr.io/brunogaosz/ljwx-bookstore/bookstore
|
||||
newTag: "bookstore-d917045"
|
||||
@@ -1,76 +0,0 @@
|
||||
# Bookstore 环境配置
|
||||
|
||||
# 副本数
|
||||
replicaCount: 2
|
||||
|
||||
# 镜像配置
|
||||
image:
|
||||
repository: ghcr.io/brunogaosz/ljwx-bookstore/bookstore
|
||||
tag: "main-latest" # CI 会更新为 Git SHA
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
# 服务配置
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
|
||||
# Ingress 配置
|
||||
ingress:
|
||||
enabled: true
|
||||
className: "nginx"
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
|
||||
hosts:
|
||||
- host: bookstore.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: bookstore-tls
|
||||
hosts:
|
||||
- bookstore.example.com
|
||||
|
||||
# 资源限制
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "1000m"
|
||||
|
||||
# 健康检查
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health
|
||||
port: 8080
|
||||
initialDelaySeconds: 120
|
||||
periodSeconds: 30
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health/readiness
|
||||
port: 8080
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 10
|
||||
|
||||
# 数据库配置(使用 infra postgres)
|
||||
postgresql:
|
||||
enabled: false # 使用外部数据库
|
||||
|
||||
# Redis 配置
|
||||
redis:
|
||||
enabled: false # 使用外部 Redis
|
||||
|
||||
# OpenSearch 配置
|
||||
opensearch:
|
||||
enabled: false # 使用外部 OpenSearch
|
||||
|
||||
# 环境变量(通过 Secret 注入)
|
||||
env:
|
||||
SPRING_PROFILES_ACTIVE: "prod"
|
||||
SERVER_PORT: "8080"
|
||||
# 数据库连接将通过 Secret 注入
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: ljwx-bookstore
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
images:
|
||||
- name: harbor.omniverseai.net/ljwx/ljwx-bookstore
|
||||
newTag: "main-9e937b1"
|
||||
|
||||
replicas:
|
||||
- name: ljwx-bookstore
|
||||
count: 1
|
||||
@@ -1,530 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ljwx-health-device-approval-config
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-boot
|
||||
component: device-approval
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
data:
|
||||
DEVICE_APPROVAL_AUTO_CLEANUP_DAYS: '30'
|
||||
DEVICE_APPROVAL_BATCH_SIZE_LIMIT: '100'
|
||||
DEVICE_APPROVAL_TIMEOUT_HOURS: '72'
|
||||
DEVICE_APPROVAL_EVENT_THREAD_POOL_SIZE: '10'
|
||||
DEVICE_APPROVAL_EVENT_QUEUE_CAPACITY: '100'
|
||||
DEVICE_APPROVAL_METRICS_ENABLED: 'true'
|
||||
DEVICE_APPROVAL_LOG_LEVEL: INFO
|
||||
DEVICE_APPROVAL_EVENT_LISTENER_LOG_LEVEL: INFO
|
||||
DEVICE_APPROVAL_METRICS_SERVICE_LOG_LEVEL: DEBUG
|
||||
DEVICE_APPROVAL_QUERY_CACHE_TTL: '300'
|
||||
DEVICE_APPROVAL_HISTORY_QUERY_OPTIMIZATION: 'true'
|
||||
application-device-approval.yml: "# Device V2.1 - \u8BBE\u5907\u7ED1\u5B9A\u5BA1\
|
||||
\u6279\u6A21\u5757\u4E13\u7528\u914D\u7F6E\n# \u81EA\u52A8\u52A0\u8F7D\u5F53 spring.profiles.include=device-approval\
|
||||
\ \u65F6\u751F\u6548\n\ndevice:\n approval:\n # \u5BA1\u6279\u6D41\u7A0B\u914D\
|
||||
\u7F6E\n auto-cleanup-days: ${DEVICE_APPROVAL_AUTO_CLEANUP_DAYS:30}\n batch-size-limit:\
|
||||
\ ${DEVICE_APPROVAL_BATCH_SIZE_LIMIT:100}\n timeout-hours: ${DEVICE_APPROVAL_TIMEOUT_HOURS:72}\n\
|
||||
\n # \u4E8B\u4EF6\u53D1\u5E03\u914D\u7F6E\n events:\n async-thread-pool-size:\
|
||||
\ ${DEVICE_APPROVAL_EVENT_THREAD_POOL_SIZE:10}\n async-queue-capacity: ${DEVICE_APPROVAL_EVENT_QUEUE_CAPACITY:100}\n\
|
||||
\n # \u53EF\u89C2\u6D4B\u6027\u914D\u7F6E\n observability:\n metrics-enabled:\
|
||||
\ ${DEVICE_APPROVAL_METRICS_ENABLED:true}\n log-level: ${DEVICE_APPROVAL_LOG_LEVEL:INFO}\n\
|
||||
\ event-listener-log-level: ${DEVICE_APPROVAL_EVENT_LISTENER_LOG_LEVEL:INFO}\n\
|
||||
\ metrics-service-log-level: ${DEVICE_APPROVAL_METRICS_SERVICE_LOG_LEVEL:DEBUG}\n\
|
||||
\n # \u6027\u80FD\u4F18\u5316\u914D\u7F6E\n performance:\n query-cache-ttl:\
|
||||
\ ${DEVICE_APPROVAL_QUERY_CACHE_TTL:300}\n history-query-optimization: ${DEVICE_APPROVAL_HISTORY_QUERY_OPTIMIZATION:true}\n\
|
||||
\n# Logback \u65E5\u5FD7\u914D\u7F6E (\u8986\u76D6\u9ED8\u8BA4\u914D\u7F6E)\n\
|
||||
logging:\n level:\n com.ljwx.modules.health.service.DeviceBindApprovalServiceImpl:\
|
||||
\ ${DEVICE_APPROVAL_LOG_LEVEL:INFO}\n com.ljwx.modules.health.listener.DeviceBindApprovalEventListener:\
|
||||
\ ${DEVICE_APPROVAL_EVENT_LISTENER_LOG_LEVEL:INFO}\n com.ljwx.modules.health.service.DeviceBindApprovalMetricsService:\
|
||||
\ ${DEVICE_APPROVAL_METRICS_SERVICE_LOG_LEVEL:DEBUG}\n"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ljwx-health-admin
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-admin
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: ljwx-admin
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ljwx-health-bigscreen
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-bigscreen
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 5225
|
||||
targetPort: 5225
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: ljwx-bigscreen
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ljwx-health-boot
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-boot
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 9998
|
||||
targetPort: 9998
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: ljwx-boot
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ljwx-health-admin
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-admin
|
||||
component: frontend
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-admin
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-admin
|
||||
component: frontend
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: harbor-secret
|
||||
containers:
|
||||
- name: ljwx-admin
|
||||
image: harbor.eu.lingjingwanxiang.cn/ljwx-health/ljwx-admin:dev-latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
restartPolicy: Always
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ljwx-health-bigscreen
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-bigscreen
|
||||
component: dashboard
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-bigscreen
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-bigscreen
|
||||
component: dashboard
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: harbor-secret
|
||||
containers:
|
||||
- name: ljwx-bigscreen
|
||||
image: harbor.eu.lingjingwanxiang.cn/ljwx-health/ljwx-bigscreen:dev-latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 5225
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: ENV
|
||||
value: production
|
||||
- name: DB_HOST
|
||||
value: mysql-lb.infra.svc.cluster.local
|
||||
- name: DB_PORT
|
||||
value: '3306'
|
||||
- name: DB_NAME
|
||||
value: ljwx_health
|
||||
- name: REDIS_HOST
|
||||
value: redis-lb.infra.svc.cluster.local
|
||||
- name: REDIS_PORT
|
||||
value: '6379'
|
||||
- name: LOG_LEVEL
|
||||
value: INFO
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: ljwx-boot-secrets
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 5225
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 5225
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
restartPolicy: Always
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ljwx-health-boot
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-boot
|
||||
component: backend
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-boot
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
prometheus.io/scrape: 'true'
|
||||
prometheus.io/port: '9998'
|
||||
prometheus.io/path: /actuator/prometheus
|
||||
labels:
|
||||
app: ljwx-boot
|
||||
component: backend
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: harbor-secret
|
||||
containers:
|
||||
- name: ljwx-boot
|
||||
image: harbor.eu.lingjingwanxiang.cn/ljwx-health/ljwx-boot:dev-latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 9998
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: SPRING_PROFILES_ACTIVE
|
||||
value: prod
|
||||
- name: SPRING_DATASOURCE_URL
|
||||
value: jdbc:mysql://mysql.infra.svc.cluster.local:3306/ljwx_health?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
- name: SPRING_DATASOURCE_DRIVER_CLASS_NAME
|
||||
value: com.mysql.cj.jdbc.Driver
|
||||
- name: SPRING_REDIS_HOST
|
||||
value: redis-lb.infra.svc.cluster.local
|
||||
- name: SPRING_REDIS_PORT
|
||||
value: '6379'
|
||||
- name: SPRING_REDIS_DATABASE
|
||||
value: '1'
|
||||
- name: SPRING_REDIS_PASSWORD
|
||||
value: '123456'
|
||||
- name: REDIS_HOST
|
||||
value: redis-lb.infra.svc.cluster.local
|
||||
- name: REDIS_PORT
|
||||
value: '6379'
|
||||
- name: REDIS_DB
|
||||
value: '1'
|
||||
- name: REDIS_PASSWORD
|
||||
value: '123456'
|
||||
- name: LOG_LEVEL
|
||||
value: INFO
|
||||
- name: SPRING_FLYWAY_ENABLED
|
||||
value: 'false'
|
||||
- name: SPRING_FLYWAY_BASELINE_ON_MIGRATE
|
||||
value: 'true'
|
||||
- name: SPRING_FLYWAY_LOCATIONS
|
||||
value: classpath:db/baseline
|
||||
- name: OTEL_SERVICE_NAME
|
||||
value: ljwx-boot
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: http://tempo.infra.svc.cluster.local:4317
|
||||
- name: OTEL_TRACES_SAMPLER
|
||||
value: parentbased_traceidratio
|
||||
- name: OTEL_TRACES_SAMPLER_ARG
|
||||
value: '0.1'
|
||||
- name: SPRING_PROFILES_INCLUDE
|
||||
value: device-approval
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: ljwx-boot-secrets
|
||||
- configMapRef:
|
||||
name: ljwx-health-device-approval-config
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /actuator/health/liveness
|
||||
port: 9998
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
readinessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /actuator/health/readiness
|
||||
port: 9998
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
resources:
|
||||
limits:
|
||||
cpu: 2000m
|
||||
memory: 2Gi
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
volumeMounts:
|
||||
- name: logs
|
||||
mountPath: /app/logs
|
||||
- name: device-approval-config
|
||||
mountPath: /app/config/application-device-approval.yml
|
||||
subPath: application-device-approval.yml
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: logs
|
||||
emptyDir: {}
|
||||
- name: device-approval-config
|
||||
configMap:
|
||||
name: ljwx-health-device-approval-config
|
||||
items:
|
||||
- key: application-device-approval.yml
|
||||
path: application-device-approval.yml
|
||||
restartPolicy: Always
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ljwx-health-api-ingress
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: 'true'
|
||||
traefik.ingress.kubernetes.io/router.middlewares: ljwx-health-ljwx-health-strip-api-prefix@kubernetescrd
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
- health-admin.lingjingwanxiang.cn
|
||||
secretName: ljwx-health-admin-tls
|
||||
rules:
|
||||
- host: health-admin.lingjingwanxiang.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /api
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ljwx-health-boot
|
||||
port:
|
||||
number: 9998
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ljwx-health-ingress
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: 'true'
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: 'true'
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
- health-admin.lingjingwanxiang.cn
|
||||
secretName: ljwx-health-admin-tls
|
||||
- hosts:
|
||||
- health-dashboard.lingjingwanxiang.cn
|
||||
secretName: ljwx-health-bigscreen-tls
|
||||
rules:
|
||||
- host: health-admin.lingjingwanxiang.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ljwx-health-admin
|
||||
port:
|
||||
number: 80
|
||||
- host: health-dashboard.lingjingwanxiang.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ljwx-health-bigscreen
|
||||
port:
|
||||
number: 5225
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: ljwx-health-strip-api-prefix
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
stripPrefix:
|
||||
prefixes:
|
||||
- /api
|
||||
---
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: ljwx-health-boot
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-boot
|
||||
release: monitoring
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-boot
|
||||
endpoints:
|
||||
- port: http
|
||||
path: /actuator/prometheus
|
||||
interval: 30s
|
||||
scrapeTimeout: 10s
|
||||
relabelings:
|
||||
- sourceLabels:
|
||||
- __meta_kubernetes_service_name
|
||||
targetLabel: service
|
||||
- sourceLabels:
|
||||
- __meta_kubernetes_namespace
|
||||
targetLabel: namespace
|
||||
- sourceLabels:
|
||||
- __meta_kubernetes_pod_name
|
||||
targetLabel: pod
|
||||
---
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: ljwx-health-bigscreen
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-bigscreen
|
||||
release: monitoring
|
||||
helm.sh/chart: ljwx-health-0.1.0
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/instance: ljwx-health
|
||||
app.kubernetes.io/version: 1.0.6
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-bigscreen
|
||||
endpoints:
|
||||
- port: http
|
||||
path: /metrics
|
||||
interval: 30s
|
||||
scrapeTimeout: 10s
|
||||
relabelings:
|
||||
- sourceLabels:
|
||||
- __meta_kubernetes_service_name
|
||||
targetLabel: service
|
||||
- sourceLabels:
|
||||
- __meta_kubernetes_namespace
|
||||
targetLabel: namespace
|
||||
- sourceLabels:
|
||||
- __meta_kubernetes_pod_name
|
||||
targetLabel: pod
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# ljwx-health Common ConfigMap
|
||||
# 公共配置(非敏感信息)
|
||||
# =============================================================================
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ljwx-health-config
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-health
|
||||
data:
|
||||
# Spring 配置
|
||||
SPRING_PROFILES_ACTIVE: "k8s"
|
||||
|
||||
# 数据库配置(连接 infra namespace)
|
||||
DB_HOST: "mysql-infra.infra.svc.cluster.local"
|
||||
DB_PORT: "3306"
|
||||
DB_NAME: "ljwx_health"
|
||||
DB_USER: "root"
|
||||
|
||||
# Spring DataSource 配置
|
||||
SPRING_DATASOURCE_URL: "jdbc:mysql://mysql-infra.infra.svc.cluster.local:3306/ljwx_health?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"
|
||||
SPRING_DATASOURCE_USERNAME: "root"
|
||||
SPRING_DATASOURCE_DRIVER_CLASS_NAME: "com.mysql.cj.jdbc.Driver"
|
||||
|
||||
# Redis 配置(连接 infra namespace)
|
||||
REDIS_HOST: "redis-infra-master.infra.svc.cluster.local"
|
||||
REDIS_PORT: "6379"
|
||||
REDIS_DB: "1"
|
||||
|
||||
# Spring Redis 配置
|
||||
SPRING_REDIS_HOST: "redis-infra-master.infra.svc.cluster.local"
|
||||
SPRING_REDIS_PORT: "6379"
|
||||
SPRING_REDIS_DATABASE: "1"
|
||||
|
||||
# 日志配置
|
||||
LOG_LEVEL: "info"
|
||||
|
||||
# OSS 配置
|
||||
OSS_BUCKET_NAME: "ljwx-health"
|
||||
OSS_ENDPOINT: "http://minio.infra.svc.cluster.local:9000"
|
||||
|
||||
# Ollama 配置
|
||||
OLLAMA_API_URL: "http://ollama.infra.svc.cluster.local:11434"
|
||||
OLLAMA_MODEL_NAME: "ljwx-health-enhanced:latest"
|
||||
|
||||
# LJWX 配置
|
||||
LJWX_LOGO_UPLOAD_PATH: "/app/uploads"
|
||||
LJWX_LICENSE_ENABLED: "false"
|
||||
LJWX_LICENSE_TRIAL_MODE: "false"
|
||||
|
||||
# 时区配置
|
||||
TZ: "Asia/Shanghai"
|
||||
|
||||
# Flyway 数据库迁移配置
|
||||
# ✅ 启用 Flyway - 使用新基线 V1.0.0(2026-01-03)
|
||||
SPRING_FLYWAY_ENABLED: "true"
|
||||
SPRING_FLYWAY_BASELINE_ON_MIGRATE: "true" # 已有数据库从 baseline 开始
|
||||
SPRING_FLYWAY_BASELINE_VERSION: "0"
|
||||
SPRING_FLYWAY_LOCATIONS: "classpath:db/migration"
|
||||
SPRING_FLYWAY_VALIDATE_ON_MIGRATE: "true"
|
||||
SPRING_FLYWAY_CLEAN_DISABLED: "true" # 生产环境禁用 clean
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Script: create-secret.sh
|
||||
# Description: 创建 ljwx-health-secret Secret
|
||||
# Usage: ./create-secret.sh
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 默认值
|
||||
NAMESPACE="ljwx-health"
|
||||
SECRET_NAME="ljwx-health-secret"
|
||||
|
||||
# 从环境变量或提示用户输入
|
||||
read_secret() {
|
||||
local var_name=$1
|
||||
local prompt=$2
|
||||
local default_value=$3
|
||||
|
||||
if [ -n "${!var_name}" ]; then
|
||||
echo "${!var_name}"
|
||||
elif [ -n "$default_value" ]; then
|
||||
read -p "$prompt [$default_value]: " value
|
||||
echo "${value:-$default_value}"
|
||||
else
|
||||
read -p "$prompt: " value
|
||||
echo "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
echo -e "${GREEN}=== LJWX Health Secret 创建工具 ===${NC}\n"
|
||||
|
||||
# 数据库配置
|
||||
echo -e "${YELLOW}数据库配置(连接 infra namespace MySQL)${NC}"
|
||||
DB_PASSWORD=$(read_secret "DB_PASSWORD" "数据库密码" "123456")
|
||||
|
||||
# Redis 配置
|
||||
echo -e "\n${YELLOW}Redis 配置(连接 infra namespace Redis)${NC}"
|
||||
REDIS_PASSWORD=$(read_secret "REDIS_PASSWORD" "Redis 密码" "123456")
|
||||
|
||||
# OSS 配置
|
||||
echo -e "\n${YELLOW}OSS 配置(MinIO)${NC}"
|
||||
OSS_ACCESS_KEY=$(read_secret "OSS_ACCESS_KEY" "OSS Access Key" "minioadmin")
|
||||
OSS_SECRET_KEY=$(read_secret "OSS_SECRET_KEY" "OSS Secret Key" "minioadmin")
|
||||
|
||||
# 创建 namespace(如果不存在)
|
||||
echo -e "\n${GREEN}创建 namespace: $NAMESPACE${NC}"
|
||||
kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# 删除旧 Secret(如果存在)
|
||||
echo -e "${GREEN}删除旧 Secret(如果存在)${NC}"
|
||||
kubectl delete secret $SECRET_NAME -n $NAMESPACE --ignore-not-found=true
|
||||
|
||||
# 创建新 Secret
|
||||
echo -e "${GREEN}创建 Secret: $SECRET_NAME${NC}"
|
||||
kubectl create secret generic $SECRET_NAME \
|
||||
-n $NAMESPACE \
|
||||
--from-literal=DB_PASSWORD="$DB_PASSWORD" \
|
||||
--from-literal=REDIS_PASSWORD="$REDIS_PASSWORD" \
|
||||
--from-literal=OSS_ACCESS_KEY="$OSS_ACCESS_KEY" \
|
||||
--from-literal=OSS_SECRET_KEY="$OSS_SECRET_KEY"
|
||||
|
||||
# 验证
|
||||
echo -e "\n${GREEN}验证 Secret:${NC}"
|
||||
kubectl get secret $SECRET_NAME -n $NAMESPACE
|
||||
|
||||
echo -e "\n${GREEN}✅ Secret 创建成功!${NC}"
|
||||
echo -e "\n${YELLOW}提示:${NC}"
|
||||
echo " 1. Secret 包含敏感信息,请妥善保管"
|
||||
echo " 2. 数据库连接地址: mysql-infra.infra.svc.cluster.local:3306"
|
||||
echo " 3. Redis 连接地址: redis-infra.infra.svc.cluster.local:6379"
|
||||
echo " 4. Argo CD 应用将通过 GitOps 自动部署"
|
||||
echo " 5. 访问地址: http://health-admin.omniverseai.net (HTTP)"
|
||||
echo " 6. 大屏地址: http://health-dashboard.omniverseai.net (HTTP)"
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# ljwx-admin Deployment Configuration
|
||||
# Vue3 前端管理后台(Nginx 静态服务)
|
||||
# =============================================================================
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ljwx-admin
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-admin
|
||||
component: frontend
|
||||
version: v1.0.0
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 0
|
||||
maxSurge: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-admin
|
||||
component: frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-admin
|
||||
component: frontend
|
||||
version: v1.0.0
|
||||
annotations:
|
||||
kubectl.kubernetes.io/restartedAt: "20260103-072537"
|
||||
spec:
|
||||
# Harbor 镜像拉取凭证
|
||||
imagePullSecrets:
|
||||
- name: harbor-registry
|
||||
|
||||
containers:
|
||||
- name: ljwx-admin
|
||||
image: harbor.omniverseai.net/ljwx-health/ljwx-admin:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
|
||||
# 环境变量
|
||||
env:
|
||||
- name: TZ
|
||||
value: "Asia/Shanghai"
|
||||
# 后端 API 地址(通过 Ingress 访问)
|
||||
- name: VITE_API_URL
|
||||
value: "https://health-admin.omniverseai.net/api"
|
||||
|
||||
# 资源配置
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
|
||||
# 健康检查
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
successThreshold: 1
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
successThreshold: 1
|
||||
|
||||
# 重启策略
|
||||
restartPolicy: Always
|
||||
terminationGracePeriodSeconds: 30
|
||||
dnsPolicy: ClusterFirst
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# ljwx-bigscreen Deployment Configuration
|
||||
# Flask 大屏监控服务
|
||||
# =============================================================================
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ljwx-bigscreen
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-bigscreen
|
||||
component: dashboard
|
||||
version: v1.0.0
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 0
|
||||
maxSurge: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-bigscreen
|
||||
component: dashboard
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-bigscreen
|
||||
component: dashboard
|
||||
version: v1.0.0
|
||||
annotations:
|
||||
kubectl.kubernetes.io/restartedAt: "20260103-072537"
|
||||
spec:
|
||||
# Harbor 镜像拉取凭证
|
||||
imagePullSecrets:
|
||||
- name: harbor-registry
|
||||
|
||||
# 初始化容器 - 等待数据库就绪
|
||||
initContainers:
|
||||
- name: wait-for-mysql
|
||||
image: busybox:1.35
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
echo "Waiting for MySQL to be ready..."
|
||||
until nc -z mysql-infra.infra.svc.cluster.local 3306; do
|
||||
echo "MySQL not ready, sleeping for 2 seconds..."
|
||||
sleep 2
|
||||
done
|
||||
echo "MySQL is ready!"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
|
||||
# 主容器
|
||||
containers:
|
||||
- name: ljwx-bigscreen
|
||||
image: harbor.omniverseai.net/ljwx-health/ljwx-bigscreen:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 5225
|
||||
protocol: TCP
|
||||
|
||||
# 环境变量 - 从 ConfigMap
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: ljwx-health-config
|
||||
|
||||
# 环境变量 - 从 Secret(敏感信息)
|
||||
env:
|
||||
# 数据库配置
|
||||
- name: MYSQL_HOST
|
||||
value: "mysql-infra.infra.svc.cluster.local"
|
||||
- name: MYSQL_PORT
|
||||
value: "3306"
|
||||
- name: MYSQL_DATABASE
|
||||
value: "ljwx_health"
|
||||
- name: MYSQL_USER
|
||||
value: "root"
|
||||
- name: MYSQL_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-health-secret
|
||||
key: DB_PASSWORD
|
||||
|
||||
# Redis 配置
|
||||
- name: REDIS_HOST
|
||||
value: "redis-infra-master.infra.svc.cluster.local"
|
||||
- name: REDIS_PORT
|
||||
value: "6379"
|
||||
- name: REDIS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-health-secret
|
||||
key: REDIS_PASSWORD
|
||||
|
||||
# Flask 配置
|
||||
- name: FLASK_ENV
|
||||
value: "production"
|
||||
- name: FLASK_APP
|
||||
value: "run.py"
|
||||
|
||||
# 资源配置
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
|
||||
# 健康检查
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 5225
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 10
|
||||
failureThreshold: 3
|
||||
successThreshold: 1
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 5225
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
successThreshold: 1
|
||||
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 5225
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 20
|
||||
successThreshold: 1
|
||||
|
||||
# 生命周期
|
||||
lifecycle:
|
||||
preStop:
|
||||
exec:
|
||||
command: ["/bin/sh", "-c", "sleep 10"]
|
||||
|
||||
# 重启策略
|
||||
restartPolicy: Always
|
||||
terminationGracePeriodSeconds: 30
|
||||
dnsPolicy: ClusterFirst
|
||||
@@ -0,0 +1,173 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# ljwx-boot Deployment Configuration
|
||||
# Spring Boot 后端服务,连接 infra namespace 的 MySQL 和 Redis
|
||||
# =============================================================================
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ljwx-boot
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-boot
|
||||
component: backend
|
||||
version: v1.0.0
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 0
|
||||
maxSurge: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-boot
|
||||
component: backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-boot
|
||||
component: backend
|
||||
version: v1.0.0
|
||||
annotations:
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "9998"
|
||||
prometheus.io/path: "/actuator/prometheus"
|
||||
kubectl.kubernetes.io/restartedAt: "20260103-080347"
|
||||
spec:
|
||||
# Harbor 镜像拉取凭证
|
||||
imagePullSecrets:
|
||||
- name: harbor-registry
|
||||
|
||||
# 初始化容器 - 等待数据库就绪
|
||||
initContainers:
|
||||
- name: wait-for-mysql
|
||||
image: busybox:1.35
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
echo "Waiting for MySQL to be ready..."
|
||||
until nc -z mysql-infra.infra.svc.cluster.local 3306; do
|
||||
echo "MySQL not ready, sleeping for 2 seconds..."
|
||||
sleep 2
|
||||
done
|
||||
echo "MySQL is ready!"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
|
||||
# 主容器
|
||||
containers:
|
||||
- name: ljwx-boot
|
||||
image: harbor.omniverseai.net/ljwx-health/ljwx-boot:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 9998
|
||||
protocol: TCP
|
||||
|
||||
# 环境变量 - 从 ConfigMap
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: ljwx-health-config
|
||||
|
||||
# 环境变量 - 从 Secret(敏感信息)
|
||||
env:
|
||||
# 数据库密码(旧格式)
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-health-secret
|
||||
key: DB_PASSWORD
|
||||
|
||||
# Spring 数据库密码(新格式)
|
||||
- name: SPRING_DATASOURCE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-health-secret
|
||||
key: DB_PASSWORD
|
||||
|
||||
# Redis 密码(旧格式)
|
||||
- name: REDIS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-health-secret
|
||||
key: REDIS_PASSWORD
|
||||
|
||||
# Spring Redis 密码(新格式)
|
||||
- name: SPRING_REDIS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-health-secret
|
||||
key: REDIS_PASSWORD
|
||||
|
||||
# OSS 密钥
|
||||
- name: OSS_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-health-secret
|
||||
key: OSS_ACCESS_KEY
|
||||
- name: OSS_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-health-secret
|
||||
key: OSS_SECRET_KEY
|
||||
|
||||
# 资源配置
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: 2000m
|
||||
memory: 2Gi
|
||||
|
||||
# 健康检查
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health/liveness
|
||||
port: 9998
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 120
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 10
|
||||
failureThreshold: 3
|
||||
successThreshold: 1
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health/readiness
|
||||
port: 9998
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
successThreshold: 1
|
||||
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /actuator/health
|
||||
port: 9998
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 30
|
||||
successThreshold: 1
|
||||
|
||||
# 生命周期
|
||||
lifecycle:
|
||||
preStop:
|
||||
exec:
|
||||
command: ["/bin/sh", "-c", "sleep 15"]
|
||||
|
||||
# 重启策略
|
||||
restartPolicy: Always
|
||||
terminationGracePeriodSeconds: 60
|
||||
dnsPolicy: ClusterFirst
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# ljwx-health Ingress Configuration
|
||||
# 路由配置:
|
||||
# - health-admin.omniverseai.net -> ljwx-admin (Vue3 前端)
|
||||
# - health-dashboard.omniverseai.net -> ljwx-bigscreen (Flask 大屏)
|
||||
# - health-admin.omniverseai.net/api -> ljwx-boot (后端 API)
|
||||
# =============================================================================
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ljwx-health-ingress
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-health
|
||||
annotations:
|
||||
# CORS 配置
|
||||
nginx.ingress.kubernetes.io/enable-cors: "true"
|
||||
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
|
||||
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
|
||||
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization"
|
||||
# 请求大小限制
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
|
||||
# 超时配置
|
||||
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
# =========================================================================
|
||||
# Rule 1: health-admin.omniverseai.net
|
||||
# =========================================================================
|
||||
- host: health-admin.omniverseai.net
|
||||
http:
|
||||
paths:
|
||||
# 后端 API 路由
|
||||
- path: /api
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ljwx-boot
|
||||
port:
|
||||
number: 9998
|
||||
# 前端静态资源
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ljwx-admin
|
||||
port:
|
||||
number: 80
|
||||
|
||||
# =========================================================================
|
||||
# Rule 2: health-dashboard.omniverseai.net
|
||||
# =========================================================================
|
||||
- host: health-dashboard.omniverseai.net
|
||||
http:
|
||||
paths:
|
||||
# 大屏应用
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ljwx-bigscreen
|
||||
port:
|
||||
number: 5001
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# Flyway Repair Job
|
||||
# 修复失败的迁移记录,清理 flyway_schema_history 表
|
||||
# =============================================================================
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: ljwx-health-flyway-repair
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-health
|
||||
component: flyway-repair
|
||||
annotations:
|
||||
argocd.argoproj.io/hook: PreSync
|
||||
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
||||
argocd.argoproj.io/sync-wave: "1" # 在 init-db (wave 0) 之后运行
|
||||
spec:
|
||||
ttlSecondsAfterFinished: 300
|
||||
backoffLimit: 3
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-health
|
||||
component: flyway-repair
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: flyway-repair
|
||||
image: mysql:8.0
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
echo "检查并修复 Flyway schema history..."
|
||||
|
||||
# 删除所有失败的迁移记录
|
||||
mysql -h mysql-infra.infra.svc.cluster.local -u root -p${MYSQL_ROOT_PASSWORD} ljwx_health <<EOF
|
||||
|
||||
-- 检查 flyway_schema_history 表是否存在
|
||||
SELECT CASE WHEN COUNT(*) > 0 THEN 'Flyway history table exists'
|
||||
ELSE 'No flyway history table found' END AS status
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'ljwx_health'
|
||||
AND table_name = 'flyway_schema_history';
|
||||
|
||||
-- 显示所有迁移记录
|
||||
SELECT CONCAT('Total migrations before cleanup: ', COUNT(*)) AS count FROM flyway_schema_history;
|
||||
|
||||
-- 完全删除 flyway_schema_history 表,让 Flyway 重新开始
|
||||
DROP TABLE IF EXISTS flyway_schema_history;
|
||||
|
||||
SELECT 'Flyway schema history table dropped. Flyway will recreate it on next run.' AS result;
|
||||
|
||||
EOF
|
||||
|
||||
echo "✅ Flyway schema history 修复完成"
|
||||
env:
|
||||
- name: MYSQL_ROOT_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-health-secret
|
||||
key: DB_PASSWORD
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# Database Initialization Job
|
||||
# 在 ljwx-boot 启动前创建数据库,让 Flyway 可以运行迁移
|
||||
# =============================================================================
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: ljwx-health-init-db
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-health
|
||||
component: init-db
|
||||
annotations:
|
||||
argocd.argoproj.io/hook: PreSync
|
||||
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
||||
argocd.argoproj.io/sync-wave: "0" # 首先运行
|
||||
spec:
|
||||
ttlSecondsAfterFinished: 300 # 5分钟后自动清理
|
||||
backoffLimit: 3
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-health
|
||||
component: init-db
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: init-database
|
||||
image: mysql:8.0
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
echo "检查并创建 ljwx_health 数据库..."
|
||||
mysql -h mysql-infra.infra.svc.cluster.local -u root -p${MYSQL_ROOT_PASSWORD} <<EOF
|
||||
CREATE DATABASE IF NOT EXISTS ljwx_health
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
SHOW DATABASES LIKE 'ljwx_health';
|
||||
|
||||
SELECT 'Database ljwx_health is ready' AS status;
|
||||
EOF
|
||||
|
||||
echo "✅ 数据库初始化完成"
|
||||
env:
|
||||
- name: MYSQL_ROOT_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-health-secret
|
||||
key: DB_PASSWORD
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
@@ -1,2 +1,22 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: ljwx-health
|
||||
|
||||
resources:
|
||||
- all.yaml
|
||||
- namespace.yaml
|
||||
- configmap.yaml
|
||||
- job-init-db.yaml
|
||||
- job-flyway-repair.yaml
|
||||
- deployment-boot.yaml
|
||||
- deployment-admin.yaml
|
||||
- deployment-bigscreen.yaml
|
||||
- service-boot.yaml
|
||||
- service-admin.yaml
|
||||
- service-bigscreen.yaml
|
||||
- ingress.yaml
|
||||
|
||||
commonLabels:
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: ljwx-health
|
||||
labels:
|
||||
name: ljwx-health
|
||||
app.kubernetes.io/name: ljwx-health
|
||||
app.kubernetes.io/component: health-monitoring
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ljwx-admin
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-admin
|
||||
component: frontend
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
selector:
|
||||
app: ljwx-admin
|
||||
component: frontend
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ljwx-bigscreen
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-bigscreen
|
||||
component: dashboard
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 5001
|
||||
targetPort: 5001
|
||||
protocol: TCP
|
||||
selector:
|
||||
app: ljwx-bigscreen
|
||||
component: dashboard
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ljwx-boot
|
||||
namespace: ljwx-health
|
||||
labels:
|
||||
app: ljwx-boot
|
||||
component: backend
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 9998
|
||||
targetPort: 9998
|
||||
protocol: TCP
|
||||
selector:
|
||||
app: ljwx-boot
|
||||
component: backend
|
||||
@@ -1,13 +0,0 @@
|
||||
resources:
|
||||
- ../../base
|
||||
namespace: ljwx-health
|
||||
images:
|
||||
- name: harbor.eu.lingjingwanxiang.cn/ljwx-health/ljwx-boot
|
||||
newName: harbor.eu.lingjingwanxiang.cn/ljwx-health/ljwx-boot
|
||||
newTag: "latest"
|
||||
- name: harbor.eu.lingjingwanxiang.cn/ljwx-health/ljwx-admin
|
||||
newName: harbor.eu.lingjingwanxiang.cn/ljwx-health/ljwx-admin
|
||||
newTag: "latest"
|
||||
- name: harbor.eu.lingjingwanxiang.cn/ljwx-health/ljwx-bigscreen
|
||||
newName: harbor.eu.lingjingwanxiang.cn/ljwx-health/ljwx-bigscreen
|
||||
newTag: "latest"
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: ljwx-health
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
images:
|
||||
- name: harbor.omniverseai.net/ljwx-health/ljwx-admin
|
||||
newTag: "main-1f8b811"
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: ljwx-health
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
images:
|
||||
- name: harbor.omniverseai.net/ljwx-health/ljwx-bigscreen
|
||||
newTag: "main-1f8b811"
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: ljwx-health
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
images:
|
||||
- name: harbor.omniverseai.net/ljwx-health/ljwx-boot
|
||||
newTag: "main-1f8b811"
|
||||
@@ -1,33 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ljwx-platform-admin-ui-nginx
|
||||
data:
|
||||
default.conf: |
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ljwx-platform-screen-nginx
|
||||
data:
|
||||
default.conf: |
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ljwx-platform-admin-ui
|
||||
labels:
|
||||
app: ljwx-platform-admin-ui
|
||||
project: ljwx-platform
|
||||
component: frontend
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-platform-admin-ui
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-platform-admin-ui
|
||||
project: ljwx-platform
|
||||
component: frontend
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: regcred
|
||||
containers:
|
||||
- name: ljwx-platform-admin-ui
|
||||
image: harbor.eu.lingjingwanxiang.cn/ljwx/ljwx-platform-admin-ui:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: "50m"
|
||||
memory: "64Mi"
|
||||
limits:
|
||||
cpu: "200m"
|
||||
memory: "128Mi"
|
||||
volumeMounts:
|
||||
- name: nginx-conf
|
||||
mountPath: /etc/nginx/conf.d/default.conf
|
||||
subPath: default.conf
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: nginx-conf
|
||||
configMap:
|
||||
name: ljwx-platform-admin-ui-nginx
|
||||
@@ -1,61 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ljwx-platform-screen
|
||||
labels:
|
||||
app: ljwx-platform-screen
|
||||
project: ljwx-platform
|
||||
component: screen
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-platform-screen
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-platform-screen
|
||||
project: ljwx-platform
|
||||
component: screen
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: regcred
|
||||
containers:
|
||||
- name: ljwx-platform-screen
|
||||
image: harbor.eu.lingjingwanxiang.cn/ljwx/ljwx-platform-screen:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: "50m"
|
||||
memory: "64Mi"
|
||||
limits:
|
||||
cpu: "200m"
|
||||
memory: "128Mi"
|
||||
volumeMounts:
|
||||
- name: nginx-conf
|
||||
mountPath: /etc/nginx/conf.d/default.conf
|
||||
subPath: default.conf
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: nginx-conf
|
||||
configMap:
|
||||
name: ljwx-platform-screen-nginx
|
||||
@@ -1,92 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ljwx-platform
|
||||
labels:
|
||||
app: ljwx-platform
|
||||
project: ljwx-platform
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-platform
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-platform
|
||||
project: ljwx-platform
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: regcred
|
||||
containers:
|
||||
- name: ljwx-platform
|
||||
image: harbor.eu.lingjingwanxiang.cn/ljwx-platform/ljwx-platform:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
volumeMounts:
|
||||
- name: app-logs
|
||||
mountPath: /workspace/logs
|
||||
env:
|
||||
- name: APP_PORT
|
||||
value: "8080"
|
||||
- name: SPRING_PROFILES_ACTIVE
|
||||
value: "prod"
|
||||
- name: SPRING_FLYWAY_VALIDATE_ON_MIGRATE
|
||||
value: "false"
|
||||
- name: DB_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-platform-db
|
||||
key: DB_URL
|
||||
- name: DB_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-platform-db
|
||||
key: DB_USERNAME
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-platform-db
|
||||
key: DB_PASSWORD
|
||||
- name: SPRING_DATASOURCE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-platform-db
|
||||
key: DB_URL
|
||||
- name: SPRING_DATASOURCE_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-platform-db
|
||||
key: DB_USERNAME
|
||||
- name: SPRING_DATASOURCE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-platform-db
|
||||
key: DB_PASSWORD
|
||||
- name: JWT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: ljwx-platform-app
|
||||
key: JWT_SECRET
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: http
|
||||
initialDelaySeconds: 90
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: http
|
||||
initialDelaySeconds: 45
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
cpu: "300m"
|
||||
memory: "512Mi"
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: "1Gi"
|
||||
volumes:
|
||||
- name: app-logs
|
||||
emptyDir: {}
|
||||
@@ -1,51 +0,0 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ljwx-platform
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: dnspod-letsencrypt
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
- platform.lingjingwanxiang.cn
|
||||
secretName: ljwx-platform-tls
|
||||
- hosts:
|
||||
- platform-admin.lingjingwanxiang.cn
|
||||
secretName: ljwx-platform-admin-tls
|
||||
- hosts:
|
||||
- platform-screen.lingjingwanxiang.cn
|
||||
secretName: ljwx-platform-screen-tls
|
||||
rules:
|
||||
- host: platform.lingjingwanxiang.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ljwx-platform
|
||||
port:
|
||||
number: 8080
|
||||
- host: platform-admin.lingjingwanxiang.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ljwx-platform-admin-ui
|
||||
port:
|
||||
number: 80
|
||||
- host: platform-screen.lingjingwanxiang.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: ljwx-platform-screen
|
||||
port:
|
||||
number: 80
|
||||
@@ -1,15 +0,0 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- deployment-admin-ui.yaml
|
||||
- deployment-screen.yaml
|
||||
- service.yaml
|
||||
- service-admin-ui.yaml
|
||||
- service-screen.yaml
|
||||
- ingress.yaml
|
||||
- configmap-nginx.yaml
|
||||
- secret.yaml
|
||||
- regcred.yaml
|
||||
- networkpolicy.yaml
|
||||
@@ -1,160 +0,0 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: ljwx-platform-default-deny
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
---
|
||||
# ── Backend ────────────────────────────────────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: ljwx-platform-allow-ingress-from-traefik
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: ljwx-platform
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: traefik
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8080
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: ljwx-platform-allow-egress-dns-and-db
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: ljwx-platform
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: infra
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: postgres
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5432
|
||||
---
|
||||
# ── Admin UI ───────────────────────────────────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: ljwx-platform-admin-ui-allow-ingress-from-traefik
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: ljwx-platform-admin-ui
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: traefik
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: ljwx-platform-admin-ui-allow-egress-dns
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: ljwx-platform-admin-ui
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
---
|
||||
# ── Screen ─────────────────────────────────────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: ljwx-platform-screen-allow-ingress-from-traefik
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: ljwx-platform-screen
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: traefik
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: ljwx-platform-screen-allow-egress-dns
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: ljwx-platform-screen
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: regcred
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
data:
|
||||
.dockerconfigjson: eyJhdXRocyI6eyJoYXJib3IuZXUubGluZ2ppbmd3YW54aWFuZy5jbiI6eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJIYXJib3IxMjM0NSIsImF1dGgiOiJZV1J0YVc0NlNHRnlZbTl5TVRJek5EVT0ifSwiaGFyYm9yLmV1LmxpbmdqaW5nd2FueGlhbmcuY246ODA4MCI6eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJIYXJib3IxMjM0NSIsImF1dGgiOiJZV1J0YVc0NlNHRnlZbTl5TVRJek5EVT0ifX19
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: ljwx-platform-db
|
||||
type: Opaque
|
||||
stringData:
|
||||
DB_URL: jdbc:postgresql://postgres-lb.infra.svc.cluster.local:5432/postgres
|
||||
DB_USERNAME: postgres
|
||||
DB_PASSWORD: ChangeMeInProduction123!
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: ljwx-platform-app
|
||||
type: Opaque
|
||||
stringData:
|
||||
JWT_SECRET: ChangeThisToAtLeast32BytesBeforeProductionUse
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ljwx-platform-admin-ui
|
||||
labels:
|
||||
app: ljwx-platform-admin-ui
|
||||
project: ljwx-platform
|
||||
spec:
|
||||
selector:
|
||||
app: ljwx-platform-admin-ui
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
name: http
|
||||
type: ClusterIP
|
||||
@@ -1,16 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ljwx-platform-screen
|
||||
labels:
|
||||
app: ljwx-platform-screen
|
||||
project: ljwx-platform
|
||||
spec:
|
||||
selector:
|
||||
app: ljwx-platform-screen
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
name: http
|
||||
type: ClusterIP
|
||||
@@ -1,14 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ljwx-platform
|
||||
spec:
|
||||
selector:
|
||||
app: ljwx-platform
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
name: http
|
||||
type: ClusterIP
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: ljwx-platform
|
||||
resources:
|
||||
- ../../base
|
||||
images:
|
||||
- name: harbor.eu.lingjingwanxiang.cn/ljwx-platform/ljwx-platform
|
||||
newName: harbor.eu.lingjingwanxiang.cn/ljwx/ljwx-platform
|
||||
newTag: sha-9f1266d1
|
||||
- name: harbor.eu.lingjingwanxiang.cn/ljwx/ljwx-platform-admin-ui
|
||||
newName: harbor.eu.lingjingwanxiang.cn/ljwx/ljwx-platform-admin-ui
|
||||
newTag: sha-b7766e9
|
||||
- name: harbor.eu.lingjingwanxiang.cn/ljwx/ljwx-platform-screen
|
||||
newName: harbor.eu.lingjingwanxiang.cn/ljwx/ljwx-platform-screen
|
||||
newTag: sha-b7766e9
|
||||
@@ -1,256 +0,0 @@
|
||||
# ljwx-template GitOps 配置
|
||||
|
||||
此目录包含 ljwx-template 项目的 GitOps 部署配置,配合 Argo CD 使用。
|
||||
|
||||
## 架构说明
|
||||
|
||||
### 仓库分离策略
|
||||
|
||||
**代码仓库** (`ljwx-template`):
|
||||
- 业务代码(FastAPI + Spring Boot)
|
||||
- Helm Chart (`deploy/helm/app/`)
|
||||
- CI Pipeline(GitHub Actions)
|
||||
|
||||
**GitOps 仓库** (`ljwx-deploy` - 本仓库):
|
||||
- Argo CD Application 定义
|
||||
- 环境特定配置
|
||||
- AppProject 权限定义
|
||||
|
||||
**优势**:
|
||||
- ✅ 权限分离:开发人员无需访问部署配置
|
||||
- ✅ 审批独立:部署变更有独立的 PR 流程
|
||||
- ✅ 回滚清晰:部署配置历史独立管理
|
||||
- ✅ 安全性高:Secrets 不在代码仓库
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
apps/ljwx-template/
|
||||
├── base/
|
||||
│ └── values-common.yaml # 所有环境共享配置
|
||||
└── overlays/
|
||||
├── eu-dev/
|
||||
│ └── values.yaml # EU Dev 环境配置
|
||||
├── mac-dev/
|
||||
│ └── values.yaml # Mac Dev 环境配置
|
||||
└── prod/
|
||||
└── values.yaml # 生产环境配置
|
||||
```
|
||||
|
||||
## 配置覆盖顺序
|
||||
|
||||
Argo CD 会按以下顺序合并配置:
|
||||
|
||||
1. **Helm Chart 默认值** (`ljwx-template/deploy/helm/app/values.yaml`)
|
||||
2. **通用配置** (`base/values-common.yaml`)
|
||||
3. **环境特定配置** (`overlays/{env}/values.yaml`)
|
||||
|
||||
后面的配置会覆盖前面的。
|
||||
|
||||
## 环境说明
|
||||
|
||||
### EU Dev (`overlays/eu-dev/`)
|
||||
- **用途**: EU 团队开发环境
|
||||
- **命名空间**: `dev`
|
||||
- **特点**: DEBUG 日志,1 副本,资源限制较小
|
||||
|
||||
### Mac Dev (`overlays/mac-dev/`)
|
||||
- **用途**: Mac k3s 本地开发环境
|
||||
- **命名空间**: `dev`
|
||||
- **特点**: DEBUG 日志,1 副本,本地数据库
|
||||
|
||||
### Prod (`overlays/prod/`)
|
||||
- **用途**: 生产环境
|
||||
- **命名空间**: `ljwx-template-prod`
|
||||
- **特点**: INFO 日志,3 副本,HPA,HTTPS Ingress
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 修改配置
|
||||
|
||||
1. 克隆本仓库:
|
||||
```bash
|
||||
git clone git@github.com:BrunoGaoSZ/ljwx-deploy.git
|
||||
cd ljwx-deploy/apps/ljwx-template
|
||||
```
|
||||
|
||||
2. 编辑环境特定配置:
|
||||
```bash
|
||||
# 修改 EU Dev 配置
|
||||
vim overlays/eu-dev/values.yaml
|
||||
|
||||
# 例如:调整副本数
|
||||
# apiFast:
|
||||
# replicaCount: 2
|
||||
```
|
||||
|
||||
3. 提交变更:
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "feat(ljwx-template): increase eu-dev replicas to 2"
|
||||
git push
|
||||
```
|
||||
|
||||
4. Argo CD 自动检测变更并部署(~30 秒)
|
||||
|
||||
### 查看部署状态
|
||||
|
||||
```bash
|
||||
# CLI
|
||||
argocd app get ljwx-template-eu-dev
|
||||
|
||||
# Web UI
|
||||
open https://argocd.example.com
|
||||
```
|
||||
|
||||
### 回滚配置
|
||||
|
||||
```bash
|
||||
# Git 回滚
|
||||
git revert HEAD
|
||||
git push
|
||||
|
||||
# 或使用 Argo CD 回滚
|
||||
argocd app rollback ljwx-template-eu-dev
|
||||
```
|
||||
|
||||
## 常见配置修改
|
||||
|
||||
### 1. 调整资源限制
|
||||
|
||||
```yaml
|
||||
# overlays/eu-dev/values.yaml
|
||||
apiFast:
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi" # 从 128Mi 提升
|
||||
cpu: "500m" # 从 100m 提升
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "1000m"
|
||||
```
|
||||
|
||||
### 2. 更新镜像版本
|
||||
|
||||
```yaml
|
||||
# overlays/eu-dev/values.yaml
|
||||
apiFast:
|
||||
image:
|
||||
tag: "main-abc1234" # 使用特定 Git SHA
|
||||
```
|
||||
|
||||
### 3. 启用 HPA
|
||||
|
||||
```yaml
|
||||
# overlays/eu-dev/values.yaml
|
||||
apiFast:
|
||||
autoscaling:
|
||||
enabled: true
|
||||
minReplicas: 2
|
||||
maxReplicas: 5
|
||||
targetCPUUtilizationPercentage: 70
|
||||
```
|
||||
|
||||
### 4. 更新数据库连接
|
||||
|
||||
```yaml
|
||||
# overlays/eu-dev/values.yaml
|
||||
database:
|
||||
host: new-postgres-host.example.com
|
||||
port: 5432
|
||||
name: new_database
|
||||
```
|
||||
|
||||
### 5. 修改日志级别
|
||||
|
||||
```yaml
|
||||
# overlays/prod/values.yaml
|
||||
apiFast:
|
||||
env:
|
||||
LOG_LEVEL: "WARNING" # 从 INFO 调整为 WARNING
|
||||
```
|
||||
|
||||
## 安全最佳实践
|
||||
|
||||
### 1. Secrets 管理
|
||||
|
||||
**禁止在 Git 中存储 Secrets!**
|
||||
|
||||
使用以下方案之一:
|
||||
- **ExternalSecrets Operator** (推荐)
|
||||
- **SealedSecrets**
|
||||
- **Vault**
|
||||
- 手动创建 K8s Secrets
|
||||
|
||||
示例(ExternalSecrets):
|
||||
```yaml
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: db-credentials-eu-dev
|
||||
spec:
|
||||
secretStoreRef:
|
||||
name: vault-backend
|
||||
target:
|
||||
name: db-credentials-eu-dev
|
||||
data:
|
||||
- secretKey: DB_USER
|
||||
remoteRef:
|
||||
key: ljwx/eu-dev/database
|
||||
property: username
|
||||
```
|
||||
|
||||
### 2. 权限控制
|
||||
|
||||
- 使用 `ljwx:developer` 角色:只读权限
|
||||
- 使用 `ljwx:operator` 角色:同步权限
|
||||
- 生产环境需要额外审批
|
||||
|
||||
### 3. 变更审计
|
||||
|
||||
所有变更都通过 Git PR:
|
||||
- 需要 Code Review
|
||||
- 自动触发 CI 检查
|
||||
- 可追溯历史
|
||||
|
||||
## 故障排查
|
||||
|
||||
### Application 无法同步
|
||||
|
||||
```bash
|
||||
# 查看详细状态
|
||||
argocd app get ljwx-template-eu-dev
|
||||
|
||||
# 查看差异
|
||||
argocd app diff ljwx-template-eu-dev
|
||||
|
||||
# 强制刷新
|
||||
argocd app sync ljwx-template-eu-dev --force
|
||||
```
|
||||
|
||||
### 配置未生效
|
||||
|
||||
检查配置覆盖顺序:
|
||||
```bash
|
||||
# 查看最终渲染的 manifest
|
||||
argocd app manifests ljwx-template-eu-dev
|
||||
```
|
||||
|
||||
### PreSync Hook 失败
|
||||
|
||||
查看 Migration Job 日志:
|
||||
```bash
|
||||
kubectl logs -n dev job/ljwx-template-migration-xxx
|
||||
```
|
||||
|
||||
## 相关文档
|
||||
|
||||
- **代码仓库**: https://github.com/BrunoGaoSZ/ljwx-template
|
||||
- **Helm Chart 说明**: `ljwx-template/deploy/helm/app/README.md`
|
||||
- **完整部署指南**: `ljwx-template/deploy/argo/README.md`
|
||||
- **Argo CD 官方文档**: https://argo-cd.readthedocs.io/
|
||||
|
||||
## 联系方式
|
||||
|
||||
- 问题反馈: https://github.com/BrunoGaoSZ/ljwx-template/issues
|
||||
- 团队: ljwx-team@example.com
|
||||
@@ -1,11 +0,0 @@
|
||||
# ljwx-template 通用配置(跨环境共享)
|
||||
# 此文件定义所有环境的公共配置,具体环境在 overlays 中覆盖
|
||||
|
||||
# 通用标签
|
||||
commonLabels:
|
||||
project: ljwx-template
|
||||
managed-by: argocd
|
||||
|
||||
# 通用注解
|
||||
commonAnnotations:
|
||||
maintained-by: ljwx-team
|
||||
@@ -1,50 +0,0 @@
|
||||
# EU Dev 环境配置覆盖
|
||||
|
||||
# FastAPI 配置
|
||||
apiFast:
|
||||
enabled: true
|
||||
replicaCount: 1 # dev 环境资源有限
|
||||
image:
|
||||
tag: "main-5205c31" # CI 会自动更新为 Git SHA
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "250m"
|
||||
env:
|
||||
LOG_LEVEL: "DEBUG" # dev 环境开启 debug
|
||||
WEB_CONCURRENCY: "1"
|
||||
autoscaling:
|
||||
enabled: false # dev 不启用自动扩展
|
||||
# Spring Boot 配置
|
||||
bootAdmin:
|
||||
enabled: false # dev 环境暂不启用
|
||||
# 数据库配置(指向 dev 集群的数据库)
|
||||
database:
|
||||
host: postgres-dev-eu.default.svc.cluster.local
|
||||
port: 5432
|
||||
name: app_dev_eu
|
||||
secretName: db-credentials-eu-dev
|
||||
# Redis 配置
|
||||
redis:
|
||||
host: redis-dev-eu.default.svc.cluster.local
|
||||
port: 6379
|
||||
secretName: redis-credentials-eu-dev
|
||||
# Migration 配置
|
||||
migration:
|
||||
enabled: true
|
||||
image:
|
||||
tag: "main-latest"
|
||||
# OpenTelemetry 配置
|
||||
otel:
|
||||
enabled: true
|
||||
endpoint: "http://tempo-eu-dev:4317"
|
||||
# Ingress 配置(dev 环境使用 NodePort 或禁用)
|
||||
ingress:
|
||||
enabled: false
|
||||
# ServiceMonitor(如果有 Prometheus Operator)
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
interval: 15s # dev 环境更频繁采集
|
||||
@@ -1,61 +0,0 @@
|
||||
# Mac Dev 环境配置覆盖
|
||||
|
||||
# FastAPI 配置
|
||||
apiFast:
|
||||
enabled: true
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
tag: "main-latest"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "250m"
|
||||
|
||||
env:
|
||||
LOG_LEVEL: "DEBUG"
|
||||
WEB_CONCURRENCY: "1"
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
|
||||
# Spring Boot 配置
|
||||
bootAdmin:
|
||||
enabled: false
|
||||
|
||||
# 数据库配置(Mac k3s 本地数据库)
|
||||
database:
|
||||
host: postgres-mac-dev.default.svc.cluster.local
|
||||
port: 5432
|
||||
name: app_dev_mac
|
||||
secretName: db-credentials-mac-dev
|
||||
|
||||
# Redis 配置
|
||||
redis:
|
||||
host: redis-mac-dev.default.svc.cluster.local
|
||||
port: 6379
|
||||
secretName: redis-credentials-mac-dev
|
||||
|
||||
# Migration 配置
|
||||
migration:
|
||||
enabled: true
|
||||
image:
|
||||
tag: "main-latest"
|
||||
|
||||
# OpenTelemetry 配置
|
||||
otel:
|
||||
enabled: true
|
||||
endpoint: "http://tempo-mac-dev:4317"
|
||||
|
||||
# Ingress 配置
|
||||
ingress:
|
||||
enabled: false
|
||||
|
||||
# ServiceMonitor
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
interval: 15s
|
||||
@@ -1,96 +0,0 @@
|
||||
# 生产环境配置覆盖
|
||||
|
||||
# FastAPI 配置
|
||||
apiFast:
|
||||
enabled: true
|
||||
replicaCount: 3 # 生产环境多副本
|
||||
|
||||
image:
|
||||
tag: "v1.0.0" # 生产使用具体版本号
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "1000m"
|
||||
|
||||
env:
|
||||
LOG_LEVEL: "INFO" # 生产环境 INFO 级别
|
||||
WEB_CONCURRENCY: "4"
|
||||
|
||||
autoscaling:
|
||||
enabled: true
|
||||
minReplicas: 3
|
||||
maxReplicas: 10
|
||||
targetCPUUtilizationPercentage: 70
|
||||
|
||||
# Spring Boot 配置
|
||||
bootAdmin:
|
||||
enabled: true
|
||||
replicaCount: 2
|
||||
|
||||
image:
|
||||
tag: "v1.0.0"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "1Gi"
|
||||
cpu: "1000m"
|
||||
limits:
|
||||
memory: "2Gi"
|
||||
cpu: "2000m"
|
||||
|
||||
env:
|
||||
SPRING_PROFILES_ACTIVE: "prod"
|
||||
|
||||
# 数据库配置(生产 RDS/云数据库)
|
||||
database:
|
||||
host: prod-db.example.com
|
||||
port: 5432
|
||||
name: app_prod
|
||||
secretName: db-credentials-prod # 使用 ExternalSecrets 或 SealedSecrets
|
||||
|
||||
# Redis 配置(生产 ElastiCache/云 Redis)
|
||||
redis:
|
||||
host: prod-redis.example.com
|
||||
port: 6379
|
||||
secretName: redis-credentials-prod
|
||||
|
||||
# Migration 配置
|
||||
migration:
|
||||
enabled: true
|
||||
image:
|
||||
tag: "v1.0.0"
|
||||
backoffLimit: 5
|
||||
ttlSecondsAfterFinished: 3600 # 保留 1 小时
|
||||
|
||||
# OpenTelemetry 配置
|
||||
otel:
|
||||
enabled: true
|
||||
endpoint: "http://tempo-prod:4317"
|
||||
|
||||
# Ingress 配置(生产环境启用 HTTPS)
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
||||
nginx.ingress.kubernetes.io/rate-limit: "100"
|
||||
hosts:
|
||||
- host: api.ljwx.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
service: api-fast
|
||||
tls:
|
||||
- secretName: api-ljwx-tls
|
||||
hosts:
|
||||
- api.ljwx.example.com
|
||||
|
||||
# ServiceMonitor
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
interval: 30s
|
||||
@@ -0,0 +1,44 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ljwx-website
|
||||
labels:
|
||||
app: ljwx-website
|
||||
component: web
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ljwx-website
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ljwx-website
|
||||
component: web
|
||||
spec:
|
||||
containers:
|
||||
- name: ljwx-website
|
||||
image: harbor.omniverseai.net/ljwx/ljwx-website:latest
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 80
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "25m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
+3
-3
@@ -4,6 +4,6 @@ kind: Kustomization
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress.yaml
|
||||
- secret.yaml
|
||||
- regcred.yaml
|
||||
|
||||
commonLabels:
|
||||
app: ljwx-website
|
||||
@@ -1,14 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: agent-platform
|
||||
name: ljwx-website
|
||||
labels:
|
||||
app: agent-platform
|
||||
app: ljwx-website
|
||||
spec:
|
||||
selector:
|
||||
app: agent-platform
|
||||
ports:
|
||||
- name: http
|
||||
port: 8000
|
||||
targetPort: http
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: ljwx-website
|
||||
@@ -1,70 +0,0 @@
|
||||
# ljwx-website EU Development Environment Configuration
|
||||
# This file is automatically updated by CI/CD pipeline
|
||||
|
||||
website:
|
||||
enabled: true
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: harbor.eu.lingjingwanxiang.cn:8080/ljwx/ljwx-website
|
||||
tag: "main-latest" # Updated by CI
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "32Mi"
|
||||
cpu: "25m"
|
||||
limits:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
|
||||
probes:
|
||||
liveness:
|
||||
path: /
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
readiness:
|
||||
path: /
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
|
||||
# Ingress configuration
|
||||
ingress:
|
||||
enabled: true
|
||||
className: traefik # k3s default ingress controller
|
||||
|
||||
# Fixed subdomain
|
||||
host: "website.lingjingwanxiang.cn"
|
||||
baseDomain: "lingjingwanxiang.cn"
|
||||
|
||||
path: /
|
||||
pathType: Prefix
|
||||
|
||||
annotations:
|
||||
# Traefik annotations
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
|
||||
# cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: "website-tls"
|
||||
# clusterIssuer: "letsencrypt-prod"
|
||||
|
||||
# ServiceMonitor (Prometheus Operator)
|
||||
serviceMonitor:
|
||||
enabled: false
|
||||
interval: 30s
|
||||
|
||||
# Namespace labels
|
||||
namespaceLabels:
|
||||
project: ljwx-website
|
||||
managed-by: argocd
|
||||
environment: dev-eu
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: ljwx-website-dev
|
||||
resources:
|
||||
- ../../base
|
||||
images:
|
||||
- name: harbor.omniverseai.net/ljwx/ljwx-website
|
||||
newTag: "main-abde1fb"
|
||||
replicas:
|
||||
- name: ljwx-website
|
||||
count: 1
|
||||
@@ -2,5 +2,5 @@ resources:
|
||||
- ../../base
|
||||
images:
|
||||
- name: nginx
|
||||
newName: ghcr.io/nginxinc/nginx-unprivileged
|
||||
newTag: "1.27-alpine"
|
||||
newName: harbor.omniverseai.net/dockerhub/library/nginx
|
||||
newTag: "alpine"
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: quality-dashboard
|
||||
labels:
|
||||
app: quality-dashboard
|
||||
component: monitoring
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: quality-dashboard
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: quality-dashboard
|
||||
component: monitoring
|
||||
spec:
|
||||
containers:
|
||||
- name: dashboard
|
||||
image: harbor.omniverseai.net/ljwx/quality-dashboard:dev
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "200m"
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
|
||||
commonLabels:
|
||||
app.kubernetes.io/name: quality-dashboard
|
||||
app.kubernetes.io/part-of: youngth-guard
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: quality-dashboard
|
||||
labels:
|
||||
app: quality-dashboard
|
||||
component: monitoring
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: quality-dashboard
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: dev
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
images:
|
||||
- name: harbor.omniverseai.net/ljwx/quality-dashboard
|
||||
newTag: "dev"
|
||||
|
||||
replicas:
|
||||
- name: quality-dashboard
|
||||
count: 1
|
||||
@@ -1,69 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: youngth-guard-backend
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: youngth-guard-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: youngth-guard-backend
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: ghcr-secret
|
||||
containers:
|
||||
- name: backend
|
||||
image: ghcr.io/brunogaosz/youngth-guard-backend:latest
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
env:
|
||||
- name: ENV
|
||||
value: "production"
|
||||
- name: LOG_LEVEL
|
||||
value: "info"
|
||||
# Database configuration from infra Postgres
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: DATABASE_URL
|
||||
- name: POSTGRES_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_HOST
|
||||
- name: POSTGRES_PORT
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_PORT
|
||||
- name: POSTGRES_DB
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_DB
|
||||
- name: POSTGRES_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_USER
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_PASSWORD
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8000
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
@@ -1,7 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: ghcr-secret
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
data:
|
||||
.dockerconfigjson: eyJhdXRocyI6eyJnaGNyLmlvIjp7InVzZXJuYW1lIjoiQnJ1bm9HYW9TWiIsInBhc3N3b3JkIjoiZ2hwX05UVVNwTW9kOURhcXAxeGxCTWliOGxnV09WMUJidTF5MjJ4cyIsImVtYWlsIjoieW91ci1lbWFpbEBleGFtcGxlLmNvbSIsImF1dGgiOiJRbkoxYm05SFlXOVRXanBuYUhCZlRsUlZVM0JOYjJRNVJHRnhjREY0YkVKTmFXSTRiR2RYVDFZeFFtSjFNWGt5TW5oeiJ9fX0=
|
||||
@@ -1,4 +0,0 @@
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ghcr-secret.yaml
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: youngth-guard-backend
|
||||
spec:
|
||||
selector:
|
||||
app: youngth-guard-backend
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8000
|
||||
targetPort: 8000
|
||||
type: ClusterIP
|
||||
@@ -1,163 +0,0 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: db-migration
|
||||
annotations:
|
||||
# ============================================
|
||||
# ArgoCD PreSync Hook
|
||||
# ============================================
|
||||
# 这个 Job 会在应用部署前执行
|
||||
# 如果失败,会阻止应用部署
|
||||
argocd.argoproj.io/hook: PreSync
|
||||
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
||||
argocd.argoproj.io/sync-wave: "-1"
|
||||
labels:
|
||||
app: youngth-guard-backend
|
||||
component: migration
|
||||
spec:
|
||||
backoffLimit: 0
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
component: migration
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
|
||||
# ============================================
|
||||
# Init Container: 等待数据库就绪
|
||||
# ============================================
|
||||
initContainers:
|
||||
- name: wait-for-db
|
||||
image: postgres:16-alpine
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
echo "Waiting for database to be ready..."
|
||||
until pg_isready -h $POSTGRES_HOST -p $POSTGRES_PORT -U $POSTGRES_USER; do
|
||||
echo "Database is unavailable - sleeping"
|
||||
sleep 2
|
||||
done
|
||||
echo "Database is ready!"
|
||||
env:
|
||||
- name: POSTGRES_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_HOST
|
||||
- name: POSTGRES_PORT
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_PORT
|
||||
- name: POSTGRES_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_USER
|
||||
- name: PGPASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_PASSWORD
|
||||
|
||||
# ============================================
|
||||
# Main Container: Flyway Migration
|
||||
# ============================================
|
||||
containers:
|
||||
- name: flyway
|
||||
image: flyway/flyway:10-alpine
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
|
||||
# 构建 Flyway 连接 URL
|
||||
export FLYWAY_URL="jdbc:postgresql://${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
|
||||
|
||||
echo "============================================"
|
||||
echo "Database Migration with Flyway"
|
||||
echo "============================================"
|
||||
echo "Database: $FLYWAY_URL"
|
||||
echo "User: $FLYWAY_USER"
|
||||
echo "Migrations: /flyway/sql"
|
||||
echo "============================================"
|
||||
|
||||
# 列出迁移文件
|
||||
echo "Available migrations:"
|
||||
ls -la /flyway/sql/ || echo "No migrations found"
|
||||
|
||||
# 执行迁移
|
||||
echo ""
|
||||
echo "Running migrations..."
|
||||
flyway -connectRetries=3 migrate
|
||||
|
||||
# 显示迁移历史
|
||||
echo ""
|
||||
echo "Migration history:"
|
||||
flyway info
|
||||
|
||||
echo ""
|
||||
echo "✅ Migration completed successfully"
|
||||
|
||||
env:
|
||||
# Flyway 配置
|
||||
- name: FLYWAY_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_USER
|
||||
- name: FLYWAY_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_PASSWORD
|
||||
|
||||
# 从 Secret 获取连接信息
|
||||
- name: POSTGRES_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_HOST
|
||||
- name: POSTGRES_PORT
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_PORT
|
||||
- name: POSTGRES_DB
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-youngth-guard-dev
|
||||
key: POSTGRES_DB
|
||||
|
||||
# Flyway 选项
|
||||
- name: FLYWAY_BASELINE_ON_MIGRATE
|
||||
value: "true"
|
||||
- name: FLYWAY_BASELINE_VERSION
|
||||
value: "0"
|
||||
- name: FLYWAY_OUT_OF_ORDER
|
||||
value: "false"
|
||||
- name: FLYWAY_VALIDATE_ON_MIGRATE
|
||||
value: "true"
|
||||
|
||||
volumeMounts:
|
||||
- name: migrations
|
||||
mountPath: /flyway/sql
|
||||
readOnly: true
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "200m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
|
||||
# ============================================
|
||||
# Volumes: 挂载迁移文件
|
||||
# ============================================
|
||||
volumes:
|
||||
- name: migrations
|
||||
configMap:
|
||||
name: db-migrations
|
||||
@@ -1,140 +0,0 @@
|
||||
apiVersion: v1
|
||||
data:
|
||||
V1__initial_schema.sql: |
|
||||
-- ============================================
|
||||
-- Migration: V1__initial_schema
|
||||
-- Description: 初始化 youngth-guard 数据库 Schema
|
||||
-- Author: System
|
||||
-- Date: 2025-01-18
|
||||
-- ============================================
|
||||
-- Description:
|
||||
-- 创建 youngth-guard 应用的初始数据库结构
|
||||
-- 包括用户表、评估记录表等核心表
|
||||
--
|
||||
-- Rollback Strategy:
|
||||
-- 如需回滚,创建 V2 迁移删除这些表
|
||||
--
|
||||
-- Performance Impact:
|
||||
-- 预估执行时间:< 5 秒
|
||||
-- 无锁表操作
|
||||
-- 对应用无影响(首次创建)
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- Step 1: 创建用户表
|
||||
-- ============================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at);
|
||||
|
||||
COMMENT ON TABLE users IS '用户表';
|
||||
COMMENT ON COLUMN users.id IS '用户 ID(主键)';
|
||||
COMMENT ON COLUMN users.username IS '用户名(唯一)';
|
||||
COMMENT ON COLUMN users.email IS '邮箱(唯一)';
|
||||
COMMENT ON COLUMN users.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN users.updated_at IS '更新时间';
|
||||
|
||||
-- ============================================
|
||||
-- Step 2: 创建心理评估记录表
|
||||
-- ============================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS assessments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
score INTEGER NOT NULL CHECK (score >= 0 AND score <= 100),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
assessment_type VARCHAR(50) NOT NULL DEFAULT 'general',
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_assessments_user_id ON assessments(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_assessments_status ON assessments(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_assessments_type ON assessments(assessment_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_assessments_created_at ON assessments(created_at);
|
||||
|
||||
COMMENT ON TABLE assessments IS '心理评估记录表';
|
||||
COMMENT ON COLUMN assessments.id IS '评估 ID(主键)';
|
||||
COMMENT ON COLUMN assessments.user_id IS '用户 ID(外键)';
|
||||
COMMENT ON COLUMN assessments.score IS '评估分数(0-100)';
|
||||
COMMENT ON COLUMN assessments.status IS '评估状态:pending/completed/cancelled';
|
||||
COMMENT ON COLUMN assessments.assessment_type IS '评估类型:general/anxiety/depression等';
|
||||
COMMENT ON COLUMN assessments.notes IS '评估备注';
|
||||
COMMENT ON COLUMN assessments.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN assessments.updated_at IS '更新时间';
|
||||
|
||||
-- ============================================
|
||||
-- Step 3: 创建审计日志表
|
||||
-- ============================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
action VARCHAR(50) NOT NULL,
|
||||
entity_type VARCHAR(50) NOT NULL,
|
||||
entity_id INTEGER,
|
||||
changes JSONB,
|
||||
ip_address INET,
|
||||
user_agent TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_user_id ON audit_logs(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs(action);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_entity ON audit_logs(entity_type, entity_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at);
|
||||
|
||||
COMMENT ON TABLE audit_logs IS '审计日志表';
|
||||
COMMENT ON COLUMN audit_logs.id IS '日志 ID(主键)';
|
||||
COMMENT ON COLUMN audit_logs.user_id IS '操作用户 ID';
|
||||
COMMENT ON COLUMN audit_logs.action IS '操作类型:create/update/delete/login等';
|
||||
COMMENT ON COLUMN audit_logs.entity_type IS '实体类型:user/assessment等';
|
||||
COMMENT ON COLUMN audit_logs.entity_id IS '实体 ID';
|
||||
COMMENT ON COLUMN audit_logs.changes IS '变更内容(JSON 格式)';
|
||||
COMMENT ON COLUMN audit_logs.ip_address IS '操作 IP 地址';
|
||||
COMMENT ON COLUMN audit_logs.user_agent IS '用户代理';
|
||||
COMMENT ON COLUMN audit_logs.created_at IS '创建时间';
|
||||
|
||||
-- ============================================
|
||||
-- Verification
|
||||
-- ============================================
|
||||
|
||||
-- 验证表已创建
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'users') THEN
|
||||
RAISE EXCEPTION 'Table users was not created!';
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'assessments') THEN
|
||||
RAISE EXCEPTION 'Table assessments was not created!';
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'audit_logs') THEN
|
||||
RAISE EXCEPTION 'Table audit_logs was not created!';
|
||||
END IF;
|
||||
|
||||
RAISE NOTICE 'All tables created successfully!';
|
||||
END $$;
|
||||
|
||||
-- ============================================
|
||||
-- Notes:
|
||||
-- - 所有表都使用 IF NOT EXISTS 确保幂等性
|
||||
-- - 索引已根据常用查询场景优化
|
||||
-- - 审计日志表使用 BIGSERIAL 应对大量日志
|
||||
-- - 使用 JSONB 类型存储灵活的变更数据
|
||||
-- ============================================
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: db-migrations
|
||||
@@ -1,11 +0,0 @@
|
||||
resources:
|
||||
- ../../base
|
||||
- db-migration-job.yaml
|
||||
- db-migrations-configmap.yaml
|
||||
|
||||
namespace: dev
|
||||
|
||||
images:
|
||||
- name: ghcr.io/brunogaosz/youngth-guard-backend
|
||||
newName: ghcr.io/brunogaosz/youngth-guard-backend
|
||||
newTag: "dev-b90d0d9"
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/BrunoGaoSZ/ljwx-deploy.git
|
||||
repoURL: http://192.168.1.83:33000/gao/ljwx-deploy.git
|
||||
targetRevision: main
|
||||
path: cluster
|
||||
destination:
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: apps-bootstrap
|
||||
namespace: argocd
|
||||
# This finalizer ensures Application cascading delete
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
|
||||
# Source: argocd-apps directory contains all Application manifests
|
||||
source:
|
||||
repoURL: http://192.168.1.83:33000/gao/ljwx-deploy.git
|
||||
targetRevision: main
|
||||
path: argocd-apps
|
||||
|
||||
# Only include Application resources, exclude this bootstrap file
|
||||
directory:
|
||||
recurse: false
|
||||
include: '*-dev.yaml' # Only auto-create *-dev.yaml applications
|
||||
|
||||
# Destination: Create Applications in argocd namespace
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: argocd
|
||||
|
||||
# Sync policy: Auto-create/delete Applications based on Git
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true # Auto-delete Applications removed from Git
|
||||
selfHeal: true # Auto-fix manual changes
|
||||
allowEmpty: false
|
||||
syncOptions:
|
||||
- CreateNamespace=false # argocd namespace already exists
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/BrunoGaoSZ/ljwx-deploy.git
|
||||
repoURL: http://192.168.1.83:33000/gao/ljwx-deploy.git
|
||||
targetRevision: main
|
||||
path: apps/nginx/overlays/dev
|
||||
destination:
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: gitea-runner
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: http://192.168.1.83:33000/gao/ljwx-deploy.git
|
||||
targetRevision: main
|
||||
path: infra/gitea-runner
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: gitea-runner
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/BrunoGaoSZ/ljwx-deploy.git
|
||||
repoURL: http://192.168.1.83:33000/gao/ljwx-deploy.git
|
||||
targetRevision: main
|
||||
path: infra/arc
|
||||
destination:
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: youngth-guard-backend
|
||||
name: backend-dev
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/BrunoGaoSZ/ljwx-deploy.git
|
||||
repoURL: http://192.168.1.83:33000/gao/ljwx-deploy.git
|
||||
targetRevision: main
|
||||
path: apps/youngth-guard/overlays/dev
|
||||
path: apps/backend/overlays/dev
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: dev
|
||||
@@ -1,60 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: ljwx-template-eu-dev
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
labels:
|
||||
environment: eu-dev
|
||||
project: ljwx-template
|
||||
annotations:
|
||||
argocd.argoproj.io/manifest-generate-paths: .
|
||||
spec:
|
||||
project: ljwx
|
||||
|
||||
# 多源配置:从两个 repo 拉取配置
|
||||
sources:
|
||||
# 源 1: Helm Chart(代码仓库)
|
||||
- repoURL: 'https://github.com/BrunoGaoSZ/ljwx-template.git'
|
||||
targetRevision: main
|
||||
path: deploy/helm/app
|
||||
helm:
|
||||
valueFiles:
|
||||
# 引用源 2 的 values 文件
|
||||
- $values/apps/ljwx-template/base/values-common.yaml
|
||||
- $values/apps/ljwx-template/overlays/eu-dev/values.yaml
|
||||
|
||||
# 源 2: Values 文件(GitOps 仓库)
|
||||
- repoURL: 'https://github.com/BrunoGaoSZ/ljwx-deploy.git'
|
||||
targetRevision: main
|
||||
ref: values # 定义引用名称
|
||||
|
||||
# 目标配置:部署到哪个集群和命名空间
|
||||
destination:
|
||||
server: 'https://kubernetes.default.svc'
|
||||
namespace: dev # 使用已有的 dev 命名空间
|
||||
|
||||
# 同步策略
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true # 自动删除 Git 中不存在的资源
|
||||
selfHeal: true # 自动修复手动更改
|
||||
allowEmpty: false
|
||||
syncOptions:
|
||||
- CreateNamespace=false # dev 命名空间已存在
|
||||
- PrunePropagationPolicy=foreground
|
||||
- PruneLast=true # 先部署新资源,再删除旧资源
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 5s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
|
||||
# 忽略某些字段的差异(避免误报)
|
||||
ignoreDifferences:
|
||||
- group: apps
|
||||
kind: Deployment
|
||||
jsonPointers:
|
||||
- /spec/replicas # 如果使用 HPA,忽略 replicas 差异
|
||||
@@ -1,56 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: ljwx-template-mac-dev
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
labels:
|
||||
environment: mac-dev
|
||||
project: ljwx-template
|
||||
annotations:
|
||||
argocd.argoproj.io/manifest-generate-paths: .
|
||||
spec:
|
||||
project: ljwx
|
||||
|
||||
# 多源配置:从两个 repo 拉取配置
|
||||
sources:
|
||||
# 源 1: Helm Chart(代码仓库)
|
||||
- repoURL: 'https://github.com/BrunoGaoSZ/ljwx-template.git'
|
||||
targetRevision: main
|
||||
path: deploy/helm/app
|
||||
helm:
|
||||
valueFiles:
|
||||
- $values/apps/ljwx-template/base/values-common.yaml
|
||||
- $values/apps/ljwx-template/overlays/mac-dev/values.yaml
|
||||
|
||||
# 源 2: Values 文件(GitOps 仓库)
|
||||
- repoURL: 'https://github.com/BrunoGaoSZ/ljwx-deploy.git'
|
||||
targetRevision: main
|
||||
ref: values
|
||||
|
||||
destination:
|
||||
server: 'https://kubernetes.default.svc'
|
||||
namespace: dev # Mac k3s 也使用 dev 命名空间
|
||||
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
allowEmpty: false
|
||||
syncOptions:
|
||||
- CreateNamespace=false
|
||||
- PrunePropagationPolicy=foreground
|
||||
- PruneLast=true
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 5s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
|
||||
ignoreDifferences:
|
||||
- group: apps
|
||||
kind: Deployment
|
||||
jsonPointers:
|
||||
- /spec/replicas
|
||||
@@ -1,17 +1,17 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: infra-data
|
||||
name: frontend-dev
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/BrunoGaoSZ/ljwx-deploy.git
|
||||
repoURL: http://192.168.1.83:33000/gao/ljwx-deploy.git
|
||||
targetRevision: main
|
||||
path: infra/data
|
||||
path: apps/frontend/overlays/dev
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: infra
|
||||
namespace: dev
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
@@ -1,34 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: ljwx-bookstore
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
labels:
|
||||
environment: bookstore
|
||||
project: ljwx-bookstore
|
||||
spec:
|
||||
project: ljwx
|
||||
source:
|
||||
repoURL: 'https://github.com/BrunoGaoSZ/ljwx-deploy.git'
|
||||
targetRevision: main
|
||||
path: apps/ljwx-bookstore/overlays/bookstore
|
||||
destination:
|
||||
server: 'https://kubernetes.default.svc'
|
||||
namespace: bookstore
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
allowEmpty: false
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- PrunePropagationPolicy=foreground
|
||||
- PruneLast=true
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 5s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
@@ -1,34 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: ljwx-platform
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
labels:
|
||||
environment: prod
|
||||
project: ljwx-platform
|
||||
spec:
|
||||
project: ljwx
|
||||
source:
|
||||
repoURL: https://github.com/BrunoGaoSZ/ljwx-deploy.git
|
||||
targetRevision: main
|
||||
path: apps/ljwx-platform/overlays/prod
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: ljwx-platform
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
allowEmpty: false
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- PrunePropagationPolicy=foreground
|
||||
- PruneLast=true
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 5s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user