본문 바로가기

Tech/Docker

Dockerfile & Commit : 나만의 애플리케이션을 Docker Image로 만들어 보자

나만의 Docker Image를 만들어보자

도커 허브에서 우리가 필요로 하는 이미지를 docker pull 명령어를 통해 마음껏 가져올 수 있습니다. 하지만, 도커 허브에 존재하지 않는, 우리만의 프로젝트를 이미지로 만들어 공유해야 할 순간이 있습니다. 이러한 순간을 대비해, 직접 커스텀 이미지를 만드는 방법을 알아보고자 합니다. 나만의 커스텀 이미지를 만들기 위해선 크게 Dockerfile을 이용하는 방법과, Commit 명령어를 이용하는 방법 2가지로 나눠볼 수 있습니다. 해당 포스팅에서 얻어갈 수 있는 정보는 아래와 같습니다.

  1. Commit을 통해 이미지 생성하기
  2. Dockerfile을 통해 이미지 생성하기

Commit을 통해 이미지 생성하기

commit 명령어는 현재 작업중인 컨테이너를 이미지로 변환해 주는 명령어입니다. 컨테이너가 활용되는 사이클은 아래와 같습니다.

컨테이너가 활용되는 사이클

(a)우선 작업할 베이스 환경이 될 특정 이미지를 도커 허브로부터 가져옵니다. (b)해당 이미지를 컨테이너로 실행시킵니다. (c)실행된 컨테이너에서 우리만의 애플리케이션을 구현하고, 환경을 구성합니다. (d)그렇게 수정된 나만의 컨테이너를 다시 이미지로 변환합니다. 이렇게 만들어진 우리만의 커스텀 이미지를 도커 헙에 올려 공유를 하거나, 서버로 올려 애플리케이션을 구동하게 됩니다. 이처럼 사용 중인 컨테이너를(수정된 컨테이너를) 다시 이미지로 변환하고 싶을 때, 단순히 commit 명령어를 사용할 수 있습니다.

commit의 역할

위의 사이클을 단계별로 밟아보며, commit 명령어를 통해 직접 이미지를 만들어 보겠습니다. 이때 특정 애플리케이션은 streamlit에서 제공해 주는 간단한 대시보드라 가정하겠습니다.

 

1. 우선 애플리케이션을 위한 베이스가 될 파이썬 이미지를 가져옵니다.(a에 해당합니다.)

docker pull python:3.9-slim

 

2. 가져온 이미지를 컨테이너로 실행합니다.(b에 해당합니다.)

docker run -it --name dashboard -p 8080:8080 python:3.9-slim /bin/bash
  • -it : 컨테이너 내부에서 명령어를 입출력할 수 있도록 하겠다.
  • --name : 컨테이너 이름을 dashboard라 명하겠다.
  • -p : 호스트 포트인 8080과 컨테이너 포트인 8080을 서로 연결하겠다. 포트포워딩 하겠다.

3. 실행된 컨테이너 내에서, 애플리케이션을 위한 환경과 코드파일을 세팅해 보도록 하겠습니다.(c에 해당합니다.)

apt-get update
mkdir app
cd app
apt-get install -y build-essential
apt-get install -y curl
apt-get install -y software-properties-common
apt-get install -y git
git clone https://github.com/streamlit/streamlit-example.git .
pip3 install -r requirements.txt

 

4. streamlit 파일 실행

streamlit run streamlit_app.py --server.port=8080 --server.address=0.0.0.0
  • streamlit으로 streamlit_app.py파일을 실행시키고, 해당 결과물을 0.0.0.0:8080 주소로 개시하겠다는 말입니다.

5. 호스트에서 0.0.0.0:8080 주소로 들어가게 되면, 아래와 같이 대시보드를 확인할 수 있습니다.

streamlit 대시보드

이렇게 파이썬의 기본 컨테이너로부터, 해당 대시보드를 위한 환경 설정과 코드를 마련해 봤고, 직접 대시보드를 확인해 봤습니다. 이제 이렇게 수정된 컨테이너를 다시 이미지로 변환해 보겠습니다.

 

6. commit 명령어로 컨테이너를 이미지로 변환시켜 줍니다. (exit을 통해 컨테이너 외부로 나간 다음 명령어를 수행해 줍니다.) (d에 해당합니다.)

docker commit [option] CONTAINER [repository:tag]
docker commit dashboard dashboard:latest

 

