GitHub

코인 자동 매매 시스템 구축에서 문제가 생긴 이유 | aws ec2

hojun lee · 07/02/2025
커버이미지

크게 3가지 문제

문제가 발생했다. 결국 휴먼 에러가 컸지만, 개발 구축 배포를 모두 하다보니 문제를 잡기가 쉬운 상황이 아니었다.

로컬 환경에서는 완벽하게 작동하던 암호화폐 자동매매 시스템이 AWS EC2로 배포 되면서 마주친 장애물들 이 있었다.

로컬에서는 되는데 클라우드에선 왜 안됨 ㅡㅡ!

의 전형적인 문제였고 나의 문제였다. 각각이 서로 다른 계층의 문제였기에 체계적인 접근이 필요했다.

1. Docker Compose와 Poetry 혼용 문제

문제 상황

로컬 개발 환경에서는 Poetry로 의존성을 관리하고, 배포 환경에서는 Docker Compose를 사용하려다 발생한 복합적인 문제.

해결방법 local과 product 환경 모두 docker compose로 통합하고 git으로 형상관리 진행

# 문제가 된 코드 패턴
from dotenv import load_dotenv  # Poetry 환경에서 추가됨
load_dotenv()  # Docker 환경에서는 불필요

ACCESS_KEY = os.getenv('UPBIT_ACCESS_KEY')  # None 반환

핵심 문제점

메모리 오버헤드: Poetry 런타임과 가상환경이 컨테이너당 80-100MB를 추가로 점유하여 t2.micro의 1GB RAM에서는 5개 서비스 운영이 불가능.

환경변수 충돌: Docker Compose의 env_file과 Poetry의 load_dotenv()가 충돌하며, 컨테이너 내부에서 환경변수가 제대로 로드되지 않았음.

빌드 캐시 무효화: Poetry의 pyproject.toml과 Docker의 requirements.txt가 서로 다른 의존성 버전을 참조하여 빌드 시마다 전체 재설치가 발생했.

해결 전략

1단계: 역할 분리

# 개발 단계 - Poetry로 의존성 고정
poetry export -f requirements.txt --output requirements.txt --without-hashes

2단계: Multi-stage Docker 빌드

FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

FROM python:3.12-slim AS production
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
# Poetry 런타임 제거로 메모리 절약

3단계: 환경변수 통일

# load_dotenv() 제거
# Docker Compose의 env_file만 사용
ACCESS_KEY = os.getenv('UPBIT_ACCESS_KEY')  # 정상 동작

4단계: docker compose 환경을 통일 그냥 다 엎어버리고 docker compose 환경으로 통일했다. 아주 속이 시원했다. local에서 test하고 git으로 push 한 뒤 ec2에서 clone하고 docker compose up --build -d 하니까 뚝딱 됐다. 같은 환경이 최고다.

2. Upbit API IP 검증 문제

문제 상황

로컬 환경에서는 완벽하게 동작하던 Upbit API 호출이 EC2 환경에서 계속 실패했다.

# 실패하는 API 호출
try:
    balance = upbit.get_balances()
    print(balance)
except Exception as e:
    print(f"API 호출 실패: {e}")
    # EC2에서만 "Unauthorized" 오류 발생

디버깅 과정

1차 시도: 인증 정보 검증

# API 키 존재 여부 확인
print(f"Access Key exists: {bool(os.getenv('UPBIT_ACCESS_KEY'))}")
print(f"Secret Key exists: {bool(os.getenv('UPBIT_SECRET_KEY'))}")
# 모두 True로 확인됨

2차 시도: 네트워크 연결 테스트

# EC2에서 Upbit API 서버 연결 확인
curl -I https://api.upbit.com/v1/market/all
# HTTP/1.1 200 OK - 네트워크는 정상

3차 시도: API 응답 상세 분석

import requests

response = requests.get('https://api.upbit.com/v1/accounts', 
                       headers={'Authorization': f'Bearer {jwt_token}'})
print(f"Status Code: {response.status_code}")
print(f"Response: {response.text}")
# "IP address verification failed" 메시지 발견

근본 원인과 해결

