SW 마에스트로에 합격하기 전부터 진행했던 팀 프로젝트가 벌써 마무리 단계에 있다. 마무리 작업을 진행하면서 여러가지 문제와 요구사항이 발생했다. 이러한 부분들을 코드에 반영할 때마다 EC2에서 매번 서버를 내리고 다시 jar 파일을 실행시키곤 했는데, 이 과정이 배우 비효율적이라 생각했기 때문에 최근에 배웠던 Github Actions를 사용한 CI/CD 파이프라인을 프로젝트에 적용시키기로 했다.
Github Action을 최근에 사용해보았기 때문에 별 문제 없이 빠르게 적용할 수 있을 것이라 생각했지만.. 뭐든 호락호락하게 넘어가는 법이 없다. 팀 프로젝트에 Github action을 적용하면서 이 전까진 볼 수 없었던 다양한 문제와 마주쳤고 이에 대한 해결 과정에 대해 기록하려 한다.
🔨 1. submodule
첫 번째 문제는 runner에서 프로젝트 파일을 build 시키는 도중 발생했다.
Test시 context를 불러올 수 없어서 생긴 문제였고 ConfigDataResourceNotFoundException 이 발생한 것으로 보아, 데이터 베이스 Connection 관련 문제일 것이라 생각했다.
하지만, 프로젝트의 test db는 In-memory DB인 H2 데이터베이스를 사용하고 있었고, Test 경로에 별도로 application.yml 파일을 만들지도 않았기 때문에 뭔가 다른 문제가 있을 것이라 생각했다.
그러던 도중, submodule의 존재를 깨닫게 되었다. 데이터베이스 접속 정보나 Oauth같은 토큰 값은 외부에 절대 노출되면 안되는 값이다. 따라서, Private Repository를 생성하여 해당 Repo를 기존 프로젝트에 submodule로 추가하여 보안에 민감한 값을 관리하게 되는데, 이 submodule을 받아오기 위해선 action store에서 제공하는 checkout에 설정을 추가해주어야 한다는 것을 알게되었다.
따라서, actions/checkout@v3에 다음의 설정 값을 추가해주었다.
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
token: ${{ secrets.GIT_TOKEN }}
submodules: true
- token: private repository에 접근하기 위해서 Git 계정의 token 값이 필요하다.
- submodules: true로 설정해주어야 프로젝트에 포함된 submodule코드까지 받아온다.
${{ secrets.GIT_TOKEN }} 값은 Git 계정에 대한 TOKEN을 발급받고 이를 actions에서 사용할 수 있도록 Actions 환경 변수에 추가해주면 된다.
그렇게 submodule까지 추가하고 나니 Actions 동작은 문제 없이 모두 수행되었다.
🔨 2. CodeDeploy 에러 - 중복 파일
Actions 동작에 이상이 없다는 것을 확인하고 CodeDeploy의 배포 현황을 확인해봤더니 다음과 같은 메시지와 함께 배포에 실패했다.
CodeDeploy - 배포 - 배포 내역에서 제일 아래쪽에 있는 View events를 클릭하면 어떤 과정에서 에러가 발생했는지 자세히 알 수 있다.
해당 화면에서 다음과 같은 문구를 볼 수 있었다.
"The deployment failed because a specified file already exists at this location: /home/ubuntu/..."
원인은 파일 경로에 이미 같은 파일이 존재하기 때문이었다. 본 문제를 해결하기 위한 방법은 2가지가 있다.
- BeforeInstall 동작에 기존 디렉터리 삭제 추가
- 이미 존재하는 파일을 덮어쓰도록 함
나는 2번 방식을 사용하여 해결하였다. 덮어쓸 수 있다면 굳이 디렉터리를 삭제해야하나 싶기도 했고 무엇보다 제일 간단했기 때문이다.
files:
- source: /
destination: /home/ubuntu/moduform
overwrite: yes
file_exists_behavior: OVERWRITE
files 속성에 overwrite: yes 와 바로 밑에
file_exists_behavior: OVERWRITE
구문을 추가하였다.
🔨 3. CodeDeploy 에러 - STDOUT
앞선 문제를 해결하니, 이번엔 또 다른 문제가 발생하였다.
"Script at specified location: scripts/deploy.sh failed to close STDOUT"
appspec.yml에 AfterInstall 동작의 timeout을 3분으로 설정하였는데, AfterInstall 동작이 정확히 3분째되고 배포에 실패한 것으로 보아 AfterInstall 동작으로 지정한 배포 스크립트에 문제가 있다고 판단하였다.
그리고 예상대로 배포 스크립트에서 문제를 찾을 수 있었다.
다음과 같이 & 옵션만 추가하여 실행하면 CodeDeploy에서 배포한 애플리케이션을 백그라운드에서 실행시킬 수 없다.
nohup java -jar $JAR_PATH &
다음과 같이 표준 오류 출력과 입력을 /dev/null파일로 리디렉션해야한다.
따라서 스크립트를 다음과 같이 수정했다.
# /dev/null 2> /dev/null < /dev/null &
nohup java -jar $JAR_PATH > /dev/null 2> /dev/null < /dev/null &
# Option
nohup java -jar $JAR_PATH --spring.profiles.active=prod > /dev/null 2> /dev/null < /dev/null &
🔨 4. CodeDeploy 에러 - EC2 용량 부족
또 새로운 에러가 발생했다. 하지만 이번 오류는 이전과 다르게 특이한 점을 발견할 수 있었다.
이전 에러는 AfterInstall단에서 발생하였는데 이번엔 배포 첫 이벤트에서 발생했다. 따라서 이전과는 전혀 관계없는 곳에서 새로운 문제가 발생했다고 생각했다.
그리고 에러 메시지를 읽어보니 device의 공간이 없다는 것을 알 수 있었다.
"No space left on device @ fptr_finalize_flush - /opt/codedeploy-agent/deployment-root/ongoing-deployment/..."
EC2에서 df -h 명령어로 용량을 확인해보았더니 Use %가 100인 것을 확인할 수 있었다.
(스크린샷은 못찍었습니다...)
따라서 EC2가 사용하고 있는 스토리지를 프리티어 최대 용량인 30GB로 늘리고 인스턴스를 재부팅 시켜주었다.
다시 df -h 명령어로 용량을 확인해보았더니 이전과 다르게 용량에 여유가 생긴 것을 확인할 수 있었고
다시 Actions를 run 시켜보았더니
마침내 배포에 성공할 수 있었다.
참고
- https://github.com/Woohan-TDD/book-aws-study/issues/6
- https://lemontia.tistory.com/1080
- https://prohannah.tistory.com/201
- https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/troubleshooting-deployments.html