지금까지는 쭉 젠킨스를 사용해왔다. 젠킨스는 수많은 기능을 지원해주는 다양한 플러그인 생태계가 존재하고, 오픈소스이다. 지금까지 사용해왔을 때 딱히 아쉬운 점을 못 느꼈다. 다만 기본적으로 무거운 편이며 나는 젠킨스의 강력한 기능들을 다 사용할 필요도 없고, 쓰지도 않고 있기 때문에, 다소 과한 옵션이라고 생각이 든다.
젠킨스를 제외하고 다른 옵션을 시도할 때, 무엇이 베스트일까..
상황은 이렇다. 현재 AWS EC2 2대에 SpringBoot로 만든 서버를 배포해야 한다.
여러 개를 고민하다가, 깃허브 액션을 골랐다. 위의 조건 대부분을 달성할 수 있으면서 가장 간단하고 빠른 솔루션이라고 생각했다. 무엇보다, 회사 명의로 깃허브 엔터프라이즈 플랜을 신청해서 받아냈기 때문에 (내가 신청함..) 이 때가 적극적으로 써 먹어볼 기회라 생각했다.
mkdir ci-key
# 키 생성
ssh-keygen -t rsa -b 4096 -f ~/Downloads/ci-key/ci-key
# 새로 생성한 키를 접속할 서버에 등록합니다.
cat ~/Downloads/ci-key/ci-key.pub | ssh -i "pem키경로" <SSH_USER>@<SSH_HOST> "cat >> ~/.ssh/authorized_keys"
# permission denied 뜨면
chmod 400 "pem경로"
# 접속 테스트
ssh -i ~/Downloads/ci-key <SSH_USER>@<SSH_HOST>
# sudo 명령을 실행할 때 비밀번호를 입력하지 않도록
sudo visudo
shane ALL=(ALL:ALL) NOPASSWD: /usr/sbin/fuser -k 8080/tcp, /usr/bin/nohup
chmod 777 'path'
# workflow의 이름
name: api workflow
on:
workflow_dispatch: # manullay trigger
inputs:
BRANCH:
description: 'Branch to use'
required: true
default: 'dev'
type: choice
options:
- dev
- prod
- stage
- dev2
permissions:
contents: read
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }} # 무적권 main으로 빠진다..내가 생각한개념이 아닌듯..
jobs:
build: # build job
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
#ref: ${{ github.event.inputs.target-branch }} # 왜 안 먹히냐...
ref: ${{ inputs.branch }} # 내가 선택한 브랜치로 체크아웃
- name: Determine Branch
run: echo "${BRANCH_NAME}"
- name: input branch show Scripts
run: |
if [[ "${{ inputs.branch }}" == "stage" ]]; then
echo "hi stage"
elif [[ "${{ inputs.branch }}" == "dev" ]]; then
echo "hi dev"
elif [[ "${{ inputs.branch }}" == "prod" ]]; then
echo "hi prod"
elif [[ "${{ inputs.branch }}" == "dev2" ]]; then
echo "hi dev2"
else
echo "No specific script found for this branch. but maybe kt?"
fi
# - name: Set up JDK 17
# uses: actions/setup-java@v3
# with:
# java-version: '17'
# distribution: 'temurin'
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'corretto'
java-version: '17'
- name: Build with Gradle
run: ./gradlew clean build -x test
shell: bash
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: cicdsample
path: build/libs/*.jar
- name: Upload script
uses: actions/upload-artifact@v2
with:
name: cicdsample
path: scripts/*.sh
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v2
with:
name: cicdsample
- name: Setup SSH
uses: webfactory/ssh-agent@v0.5.4
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Add remote server to known hosts
run: |
mkdir -p ~/.ssh
ssh-keyscan ${{ secrets.SERVER_IP1 }} >> ~/.ssh/known_hosts
ssh-keyscan ${{ secrets.SERVER_IP2 }} >> ~/.ssh/known_hosts
- name: check File List
run: |
pwd
ls -al
- name: SCP transfer
run: |
scp deploy-dev.sh deploy-prod.sh *.jar ${{ secrets.SSH_USER }}@${{ secrets.SERVER_IP1 }}:/springboot/api/deploy
scp deploy-dev.sh deploy-prod.sh *.jar ${{ secrets.SSH_USER }}@${{ secrets.SERVER_IP2 }}:/springboot/api/deploy
- name: Execute remote commands
run: |
if [[ "${{ inputs.branch }}" == "dev" ]]; then
ssh -v ${{ secrets.SSH_USER }}@${{ secrets.SERVER_IP1 }} "sudo sh /springboot/api/deploy/deploy-dev.sh"
ssh -v ${{ secrets.SSH_USER }}@${{ secrets.SERVER_IP2 }} "sudo sh /springboot/api/deploy/deploy-dev.sh"
elif [[ "${{ inputs.branch }}" == "prod" ]]; then
ssh -v ${{ secrets.SSH_USER }}@${{ secrets.SERVER_IP1 }} "sudo sh /springboot/api/deploy/deploy-prod.sh"
ssh -v ${{ secrets.SSH_USER }}@${{ secrets.SERVER_IP2 }} "sudo sh /springboot/api/deploy/deploy-prod.sh"
else
echo "No specific script found for this"
fi
간단히 설명하자면, 내가 선택한 브랜치로 체크아웃하게 한 다음, 빌드에 필요한 기타설정을 세팅하고, 빌드를 진행한 뒤, 프로젝트 내의 scripts 폴더 안의 파일들과 jar파일들을 업로드한 후 다운받았다. 그리그 그 파일들을 미리 세팅해둔 키를 통해 각각의 인스턴스에 전송하고, 스크립트 파일을 실행하는 단계로 설정된다.
echo " "
echo "========================"
echo "Path move"
echo "========================"
cd /springboot/api/deploy
pwd
echo " "
echo "========================"
echo "remove exist process"
echo "========================"
PORT=2001
PID=$(sudo lsof -t -i:$PORT)
if [ -n "$PID" ]; then
# 프로세스가 실행 중인 경우 종료합니다.
echo "포트 $PORT를 사용하는 프로세스를 종료합니다 (PID: $PID)..."
sudo kill -9 "$PID"
sleep 5
else
echo "포트 $PORT를 사용하는 프로세스가 실행 중이지 않습니다."
fi
echo " "
echo "========================"
echo "Jar execute"
echo "========================"
# Gradle 설정에서
# tasks.jar {
# enabled = false
# }
sudo nohup /usr/bin/java -jar *.jar --spring.profiles.active=prod > nohup.log 2>&1 &
간단한 배포 스크립트 전문이다.
권한 설정 꼭 사전에 체크하자.. 이것 땜에 2시간 날렸다..
두 개의 JOB으로 구분되어 실행되는 걸 볼 수 있다.
두개의 인스턴스 모두 프로세스가 떠진 것을 확인할 수 있다.
과금 옵션이 아닌 기본 컴퓨팅 스펙
깃허브 액션의 컴퓨팅 파워가 답답하다면, 과금을 할 수도 있으나, SELF HOSTED RUNNER를 고려해볼 수도 있다.
들어가면 명령어 스크립트가 나올 것이다. 본인이 액션 호스트로 쓸 서버에 고대로 따라치면 된다. 실행하고, 라벨도 불여주자.
jobs:
build: # build job
runs-on: [self-hosted, macOS, ARM64, kang]
러너 교체
https://fe-developers.kakaoent.com/2022/220106-github-actions/
https://zzsza.github.io/development/2020/06/06/github-action/
https://shanepark.tistory.com/388
https://shanepark.tistory.com/465
https://stackoverflow.com/questions/58033366/how-to-get-the-current-branch-within-github-actions
https://www.daleseo.com/github-actions-artifacts/
https://danawalab.github.io/common/2022/08/24/Self-Hosted-Runner.html