문제: Upbit은 보안상 API 사용 시 허용된 IP 주소에서만 호출을 허용한다. 로컬 개발 환경의 IP는 등록되어 있었지만, EC2의 공인 IP는 등록되지 않았다.

해결:

  1. AWS EC2 인스턴스의 탄력적 IP 주소 확인
  2. Upbit 개발자 센터 → API 관리 → 허용 IP 추가
  3. EC2 공인 IP 추가 후 API 정상 동작 확인
# 해결 후 정상 동작
balance = upbit.get_balances()
krw_balance = [b for b in balance if b['currency'] == 'KRW'][0]
print(f"원화 잔고: {krw_balance['balance']} KRW")

3. EC2 보안 그룹 매핑 문제

문제 상황

Streamlit 대시보드를 8501 포트에서 실행했으나, 외부에서 http://공인IP:8501 접속이 불가능.

체계적 디버깅 과정

1단계: 애플리케이션 레벨 확인

# EC2 내부에서 로컬 접속 테스트
ubuntu@ec2:~$ curl http://localhost:8501
...  # 정상 응답

2단계: 네트워크 바인딩 확인

# 포트 리스닝 상태 확인
ubuntu@ec2:~$ sudo ss -tulpn | grep 8501
tcp LISTEN 0 4096 0.0.0.0:8501 0.0.0.0:* users:(("docker-proxy",pid=15320,fd=7))
# 0.0.0.0으로 바인딩됨 - 정상

3단계: 운영체제 방화벽 확인

# Ubuntu 방화벽 상태 확인
ubuntu@ec2:~$ sudo ufw status
Status: inactive  # 방화벽 비활성화 - 문제 없음

4단계: Docker 포트 매핑 확인

# Docker 컨테이너 포트 확인
ubuntu@ec2:~$ docker compose ps
PORTS: 0.0.0.0:8501->8501/tcp  # 포트 매핑 정상

5단계: AWS 보안 그룹 확인 여기서 결정적인 문제를 발견.

근본 원인: 보안 그룹 생성 ≠ 인스턴스 연결

문제의 핵심:

  • ✅ 8501 포트를 허용하는 보안 그룹을 올바르게 생성했음
  • ❌ 하지만 해당 보안 그룹을 EC2 인스턴스에 연결하지 않았음
  • 📋 인스턴스는 여전히 기본 보안 그룹(SSH만 허용)을 사용 중

해결 과정

1단계: 현재 보안 그룹 확인 AWS 콘솔 → EC2 → 인스턴스 → 보안 탭에서 기본 보안 그룹만 연결되어 있음을 확인

2단계: 보안 그룹 추가

인스턴스 선택 → 작업 → 보안 → 보안 그룹 변경
→ 새로 생성한 보안 그룹(8501 포트 포함) 추가 선택
→ 저장

3단계: 즉시 적용 확인

# 브라우저에서 즉시 접속 가능
http://공인IP:8501  # Streamlit 대시보드 정상 표시

교훈과 예방책

보안 그룹 관리 체크리스트:

  1. ✅ 필요한 포트로 보안 그룹 생성
  2. 인스턴스에 보안 그룹 연결 (가장 중요!)
  3. ✅ 변경 사항 즉시 적용 확인
  4. ✅ 최소 권한 원칙으로 불필요한 포트 제거

결론: 계층별 문제 해결의 중요성

이 세 가지 문제는 각각 다른 계층에서 발생했습니다:

  1. 애플리케이션 계층: Poetry/Docker 환경 충돌
  2. API/서비스 계층: Upbit IP 인증 문제
  3. 네트워크 계층: AWS 보안 그룹 설정

각 문제는 독립적이었지만, 체계적인 디버깅 프로세스(내부 → 외부, 하위 → 상위 계층)를 통해 순차적으로 해결할 수 있었다. 특히 "로컬에서는 되는데 클라우드에서는 안 된다"는 문제의 90%는 네트워크 설정이나 환경 차이에서 비롯되므로, 환경별 차이점을 체계적으로 점검하는 것이 핵심.

앞으로 천천히 잘 확인하자. 뭔가 이상하다 싶으면 commit 하고 rollback 하고 다시 시작하자.

그게 제일 빠르다.