본문 바로가기

AI/MLOps

[12/11] 아이펠 리서치 15기 TIL | Docker 이미지 구조

반응형

이번 포스팅은 Docker 이미지 구조에 대해 더 자세히 정리했다.

내용은 이전 포스팅과 연결된다.


 

FROM python:3.11
WORKDIR /code

COPY ./requirements.txt /code/requirements.txt
COPY ./app /code/app

RUN apt-get update
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]

 

이미지는 이 명령어들을 하나하나 실행할때마다 각각의 스냅샷들을 기록해놓고, 이 변화들이 쌓이는 개념이다.

 

그래서 빌드를 할 때 상위 명령어부터 차례대로 실행되는데, 만약 빌드를 하는 도중에 중단하면 docker images에는 나타나지 않지만 캐시는 남는다. 그래서 다음에 동일한 Dockerfile로 빌드를 시도하면 Docker는 이 캐시된 레이어를 재사용해서 중단된 지점 이후부터 빌드를 재개한다. (이 캐시들은 이전 포스팅에서 언급했던 docker image prune 명령어로 없앨 수 있다.)

 

갑자기 이 얘기를 왜 하냐?

 

패키지 설치하는 requirements.txt 파일은 평소에 업데이트가 자주 되니까(패키지를 담고 있는 파일이니까 의존성 문제 등 이것저것 건들 게 많음), 다음에 실행할때도 업데이트된 COPY ./requirements.txt /code/requirements.txt 줄부터 실행된다.

하지만 RUN apt-get update 같이 자주 실행할 필요가 없는 명령어도 윗줄부터 실행되니까 어쩔 수 없이 매번 실행된다.

 

그래서 자주 변경되지 않는 명령어는 되도록이면 위쪽에 두는 게 좋다.

 

근데 그렇다고 순서를 아무렇게나 배치하면 빌드의 논리적 흐름을 깨뜨릴 수 있기 때문에 잘 생각하고 해야한다.

예를 들면, 

RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt

COPY ./requirements.txt /code/requirements.txt

이런 식으로 배치하면 당연히 안 된다.


Dive

Docker 이미지를 분석하는 데 사용되는 오픈 소스 도구이다.

Docker 이미지의 레이어 구조를 시각적으로 탐색하고, 각 레이어가 이미지 크기에 얼마나 기여했는지 분석하여 이미지를 최적화할 수 있게 한다.

 

dive 설치는 여기를 참고하면 된다.

 

설치를 완료했다면, 먼저 dive에 fastapi-app 이미지를 넣어보자.

dive fastapi-app

결과 화면

뭐가 많이 나오는데, 간단히 알아보자.

 

좌측 상단의 Layers는 Dockerfile의 각 명령어가 이미지 크기에 얼마나 기여했는지 보여준다.

ex: FROM blobs는 120 MB, 등등

 

우측은 현재 선택된 레이어(FROM blobs)에서 파일 시스템에 어떤 변화가 있었는지 보여준다.

ex: 변경점, 스냅샷 등

 

좌측 하단에는 Image Details가 보이는데, 이미지의 상태를 간단히 보여준다.

이미지 이름, 이미지의 최종 크기, 낭비된 공간, 이미지 효율성 점수가 포함되어 있다.


도커 최적화 [기본]: 4분 26초 소요

 

코드는 자주 업데이트 되지만 패키지는 상대적으로 자주 바뀌지 않는다.

그러므로 소스코드를 변경할 때마다 pip install 작업을 할 필요가 없다.

 

그렇다면?

 

자주 변경되지 않는 것들만 모아서 base 이미지로 만들어 놓고,

그 base 이미지를 기반으로 새로운 이미지를 만들게 디자인하면 된다.

도커 최적화 [캐싱]: 34초 소요 (- 87 %)

 

지금까지 작업했던 basic 폴더 말고 Optimization 폴더에 가서 base 파일을 확인해보자.

ls
Dockerfile  Dockerfile.base  README.md  app  requirements.txt
vi Dockerfile.base


FROM python:3.11
WORKDIR /code