이제 저희가 사용했던 컨테이너 dashboard가 dashboard:latest라는 이미지로 변환이 되었습니다. 아래 명령어로 이미지가 옳게 변환되었는지 확인해 볼 수 있습니다.

 

이처럼 commit 명령어를 사용하면, 손쉽게 컨테이너로부터 이미지를 얻어낼 수 있습니다. 하지만, commit방법에는 한 가지 아쉬운 점이 있습니다. 위의 예시처럼 핵심적인 환경만 세팅하고 이미지로 변환한다면 문제가 없겠지만, 일반적으론 컨테이너 내부에 자신도 무슨 짓을 해놓았는지 모르는 경우가 많습니다. 그런 상태에서 이미지로 변환을 한다면 프로젝트에 필요없는 설치 파일들이나 라이브러리들이 포함되어 효율적인 이미지가 되진 못하겠죠. 심지어 이렇게 변환된 이미지는 그 속이(레이어가) 어떤 구성을 가지는지 알 수 없습니다. 이 보다 더 체계적이고, 효율적인 이미지를 만드는 방법을 아래의 Dockerfile을 통해서 소개하겠습니다.

Dockerfile을 통해 이미지 생성하기

dockerfile은 원하는 image를 생성하기 위한 레시피 역할을 합니다. 우리가 만들고자 하는 이미지가 어떻게 구성되어 있는지 모든 정보를 다 담아 놓은 파일이죠. 마치 원하는 음식을 만들기 위한 레시피와 비슷합니다.

위에선 commit 명령어로 이미지를 만들었다면, 이번엔 dockerfile을 활용하여 이미지를 만들어 보겠습니다. 아래는 위와 동일한 역할을 하는 streamlit 대시보드를 위한 dockerfile입니다.

FROM python:3.9-slim

WORKDIR /app

RUN apt-get update && apt-get install -y \
    build-essential \
    curl \
    software-properties-common \
    git

RUN git clone https://github.com/streamlit/streamlit-example.git .

RUN pip3 install -r requirements.txt
  • [FROM]
    이미지를 생성할 때, 처음부터 만들기보다는 잘 만들어진 이미지를 베이스로 두고 그 위에다 원하는 기능을 추가하며, 원하는 이미지를 만듭니다. 이때, 어떤 이미지를 베이스로 할지 정해줍니다. 해당 예제에서는 python:3.9-slim을 기반으로 한 이미지를 만듭니다.
  • [WORKDIR]
    아래에 계속될 여러 명령어들이 수행될 디렉터리 위치를 정해 줍니다. 해당 예시에서는 /app이라는 폴더 내에서 추후 명령들이 수행되도록 합니다. 만약 app이라는 폴더가 없다면, 새로 생성하게 됩니다.
  • [RUN]
    이미지가 생성될 때, 실행시켜 줄 명령어를 작성해 줍니다. 저희는 streamlit 대시보드를 실행할 것이기에, 이를 위한 명령어들을 작성해 줍니다. 이때, RUN 하나당 하나의 레이어가 생성되기에 무분별하게 RUN을 사용하는 것은 비효율적입니다. 해당 예시에선 모든 명령어를 하나의 RUN으로 처리하였습니다. (RUN 뿐만 아니라, ADD, COPY도 마찬가집니다. 효율적인 dockerfile을 위한 글을 추천합니다.)

이렇게 완성된 dockerfile을 빌드하면, 우리가 원하는 이미지를 얻게 됩니다. 이때 dockerfile의 파일이름은 항상 dockerfile로 둡니다. 이제 해당 dockerfile이 있는 위치로 가서 아래 명령어를 통해 이미지를 빌드해 보겠습니다.

docker build -t [repository:tag] [dockerfile의 위치]
docker build -t dashboard:dockerfile .
  • 위의 명령어는, 아래의 그림처럼 Dockerfile을 Image로 변환해 줍니다.

build의 역할

docker images로 생성된 이미지를 확인해 보면, 우리가 빌드한 이미지를 확인할 수 있습니다.

 

해당 이미지가 잘 생성되었는지, 컨테이너로 실행시켜 보겠습니다.

docker run -p 8080:8080 -it --name dashboard dashboard:dockerfile /bin/bash

 

그 후 실행되는 명령창에 아래와 같이 streamlit을 실행시켜 주면 대시보드를 localhost:8080으로 접속할 수 있습니다.

streamlit run streamlit_app.py --server.port=8080 --server.address=0.0.0.0

 

 

