Skip to content

caminobelllo/Docker-Image-Optimization

 
 

Repository files navigation

🐳 Docker 이미지 최적화

배포 파이프라인의 속도와 이미지 크기를 고려한 4단계 Docker 이미지 최적화 전략을 적용
Jenkins CI/CD 파이프라인으로 단계별 수치 자동 측정


📋 목차



👥 멤버

서가영
@caminobelllo
박성준
@Sungjun24s
이채유
@chaeyuuu

⚙️ 기술 스택

실행 환경 구성

소스 코드

형상 관리 & 협업 도구


🗂️ 프로젝트 구조

docker-optimization-project/
├── Dockerfile                  # 최종 최적화 이미지 (Step 4: 레이어 캐시 최적화)
├── docker/
│   ├── dockerfile.naive        # 최적화 전 베이스라인
│   ├── dockerfile.step01       # Step 1: 베이스 이미지 교체
│   └── dockerfile.step03       # Step 2: 멀티 스테이지 적용
├── .dockerignore               # 빌드 컨텍스트 필터링
└── src/

🚀 최적화 전략 — 4단계

두 가지 측면에서 최적화를 진행하였습니다.
1. 이미지 용량
2. 속도


📌 최적화 전 (Naive)

빌드 도구, 소스코드, JDK가 전부 최종 이미지에 포함된 상태

FROM eclipse-temurin:17-jdk
WORKDIR /app
COPY . .
RUN ./gradlew build -x test
CMD ["java", "-jar", "build/libs/docker-optimization-0.0.1-SNAPSHOT.jar"]

📈 빌드 결과

image image

➡️ 최적화 전 docker image 크기 : 832MB


📦 이미지 크기 최적화

1️⃣ 베이스 이미지 교체

기존 jdk를 Alpine 기반으로 교체해 이미지 크기 감소

# Before
FROM eclipse-temurin:17-jdk        # Ubuntu 기반
 
# After
FROM eclipse-temurin:17-jdk-alpine  # Alpine 기반
image image

➡️ 베이스 이미지 교체 전 후 이미지 크기 비교

Before After
이미지 크기 832MB 746B

2️⃣ 멀티스테이지 빌드

빌드 환경과 실행 환경을 분리하여 최종 이미지에 JAR 파일만 포함

FROM eclipse-temurin:17-jdk-alpine AS builder
WORKDIR /app
COPY . .
COPY application.yml src/main/resources/application.yml
RUN chmod +x ./gradlew && ./gradlew build -x test

FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
 

최종 이미지에 Gradle, JDK, 소스코드 미포함

image image
Before After
이미지 크기 832MB 247MB

✅ 최적화 방법에 따른 이미지 크기 비교

fileSize


⚡ 속도 최적화

3️⃣ .dockerignore 추가

  • docker build 실행 시 불필요한 파일을 빌드 컨텍스트에서 제외
  • .dockerignore 없이 COPY . . 사용 시 .git/ · build/ 등 전체 전송
.git/
build/
.idea/
*.md
Dockerfile*
docker-compose*
Jenkinsfile
image

➡️ 멀티 스테이지 빌드를 진행한 경우와 이미지 용량 크기는 동일하나, docker push 속도 27s 감소


4️⃣ 레이어 캐시 최적화

  • Docker는 레이어 변경 시 이하 레이어를 전부 재실행
  • 변경 빈도가 낮은 의존성을 위쪽, 소스코드를 아래쪽 레이어에 배치
# Before: 소스 변경 시 의존성 전체 재다운로드
COPY . .
RUN ./gradlew build

# After: 의존성 레이어 캐시 유지
COPY gradlew .
COPY gradle gradle
COPY build.gradle settings.gradle ./
RUN ./gradlew dependencies --no-daemon
COPY src src
RUN ./gradlew bootJar --no-daemon -x test
image

➡️ 코드 변경 없이 재빌드를 했을 때, 캐싱되어 있던 기존 레이어를 재사용하므로 빌드 속도 약 30배 증가 (1m 32s -> 3s)


