AWS 프리티어 EC2 메모리 부족

AWS 프리티어 EC2 메모리 부족

AWS 프리티어 사용시 꼭 명심해야 할 내용

 

AWS 프리티어 EC2를 사용 중 겪은 문제다.

깃허브 웹 훅과 젠킨스를 이용한 CI/CD 구축을 성공적으로 마치고

jar를 실행하면 스프링 부트가 실행되며 EC2가 먹통이 돼버리는 현상이 발생했다.

젠킨스 로그는 온통 성공했다는 내역뿐이고

EC2가 그냥 갑자기 멈춰버려 시스템 로그조차 없는 상황이었다.

 

키보드 입력도 안 먹히고 외부 접속도 안된다. 강제 종료 - 재시작밖에 할 수 없는 상황이었다

 

젠킨스의 로그는 온통 성공뿐이고, 젠킨스가 쉘 스크립트를 실행한 직후 EC2가 뻗어버려서

“쉘 스크립트가 잘못됐나?”

라는 생각을 갖게 됐다.

그래서 아무 잘못 없는 애꿎은 쉘 스크립트를 붙잡고 반나절을 허비했다.

프로젝트


  • spring boot 2.4.3
  • gradle 6.8.2
  • java 11
  • jenkins 2.263.4

현상


Jenkins Build Excute Shell 단계

java -jar 명령어 입력 시 EC2가 정지되어 배포가 되지 않는 문제

AWS EC2 대시보드에서 재부팅하면 EC2는 정상화되나 이후 jar 재실행시 계속 멈춤

젠킨스에서 배포 자동화를 시도할 때에만 EC2가 멈추는 문제가 발생하며

SFTP로 EC2 접속해 직접 쉘 스크립트를 동작시키면 정상 배포

점검


gradle wrapper를 이용한 jar 빌드 자동화 테스트 정상

GitHub push시 webhook을 이용한 jar 빌드 자동화 테스트 정상

위 상황에서 빌드 자동화 후 쉘 스크립트 실행(배포)까지 넣으면 현재 문제 발생

gradle build 완료 후 jar을 이용한 쉘 스크립트

젠킨스가 job을 끝낼 시 하위 프로세스를 kill 하게끔 설계되어 있어 job이 끝나고

하위 프로세스가 배포를 진행 중 강제로 죽어버리는 상황이 발생.

때문에 dontKillMe 옵션을 추가하여 job이 끝나더라도 하위 프로세스는 죽이지 않도록 설정.

nohup 실행(no hang up) 시 출력 스트림이 종료되지 않아 프로세스가 대기상태로 들어가는 현상이 발생함으로,

출력 스트림을 끊어주기 위해 /dev/null & 옵션 추가

 

# file: 'jenkinsScript.sh'
target=/home/ubuntu
jenkins=/var/lib/jenkins/workspace/aaaaa/build/libs
filename=aaaaa.jar

echo "deleting ${filename}!"
cd $target
if test -e $filename
then sudo rm -f $filename
fi
echo "delete done!"

