Docker - image 크기 줄이기

우야·2021년 8월 11일
2

1. 가벼운 Base image를 사용
이미지에는 불필요한 것들이 많이 포함되어 있을수 있기때문에 debian계열로는 slim, jessie, alpine등을 사용하여 용량을 줄일 수 있음
단, 필요한 패키지, 파일이 잇을수 있음

2. Dockerfile 명령을 체인으로 사용
Dockerfile에서 RUN명령을 개별로 실행시 실행이 끝날때마다 중간 이미지가 생성
체인으로 명령을 실행하면 한개만 만들어지기 때문에 크기가 줄어듬

개별 실행 예제

RUN wget -nv
RUN tar -xvf someutility-v1.0.0.tar.gz
RUN mv /tmp/someutility-v1.0.0/someutil /usr/bin/someutil

체인 실행 예제

RUN wget -nv \
tar -xvf someutility-v1.0.0.tar.gz \
mv /tmp/someutility-v1.0.0/someutil /usr/bin/someutil

3. 중간 이미지를 하나로 합치는 방법
docker export, import를 사용하면 docker layer를 하나로 합쳐서 사용할 수 있음
이미지에 너무 많은 레이어가 있어서 크기가 큰 경우 강제로 모든 레이어를 하나로 줄여 준다.
- 단, 몇가지 고려할 사항이 있다.
- 히스토리가 삭제 되기때문에 권장하지는 않는다.
- image 실행시 필요한 명령어를 추가해야할 수 있음
- Docker - save, load VS export, import 페이지를 찾아보면 좋을것 같다.

4. 빌드 도구를 설치 하지않음
꼭 필요한 경우가 아니라면, 소스를 빌드해서 이미지를 넣으면, 불필요한 빌드 도구가 차지하는 공간을 줄일 수 있음

5. 패키지 관리자를 정리
apt-get을 실행 했다면, apt-get clean을 넣어주고, --no-install-recommends옵션(최소 설치)을 넣는다.
/var/cache/apt/archives 디렉토리에 있는 다운로드 파일을 지워줌
/var/lib/pat/lists 디렉토리를 지워 패키지 리스트 파일도 지워줌

