Post

t2.micro에 프로젝트 욱여넣기

다수의 Docker 컨테이너로 구성된 MSA 프로젝트를 t2.micro 인스턴스에 배포하며 겪은 과정을 공유합니다.

t2.micro에 프로젝트 욱여넣기

서론

부스트캠프에서 지원받던 서버 크레딧이 소진되고, 진행했던 프로젝트의 배포는 잠시 멈춰있었습니다. 최근 다시 배포를 준비하며, AWS EC2의 프리티어 사양인 t2.micro 인스턴스에 올리는 도전을 시작했습니다. 이 글은 반짝이는 성공 후기보다는, 문제 해결 과정에서 겪었던 수많은 오류와 고민의 흔적을 담은 기록입니다. 저의 삽질 경험이 작은 도움이 되기를 바랍니다!

1. t2.micro로도 가능할까?

모든 일의 시작은 제 프로젝트가 t2.micro에서 버틸 수 있을지 분석하는 것이었습니다. 제 프로젝트는 여러 서비스가 얽혀있는 MSA 구조를 가지고 있었죠.

  • 애플리케이션 (5개): Nginx(FE), API Server, Collaborative Server (2개), Load Balancer
  • 데이터베이스 (3개): MySQL, MongoDB, Redis

각 서비스가 Docker 컨테이너로 실행될 때 필요한 최소 메모리를 어림잡아 계산해 봤습니다.

  • API Server (NestJS): ~150MB
  • Collaborative Server 2개 (WebSocket): ~400MB
  • 기타 서비스 + DB: ~750MB
  • 📊 총 예상 메모리 사용량: 약 1.3GB

t2.micro 인스턴스의 RAM은 고작 1GB이기 때문에, 어려움을 직감했습니다.

2. 전략 세우기

현실의 벽에 부딪힌 저는 새로운 배포 전략을 생각했습니다.

저의 우선적인 목표는 프로젝트를 일단 ‘배포’하는 것에 있으니, 최대한 지금까지의 프로젝트 코드를 수정하지 않고, 기존에 존재하던 CI/CD 로직도 건드리지 않는 선에서 진행했습니다.

  1. 서비스 축소: 로컬에서 2개로 운영하던 실시간 협업 서버를 배포 환경에서는 1개로 줄여 리소스를 확보했습니다.
  2. 배포용 설정 파일 분리: 이 전략을 바탕으로, 배포에 최적화된 docker-compose.deploy.ymlnginx.deploy.conf 파일을 새로 만들었습니다.

3. 쉘 스크립트 활용하기

부스트캠프를 진행하면서 초반에 쉘 스크립트를 배웠던 적이 있습니다. 그때는 주어진 요구사항을 요구하는데 급급했지만 이번에 배포 환경에서 실제로 활용해보면서 그 유용성을 크게 느낄 수 있었습니다.

3-1. 리눅스(우분투) 환경 설정

인스턴스를 처음 시작하고 나서 패키지 업데이트&설치하고, 메모리 스왑 설정하고, 방화벽을 열어주고, Git 설정하고 … 등의 많은 초기 설정이 필요합니다.

이러한 과정들을 쉘스크립트로 미리 적어놓으면 커맨드 한 번으로 모든 설정이 끝나니 매우 간편했습니다. 제가 사용했던 세팅 스크립트를 혹시 필요하신 분이 있다면 사용하셔도 좋을 것 같습니다.

1
curl -fsSL https://raw.githubusercontent.com/boostcampwm-2024/refactor-web29-honeyflow/chore/ec2-deploy/setup.sh | bash

위 커맨드를 실행하시면 패키지 업데이트, Docker 설치, Docker Compose 설치, 방화벽 설정(22,80,443,3000,4242,9001), 2GB 메모리 스왑 파일 생성, Git 기본 설정, 도커 명령어 별칭 설정이 진행됩니다.

자세한 스크립트는 여기서 확인하실 수 있습니다.

3-2. 도커를 통한 deploy

마찬가지로, 도커 명령어들에 대해서도 쉘 스크립트를 작성했습니다. docker 설치 여부 및 환경변수 존재 여부등을 검증하고, 기존 컨테이너 중지 후 빌드와 컨테이너 재실행이 이루어질 수 있도록 했습니다.

배포 스크립트 코드는 여기서 확인하실 수 있습니다.

4. 끝없는 트러블슈팅

이후 배포 시도에서 마주했던 문제들을 공유합니다.

