Files
ljwx-stock/.gitea/workflows/cicd.yml
T
brunoandClaude 4606a575d0 feat: 重构部署架构 - 简化K8s部署流程
主要变更:
- 重构CI/CD流程: 移除Harbor依赖,直接部署到K8s
- 优化命名空间: 从claude-stock简化为stock
- 新增一键部署脚本: deploy.sh支持完整K8s部署流程
- 分离健康检查端点:
  - /health: K8s探针专用(快速响应)
  - /api/health: 详细健康检查(包含数据库/缓存状态)
- 简化Nginx配置: API路由交由Ingress处理
- 新增K8s部署清单: k8s/stock-deployment.yaml(PostgreSQL+Redis+Backend+Frontend+Ingress)
- 新增完整部署文档: DEPLOYMENT.md(一键部署/手动部署/CI CD/故障排查)
- 添加前端重构总结文档: frontend/REFACTOR_SUMMARY.md

技术改进:
✓ 简化CI/CD流程(5个stage: build, test, deploy, verify, notify)
✓ 使用artifact在job间传递镜像(避免重复构建)
✓ K8s健康检查配置优化(liveness 45s, readiness 20s)
✓ 资源限制合理化(backend 2Gi, frontend 512Mi)
✓ Ingress统一路由(/api → backend, / → frontend)

部署方式:
1. 一键部署: ./deploy.sh
2. CI/CD自动部署: git push origin main
3. 手动部署: kubectl apply -f k8s/stock-deployment.yaml

访问地址: https://stock.omniverseai.net

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 20:01:57 +08:00

260 lines
8.8 KiB
YAML

