Compare commits

...

6 Commits
master ... dev

Author SHA1 Message Date
zyc
3e7f0c028f fix: 后端健康检查改用TCP探针,修复CrashLoopBackOff
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m4s
后端没有/api/health/路由,HTTP探针返回404导致Pod被反复重启,
改用tcpSocket探针检测8000端口即可。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 16:25:23 +08:00
zyc
f6d50055d1 fix: 前端API地址按环境动态注入
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m39s
- Dockerfile添加VITE_API_BASE_URL build arg
- dev环境构建时注入 airlabs-manage-api.test.airlabs.art
- master环境构建时注入 airlabs-manage-api.airlabs.art

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 16:18:01 +08:00
zyc
b2e38ff36d fix: dev环境使用test子域名,ingress域名按分支替换
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m37s
- master: airlabs-manage-web.airlabs.art / airlabs-manage-api.airlabs.art
- dev: airlabs-manage-web.test.airlabs.art / airlabs-manage-api.test.airlabs.art
- 同步替换CORS_ORIGINS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 16:04:51 +08:00
zyc
7a9d805de9 refactor: 合并CI为单文件deploy.yaml,与jimeng-clone保持一致
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 7m30s
- 合并 deploy-web.yaml + deploy-backend.yaml → deploy.yaml
- 一次构建同时打包 backend 和 web 镜像
- 一次部署同时更新所有 k8s 资源

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:54:17 +08:00
zyc
8b6dc47a41 fix: 修正CI workflow secret名称,与jimeng-clone保持一致
Some checks failed
Build and Deploy Web / build-and-deploy (push) Successful in 5m21s
Build and Deploy Backend / build-and-deploy (push) Has been cancelled
- prod环境硬编码CR server/username/org
- dev环境CR_ORG硬编码为dev
- kubeconfig使用VOLCANO_TEST/PROD_KUBE_CONFIG

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:43:37 +08:00
zyc
afbc8594ee fix dev
All checks were successful
Build and Deploy Web / build-and-deploy (push) Successful in 1m24s
Build and Deploy Backend / build-and-deploy (push) Successful in 1m14s
2026-04-07 15:21:21 +08:00
8 changed files with 281 additions and 136 deletions

View File

@ -1,59 +0,0 @@
name: Build and Deploy Backend
on:
push:
branches:
- main
- master
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
config-inline: |
[registry."docker.io"]
mirrors = ["https://docker.m.daocloud.io", "https://docker.1panel.live", "https://hub.rat.dev"]
- name: Login to Huawei Cloud SWR
uses: docker/login-action@v2
with:
registry: ${{ secrets.SWR_SERVER }}
username: ${{ secrets.SWR_USERNAME }}
password: ${{ secrets.SWR_PASSWORD }}
- name: Build and Push Backend
uses: docker/build-push-action@v4
with:
context: ./backend
push: true
provenance: false
tags: ${{ secrets.SWR_SERVER }}/${{ secrets.SWR_ORG }}/airlabs-manage-backend:latest
- name: Setup Kubectl
run: |
curl -LO "https://files.m.daocloud.io/dl.k8s.io/release/v1.28.2/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
- name: Deploy to K3s
uses: Azure/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG }}
- name: Update K8s Manifests
run: |
# 替换镜像地址
sed -i "s|\${CI_REGISTRY_IMAGE}/backend:latest|${{ secrets.SWR_SERVER }}/${{ secrets.SWR_ORG }}/airlabs-manage-backend:latest|g" k8s/backend-deployment-prod.yaml
# 应用配置
kubectl apply -f k8s/backend-deployment-prod.yaml
kubectl apply -f k8s/backend-ingress.yaml
kubectl rollout restart deployment/airlabs-manage-backend

View File

@ -1,59 +0,0 @@
name: Build and Deploy Web
on:
push:
branches:
- main
- master
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
config-inline: |
[registry."docker.io"]
mirrors = ["https://docker.m.daocloud.io", "https://docker.1panel.live", "https://hub.rat.dev"]
- name: Login to Huawei Cloud SWR
uses: docker/login-action@v2
with:
registry: ${{ secrets.SWR_SERVER }}
username: ${{ secrets.SWR_USERNAME }}
password: ${{ secrets.SWR_PASSWORD }}
- name: Build and Push Web
uses: docker/build-push-action@v4
with:
context: ./frontend
push: true
provenance: false
tags: ${{ secrets.SWR_SERVER }}/${{ secrets.SWR_ORG }}/airlabs-manage-web:latest
- name: Setup Kubectl
run: |
curl -LO "https://files.m.daocloud.io/dl.k8s.io/release/v1.28.2/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
- name: Deploy to K3s
uses: Azure/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG }}
- name: Update K8s Manifests
run: |
# 替换镜像地址
sed -i "s|\${CI_REGISTRY_IMAGE}/web:latest|${{ secrets.SWR_SERVER }}/${{ secrets.SWR_ORG }}/airlabs-manage-web:latest|g" k8s/web-deployment-prod.yaml
# 应用配置
kubectl apply -f k8s/web-deployment-prod.yaml
kubectl apply -f k8s/web-ingress.yaml
kubectl rollout restart deployment/airlabs-manage-web

