가벼운 도커 이미지를 만들고 유지하는 방법 6가지
컨텐츠 정보
- 조회 763
본문
도커 이미지는 세심한 주의를 기울이지 않으면 쓸데없이 커지기 쉽다. 기본 이미지 하나에 RUN 명령 몇 개만 넣었는데 어느새 이미지 크기가 200MB에 이르기도 한다. 이렇게 무거운 이미지가 정말 필요할까?
조금만 신경 쓰면 실질적인 기능 손실 없이 이미지를 훨씬 더 간소하게 만들 수 있다. 이 가이드에서는 간소한 도커 이미지를 만들고 유지하는 6가지 방법을 살펴본다.
새롭게 시작
만들 수 있는 가장 기본적인 도커 이미지는 사실 이미지가 아니라 애플리케이션에 필요한 요소를 채워 넣을 수 있는 빈 컨테이너다. 처음부터 새롭게 만드는 방법(또는 스크래치라고 함)은 미니멀한 이미지를 만들기 위한 적절한 방법으로 보일 수 있고 많은 경우 실제로 그렇지만, 몇 가지 주의 사항이 있다.
첫째, 스크래치는 애플리케이션에 무엇이 필요한지에 대해 아무것도 가정하지 않는다. 앱이 종속성 없는 하나의 바이너리로 구성되는 경우(예를 들어 고(Go) 애플리케이션) 스크래치 이미지가 효과적이지만 대다수 애플리케이션에는 링크된 라이브러리 또는 인증서 등 일종의 런타임 종속성이 있다.
애플리케이션이 단순히 시작되고 실행된다고 해서 완벽하게 작동한다고 단정할 수는 없다. 연결된 라이브러리가 누락되는 등의 빠진 부분은 테스트하지 않으면 그 존재 여부를 알 수 없다. 이는 파이썬과 같은 동적 언어에서 특히 큰 문제가 된다. 테스트하지 않으면 막상 필요할 때가 될 때까지 아무런 경고도 표시되지 않기 때문이다.
전에 해봤지만 원하는 결과를 얻지 못했던 경험 탓에 처음부터 새로 시작하는 방식을 피하고 있다면, 모든 요소를 다 투입해야 제대로 작동할 것이라는 생각부터 버려야 한다. 현재 사용 중인 언어나 런타임을 위한 최소 요구사항을 출발점으로 삼도록 설계된 런타임을 사용해 시작할 수도 있다.
‘슬림’ 런타임 이미지 사용
런타임이 필요한 언어로 작성된 많은 애플리케이션에서는 그 언어에 대한 “슬림” 런타임 이미지를 사용할 수 있다. 슬림 런타임 이미지는 해당 언어로 작성된 애플리케이션을 실행하는 데 필요한 최소한의 요소만 포함된 사전 빌드된 이미지다.
거듭 말하지만 슬림 이미지는 특정 애플리케이션이 아니라 런타임 자체를 지원하는 데 필요한 요소를 제공할 뿐이다. 예를 들어 파이파이의 서드파티 패키지가 필요한 파이썬 애플리케이션이 있다면 이미지 빌드 프로세스에서 해당 패키지를 추가해야 한다(RUN pip install 등).
특정 사용례에 맞춰 빌드되는 간소한 기본 이미지의 또 다른 좋은 소스는 구글 디스트로리스 이미지(Google Distroless Image) 컬렉션이다. 데비안 리눅스의 간소화된 인스턴스를 기반으로 하며, 여러 아키텍처에서 실행되고 파이썬 3, C로 컴파일된 프로그램, 자바(버전 17 및 21 포함), 노드.js(버전 18, 20, 22)를 위한 런타임을 제공한다. 셸 또는 패키지 관리자가 포함되어 있지 않으므로 셸 사용을 시도하지 않도록 도커파일의 ENTRYPOINT를 구성하거나(예 : ENTRYPOINT "start" 대신 ENTRYPOINT ["start"]) 기본값으로 구성된 언어 런타임에 인수를 제공하지 않도록 해야 한다.
다단계 빌드 사용
빌딩 시 사용되는 공간을 자동으로 최소화하는 편리한 방법은 다단계 빌드 프로세스다. 다단계 빌드는 도커파일에서 여러 개의 FROM 문을 사용한다. 각각의 연속된 FROM을 통해 완전히 새로운 컨테이너 이미지로 시작한 다음 이전 FROM 빌드 프로세스에서 생성된 아티팩트로 선택적으로 채울 수 있다.
예를 들어 절차의 일부로 일종의 소프트웨어 빌드 프로세스가 있는 경우 이 방법이 가장 효과적이다. 빌드 프로세스 이후 남은 것은 대부분 불필요하므로 삭제하고 필요한 부분만 새로운 빈 이미지에 복사하면 된다.
다단계 빌드의 또 다른 매우 유용한 특징은 단계에 이름을 지정하고 빌드 프로세스 전반에서 재사용할 수 있다는 점이다. 그 결과로 얻는 이미지를 저장해서 재사용하거나 향후 프로젝트에서 선택적으로 수집할 수 있다.
계층 최소화
도커파일의 각 RUN 명령은 이미지에 새 계층(layer)을 만든다. 보통 애플리케이션 요구사항을 계층화할 때는 최대한 많은 명령을 통합하려고 하는데, 처음에는 그렇게 할 필요가 없다. 컨테이너를 구축하기 위한 최선의 RUN 명령어를 실험하는 과정에서 점진적으로 발견할 수 있기 때문이다.
또한 서드파티 도커 스쿼시(docker-squash) 툴을 사용해서 이미지에서 두 개 이상의 계층을 “뭉칠” 수도 있다. 또는 여기서도 다단계 프로세스를 사용해 해당 빌드 단계에서 필요한 것만 “스크랩”한 다음 새로운 빈 단계를 채울 수 있다.
.dockerignore 사용
.dockerignore 파일은 사촌격인 깃의 .gitignore 파일과 마찬가지로 빌드 프로세스에서 파일과 디렉터리를 걸러낼 수 있다. 어떤 면에서 다단계 빌드와 상반되는 접근 방식이다. 기존 이미지에서 포함할 항목을 선택하는 것이 아니라, 아직 빌드되지 않은 이미지에서 무엇을 제외할지를 설명하는 것이기 때문이다.
일시적인 요소 또는 런타임과 관련이 없는 요소는 모두 필터링을 통해 걸러낼 만한 유력한 후보로, 깃 리포지토리와 임시 디렉토리, 빌드 아티팩트, 다운로드된 아카이브 등이 여기에 해당한다. 이 방식을 사용하면 캐시 또는 로컬 복사본에서 얻는 이점을 잃을 위험이 있는 빌드 디렉토리 사전 정리가 필요 없다. 그냥 빌드에서 제외하면 된다.
.dockerignore의 경우 .gitignore와 마찬가지로 상당히 세분화된 작업이 가능하므로 예를 들어 한 디렉터리에서 특정 파일 하나를 제외한 모든 파일을 무시하는 등의 작업을 할 수 있다. 복잡하고 계속 변경되는 필터 집합을 다루는 경우 빌드 프로세스의 일부로 .dockerignore를 프로그래밍 방식으로 생성하는 방법도 유용하다.
툴을 사용해 기존 이미지 검사 및 변경
기존 도커 이미지를 검사해 무엇이 있는지 살펴보거나 이미지를 가져와 런타임 분석을 통해 크기를 줄이는 데 사용할 수 있는 다양한 툴이 있다.
다이브(dive) 툴을 사용하면 도커 이미지를 가져와서 계층 단위로 그 내용물을 분해할 수 있다. 각 계층에서는 이전 계층에서 변경된 사항을 확인하고, 각 계층의 공간 사용이 얼마나 효율적인지(또는 비효율적인지) 추정할 수 있다. 이미지에 낭비되는 공간이나 중복 항목이 많은 경우 다이브가 신속하게 알려준다.
그보다 더 강력한 툴인 슬림(Slim)은 현재 CNCF 샌드박스(Sandbox) 프로젝트로, 컨테이너에 대해 런타임 분석을 수행해서 사용되는 것과 사용되지 않는 것을 확인한다. 슬림을 만든 사람들은 슬림을 사용해 이미지 크기를 대폭 줄일 수 있다고 주장하는데, 그 효과는 상황에 따라 다를 수 있다. 그러나 컨테이너에서 리버스 엔지니어링된 도커파일을 생성할 수 있는 xray 함수 하나만으로도 슬림을 실험해 볼 가치는 충분하다. 이 함수를 사용하면 소스를 볼 필 요 없이 컨테이너가 구축되는 방식을 편리하게 재검토할 수 있다.
dl-itworldkorea@foundryco.com
관련자료
-
링크
-
이전
-
다음





