시연 영상
https://youtu.be/z1jL7oujpFo?si=me8lxaVBkXbsyzJ-
뭐가 좋음?
깃 허브 액션을 써봐야겠다고 생각한 이유는 바로 이거다
이 뭣 같은 오라클 클라우드 때문,, 근데 안 쓸수도 없는게 인스턴스를 두개나 주고 엘라스틱 IP도 쓸수 있는데 심지어 공짜다.. 도저히 포기할 수 없는 옵션임ㄹㅇ
aws랑 똑같지만 aws에선 당연히 됐는데 왜 안되는 거??? 이러면서 고민한 부분이 바로 젠킨스이다.
spring boot를 주 백엔드 프레임워크로 쓰는 나는 자동배포가 아니면 안되는 몸이 되어 버렸다..
근데 오라클 클라우드가 제공하는 ec2는 무슨이유에서 인지 젠킨스 플러그인이 하나도 안깔린다. 고로 자동배포가 안된다.
이 때문에 다른 CI/CD 툴이 필요했던 참이었다.
그리고 나는 젠킨스를 쓰면서도 계속 기분이 나빴던 경험이 있었는데 바로 이것이다.
프리티어는 겁나 메모리가 작아서 조금만 무리를 줘도 뻗어버린다
그렇다 어느 정도는 이 녀석은 버티지만 예를 들어 이미 프로젝트가 하나 돌아가는 중에 다른 프로젝트를 빌드하면 ec2가 뻗어버린 기억이 많다..
그렇다면 그냥 로컬에서 빌드해서 빌드파일을 ec2에 올리면 안되나요??ㅜㅜ
안된다. 왜냐하면 이런 이유다.
내가 원하는건 깃 푸쉬가 원격서버의 재 배포를 행하는 것인데 만약 빌드를 로컬에서하고 배포를 원격에서 하려면 깃 푸쉬가 유발하는 웹훅이 로컬 피시를 조작해야되고 (일단 이지점에서 아웃인게 만약 조작이 되서 빌드가 된다하더라도 난 그 빌드과정이 안보이면 좋겠다고 개발에 방해되니까) 그리고 빌드용 레포지토리 빌드된 파일을 저장할 레포지토리를 따로 만들고 또 그 레포지토리에 웹훅걸어야 되고 이건 뭐 말이 안되는 짓이란 것이다!
근데 이 모든 것을 해결해주는 CI/CD 툴 깃헙 액션.. 어떻게 내가 시연영상을 만들었는지 아라보자
기술 핵심
1. 내 깃 레포지토리에 뭔가 변화(이슈든 뭐 커밋이든)가 일어나면 그걸 감지한 깃허브의 컴퓨터가 레포지토리의 약속된 위치에 있는 yml 파일을 읽는다.
2. 이 yml은 "깃허브의 컴퓨터야 이러이렇게 동작해줘"라고 레시피가 적혀있다. 즉 여기 적인 내용대로 깃허브의 컴퓨터가 동작한다. 로컬컴퓨터와 상관이 없다 즉 레시피에 폴더를 만들라, 파일을 만들라 이렇게 적혀있어도 내 레포지토리는 그대로다. 다른 사람들은 뭐 job이니 workflow니 얘기하는데 핵심은 이 yml파일이 있는 위치가 모두에게 약속되있고 레포지토리에 변화가 발생하면 이걸 읽고 깃허브컴퓨터가 동작한다는 것
3. 근데 이 yml파일엔 내 서버 컴퓨터가 동작하도록 만드는 코드도 담겨 있다.
4. 그럼 이 yml 파일의 내용을 요약하자면 (1) 푸쉬가 일어나면 그걸 감지한 깃허브컴퓨터가 빌드를 해주세요. (2)빌드한 파일을 내 서버컴퓨터에 옮겨주세요. (3)서버컴퓨터가 그 파일을 실행하게 해주세요. 라는 것
이제부터 이것을 구현해보자
만든 방법
레포지토리 만들어서 프로젝트를 담아줌
뭐 그냥 레포지토리 만들어서 프로젝트를 거기 넣어주면된다. 이건 뭐 너무 당연함
레포지토리에 조작해야 될 PC가 뭔지 어떻게 접속하는지 알 키(key)를 준다.
내 레포지토리에서 세팅에서 환경변수 편집 탭에 들어가서 4개의 변수를 만들어준다. 만약 개인키에 PassParase를 등록해 둔 상태라면 그것도 만들어 주는데 난 안만들어서 4개다
각각 {HOST: ec2 아이피, Key: ec2 접속하는 데 필요한 ppk를 openSSH 로 만든 내용물, PORT: 22(너무당연), USERNAME: ec2 접속 아이디 (ex. ec2가 우분투라면 ubuntu)}
ppk를 openSSH키로 만드는 방법은 puttyGen에서
1번으로 ppk 로드한 다음 2번으로 ssh키를 내보내면 된다.
Script 만들기
이제 액션 탭으로 간다
액션탭에는 뭐 다른사람들이 만들어 놓은 yml 파일이 많은데 그걸 선택하면 내 프로젝트 루트경로에도 해당 파일이 담긴 디렉터리가 생긴다. 이렇게 해당 파일을 만들어도 되지만 뭐 중요한건 해당 파일이 정해진경로에 있어야 된다는 것
나의 gradle.yml의 내용이다.
name: SSH Action Example
on: // 언제 깃허브 컴퓨터가 이 파일을 읽을 것인지 정함
push:
branches: [ "master" ] //마스터 브랜치의 푸쉬가 일어났을 때
pull_request:
branches: [ "master" ]
jobs:
deploy:
runs-on: ubuntu-latest // 깃허브 컴퓨터에 아래 동작을 수행하기 위한 OS 이미지
steps:
- name: Checkout Repository
uses: actions/checkout@v2 // 별의미없는거같은데 다른사람도 다적어주더라고
- name: Set up JDK
uses: actions/setup-java@v2
with:
distribution: 'adopt' // 이거 다른 거 써도 상관없는듯
java-version: '17' // 내 스프링부트가 사용하는 jdk버전을 써준다
- name: Build JAR
run: ./gradlew build // 이걸로 빌드함
- name: Deploy Prod use SCP
uses: appleboy/scp-action@master
with:
host: ${{ secrets.REMOTE_SSH_HOST }}
username: ${{ secrets.REMOTE_SSH_USERNAME }}
port: ${{ secrets.REMOTE_SSH_PORT }}
key: ${{ secrets.REMOTE_SSH_KEY }}
source: "./build/libs/*.jar" // 이걸 (아까 빌드해서 만들어진 거)
target: "/home/ubuntu/deploy" // 여기로 옮기겠단 뜻(내 서버 안에서 경로)
strip_components: 2 //strip_component는 주소를 /기준으로 잘라준다
- name: Transfer Deploy Script use SCP
uses: appleboy/scp-action@master
with:
host: ${{ secrets.REMOTE_SSH_HOST }}
username: ${{ secrets.REMOTE_SSH_USERNAME }}
port: ${{ secrets.REMOTE_SSH_PORT }}
key: ${{ secrets.REMOTE_SSH_KEY }}
source: "deploy.sh"
target: "/home/ubuntu/deploy" // 프로젝트 루트폴더의 deploy.sh를 서버로 복사하겠단 것
// 여기엔 내 서버컴퓨터가 어떤 동작을 해야하는지 써있다.
- name: Execute Server Init Script
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.REMOTE_SSH_HOST }}
username: ${{ secrets.REMOTE_SSH_USERNAME }}
port: ${{ secrets.REMOTE_SSH_PORT }}
key: ${{ secrets.REMOTE_SSH_KEY }}
script_stop: true // 별의미 없는 거긴한듯
script: chmod +x /home/ubuntu/deploy/deploy.sh && sh /home/ubuntu/deploy/deploy.sh
//deploy.sh를 동작시키는 것
스텝의 업무는 -로 구분된다. ${{ }}안에 있는 내용은 레포지터리에 알려준 환경변수이다.
보면 알겠지만 프로젝트 루트 폴더에 deploy.sh를 만들어 줘야 된다. 뭐 별 일없으면 안바꿀테니까 상관없지만 바꿀수도 있으니까..
이렇게 프로젝트 루트폴더에 해당 파일을 생성해서
#!/bin/bash
echo "> now ing app pid find!"
CURRENT_PID=$(pgrep -f demo) #demo는 실행중인 파일명을 뜻한 해당 파일을 실행하고 있는 프로세스 아이디를 찾아 저장하겠단거
echo "$CURRENT_PID"
if [ -z $CURRENT_PID ]; then # 만약 그런 프로세스가 수행되고 있지않다면
echo "> no ing app."
else # 그 프로세스가 수행중이라면
echo "> kill -9 $CURRENT_PID" # 해당 프로세스를 죽여서 포트를 확보하자(어짜피 실행중이었던 프로세스는 이전 버전의 프로젝트임)
kill -9 $CURRENT_PID
sleep 3
fi
echo "> new app deploy"
cd /home/ubuntu/deploy //14번 라인
JAR_NAME=$(ls | grep -v '\-plain' | grep '.jar$' | tail -n 1) #jar파일의 이름을 정규표현식같은거로 찾는거 여기서는 -plain이 안들어있는 .jar파일을 찾느다
echo "> JAR Name: $JAR_NAME"
# nohup java -jar -Duser.timezone=Asia/Seoul $JAR_NAME &
nohup java -jar -Duser.timezone=Asia/Seoul $JAR_NAME 1>nohup/stdout.txt 2>nohup/stderr.txt & # 14번 라인에서 말하듯 cd해서 이동한 주소에서 nohup이라는 폴더가 없으면 오류가 발생한다.
sleep 2
# end of the script
이렇게 하면 푸쉬를 했을 때 yml에 깃허브컴퓨터가 "아 yml 읽어보니까 지금 내가 여기 적힌대로 수행해야 되네"하고 생각하고 빌드를 한뒤 그 결과 파일을 내 서버 컴퓨터에 보내서 "야 니 레포지터리에 있는 deploy.sh가 이건데 읽어보고 이거대로 수행해!"이렇게 얘기하고 내 서버컴퓨터가 deploy.sh를 읽어서 그대로 수행을 하게되면서 자동 푸쉬가 일어난다.
주의
gradlew의 실행권한 이슈
gradlew 파일의 실행권한을 깃배쉬로 추가해줘야 github action에서 작동할 수 있다.
gradlew가 있는 디렉터리에서
git update-index --chmod=+x gradlew
을 입력해서 실행권한을 추가해주
결론
정말 훌륭한 툴인 것 같다 앞으로는 aws도 이것으로 할 것같다