View File

@ -0,0 +1,212 @@
name: Build and Deploy
on:
push:
branches:
- master
- dev
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
run: |
git clone --depth=1 --branch=${{ github.ref_name }} https://gitea.airlabs.art/${{ github.repository }}.git .
- name: Set environment by branch
run: |
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
BUILD_DATE=$(date +%Y%m%d)
if [[ "${{ github.ref_name }}" == "master" ]]; then
echo "IMAGE_TAG=prod-${BUILD_DATE}-${SHORT_SHA}" >> $GITHUB_ENV
echo "CR_SERVER_ACTIVE=gitea-prod-cn-shanghai.cr.volces.com" >> $GITHUB_ENV
echo "CR_USERNAME_ACTIVE=seaislee@76339115" >> $GITHUB_ENV
echo "CR_PASSWORD_ACTIVE=${{ secrets.CR_PROD_PASSWORD }}" >> $GITHUB_ENV
echo "CR_ORG=prod" >> $GITHUB_ENV
echo "DEPLOY_ENV=production" >> $GITHUB_ENV
echo "DOMAIN_WEB=airlabs-manage-web.airlabs.art" >> $GITHUB_ENV
echo "DOMAIN_API=airlabs-manage-api.airlabs.art" >> $GITHUB_ENV
elif [[ "${{ github.ref_name }}" == "dev" ]]; then
echo "IMAGE_TAG=dev-${BUILD_DATE}-${SHORT_SHA}" >> $GITHUB_ENV
echo "CR_SERVER_ACTIVE=${{ secrets.CR_SERVER }}" >> $GITHUB_ENV
echo "CR_USERNAME_ACTIVE=${{ secrets.CR_USERNAME }}" >> $GITHUB_ENV
echo "CR_PASSWORD_ACTIVE=${{ secrets.CR_PASSWORD }}" >> $GITHUB_ENV
echo "CR_ORG=dev" >> $GITHUB_ENV
echo "DEPLOY_ENV=development" >> $GITHUB_ENV
echo "DOMAIN_WEB=airlabs-manage-web.test.airlabs.art" >> $GITHUB_ENV
echo "DOMAIN_API=airlabs-manage-api.test.airlabs.art" >> $GITHUB_ENV
fi
- name: Login to Volcano Engine CR
run: |
echo "${{ env.CR_PASSWORD_ACTIVE }}" | docker login --username "${{ env.CR_USERNAME_ACTIVE }}" --password-stdin ${{ env.CR_SERVER_ACTIVE }}
- name: Build and Push Backend
id: build_backend
run: |
set -o pipefail
for attempt in 1 2 3; do
echo "Build backend attempt $attempt/3..."
DOCKER_BUILDKIT=0 docker build \
--tag ${{ env.CR_SERVER_ACTIVE }}/${{ env.CR_ORG }}/airlabs-manage-backend:${{ env.IMAGE_TAG }} \
--tag ${{ env.CR_SERVER_ACTIVE }}/${{ env.CR_ORG }}/airlabs-manage-backend:latest \
./backend 2>&1 | tee /tmp/build.log && break
echo "Attempt $attempt failed, retrying in 10s..." && sleep 10
done
for attempt in 1 2 3; do
docker push ${{ env.CR_SERVER_ACTIVE }}/${{ env.CR_ORG }}/airlabs-manage-backend:${{ env.IMAGE_TAG }} && \
docker push ${{ env.CR_SERVER_ACTIVE }}/${{ env.CR_ORG }}/airlabs-manage-backend:latest && break
echo "Push attempt $attempt failed, retrying in 10s..." && sleep 10
done
- name: Build and Push Web
id: build_web
run: |
set -o pipefail
for attempt in 1 2 3; do
echo "Build web attempt $attempt/3..."
DOCKER_BUILDKIT=0 docker build \
--build-arg VITE_API_BASE_URL=https://${{ env.DOMAIN_API }}/api \
--tag ${{ env.CR_SERVER_ACTIVE }}/${{ env.CR_ORG }}/airlabs-manage-web:${{ env.IMAGE_TAG }} \
--tag ${{ env.CR_SERVER_ACTIVE }}/${{ env.CR_ORG }}/airlabs-manage-web:latest \
./frontend 2>&1 | tee -a /tmp/build.log && break
echo "Attempt $attempt failed, retrying in 10s..." && sleep 10
done
for attempt in 1 2 3; do
docker push ${{ env.CR_SERVER_ACTIVE }}/${{ env.CR_ORG }}/airlabs-manage-web:${{ env.IMAGE_TAG }} && \
docker push ${{ env.CR_SERVER_ACTIVE }}/${{ env.CR_ORG }}/airlabs-manage-web:latest && break
echo "Push attempt $attempt failed, retrying in 10s..." && sleep 10
done
- name: Setup Kubectl
run: |
if ! command -v kubectl &>/dev/null; then
for attempt in 1 2 3; do
curl -LO "https://files.m.daocloud.io/dl.k8s.io/release/v1.28.0/bin/linux/amd64/kubectl" && break
echo "Download attempt $attempt failed, retrying in 5s..." && sleep 5
done
chmod +x kubectl && mv kubectl /usr/bin/kubectl
fi
kubectl version --client
- name: Set kubeconfig
run: |
mkdir -p $HOME/.kube
if [[ "${{ github.ref_name }}" == "master" ]]; then
printf '%s\n' '${{ secrets.VOLCANO_PROD_KUBE_CONFIG }}' > $HOME/.kube/config
elif [[ "${{ github.ref_name }}" == "dev" ]]; then
printf '%s\n' '${{ secrets.VOLCANO_TEST_KUBE_CONFIG }}' > $HOME/.kube/config
fi
chmod 600 $HOME/.kube/config
echo "kubeconfig lines: $(wc -l < $HOME/.kube/config)"
grep server $HOME/.kube/config || echo "WARNING: no server found in kubeconfig"
- name: Deploy to K3s
id: deploy
run: |
echo "Environment: ${{ env.DEPLOY_ENV }}"
CR_IMAGE="${{ env.CR_SERVER_ACTIVE }}/${{ env.CR_ORG }}"
# Replace image placeholders
sed -i "s|\${CI_REGISTRY_IMAGE}/backend:latest|${CR_IMAGE}/airlabs-manage-backend:${{ env.IMAGE_TAG }}|g" k8s/backend-deployment-prod.yaml
sed -i "s|\${CI_REGISTRY_IMAGE}/web:latest|${CR_IMAGE}/airlabs-manage-web:${{ env.IMAGE_TAG }}|g" k8s/web-deployment-prod.yaml
# Replace domain placeholders in ingress
sed -i "s|airlabs-manage-web.airlabs.art|${{ env.DOMAIN_WEB }}|g" k8s/web-ingress.yaml
sed -i "s|airlabs-manage-api.airlabs.art|${{ env.DOMAIN_API }}|g" k8s/backend-ingress.yaml
# Replace CORS origin
sed -i "s|https://airlabs-manage-web.airlabs.art|https://${{ env.DOMAIN_WEB }}|g" k8s/backend-deployment-prod.yaml
for attempt in 1 2 3; do
echo "Deploy attempt $attempt/3..."
{
# Create/update image pull secret
kubectl create secret docker-registry cr-pull-secret \
--docker-server="${{ env.CR_SERVER_ACTIVE }}" \
--docker-username="${{ env.CR_USERNAME_ACTIVE }}" \
--docker-password="${{ env.CR_PASSWORD_ACTIVE }}" \
--dry-run=client -o yaml | kubectl apply -f -
# Apply manifests
kubectl apply -f k8s/cert-manager-issuer.yaml
kubectl apply -f k8s/backend-deployment-prod.yaml
kubectl apply -f k8s/backend-ingress.yaml
kubectl apply -f k8s/web-deployment-prod.yaml
kubectl apply -f k8s/web-ingress.yaml
kubectl rollout restart deployment/airlabs-manage-backend
kubectl rollout restart deployment/airlabs-manage-web
} 2>&1 | tee /tmp/deploy.log && break
echo "Attempt $attempt failed, retrying in 10s..."
sleep 10
done
- name: Report failure to Log Center
if: failure()
run: |
BUILD_LOG=""
DEPLOY_LOG=""
FAILED_STEP="unknown"
if [[ "${{ steps.build_backend.outcome }}" == "failure" || "${{ steps.build_web.outcome }}" == "failure" ]]; then
FAILED_STEP="build"
if [ -f /tmp/build.log ]; then
BUILD_LOG=$(tail -50 /tmp/build.log | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g')
fi
elif [[ "${{ steps.deploy.outcome }}" == "failure" ]]; then
FAILED_STEP="deploy"
if [ -f /tmp/deploy.log ]; then
DEPLOY_LOG=$(tail -50 /tmp/deploy.log | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g')
fi
fi
ERROR_LOG="${BUILD_LOG}${DEPLOY_LOG}"
if [ -z "$ERROR_LOG" ]; then
ERROR_LOG="No captured output. Check Gitea Actions UI for details."
fi
if [[ "$FAILED_STEP" == "deploy" ]]; then
SOURCE="deployment"
ERROR_TYPE="DeployError"
else
SOURCE="cicd"
ERROR_TYPE="DockerBuildError"
fi
curl -s -X POST "https://qiyuan-log-center-api.airlabs.art/api/v1/logs/report" \
-H "Content-Type: application/json" \
-d "{
\"project_id\": \"airlabs_manage\",
\"environment\": \"${{ env.DEPLOY_ENV }}\",
\"level\": \"ERROR\",
\"source\": \"${SOURCE}\",
\"commit_hash\": \"${{ github.sha }}\",
\"repo_url\": \"https://gitea.airlabs.art/${{ github.repository }}.git\",
\"error\": {
\"type\": \"${ERROR_TYPE}\",
\"message\": \"[${FAILED_STEP}] Build and Deploy failed on branch ${{ github.ref_name }}\",
\"stack_trace\": [\"${ERROR_LOG}\"]
},
\"context\": {
\"job_name\": \"build-and-deploy\",
\"step_name\": \"${FAILED_STEP}\",
\"workflow\": \"${{ github.workflow }}\",
\"run_id\": \"${{ github.run_number }}\",
\"branch\": \"${{ github.ref_name }}\",
\"actor\": \"${{ github.actor }}\",
\"commit\": \"${{ github.sha }}\",
\"run_url\": \"https://gitea.airlabs.art/${{ github.repository }}/actions/runs/${{ github.run_number }}\"
}
}" || true
- name: Docker Cleanup
if: always()
run: |
docker container prune -f
docker image prune -f
docker builder prune -a -f
echo "Disk usage after cleanup:"
df -h / | tail -1

