본문 바로가기

책/파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습

11 모델 배포 (4) 도커(Docker), VSCode 실습

1. 도커란?

도커(Docker)는 컨테이너 기반의 가상화 플랫폼으로 애플리케이션을 구축, 배포, 실행하기 위한 오픈소스 플랫폼이다. 도커는 애플리케이션과 해당 종속성(라이브러리, 프레임워크, 환경 설정 등)을 격리된 환경으로 패키징하여 실행 가능한 단위인 컨테이너(Container)를 생성한다.

 

도커의 핵심 개념과 기능

컨테이너

  • 도커의 핵심 요소로, 격리된 실행 환경을 제공한다. 컨테이너는 애플리케이션과 그에 필요한 종속성을 포함하며, 호스트 시스템과는 독립적으로 실행된다.

 

이미지

  • 컨테이너 실행에 필요한 파일과 설정을 포함하는 읽기 전용 템플릿이다. 이미지는 컨테이너를 만들기 위한 기반이 되며, 도커 파일(Dockerfile)에 정의된 내용을 바탕으로 생성된다. 이미지는 여러 개의 컨테이너를 생성할 때마다 독립적으로 사용된다.

 

도커 파일

  • 도커 이미지를 생성하기 위한 빌드 스크립트를 의미한다.

 

도커 레지스트리

  • 도커 이미지를 저장하고 공유하기 위한 중앙 저장소를 의미한다. 대표적으로 도커 허브, AWS ECR(Amazon Elastic Container Registry), ACR(Azure Container Registry) 등이 있다.

 

컨테이너 오케스트레이션

  • 도커를 사용하여 여러 컨테이너를 자동으로 관리하고 조정하는 기능을 의미한다. 쿠버네티스와 같은 오케스트레이션 도구를 사용하여 컨테이너의 배포, 확장, 로드 밸런싱 등을 자동화할 수 있다.

 

 

파이토치 모델을 도커로 배포할 때 장점

환경 격리

  • 도커는 컨테이너화된 환경을 제공하여 모델 실행을 격리시킨다. 이는 파이토치 모델의 종속성 및 환경 설정에 대한 문제를 최소화하고 모델 실행 환경을 일관되게 유지할 수 있다.
  • 예를 들어 모델이 필요로 하는 특정 버전의 파이토치, CUDAN 라이브러리, 기타 프레임워크 등이 필요한 경우 도커 컨테이너 내에 이러한 종속성을 설정해 항상 일관된 환경을 유지할 수 있다.

 

환경 관리

  • 도커 컨테이너를 사용하면 모델 배포와 관련된 관리 작업을 단순화할 수 있다. 도커 이미지에는 코드, 환경 변수, 라이브러리 및 프레임워크 등에 대한 설정을 포함하고 있으므로 깃허브나 도커 허브와 같은 저장소에 업로드하여 버전 관리 및 파이프라인을 통합할 수 있다.

 

확장성

  • 도커는 컨테이너화된 애플리케이션의 확장을 용이하게 한다. 모델이 더 많은 데이터를 처리해야 하는 경우 도커 컨테이너를 여러 개 실행하거나 컨테이너 오케스트레이션을 활용해 컨테이너화된 모델을 클러스터에 배포하여 작업을 분산시킬 수 있다.

 

이식성

  • 도커 컨테이너는 독립적인 실행 단위이므로 모델을 개발한 환경과 상관없이 어디서든 실행할 수 있다.

 

유지보수

  • 도커는 버전을 격리해 관리할 수 있으므로 모델이 업데이트되거나 변경될 때 기존 환경에 영향을 주지 않으면서 새로운 버전의 모델을 배포할 수 있다.

 

 

2. 빌드 및 배포

모델 배포 포스팅에서 만들었던 Flask 모델 서빙 코드를 도커로 배포해 보는 실습을 해본다.

 

저는 Windows OS를 사용하고 있고 Docker desktop과 WSL2가 설치되어 있습니다. 그리고 VS code 환경에서 실습합니다.

 

 

도커 파일 작성

도커 파일은 도커 이미지를 빌드하기 위한 파일이다. 작업 경로에서 다음과 같은 디렉터리 구조가 되도록 한다.