name: Claude Stock CI/CD Pipeline
on:
push:
branches:
- main
- develop
paths-ignore:
- '*.md'
- 'docs/**'
- '.gitignore'
pull_request:
branches:
- main
- develop
env:
K8S_NAMESPACE: stock
BACKEND_IMAGE: stock-backend:latest
FRONTEND_IMAGE: stock-frontend:latest
jobs:
# ==================== 构建镜像 ====================
build:
name: Build Docker Images
runs-on: ubuntu-latest
strategy:
matrix:
service:
- backend
- frontend
steps:
- name: 📥 检出代码
uses: actions/checkout@v3
- name: 🔧 设置 Docker Buildx
uses: docker/setup-buildx-action@v2
- name: 📦 构建 ${{ matrix.service }} 镜像
run: |
cd ${{ matrix.service }}
if [ "${{ matrix.service }}" = "backend" ]; then
IMAGE_NAME="${{ env.BACKEND_IMAGE }}"
else
IMAGE_NAME="${{ env.FRONTEND_IMAGE }}"
fi
echo "🏗️ 构建镜像: $IMAGE_NAME"
docker build -t $IMAGE_NAME .
echo "💾 保存镜像到本地"
docker save $IMAGE_NAME -o /tmp/${{ matrix.service }}-image.tar
echo "✅ 镜像构建完成"
- name: 📤 上传镜像 artifact
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.service }}-image
path: /tmp/${{ matrix.service }}-image.tar
retention-days: 1
# ==================== 测试阶段 ====================
test:
name: Run Tests
runs-on: ubuntu-latest
needs: build
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
POSTGRES_DB: test_db
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: 📥 检出代码
uses: actions/checkout@v3
- name: 🐍 设置 Python 环境
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: 📦 安装依赖
working-directory: backend
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov pytest-asyncio httpx
- name: ✅ 运行单元测试
working-directory: backend
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/test_db
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
TUSHARE_TOKEN: ${{ secrets.TUSHARE_TOKEN }}
ALPHA_VANTAGE_API_KEY: ${{ secrets.ALPHA_VANTAGE_API_KEY }}
run: |
pytest --cov=app --cov-report=xml --cov-report=term || true
echo "✅ 测试完成"
# ==================== 部署到 Kubernetes ====================
deploy:
name: Deploy to Kubernetes
runs-on: ubuntu-latest
needs: [build, test]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: 📥 检出代码
uses: actions/checkout@v3
- name: 📥 下载 backend 镜像
uses: actions/download-artifact@v3
with:
name: backend-image
path: /tmp
- name: 📥 下载 frontend 镜像
uses: actions/download-artifact@v3
with:
name: frontend-image
path: /tmp
- name: 🔧 配置 kubectl
env:
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }}
run: |
mkdir -p ~/.kube
echo "$KUBECONFIG_CONTENT" | base64 -d > ~/.kube/config
chmod 600 ~/.kube/config
kubectl version --client
- name: 📦 加载 Docker 镜像到 K8s
run: |
echo "加载 backend 镜像..."
docker load -i /tmp/backend-image.tar
echo "加载 frontend 镜像..."
docker load -i /tmp/frontend-image.tar
echo "✅ 镜像加载完成"
docker images | grep stock
- name: 📋 创建 namespace
run: |
kubectl create namespace ${{ env.K8S_NAMESPACE }} --dry-run=client -o yaml | kubectl apply -f -
- name: 🔐 创建 K8s Secrets (API Keys)
run: |
# 创建 API Keys secret
kubectl create secret generic stock-secrets \
--from-literal=OPENROUTER_API_KEY="${{ secrets.OPENROUTER_API_KEY }}" \
--from-literal=ALPHA_VANTAGE_API_KEY="${{ secrets.ALPHA_VANTAGE_API_KEY }}" \
--from-literal=TUSHARE_TOKEN="${{ secrets.TUSHARE_TOKEN }}" \
--from-literal=DATABASE_URL="postgresql://stockai:stockai2025@stock-postgres:5432/stock_ai" \
--from-literal=REDIS_URL="redis://stock-redis:6379/0" \
-n ${{ env.K8S_NAMESPACE }} \
--dry-run=client -o yaml | kubectl apply -f -
echo "✅ K8s Secrets 已创建"
- name: 🚀 部署到 Kubernetes
run: |
echo "部署应用到 namespace: ${{ env.K8S_NAMESPACE }}"
kubectl apply -f k8s/stock-deployment.yaml
- name: ⏳ 等待部署完成
run: |
echo "⏳ 等待 PostgreSQL..."
kubectl wait --for=condition=Ready pod -l app=stock-postgres -n ${{ env.K8S_NAMESPACE }} --timeout=180s 2>/dev/null || true
echo "⏳ 等待 Redis..."
kubectl wait --for=condition=Ready pod -l app=stock-redis -n ${{ env.K8S_NAMESPACE }} --timeout=180s 2>/dev/null || true
echo "⏳ 等待 backend 就绪..."
kubectl rollout status deployment/stock-backend -n ${{ env.K8S_NAMESPACE }} --timeout=5m || true
echo "⏳ 等待 frontend 就绪..."
kubectl rollout status deployment/stock-frontend -n ${{ env.K8S_NAMESPACE }} --timeout=5m || true
- name: 📊 获取部署状态
if: always()
run: |
echo "=== Deployment Status (Namespace: ${{ env.K8S_NAMESPACE }}) ==="
kubectl get deployments -n ${{ env.K8S_NAMESPACE }}
echo ""
echo "=== Pod Status ==="
kubectl get pods -n ${{ env.K8S_NAMESPACE }} -o wide
echo ""
echo "=== Service Status ==="
kubectl get svc -n ${{ env.K8S_NAMESPACE }}
echo ""
echo "=== Ingress Status ==="
kubectl get ingress -n ${{ env.K8S_NAMESPACE }}
# ==================== 部署后验证 ====================
verify:
name: Verify Deployment
runs-on: ubuntu-latest
needs: deploy
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: 🔧 配置 kubectl
env:
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }}
run: |
mkdir -p ~/.kube
echo "$KUBECONFIG_CONTENT" | base64 -d > ~/.kube/config
chmod 600 ~/.kube/config
- name: ✅ 健康检查
run: |
echo "执行健康检查..."
for i in {1..30}; do
BACKEND_POD=$(kubectl get pod -n ${{ env.K8S_NAMESPACE }} -l app=stock-backend -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
if [ ! -z "$BACKEND_POD" ]; then
if kubectl exec -n ${{ env.K8S_NAMESPACE }} $BACKEND_POD -- curl -f http://localhost:8000/health > /dev/null 2>&1; then
echo "✅ 健康检查通过"
echo "✅ 应用已成功部署到 namespace: ${{ env.K8S_NAMESPACE }}"
echo "🌐 访问地址: https://stock.omniverseai.net"
exit 0
fi
fi
echo "⏳ 等待服务就绪... ($i/30)"
sleep 10
done
echo "❌ 健康检查超时"
exit 1
# ==================== 通知阶段 ====================
notify:
name: Deployment Summary
runs-on: ubuntu-latest
needs: [build, test, deploy, verify]
if: always()
steps:
- name: 📊 生成部署总结
run: |
echo "## 🚀 Claude Stock 部署总结" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **仓库**: ${{ github.repository }}" >> $GITHUB_STEP_SUMMARY
echo "- **分支**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **提交**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "- **命名空间**: ${{ env.K8S_NAMESPACE }}" >> $GITHUB_STEP_SUMMARY
echo "- **访问地址**: https://stock.omniverseai.net" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 任务状态" >> $GITHUB_STEP_SUMMARY
echo "- Build: ${{ needs.build.result }}" >> $GITHUB_STEP_SUMMARY
echo "- Test: ${{ needs.test.result }}" >> $GITHUB_STEP_SUMMARY
echo "- Deploy: ${{ needs.deploy.result }}" >> $GITHUB_STEP_SUMMARY
echo "- Verify: ${{ needs.verify.result }}" >> $GITHUB_STEP_SUMMARY