使用 GitHub Actions 自动构建 Docker 镜像
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
目录
介绍
当我发布新版本的 Web 应用程序时,我遵循了以下流程,这听起来可能对您来说并不陌生:
- 我已将所有内容合并到主分支。
- 通过 SSH 连接到部署服务器
- git fetch
- git checkout
- git pull
- 构建 Docker 镜像(这非常耗时,而且一旦 Web 应用程序足够大,就会失败)
- 这样我就需要在我的电脑或其他特定服务器上构建镜像,以避免生产服务器崩溃。
- 将其推送到注册表
- 返回服务器并从注册表中提取数据。
- 意识到你没有更新环境变量
- 啊啊啊啊啊啊啊!!!

我现在是这样做的:
- 推送一个新的语义化版本控制(semver)发布标签。例如,
1.0.1从我喜欢的任何分支推送。 - 就是这样!
这与完美的 Git 策略完美契合。
设置自托管运行程序
如果您已经了解 GitHub Actions 的所有功能,只想了解如何构建 Docker 镜像,请继续阅读“GitHub Actions 构建 Docker 镜像”部分。
定义:运行器是一个实例,它可以在你的 GitHub Action 中运行任何你想要执行的操作。从打印输出Hello World到构建和部署应用,甚至帮你煮咖啡(真的!),它都能做到。你可以把它想象成一个用户,它可以在远程服务器(Linux、Windows 等)上运行你指定的任何操作。
GitHub 为您提供 2 个选项:
- 每月可免费使用他们的跑步机最多 X 分钟,之后再付费。
- 搭建一个自托管的运行环境并免费使用。
如果你选择第一个选项,请跳过此部分;否则,请继续阅读。
由于我已经有了家庭实验室,而且搭建运行环境也很容易,所以自托管是省钱的好办法。你也可以用旧电脑,甚至是闲置的树莓派来搭建运行环境。
如果您想了解更多关于自托管运行器的信息,请查看GitHub 的文档。
以下是搭建自托管运行环境所需的内容:
- 选择你想要运行的操作系统。我个人比较喜欢Ubuntu,它几乎可以满足我的所有需求,所以我选择了Linux系统。
- 请确保运行器中已安装所有必要的软件包。就我的使用场景而言,我需要先安装 Docker。
- 前往您的 GitHub 代码库,然后点击“设置”。

- 在左侧面板中,点击“操作”,然后点击“跑者” 。

- 选择新的自托管 Runner

- 选择您要使用的操作系统和架构

- 请按照您所选内容下方显示的终端命令完成运行器的设置。由于 GitHub 会不时更新这些命令,因此我不想在此处发布它们,请直接按照 GitHub 上的说明操作即可。
- 在 Linux 机器上完成运行程序的设置后,您应该可以在之前的运行程序页面中看到它,就可以使用它了!

