jjunhub

docker buildx 빌드 시간 단축하기 본문

Trouble Shooting

docker buildx 빌드 시간 단축하기

jjunhub 2025. 2. 8. 10:09

부제 : 조건에 맞을 땐, buildx 대신 build --platform 쓰자

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             



상황

AWS ECR에 ARM64, AMD64 두 플랫폼에서 각각 사용할 수 있는 이미지를 올리고자 하였다.

dev용 ec2는 t2.micro ← x86 = AMD64

prod용 pod는 t4 이상 ← ARM64

 

문제 코드

name: Deploy dev server

on:
  push:
    branches:
      - "develop"
  pull_request:
    branches:
      - "develop"
  workflow_dispatch:

jobs:
  build:
    name: Build Java Project With Dockerfile
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout the repo
        uses: actions/checkout@v4

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

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

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
            aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
            aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
            aws-region: ap-northeast-2

      - name: Login to Amazon ECR
        run: |
            aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com
      - name: Build and push Docker image to Amazon ECR with Tag
        run: |
          COMMIT_SHA=$(git rev-parse --short HEAD)
          docker buildx build \\
            --platform linux/amd64,linux/arm64 \\
            -t ${{ secrets.AWS_ACCOUNT_ID }}/${{ secrets.ECR_REPOSITORY }}:$COMMIT_SHA  \\
            -t ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com/${{ secrets.ECR_REPOSITORY }}:$COMMIT_SHA  \\
            --push .

해당 코드는 buildx 도구를 불러와서 2가지 OS에 대한 이미지를 만드는 코드이다.

 

문제점

buildx 도구는 진~짜 많은 플랫폼에 대해서 빌드할 수 있게 설정하여 가져온다.

따라서 이 docker buildx 명령어를 사용하는 순간 엄청나게 오래 걸릴 수 밖에 없는 구조이다.

 

문제 해결

문제가 되는 buildx를 쓰지말자!가 목표였다. 이를 위해서 docker build를 직접 2차례 진행하였다. arm64용으로 한 번( for MAC OS ), amd64용으로 한 번(for Ubuntu) 그리고 나서 이 두 개의 이미지를 한 개의 manifest로 묶어서 Index 처리를 해주었다.

아래 코드를 통해서 문제 해결을 시도했다.

name: Deploy dev server

on:
  push:
    branches:
      - "develop"
  pull_request:
    branches:
      - "develop"
  workflow_dispatch:

jobs:
  build:
    name: Build Java Project With Dockerfile
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout the repo
        uses: actions/checkout@v4

      - name: Set commit SHA
        run: echo "COMMIT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-2

      - name: Login to Amazon ECR
        run: |
          aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com

      - name: Build and push Docker image for amd64
        run: |
          docker build --platform linux/amd64 \\
            -t ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com/${{ secrets.ECR_REPOSITORY }}:${{ env.COMMIT_SHA }}-amd64 .
          docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com/${{ secrets.ECR_REPOSITORY }}:${{ env.COMMIT_SHA }}-amd64

      - name: Build and push Docker image for arm64
        run: |
          docker build --platform linux/arm64 \\
            -t ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com/${{ secrets.ECR_REPOSITORY }}:${{ env.COMMIT_SHA }}-arm64 .
          docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com/${{ secrets.ECR_REPOSITORY }}:${{ env.COMMIT_SHA }}-arm64

      - name: Create and push Docker manifest
        run: |
          docker manifest create ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com/${{ secrets.ECR_REPOSITORY }}:dev-${{ env.COMMIT_SHA }} \\
            --amend ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com/${{ secrets.ECR_REPOSITORY }}:${{ env.COMMIT_SHA }}-amd64 \\
            --amend ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com/${{ secrets.ECR_REPOSITORY }}:${{ env.COMMIT_SHA }}-arm64

          docker manifest push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com/${{ secrets.ECR_REPOSITORY }}:dev-${{ env.COMMIT_SHA }}

위 코드로 빌드 시에, ECR에 다음과 같이 index가 생긴다.

 

결과

6분 54초에서 1분 48초로 약 70프로 시간이 단축되었다.

 

 

개선 방식의 문제점 & 해결책

이렇게 여러 번 테스트 하면서 느낀 점은.. ECR 용량이 금방 터지겠구나였다. 하나의 이미지가 여러 플랫폼을 지원할 수 있도록 제공하는 것이 아니기 때문에 반드시 2배 이상이 소요되기 때문이다.

 

따라서 이를 위해서 ECR LifeCycle 설정까지 수행하였다.

이는 ECR 이미지 개수들을 내가 작성한 정책(조건)에 맞춰서 관리해주는 설정이다.

위처럼 설정하여 dev- 접두사가 붙은 이미지 세트들에 대해서 2개씩만 존재하도록 설정하였다. dev- 접두사를 붙인 이유는 prod- 접두사를 따로 붙여서, dev 이미지를 여러 번 배포하더라도 이와 별개로 prod- 이미지를 유지하기 위해서이다.

 

느낀점

의외로 무식한 방법이 제일 빠르다!

 

추가로..

Q : 왜 도커에서 buildx가 주로 사용되는가? 그리고 왜 여기선 사용하지 않았는가?

A : buildx를 사용하면 거의 대부분의 크로스 플랫폼 빌드, 병렬 빌드, 캐시 개선 등의 기능을 제공받을 수 있다. 하지만 buildx 도구를 위해 불러오는 것만으로도 overhead가 엄청나게 큰 편이다. 이번 프로젝트의 경우에는 AMD64, ARM64 딱 2가지의 OS만 처리하면 돼서 이를 기본으로 제공하는 docker —platform 옵션으로 해결하는 것이 최적의 상황으로 느껴진다.