作者 青鸟 最近的实习工作中,遇到了一个有意思的需求,特此记录一下,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中了。