작은 이미지의 예술: AI와 Java 컨테이너에서 수백 MB를 절감하는 실용 기술
Source: Dev.to
컨테이너가 커지는 이유
대부분의 팀은 이런 느낌을 잘 안다: 컨테이너가 드디어 정상 작동하고, 모델이 로드되며, JVM이 시작되고, 엔드포인트가 응답한다. 그런데 누군가 이미지 크기를 지적한다—때로는 800 MB 이상이다. 이는 놀라운 일은 아니지만, 큰 크기는 여전히 문제를 일으킨다. 로컬 개발이 느려지고, CI 파이프라인에 압박을 가하며, 시스템이 어떻게 진화하는지 조용히 영향을 미친다.
AI와 Java 컨테이너가 크게 성장하는 이유는 이해할 수 있다. 머신러닝 스택은 네이티브 라이브러리, 컴파일된 확장자를 가진 Python wheel, CUDA 의존성, 테스트 도구 등을 필요로 한다. Java 이미지에는 전체 JDK, 디버깅 도구, 한때는 유용했지만 이제는 정리되지 않은 빌드 산출물이 포함될 수 있다.
일반적인 패턴
- 베이스 이미지 선택 – 팀은 안전하고 익숙하다고 느끼는 Ubuntu나 다른 전체 배포판을 기본으로 선택하는 경우가 많다. 선택한 베이스 이미지가 이후 모든 것에 영향을 미친다.
- 잊혀진 의존성 – 많은 컨테이너에 여전히 테스트 중에 유용했지만 제거되지 않은 라이브러리가 포함되어 있다. Python에서는 전이 의존성이 이미지 크기를 조용히 증가시킬 수 있다. Java에서는 사용되지 않는 모듈이 클래스패스에 남아 있다.
- 레이어 순서 – 컨테이너 레이어를 하나의 이야기처럼 다루라. 각 레이어는 다음 질문에 답해야 한다: 왜 여기 있나요? 언제 필요했나요? 아직도 필요하나요?
실용적인 기법
이미지 크기를 줄이는 것은 보통 하나의 큰 수정보다는 여러 작은 변경에서 비롯된다:
- 패키지 캐시 정리 – 설치 후
apt/yum캐시,pipwheel, 기타 임시 파일을 제거한다. - 재사용을 위한 레이어 재배치 – 변경이 거의 없는 레이어(예: 베이스 OS, 언어 런타임)를 앞쪽에 배치해 빌드 간 캐시가 활용되도록 한다.
- 심볼 스트립 – 바이너리와 라이브러리에
strip을 사용해 디버그 심볼을 제거한다. - 런타임 전용 이미지 사용 – Java는 전체 JDK 대신 JRE나
jlink로 만든 최소 런타임을 사용한다. Python은 슬림 베이스 이미지를 사용하고 런타임 의존성만 설치한다. - 멀티‑스테이지 빌드 – 중간 단계에서 코드를 컴파일하고 자산을 빌드한 뒤, 최종 단계에서는 최종 산출물만 최소 이미지에 복사한다.
- 빌드 산출물 명시적 제외 –
.dockerignore에 소스 파일, 테스트 스위트, 런타임에 필요 없는 문서 등을 추가한다. - 언어별 도구 활용 – Java는
jlink나jpackage같은 도구로 커스텀 런타임을 만든다. Python은pip install --no-cache-dir와pip freeze를 사용해 필요한 패키지만 고정한다.
작은 이미지의 장점
- 빠른 개발 사이클 – 이미지 풀링, 빌드, 푸시가 더 빨라진다.
- CI 부하 감소 – 레이어가 작아지면 저장소와 대역폭 사용량이 줄어든다.
- 명확한 가정 – 최소 이미지는 런타임에 무엇이 필요한지 명확히 보여주어 호기심과 의도적인 설계를 촉진한다.
- 보안 향상 – 패키지가 적을수록 공격 표면이 줄어들고 취약점 스캔이 간단해진다.
- 자원 활용 효율 – 작은 이미지는 노드의 디스크 공간을 덜 차지하고 오케스트레이션 플랫폼에서 더 빠르게 시작될 수 있다.
설계 습관 기르기
AI와 Java 컨테이너에서 수백 메가바이트를 절감하는 것은 단순히 성능 문제만이 아니다; 이는 인내, 호기심, 그리고 더 이상 맞지 않는 오래된 결정을 재고하려는 의지를 중시하는 설계 습관이다. 진정한 기술은 “이미 충분히 좋다”고 생각되는 것을 언제 재검토해야 하는지를 알고, 이러한 점진적인 개선을 꾸준히 적용하는 데 있다.