빗썸 자동매매 스캘핑 봇 개발 일지 세 번째 글로 다시 인사드립니다. 지난 시간에는 (이전 글 참조) 봇 개발의 생산성을 극대화하기 위한 'Hot Reloading' 환경을 구축하는 방법을 다루었습니다. 하지만 실제 개발 과정에서 예상치 못한 여러 문제에 부딪혔고, 이 글은 그 문제들을 극복하고 마침내 완벽한 솔루션을 찾아낸 과정을 기록한 일지입니다. 같은 문제로 어려움을 겪고 있는 개발자 여러분께 도움이 되었으면 합니다.
Hot Reloading, 생각보다 까다로운 문제
스캘핑 봇 개발은 빠른 테스트와 수정이 필수적입니다. 이를 위해 로컬에서 코드를 수정하고 저장하면, 서버의 컨테이너가 자동으로 재시작되는 'Hot Reloading' 환경이 반드시 필요했습니다.
이전 설정을 통해 로컬의 변화가 서버에서 감지 되지 않는 오류를 확인했고 조사결과 서버에서 핫로딩하는 로직과 로컬과 서버의 구조를 일치시키는 로직은 있으나 로컬에서 서버로 변화된 내용을 전송하는 로직이 없다는 점을 깨달았습니다.
그래서 다음과 같이 흐름을 수정 했습니다:
- 로컬에서 코드를 수정하면,
- rsync를 통해 원격 서버로 파일이 자동 동기화되고 (Remote syncing)
- 서버의 Docker 컨테이너가 변경된 파일을 감지하여 봇을 재시작 (Docker volume syncing) -> 이 부분이 2회 차 설정 부분
하지만 이 간단해 보이는 과정은 여러 '벽'에 부딪혔습니다.
과정
윈도우와 리눅스 연동문제이니 리눅스환경으로 프로젝트 파일을 옮기는 것이 가장 간단한 방법이었지만 현재 깃과 연동되어 있어 옮길 수가 없어 리눅스폴더를 통해 설정하고 윈도우폴더의 변화를 감지하는 방식으로 구현하기로 했습니다.
PowerShell이나 커멘드라인을 관리자권한을 열고 로컬에 Linux 섭스트림 설치. 설치가 완료되면 wsl이란 명령어를 통해 리눅스환경으로 전환가능
wsl --install
리눅스 환경에서 rsync inotify-tools 설치
sudo apt-get update
sudo apt-get install rsync
참고로, 설치 시 비밀번호를 요구하는데 생각나지 않는 경우 루트유저로 로그인해서 변경가능
wsl --user root
passwd <userName>
home 폴더에서 .sh 파일생성하고 나노(리눅스 텍스트 편집기)로 열기
cd ~
nano ~/polling-sync.sh
이후, 아래코드 삽입 (키도 윈도우 폴더에 두고 필요시 복사 후 삭제하는 방식을 채택)
#!/bin/bash
# --- Configuration ---
LOCAL_PATH="/mnt/<projectPath>"
REMOTE_USER="ubuntu"
REMOTE_IP="<serverIP>"
REMOTE_PATH="~/<serverPath>"
SSH_KEY_NAME="<keyName>"
# --- Main Script ---
echo "Starting automatic sync to server (polling every 2 seconds)..."
while true; do
# Find files modified in the last 2 seconds (0.034 minutes)
# and store the list in a temporary file.
temp_file=$(mktemp)
(cd "$LOCAL_PATH" && find . -mmin -0.034 -type f > "$temp_file")
# Check if the temporary file is not empty
if [ -s "$temp_file" ]; then
echo "Change detected. Syncing..."
# Copy the key to a temporary location to bypass permission issues
cp "${LOCAL_PATH}/${SSH_KEY_NAME}" /tmp/${SSH_KEY_NAME}
chmod 600 /tmp/${SSH_KEY_NAME}
# Use rsync to copy the changed files
rsync -avz --progress --files-from="$temp_file" -e "ssh -i /tmp/${SSH_KEY_NAME} -o StrictHostKeyChecking=no" "$LOCAL_PATH/" "${REMOTE_USER}@${REMOTE_IP}:${REMOTE_PATH}"
if [ $? -eq 0 ]; then
echo "Sync complete."
else
echo "rsync error: Failed to sync files."
fi
# Clean up the temporary key
rm /tmp/${SSH_KEY_NAME}
fi
# Remove the temporary file
rm "$temp_file"
sleep 2
done
이후 파일실행
~/polling-sync.sh
시행착오
하지만 로컬이 윈도우인데 반해 서버는 리눅스라 두 OS를 부드럽게 연결하는 것이 생각보다 쉽진 않았는데요.
- 문제 #1: 로컬에서 파일 변경 감지 실패 처음에는 inotifywait나 find 명령어로 로컬 파일 변경을 감지하려 했지만, 윈도우 파일 시스템과 WSL(Windows Subsystem for Linux) 간의 파일 변경 이벤트가 제대로 전달되지 않는 근본적인 한계에 부딪혔습니다.
- 해결책: 이벤트 기반의 감지 대신, '폴링(Polling)' 방식의 스크립트를 도입했습니다. 일정 시간(2초)마다 모든 파일의 최근 수정 시간을 확인하는 방식으로, 운영체제 간의 이벤트 전달 문제를 완전히 회피했습니다.
- 문제 #2: rsync 권한 거부 오류 폴링 스크립트로 파일 변경을 감지하고 rsync를 실행하는 데 성공했지만, Permission denied (publickey) 오류가 발생했습니다. 이는 SSH 키가 윈도우 파일 시스템에 있어 rsync가 리눅스 권한 설정을 적용할 수 없었기 때문입니다.
- 해결책: rsync가 실행될 때마다 SSH 키를 /tmp와 같은 리눅스 파일 시스템으로 임시 복사하여 권한 문제를 해결했습니다. 이 방법은 원본 키를 이동하지 않으면서도 rsync가 정상적으로 작동하도록 보장합니다.
최종 완성된 Hot Reloading 시스템
수많은 시행착오 끝에, 로컬에서 서버로 파일을 동기화하고, 서버 컨테이너가 이를 완벽하게 감지하여 재시작하는 최종적인 시스템이 완성되었습니다.
최종 docker-compose.yml 설정 command 필드를 직접 수정하여, start_reloader.sh같은 wrapper를 거치지 않고 nodemon을 직접 가동하도록 하는 스크립트를 내장시켰습니다. 이 방식은 로컬에서 파일을 수정하여 동기화할 때뿐만 아니라, SSH로 서버에 직접 접속하여 파일을 수정할 때도 즉각적인 핫 로딩이 가능합니다.
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: nodemon --legacy-watch --polling-interval 1000 --verbose --watch . --ext py,json --ignore "logs/*" --exec "python>
최종 Hot Reloading 작동 시퀀스 (로컬에서 polling-sync.sh작동은 매번 수동으로 필요 - 한계이기도 하지만 통제의 수단이기도 함))
- 로컬 변경: 개발자가 로컬에서 main.py 파일을 수정하고 저장합니다.
- polling-sync.sh 작동: 백그라운드에서 실행 중인 polling-sync.sh 스크립트가 2초마다 파일 변경을 감지합니다.
- rsync 동기화: 스크립트가 /tmp에 SSH 키를 복사하고, rsync 명령어로 변경된 파일을 서버로 동기화합니다.
- 컨테이너 재시작: 서버의 Docker 컨테이너가 find 명령어로 변경된 파일을 감지하고, pkill과 python main.py 명령어를 통해 봇 프로세스를 즉시 재시작합니다.
더 빠르고 즐거운 봇 개발을 위한 첫걸음
이번 Hot Reloading 환경 구축 과정은 단순히 코드를 실행하는 문제를 넘어, 개발 환경 자체의 복잡성을 이해하는 중요한 경험이었습니다. 비록 많은 시행착오가 있었지만, 그 덕분에 더 견고하고 신뢰성 높은 시스템을 구축할 수 있었습니다. 이제 코드 수정 시 번거로운 재배포 과정 없이 즉시 변경사항을 확인하며 개발에 몰입할 수 있게 되었죠.
다음 시간에는 봇의 핵심인 매매 전략을 설계하고 실제 API를 통해 연동하는 과정에 대해 더 자세히 다뤄볼 예정입니다. 앞으로의 스캘핑 봇 개발 일지에도 많은 관심 부탁드립니다.