새소식

독서/시작하세요! 도커 & 쿠버네티스

5. Dockerfile 명령어

  • -

ADD vs COPY

 Copy는 로컬 디렉터리에서 읽어 들인 컨텍스트로부터 이미지에 파일을 복사하는 역할을 합니다. 사용하는 형식은 ADD와 같습니다. 하지만 COPY는 로컬의 파일만 이미지에 추가할 수 있지만 ADD는 외부 URL 및 tar 파일에서도 파일을 추가할 수 있다는 점에서 다릅니다. 즉 COPY 기능이 ADD에 포함되는 것 입니다.

 

 그러나 ADD를 사용하는 것은 그다지 권장하지 않습니다. ADD로 URL이나 tar 파일을 추가할 경우 이미지에 정확히 어떤 파일이 추가될지 알 수 없지만, COPY는 로컬 컨텍스트로부터 파일을 직접 추가하기 때문에 빌드 시점에서도 어떤 파일이 추가될지 명확합니다.

 

 

ENTRYPOINT vs CMD

CMD는 컨테이너가 시작될 때 실행할 명령어를 설정합니다. 이는 docker run 명령어에서 맨 뒤에 입력했던 커맨드와 같은 역할을 합니다. 그러나 컨테이너의 실행 옵션에는 CMD와 유사한 ENTRYPOINT라는 명령어도 존재합니다. entrypoint는 커맨드와 동일하게 시작될 때 수행할 명령을 지정한다는 점에서 같습니다. 그러나 entrypoint는 커맨드를 인자로 받아 사용할 수 있는 스크립트의 역할을 할 수 있다는 점에서 다릅니다.

 entrypoint를 입력하지 않은 것과 특정 명령어를 넣은 것의 차이를 보면, /bin/bash 라는 내용이 터미널에 출력됐습니다. 컨테이너에 entrypoint가 설정되면 run 명령어의 맨 마지막에 입력된 cmd를 인자로 삼아 명령어를 출력합니다. entrypoint가 설정되지 않았다면, cmd에 설정된 명령어를 그대로 실행하지만 설정됐다면 cmd는 단지 entrypoint에 대한 인자의 기능을 합니다.

 

 

entrypoint를 이용한 스크립트 실행

일반적으로는 스크립트 파일을  entrypoint의 인자로 사용해 컨테이너가 시작될 때마다 해당 스크립트 파일을 실행하도록 설정합니다. 스크립트 파일을 entrypoint에 설정하려면 스크립트 파일의 이름을 entrypoint의 인자로 입력합니다.

 

ex) # docker run -i -t --name entrypoint_sh --entrypoint="/test.sh" ubuntu:14.04 /bin/bash

단, 실행할 스크립트 파일은 컨테이너 내부에 존재해야 합니다. 이는 이미지 내에 스크립트 파일이 존재해야 한다는 것을 의미합니다. 지금까지 설명한 Dockerfile 중 이미지에 파일을 추가하는 좋은 방법은 바로 COPY Or ADD 입니다.

 

이미지를 빌드할 때 이미지가 작동하기 위해서는 아래와 같은 단계를 거칩니다.

1. 어떤 설정 및 실행이 필요한지에 대해 스크립트로 정리

2. ADD 또는 COPY로 스크립트를 이미지로 복사

3. ENTRYPOINT를 이 스크립트로 설정

4. 이미지를 빌드해 사용

5. 스크립트에서 필요한 인자는 docker run 명령어에서 cmd로 entrypoint의 스크립트에 전달

 

 

JSON 배열 형태와 일반 형식의 차이점

 CMD 혹은 ENTRYPOINT에 설정하려는 명령어를 /bin/sh/로 사용할 수 없다면 JSON 배열의 형태로 명령어를 설정해야 합니다. JSON 배열 형태가 아니라면 실제로 이미지를 생성할 때 cmd 와 entrypoint에 /bin/sh -c 가 앞에 추가됩니다.

 

ex) CMD echo test

# -> /bin/sh -c echo test

ex) ENTRYPOINT /entrypoint.sh

# -> /bin/sh -c /entrypoint.sh

 

#실제 컨테이너에서 실행되는 명령어는 /bin/sh -c entrypoint.sh /bin/sh -c echo test

 

ex) CMD ["echo", "test"]

# -> echo test

 

ex) ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]

