CI는 지속적 통합, CD는 지속적 제공이라는 의미가 있다.
지속적 통합 Continuous Integration의 줄인 표현이다. CI는 변경 사항을 자동으로 테스트해 애플리케이션에 문제가 없다는 것을 보장한다. 그리고 코드를 정기적으로 빌드하고, 테스트하므로 여러 명이 동시에 작업을 하는 경우 충돌을 방지하고 모니터링 할 수 있다.
CD는 지속적 제공(Continous Delivery)과 지속적 배포(Continues delpolyment) 두 가지 의미를 모두 갖는다.
CD는 CI 작업을 끝낸 다음에 실행하는 작업이다. 배포 준비가 된 코드를 자동으로 서버에 배포하는 작업을 자동화하는 것이다.
깃허브 액션은 깃허브에서 제공하는 서비스이다. 리포지토리에 특정 이벤트가 발생하면 특정 작업을 하거나, 주기적으로 특정 작업을 반복할 수 있게 한다.
현재 만든 프로젝트를 먼저 리포지토리에 push 해준다.
github 리포지토리 -> Settings -> Secrets and variables -> actions에 값을 추가한다.

프로젝트 최상단에 .github/workflows 디렉터리를 생성한다.
그리고 그 아래에 ci.yml 파일을 생성해준다.

# worlflow 이름 지정
name: CI
# workflow가 시작될 조건 지정
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest # 실행 환경 지정
# 실행 스텝 지정
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '21' # 사용하고 있는 Java에 맞게 작성
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
env:
JWT_ACCESS_EXPIRATION: ${{ secrets.JWT_ACCESS_EXPIRATION }}
JWT_REFRESH_EXPIRATION: ${{ secrets.JWT_REFRESH_EXPIRATION }}
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
OAUTH2_GOOGLE_CLIENT_ID: ${{ secrets.OAUTH2_GOOGLE_CLIENT_ID }}
OAUTH2_GOOGLE_CLIENT_SECRET: ${{ secrets.OAUTH2_GOOGLE_CLIENT_SECRET }}
OAUTH2_NAVER_CLIENT_ID: ${{ secrets.OAUTH2_NAVER_CLIENT_ID }}
OAUTH2_NAVER_CLIENT_SECRET: ${{ secrets.OAUTH2_NAVER_CLIENT_SECRET }}
SPRING_DATASOURCE_PASSWORD: ${{ secrets.SPRING_DATASOURCE_PASSWORD }}
SPRING_DATASOURCE_URL: ${{ secrets.SPRING_DATASOURCE_URL }}
SPRING_DATASOURCE_USERNAME: ${{ secrets.SPRING_DATASOURCE_USERNAME }}
run: ./gradlew clean build
GitHub Actions를 사용하여 CI(Continuous Integration)를 수행하는 GitHub Actions Workflow 파일이다.
주요 내용은 main 브랜치에 push될 때 Java 21 환경에서 Gradle로 빌드하는 작업을 수행하는 것이다.
이제 이 파일을 리포지토리에 push 해준다.

워크플로가 성공적으로 동작하면 초록색 체크 모양으로 표시된다.
현재 프로젝트에서 빌드를 진행하면 두 가지 jar 파일이 생성된다. 이 중에서 plain 접미사가 붙은 파일은 애플리케이션 실행에 필요한 의존성을 포함하지 않고 소스 코드의 클래스 파일과 리소스 파일만 포함한다.
plain이 붙은 파일로는 서비스를 실행할 수 없으므로 일반 jar파일만 생성되도록 gradle을 수정한다.
jar {
enabled = false
}
ci 파일 이름을 cicd로 변경한다.
# workflow 이름 지정
name: CI/CD
# workflow가 시작될 조건 지정
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest # 실행 환경 지정
# 실행 스텝 지정
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '21' # 사용하고 있는 Java에 맞게 작성
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
env:
JWT_ACCESS_EXPIRATION: ${{ secrets.JWT_ACCESS_EXPIRATION }}
JWT_REFRESH_EXPIRATION: ${{ secrets.JWT_REFRESH_EXPIRATION }}
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
OAUTH2_GOOGLE_CLIENT_ID: ${{ secrets.OAUTH2_GOOGLE_CLIENT_ID }}
OAUTH2_GOOGLE_CLIENT_SECRET: ${{ secrets.OAUTH2_GOOGLE_CLIENT_SECRET }}
OAUTH2_NAVER_CLIENT_ID: ${{ secrets.OAUTH2_NAVER_CLIENT_ID }}
OAUTH2_NAVER_CLIENT_SECRET: ${{ secrets.OAUTH2_NAVER_CLIENT_SECRET }}
SPRING_DATASOURCE_PASSWORD: ${{ secrets.SPRING_DATASOURCE_PASSWORD }}
SPRING_DATASOURCE_URL: ${{ secrets.SPRING_DATASOURCE_URL }}
SPRING_DATASOURCE_USERNAME: ${{ secrets.SPRING_DATASOURCE_USERNAME }}
run: ./gradlew clean build
# 현재 시간 가져오기
- name: Get current time
uses: josStorer/get-current-time@v2
id: current-time
with:
format: yyyy-MM-dd'T'HH-mm-ss
utcOffset: "+09:00"
# 배포용 패키지 경로 저장
- name: Set JAR file path
run: echo "JAR_FILE=$(ls ./build/libs/*.jar | head -n 1)" >> $GITHUB_ENV
# 빈스토크 배포
- name: Beanstalk Deploy
uses: einaregilsson/beanstalk-deploy@v21
with:
aws_access_key: ${{ secrets.AWS_ACCESS_KEY }} # AWS Access Key
aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # AWS Secret Key
application_name: springboot-developer
environment_name: springboot-developer-env
version_label: github-action-${{ steps.current-time.outputs.formattedTime }}
region: ap-northeast-2
deployment_package: ${{ env.JAR_FILE }}

application_name과 environment_name은 Elastic Beanstalk에 나와있는 정보를 입력한다.
이제 AWS IAM에 접속한 뒤 사용자 -> 사용자 생성을 클릭한다.


이 권한은 빈스토크를 사용하기 위해 필요한 모든 관리 권한을 사용자에게 제공하는 권한이다. 이 권한은 너무 광범위 하기 때문에 정말 필요한 권한만 주는 것이 좋지만 편의를 위해 해당 권한을 사용한다.
생성 후 사용자 이름을 눌러 액세시 키를 만들어준다.


액세스 키는 이 화면에서만 확인 가능하기 때문에 값을 미리 복사하거나 .csv 파일은 다운로드 해준다.


복사한 값을 github secret에 등록해준다.
이제 이 파일은 push 해준다.
프로젝트 파일을 Elastic Beanstalk에 배포한 뒤 github actions을 확인한다.
CI/CD 구축을 해두니 현재 AWS 인스턴스의 메모리 부족으로 인해 문제가 발생하고 있다.
프로젝트 최상위에 .ebextensions 디렉터리를 추가해준다.
01_setup_swap.config
// 01_setup_swap.config
commands:
01setup_swap:
test: test ! -e /var/swapfile
command: |
/bin/dd if=/dev/zero of=/var/swapfile bs=128M count=16
/bin/chmod 600 /var/swapfile
/sbin/mkswap /var/swapfile
/sbin/swapon /var/swapfile
