GitHub Actions + Docker Buildx로 레이어 캐시 적용하기 – CI 빌드 시간 70% 단축하기
2025. 11. 26. 20:24ㆍInfra
Docker 레이어 캐시를 GitHub Actions에서 Buildx와 ECR를 활용해 적용한 과정을 공유하려고 합니다. 특히, 의존성 설치 단계를 분리해 캐시 히트율을 극대화하고, 빌드 시간을 70% 이상 단축한 사례를 소개합니다.
Docker 레이어 캐시가 필요한가?
Docker는 레이어 기반으로 이미지를 빌드합니다. RUN, COPY, ADD 등의 명령어는 각각 하나의 레이어를 생성하고, 이 레이어가 변경되지 않으면 재사용(캐시)됩니다.
하지만 CI 환경에서는: • 매번 새로운 runner 사용 → 로컬 캐시 없음 • requirements.txt가 자주 변경되지 않는데도 COPY ./app /app 후 pip install이 매번 실행됨 • 결과적으로 의존성 설치에 3~5분 소요
이걸 해결하기 위해 Docker Buildx의 레지스트리 기반 캐시를 도입했습니다.
1. GitHub Actions에 Buildx 설정 추가
name: Deploy to DEV Amazon ECR and EKS
on:
push:
branches:
- dev
env:
AWS_REGION: ap-northeast-2
ECR_REPOSITORY: test
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
# buildx 설정 추가
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build, tag, and push image
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ steps.tag.outputs.date }}
run: {}
2. 기존 빌드 스텝 → Buildx + 캐시 적용
# 변경 전 (기존 코드)
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f docker/Dockerfile .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
# 변경 후 (Buildx + 레지스트리 캐시)
BRANCH_NAME=$(echo "${{ github.ref_name }}" | sed 's|/|-|g' | sed 's/[^a-zA-Z0-9_.-]//g')
CACHE_TAG="cache-$BRANCH_NAME"
docker buildx build \
--cache-from type=registry,ref=$ECR_REGISTRY/$ECR_REPOSITORY:$CACHE_TAG \
--cache-to type=registry,ref=$ECR_REGISTRY/$ECR_REPOSITORY:$CACHE_TAG,mode=max \
-t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
-f docker/Dockerfile \
--push .
- --cache-from : ECR에 저장된 캐시 레이어를 가져와 재사용
- --cache-to : 빌드 후 캐시를 ECR에 저장 (mode=max → 모든 레이어 저장)
- CACHE_TAG : 브랜치별 캐시 분리 (cache-main, cache-feature-x)
- 브랜치별 캐시 분리로 main 브랜치의 변경이 feature 브랜치 캐시에 영향을 주지 않도록 처리
ECR 캐시 이미지 생성 확인

3. Dockerfile 최적화 – 의존성 설치 레이어 분리
Dockerfile의 레이어 순서는 캐시 효율을 결정합니다. 자주 변경되는 코드는 가장 아래에, 드물게 변경되는 의존성은 위에 배치해야 합니다.
기존 DockerFile
FROM python:3.12.4
ARG DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Seoul
RUN apt-get update \
&& apt-get --no-install-recommends install -yq tzdata nano cron postgresql-client \
&& apt-get autoremove \
&& apt-get clean \
COPY ./app /app
RUN pip install --no-cache-dir -r /app/requirements.txt
## 불필요한 suid, guid 제거, cronjob 관련 이관
...생략
USER appuser
WORKDIR /app
EXPOSE 8000
캐시 hit를 높이기 위한 DockerFile
- app/ 디렉토리 내 어떤 파일이라도 변경되면 pip install 레이어가 무효화됨
FROM python:3.12.4
ARG DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Seoul
RUN apt-get update \
&& apt-get --no-install-recommends install -yq tzdata nano cron postgresql-client \
&& apt-get autoremove \
&& apt-get clean
# 변경 점이 적은 requirements install 부분을 앞에서 처리
COPY app/requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir -r /app/requirements.txt
## 불필요한 suid, guid 제거, cronjob 관련 이관
...생략
# 변경사항이 많은 app copy는 후처리
COPY ./app /app
USER appuser
WORKDIR /app
EXPOSE 8000
- requirements.txt만 먼저 복사
- pip install 실행 → 이 레이어는 requirements.txt가 변하지 않으면 캐시 재사용
- 나중에 전체 app/ 복사 → 코드 변경 시에도 의존성 설치 스킵
빌드 시간 비교
| 조건 | 빌드 시간 | 비고 |
| 변경 전 | 3분 10초~ 4분 10초 | 의존성 매번 설치 |
| 변경 후 | 40초~ 1분 40초 | 캐시 히트 시 |
| 감소율 | 60% ~ 78% |

주의사항 및 팁
- 캐시 태그 충돌 방지 → cache-$BRANCH_NAME으로 브랜치별 분리
- mode=max 사용 시 용량 주의 → 모든 레이어 저장 → ECR 비용 증가 가능 → 필요 시 mode=min 또는 주기적 삭제 정책 고려
- ECR 권한 설정 → GitHub Actions의 IAM 역할에 ecr:BatchGetImage, ecr:BatchCheckLayerAvailability, ecr:PutImage 권한 필요
마무리
Docker 레이어 캐시는 CI/CD 파이프라인 속도를 결정짓는 핵심 요소입니다. 특히 Python 프로젝트처럼 의존성 설치가 무거운 경우, 레이어 순서 조정과 Buildx 캐시는 필수입니다.
GitHub Actions에서 Docker 빌드 속도가 느리다면 고려해보는게 좋습니다.
'Infra' 카테고리의 다른 글
| Docker Desktop for Mac 없이 Docker 사용하기 (0) | 2025.05.07 |
|---|---|
| OpenLens Extensions (0) | 2024.12.12 |
| k8s Failed to get /version for clusterId={clusterId}: Unauthorized (0) | 2024.11.29 |
| MSA 사례 및 사례 별 MSA 적용 절차 정리 (0) | 2024.02.28 |
| Airflow DockerCompose Test (0) | 2023.12.13 |