第八章:GitHub Actions 实战:构建 Docker 镜像并部署到 K8s

本章将前几章的知识点串联起来,通过一个从代码提交到 K8s 部署的完整实战案例,展示 GitHub Actions 如何在实际项目中落地。你将学会:构建 Docker 镜像、推送到 GitHub Container Registry(GHCR)、通过 GitOps 方式部署到 Kubernetes 集群。读完本章,你将具备用 GitHub Actions 搭建企业级 CI/CD 流水线的能力。

一、实战目标与架构

目标:构建一条完整的 CI/CD 流水线,实现:

代码推送到 main 分支 → 自动触发 Workflow

编译 Java 应用 → 运行单元测试 → 构建 Docker 镜像

推送镜像到 GHCR

通过 kubectl 或 GitOps 方式部署到 Kubernetes 集群

整体架构:

text

代码推送 → GitHub Actions Runner → 编译 & 测试 → Docker 构建 → 推送 GHCR → 部署 K8s

二、准备工作

2.1 创建 GitHub Personal Access Token(PAT)

用于 GHCR 认证:

GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)

勾选 write:packages 和 repo 权限

生成 Token 并保存(重要:离开页面后无法再次查看)

2.2 配置 Secrets

在仓库 Settings → Secrets and variables → Actions 中添加:

2.3 项目结构

text

my-spring-boot-app/

├── .github/

│ └── workflows/

│ └── ci-cd.yml # 主 Workflow 文件

├── Dockerfile

├── pom.xml

└── src/

└── main/

└── java/

三、完整 Workflow 文件

在 .github/workflows/ci-cd.yml 中编写完整流水线:

yaml 复制代码
name: CI/CD Pipeline

