소감 & 회고록

[회고] 인스턴스 용량 부족에서 서버 다운까지

왈왈디 2024. 9. 12. 18:10
728x90

최근 업무 중 테스트(QA) 서버가 다운되는 일이 발생했다.

긴박하게 복구해야만 하는 상황이었는데, 

팀원들끼리 빠르게 해결하지 못해, 결국 CTO님이 나서주셔서 복구되었다.

그 과정에 아쉬움이 남아, 다음에는 어떻게 더 잘 할 수 있을지 회고해보자.

 

사건의 발단

시작은 AWS Elastic Beanstalk의 인스턴스 상태가 Warning으로 표시되면서 

90% of root file system is in use. 800 MB free. 라는 경고문을 보여주면서였다.


경고라고 해도 서버에는 아무 이상이 없었기에 별다른 조치를 취하지 않았다.

 

이 상태로 계속 거의 매일 배포하면서 작업을 이어나가고 있었는데,

어느날 갑자기 배포 중 코드 파이프라인 배포 단계가 실패하며,

빈스톡에서는 인스턴스 상태가 No Data로 표시됐다.

(캡쳐를 안 해둬서 안타깝게도 사진이 없다.)

 

빈스톡이 노출하는 에러 메시지를 읽어보니,

저장 공간이 부족하다는 것이었다.

2024/09/05 03:07:54.843015 [ERROR] failed to populate deployment meta data with error failed to generate App version manifest file with error write /opt/elasticbeanstalk/deployment/app_version_manifest.json: no space left on device

 

서버는 정상 동작했으나, 배포가 안 됐다.

QA 기간이라 빠르게 배포가 이루어져야 하는 상황이었다.

 

저장 공간을 확보하고자 무언가 지워야 했고, 

마음이 급했던 팀원이 인스턴스에 접속해

