본문 바로가기

프로젝트

스캘핑 봇 개발 일지 #2. 속도 향상! Docker 컨테이너에서 Hot Reloading 환경 구축하기 (feat. Nodemon)

반응형

전략 테스트 전 개발 효율을 극대화하는 실시간 코드 반영 시스템

안녕하세요. 빗썸 자동매매 스캘핑 봇 개발 일지 세 번째 글로 다시 인사드립니다. (이전 시리즈는 여기에서 확인하실 수 있습니다.) 지난 시간에는 봇의 기본적인 아키텍처와 핵심 기능을 설계하는 데 집중했다면, 오늘은 봇 개발 과정의 생산성을 혁신적으로 높여줄 'Hot Reloading' 환경 구축에 대해 이야기하려 합니다. 빠른 시장 변화에 대응하는 봇 개발은 신속한 테스트와 수정이 필수적인데요, 매번 코드를 수정할 때마다 Docker 이미지를 재빌드하고 컨테이너를 재시작하는 과정은 생각보다 많은 시간을 소모하게 됩니다. 이러한 비효율을 해소하기 위한 Hot Reloading 환경 구축 과정을 자세히 공유합니다.

"개발은 속도다!" 왜 Hot Reloading이 필요한가요?

스캘핑 봇은 짧은 순간의 가격 변동을 포착하여 매매하는 전략이 핵심입니다. 따라서 개발 과정에서도 다양한 전략과 로직을 빠르게 적용하고, 그 결과를 즉시 확인하며 수정해 나가는 민첩함이 중요합니다. Docker 컨테이너 환경에서 개발을 진행할 경우, 코드 변경 시마다 컨테이너를 재배포하거나 이미지를 재빌드해야 하는 번거로움이 있습니다. 이는 개발 생산성을 저해하는 요인이 되죠. Hot Reloading은 코드를 저장하는 즉시 변경사항을 애플리케이션에 반영하여, 이러한 비효율을 해소하고 개발 시간을 획기적으로 단축시켜 줍니다. 이번 글에서는 Docker 환경에서 Nodemon을 활용하여 Hot Reloading 시스템을 구축하는 과정을 상세히 안내해 드리고자 합니다.

Docker 환경 이해하기: CMD vs. command, 그리고 Volume Mount

Hot Reloading 환경을 이해하기 위해서는 Docker의 기본적인 실행 방식과 볼륨 마운트 개념을 먼저 숙지하는 것이 중요합니다.

  • DockerfileCMDdocker-compose.ymlcommand: Dockerfile 내의 CMD는 이미지가 구동될 때 기본적으로 실행할 명령어를 설정하는 부분입니다. 이는 이미지의 '기본적인 실행 계획'이라고 볼 수 있습니다. 반면 docker-compose.yml 파일의 command는 특정 서비스를 실행할 때 어떤 명령어를 사용할지 구체적으로 지시하는 것으로, 마치 컨테이너에게 "이렇게 시작해라!" 하고 최종 명령을 내리는 것과 같습니다. docker-compose.ymlcommandDockerfileCMD보다 우선권을 가지므로, 동일한 명령어여도 docker-compose.yml의 설정이 적용됩니다.
  • volumes: - .:/app의 중요성: 로컬 코드 변경 사항을 컨테이너 내부로 실시간 동기화 docker-compose.ymlvolumes 설정 중 - .:/app은 다음과 같은 의미를 가집니다.
    • .: 이 명령어(docker-compose)가 실행되는 호스트(사용자 개발 환경)의 현재 디렉토리를 의미합니다.
    • :/app: Docker 컨테이너 내부의 /app 디렉토리를 의미합니다. 이 설정은 호스트의 현재 디렉토리 내용을 컨테이너 내부의 /app 디렉토리와 실시간으로 동기화(마운트)시킵니다. 따라서 로컬에서 소스 코드를 수정하고 저장하면, 해당 변경사항이 컨테이너 내부의 파일에도 즉시 반영되어 Hot Reloading의 핵심 전제 조건을 충족시킵니다.
  • start_reloader.sh 스크립트의 도입 이유: while true와 같은 반복적인 로직이나 여러 명령어를 조합하여 실행해야 하는 경우, 이를 CMDcommand 필드에 직접 넣는 것은 가독성이 떨어지고 Docker 명령어의 본래 목적과도 맞지 않아 복잡합니다. start_reloader.sh와 같은 셸 스크립트는 이러한 복잡한 실행 로직(예: 파일을 계속 감시하다가 변경되면 재시작하는 코드)을 깔끔하게 담아둘 수 있습니다. 즉, Dockerfile이나 docker-compose.yml에 직접 작성하기 어려운 코드들을 실행시키는 '컨테이너 내부의 작은 실행 파일' 역할을 합니다. docker-compose.ymlcommand가 이 스크립트를 최초로 실행해 주는 역할을 합니다.