# 触发条件:main 分支推送或 PR
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# 全局环境变量
env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  # ========== Job 1: 构建与测试 ==========
  build-and-test:
    name: Build and Test
    runs-on: ubuntu-latest
    
    steps:
      # 1. 拉取代码
      - name: Checkout code
        uses: actions/checkout@v4

      # 2. 设置 JDK 17
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: maven

      # 3. 编译并运行测试
      - name: Build with Maven
        run: mvn clean package

      # 4. 上传测试报告
      - name: Upload test reports
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-reports
          path: target/surefire-reports/

      # 5. 上传 JAR 包(供后续 Job 使用)
      - name: Upload JAR artifact
        uses: actions/upload-artifact@v4
        with:
          name: application-jar
          path: target/*.jar

  # ========== Job 2: 构建并推送 Docker 镜像 ==========
  build-and-push:
    name: Build and Push Docker Image
    runs-on: ubuntu-latest
    needs: build-and-test       # 依赖上一个 Job
    # 仅在 main 分支执行
    if: github.ref == 'refs/heads/main'
    
    steps:
      # 1. 拉取代码
      - name: Checkout code
        uses: actions/checkout@v4

      # 2. 设置 Docker Buildx(支持多平台构建)[reference:56]
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      # 3. 登录 GHCR[reference:57]
      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GHCR_TOKEN }}

      # 4. 提取镜像元数据(生成标签)[reference:58]
      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=semver,pattern={{version}}
            type=sha,prefix=,format=short
            type=ref,event=branch
            type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}

      # 5. 构建并推送镜像[reference:59]
      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  # ========== Job 3: 部署到 Kubernetes ==========
  deploy:
    name: Deploy to Kubernetes
    runs-on: ubuntu-latest
    needs: build-and-push
    if: github.ref == 'refs/heads/main'
    
    steps:
      # 1. 拉取代码(获取 K8s 部署清单)
      - name: Checkout code
        uses: actions/checkout@v4

      # 2. 配置 kubectl(使用 kubeconfig)
      - name: Setup kubeconfig
        run: |
          mkdir -p $HOME/.kube
          echo "${{ secrets.K8S_CONFIG }}" | base64 -d > $HOME/.kube/config

      # 3. 部署到 K8s
      - name: Deploy to Kubernetes
        run: |
          # 替换镜像标签
          sed -i "s|{{IMAGE_TAG}}|${{ github.sha }}|g" k8s/deployment.yaml
          # 应用部署
          kubectl apply -f k8s/namespace.yaml
          kubectl apply -f k8s/deployment.yaml
          kubectl apply -f k8s/service.yaml
          # 等待部署完成
          kubectl rollout status deployment/my-app -n myapp

      # 4. 发送部署通知(可选)
      - name: Notify deployment
        if: success()
        run: |
          curl -X POST -H "Content-type: application/json" \
            --data '{"text":"✅ 部署成功: ${{ github.repository }} 版本 ${{ github.sha }}"}' \
            ${{ secrets.SLACK_WEBHOOK }}

四、关键步骤详解

4.1 使用 actions/upload-artifact 传递构建产物

build-and-test Job 编译生成的 JAR 包通过 upload-artifact 保存,供后续 Job 使用。但本例中 build-and-push Job 重新拉取代码并构建镜像(更常见的方式),实际项目可根据需要选择传递 JAR 或重新构建。

4.2 Docker Buildx 与缓存优化

docker/setup-buildx-action@v3 初始化 Buildx 构建器,支持多平台构建和高级缓存。

cache-from: type=gha 和 cache-to: type=gha,mode=max 利用 GitHub Actions 的内置缓存,显著加速后续构建。

4.3 镜像标签策略

docker/metadata-action@v5 自动生成多种标签:

type=semver:语义化版本标签(需 Git Tag)

type=sha:基于 commit SHA 的标签

type=ref,event=branch:分支名标签(如 main)

type=raw,value=latest:仅 main 分支生成 latest 标签

4.4 K8s 部署清单模板

k8s/deployment.yaml 中使用占位符 {{IMAGE_TAG}},在部署前由 sed 替换为实际 commit SHA:

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: ghcr.io/your-org/my-app:{{IMAGE_TAG}}
        ports:
        - containerPort: 8080

五、进阶:使用 OpenID Connect(OIDC)替代静态 Secrets

生产环境推荐使用 OIDC 替代静态 Secrets,避免长期有效的 Token 泄露风险。

配置步骤:

在云平台(AWS/Azure/GCP)配置 OIDC 身份提供商

在 Workflow 中使用 azure/login@v2 或 aws-actions/configure-aws-credentials@v4 通过 OIDC 认证

无需在 Secrets 中存储云平台凭证

yaml 复制代码
- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789012:role/github-actions-role
    aws-region: us-east-1

六、Workflow 执行与调试

6.1 查看执行状态

在 GitHub 仓库的 Actions 选项卡中查看所有 Workflow 运行记录

每个 Job 可展开查看实时日志

6.2 常用调试技巧

yaml 复制代码
# 打印上下文变量(用于调试)
- name: Debug - Print context
  run: |
    echo "Repository: ${{ github.repository }}"
    echo "SHA: ${{ github.sha }}"
    echo "Ref: ${{ github.ref }}"
    echo "Actor: ${{ github.actor }}"

# 列出当前目录内容
- name: Debug - List files
  run: ls -la

6.3 重新运行失败的 Job

在 Actions 页面,点击失败的 Workflow,右上角 Re-run jobs → 可选择重新运行所有 Job 或仅失败的 Job。

七、最佳实践总结

使用 .github/workflows/ 目录:所有 Workflow 文件存放在此。

版本锁定 Action:使用 @v4 而非 @main。

最小权限原则:为不同环境配置不同的 Secrets 和保护规则。

使用 OIDC 替代静态凭证:提高安全性。

缓存依赖:使用 actions/cache 或 docker/build-push-action 的缓存功能加速构建。

使用矩阵策略:并行测试多版本、多平台。

Job 依赖管理:用 needs 控制执行顺序。

条件执行:用 if 控制 Step/Job 在特定条件下执行。

上传测试报告:用 upload-artifact 保存测试结果。

安全审查第三方 Action:检查 Star 数、更新时间、源代码。

八、小结

本章通过一个完整的 Spring Boot + Docker + Kubernetes 案例,展示了 GitHub Actions 的核心能力:

CI:代码拉取 → Maven 构建 → 单元测试

CD:Docker 镜像构建 → 推送 GHCR → K8s 部署

GitHub Actions 与 GitHub 仓库的无缝集成、丰富的 Marketplace 生态以及免费的托管 Runner,使其成为个人项目和中小团队 CI/CD 的首选方案。