RUN apt-get update && apt-get install -y \
    aufs-tools \
    automake \
    build-essential \
    curl \
    dpkg-sig \
    libcap-dev \
    libsqlite3-dev \
    mercurial \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
 && rm -rf /var/lib/apt/lists/*

6. 불필요한 파일 정리
curl을 통한 파일다운로드시 필요한 파일을 설치한 후 삭제
시점은 Docker명령어에수 수행해야하며 체인을 사용해야지만 최종이미지에 포함되지 않는다.
만약 개별로 수행시 각 RUN마다 레이어가 생겨서 file.zip이 포함된다.

RUN curl http://xx.xxx.com/file.zip \
RUN tar xvzf file.zip \
RUN rm file.zip

7. docker image에서 불필요한 Layer가 있는지 확인
--no-trunc: 명령어, 설명을 ...으로 표시하지 않고 전체를 보여준다.
각 layer마다 사용된 용량이 표시된다.

$ docker history gcr.io/kfserving/pytorchserver:0.3.0 --no-trunc
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
2e560760b2db        6 months ago        /bin/sh -c #(nop) ENTRYPOINT &{["/run.sh"]}     0 B                 
<missing>           6 months ago        /bin/sh -c #(nop) COPY file:6d7449b1aeffb25aa   1.602 kB            
<missing>           6 months ago        /bin/sh -c #(nop) EXPOSE 3000/tcp               0 B                 
<missing>           6 months ago        /bin/sh -c #(nop) VOLUME [/var/lib/grafana /v   0 B                 
<missing>           6 months ago        |1 GRAFANA_VERSION=3.1.1-1470047149 /bin/sh -   137.7 MB            
<missing>           6 months ago        /bin/sh -c #(nop) ARG GRAFANA_VERSION           0 B                 
<missing>           11 months ago       /bin/sh -c #(nop) CMD ["/bin/bash"]             0 B                 
<missing>           11 months ago       /bin/sh -c #(nop) ADD file:b5391cb13172fb513d   125.1 MB

8. 상황에 맞게 ADD 명령어 사용
ADD, COPY는 기능을 포함하면서도, HTTP로 파일을 다운로드하거나, tar/zip파일을 풀어 설치하는 기능을 가지고 있음.
하지만, curl, wget을 사용하는 것이 좋다.
이유 : ADD를 통해 압축파일을 다운받고, 압축파일을 지워야 하는데 체인으로 처리 할수 없다.

ADD http://xx.xxx.com/file.tar.gz
RUN tar zvxf file.tar.gz
RUN rm file.tar.gz

ADD대신 RUN을 사용

RUN wget http://xx.xxx.com/file.zip \
    tar xvzf filr.tar.gz \
    rm file.tar.gz

ADD를 상용하기에 적합한 케이스

  • 로컬 압축파일을 압축을 해제할때,
ADD file.tar.gz ./
  • 만약 copy와 run을 사용하게 되면, 불필요한 layer를 생성하게 된다.
COPY file.tar.gz ./
RUN tar xvzf file.tar.gz

9. 프로그램잉 언어마다 패키지 매니저가 제공하는 Lock 파일 시스템 활용
Python은 관행적으로 의존성 패키지 파일을 requirements.txt으로 사용했다.
그러나 요즘은 조금 더 발전된 Locking 시스템을 갖춘 Pipenv, Poetry를 사용하는 것을 추천한다.
Cache Layer의 장점을 얻을 수 있고, 예상치 못한 패키지 버전 업데이트도 방지할 수 있다.

FROM python:3.8-slim-buster

WORKDIR /tmp
RUN pip install pipenv

COPY Pipfile /tmp/Pipfile
COPY Pipfile.lock /tmp/Pipfile.lock

RUN pipenv install --system --deploy

CMD ["pip", "freeze"]

10. 멀티-스테이지 빌드

Dockerfile 1개에 FROM 구문을 여러 개 두는 방식
각 FROM 명령문을 기준으로 스테이지를 구분하여, 특정 스테이지에서 생성된 것중에 사용되지 않거나 불필요한 모든 것을 무시하고, 필요한 부분만 가져와서 새로운 베이스 이미지로 빌드하여 생성

  • Pipfile에는 uwgi라는 패키지의 의존성을 명시하고 있고, 이 패키지를 위해서는 gcc가 필요하기때문에 apt-get install gcc를 실행 해야함
    여기서, gcc는 uwgi설치에만 관여하고, 실제 애플리케이션 실행환경에는 필요 하지 않기때문에 멀티-스테이지 빌드를 사용할 수 있음
  • COPY --from=builder: 전 단계 스테이지 빌드에서 생성된 특정 결과물만 새로운 BASE이미지로 복사해서 이미지를 생성
FROM python:3.8-slim-buster AS builder

RUN apt-get update && apt-get install -y gcc

WORKDIR /tmp
RUN pip install pipenv

COPY Pipfile /tmp/Pipfile
COPY Pipfile.lock /tmp/Pipfile.lock

RUN pipenv install --system --deploy

FROM python:3.8-slim-buster
COPY --from=builder /usr/local/lib/python3.8/site-packages /usr/local/lib/python3.8/site-packages

CMD ["pip", "freeze"]

11. 개발용 패키지 배제
Production용에는 개발시에 필요한 패키지를 포함하지 않게 하여 image 크기를 줄일 수 있다.

예를 들면 nodejs기반의 어플리케이션을 개발한다 치면 npm install은 기본 의존성 모듈과 더불어 개발용 모듈까지도 설치한다. npm install시 개발용 모듈은 설치하지 않도록 하는것이 바람직하다 할 수 있겠다.

예1) 요즘은 gulp와 같은 빌더를 이용하기 때문에 개발용 모듈을 배제하기가 쉽지 않다(gulp는 개발용 모듈로 포함되어 있다)
해결) 프로젝트 파일들을 바로 복사한뒤에 npm install을 하지 말고, gulp 등을 빌드를 거치고 난 다음에 프로젝트 파일들을 컨테이너로 복사하는 것이다.
즉, 컨테이너에 옮겨서 빌드/테스트 하지 말고 빌드/테스트를 gulp로 끝낸 후에 컨테이너로 복사하라는 것이다.
물론 jenkins등의 연동을 통해서 test 자동화가 필요할 때는 docker-compose 등을 통해서 테스트용 dockerfile을 별도로 분리해 처리하는 것이 한 방법이겠다.

12. .dockerignore 활용하기
docker build시 명령어 COPY등을 통해서 프로젝트 파일을 컨테이너로 복사할 때 폭더, 파일등을 배제 하는 역할

profile
Fullstack developer

0개의 댓글