- 도커 이미지에는 우리가 패키징에 포함시킨 모든 파일이 들어있다.
- 이들 파일은 나중에 컨테이너의 파일 시스템을 형성한다.
- 이 외에도 이미지에는 자신에 대한 여러 메타데이터 정보도 들어 있다.
- 이 정보 중에는 이미지가 어떻게 빌드 됐는지에 대한 간단한 이력도 포함된다.
- 이 정보를 이용하면 이미지를 구성하는 각 레이어는 무엇이고 이들 레이어가 어떤 명령으로 빌드 됐는지 알 수 있다.
docker image history [image 명]
-
이 명령을 입력하면 한 줄마다 한 레이어에 대한 정보가 출력 된다.
-
CREATE BY
항목은 해당 레이어를 구성한 Dockerfile 스크립트의 인스트럭션이다. -
Dockerfile 인스트럭션과 이미지 레이어는 1:1관계를 갖는다.
-
이 부분에 대해 좀 더 자세히 설명할 텐데, 이미지 레이어를 제대로 이해해야 Docker를 효율적으로 활용이 가능하기 때문이다.
-
docker 이미지는 이미지 레이어가 모인 논리적 대상이다. 레이어는 도커 엔진의 캐시에 물리적으로 저장된 파일이다.
-
이 점이 왜 중요하나면, 이미지 레이어는 여러 이미닞와 컨테이너에서 공유된다
-
만약 NodeJS 애플리케이션이 실행되는 컨테이너를 여러 개 실행한다면 이들 컨테이너는 모두 Node.JS 런타임이 들어 있는 이미지 레이어를 공유한다.
- diamol/node 이미지는 최소한의 운영체제 레이어와 NodeJS 런타임을 포함한다.
- 리눅스 이미지는 약 75MB의 디스크 용량을 차지하낟.
- 윈도우 운영체제 기반 레이어는 이보다 크기 때문에 윈도우 버전은 약 300MB 정도 된다.
- 우리가 만든 web-ping 이미지는 diamol/node 이미지를 기반으로 하므로 기반 이미지의 모든 레이어를 포함한다.
- Dockerfile 스크립트의 FROM 인스트럭션의 의미가 바로 이것이다.
- 기반 레이어 위에 추가한 app.js 파일은 불과 몇 KB에 지나지 않는다.
- 그럼 web-ping 이미지의 전체 용량은 얼마나 될까?
docker image ls
로 출력된 이미지 목록에서 각 이미지의 용량을 확인 할 수 있다.- 아무 필터 조건을 걸지 않고 출력한 이미지 목록은 다음과 같다.
!Pasted image 20230607092902.png
-
언뜻 보면 diamol/node, 도커 허브에서 내려받은 diamol/ch-03-web-ping, 그리고 새로 빌드한 web-ping까지 세 이미지가 모두 비슷한 용량을 점유하는 것처럼 보인다.
- 리눅스 버전의 경우 약 75MB
-
분명 기반 레이어를 공유할 텐데,
docker image ls
명령으로 출력된 결과는 각각 75MB 씩 도합 255MB의 디스크 용량을 점유하는 것으로 나온다. -
하지만, 이는 사실이 아니다 이미지 목록의 SIZE 항목에 나오는 수치는 이미지의 논리적인 용량이지 해당 이미지가 실제로 차지하는 디스크 옹량을 나타내는 것이 아니다.
-
다른 이미지와 레이어를 공유하면 여기에 나온 수치보다 디스크 용량을 훨씬 덜 차지한다.
-
이미지 목록 확인에서는 이를 확인 할 수 없지만, 다른 명령으로 확인 가능하다.
- 이미지 목록에서는 이미지의 용량 총합이 363.96MB로 나온다.
- 그러나 이 수치는 논리적 용량이다.
- 이미지 저장에 실제 사용된 디스크 용량은 system df 명령으로 사용 가능하다.
-
이 명령을 입력해 나온 출력 결과를 보면 이미지 캐시의 실제 용량은 약 202.2MB를 차지하는 것으로 나온다.
-
163MB는 이미지끼리 레이어를 공유한 것으로, 약 45%dml 디스크 공간이 절약됐다.
-
이렇게 절약되는 디스크 공간은 대개 런타임 등 같은 기반 레이어를 공유하는 애플리케이션의 숫자가 많을 수록 더욱 늘어난다.
-
이들 기반 레이어가 Java, .net, php 그 무엇이든 도커의 동작 방식은 같다.
-
이미지 레이어를 여러 이미지가 공유한다면, 공유되는 레이어는 수정할 수 없어야한다.
-
만약 이미지의 레이어를 수정할 수 있다면, 그 구정이 레이어를 공유하는 다른 이미지에도 영향을 미치게 된다.
-
도커는 이미지 레이어를 읽기 전용으로 만들어 두어 이런 문제를 방지한다.
-
이미지를 빌드하면서 레이어가 만들어지면, 레이어는 다른 이미지에서 재 사용 될 수 있다.
-
그러나 레이어를 수정할 수는 없다.
-
이 점은 이제 살펴볼 Dockerfile 스크립트 죄적화에 도커 이미지의 용량을 줄이고, 빌드를 바르게 만드는 기법에서 특히 잘 활용된다.
이미지 레이어 캐시를 이용한 Dockerfile 스크립트 최적화
- 우리가 조금 전에 빌드한
web-ping
이미지에는 애플리케이션이 구현된 자바 스크립트 파일이 들어 있다. - 이 파일을 수정하고 이미지를 다시 빌드하면, 새로운 이미지 레이어가 생긴다.
- 도커의 이미지 레이어가 특정한 순서대로만 배치된다고 가정한다.
- 그래서 이 순서 중간에 있는 레이어가 변경되면, 변경된 레이어보다 위에 오는 레이어를 재 사용할 수 없다.
- ch03-web-ping 디렉터리에 있는 app.js 파일을 수정하라
- 반드시 코드를 수정할 필요는 없고, 파일에 빈 줄을 추가하는 정도로도 충분하다.
- 그 다음 새로운 버전의 도커 이미지를 빌드 한다.
docker image build -t web-ping:v2. .
-
이미지를 다시 빌드 하면 아래와 같은 출력 내용을 볼 수 있습니다.
-
도커는 캐시에 일치하는 레이어가 있는지 확인하기 위해 해시값을 이용합니다.
- 해시는 입력값이 같은지 확인 할 수 있는 일종의 디지털 지문이다.
- 해시 값은 Dockerfile 스크립트의 인스트럭션과 인스트럭션에 의해 복사되는 파일의 내용으로부터 계산되는데,
- 기존 이미지 레이어에 해시값이 일치하는 것이 없다면 캐시 미스가 발생하고, 해당 인스트럭션이 실행 된다.
-
한번 인스트럭션이 실행되면 그 다음에 오는 인스트럭션은 수정된 것이 없더라도 모두 실행된다.
-
우리가 만들었던 자그마한 이미지도 이에 영향을 받을 수 잇다.
-
지난 번 이미지 빌드 이후
app.js
파일이 수정 됐으므로 6단계의COPY
인스트럭션은 실제로 실행될 것이다. -
7단계에 있는 CMD 인스트럭션은 변경된 것이 없지만, 6단계 인스트럭션이 실행됐으므로, 함께 실행 된다.
-
이러한 연유로 Dockerfile 스크립트의 인스트럭션은 잘 수정하지 않느느 인스트럭션이 앞으로 오고, 자주 수정되는 인스트럭션은 뒤에 오도록 배치해야한다.
-
이렇게 해야 캐시에 저장된 이미지 레이어를 되도록 많이 재사용 가능하다.
-
이미지를 공유하는 과정에느 시간은 물론이고 디스크 용량, 네트워크 대역폭을 모두 절약 가능하다.
-
web-ping
이미지의 Dockerfile 스크립트에는 인스트럭션이 일곱 개 뿐이다. -
그러나 이 짧은 스크립트에도 개선의 여지가 있다.
-
CMD 인스트럭션은 스크립트 마지막에 위치할 필요가 없다.
-
이 인스트럭션은
FROM
인스트럭션 뒤라면, 어디에 배치해도 무방하다. -
또한, ENV 인스트럭션 하나로 여러 개의 환경 변수를 정의 할 수 있으므로, 세 개의
ENV
인스트럭션을 하나로 합칠 수 있다. -
이렇게 최적화된 Dockerfile 스크립트에 아래에 예제로 표현 했다.
FROM diamol/node
CMD ["node", "/web-ping/app,js"]
ENV TARGET = "blog.sixeyed.com" \
METHOD = "HEAD" \
INTERVAL = "3000"
WORKDIR /web-ping
COPY app.js .
- 최적화를 마친 Dockerfile 스크립트도 예제 코드로 제공된다.
- web-ping-optimized 디렉토리로 이동하여 새로운 스크립트로 다시 빌드 해보기
- 이전과 비교해서 빌드 과정에 큰 차이가 느껴지지는 않는다.
- 일곱 단계의 인스트럭션이 다섯 단계로 줄긴 했으나, 결과는 동일하다.
- 이미지로 컨테이너를 실행해 보면, 동작도 동일 했다.
- 그러나 app.js 파일을 다시 수정하고 이미지를 빌드해보면, 마지막 단계를 제외하고는 모든 레이어를 캐시에서 재사용한다.
- 우리가 원하는 최적화가 바로 이것이다.