2 분 소요

AWS EC2 (t2.micro) 인스턴스에 배포 중 문제 발생

Sprinter 프로젝트를 동아리방의 온프레미스 서버에 배포하였는데, 공인 IP를 얻으려면 대학원생 급이 아니고서야 학교측에서 쉽게 허가를 내 주지 않기 때문에 하는 수 없이 학교 외부 망에서도 접속하기 위하여 AWS를 사용하여 배포하였다. 대학생이므로 free tier를 사용하기 위해 AWS계정을 하나 더 만들었고, 무료로 사용할 수 있는 인스턴스 유형인 t2.micro를 선택하였다.

t2.micro의 가용 자원은 다음과 같다.

  • 1개의 vCPU (i386, x86_64)
  • 1 GiB 메모리

내 프로젝트는 Docker를 사용해서 4개의 Container를 띄워야 했다.

  • nginx (리버스 프록시용)
  • openjdk (백엔드)
  • mysql (데이터베이스)
  • node (프론트엔드)

docker compose로 컨테이너들을 일괄 실행했는데 …

------                        
 > [frontend build 6/6] RUN npm run build:    
2.435                                                 
2.435 > sprinterfe@0.1.0 build                      
2.435 > react-scripts build                            
2.435                                        
8.333 Creating an optimized production build...      
110.0    
110.0 <--- Last few GCs --->                      
110.0                                    
110.0 [22:0x7e6944d2c3f0]   106434 ms: Mark-sweep (reduce) 471.8 (487.6) -> 471.2 (487.3) MB, 768.4 / 0.0 ms  (average mu = 0.308, current mu = 0.018) allocation failure scavenge might not succeed                                                                                      
110.0 [22:0x7e6944d2c3f0]   107216 ms: Mark-sweep (reduce) 472.4 (487.3) -> 472.0 (488.1) MB, 777.5 / 0.0 ms  (average mu = 0.185, current mu = 0.006) allocation failure scavenge might not succeed
110.0 
110.0 
110.0 <--- JS stacktrace --->
110.0 
110.0 FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
------

node 컨테이너에서 빌드중에 메모리가 부족하여 중단되었다.
여기서 두 가지 해결 방안이 있었다.

  1. 조금 더 비싼 EC2 인스턴스 유형을 선택하여 확실히 해결한다.
  2. 스왑 파일을 만들어 부족한 물리 메모리를 스왑시킨다.

가급적 추가 비용을 지출하지 않는 방향을 원했으므로, 스왑 설정을 하기로 결정했다.

Linux에서의 메모리 스왑

우리가 흔하게 사용하는 Windows 운영체제는 물리 메모리가 부족해지면 자동으로 가상 메모리(페이징 파일)를 사용하여 스왑이 일어난다. 이때 스왑 파일의 용량은 자동 조절되며, 사용자가 직접 설정할 수도 있다.

그러나 내 인스턴스에는 Ubuntu 24.04가 설치되어 있다. 대부분의 Linux 배포판 OS는 메모리 스왑이 자동 설정되지 않는다. 그래서 사용자가 직접 스왑 파티션 또는 스왑 파일을 만들어야 한다. 스왑이 없으면 메모리 부족 시 OOM(Out Of Memory)으로 프로세스가 종료될 수 있다.

따라서 스왑 파일을 하나 만들고, 메모리 스왑을 설정하였다. 방법은 다음과 같다. 내용은 AWS의 기술 블로그를 참고했다. 링크

  1. dd 명령을 사용하여 루트 파일 시스템에 swapfile을 생성한다.
    $ sudo dd if=/dev/zero of=/swapfile bs=128M count=32
    여기서 if란 input file이며, if=/dev/zero로 설정되었다. /dev/zero파일은 무한한 0값을 출력하는 특수 파일이다.
    따라서 입력 파일을 0으로 꽉 채운다.
    of란 output file이며, of=/swapfile로 설정되었다. bs란 block size이며, 128MB로 설정되었다. count는 블록 수다. 따라서 128MB * 32 = 총 4GB의 swapfile을 생성하였다.

  2. swapfile의 읽기 및 쓰기 권한을 업데이트한다.
    $ sudo chmod 600 /swapfile

  3. linux의 스왑 영역을 설정한다.
    $ sudo mkswap /swapfile

  4. 스왑 공간에 swapfile을 추가하여 swapfile을 즉시 사용할 수 있도록 한다.
    $ sudo swapon /swapfile

  5. 절차가 성공적으로 완료되었는지 확인한다.
    $ sudo swapon -s

  6. 부팅 시 자동으로 스왑 파일 사용을 시작할 수 있도록 한다.
    $ sudo vi /etc/fstab
    다음과 같이 편집 후 저장 -> /swapfile swap swap defaults 0 0

  7. free 명령어로 메모리 상태를 확인해본다.
    $ free

     total        used        free      shared  buff/cache   available
     Mem:          980380      792024       81760         228      264884      188356
     Swap:        4194300      553472     3640828
    

swapfile을 만들어서 총 4GB의 메모리 스왑공간을 추가로 확보하였다. 그리고 나서 컨테이너들을 올려보았는데 … frontend 컨테이너가 또 죽어버렸다.

Node.js의 메모리 한도 명시적 확장

Node.js의 메모리 한도는 다음과 같다.

  • 64bit 시스템: 약 1400MB
  • 32bit 시스템: 약 700MB

이 값은 V8엔진의 기본 힙 메모리 크기 한도이며, Node.js는 이 한도를 넘으면 Javascript heap out of memory 에러를 발생시킨다.
따라서 frontend의 dockerfile을 수정하여 Node의 메모리 한도를 늘려주기로 했다.

ENV NODE_OPTIONS=--max_old_space_size=2048

이 설정을 해주고 나니 드디어 정상적으로 모든 컨테이너가 실행되어 성공적으로 배포하였다.

댓글남기기