GitHub Action 的结构
GitHub 操作定义在.yml文件中。它们本质上是一组指令,告诉运行器该做什么。
它们存在.github/workflows/于您的存储库中,一旦您将它们推送到您的主分支,它们就会生效并正常工作。
你可以直接使用 GitHub 的网页界面或你喜欢的代码编辑器编写代码,然后将其推送到你的主分支。
如果你使用 VSCode,它有一个 GitHub Actions 扩展程序,可以非常方便地编辑工作流并查看正在运行的程序。
一个简单的 GitHub action 由 4 个主要部分组成:
1.name
很简单,这只是你想给这个操作起的名字,以便以后跟踪它。
name: My GitHub action name
2.on
这就是你想要触发操作的条件。最常见的情况是,当代码推送到特定分支或发起拉取请求时,触发某个操作。但触发条件还有很多。
您可以在这里查看所有触发器的文档:
https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
以下是一个示例代码:
- 能够手动触发 GitHub 操作。有关如何从 GitHub Web 界面触发此操作的更多信息,请参阅“手动运行工作流”。
- 当有推送到
my-branch-nameandmy-other-branch-name分支时触发该操作。
on:
# Trigger the action manually from the UI
workflow_dispatch:
# Trigger the action when pushing to certain branches
push:
branches:
- 'my-branch-name'
- 'my-other-branch-name'
3.env
您可以在此处为特定操作设置环境变量和密钥。这可以与 GitHub 网站上的仓库变量和密钥功能结合使用。
如果你在多个步骤或作业中使用某个东西,最好在这里进行设置,这样你只需要更改一行代码即可。
env:
DOCKER_IMAGE_NAME: my-image
4.jobs
这些是您要执行的各个步骤。每个作业都包含一系列顺序步骤,并且每个作业都可以异步运行。您还可以创建依赖关系,以便某个作业只有在前一个作业完成后才会运行。
工作和步骤之间的区别
- 作业是独立的、异步的任务。这意味着,如果您有多个可以异步(同时)执行的任务(例如:部署应用程序和上传新文档),您可以将它们放在不同的作业中。如果您有多个运行器,则可以同时执行多个作业。
- 步骤是指同步执行的操作。如果您有需要按顺序执行的任务(例如:构建 Docker 镜像并将其推送到 Docker 镜像仓库),请将它们设置为步骤而不是作业。
示例代码:
jobs:
build_docker_images:
# Job name that shows in the GitHub UI
name: Build Docker Images
# Runner to use
runs-on: self-hosted
steps:
- name: Checkout
uses: actions/checkout@v3
...
您可以在单个操作文件中设置任意数量的作业和步骤。
GitHub Action 构建 Docker 镜像
在本节中,我将向您展示如何从 A 构建 Docker 镜像Dockerfile并将其推送到 Docker Hub 或您的私有 Docker 镜像仓库。所有这一切都只需创建一个版本标签即可完成。
流程概述及要求
首先,如果您使用的是自托管运行器,请检查它是否正在运行,并且是否在您想要部署此功能的存储库中可用。同时,请确保您了解GitHub action 的结构。
以下是具体要求:
当我推送版本标签时,我想获取版本名称(例如1.0.1:),然后构建 Docker 镜像并使用该版本号以及latest标签(例如:my-image:1.0.1和my-image:latest)对其进行标记。
之后,我想把镜像推送到我自托管的私有 Docker Registry,并从运行器中删除所有数据。不过,你也可以这样做,将 Docker 镜像推送到 DockerHub。
我还想存储构建缓存,以便将来构建镜像时速度更快。
以下是流程概述:
- 手动触发操作,或通过推送版本标签触发操作。
- 使用 Git 检出该特定标签的代码
- 从标签中获取版本号
- 构建 Docker 镜像
- 镜像构建完成后,用版本号和我选择的 Docker 镜像仓库地址标记它。
- 将镜像推送到注册表
1. 环境变量和密钥
环境变量
为了方便日后需要更改注册表 URL 或镜像名称,我设置了以下环境变量:
# Workflow environment variables
env:
DOCKER_IMAGE_NAME: my-image
DOCKER_REGISTRY_URL: myregistry.domain.com
秘密
我还想在我的 reo 中创建这些 GitHub Action Secret,以便能够访问 Docker 镜像仓库。我喜欢在工作流文件中添加注释,这样如果我移动了仓库或其他什么操作,就不会忘记了。
看在上帝的份上,请不要把这些内容放到你的工作流程文件中。
# Required actions secrets:
# - DOCKER_USERNAME: Username for docker registry login
# - DOCKER_PASSWORD: Password for docker registry login
2. 触发器
我希望不仅在推送版本标签时触发该操作,而且手动操作也能触发该操作。
name: Build release Docker image
on:
# Trigger the action manually from the UI
workflow_dispatch:
# Trigger the action when a version tag is pushed
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+' # Push events to matching numeric semver tags, i.e., 1.0.0, 20.15.10
4. 作业设置
我只需要一份工作,因为我的所有步骤都需要按顺序进行。
jobs:
build_docker_images:
# Job name that shows in the GitHub Website:
name: Build Docker Images
# Runner to use:
runs-on: self-hosted
# Begin the steps
steps:
- step1
- step2
- ...
4.1 从 GitHub 获取代码
GitHub 和 Docker 等许多其他服务都提供了现成的步骤。在本例中,我们将使用actions/checkout@v3GitHub 提供的步骤,该步骤会从代码仓库中获取代码。
# Checkout the code
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Do not get extra git branches to save time
4.2 设置 Docker 构建器
与 GitHub 类似,Docker 也提供了一系列现成的步骤,在本例中,其中包含一个构建器设置操作。
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
4.3 登录 Docker Registry
- name: Login to Docker Registry
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
4.4 获取版本号
这里我只获取了标签名称(X.Y.Z)。由于我们将触发器限制为仅对数字语义版本标签触发,因此我们可以确信标签的格式是正确的:X.Y.Z。
- name: Get the tag name
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
4.5 构建并推送 Docker 镜像
Docker 为此步骤提供了一个非常方便的即用型操作。
- name: Build and Push the Docker Image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.get_version.outputs.VERSION }}
${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:latest
cache-from: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache
cache-to: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache,mode=max
由于这一步并不那么直接,让我解释一下:
context: .我们指示构建器使用仓库的根目录作为构建上下文。例如,如果您有一个包含后端和前端目录的单体仓库,这将非常有用。您可以为每个目录创建两个作业,并在它们的上下文中切换。file: ./Dockerfile我们正在告诉构建器 Dockerfile 的位置push: true我们也想推广这些图片。- 标签部分表明我们要推送此镜像的两个标签。例如,如果我们想推送到多个目录,这也很有用。这里我使用了在工作流程开始时定义的环境变量,并推送了版本标签和最新标签。
cache-from:这意味着我们从哪里获取构建缓存,以加快后续构建速度。在本例中,我们从注册表中获取。cache-to:与此类似chache-from,我们指示构建缓存应该存储在哪里,在本例中是注册表。
用于发布 Docker 镜像的完整 GitHub Actions 模板
以下是完整的操作代码。您可以将其复制粘贴到您的 YML 文件中,并根据需要进行修改:
# This workflow will build a Docker image
# and push them to a private docker registry when a release tag (i.e.: 1.0.1)
# is pushed to the repository.
#
# It runs on a self hosted runner.
# You can change the runner to 'ubuntu-latest' if you want to use a GitHub hosted runner.
#
# Required actions secrets in the repository:
# - DOCKER_USERNAME: Username for docker registry login
# - DOCKER_PASSWORD: Password for docker registry login
# Script environment variables
env:
DOCKER_IMAGE_NAME: my-image
DOCKER_REGISTRY: myregistry.mydomain.com
name: Build and Push Docker Image
# Triggers
on:
# Trigger the action manually from the UI
workflow_dispatch:
# Trigger the action when a version tag is pushed
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+' # Push events to matching numeric semver tags, i.e., 1.0.0, 20.15.10
jobs:
build-and-push-landing-page:
name: Build and Push Landing Page Docker Image
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Registry
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Get the tag name
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Build and push Landing Page Docker image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.get_version.outputs.VERSION }}
${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:latest
cache-from: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache
cache-to: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache,mode=max
结论
通过这种设置,您现在可以自动获得可在您选择的镜像仓库中使用的 Docker 镜像。
我将在后续文章中介绍如何在 Docker 镜像构建完成并推送到镜像仓库后自动部署该镜像。
文章来源:https://dev.to/onticdani/automatically-build-docker-images-with-github-actions-3n8e