3. Hot Reloading 첫 시도: Watchgod와의 여정 (그리고 아쉬운 작별)

Python 프로젝트에서 Hot Reloading을 구현하기 위해 처음에는 watchgod 라이브러리를 고려했습니다. 파일 변경을 감지하여 Python 프로세스를 재시작하는 기능을 제공하는 유망한 도구였기 때문입니다.

초기에 다음과 같은 watchgod 명령어를 start_reloader.sh에 사용하여 시도했습니다:

exec watchgod auto python main.py .

또는

exec python -m watchgod --recursive --ext py --ignore-dirs=logs . -- python main.py

그러나 이 과정에서 unrecognized argumentsImportError와 같은 오류가 반복적으로 발생했습니다. watchgod의 버전이나 환경적인 요인으로 인해 명령어 형식이 예상대로 작동하지 않았고, 안정적인 작동을 확보하는 데 어려움이 있었습니다. 이러한 시행착오 끝에, 개발의 흐름을 끊지 않기 위해 좀 더 범용적이고 안정적인 대안을 찾아보기로 결정했습니다.

Nodemon 활용! Hot Reloading 환경 구축 상세 가이드

Python 프로젝트임에도 불구하고, Node.js 생태계에서 널리 사용되는 nodemon은 파일 변경 감지 및 프로세스 재시작 기능이 매우 강력하고 유연합니다. --exec 옵션을 통해 어떤 명령어든 실행할 수 있기 때문에 Python 프로젝트에서도 훌륭하게 활용될 수 있습니다.

 

4.1. Dockerfile 수정: Node.js, npm, Nodemon 전역 설치 과정 Dockerfile을 수정하여 nodemon이 실행될 수 있는 환경을 먼저 구축해야 합니다.

FROM python:3.10-slim

ENV PYTHONUNBUFFERED=1

WORKDIR /app

# Install Node.js and npm
RUN apt-get update && apt-get install -y nodejs npm

# Install nodemon globally
RUN npm install -g nodemon

COPY requirements.txt .

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

COPY . .

# start_reloader.sh 스크립트에 실행 권한 부여 (권한 문제 사전 방지)
RUN chmod +x ./start_reloader.sh

CMD ["./start_reloader.sh"]

이 단계에서는 apt-get을 통해 nodejsnpm을 설치하고, npm install -g nodemon 명령으로 nodemon을 전역에 설치합니다. 또한, RUN chmod +x ./start_reloader.shDockerfile에 명시하여, 이미지 빌드 시점에 스크립트에 실행 권한을 부여함으로써 이후 발생할 수 있는 'permission denied' 오류를 사전에 방지합니다.

 

4.2. start_reloader.sh 스크립트 작성 start_reloader.sh 스크립트에는 nodemon 명령어를 넣어 파일 변경을 감지하고 python main.py를 실행하도록 합니다.

#!/bin/bash

echo "--- Nodemon 기반 Hot Reloading 시작 ---"

