목표: 빌드 시간↓ / 전송량↓ / 실패율↓ / 배포 속도↑
1) 병목을 먼저 정의
대부분 여기서 터집니다.
- 빌드 캐시 미사용: 매번 gradle deps / npm install 재실행
- Docker Hub rate limit: 베이스 이미지 pull에서 429, 인증 꼬임
- 레이어 설계 구림: 코드 한 줄 바뀌어도 “전체 레이어 재빌드”
- Registry가 멀다/느리다: 온프레미스가 해외 Registry를 매번 pull
- 태그 전략 부재: 롤백/디버깅 불가, “latest”만 씀
해결책은 크게 3축입니다.
- 빌드 최적화 (Dockerfile)
- CI 최적화 (Actions 캐시 + Buildx)
- Registry/배포 최적화 (GHCR 또는 사내 Registry + 미러/프록시)
2) Dockerfile 최적화 (레이어 캐시가 먹게 만들기)
핵심 규칙
- “자주 바뀌는 것”은 아래로, “덜 바뀌는 것”은 위로
- 의존성 설치 레이어를 코드 복사보다 먼저
- 멀티스테이지 + 런타임 슬림화
Spring/Gradle 예시(정석)
# syntax=docker/dockerfile:1.7
FROM gradle:8.7-jdk21-alpine AS builder
WORKDIR /app
# 1) 의존성 캐시용 파일 먼저
COPY gradle gradle
COPY gradlew settings.gradle* build.gradle* ./
RUN --mount=type=cache,target=/home/gradle/.gradle \
./gradlew dependencies --no-daemon || true
# 2) 소스는 나중에
COPY src src
RUN --mount=type=cache,target=/home/gradle/.gradle \
./gradlew bootJar --no-daemon -x test
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
ENTRYPOINT ["java","-jar","/app/app.jar"]# syntax=docker/dockerfile:1.7
FROM gradle:8.7-jdk21-alpine AS builder
WORKDIR /app
# 1) 의존성 캐시용 파일 먼저
COPY gradle gradle
COPY gradlew settings.gradle* build.gradle* ./
RUN --mount=type=cache,target=/home/gradle/.gradle \
./gradlew dependencies --no-daemon || true
# 2) 소스는 나중에
COPY src src
RUN --mount=type=cache,target=/home/gradle/.gradle \
./gradlew bootJar --no-daemon -x test
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
ENTRYPOINT ["java","-jar","/app/app.jar"]
포인트:
- BuildKit의 --mount=type=cache가 Gradle 캐시를 CI에서도 활용 가능하게 해줌.
- dependencies 레이어가 살아있으면 코드만 바뀔 때 빌드가 훨씬 빨라짐.
3) GitHub Actions 최적화 (Buildx + Registry Cache)
“진짜” 체감 나는 2개
- docker/build-push-action + cache-to/cache-from
- metadata-action으로 태그/라벨 표준화
예시: GHCR(추천)로 푸시 + Registry 캐시
name: build-and-push
on:
push:
branches: [ "main" ]
permissions:
contents: read
packages: write
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=sha
type=raw,value=latest
type=ref,event=branch
- name: Build & Push (with registry cache)
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=maxname: build-and-push
on:
push:
branches: [ "main" ]
permissions:
contents: read
packages: write
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=sha
type=raw,value=latest
type=ref,event=branch
- name: Build & Push (with registry cache)
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=max
왜 GHCR 추천?
- Docker Hub 대비 rate limit 스트레스가 훨씬 적고, GitHub Actions와 권한/토큰이 자연스럽게 연결됨.
- 사내/개인 프로젝트에 특히 안정적.
4) Registry 선택 전략 (온프레미스 배포 기준)
A안) GHCR 사용 (가장 간단, 운영 부담 최소)
- CI → GHCR push
- 온프레미스 서버 → GHCR pull
단점: 사내 네트워크가 해외로 나가야 함(보안 정책에 따라 막힐 수 있음)
B안) 사내 Registry 운영 (가장 빠름, 가장 통제 가능)
- 온프레미스 내부에 registry:2 띄우고
- CI는 거기로 푸시하거나, 내부가 외부 pull을 캐시하도록 구성
5) 온프레미스 “Pull-through Cache”로 속도/안정성 올리기
외부 Registry(Docker Hub, GHCR 등)를 매번 땡기지 말고 사내 Registry를 캐시 프록시로 쓰는 방식.
docker registry(2) 프록시 설정 예시
/opt/registry/config.yml
version: 0.1
proxy:
remoteurl: https://registry-1.docker.io
storage:
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
proxy:
remoteurl: https://registry-1.docker.io
storage:
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
실행:
docker run -d --name registry \
-p 5000:5000 \
-v /opt/registry/config.yml:/etc/docker/registry/config.yml \
-v /opt/registry/data:/var/lib/registry \
registry:2
-p 5000:5000 \
-v /opt/registry/config.yml:/etc/docker/registry/config.yml \
-v /opt/registry/data:/var/lib/registry \
registry:2
이제 온프레미스 Docker daemon에 “미러”로 지정하면,
베이스 이미지 pull이 내부 캐시로 빨라지고 rate limit도 완화됩니다.
/etc/docker/daemon.json
{
"registry-mirrors": ["http://YOUR_REGISTRY_IP:5000"]
}
"registry-mirrors": ["http://YOUR_REGISTRY_IP:5000"]
}
sudo systemctl restart docker
6) 태그/롤백 전략 (DevOps 관점 필수)
최소 3종은 고정으로 가세요.
- :sha-<gitsha> (배포/롤백의 기준)
- :latest (편의용, 운영 기준 X)
- :main 또는 :release-YYYYMMDD (환경별 트래킹)
운영 배포는 “sha 태그” 또는 “digest 고정”이 안전합니다.
- docker pull ghcr.io/org/app@sha256:... (변경 불가)
7) Registry 운영 최적화(사내 운영 시)
- 디스크: 레지스트리 데이터는 SSD 권장
- 정리:
- 오래된 태그 삭제 정책
- garbage-collect(레지스트리 GC) 주기 운영
- 네트워크:
- 온프레미스 내부망에서 접근되게 배치
- 가능하면 reverse proxy(Nginx)로 TLS 종단 + 인증
8) 체크리스트 (바로 적용)
- Dockerfile 레이어 재구성 (의존성 먼저)
- Actions: Buildx + cache-to/cache-from 적용
- Registry: GHCR로 이동(또는 사내 registry 구축)
- 온프레미스: pull-through cache 또는 registry mirror 적용
- 태그: sha 기반으로 배포/롤백 체계 고정
LIST
'DevOps & Infra' 카테고리의 다른 글
| React(Vite)가 JSP보다 Reload가 빠른 이유 (0) | 2026.02.25 |
|---|---|
| Claude Code + 온프레미스 서버에서 IaC 적용하는 법과 효율성 (0) | 2026.02.25 |
| GitHub Actions로 온프레미스 서버 자동배포하기 (0) | 2026.02.25 |
| Infrastructure as Code (IaC)란 무엇인가? (0) | 2026.02.25 |
| DevOps란 무엇인가? (0) | 2026.02.25 |