Dockerfile(파일)
container(폴더)
L BertForSequenceClassification.pt
L app_flask.py
L requirements.txt

 

필자의 VScode 작업환경을 예시로 들면 다음과 같이 되어 있다.

 

Dockerfile은 파일명을 "Dockerfile"로 만들어야 도커 이미지를 빌드할 때 해당 스크립트를 찾아서 실행된다. 그리고 따로 확장자를 지정하지 않아도 알아서 저렇게 도커 이미지가 생기더라..(신기했음)

 

requirements.txt는 다음과 같다.

transformers==4.33.2
flask==2.3.3

 

 

이제 Dockerfile 스크립트를 보자.

FROM

  • Docker 컨테이너에 사용할 베이스 이미지를 지정한다. 'pytorch/pytorch' 이미지를 가져와서 태그를 2.2.1-cuda11.8-cudnn8-runtime으로 설정한다.

 

COPY

  • 로컬 디렉토리인 container의 내용을 Docker 컨테이너 내의 '/app' 디렉토리로 복사한다.

 

WORKDIR

  • Dockerfile에서 이후 명령들이 작업할 디렉토리를 '/app'으로 설정한다. 이후의 RUN 작업은 '/app' 디렉토리 내에서 진행된다.

 

RUN

  • requirements.txt에 명시된 패키지들을 pip를 사용하여 Docker 컨테이너에 설치한다. --no-cache-dir 플래그는 설치 중에 캐시를 사용하지 않도록 한다.
  • chmod +x : '/app' 디렉토리에 있는 app_flask.py 파일에 대해 실행 권한을 설정한다.

 

EXPOSE

  • 컨테이너가 실행될 떄 포트 8000에서 수신 대기할 것임을 Docker에 알린다.

 

CMD

  • 컨테이너가 시작될 때 실행될 명령을 지정한다. Python 인터프리터를 사용하여 app_flask.py를 실행한다. 해당 파이썬 스크립트 내부에는 Flask 애플리케이션을 실행하는 코드가 있으므로 Flask 웹 애플리케이션이 시작한다.
FROM pytorch/pytorch:2.2.1-cuda11.8-cudnn8-runtime

COPY container /app

WORKDIR /app

RUN pip install --no-cache-dir -r requirements.txt

RUN chmod +x /app/app_flask.py

EXPOSE 8000

CMD ["python", "app_flask.py"]

 

 

도커 이미지 빌드하기

이제 터미널에 다음과 같은 명령을 실행한다. docker build는 도커 이미지를 빌드하는 명령이다. -t 옵션은 빌드한 이미지에 태그를 지정하는 옵션이며, myapp이라는 이미지 이름을 사용하고, latest라는 태그를 지정한다. '.'은 도커 이미지를 빌드할 때 사용할 컨텍스트 경로를 나타내며, 현재 디렉터리를 컨텍스트 경로로 지정한다.

docker build -t myapp:latest .

 

 

빌드가 완료되면 docker images 명령으로 호스트 시스템에 있는 이미지들을 확인할 수 있다.

 

 

도커 컨테이너 실행

docker run : Docker 컨테이너를 실행한다.

-it : 이 옵션은 컨테이너를 대화형으로 실행하고, 터미널을 연결하는 역할을 한다.

  • '-i'는 stdin을 유지하고 '-t'는 tty를 할당하는 것을 의미한다. "stdin을 유지한다"는 표현은 컨테이너가 실행될 때 표준 입력(Standard Input)이 유지되어 사용자가 컨테이너와 상호작용할 수 있도록 하는 것을 의미한다. 예를 들어 컨테이너가 실행 중에 사용자로부터 입력을 받아야 하는 경우이다. "tty를 할당한다"는 표현은 컨테이너가 TTY(텍스트 터미널)를 할당받아 터미널 환경을 제공한다는 것을 의미한다. 이것은 사용자가 컨테이너 내부에서 터미널 명령을 실행하거나 대화형 프로세스를 시작하는 데 사용된다. TTY 할당이 없으면 터미널과 상호작용할 수 없고, 백그라운드에서 실행되는 프로세스와 같이 단순한 작업만 가능하다.

