Compare commits

..

No commits in common. "dev" and "master" have entirely different histories.
dev ... master

8 changed files with 136 additions and 281 deletions

View File

@ -0,0 +1,59 @@
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

@ -0,0 +1,59 @@
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

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

View File

@ -9,9 +9,6 @@ RUN npm install
COPY . . 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 RUN npm run build
# Production stage # Production stage

View File

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

View File

@ -1,14 +0,0 @@
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,37 +14,19 @@ spec:
labels: labels:
app: airlabs-manage-web app: airlabs-manage-web
spec: spec:
imagePullSecrets:
- name: cr-pull-secret
containers: containers:
- name: airlabs-manage-web - name: airlabs-manage-web
image: ${CI_REGISTRY_IMAGE}/web:latest image: ${CI_REGISTRY_IMAGE}/web:latest
imagePullPolicy: Always imagePullPolicy: Always
ports: ports:
- containerPort: 80 - 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: resources:
requests: requests:
memory: "64Mi" memory: "64Mi"
cpu: "50m" cpu: "100m"
limits: limits:
memory: "256Mi" memory: "512Mi"
cpu: "250m" cpu: "500m"
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service