echo "copying ${filename}!"
sudo cp $jenkins/*.jar $target/$filename
sudo chmod 755 $target/$filename
sudo chown -R jenkins:jenkins $filename
echo "copy done!"

echo "stop service!"
sudo pkill -9 -f $filename
sleep 3

빌드후 생성된 jar를 따로 만든 폴더에 복사하고 권한을 젠킨스로 바꿔줌.

그리고 방금 가져온 jar를 실행하기 전에 실행 중인 jar 프로세스를 kill

 

Checking out Revision b513a321cdc7a84a6e3b605d69eb23d307dd9822 (refs/remotes/origin/master)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f b513a321cdc7a84a6e3b605d69eb23d307dd9822 # timeout=10
Commit message: "웹훅 테스트"
 > git rev-list --no-walk b513a321cdc7a84a6e3b605d69eb23d307dd9822 # timeout=10
[Gradle] - Launching build.
[aaaaa] $ /var/lib/jenkins/workspace/aaaaa/gradlew
Starting a Gradle Daemon, 3 busy and 1 incompatible and 4 stopped Daemons could not be reused, use --status for details
> Task :help

Welcome to Gradle 6.8.2.

To run a build, run gradlew <task> ...

To see a list of available tasks, run gradlew tasks

To see a list of command-line options, run gradlew --help

To see more detail about a task, run gradlew help --task <task>

For troubleshooting, visit https://help.gradle.org

BUILD SUCCESSFUL in 12s
1 actionable task: 1 executed
Build step 'Invoke Gradle script' changed build result to SUCCESS
[aaaaa] $ /bin/sh -xe /tmp/jenkins9723117549153308943.sh
+ /home/ubuntu/aaaaa/jenkinsScript.sh
deleting aaaaa.jar!
delete done!
copying aaaaa.jar!
copy done!
stop service!
Killed
+ echo start service!
start service!
+ export JENKINS_NODE_COOKIE=dontKillMe
+ export BUILD_ID=dontKillMe
+ echo started service success!
started service success!
+ nohup java -jar -Duser.timezone=KST /home/ubuntu/aaaaa-1.0.jar >> /dev/null &
Finished: SUCCESS

빌드 - 배포까지 정상적으로 완료됐다는 로그가 뜨며

직후 EC2가 정지됨.

해결


문득 어린 시절의 똥컴이 생각나며 “설마…메모리가 부족한가?” 라는 막연한 의심이 생겼다.

프리티어의 사양을 보니 맙소사.. 메모리가 1GB였다.

즉시 EC2를 재부팅시켜 정상화시키고 젠킨스만 띄운 후 메모리를 확인해보니 가용 메모리가 10%가 남은 상태였다.

리눅스에서 메모리의 상태를 확인하는 명령어는 free를 입력하면 된다

그 상태에서 스프링 부트를 띄우니 EC2가 멈춰버렸구나 라는 생각이 섬광처럼 스쳐 지나갔다.

메모리를 늘리거나 젠킨스와 스프링 부트를 분리시켜야 한다고 생각했다.

메모리를 늘리자니 과금을 해야 하고 분리시키자니 귀찮았다.

AWS에서 관련 키워드를 검색해보니 리눅스는 하드디스크를 가상 메모리로 전환시켜 사용할 수 있다는 정보를 확인했다.

이를 스와핑(Swapping)이라고 부른댄다.

AWS 고객지원 센터 링크

AWS에서는 메모리의 양에 따라 스왑 메모리의 크기를 아래와 같이 권장하고 있다.

 

물리적 RAM 크기권장 스왑 메모리
RAM 2GB 이하RAM 용량의 2배(최소 32MB)
RAM 2GB 초과, 32GB 미만4GB + (RAM – 2GB)
RAM 32GB 이상RAM 용량의 1배

💥 주의: 스왑 메모리는 절대로 32MB 미만이 되지 않아야 한다.

스왑 파일 생성


$ sudo dd if=/dev/zero of=/swapfile bs=128M count=16

1. dd 명령을 사용하여 루트 파일 시스템에 스왑 파일을 생성한다.


명령에서 bs는 블록 크기이고 count는 블록 수이다.

지정한 블록 크기는 인스턴스에서 사용 가능한 메모리보다 작아야 한다.

그렇지 않으면 memory exhausted 오류가 발생한다.

프리티어의 메모리는 1GB이니, 권장사항대로라면 2GB를 증설시켜야 한다.

이 예제 dd 명령에서 스왑 파일은 2GB(128MB x 16 = 2,048MB)이다.

$ sudo chmod 600 /swapfile

2. 스왑 파일에 대한 읽기 및 쓰기 권한을 업데이트


$ sudo mkswap /swapfile

3. Linux 스왑 영역을 설정


$ sudo swapon /swapfile

4. 스왑 공간에 스왑 파일을 추가하여 스왑 파일을 즉시 사용할 수 있도록 만든다


$ sudo swapon -s

5. 절차가 성공했는지 확인


$ sudo vi /etc/fstab

6. /etc/fstab 파일을 편집하여 부팅 시 스왑 파일을 활성화


/swapfile swap swap defaults 0 0

편집기에서 파일을 연 후 파일 끝에 다음 줄을 새로 추가하고 파일을 저장한 다음 종료하고,

free를 다시 입력하여 메모리를 확인해본다.


후기

컴퓨터가 뻗어버리면 메모리가 부족할 거라는 생각을 가장 처음으로 했어야 했는데,

평소 메모리 16GB 또는 32GB만 쓰다 보니 애초에 메모리가 부족할 것이라는 생각자체를 못했다.

결과론적으로 보면 정말 어처구니없는 실수다.

그래도 해결해내서 다행이고,

다음에 또 생각지 못한 상황을 마주친다면 그때에는 꼭 기초적인 것부터 점검해나가는 시도를 해야겠다.

그리고 평소 프리티어를 자주 사용하는데, 메모리를 2GB 늘려 총 3GB로 써보니 EC2가 정말 많이 빨라졌다.

앞으로 프리티어를 사용할 일이 생긴다면 이 옵션은 Default로 놓고 가야겠다고 생각했다.


© 2022. All rights reserved.