exec nodemon --legacy-watch --watch . --ext py --ignore "logs/*" --exec "python main.py"
  • #!/bin/bash: 스크립트를 Bash 셸로 실행하도록 지시하는 '쉬뱅'입니다.
  • exec: 현재 셸 프로세스를 새로운 nodemon 프로세스로 교체합니다.
  • --legacy-watch: 이 옵션은 특히 Docker 환경과 같이 특정 파일 시스템에서 nodemon이 자동으로 파일 변경을 감지하지 못하는 문제를 해결하기 위해 사용됩니다. 이 옵션 없이는 변경이 감지되지 않는 경우가 많았기에 안정적인 작동을 위해 추가했습니다. 서버 성능 관점에서 --legacy-watch 옵션은 거의 영향을 주지 않습니다. 파일 감시 작업은 봇 프로세스와는 별개의 nodemon 프로세스에 의해 처리됩니다. nodemon의 역할은 단순히 파일 시스템을 주기적으로 확인하는(일반적으로 1초마다) 가벼운 작업입니다. 이 검사는 프로젝트 전체를 깊게 스캔하는 것이 아니라, 파일의 메타데이터(예: 마지막 수정 시간)를 간단하고 빠르게 확인하는 방식입니다. 따라서 봇의 핵심 거래 및 API 로직에 필요한 자원보다 훨씬 적은 CPU와 메모리를 사용하므로, 서버 부하에 대한 걱정은 내려두셔도 좋습니다.
  • --watch .: 현재 디렉토리(.) 내의 모든 파일 변경을 감지합니다.
  • --ext py: .py 확장자를 가진 파일들만 변경 감지 대상으로 지정합니다.
  • --ignore "logs/*": logs 디렉토리 하위의 파일들은 변경 감지에서 제외합니다. 로그 파일이 수시로 업데이트될 때마다 불필요하게 봇이 재시작되는 것을 방지합니다.
  • --exec "python main.py": 파일 변경이 감지되면 python main.py 명령을 실행하도록 합니다.

4.3. docker-compose.yml 최종 설정 확인 docker-compose.yml 파일은 start_reloader.sh 스크립트가 실행되도록 설정되어 있어야 합니다.

version: '3.8'

services:
  bithumb-bot:
    build: .
    image: mybot:latest
    container_name: bithumb-bot
    env_file: .env
    restart: unless-stopped
    working_dir: /app
    volumes:      
      - ./logs:/app/logs
      - .:/app # 로컬과 컨테이너 파일 시스템 동기화
    command: ./start_reloader.sh # Hot Reloading 스크립트 실행

여기서 volumes: - .:/app 설정은 여전히 로컬 코드 변경을 컨테이너로 동기화하는 데 필수적이며, command: ./start_reloader.sh는 컨테이너 시작 시 Nodemon 스크립트를 실행하는 역할을 담당합니다.

5. 배포 및 테스트: 드디어 실시간 반영!

모든 설정이 완료되었다면, 이제 서버에 배포하여 Hot Reloading이 제대로 작동하는지 확인합니다.

  • 변경된 파일들 서버로 전송: Dockerfile, start_reloader.sh, docker-compose.yml (필요하다면 requirements.txt) 파일들을 서버로 전송합니다.
  • Docker Compose 빌드 및 실행: 프로젝트 폴더에서 다음 명령어를 실행합니다.(--build 옵션은 Dockerfile이나 소스 코드가 변경되었을 때 항상 이미지를 새로 빌드하도록 지시합니다. 만약 현재 Docker 이미지 캐시를 따로 지우지 않는다면, 코드 변경 시에는 이 --build 옵션을 반드시 사용해야 변경사항이 컨테이너에 반영됩니다.)
  • docker-compose down # 기존 컨테이너 중지 및 삭제 docker-compose up -d --build # 새로 빌드하고 백그라운드에서 실행
  • 실시간 로그 확인 및 Hot Reloading 테스트:이 명령어로 봇의 로그를 실시간으로 확인합니다. 그 후 로컬 환경에서 main.py나 다른 .py 파일의 내용을 간단히 수정하고 저장해 보세요. 로그에 [nodemon] restarting due to changes...와 같은 메시지가 나타난다면, Hot Reloading 환경 구축에 성공하신 것입니다.

  • docker logs -f bithumb-bot

6. 결론: 더 빠르고 즐거운 봇 개발을 위한 첫걸음

이번 Hot Reloading 환경 구축은 빗썸 자동매매 스캘핑 봇 개발의 효율성을 극대화하는 중요한 밑작업이었습니다. 이제 코드를 수정할 때마다 번거로운 재배포 과정 없이 즉시 변경사항을 확인하며 개발에 몰입할 수 있게 되었죠. 이는 버그 수정 시간을 단축하고, 다양한 전략 아이디어를 빠르게 실험하는 데 큰 도움이 될 것입니다.

다음 시간에는 봇의 핵심인 매매 전략을 설계하고 실제 API를 통해 연동하는 과정에 대해 더 자세히 다뤄볼 예정입니다. 앞으로의 스캘핑 봇 개발 일지에도 많은 관심 부탁드립니다. 감사합니다!

728x90
반응형