ec2의 메모리 부족으로 인한 문제

Problem

Travis CI를 이용해서 배포 시스템을 구축하기 위해 아래의 과정을 진행했다.

  • ec2 인스턴스에 git 설치, 프로젝트 clone
      $ sudo yum install git
      $ git clone <remote-repository>
    
  • ec2 인스턴스에 jdk11 설치
      $ sudo curl -L https://corretto.aws/downloads/latest/amazon-corretto-11-x64-linux-jdk.rpm -o jdk11.rpm
      $ sudo yum localinstall jdk11.rpm
    
  • gradle.build와 동일한 위치에 .travis.yml 작성
      language: java
      jdk:
          - open: openjdk11
    
      branches:
          only:
              - main
      cache:
          directories:
              - '$HOME/Vibecap_Back/.m2/repository
              - '$HOME/.gradle
    
      script: "./gradlew clean build -x test"
    
      notifications:
              email:
                  recipients:
                      - <email>
    
  • Travis 회원가입 및 원격 리포지토리 연결
    Travis 사이트에 접속해 회원가입을 하고 깃허브 계저에 대해 Trial Plan을 적용했다. 그런데 organization과 개인은 plan이 따로 적용되었다. 그래서 organization에 대해서도 Trial Plan을 적용했는데 총 2달러가 결제되었다…

problem 1

We are unable to start your build at this time. You exceeded the number of users allowed for your plan. Please review your plan details and follow the steps to resolution. 라는 메세지를 보여줄 뿐 main 브랜치에 커밋을 push 해도 아무런 반응이 없었다.

errmsg

  • ec2에서 .jar파일 빌드 travis는 일단 문의 메일을 보내고 그 다음 과정인 빌드를 진행해보았다.
      $ ./gradlew build -x test
    

problem 2

gradlew을 통해 빌드를 시도하면 빌드가 진행되지 않고 시간만 흐르는 현상이 발생했다.

원인 파악

problem 2의 원인은 메모리 부족이다.
$ free 명령어를 사용하면 ec2 인스턴스의 메모리를 확인할 수 있는데 1GB정도였다.

ec2 memory

서버 애플리케이션이 사용중인 메모리가 370MB정도였고 사용 가능한 공간은 470MB였다.
gradle daemon은 최대 512MB를 사용한다고 한다.

solution

임시

gradlew을 실행하다가 서버가 멈춰버렸는데 갑작스럽게 서버를 업데이트해달라는 요청을 받았다.

update request

안 그래도 배포 과정을 자동화해야지 항상 생각했는데 이번 기회에 마무리했다.

deployment diagram

엄밀히 말하면 완전한 자동은 아니다. 두 개의 스크립트를 직접 실행해줘야 하기 때문이다.

  1. 코드 수정, remote repository에 push
  2. integrate.py 실행
    • .jar 빌드.
    • docker image 생성 -> docker hub에 업로드
  3. EC2 인스턴스에서 deploy.sh 실행
    • 기존에 실행중이던 docker container 중지
    • 중지된 컨테이너 삭제
    • 사용되지 않는 docker image 삭제
    • docker hub에서 이미지 다운
    • 다운받은 이미지로 컨테이너 생성 & 실행

지금까지는 3번 과정의 명령어를 하나씩 실행했었는데 훨씬 간편해졌다.

개발한 앱의 depencency와 코드가 많지 않고 로컬 PC가 서버보다 성능이 좋기 때문에 이 방법이 더 적합한것 같다.

deploy.sh

#! /bin/sh

# 기존에 실행중이던 컨테이너를 종료하고 삭제
echo "stop and remove docker container...\n"
docker stop vibecap-server
if [ $? < 0 ]
then
        echo "no container exist\n"
else
        docker rm vibecap-server
fi

# 더 이상 사용되지 않는 이미지들을 모두 삭제
echo "remove dangling images...\n"
docker image prune

# 도커 허브에서 새로운 이미지 다운로드
echo "pull docker image from docker hub...\n"
docker image pull mingeun2154/vibecap-server:0.0.0

# 다운받은 이미지로 도커 컨테이너 실행
docker container run -v /home/ec2-user/vibecap/logs:/app/logs -d --name vibecap-server -p 8080:8080 mingeun2154/vib    ecap-server:0.0.0

swap

swap space에 대한 자세한 내용은memory management 게시글 참고
swap space diagram

Ubuntu documentation에 따르면 RAM 용량이 1GB 이하인 경우에는 swap space를 사용하는 것을 강력 추천(highly recommend)한다. Swap space의 크기는 최소 (RAM 크기) * 1에서 최대 (RAM 크기) * 2를 권장한다.

disk는 main memory보다 1000배정도 느리다.
main memory에 접근하면 1초만에 끝나는 연산을 disk에 접근하면서 실행하면 15분정도 걸린다. (SSD는 1분정도)

swap space를 할당하는 방법

swap space를 생성한 뒤 free 명령어로 확인
free

진짜 swap space를 할당받으면 빌드가 정상적으로 진행되는지 확인해보았다.

$ ./gradlew build -x test

build successful 로컬에서 1~2초정도 걸렸는데 1분정도 걸렸다. ec2 인스턴스의 보조기억장치가 magnetic disk였으면 15분정도 걸렸을 것이다.

Travis CI 적용

게시글을 작성하면서 Travis와 메일을 주고 받다보니 problem 1이 해결되었다. 이제 swap space도 만들었으니 Travis를 통해 CI를 구현할 수 있게 되었다.

problem 3

build error

또 다른 문제가 발생했다.
.gitignore에 등록된 파일들을 인식하지 못해 필요한 symbol을 찾지 못하고 빌드에 실패하는 문제가 발생했다.

problem 3 해결방법

로컬에 travis를 설치하고 encrypt-file 을 이용해 파일을 암호화한다.

$ sudo gem install travis

travis의 도메인이 org에서 com으로 변경되면서 바뀐 내용들이 많다.

$ travis <command> --com <arguments...>
$ travis login --com --github-token <PAT>
$ travis encrypt-file --com <암호화 대상 .tar 파일> --add

–com 을 전달해야 한다.

before_install:
- openssl aes-256-cbc -K $encrypted_abe066967087_key -iv $encrypted_abe066967087_iv
  -in secret.tar.enc -out src/main/java/com/example/vibecap_back/global/config/security/secret.tar
  -d
- openssl aes-256-cbc -K $encrypted_4769db5680b9_key -iv $encrypted_4769db5680b9_iv
  -in prod-properties.tar.enc -out src/main/resources/prod-properties.tar -d
- tar xvf src/main/java/com/example/vibecap_back/global/config/security/secret.tar
- tar xvf src/main/resources/prod-properties.tar
  • src/main/java/com/example/vibecap_back/global/config/security/Secret.java secret.tar.enc로 암호화되어 업로드 되고 서버 host에서 복호화 한 뒤 원래의 위치에 압축을 해제한다.
  • src/main/resources/prod-properties 똑같이 암호화 -> 복호화 -> 원래 위치에 압축 해제 과정을 거친다.

build success

references

Tags:

Categories:

Updated:

Comments