作者 青鸟
最近的实习工作中,遇到了一个有意思的需求,特此记录一下,github action自动构建镜像的方案

问题

传统的github action构建镜像的场景针对的场景是固定的Dockerfile和镜像名称。但是我们的需求是针对每一个push或者pr的Dockerfile自动构建出相应的镜像。于是乎我们需要从commit中提取出Dockerfile的路径与构建镜像的名称,并自动构建与推送镜像。

解决

我们可以编写一个shell脚本,使用git diff提取出变化或者添加了的Dockerfile文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash

# 获取变更的文件列表
DIFF_OUTPUT=$(git diff --name-only "$1" "$2"|grep Dockerfile)

# 提取上一级目录
PARENT_DIRS=()
FILE_PATHS=()
for FILE_PATH in $DIFF_OUTPUT; do
if [[ ! -f "$FILE_PATH" ]]; then
echo "File $FILE_PATH does not exist, skipping."
continue
fi
PARENT_DIR=$(dirname "$FILE_PATH" | awk -F'/' '{print $(NF)}')
PARENT_DIRS+=("$PARENT_DIR")
FILE_PATHS+=("$FILE_PATH")
done

# 将数组转换为字符串
PARENT_DIRS_STRING=$(IFS=,; echo "${PARENT_DIRS[*]}")
DIFF_OUTPUT_STRING=$(IFS=,; echo "${FILE_PATHS[*]}")
echo "PARENT_DIRS=$PARENT_DIRS_STRING"
echo "DIFF_OUTPUT=$DIFF_OUTPUT_STRING"

# 输出到环境变量
echo "PARENT_DIRS=$PARENT_DIRS_STRING" >> $GITHUB_ENV
echo "DIFF_OUTPUT=$DIFF_OUTPUT_STRING" >> $GITHUB_ENV

上述脚本在获取了文件之后将变化了的Dockerfile的文件路径与上一级目录的名字存入了环境变量中,之后就是构建镜像了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash

# 打印环境变量以进行调试
echo "PARENT_DIRS=$PARENT_DIRS"
echo "DIFF_OUTPUT=$DIFF_OUTPUT"

# 将环境变量读取为数组
IFS=',' read -r -a DIFF_OUTPUT_ARRAY <<< "$DIFF_OUTPUT"
IFS=',' read -r -a PARENT_DIRS_ARRAY <<< "$PARENT_DIRS"

# 打印数组内容以进行调试
echo "DIFF_OUTPUT array: ${DIFF_OUTPUT_ARRAY[@]}"
echo "PARENT_DIRS array: ${PARENT_DIRS_ARRAY[@]}"

# 构建并推送每个Docker镜像
for i in "${!DIFF_OUTPUT_ARRAY[@]}"; do
DOCKERFILE_PATH=${DIFF_OUTPUT_ARRAY[$i]}
PARENT_DIR=${PARENT_DIRS_ARRAY[$i]}
TAG="$PARENT_DIR:latest"
echo "Building and pushing image for $DOCKERFILE_PATH with tag $TAG and user as $DOCKER_USERNAME"
docker buildx build --push \
--file $DOCKERFILE_PATH \
--platform linux/amd64\
--tag "$DOCKER_USERNAME/$TAG" \
.
done

这样子变可以通过环境变量中的文件路径与上一级目录名来构建镜像

完整的github-action文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
name: Build Docker Images

on:
push:
branches: ['main']

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
directory: [system]
name: [devbox-system]

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Get changed files
id: getfile
run: |
chmod +x script/get_changed_files.sh
script/get_changed_files.sh ${{ github.event.before }} ${{ github.sha }}
shell: bash

- name: Echo output
run: |
echo "Changed files: ${{ env.DIFF_OUTPUT }}"
echo "Parent directories: ${{ env.PARENT_DIRS }}"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push Docker image
run: |
chmod +x script/build_and_push_images.sh
script/build_and_push_images.sh
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
shell: bash

这样子我们就可以在每一次push的时候自动构建镜像并推送到docker hub中了。