이때, dockerfile을 잘 활용하면, 추가적인 명령어 없이 바로 대시보드를 실행시킬 수 있도록 할 수 있습니다. 아래와 같이 dockerfile에 특정 명령문을 추가해 보겠습니다.

FROM python:3.9-slim

WORKDIR /app

RUN apt-get update && apt-get install -y \
    build-essential \
    curl \
    software-properties-common \
    git

RUN git clone https://github.com/streamlit/streamlit-example.git .

RUN pip3 install -r requirements.txt

ENTRYPOINT ["streamlit", "run", "streamlit_app.py", "--server.port=8080", "--server.address=0.0.0.0"]
  • [ENTRYPOINT]
    ENTRYPOINT는 컨테이너를 실행할 때 자동으로 특정 명령어를 실행시켜 줍니다. 보통 컨테이너 내부에서 항시 작동되어야 할 서버를 띄울 때 사용합니다. 이때 RUN과 ENTRYPOINT와 헷갈리지 마셔야 합니다. RUN은 이미지가 생성될 때 실행되는 것이고, ENTRYPOINT는 컨테이너가 생성될 때 실행된다는 차이점이 있습니다. ENTRYPOINT와 유사한 역할을 하는 CMD라는 것이 있습니다. 이 둘의 차이점을 간단히 살펴보고 가겠습니다.
    [CMD] : 컨테이너 실행 시, 실행되며 추가적인 파라미터로 대체될 수 있습니다.
    [ENTRYPOINT] : 컨테이너 실행 시, 무조건 실행되며 다른 파라미터로 대체될 수 없습니다.

위의 파일처럼, ENTRYPOINT를 작성해 주면, 컨테이너 실행 시 자동으로 streamlit의 대시보드를 실행시켜 주기 때문에 추가적인 명령 없이 대시보드를 띄울 수 있게 됩니다. 이제 위의 dockerfile을 dashboard:dockerfile_fix라는 이름의 이미지로 빌드해 보겠습니다.

docker build -t dashboard:dockerfile_fix .

 

이제 아래의 명령어로 컨테이너를 실행시키면 더 특별한 작업 없이 바로 대시보드가 실행되고, 저희는 localhost의 8080 포트로 해당 대시보드에 접근할 수 있게 됩니다.

docker run -p 8080:8080 -it --name dashboard dashboard:dockerfile_fix

 

 

Dockerfile을 활용하여 이미지를 빌드하는 방법에 대해서 알아봤습니다. Dockefile을 활용하면 아래와 같은 장점이 있습니다.

  1. 이미지가 어떻게 구성되어 있는지 명확하게 알 수 있다.
    dockerfile을 통해 해당 이미지가 어떤 레이어로 구성되어 있는지 파악이 가능합니다.
  2. 이미지를 공유하는 것에 비해, Dockerfile을 공유하는 것이 훨씬 수월하다.
    이미지의 용량은 꽤 클 수 있습니다. 용량이 큰 이미지를 공유하는 것보다, 스크립트 파일인 dockerfile을 공유하는 것이 훨씬 더 효율적입니다.
  3. 컨테이너 실행 시 특정 행동을 취할 수 있도록 지정해 줄 수 있다.
    위의 예시에서 본 ENTRYPOINT의 역할을 말합니다.

정리하자면!

나만의 애플리케이션 환경을 이미지로 만들기 위해 commitdockerfile을 활용하는 2가지 방법에 대해 알아봤습니다. commit은 사용 중인 컨테이너에서 바로 이미지로 만들어 낼 수 있는 아주 간단한 방법입니다. 현재의 컨테이너를 백업하고 공유하는데 유용하죠. 하지만, 이렇게 만들어진 이미지에선 내부를 살펴보기가 힘들며, 체계적이고 효율적인 이미지를 만들기 어려울 수 있습니다. dockerfile은 스크립트 파일로 부터 우리가 원하는 이미지 속성을 하나씩 명시해 주기에, 좀 더 체계적이고 필수적인 구성요소로 이미지를 만들 수 있습니다. 때문에 이미지가 어떤 레이어로 구성될지 쉽게 파악이 가능하죠. 또한, 스크립트 파일 하나로 공유할 수 있기 때문에 용량이 큰 이미지를 공유하는 것보다 더 효율적일 수 있습니다. commit과 dockerfile, 이 2가지 방식에 대해선 각각의 장단점이 존재할 수 있기에 환경에 따라서 선택해 우리가 원하는 이미지를 생성하면 되겠습니다.

 

Reference