rm -rf /opt/elasticbeanstalk/deployment/*

rm -rf /var/log

명령어를 실행시켰다.

 

그 이후로 서버도 동작하지 않고, 배포도 안됐다.

QA가 활발히 진행되던 시점이었고,

3시간 후 아주 중요한 앱 시연이 있는 상황이었다.

 

QA팀원들, 앱/프론트 개발자들 모두 업무가 중단됐다.

상황은 더 급해졌다.

 

문제의 원인

AWS 엘라스틱 빈스톡의 배포 방식에는 아래와 같이 5가지가 있는데 (AWS 공식 문서)

우리는 이 중 롤링 방식을 사용했었다.

(사실 배포 중 서버가 중단되지 않게 하려고 롤링 방식을 사용했는데,

이제보니 인스턴스가 1대일 때는 한 번에 모두 방식과 롤링 방식에 차이가 없다.

롤링 방식은 인스턴스가 여러 대일 때 인스턴스를 여러 그룹으로 나누어 순차적으로 배포하는 방식이다.

원하던 바를 위해서는 추가 배치를 사용한 롤링 방식을 사용했어야 했다.)

AWS 공식 문서

한 번에 모두 방식과 롤링 방식은 배포 시 인스턴스를 새로 생성하지 않는다.

그렇기에 인스턴스 내에 로그 파일의 크기가 매우 커졌던 것이다.

 

거의 매일 배포를 했음에도 인스턴스의 실행 시간이 30일이 넘어 있었다.

 

문제의 원인은 동일한 인스턴스를 오래도록 사용하면서 로그 파일을 정리해주지 않아, 

디스크 용량이 부족해진 것이었다.

 

우리는 /var/log 디렉토리를 통째로 삭제했고,

/var/log 디렉토리에는 운영 체제와 관련된 로그뿐만 아니라

애플리케이션, 서비스, 데몬 프로세스 등 다양한 로그 파일이 저장되어있는데,

여러 프로세스가 로그 파일을 찾지 못해 강제 종료되었던 것으로 추정된다.

 

디렉토리를 통째로 삭제하기보다 오래된 로그 파일을 확인 후 삭제하거나,

로그로테이트(logrotate) 등의 로그 관리 도구를 사용하는 것이 방법이었을 것 같다.

 

혹은 배포에 필요한 전처리 작업을 모두 자동화하도록 스크립트로 작성해두고

추가 배치를 사용한 롤링 방식으로 배포하는 것도 방법이다.

 

복구 노력

서버가 다운되고 최대한 빨리 복구하고자 다음 것들을 시도했다.

  • 사용 중이던 인스턴스 종료 후 재배포
  • 엘라스틱 빈스톡 환경 복제 후 배포
  • 엘라스틱 빈스톡 환경 새로 생성 후 배포

그러나 세 가지 방식 모두 인스턴스는 잘 떴으나

배포가 실패했다.

npm ERR! code ENOENT
npm ERR! syscall open
npm ERR! path /var/app/staging/package.json
npm ERR! errno -2
npm ERR! enoent Could not read package.json: Error: ENOENT: no such file or directory, open '/var/app/staging/package.json'
npm ERR! enoent This is related to npm not being able to find a file. npm ERR! enoent

 

그러다 우리 node 서버에서 java 코드를 실행시키기 위해

인스턴스에 JDK를 설치한 적이 있다는 사실이 떠올랐다.

 

sudo yum search java 로 검색했을 때 

아래 두 패키지 중 하나를 설치해야 할 것 같았다.

java-1.8.0-amazon-corretto

java-1.8.0-amazon-corretto-devel

 

검색해보니 전자는 runtime 환경에서 필요한 것이고,

후자는 개발 환경에서 필요한 것이라고 나와서

개발 환경이 아니니 전자를 설치하면 되겠다고 생각했다.

 

java-1.8.0-amazon-corretto 패키지를 설치하자

여전히 배포는 실패했고, 에러 메시지가 바뀌었다.

npm ERR! gyp: Call to 'node findJavaHome.js' returned exit status 1 while in binding.gyp. while trying to load binding.gyp
npm ERR! gyp ERR! configure error
npm ERR! gyp ERR! stack Error: `gyp` failed with exit code: 1

 

아무리 봐도 java 관련한 이슈인 것 같은데,

이미 java도 설치했고 

시간은 가는데 뭐가 문제인지 알아내지 못했다.

 

사건 종료

시연회 시작 30분 전에 투입되신 CTO님께서 에러 메시지를 보시더니

javac 설치가 안된 거 아니냐 물으셨다.

그 때 아차 싶으면서 java를 설치할 때 runtime 환경이 아니라,

dev 어쩌구라고 적힌 패키지를 설치해야 했다는 직감이 들었다.

 

그렇게 java-1.8.0-amazon-corretto-devel 패키지를 설치하고

상황은 종결되었다.

npm의 java 패키지 설치를 위해서는 JRE(Java Runtime Environment) 뿐만 아니라

JDK(Java Development Kit)가 필요했던 것이다.

 

서버가 복구된 시점은 시연회 시작 2분 전이었다.

 

반성하는 점

물론 팀원들 중 java에 익숙한 사람이 있었다면 더 빨리 해결됐을 수도 있겠지만,

java든 node든 문제의 종류를 떠나서

문제를 해결하는 과정에 대해 반성하게 됐다.

 

처음 문제가 발생했을 때 해결하던 사람은 나와 다른 팀원 둘이었다.

 

시간이 없는데 서버가 빨리 복구되지 않자,

선임 개발자분들이 한 분 씩 두 분이 더 투입되었다.

 

새로운 분들이 투입될 때마다,

지금까지 어떤 조치를 취했고, 그 조치로 인해 어떤 결과가 발생했는지

새로 만든 환경 이름은 무엇인지 다 다시 설명해야 했다.

 

설명하는 데에도 시간이 걸렸고,

이미 했던 조치를 다시 해보자는 의견도 있었다.

혹시 잘못 실행했을 수 있으니 다시 해보자는 것이었다.

 

그렇게 두명에서 네 명으로 늘어났는데,

마지막에 CTO님이 등장하시기 전까지 두 명이서 했던 조치 외에 새로운 시도는 해보지 못하고

지금까지의 상황을 설명하고 이미 했던 조치를 다시 하는 데에 시간을 다 썼다.

 

이 상황에서 내가 꼽은 아쉬웠던 점은 세 가지다.

 

첫 번째는 처음부터 어떤 해결책을 시도해볼 때

그것을 모두가 볼 수 있는 곳에 기록하지 않았다는 점이다.

 

처음 용량 부족 때문에 서버에서 실행한 명령어부터 종료시킨 인스턴스 id, 새로 실행한 인스턴스 id,

복제한 빈스톡 환경 이름, 새로 생성한 환경 이름, java 설치를 위해 실행한 명령어,

그리고 이 작업들을 각각 누가 직접 실행했는지 등을 모두가 볼 수 있게 정리했다면,

새로운 조력자가 투입되었을 때 불필요하게 시간이 소모되지 않았을 것이다.

 

그 대신 지금까지 취한 방안들을 합리적으로 검토하거나

해결을 위한 새로운 방안이 제시될 수 있었을 것이다.

 

두 번째는 감정적인 신뢰의 문제를 떠나서,

실행 내용에 대한 검토가 필요하다는 것이다.

 

처음 rm -rf /opt/elasticbeanstalk/deployment/*

rm -rf /var/log 명령어를 누군가 검토했다면 사건이 시작되지 않았을 것이고,
java-1.8.0-amazon-corretto 자바 설치 명령어를 검토했다면

문제를 더 빨리 해결했을 수 있을 것 같다.

 

즉, 1명이 실행안을 제시하거나/실행하고

1명이 검토하는 프로세스를 거쳤다면 좋았을 것 같다.

 

세 번째는 에러 메시지를 더 주의깊게 보지 않은 점이다.

매번 느끼는 것이지만 에러 메시지는 정말 많은 것을 이야기해준다.

 

에러 메시지를 자세히 읽고 신뢰했다면 java를 설치했음에도

여전히 java 관련 이슈가 해결되지 않았음을 알 수 있지 않았을까.

앞으로는 에러 메시지를 의심하기 보단 내가 어떤 착각에 빠져있는지를 의심해야겠다.

 

개선 방안

이슈 발생 시 효율적으로 문제를 해결하기 위해

이슈 해결 과정 기록 템플릿을 두고  작성하는 방식을 생각해봤다.

 

아래의 요소들을 작성하는 것이다.

  • 대응 방안
  • 세부 사항
  • 실행자
  • 실행 여부 (완료, 진행 중, 예정)
  • 실행 결과 (에러 발생 여부, 에러 메시지 등)
  • 검토자

예시는 아래와 같다.

대응 방안 세부 사항 실행자 실행 여부 실행 결과 검토자
api 빈스톡 복제 api-copy 라는 이름으로 복제 장왈왈 완료 처음과 동일한 에러 발생 김멍자
인스턴스 접속하여 yum으로 java 설치 sudo yum install java-1.8.0-amazon-corretto 명령어 사용 김멍자 진행 중 새로운 서버 에러 발생 npm ERR! gyp: Call to 'node findJavaHome.js' returned exit status 1 while in binding.gyp. while trying to load binding.gyp npm ERR! gyp ERR! configure error npm ERR! gyp ERR! stack Error: gyp failed with exit code: 1 이춘식
...          

 

728x90