找回密码
 立即注册

QQ登录

只需一步,快速开始

peUfSYR.png
查看: 51|回复: 0

Day 22 CI/CD 与 GitHub Actions

[复制链接]

876

主题

13

回帖

2808

积分

管理员

积分
2808
发表于 2026-3-29 16:17:53 | 显示全部楼层 |阅读模式
Docker 30天实战系列 · 第22天

你有没有经历过这样的场景:代码写完了,本地测试也过了,然后开始手动构建镜像、打 tag、推到镜像仓库。做了三遍之后发现——第一次忘了改版本号,第二次推错了仓库,第三次终于对了,但已经浪费了一个小时。

更惨的是,团队里每个人构建出来的镜像居然不一样,因为大家本地环境不同。这种"在我机器上没问题"的噩梦,从开发阶段一直延续到了部署阶段。

今天我们就来彻底解决这个问题。用 GitHub Actions 搭建一条自动化流水线,让代码推上去之后,构建、测试、推镜像全部自动完成。你只管写代码,剩下的交给机器。

本文你将学到

  • GitHub Actions 的核心概念和工作原理
  • 编写完整的 Docker 构建推送 workflow
  • 多架构镜像构建(同时支持 x86 和 ARM)
  • 构建缓存优化,让 CI 速度翻倍
  • 实战调试 workflow 的常用技巧

    阅读时间: 约 12 分钟
    实操时间: 约 30 分钟
    难度等级: 中级(需要有 Docker 基础和 GitHub 账号)

    ━━━━━━━━━━━━━━━━━━━━

    什么是 CI/CD

    先说说这两个缩写。CI 是 Continuous Integration(持续集成),CD 是 Continuous Delivery/Deployment(持续交付/部署)。

    打个比方:你开了一家面包店。

  • CI 就像是你的自动和面机——面粉倒进去,自动揉面、发酵、检查面团质量。每次有新面粉(代码)进来,机器自动帮你处理好,发现面粉有问题(测试失败)立刻报警。
  • CD 就是和面之后自动送进烤箱、烤好了自动摆上货架。整个过程不需要人工介入。

    对应到 Docker 的场景:
    1. 代码推送 → 自动运行测试 → 自动构建镜像 → 自动推送到镜像仓库 → 自动部署到服务器
    2.    CI 阶段                                    CD 阶段
    复制代码

    GitHub Actions 核心概念

    GitHub Actions 是 GitHub 内置的 CI/CD 平台,免费额度对个人项目完全够用。在动手之前,先理清几个核心概念:
    1. ┌─────────────────────────────────────────────┐
    2. │                 Workflow                      │
    3. │  (.github/workflows/xxx.yml)                 │
    4. │                                              │
    5. │  ┌─────────────┐    ┌─────────────┐         │
    6. │  │   Job 1      │    │   Job 2      │        │
    7. │  │  (构建测试)   │───→│  (推送镜像)   │        │
    8. │  │              │    │              │         │
    9. │  │  ┌────────┐  │    │  ┌────────┐  │        │
    10. │  │  │ Step 1 │  │    │  │ Step 1 │  │        │
    11. │  │  │ 拉代码  │  │    │  │ 登录仓库│  │        │
    12. │  │  └────────┘  │    │  └────────┘  │        │
    13. │  │  ┌────────┐  │    │  ┌────────┐  │        │
    14. │  │  │ Step 2 │  │    │  │ Step 2 │  │        │
    15. │  │  │ 跑测试  │  │    │  │ 推镜像  │  │        │
    16. │  │  └────────┘  │    │  └────────┘  │        │
    17. │  └─────────────┘    └─────────────┘         │
    18. └─────────────────────────────────────────────┘
    复制代码

  • Workflow(工作流):一个 YAML 文件,定义了整个自动化流程
  • Job(任务):Workflow 里的一组步骤,跑在同一台虚拟机上
  • Step(步骤):Job 里的单个操作,可以是运行命令或调用别人写好的 Action
  • Action(动作):可复用的步骤模块,就像积木一样拼装使用
  • Runner(运行器):实际执行任务的虚拟机,GitHub 免费提供

    实操:搭建 Docker 自动构建流水线

    第一步:准备项目

    我们用一个简单的 Node.js 项目来演示。先创建项目结构:
    1. mkdir docker-ci-demo && cd docker-ci-demo
    2. git init
    复制代码

    创建一个简单的应用
    1. app.js
    复制代码

    1. const http = require('http');
    2. const server = http.createServer((req, res) => {
    3.   res.writeHead(200, { 'Content-Type': 'application/json' });
    4.   res.end(JSON.stringify({
    5.     status: 'ok',
    6.     version: process.env.APP_VERSION || '1.0.0',
    7.     timestamp: new Date().toISOString()
    8.   }));
    9. });
    10. server.listen(3000, () => console.log('Server running on port 3000'));
    复制代码

    创建
    1. Dockerfile
    复制代码

    1. FROM node:20-alpine
    2. WORKDIR /app
    3. COPY app.js .
    4. EXPOSE 3000
    5. CMD ["node", "app.js"]
    复制代码

    第二步:编写基础 Workflow

    创建
    1. .github/workflows/docker-build.yml
    复制代码

    1. name: Build and Push Docker Image
    2. # 触发条件:推送到 main 分支,或打 tag
    3. on:
    4.   push:
    5.     branches: [main]
    6.     tags: ['v*']
    7.   pull_request:
    8.     branches: [main]
    9. env:
    10.   REGISTRY: ghcr.io
    11.   IMAGE_NAME: ${{ github.repository }}
    12. jobs:
    13.   build-and-push:
    14.     runs-on: ubuntu-latest
    15.     # 授予 GITHUB_TOKEN 权限,用于推送到 ghcr.io
    16.     permissions:
    17.       contents: read
    18.       packages: write
    19.     steps:
    20.       # 1. 拉取代码
    21.       - name: Checkout code
    22.         uses: actions/checkout@v4
    23.       # 2. 登录到 GitHub Container Registry
    24.       - name: Log in to Container Registry
    25.         if: github.event_name != 'pull_request'
    26.         uses: docker/login-action@v3
    27.         with:
    28.           registry: ${{ env.REGISTRY }}
    29.           username: ${{ github.actor }}
    30.           password: ${{ secrets.GITHUB_TOKEN }}
    31.       # 3. 提取镜像标签和标签
    32.       - name: Extract metadata
    33.         id: meta
    34.         uses: docker/metadata-action@v5
    35.         with:
    36.           images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
    37.           tags: |
    38.             type=ref,event=branch
    39.             type=semver,pattern={{version}}
    40.             type=semver,pattern={{major}}.{{minor}}
    41.             type=sha
    42.       # 4. 构建并推送
    43.       - name: Build and push Docker image
    44.         uses: docker/build-push-action@v5
    45.         with:
    46.           context: .
    47.           push: ${{ github.event_name != 'pull_request' }}
    48.           tags: ${{ steps.meta.outputs.tags }}
    49.           labels: ${{ steps.meta.outputs.labels }}
    复制代码

    这个 workflow 做了什么:

  • 代码推送到 main 或打 tag 时触发
  • 登录 GitHub 自带的容器镜像仓库(ghcr.io,免费的)
  • 自动根据分支名、tag 名生成镜像标签
  • 构建 Docker 镜像并推送

    第三步:提交并触发
    1. git add .
    2. git commit -m "feat: add Docker CI/CD workflow"
    3. git remote add origin https://github.com/你的用户名/docker-ci-demo.git
    4. git push -u origin main
    复制代码

    推送之后,打开 GitHub 仓库页面,点击 Actions 标签页,就能看到 workflow 正在运行了。

    预期输出(在 Actions 页面看到的日志):
    1. Run docker/build-push-action@v5
    2.   Building Docker image...
    3.   #1 [internal] load build definition from Dockerfile
    4.   #2 [internal] load .dockerignore
    5.   #3 [1/2] FROM node:20-alpine
    6.   #4 [2/2] COPY app.js .
    7.   #5 exporting to image
    8.   Pushing ghcr.io/yourname/docker-ci-demo:main
    9.   Done!
    复制代码

    进阶:多架构构建

    现在越来越多人用 ARM 架构的机器(比如 Mac M 系列芯片、AWS Graviton),如果你只构建 x86 镜像,ARM 用户拉下来跑会很慢(需要模拟执行)。多架构构建让一个镜像同时支持 x86 和 ARM。

    把 workflow 升级一下:
    1. jobs:
    2.   build-and-push:
    3.     runs-on: ubuntu-latest
    4.     permissions:
    5.       contents: read
    6.       packages: write
    7.     steps:
    8.       - name: Checkout code
    9.         uses: actions/checkout@v4
    10.       # 新增:安装 QEMU,用于模拟 ARM 架构
    11.       - name: Set up QEMU
    12.         uses: docker/setup-qemu-action@v3
    13.       # 新增:安装 Buildx,支持多架构构建
    14.       - name: Set up Docker Buildx
    15.         uses: docker/setup-buildx-action@v3
    16.       - name: Log in to Container Registry
    17.         if: github.event_name != 'pull_request'
    18.         uses: docker/login-action@v3
    19.         with:
    20.           registry: ${{ env.REGISTRY }}
    21.           username: ${{ github.actor }}
    22.           password: ${{ secrets.GITHUB_TOKEN }}
    23.       - name: Extract metadata
    24.         id: meta
    25.         uses: docker/metadata-action@v5
    26.         with:
    27.           images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
    28.           tags: |
    29.             type=ref,event=branch
    30.             type=semver,pattern={{version}}
    31.             type=sha
    32.       - name: Build and push Docker image
    33.         uses: docker/build-push-action@v5
    34.         with:
    35.           context: .
    36.           # 关键:指定目标平台
    37.           platforms: linux/amd64,linux/arm64
    38.           push: ${{ github.event_name != 'pull_request' }}
    39.           tags: ${{ steps.meta.outputs.tags }}
    40.           labels: ${{ steps.meta.outputs.labels }}
    复制代码

    核心变化只有三处:装 QEMU、装 Buildx、加 platforms 参数。就这么简单,你的镜像就同时支持 x86 和 ARM 了。

    进阶:缓存优化

    多架构构建有个问题——慢。因为要构建两个平台,时间直接翻倍。而且每次 CI 跑完虚拟机就销毁了,下次又要从头构建。

    Docker 层缓存可以解决这个问题。思路是把构建缓存存到 GitHub Actions Cache 里,下次构建直接复用。
    1.       - name: Build and push Docker image
    2.         uses: docker/build-push-action@v5
    3.         with:
    4.           context: .
    5.           platforms: linux/amd64,linux/arm64
    6.           push: ${{ github.event_name != 'pull_request' }}
    7.           tags: ${{ steps.meta.outputs.tags }}
    8.           labels: ${{ steps.meta.outputs.labels }}
    9.           # 缓存配置
    10.           cache-from: type=gha
    11.           cache-to: type=gha,mode=max
    复制代码

    就加了两行:
    1. cache-from
    复制代码
    1. cache-to
    复制代码
    ,指定用 GitHub Actions 的缓存(
    1. type=gha
    复制代码
    )。
    1. mode=max
    复制代码
    表示缓存所有层,不只是最终层。

    实际效果对比:
    1. 无缓存构建:  约 3-5 分钟(多架构)
    2. 有缓存构建:  约 30-60 秒(依赖没变的情况下)
    复制代码

    速度提升 5-10 倍,非常值得。

    完整的生产级 Workflow

    把上面所有内容整合起来,加上测试环节,这是一个生产可用的完整配置:
    1. name: Docker CI/CD Pipeline
    2. on:
    3.   push:
    4.     branches: [main]
    5.     tags: ['v*']
    6.   pull_request:
    7.     branches: [main]
    8. env:
    9.   REGISTRY: ghcr.io
    10.   IMAGE_NAME: ${{ github.repository }}
    11. jobs:
    12.   # 第一阶段:测试
    13.   test:
    14.     runs-on: ubuntu-latest
    15.     steps:
    16.       - uses: actions/checkout@v4
    17.       - uses: actions/setup-node@v4
    18.         with:
    19.           node-version: '20'
    20.       - run: npm ci
    21.       - run: npm test
    22.   # 第二阶段:构建并推送镜像(测试通过后才执行)
    23.   build-and-push:
    24.     needs: test
    25.     runs-on: ubuntu-latest
    26.     if: github.event_name != 'pull_request'
    27.     permissions:
    28.       contents: read
    29.       packages: write
    30.     steps:
    31.       - name: Checkout code
    32.         uses: actions/checkout@v4
    33.       - name: Set up QEMU
    34.         uses: docker/setup-qemu-action@v3
    35.       - name: Set up Docker Buildx
    36.         uses: docker/setup-buildx-action@v3
    37.       - name: Log in to Container Registry
    38.         uses: docker/login-action@v3
    39.         with:
    40.           registry: ${{ env.REGISTRY }}
    41.           username: ${{ github.actor }}
    42.           password: ${{ secrets.GITHUB_TOKEN }}
    43.       - name: Extract metadata
    44.         id: meta
    45.         uses: docker/metadata-action@v5
    46.         with:
    47.           images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
    48.           tags: |
    49.             type=ref,event=branch
    50.             type=semver,pattern={{version}}
    51.             type=semver,pattern={{major}}.{{minor}}
    52.             type=sha
    53.       - name: Build and push
    54.         uses: docker/build-push-action@v5
    55.         with:
    56.           context: .
    57.           platforms: linux/amd64,linux/arm64
    58.           push: true
    59.           tags: ${{ steps.meta.outputs.tags }}
    60.           labels: ${{ steps.meta.outputs.labels }}
    61.           cache-from: type=gha
    62.           cache-to: type=gha,mode=max
    复制代码

    注意
    1. needs: test
    复制代码
    这行——构建任务依赖测试任务,测试不过就不构建。这就像工厂的质检环节,不合格的产品不允许出厂。

    推送到 Docker Hub

    如果你想推到 Docker Hub 而不是 ghcr.io,改动很小:
    1. env:
    2.   REGISTRY: docker.io
    3.   IMAGE_NAME: 你的DockerHub用户名/你的项目名
    4. # 登录步骤改成:
    5. - name: Log in to Docker Hub
    6.   uses: docker/login-action@v3
    7.   with:
    8.     username: ${{ secrets.DOCKERHUB_USERNAME }}
    9.     password: ${{ secrets.DOCKERHUB_TOKEN }}
    复制代码

    需要在 GitHub 仓库的 Settings > Secrets and variables > Actions 里添加
    1. DOCKERHUB_USERNAME
    复制代码
    1. DOCKERHUB_TOKEN
    复制代码
    两个密钥。Token 在 Docker Hub 的 Account Settings > Security 里生成。

    调试 Workflow 的实用技巧

    workflow 写错了怎么办?分享几个实用技巧:

    1. 本地验证 YAML 语法
    1. # 安装 actionlint(workflow 语法检查工具)
    2. brew install actionlint    # macOS
    3. # 或
    4. go install github.com/rhysd/actionlint/cmd/actionlint@latest
    5. # 检查语法
    6. actionlint .github/workflows/docker-build.yml
    复制代码

    2. 用 act 在本地跑 workflow
    1. # 安装 act
    2. brew install act    # macOS
    3. # 本地运行(需要 Docker)
    4. act push --job build-and-push
    复制代码
    1. act
    复制代码
    会用 Docker 模拟 GitHub Actions 的运行环境,不用每次都推代码触发了。

    3. 加调试步骤

    在 workflow 里临时加一步,打印环境信息:
    1.       - name: Debug info
    2.         run: |
    3.           echo "Event: ${{ github.event_name }}"
    4.           echo "Ref: ${{ github.ref }}"
    5.           echo "SHA: ${{ github.sha }}"
    6.           docker version
    7.           docker buildx version
    复制代码

    常见问题 Q&A

    Q1: 构建时报 "denied: permission_denied",怎么回事?

    大概率是权限配置问题。检查两个地方:一是 workflow 里
    1. permissions
    复制代码
    部分有没有
    1. packages: write
    复制代码
    ;二是仓库的 Settings > Actions > General 里,"Workflow permissions" 要选 "Read and write permissions"。如果推 Docker Hub,检查 Secrets 里的 Token 是否有 push 权限。

    Q2: 多架构构建太慢了,有什么办法?

    除了前面说的
    1. cache-from/cache-to
    复制代码
    ,还可以考虑把两个架构拆成两个并行 Job 分别构建,最后用
    1. docker buildx imagetools create
    复制代码
    合并成一个 manifest。这样两个架构同时构建,时间减半。另外,Dockerfile 优化也很关键——把不常变的层放前面,利用好层缓存。

    Q3: 我想在 PR 的时候也构建镜像(但不推送),怎么做?

    上面的配置已经支持了。关键在
    1. push: ${{ github.event_name != 'pull_request' }}
    复制代码
    ,PR 触发时 push 为 false,只构建不推送。这样可以在合并前验证 Dockerfile 有没有写坏。

    小结

    今天我们从零搭建了一条完整的 Docker CI/CD 流水线:

  • 基础 workflow:代码推送自动构建、自动推镜像
  • 多架构构建:一份 Dockerfile 同时产出 x86 和 ARM 镜像
  • 缓存优化:利用 GitHub Actions Cache 大幅加速构建
  • 生产级配置:先测试再构建,质量门禁一个不少

    自动化的好处不只是省时间。更重要的是一致性——每次构建都在相同的环境下执行,不会再出现"在我机器上没问题"的尴尬。而且整个过程有日志、可追溯、可审计,出了问题能快速定位。

    明天是 Day 23,我们来聊聊私有镜像仓库。不是所有镜像都适合放在公共仓库里,公司内部的业务代码、包含敏感配置的镜像,都需要一个安全的私有仓库来存放。我们会搭建 Harbor 私有仓库,并把它接入今天的 CI/CD 流水线。
  • 您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    扫码关注微信公众号

    Archiver|手机版|小黑屋|风叶林

    GMT+8, 2026-5-8 21:42 , Processed in 0.240149 second(s), 20 queries .

    Powered by 风叶林

    © 2001-2026 Discuz! Team.

    快速回复 返回顶部 返回列表