4-1. Frontend 빌드 실패: “JavaScript heap out of memory”

  • 문제 상황: pnpm build 명령을 실행하자마자 프론트엔드 빌드가 멈추며 메모리 부족 오류를 뱉어냈습니다.
  • 원인 분석: Vite로 프로젝트를 빌드하는 과정에서 Node.js의 기본 힙 메모리(약 500MB) 제한을 초과한 것이 원인이었습니다.
  • 해결 방법: package.json의 빌드 스크립트에 node --max-old-space-size=4096 옵션을 추가해, Node.js가 빌드 시 더 많은 메모리를 사용하도록 허용해주었습니다.
  • 해당 방법 외에도, 배포 시마다 인스턴스를 재부팅하는 것이 더 확실하게 오류를 해결할 수 있었습니다. 근본적으로는 메모리 부족을 해소해야 합니다.

4-2. Nginx: “Host not found in upstream”

  • 문제 상황: 프론트엔드 컨테이너의 Nginx 로그에 host not found라는 익숙하지만 반갑지 않은 오류가 찍혔습니다.
  • 원인 분석: 개발용 nginx.conf에는 collaborative-room1room2 서버가 하드코딩되어 있었지만, 배포용 설정에서는 collaborative-server 하나만 존재했기 때문이었죠. 설정 파일 간의 불일치가 문제였습니다.
  • 해결 방법: collaborative-server 하나만 바라보도록 수정한 배포용 nginx.deploy.conf 파일을 만들어 환경별 설정을 완벽히 분리했습니다.

4-3. WebSocket 연결 실패

  • 문제 상황: 브라우저 콘솔에서 WebSocket connection to 'ws://...' failed 오류가 계속해서 발생했습니다.
  • 원인 분석: 프론트엔드는 /ws/room1-server/... 같은 경로로 WebSocket 연결을 시도하는데, nginx.deploy.conf에는 이 경로를 처리할 location 블록이 누락되어 있었습니다.
  • 해결 방법: /ws/로 시작하는 모든 요청을 collaborative-server로 프록시하도록 nginx.deploy.conflocation /ws/ { ... } 블록을 추가하여 해결했습니다.

4-4. CORS

  • 문제 상황: API를 호출하는 모든 기능이 500 에러를 반환했지만, 브라우저 콘솔에는 명확한 원인이 나타나지 않았습니다.
  • 원인 분석: API 서버 로그에서 Not allowed by CORS 오류를 확인했습니다. API 서버의 CORS 허용 목록에 배포된 프론트엔드의 출처(e.g., http://<서버 IP>)가 빠져있었습니다.
  • 해결 방법: API 서버의 CORS 설정에 배포 환경의 출처를 명시적으로 추가하고 이미지를 다시 빌드하여 문제를 해결했습니다.

5. 배포 시간을 줄일 수는 없을까?

어찌저찌 배포를 마쳤는데, 배포 시간이 약 1200s로 20분이나 되는 긴 시간동안 배포가 진행되어야 했습니다. 시간을 최대한 단축하기 위해 Docker에서 제공하는 Buildx를 사용했지만, 오히려 시간이 1시간이 넘게 진행되어 도중에 강제종료할 수밖에 없었습니다.

Buildx가 빌드 최적화를 위해 메모리를 추가적으로 사용해서 오히려 저사양 인스턴스에서는 시간이 더 소요되는 것 같습니다. 평소에는 ‘새로운 기술들이 오버헤드가 될 수 있다’라는 점에 대해서 크게 와닿지 않고 이론적으로만 아는 상태였는데, 저사양 환경을 경험해보니 실제로 큰 단점이라는 것을 느낄 수 있었습니다.

6. 결론

이번 프로젝트를 배포하는 과정을 겪으면서 느꼈던 점들입니다.

  • 환경 분리는 선택이 아닌 필수: 개발, 테스트, 프로덕션 환경은 각자의 목적에 맞는 별도의 설정(Docker, Nginx 등)이 필요합니다.
  • 모든 해답은 로그에: 브라우저 콘솔을 넘어서, 실제 오류가 발생한 서비스의 서버 로그를 가장 먼저 확인하는 습관이 중요합니다.
  • 빌드 캐시를 적극적으로 활용하기: 적절한 도커 커맨드를 사용하여 반복적인 배포 시간을 단축할 수 있습니다.
  • 성공적인 배포를 위해서는 Docker, Nginx 같은 인프라와 CORS, API 같은 백엔드 애플리케이션 로직, 전반적인 시스템에 대해서 이해가 필요합니다.

배포는 단순히 코드를 서버에 올리는 행위가 아니라, 애플리케이션이 동작하는 환경을 깊이 이해하고 최적화해나가는 과정이었습니다. 기존에 하던 프론트엔드 개발보다 더 큰 범위에서 생각해보는 것이 재미있기도 해서, 앞으로의 프로젝트에서도 배포에 더 신경쓰고 흥미있게 바라봐야겠습니다!

This post is licensed under CC BY 4.0 by the author.