# -> /bin/bash /entrypoint.sh

 

#실제 컨테이너에서 실행되는 명령어는 /bin/bash entrypoint.sh echo test

 

 

Dockerfile 빌드할 때 주의할 점

 Dockerfile을 사용하는 데 좋은 습관이라는 것이 있습니다. 예로 하나의 명령어를 \로 나눠서 가독성을 높일 수 있도록 하거나, dockerignore 파일을 작성해 불필요한 파일을 빌드 컨텍스트에 포함하지 않는 것, 빌드 캐시를 이용해 기존에 사용했던 이미지 레이어를 재사용하는 방법도 있습니다.

 

아래는 비효율적으로 Dockerfile을 빌드하는 예 입니다. fallocate 명령어는 100MB 크기의 파일을 가상으로 만들어 컨테이너에 할당하고, 이를 이미지 레이어로 빌드합니다. 그리고 이 파일을 rm 명령어로 삭제합니다. 즉 빌드가 완료되어 최종 생성된 이미지에는 100MB 크기의 파일인 /dummy가 존재하지 않습니다.

 

# vi Dockerfile

FROM ubuntu:14.04

RUN mkdir /test

RUN fallocate -l 100m /test/dummy

RUN rm /test/dummy

 

 위 Dockerfile로 빌드한 이미지 크기는 일반적으로 생각하면 원래의 ubuntu 이미지 크기와 같아야 겠지만 추가 100MB이 생긴 290MB 정도를 차지합니다. 그러나 위에서 빌드한 이미지로 컨테이너를 생상하면 100MB 크기의 /test/dummy 파일은 존재하지 않습니다. 이미 rm 으로 삭제한 상태로 이미지를 빌드했기 때문이죠.

 

이는 컨테이너를 생성할 때 컨테이너에서 변경된 사항만 새로운 이미지 레이어로 생성하는 방식의 단점 중 하나입니다. RUN rm/test/dummy 명령어를 수행해 100MB 파일을 삭제하더라도 이는 "파일을 삭제했다" 라는 변경사항으로서의 레이어로 새롭게 저장될 뿐, 실제 100MB 크기의 파일은 이전 레이어에 남아있기 때문입니다. 즉 실제로 컨테이너에서 사용하지 못하는 파일이 이미지 레이어로 들어오기 때문에 저장공간은 차지하지만 실제로는 의미가 없는 저장 공간일 수도 있습니다. 

 

이를 방지하는 방법은 매우 간단합니다. Dockerfile을 작성할 때 &&으로 각 RUN 명령을 하나로 묶는 것입니다. 즉 하나의 RUN으로 여러개의 명령어를 실행하도록 작성하면 됩니다.

 

 But 더 알아가서, 도커의 레이어 이미지 구조는 위의 경우 단점으로 작용할 수 있지만 다르게 잘 활용하면 장점으로 작용할 수 있습니다. 예로 A, B 애플리케이션이 500MB 크기의 같은 라이브러리를 사용해야 한다면 각 애플리케이션의 이미지에 라이브러리를 각기 설치하기보다는 500MB 크기의 라이브러리 이미지를 미리 만들어 놓은 다음 이 이미지를 이용해 A, B 애플리케이션의 이미지를 생성함으로써 저장 공간을 절약할 수 있습니다. 또한 이미지를 배포할 때도 애플리케이션에 해당하는 변경된 부분만 내려받으면 되므로 이미지를 빠르게 전송할 수 있습니다.

 

다른 사람이 빌드한 이미지에 불필요한 이미지 레이어가 들어있다면 해당 이미지로 컨테이너를 생성하고 docker export, import 명령어를 사용해 컨테이너를 이미지로 만듦으로써 이미지의 크기를 줄일 수 있습니다. export된 파일을 임포트해서 다시 도커에 저장하면 레이어가 한 개로 줄어듭니다. 그러나 이전에 저장돼 있던 각종 이미지 설정은 잃어버리게 되므로 주의해야 합니다.

 

'독서 > 시작하세요! 도커 & 쿠버네티스' 카테고리의 다른 글

7. 도커 스웜  (0) 2023.12.13
6. 도커 데몬  (2) 2023.12.07
4. Dockerfile 빌드  (0) 2023.11.30
3. 도커 이미지  (0) 2023.11.29
2. 도커 볼륨 & 도커 네트워크  (1) 2023.11.28
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.