-d : 이 옵션은 컨테이너를 백그라운드에서 실행하도록 한다. 즉, 컨테이너가 실행 중일 때 터미널을 차지하지 않는다. 컨테이너를 백그라운드에서 실행하는 것에 대한 장점은 다음과 같다.

  • 자원 활용: 백그라운드에서 실행하는 컨테이너는 터미널에 접속하지 않아도 계속 실행된다. 이렇게 함으로써 터미널을 차지하지 않고 여러 컨테이너를 동시에 실행할 수 있다.
  • 비교적 적은 로그: 백그라운드에서 실행되는 컨테이너는 터미널에 로그를 계속 출력하지 않으므로, 실행 중에 로그가 너무 많이 터미널을 가로막지 않는다.
  • 서비스 환경에서의 유용성: 백그라운드에서 실행되는 컨테이너는 대부분의 서비스에서 필요로 하는 형태이다. 서버에서 웹 서비스를 운영하거나 백그라운드 작업을 수행하는 등의 경우, 컨테이너를 백그라운드에서 실행하여 지속적으로 서비스를 제공할 수 있다.
  • 자동화 및 배포: 백그라운드에서 실행되는 컨테이너는 일반적으로 자동화된 배포 및 관리 시스템과 통합된다. 이를 통해 컨테이너의 자동화된 스케일링, 로깅, 모너티링 등을 효율적으로 관리할 수 있다.
  • 유연성: 백그라운드에서 실행되는 컨테이너는 서비스를 제공하는 동안 사용자가 컨테이너와 상호작용할 필요가 없는 경우에 유용하다. 사용자가 필요한 경우 언제든디 컨테이너에 접근하여 명령을 실행할 수 있지만, 보통 이런 경우는 드물기 때문에 백그라운드에서 실행하는 것이 일반적이다.

-p 8000:8000: 이 옵션은 호스트의 포트 8000을 컨테이너의 포트 8000으로 매핑한다. 즉, 호스트의 8000 포트를 통해 컨테이너에 접근할 수 있게 된다.

--name mycontainer: 컨테이너에 이름을 지정한다.

myapp:latest: 실행할 Docker 이미지를 지정한다.

docker run -it -d -p 8000:8000 --name mycontainer myapp:latest

 

 

그런 다음 docker ps 명령을 수행하면 현재 실행중인 컨테이너의 이미지를 출력한다.

 

 

그럼 컨테이너가 잘 실행됐는지 포스트맨으로 데이터를 서버에 보내보고 response를 확인해보자.

 

flask_app.py는 시퀀스의 감정을 분류 작업을 수행하는 BERT로 추론하게 되어있는데 아주 잘 실행된 것을 확인할 수 있다.(겁나 신기하네)

 

 

도커 컨테이너 접속

이제 도커 컨테이너에 직접 접속하여 애플리케이션의 구조를 확인해본다.

 

  • docker exec: 실행 중인 Docker 컨테이너에 새로운 프로세스를 실행한다. Docker 컨테이너는 한 번 시작되면 하나 이상의 프로세스를 실행하고 있다. 여기서는 웹 서버를 실행 중이다. 컨테이너가 이미 실행 중인 상태에서 추가적인 작업이 필요한 경우 docker exec 명령을 사용하여 컨테이너 내에서 새로운 프로세스를 실행할 수 있따. 여기서는 새로운 Bash 쉘을 실행한다.
  • mycontainer: 컨테이너의 이름이나 ID
  • /bin/bash: 컨테이너 내에서 실행할 프로세스를 지정
docker exec -it mycontainer /bin/bash

 

 

컨테이너 내부 쉘에 ls 명령어를 입력하면 호스트 container 폴더가 app 폴더로 옮겨진 것을 확인할 수 있다.

 

호스트 터미널 세션으로 다시 돌아가려면 exit 명령어를 입력한다.

 

 

도커 컨테이너 중지하기

 

docker stop 명령으로 컨테이너를 중지시키면 활성화된 컨테이너 목록이 없는 것을 확인할 수 있다.

 

 

도커 컨테이너 재시작하기

 

 