View File

@ -1,9 +1,9 @@
# MySQL 数据库
DB_HOST=rm-7xv1uaw910558p1788o.mysql.rds.aliyuncs.com
DB_USER=airlabs_manage
DB_PASSWORD=Airlabs-manage123
# MySQL 数据库 (火山云测试环境)
DB_HOST=mysql-8351f937d637-public.rds.volces.com
DB_USER=zyc
DB_PASSWORD=Zyc188208
DB_PORT=3306
DB_NAME=airlabs-manage
DB_NAME=airlabs_manage
# 豆包模型配置 (火山引擎 ARK)
ARK_API_KEY=846b6981-9954-4c58-bb39-63079393bdb8

View File

@ -9,6 +9,9 @@ RUN npm install
COPY . .
ARG VITE_API_BASE_URL=https://airlabs-manage-api.airlabs.art/api
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
RUN npm run build
# Production stage

View File

@ -14,6 +14,8 @@ spec:
labels:
app: airlabs-manage-backend
spec:
imagePullSecrets:
- name: cr-pull-secret
containers:
- name: airlabs-manage-backend
image: ${CI_REGISTRY_IMAGE}/backend:latest
@ -21,30 +23,44 @@ spec:
ports:
- containerPort: 8000
env:
# Database (阿里云 RDS MySQL)
# Database
- name: DB_HOST
value: "rm-7xv1uaw910558p1788o.mysql.rds.aliyuncs.com"
value: "mysql8351f937d637.rds.ivolces.com"
- name: DB_NAME
value: "airlabs-manage"
- name: DB_USER
value: "airlabs_manage"
- name: DB_USER
value: "zyc"
- name: DB_PASSWORD
value: "Airlabs-manage123"
value: "Zyc188208"
- name: DB_PORT
value: "3306"
# 生产环境 JWT 密钥
# JWT 密钥
- name: SECRET_KEY
value: "Ui5-xEvtAhKRDtlXKzDfd7TElsVZFUhakff0qcjn8jU"
# CORS 允许的域名
- name: CORS_ORIGINS
value: "https://airlabs-manage-web.airlabs.art"
livenessProbe:
tcpSocket:
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
resources:
requests:
memory: "128Mi"
cpu: "100m"
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
memory: "1024Mi"
cpu: "1000m"
---
apiVersion: v1
kind: Service

View File

@ -0,0 +1,14 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: airlabsv001@gmail.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: traefik

View File

@ -14,19 +14,37 @@ spec:
labels:
app: airlabs-manage-web
spec:
imagePullSecrets:
- name: cr-pull-secret
containers:
- name: airlabs-manage-web
image: ${CI_REGISTRY_IMAGE}/web:latest
imagePullPolicy: Always
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
resources:
requests:
memory: "64Mi"
cpu: "100m"
cpu: "50m"
limits:
memory: "512Mi"
cpu: "500m"
memory: "256Mi"
cpu: "250m"
---
apiVersion: v1
kind: Service