COPY ./requirements.txt /code/requirements.txt
COPY ./app /code/app

RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
~                                                                          
~                                                                          
~                                                                          
~                                                                          
~                                                                          
~                                                                          
~                                                                          
~                                                                          
~                                                                          
~                                                                          
~                                                                          
~                                                                          
"Dockerfile.base" 7 lines, 168 bytes

 

이 자주 쓰는 base 도커 이미지를 미리 구워놓자.(빌드해보자.)

docker build . -f Dockerfile.base -t fastapi-app:base


[+] Building 17.9s (10/10) FINISHED                         docker:default
 => [internal] load build definition from Dockerfile.base             0.0s
 => => transferring dockerfile: 212B                                  0.0s
 => [internal] load metadata for docker.io/library/python:3.11        1.4s
 => [internal] load .dockerignore                                     0.0s
 => => transferring context: 52B                                      0.0s
 => [1/5] FROM docker.io/library/python:3.11@sha256:38639aa0267125ab  0.0s
 => [internal] load build context                                     0.0s
 => => transferring context: 253B                                     0.0s
 => CACHED [2/5] WORKDIR /code                                        0.0s
 => CACHED [3/5] COPY ./requirements.txt /code/requirements.txt       0.0s
 => [4/5] COPY ./app /code/app                                        0.0s
 => [5/5] RUN pip install --no-cache-dir --upgrade -r /code/require  16.0s
 => exporting to image                                                0.5s 
 => => exporting layers                                               0.5s 
 => => writing image sha256:67b1d4787b294c199cd1cb72c1a75bf3cd44d40d  0.0s 
 => => naming to docker.io/library/fastapi-app:base                   0.0s
docker images


REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
fastapi-app   base      67b1d4787b29   42 seconds ago   1.17GB
fastapi-app   latest    3c9dc174b172   2 hours ago      1.19GB

 


Docker Hub에 이미지 배포하기

먼저 DockerHub 가입하고, 레포지토리를 생성한다.(레포지토리 이름은 fastapi-app으로 했음)

그리고 base 이미지에 새로운 태그를 붙여준다.

docker tag fastapi-app:base connorchoi/fastapi-app:base

fastapi-app:base -> connorchoi/fastapi-app:base

새로운 태그에는 push를 위해 필자의 DockerHub 이름을 붙였다.

 

docker images를 실행하면 

REPOSITORY               TAG       IMAGE ID       CREATED          SIZE
connorchoi/fastapi-app   base      71925412b821   10 minutes ago   1.17GB
fastapi-app              base      71925412b821   10 minutes ago   1.17GB

라고 뜨는데 Image ID가 같으므로 새로운 이미지가 생성된 건 아니고 그냥 바로가기 느낌

 

이제 허브에 Push 해보자

먼저, 로그인을 해준다.

docker login

입력하고 본인의 docker hub 이름을 입력하면 웹으로 코드가 뜬다.

확인을 누르면 로그인 완료.

 

그 다음, push 해보자

docker push connorchoi/fastapi-app:base


The push refers to repository [docker.io/connorchoi/fastapi-app]
12225b4d1b6b: Pushed 
7e9f26a1728f: Pushed 
63a662e2d3f5: Pushed 
8f0fb126babf: Pushed 
6e3ea126874c: Mounted from library/python 
935c35d41239: Mounted from library/python 
4e19de02f185: Mounted from library/python 
662b8975365c: Mounted from library/python 
8e47c7b8acf3: Mounted from library/python 
0b208bc29d30: Mounted from library/python 
e9b010e49a06: Mounted from library/python 
base: digest: sha256:8dd67fb22a0fa7af0c8bb6d33bdd18279f78815134c30f9c040552f0604abfcd size: 2629

 

그리고 레포를 보면

 

push가 잘 된 것을 확인할 수 있다.


회고

이번 포스팅에서는 Dive를 통한 이미지 파일 분석, 최적화(캐싱)의 원리를 알아보았다.

 

추후

  • Docker Compose
  • Docker Swarm
  • Kubernetes

까지 정리해보겠다.

 

반응형