feat(workflow): 添加 Docker 多平台镜像合并功能 #75
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Docker | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: [ main ] | |
| env: | |
| ALIYUN_REGISTRY: "${{ secrets.ALIYUN_REGISTRY }}" | |
| ALIYUN_NAME_SPACE: "${{ secrets.ALIYUN_NAME_SPACE }}" | |
| ALIYUN_REGISTRY_USER: "${{ secrets.ALIYUN_REGISTRY_USER }}" | |
| ALIYUN_REGISTRY_PASSWORD: "${{ secrets.ALIYUN_REGISTRY_PASSWORD }}" | |
| jobs: | |
| build: | |
| name: Pull | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Before freeing up disk space | |
| run: | | |
| echo "Before freeing up disk space" | |
| echo "==============================================================================" | |
| df -hT | |
| echo "==============================================================================" | |
| # 增加可用磁盘空间 | |
| - name: Maximize build space | |
| uses: easimon/maximize-build-space@master | |
| with: | |
| root-reserve-mb: 2048 | |
| swap-size-mb: 128 | |
| remove-dotnet: 'true' | |
| remove-haskell: 'true' | |
| # 如果空间还是不够用,可以把以下开启,清理出更多空间 | |
| # remove-android: 'true' | |
| # remove-codeql: 'true' | |
| build-mount-path: '/var/lib/docker/' | |
| - name: Restart docker | |
| run: sudo service docker restart | |
| - name: Free up disk space complete | |
| run: | | |
| echo "Free up disk space complete" | |
| echo "==============================================================================" | |
| df -hT | |
| echo "==============================================================================" | |
| - name: Checkout Code | |
| uses: actions/checkout@v4 | |
| - name: Docker Setup Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build and push image Aliyun | |
| run: | | |
| docker login -u $ALIYUN_REGISTRY_USER -p $ALIYUN_REGISTRY_PASSWORD $ALIYUN_REGISTRY | |
| # 数据预处理,判断镜像是否重名 | |
| declare -A duplicate_images | |
| declare -A temp_map | |
| # 记录需要做多平台合并的镜像:key 为“最终目标镜像名”(不带平台前缀),value 为各平台镜像列表 | |
| declare -A manifest_sources | |
| declare -A manifest_count | |
| while IFS= read -r line || [ -n "$line" ]; do | |
| # 忽略空行与注释 | |
| [[ -z "$line" ]] && continue | |
| if echo "$line" | grep -q '^\s*#'; then | |
| continue | |
| fi | |
| # 获取镜像的完整名称,例如kasmweb/nginx:1.25.3(命名空间/镜像名:版本号) | |
| image=$(echo "$line" | awk '{print $NF}') | |
| # 将@sha256:等字符删除 | |
| image="${image%%@*}" | |
| echo "image $image" | |
| # 获取镜像名:版本号 例如nginx:1.25.3 | |
| image_name_tag=$(echo "$image" | awk -F'/' '{print $NF}') | |
| echo "image_name_tag $image_name_tag" | |
| # 获取命名空间 例如kasmweb, 这里有种特殊情况 docker.io/nginx,把docker.io当成命名空间,也OK | |
| name_space=$(echo "$image" | awk -F'/' '{if (NF==3) print $2; else if (NF==2) print $1; else print ""}') | |
| echo "name_space: $name_space" | |
| # 这里不要是空值影响判断 | |
| name_space="${name_space}_" | |
| # 获取镜像名例如nginx | |
| image_name=$(echo "$image_name_tag" | awk -F':' '{print $1}') | |
| echo "image_name: $image_name" | |
| # 如果镜像存在于数组中,则添加temp_map | |
| if [[ -n "${temp_map[$image_name]}" ]]; then | |
| # 如果temp_map已经存在镜像名,判断是不是同一命名空间 | |
| if [[ "${temp_map[$image_name]}" != $name_space ]]; then | |
| echo "duplicate image name: $image_name" | |
| duplicate_images[$image_name]="true" | |
| fi | |
| else | |
| # 存镜像的命名空间 | |
| temp_map[$image_name]=$name_space | |
| fi | |
| done < images.txt | |
| while IFS= read -r line || [ -n "$line" ]; do | |
| # 忽略空行与注释 | |
| [[ -z "$line" ]] && continue | |
| if echo "$line" | grep -q '^\s*#'; then | |
| continue | |
| fi | |
| echo "docker pull $line" | |
| docker pull $line | |
| platform=$(echo "$line" | awk -F'--platform[ =]' '{if (NF>1) print $2}' | awk '{print $1}') | |
| #platform=$(echo "$line" | awk -F'[= ]' '{print $2}' | awk -F'--platform' '{print $2}' | tr -d '[:space:]') | |
| echo "platform is $platform" | |
| # 如果存在架构信息 将架构信息拼到镜像名称前面 | |
| if [ -z "$platform" ]; then | |
| platform_prefix="" | |
| else | |
| platform_prefix="${platform//\//_}_" | |
| fi | |
| echo "platform_prefix is $platform_prefix" | |
| # 获取镜像的完整名称,例如kasmweb/nginx:1.25.3(命名空间/镜像名:版本号) | |
| image=$(echo "$line" | awk '{print $NF}') | |
| # 获取 镜像名:版本号 例如nginx:1.25.3 | |
| image_name_tag=$(echo "$image" | awk -F'/' '{print $NF}') | |
| # 获取命名空间 例如kasmweb 这里有种特殊情况 docker.io/nginx,把docker.io当成命名空间,也OK | |
| name_space=$(echo "$image" | awk -F'/' '{if (NF==3) print $2; else if (NF==2) print $1; else print ""}') | |
| # 获取镜像名例 例如nginx | |
| image_name=$(echo "$image_name_tag" | awk -F':' '{print $1}') | |
| name_space_prefix="" | |
| # 如果镜像名重名 | |
| if [[ -n "${duplicate_images[$image_name]}" ]]; then | |
| #如果命名空间非空,将命名空间加到前缀 | |
| if [[ -n "${name_space}" ]]; then | |
| name_space_prefix="${name_space}_" | |
| fi | |
| fi | |
| # 将@sha256:等字符删除 | |
| image_name_tag="${image_name_tag%%@*}" | |
| new_image="$ALIYUN_REGISTRY/$ALIYUN_NAME_SPACE/$platform_prefix$name_space_prefix$image_name_tag" | |
| echo "docker tag $image $new_image" | |
| docker tag $image $new_image | |
| echo "docker push $new_image" | |
| docker push $new_image | |
| # 仅对带 --platform 的镜像做多平台合并统计 | |
| if [ -n "$platform_prefix" ]; then | |
| base_image="$ALIYUN_REGISTRY/$ALIYUN_NAME_SPACE/$name_space_prefix$image_name_tag" | |
| manifest_sources["$base_image"]="${manifest_sources[$base_image]} $new_image" | |
| manifest_count["$base_image"]=$(( ${manifest_count[$base_image]:-0} + 1 )) | |
| fi | |
| echo "开始清理磁盘空间" | |
| echo "==============================================================================" | |
| df -hT | |
| echo "==============================================================================" | |
| docker rmi $image | |
| docker rmi $new_image | |
| echo "磁盘空间清理完毕" | |
| echo "==============================================================================" | |
| df -hT | |
| echo "==============================================================================" | |
| done < images.txt | |
| echo "开始创建并推送多平台 manifest(如有需要)" | |
| for base_image in "${!manifest_sources[@]}"; do | |
| count=${manifest_count[$base_image]} | |
| if [ "$count" -lt 2 ]; then | |
| echo "跳过 $base_image,仅检测到 $count 个平台" | |
| continue | |
| fi | |
| echo "为 $base_image 创建 manifest, 包含平台镜像: ${manifest_sources[$base_image]}" | |
| docker manifest create "$base_image" ${manifest_sources[$base_image]} | |
| docker manifest push "$base_image" | |
| docker manifest rm "$base_image" || true | |
| done | |
| echo "多平台 manifest 处理完成" | |
| echo "结束" |