Docker는 다양한 방식으로 활용할 수 있지만, 개인 개발 환경에서 사용되는 방법은 크게 두 가지로 나뉜다.
1️⃣ 하나의 서버에서 여러 사용자가 개별 개발 환경을 구축하는 방식
2️⃣ 일괄적으로 모델을 실행하고 평가하는 시스템을 구축하는 방식
나는 이전까지 첫 번째 방법을 사용하여 Docker 컨테이너를 실행한 후, 내부에서 개발하는 방식에 익숙했다. 하지만 이번에는 두 번째 방법으로 이미지를 직접 빌드하고 컨테이너 실행을 자동화하는 과정을 경험해보았다. 이 블로그에서는 다음과 같은 흐름으로 Docker 사용법을 정리한다.
1. 도커 사용 환경 비교
2. 이미지? 컨테이너?
3. 도커 명령어 - 정해진 이미지 내에서 컨테이너 생성
4. 도커 명령어 - 이미지 구축 및 컨테이너 생성 ★
도커 사용 환경 비교
서론의 연장선이기도 하니 스킵 가능
나는 지금까지 Docker 환경을 사용해왔지만, 직접 세팅을 하지 않고 docker run, docker exec 등의 명령어만 익혀 컨테이너를 생성한 후, 컨테이너 내부에서 개발하는 방식으로 작업해왔다. 이번에는 이미지를 직접 빌드하고, 컨테이너 실행을 자동화하는 방식을 시도해보고, 그 후기를 남겨보려 한다.
Docker를 개인 단위에서 활용하는 방법은 크게 두 가지로 나뉜다.
- 개별 개발 환경 구축 – 하나의 서버에서 여러 사용자가 각자의 컨테이너를 실행하여 독립적인 개발 환경을 유지하는 방식
- 자동화된 시스템 구축 – 동일한 작업을 반복 수행하는 애플리케이션을 컨테이너로 구성하고, 실행 및 평가 과정을 자동화하는 방식
이번 실험에서는 두 번째 방식, 즉 일괄적으로 모델을 실행하고 평가하는 시스템을 구축하는 과정을 다룬다. 비유를 하자면, 첫 번째 방식의 경우 하나의 컴퓨터에서 각자의 파일을 만들어서 그 파일 안에서만 사용자들이 개발하는 것이랑 유사하고, 두 번째 방식의 경우에는 항상 동일한 작업을 하는 어플리케이션을 만든 것이라고 비유할 수 있을 것 같다. 물론, 두 방식을 그대로 나누기는 어렵고, 2번에서 작성한 환경에서 새로운 컨테이너를 생성해서 1번처럼 사용할 수 있기는 하다.
이게 무슨 말이냐고? 다음 챕터에서 알아보자.
이미지? 컨테이너?
Docker 에서 가장 흔하게 접할 수 있는 단어가 컨테이너와 이미지이다.
정의를 먼저 살펴보면 "이미지"는 실행 환경을 포함한 템플릿, "컨테이너"는 이미지를 기반으로 실행되는 독립적인 프로세스를 의미한다. 컨테이너를 실행할 때 이미지를 기반으로 동작을 하게 되는데, 이미지는 설정 환경을 포함하여 변하지 않는 템플릿이다.
Docker Hub에 공개되어 있는 이미지를 불러와서 사용할 수도 있다.
즉, 이미지를 한 번 생성해두고 나면 그 안에서 1번(개별 개발 환경 구축)과 같이 컨테이너를 생성해서 활용할 수 있는 것이다. 단, 이때 컨테이너 내에서 코드를 수정하고 이 내용이 그대로 유지되길 원한다면 -v 볼륨 마운트 조건을 추가해야 수정 사항이 날라가지 않고 유지된다. 이에 대한 것은 네 번째 챕터에서 다루도록 한다.
도커 명령어 - 기존 이미지 내에서 컨테이너 실행
이전에 작업했던 환경으로, 당시 로컬 리눅스 환경에서 GPU 서버를 원격 접속하여 사용하였다. 따라서 관리자가 생성해둔 이미지에 접속하여 컨테이너를 생성하였다. 이후 컨테이너를 접속해서 개별 폴더 및 파일을 생성하고, 코드를 작성하는 형태였다.
이 때 사용한 명령어를 간단하게 정리해보면 다음과 같다.
- docker run
docker run -itd \ -v <호스트 경로>:<컨테이너 경로> \ # 컨테이너와 로컬(또는 서버) 파일 동기화 -u <사용자 이름> \ # 컨테이너 내 사용자 --gpus all \ --name <생성할 컨테이너 이름> \ # 내가 새로 정하는 것! <이미지 이름> \ # 지정된 것! /bin/bash
- -v : 이 부분이 일전에 설명한 볼륨 마운트이다. 볼륨 마운트를 지정함으로써 컨테이너 내에서 코드를 수정하면 호스트에 그대로 반영되기 때문에 컨테이너를 삭제해도 수정한 내용이 유지된다!
- -u: 사용자 지정하는 명령어로, 사용자를 명명하지 않으면 기본적으로는 root 권한으로 실행돼서 이후 접근이 어려워질 수 있다..(본인이 관리자가 아니라면 꼭 명명해주자)
- 이미지 이름 : 이전 연구실에서는 ufoym/deepo 이미지를 가져와서 사용했다. 해당 이미지는 여러가지 장점이 있는데, 1) TensorFlow, PyTorch, Caffe, MXNet 등이 포함되어 연구 환경에 알맞고, 2) --gpus all 옵션으로 gpu 가속이 가능하다. 본인이 빌드한 이미지이든, docker hub 에서 받아온 이미지이든 상관 없다.
- docker exec : 컨테이너에 접속
docker exec -it <컨테이너 이름> bash
- docker stop & docker rm : 컨테이너 정지 및 삭제
요 명령어들은...사실 관리자 권한이 필요한 경우가 많고, 문제가 생기지 않는 한 잘 쓰지 않았던 명령어들이다.docker stop kyuheelim_torch_ecg # 실행 중인 컨테이너 정지 docker rm kyuheelim_torch_ecg # 컨테이너 삭제
사실 1번 외에는 거의 쓰지 않았는데, 나의 경우에는 어차피 원격 서버에 접속해야하기 때문에 vs code 상에서 remote ssh 연결해서 docker extension 설치 후 바로 접속해서 사용할 수 있었다...! 이 방식을 사용하면 파일 수정 할 때도 쉽게 파일 수정이 가능하고, 무엇보다 CLI 없이 vscode의 UI 를 사용해서 컨테이너를 관리할 수 있다.
도커 명령어 - 이미지 구축 및 컨테이너 생성
오늘의 핵심이라 목차에도 별표를 쳐보았다. 사실 이미지 구축은 처음이라..꽤나 시간이 많이 걸렸지만, 돌이켜보면 사실 정말 간단한 과정이었다.
먼저 실행을 위해서는 Docker Desktop이 필요하고, Window에서 wsl 을 사용할 때는 아래 그림과 같이 wsl 동기화 과정이 필요하다.
명령어를 소개하기 앞서, 이미지를 생성하기 위해 필요한 파일들을 먼저 소개하면, docker-compose.yml, dockerfile, run.sh 가 있다.
- (필수) Dockerfile: 도커 이미지 빌드
나는 dependency 를 고려하기 위해 poetry 라는 도구를 사용했다. Poetry 에서는 pyproject라는 파일을 만들게 된다. 내가 지정한 dependency를 설치할 수 있도록 COPY 및 RUN 명령어를 사용하여 poetry를 설치하였다.# Python 3.12 기반 컨테이너 생성 FROM python:3.12 # 작업 디렉토리 설정 WORKDIR /app # 필요 파일 복사 및 패키지 설치 COPY pyproject.toml poetry.lock ./ RUN pip install poetry && poetry install --no-root --no-interaction --no-ansi # 코드 복사 및 실행 스크립트 준비 COPY . . RUN chmod +x run.sh # 컨테이너 실행 시 실행할 명령어 CMD ["./run.sh"]
또한, 내 목표는 run.sh를 실행하는 것이었기 때문에 실행할 수 있도록 권한을 부여하는 chmod와 바로 실행하도록 CMD 명령어를 작성하였다. - docker-compose.yml : 자동화 및 여러 컨테이너 관리
docker-compose.yml 의 경우 필수적이지는 않지만, 1) 여러 컨테이너 실행 및 관리를 위해서는 필수적이고, 2) docker-compose.yml 파일이 없이면 실행 코드가 길어진다.나는 2번의 이유로 yml 파일을 추가하기도 했지만, 코드를 수정해도 자동으로 동기화 할 수 있도록 volumes 를 사용할 수 있고, openai 의 api key를 포함한 .env 파일을 사용하고자 env_file을 지정하여 사용하기 위해서도 필요하였다.version: '3' services: app: build: . container_name: my_python_app working_dir: /app volumes: - "./src:/app/src" # 호스트와 컨테이너 코드 동기화 env_file: - .env # 환경 변수 파일 적용 command: ["python", "app.py"]
- run.sh
사실 이미지 구축과 무관하다면 무관한 파일이다. 나의 경우 세 개의 py 파일(src/preprocess_pdf.py, src/retriever.py, src/agent.py)을 연속적으로, 자동으로 실행하도록 하기 위해 생성한 파일이다. 본 포스트와 관련이 없을 수 있기 때문에 아래 예시만 작성해 두고 넘어가도록 하겠다.#!/bin/bash set -e # 에러 발생 시 스크립트 중단 export PYTHONUNBUFFERED=1 echo "[1] Preprocessing PDFs..." poetry run python src/preprocess_pdf.py || { echo "Error in preprocess_pdf.py"; exit 1; } echo "[2] Building Vector DB..." poetry run python src/retriever.py || { echo "Error in retriever.py"; exit 1; } echo "[3] Running Agent Evaluation..." poetry run python src/agent.py || { echo "Error in agent.py"; exit 1; } echo "Done!"
이제 이 파일들을 실행할 명령어를 살펴보자.
- docker : 개별 컨테이너를 직접 실행하고 관리
docker 명령어는 기본적으로 1개 컨테이너를 관리할 때 사용하며, dockerfile 만을 필요로 한다.
- build : 이미지 빌드
docker build -t my_python_app .
- run : 컨테이너를 실행하는 명령어로, 이미지 빌드가 선행되어야 한다.
나는 docker-compose.yml 파일을 사용하고자 했기 때문에 단일 컨테이너를 생성하고자 하였어도 docker-compose 를 사용하였다. 하지만, dockerfile 만으로도 아래와 같이 -v, --env-file 등의 명령어로 실행할 수 있고, 원하는 패키지도 직접 설치할 수 있다.
docker run -it --name my_python_app_container \ -v "$(pwd)/data:/app/data" \ -v "$(pwd)/output:/app/output" \ --env-file .env \ my_python_app
- build : 이미지 빌드
- docker-compose : 여러 개의 컨테이너를 자동으로 실행하고 관리(단일 컨테이너에서도 사용 가능)
- up
이미지가 이미 빌드되어 있다면, 기존 이미지를 기반으로 모든 컨테이너를 실행한다.
docker-compose up
- build
docker-compose.yml을 사용하여 이미지 빌드만 수행하고 컨테이너는 실행하지 않는다. 여러개 컨테이너를 관리해야 할 때 컨테이너를 모두 실행하지 않고 이미지만 생성할 때 사용한다. docker-compose.yml에 정의된 모든 이미지를 한 번에 빌드할 수 있어서 docker build 와 비교하였을 때 자동화 및 유지보수 측면에서 유리하다.
docker-compose build
- build + up
나는 빌드와 실행을 동시에 하기 위해서 단일 컨테이너임에도 불구하고 이 조합으로 실행했다.
Dockerfile 기반으로 이미지가 자동으로 생성(docker build와 동일)되고, 해당 이미지를 기반으로 docker-compose.yml에 따라 컨테이너를 생성(docker run과 동일)한다.docker-compose up --build
- up
꽤나 긴 docker 사용기였다...! 사실 docker 자체를 이해하고 사용하기보다는 그때그때 필요한 명령어만 익혀왔는데, 이렇게 정리하고 보니 꽤나 유용한 툴인 것 같다. 다음엔 깃헙과 연동해서 사용할 수 있는 방법을 찾아봐야겠다.
'기술' 카테고리의 다른 글
[Tool] Poetry 사용기 (0) | 2025.02.21 |
---|