✏️ Stage별 상세 동작

Stage 2 — Build JAR

chmod +x gradlew
START=$(date +%s)
./gradlew bootJar --no-daemon -x test
END=$(date +%s)
echo "▶ Gradle 빌드 소요 시간: $((END - START))"

빌드 성공 시 build/libs/*.jar 파일 크기 자동 출력


Stage 3 — Docker Build

START=$(date +%s)
docker build \
    -t ${IMAGE_NAME}:${IMAGE_TAG} \
    -t ${IMAGE_NAME}:latest \
    -f Dockerfile .
END=$(date +%s)
echo "▶ Docker 빌드 소요 시간: $((END - START))"
 
# 이미지 크기 출력
docker images ${IMAGE_NAME}:${IMAGE_TAG} \
    --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}'

Stage 4 — Docker Push

# Jenkins Credentials에 저장된 Docker Hub 계정으로 로그인
echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
docker push ${IMAGE_NAME}:${IMAGE_TAG}
docker push ${IMAGE_NAME}:latest

Stage 5 — Deploy

# 기존 컨테이너 중지 후 새 버전으로 재실행
docker stop ${CONTAINER_NAME} || true
docker rm   ${CONTAINER_NAME} || true
 
docker run -d \
    --name ${CONTAINER_NAME} \
    --restart unless-stopped \
    -p ${APP_PORT}:8080 \
    -e DB_HOST=172.17.0.1 \
    -e DB_USERNAME=${DB_USER} \
    -e DB_PASSWORD=${DB_PASS} \
    ${IMAGE_NAME}:${IMAGE_TAG}

DB 계정 정보는 Jenkins Credentials에 저장 후 주입 (db-username, db-password)


🏁 결론

단계별 측정 결과

Jenkins 파이프라인에서 자동 측정한 실제 수치

단계 이미지 크기 재빌드 시간 개선 포인트
Naive 832MB 2m 39s 베이스라인
베이스 이미지 교체 746MB 1m 36s 크기 감소
멀티스테이지 빌드 247MB 1m 33s 크기 감소
.dockerignore 247MB 1m 32s (특이사항 : docker push 시간 : 20s) 컨텍스트 정리
레이어 캐시 최적화 247MB 3s 재빌드 속도

최적화 전/후 핵심 수치

Naive 최종 최적화 개선율
이미지 크기 832MB 247MB 70.3% ↓
소스 변경 시 재빌드 2m 39s 3s 98.1% ↓

‼️ 트러블슈팅

1. application.yml 복사 권한 오류

cp: cannot create regular file './application.yml': Permission denied

원인

이전 빌드에서 root 권한으로 application.yml 이 생성된 후 jenkins 유저가 덮어쓸 수 없는 상태

Jenkins 워크스페이스 파일 소유권
application.yml → root 소유 (이전 빌드에서 생성)
jenkins 유저    → 쓰기 권한 없음 → cp 명령어 실패

해결

# 1. 워크스페이스 경로 확인
docker exec jenkins ls /var/jenkins_home/workspace/
 
# 2. 워크스페이스 전체 권한 변경
docker exec -u root jenkins chmod -R 777 /var/jenkins_home/workspace/docker-optimization/
 
# 3. Jenkins에서 다시 빌드 실행

2. Jenkins 컨테이너 내부에서 Docker CLI 사용 불가

해결

# 도커 소켓 마운트를 통한 해결
docker run -d \
  --name jenkins \
  -p 8080:8080 \
  -p 50000:50000 \
  -v jenkins_home:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  jenkins/jenkins:lts

3. Jenkins 컨테이너 내부에서 Docker CLI 접근 거부

해결

# Jenkins 컨테이너 안에서 GID의 그룹을 만들고 Jenkins 사용자를 추가
groupadd -g <docker-sock-gid> docker
usermod -aG docker jenkins

About

도커 이미지 최적화 프로젝트

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Java 43.2%
  • HTML 38.5%
  • CSS 16.6%
  • Dockerfile 1.7%