CI에서 도커 이미지 빌드하는 워크플로우인데, 코드 한 줄만 바꿔도 npm install부터 전부 다시 돕니다. 로컬에선 레이어 캐시 덕에 몇 초면 끝나는데 CI에선 매번 풀빌드라 5분 넘게 걸려요.
워크플로우는 대충 이렇습니다.
- uses: actions/checkout@v4
- run: docker build -t myapp:${{ github.sha }} .러너가 매번 새로 떠서 캐시가 없는 건 알겠는데, 그럼 CI에서 도커 레이어 캐시는 어떻게 재사용하나요? 매번 5분은 좀 아깝네요.
댓글 4
docker/build-push-action 쓰는 게 제일 편해요.
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
push: false
tags: myapp:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-from/to type=gha가 깃허브 액션 캐시 백엔드에 레이어를 저장해서 다음 실행 때 끌어다 씁니다. 이러면 소스만 바뀌었을 때 npm install 레이어는 캐시에서 그대로 재사용돼요.
단, 이게 먹으려면 Dockerfile 레이어 순서가 받쳐줘야 해요. package*.json 먼저 COPY하고 npm ci 한 다음에 소스 전체를 COPY 하는 순서여야, 소스만 바뀌었을 때 의존성 레이어가 캐시 히트합니다. COPY . . 먼저 하고 npm install 하면 파일 하나만 바뀌어도 그 아래가 전부 무효화돼서 캐시 넣어도 소용없어요.
모노레포에서 이미지 여러 개면 scope로 분리하세요.
cache-from: type=gha,scope=api
cache-to: type=gha,mode=max,scope=api
이미지마다 scope를 다르게(api, web, worker 식으로) 주면 서로 캐시를 안 덮어써요. scope 안 주면 다 같은 키를 공유해서 번갈아 무효화되는 참사가 납니다. 이미지 수가 많고 용량 압박이 심하면 아예 캐시를 레지스트리(type=registry, ECR이나 GHCR)로 빼는 게 10GB 제한을 안 받아서 더 안정적이에요. 규모 커지면 그쪽으로 가세요.