도커 컨테이너를 이미지로 커밋하기

  • docker commit:현재 실행 중인 Docker 컨테이너를 이미지로 저장한다.
  • mycontainer: 이미지로 저장할 Docker 컨테이너의 이름이나 ID
  • myapp:newimage: 새로 생성될 이미지의 이름과 태그를 지정한다. myapp은 이미지의 이름이고 newimage는 태그이다.

 

 

도커 허브 로그인

도커 이미지를 허브에 푸시하기 위해 도커 허브에 로그인을 해본다.

docker login

 

 

현재 나는 docker desktop에 로그인이 되어있어서 자동으로 로그인에 성공한다.

 

 

도커 이미지 푸시를 위한 태그 생성

첫 번째 줄은 환경변수 DOCKER_ID_USER에 $username을 설정하는 것이다.

그런 다음 myapp:newimage에 새로운 태그가 추가된다.

export DOCKER_ID_USER="$username"
docker tag myapp:newimage $DOCKER_ID_USER/myapp:newimage

 

그런데 현재 나는 cmd에서 실행을 하고 있어서 명령어가 좀 다르다. 위의 명령은 bash에서만 수행되는 것 같다. cmd 환경이라면 다음과 같은 명령어를 작성한다.

 

 

도커 이미지 배포하기

 

도커 허브에서 업로드가 잘 되었는지 확인할 수 있다.

 

이제 추가적인 몇 가지 명령어를 알아보자.

 

pull 명령어로 특정 이미지를 호스트에 다운로드할 수 있다.

docker pull $username/myapp:newimage

 

 

rm 명령어로 특정 컨테이너를 삭제할 수 있다. -f 옵션은 컨테이너가 실행되고 있더라도 강제적으로 제거한다.

docker rm -f mycontainer

 

 

rmi 명령어로 특정 이미지를 삭제할 수 있다.

docker rmi -f myapp:latest

 

 

3. 도커에서 주로 사용되는 명령어

 

시스템 관리

명령어 설명
docker version 도커 엔진 버전 출력
docker system info 도커 시스템 정보 출력
docker system df 도커 시스템의 디스크 사용량을 보여준다
docker system prune 사용하지 않는 이미지, 컨테이너, 네트워크 등 시스템 자원을 정리한다.
docker login 도커 레지스트리에 로그인한다.
docker logout 도커 레지스트리에서 로그아웃한다.

 

 

이미지 관리

명령어 설명
docker pull 도커 이미지를 다운로드한다.
docker image ls 다운로드된 도커 이미지 목록을 출력한다.
docker image inspect 도커 이미지의 상세 정보를 출력한다.
docker image tag 도커 이미지에 태그를 추가하거나 변경한다.
docker push 로컬에서 작성한 도커 이미지를 Docker 레지스트리에 업로드한다.
docker search 도커 레지스트리에서 이미지를 검색한다.
docker image rm 도커 이미지를 삭제한다.
docekr image prune 사용하지 않는 도커 이미지를 정리한다.

 

 

컨테이너 실행

명령어 설명
docker container create 컨테이너 생성
docker container run 컨테이너를 생성하고 실행
docker container attach 실행중인 컨테이너에 접속
docker container exec 실행중인 컨테이너에서 명령어를 실행

 

 

컨테이너 정보

명령어 설명
docker container ps 실행중인 컨테이너 목록을 출력
docker container stats 실행중인 컨테이너의 CPU, 메모리 사용량 등을 모니터링 한다.
docker container inspect 컨테이너의 상세 정보를 출력

 

 

컨테이너 관리

명령어 설명
docker container stop 실행 중인 컨테이너를 정지
docker container start 정지된 컨테이너를 시작
docker container kill 실행 중인 컨테이너를 강제로 종료
docker container restart 실행 중인 컨테이너를 재시작
docker container prune 사용하지 않는 컨테이너를 정리
docker container pause 실행 중인 컨테이너를 일시 정지
docker container unpause 정지된 컨테이너를 다시 실행
docker container rename 컨테이너의 이름을 변경
docker container cp 컨테이너와 로컬 파일 시스템 간에 파일을 복사