- 添加 Gitea Actions 工作流 (.gitea/workflows/) - cicd.yml: 完整 CICD 流程(构建、测试、推送、部署) - test.yml: 快速测试工作流 - 配置 Kubernetes 部署清单 (k8s/) - namespace.yaml: 创建 claude-stock namespace - configmap.yaml: 应用配置和 Ollama 连接配置 - deployment.yaml: 后端和前端部署(支持 Node IP 连接 Ollama) - service.yaml: 服务定义、RBAC 和网络策略 - ollama-bridge.yaml: 四种 K8s 与宿主 Ollama 通信方案 - secrets.sh: 自动创建 K8s Secrets - 部署脚本和工具 - deploy-to-k8s.sh: 一键 K8s 部署脚本 - verify-cicd.sh: CICD 环境验证脚本 - 文档 - CICD_DEPLOYMENT_GUIDE.md: 完整部署指南(中文) - QUICK_START.md: 5 分钟快速启动指南(中文) 关键功能: ✅ Gitea Runners(Docker + K8s) ✅ Harbor 镜像仓库集成 ✅ 自动化测试和安全扫描 ✅ K8s 滚动更新和健康检查 ✅ Ollama 集成(使用 Node IP 解决通信问题) ✅ 完整的部署后验证 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
245 lines
8.2 KiB
YAML
245 lines
8.2 KiB
YAML
name: 完整 CICD 流程 (Build, Test, Push to Harbor, Deploy to K8s)
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
- develop
|
|
paths-ignore:
|
|
- '*.md'
|
|
- 'docs/**'
|
|
- '.gitignore'
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
- develop
|
|
|
|
env:
|
|
HARBOR_REGISTRY: localhost:8443
|
|
HARBOR_PROJECT: claude-stock
|
|
K8S_NAMESPACE: claude-stock
|
|
IMAGE_NAME_BACKEND: claude-stock-backend
|
|
IMAGE_NAME_FRONTEND: claude-stock-frontend
|
|
REGISTRY_USER: admin
|
|
REGISTRY_PASSWORD: ${{ secrets.HARBOR_PASSWORD }}
|
|
|
|
jobs:
|
|
# ==================== 构建和推送阶段 ====================
|
|
build-and-push:
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
matrix:
|
|
service:
|
|
- backend
|
|
- frontend
|
|
steps:
|
|
- name: 📥 检出代码
|
|
uses: actions/checkout@v4
|
|
|
|
- name: 🏗️ 设置 Docker 构建环境
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: 🔑 登录 Harbor 镜像仓库
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.HARBOR_REGISTRY }}
|
|
username: ${{ env.REGISTRY_USER }}
|
|
password: ${{ env.REGISTRY_PASSWORD }}
|
|
|
|
- name: 📦 构建并推送 ${{ matrix.service }} 镜像
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: ./${{ matrix.service }}
|
|
file: ./${{ matrix.service }}/Dockerfile
|
|
push: true
|
|
tags: |
|
|
${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/${{ matrix.service }}:latest
|
|
${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/${{ matrix.service }}:${{ github.sha }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
|
|
# ==================== 测试阶段 ====================
|
|
test:
|
|
needs: build-and-push
|
|
runs-on: ubuntu-latest
|
|
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@v4
|
|
|
|
- name: 🐍 设置 Python 环境
|
|
uses: actions/setup-python@v4
|
|
with:
|
|
python-version: '3.11'
|
|
cache: 'pip'
|
|
|
|
- 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
|
|
|
|
- name: 📊 上传覆盖率报告
|
|
uses: codecov/codecov-action@v3
|
|
if: always()
|
|
with:
|
|
files: ./backend/coverage.xml
|
|
|
|
# ==================== 安全扫描 ====================
|
|
security-scan:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: 📥 检出代码
|
|
uses: actions/checkout@v4
|
|
|
|
- name: 🔐 运行 Trivy 镜像扫描
|
|
uses: aquasecurity/trivy-action@master
|
|
with:
|
|
image-ref: ${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/backend:${{ github.sha }}
|
|
format: 'sarif'
|
|
output: 'trivy-results.sarif'
|
|
severity: 'CRITICAL,HIGH'
|
|
|
|
- name: 📤 上传扫描结果
|
|
uses: github/codeql-action/upload-sarif@v2
|
|
if: always()
|
|
with:
|
|
sarif_file: 'trivy-results.sarif'
|
|
|
|
# ==================== 部署到 Kubernetes ====================
|
|
deploy:
|
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
needs: [build-and-push, test]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: 📥 检出代码
|
|
uses: actions/checkout@v4
|
|
|
|
- name: 🔐 配置 Kubernetes 凭证
|
|
run: |
|
|
mkdir -p $HOME/.kube
|
|
echo "${{ secrets.KUBECONFIG }}" | base64 -d > $HOME/.kube/config
|
|
chmod 600 $HOME/.kube/config
|
|
|
|
- name: 📋 创建 namespace
|
|
run: |
|
|
kubectl create namespace ${{ env.K8S_NAMESPACE }} --dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
- name: 🔑 创建镜像拉取 Secret
|
|
run: |
|
|
kubectl create secret docker-registry harbor-secret \
|
|
--docker-server=${{ env.HARBOR_REGISTRY }} \
|
|
--docker-username=${{ env.REGISTRY_USER }} \
|
|
--docker-password=${{ env.REGISTRY_PASSWORD }} \
|
|
--docker-email=admin@example.com \
|
|
-n ${{ env.K8S_NAMESPACE }} \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
- name: 📝 更新镜像标签
|
|
run: |
|
|
sed -i 's|IMAGE_TAG|${{ github.sha }}|g' k8s/deployment.yaml
|
|
|
|
- name: 🚀 部署到 Kubernetes
|
|
run: |
|
|
kubectl apply -f k8s/namespace.yaml
|
|
kubectl apply -f k8s/configmap.yaml
|
|
kubectl apply -f k8s/deployment.yaml
|
|
kubectl apply -f k8s/service.yaml
|
|
|
|
- name: ⏳ 等待部署完成
|
|
run: |
|
|
kubectl rollout status deployment/claude-stock-backend \
|
|
-n ${{ env.K8S_NAMESPACE }} --timeout=5m
|
|
kubectl rollout status deployment/claude-stock-frontend \
|
|
-n ${{ env.K8S_NAMESPACE }} --timeout=5m
|
|
|
|
- name: 📊 获取部署信息
|
|
if: always()
|
|
run: |
|
|
echo "=== Deployment Status ==="
|
|
kubectl get deployments -n ${{ env.K8S_NAMESPACE }}
|
|
echo "=== Pod Status ==="
|
|
kubectl get pods -n ${{ env.K8S_NAMESPACE }}
|
|
echo "=== Service Status ==="
|
|
kubectl get svc -n ${{ env.K8S_NAMESPACE }}
|
|
|
|
# ==================== 部署后验证 ====================
|
|
smoke-test:
|
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
needs: deploy
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: 📥 检出代码
|
|
uses: actions/checkout@v4
|
|
|
|
- name: 🔐 配置 Kubernetes 凭证
|
|
run: |
|
|
mkdir -p $HOME/.kube
|
|
echo "${{ secrets.KUBECONFIG }}" | base64 -d > $HOME/.kube/config
|
|
|
|
- name: 🌐 获取服务端点
|
|
run: |
|
|
BACKEND_IP=$(kubectl get svc claude-stock-backend -n ${{ env.K8S_NAMESPACE }} \
|
|
-o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo 'pending')
|
|
echo "Backend Service IP: $BACKEND_IP"
|
|
|
|
- name: ✅ 健康检查
|
|
run: |
|
|
for i in {1..30}; do
|
|
if kubectl exec -n ${{ env.K8S_NAMESPACE }} \
|
|
$(kubectl get pod -n ${{ env.K8S_NAMESPACE }} -l app=backend -o jsonpath='{.items[0].metadata.name}') \
|
|
-- curl -f http://localhost:8000/health > /dev/null 2>&1; then
|
|
echo "✅ Health check passed"
|
|
exit 0
|
|
fi
|
|
echo "⏳ Waiting for service... ($i/30)"
|
|
sleep 10
|
|
done
|
|
exit 1
|
|
|
|
# ==================== 通知阶段 ====================
|
|
notify:
|
|
if: always()
|
|
needs: [build-and-push, test, deploy, smoke-test]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: 📊 生成部署总结
|
|
run: |
|
|
echo "## 🚀 CICD 部署总结" >> $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.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/backend:${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "### 任务状态" >> $GITHUB_STEP_SUMMARY
|
|
echo "- Build & Push: ${{ needs.build-and-push.result }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "- Test: ${{ needs.test.result }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "- Deploy: ${{ needs.deploy.result }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "- Smoke Test: ${{ needs.smoke-test.result }}" >> $GITHUB_STEP_SUMMARY
|