文件解读(build-and-deploy.yml完整 CI/CD 流水线) 文件一build-and-deploy.yml完整 CI/CD 流水线作用这是项目的主要 CI/CD 流水线负责代码质量检查、容器镜像构建与推送、安全扫描、以及 Kubernetes 集群部署。它支持自动化触发推送、PR、标签和手动触发workflow_dispatch并提供了灵活的部署参数如目标集群、环境、镜像标签等。name: Build and Deploy CAMP Platform on: push: branches: [ main, develop ] tags: [ v* ] pull_request: branches: [ main ] workflow_dispatch: inputs: build: description: Build and push images required: false type: boolean default: false cluster: description: Target cluster required: true type: choice options: - local-dev - aks-dev - aks-staging - aks-prod default: local-dev environment: description: Target environment required: true type: choice options: - dev - staging - prod default: dev image_tag: description: Docker image tag required: false type: string default: latest deploy: description: Deploy after build required: false type: boolean default: false dry_run: description: Dry run deployment required: false type: boolean default: false env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true REGISTRY: ghcr.io IMAGE_NAME: kubernetes-microservices jobs: # Job 1 — build-and-lint (always runs, no secrets needed) build-and-lint: name: Build and Lint runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout code uses: actions/checkoutv4 - name: Validate project structure run: | echo Checking project structure... test -f camp-backend/requirements.txt || { echo requirements.txt not found; exit 1; } test -f camp-backend/run.py || { echo run.py not found; exit 1; } test -f helm/camp/Chart.yaml || { echo Helm Chart.yaml not found; exit 1; } test -f README.md || { echo README.md not found; exit 1; } echo ✅ Project structure validation passed - name: Validate configuration files run: | echo Checking configuration files... # Check if requirements.txt has valid content if [ -s camp-backend/requirements.txt ]; then echo ✅ requirements.txt exists and has content else echo ⚠️ requirements.txt is empty fi # Check if Helm chart is valid YAML if command -v yq /dev/null 21; then yq eval . helm/camp/Chart.yaml /dev/null echo ✅ Helm Chart.yaml is valid YAML else echo ⚠️ yq not available, skipping YAML validation fi echo ✅ Configuration files validation completed - name: Set up Helm uses: azure/setup-helmv3 with: version: v3.12.0 - name: Validate Helm chart run: | helm lint helm/camp - name: Test chart rendering run: | helm template camp-release helm/camp --dry-run - name: Validate Dockerfiles run: | echo Checking Dockerfile existence... test -f camp-backend/Dockerfile || { echo Backend Dockerfile not found; exit 1; } test -f camp-web-frontend/Dockerfile || { echo Web frontend Dockerfile not found; exit 1; } test -f camp-auth-frontend/Dockerfile || { echo Auth frontend Dockerfile not found; exit 1; } echo ✅ All Dockerfiles found # Job 2 — build-and-push (only runs on manual workflow_dispatch when build is enabled) build-and-push: name: Build and Push runs-on: ubuntu-latest needs: build-and-lint if: github.event_name workflow_dispatch github.event.inputs.build true secrets.GHCR_TOKEN ! permissions: contents: read packages: write outputs: image-tag: ${{ steps.meta.outputs.tags }} image-digest: ${{ steps.build.outputs.digest }} steps: - name: Checkout code uses: actions/checkoutv4 - name: Set up Docker Buildx uses: docker/setup-buildx-actionv3 - name: Log in to Container Registry uses: docker/login-actionv3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GHCR_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-actionv5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | typeref,eventbranch typeref,eventpr typesemver,pattern{{version}} typesemver,pattern{{major}}.{{minor}} typesemver,pattern{{major}} typesha,prefix{{branch}}- typeraw,valuelatest,enable{{is_default_branch}} - name: Build and push backend image id: build-backend uses: docker/build-push-actionv6 with: context: ./camp-backend push: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/camp-backend:${{ steps.meta.outputs.version }} labels: ${{ steps.meta.outputs.labels }} cache-from: typegha cache-to: typegha,modemax platforms: linux/amd64,linux/arm64 - name: Build and push web frontend image id: build-web uses: docker/build-push-actionv6 with: context: ./camp-web-frontend push: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/camp-web:${{ steps.meta.outputs.version }} labels: ${{ steps.meta.outputs.labels }} cache-from: typegha cache-to: typegha,modemax platforms: linux/amd64,linux/arm64 - name: Build and push auth frontend image id: build-auth uses: docker/build-push-actionv6 with: context: ./camp-auth-frontend push: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/camp-auth:${{ steps.meta.outputs.version }} labels: ${{ steps.meta.outputs.labels }} cache-from: typegha cache-to: typegha,modemax platforms: linux/amd64,linux/arm64 - name: Run security scan on images run: | echo Scanning backend image... docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy:latest image --severity HIGH,CRITICAL \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/camp-backend:${{ steps.meta.outputs.version }} echo Scanning web frontend image... docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy:latest image --severity HIGH,CRITICAL \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/camp-web:${{ steps.meta.outputs.version }} echo Scanning auth frontend image... docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy:latest image --severity HIGH,CRITICAL \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/camp-auth:${{ steps.meta.outputs.version }} # Job 3 — deploy (only runs on workflow_dispatch) deploy: name: Deploy to Cluster runs-on: ubuntu-latest needs: build-and-lint if: github.event_name workflow_dispatch github.event.inputs.deploy true steps: - name: Checkout code uses: actions/checkoutv4 - name: Set up tools run: | # Install Helm curl https://get.helm.sh/helm-v3.12.0-linux-amd64.tar.gz | tar xz sudo mv linux-amd64/helm /usr/local/bin/ # Install kubectl curl -LO https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl # Install yq sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 sudo chmod x /usr/local/bin/yq - name: Configure Azure CLI (for AKS) if: startsWith(github.event.inputs.cluster, aks) uses: azure/loginv1 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Get AKS credentials if: startsWith(github.event.inputs.cluster, aks) run: | # Extract cluster info from config CLUSTER_INFO$(yq eval .clusters.${{ github.event.inputs.cluster }} clusters/cluster-config.yaml) RESOURCE_GROUP$(echo $CLUSTER_INFO | yq eval .resourceGroup -) CLUSTER_NAME$(echo $CLUSTER_INFO | yq eval .clusterName -) az aks get-credentials \ --resource-group $RESOURCE_GROUP \ --name $CLUSTER_NAME \ --overwrite-existing - name: Configure kubectl context run: | # Extract context from config CLUSTER_CONTEXT$(yq eval .clusters.${{ github.event.inputs.cluster }}.context clusters/cluster-config.yaml) kubectl config use-context $CLUSTER_CONTEXT - name: Verify cluster connectivity run: | kubectl cluster-info kubectl get nodes - name: Deploy with Helm run: | # Set deployment parameters CLUSTER${{ github.event.inputs.cluster }} ENVIRONMENT${{ github.event.inputs.environment }} IMAGE_TAG${{ github.event.inputs.image_tag }} NAMESPACEcamp-${ENVIRONMENT} # Use image tag from build if not specified and build was run if [[ $IMAGE_TAG latest ]]; then # Check if build-and-push job ran and has outputs if [[ ${{ needs.build-and-push.outputs.image-tag }} ! ]]; then IMAGE_TAG${{ needs.build-and-push.outputs.image-tag }} else IMAGE_TAGlatest fi fi # Create namespace kubectl create namespace $NAMESPACE --dry-runclient -o yaml | kubectl apply -f - # Deploy with Helm helm upgrade --install camp-${ENVIRONMENT} helm/camp \ --namespace $NAMESPACE \ --values helm/camp/values.yaml \ --values environments/values-${ENVIRONMENT}.yaml \ --set image.tag$IMAGE_TAG \ --wait \ --timeout 10m \ ${{ github.event.inputs.dry_run true --dry-run --debug || }} - name: Verify deployment if: github.event.inputs.dry_run ! true run: | NAMESPACEcamp-${{ github.event.inputs.environment }} # Wait for pods to be ready kubectl wait --forconditionavailable --timeout300s deployment/camp-${{ github.event.inputs.environment }}-backend -n $NAMESPACE kubectl wait --forconditionavailable --timeout300s deployment/camp-${{ github.event.inputs.environment }}-web -n $NAMESPACE kubectl wait --forconditionavailable --timeout300s deployment/camp-${{ github.event.inputs.environment }}-auth -n $NAMESPACE # Show deployment status kubectl get pods -n $NAMESPACE kubectl get services -n $NAMESPACE kubectl get ingress -n $NAMESPACE - name: Run smoke tests if: github.event.inputs.dry_run ! true run: | NAMESPACEcamp-${{ github.event.inputs.environment }} # Get ingress URL INGRESS_URL$(kubectl get ingress -n $NAMESPACE -o jsonpath{.items[0].status.loadBalancer.ingress[0].ip} 2/dev/null || echo ) if [[ -n $INGRESS_URL ]]; then echo Testing application at http://$INGRESS_URL # Test health endpoints curl -f http://$INGRESS_URL/api/test || exit 1 curl -f http://$INGRESS_URL/api/health || exit 1 curl -f http://$INGRESS_URL/ || exit 1 curl -f http://$INGRESS_URL/auth || exit 1 echo ✅ Smoke tests passed else echo ⚠️ No ingress URL found, skipping smoke tests fi触发器onpush推送到main或develop分支时触发。pull_request针对main分支的 PR 事件触发。tags推送v*格式的标签时触发通常用于版本发布。workflow_dispatch手动触发支持以下输入参数build布尔是否构建并推送镜像默认 false。cluster必选目标集群local-dev / aks-dev / aks-staging / aks-prod。environment必选目标环境dev / staging / prod。image_tag字符串自定义镜像标签默认 latest。deploy布尔构建完成后是否执行部署默认 false。dry_run布尔是否仅模拟部署默认 false。环境变量envFORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true强制使用 Node.js 24 运行 JavaScript Actions。REGISTRY: ghcr.io容器镜像仓库GitHub Container Registry。IMAGE_NAME: kubernetes-microservices镜像基础名称。作业Jobs1.build-and-lint代码检查与验证触发条件始终运行无额外条件。权限仅contents: read。步骤检出代码。验证项目结构检查requirements.txt、run.py、Helm Chart 文件等。使用helm lint验证 Chart 语法。测试 Chart 渲染helm template --dry-run。检查各 Dockerfile 是否存在。目的快速反馈基础代码结构是否正确不依赖任何密钥适合所有 PR 和 push 事件。2.build-and-push构建并推送镜像触发条件仅当workflow_dispatch且build true并且secrets.GHCR_TOKEN不为空。依赖needs: build-and-lint。权限contents: readpackages: write允许推送镜像到 GHCR。输出image-tag生成的镜像标签。image-digest构建后的镜像摘要。步骤检出代码设置 Docker Buildx登录 GHCR。使用docker/metadata-action生成多种镜像标签分支、PR、语义化版本、SHA、latest 等。分别构建并推送三个服务的镜像camp-backend、camp-web、camp-auth支持多平台linux/amd64, linux/arm64并使用 GitHub Actions 缓存加速。使用 Trivy 对每个镜像进行高危/严重漏洞扫描仅扫描不阻断流程。目的在手动触发时构建生产级镜像并推送到仓库同时进行安全扫描。3.deploy部署到 Kubernetes触发条件仅workflow_dispatch且deploy true。依赖needs: build-and-lint注意它不依赖build-and-push意味着即使不构建也能部署但实际逻辑中若image_tag为 latest 且build-and-push运行过则会使用其输出的标签。步骤检出代码安装 Helm、kubectl、yq 工具。如果目标集群是 AKS则使用 Azure Login 并获取 AKS 凭据依赖secrets.AZURE_CREDENTIALS。从clusters/cluster-config.yaml读取集群上下文并切换 kubectl。验证集群连通性。使用 Helm 升级/安装应用根据environment创建命名空间如camp-dev。合并基础values.yaml和环境特定values-${ENVIRONMENT}.yaml。动态设置image.tag优先使用build-and-push输出的标签否则使用输入值或 latest。支持--dry-run模式仅模拟。验证部署等待所有 Deployment 可用展示 Pod/Service/Ingress 状态。运行冒烟测试若 Ingress 存在则测试/api/test、/api/health等端点。目的将应用部署到指定 Kubernetes 集群和环境并执行基本功能验证。关键设计特点灵活触发自动推送只做 lint手动触发可精细化控制构建和部署。条件执行构建和部署任务都有明确的if条件避免不必要的资源消耗。安全集成使用 GitHub 的 OIDC 或密钥进行 AKS 认证镜像推送到 GHCR。多环境支持通过cluster-config.yaml和environments/下的 values 文件区分不同集群和环境。可观测性部署后展示资源和执行冒烟测试。