Docker 치트시트

Key Kim
5 min readJul 18, 2021

--

Docker 컨테이너를 빌드할 때 알아두면 좋은 내용들을 정리합니다.

0. 캐시 활용하기

RUN, COPY와 같은 명령어는 새로운 Layer를 만든다. 디스크를 절약하고, 빌드 시간을 줄이기 위해선 이 Layer를 재사용하는것이 중요하다.

만약 이전 Layer의 변화가 감지되면, 그 이후부턴 캐싱이 되지 않는다.

캐싱의 조건은 각 명령어마다 다르지만 크게 두가지로 나뉜다.

스트링 매칭

RUN 명령어를 캐시할 때 스트링 매치가 조건이 된다.

RUN sudo apt install awesome-package

도커를 빌드할 때 awesome-package가 업데이트되더라도 스트링을 변경하지 않으면 캐시 히트다.

파일 해시

COPY 명령어는 파일 해시로 캐싱이된다.

COPY ./folder   .-----ls -al ./folder
.local.env
.development.env

만약 /folder 아래 파일들의 해시값이 변경되면 COPY 명령어는 캐시되지 않는다.

1. 변화가 잦은 부분은 맨 뒤에 배치하기

0번에서 언급한 캐시와 관련이 있다. 캐시가 한번 꺠지면 그 이후의 캐시도 깨지므로 명령어 순서에 주의해야한다.

COPY src/ src/  <-- 만약 소스코드가 한줄이라도 바뀌었다면??
RUN pip install tensorflow <--- 캐시를 못쓰게되서 재설치해야함
-----RUN pip install tensorflow <--- 캐시
COPY src/ src/

2. multi staging 활용하기

여러 이미지들을 중 , 필요한 부분만 빼내서 사용할 수 있다.

예로, 용량이 큰 cuda 컨테이너에서 gpu와 관련된 빌드 작업을 수행하고 결과물만 다른 컨테이너로 복사한다면 컨테이너 용량을 크게 줄일 수 있다.

// 파이썬 런타임, 소스코드, 쿠다 라이브러리가 내장된 이미지
FROM cuda:11.0 AS cuda
RUN build.sh some-huge-files.tarFROM aws.ecr/myservice AS svc
FROM aws.ecr/python AS python
COPY --from svc requirements.txt requirements.txt
COPY --from cuda /some-huge-files/ /some-huge-files
ENV PYTHON_PATH=/python/usr/lib
RUN /python/pip install -r requiremenets.txt

3. 적절한 Layer 구성을 통한 이미지 생성

RUN 명령어를 남발해서 Layer를 늘리지말자. Layer를 생성하는데도 용량이 낭비되고, 디버깅 관점에서도 많은 Layer는 좋지 않다.

또한 RUN 명령에서 실행한 내용은 Layer에 영구히 남게되고, 다른 Layer에서 변경할 수 없다. 디스크 용량을 줄이려면 명령 실행 후 남은 파일들을 지워주자.

// 내가 생각하는 좋은 예시
RUN pip install -r worker.requirements.txt && \
pip install -r app.requirements.txt && \
rm *requirements.txt
---
// 안좋은 예시
RUN pip install -r worker.requirements.txt
RUN pip install -r app.requirements.txt
RUN rm *requirements.txt # 다른 Layer이므로 이미지 크기를 줄이지 못함.

4. 각 Layer는 독립적이다

Dockerfile의 명령어들은 고유의 Layer를 생성한다.

그리고 각 Layer들은 공유되지 않는다.

아래 예시를 보자.

ARG TEST_ARG=1234
ENV TEST_ENV=1234
RUN TEST_ARG=0000 && \
TEST_ENV=0000 && \
TEST1=4321 && \
export TEST2=abcd && \
echo "TEST_ARG : $TEST_ARG\n \
TEST_ENV : $TEST_ENV\n \
TEST1 : $TEST1\n \
TEST2 : $TEST2"
RUN echo "TEST_ARG : $TEST_ARG\n \
TEST_ENV : $TEST_ENV\n \
TEST1 : $TEST1\n \
TEST2 : $TEST2"
----docker build .. Step 11/13 :
RUN TEST_ARG=0000 && ...
---> Running in 063f3c1bd3d0
TEST_ARG : 0000
TEST_ENV : 0000
TEST1 :4321
TEST2 : abcd
Removing intermediate container 063f3c1bd3d0
---> 913f8d8c654d
Step 12/13 : RUN echo "TEST_ARG : $TEST_ARG\n ...
---> Running in e71ded2dee51
TEST_ARG : 1234
TEST_ENV : 1234
TEST1 :
TEST2 :
Removing intermediate container e71ded2dee51
---> 03e3a3d3b567

두 개의 RUN이 서로 다른 Layer를 생성하므로 export 를 선언한 RUN 구문에서만 출력이 되고, 다른 Layer에서는 TEST1, TEST2이 출력되지 않는다.

각 RUN 명령이 끝나고

Removing intermediate container 063f3c1bd3d0

와 같은 로그를 볼 수 있는데 이는 해당 RUN 명령이 063f3c1bd3d0 라는 컨테이너(Layer) 에서 실행되었다는 의미이다. 즉 서로 독립적인 컨테이너에서 명령을 실행하므로 변수들은 공유되지 않는다.

ARG, ENV 의 경우 전역으로 사용할 수 있다.

--

--

No responses yet