Jenkins
Jenkins CI/CD의 장점
Jenkins는 오픈소스 CI/CD 도구로 다양한 플러그인과 함께 빌드 자동화 기능을 제공합니다.
우리의 소스코드는 GitHub나 GitLab에서 관리되고 Jenkins에서는 WebHook 이벤트를 통해 CI/CD가 가능합니다.
다양한 설정 옵션을 제공하며 팀의 요구사항에 맞게 자유롭게 구성할 수 있습니다.
Jenkins CI/CD의 단점
Jenkins를 위한 별도의 서버와 설치 번거로움이 존재합니다.
CI/CD를 담당하는 별도의 서버가 필요하며 설치 시 Context Path 문제로 Nginx의 Reverse Proxy가 제대로 동작하지 않는다는 문제와 다양한 플러그인 설치 등 해야 할 작업들이 많았습니다.
배포 스크립트 문법
배포 파이프라인 작성을 위해 스크립트 문법을 새로 숙지해야 한다는 문제가 많았습니다.
GitHub Action
GitHub에서는 Github Actions을 통한 CI 기능을 제공합니다.
GitHub Actions는 GitHub 레포지토리와 강력하게 통합되어 있어 설정이 간단하고 사용하기 쉽습니다. 별도의 서버나 외부 서비스를 구축할 필요 없이 GitHub에서 제공하는 기능으로 CI를 실행할 수 있습니다.
GitHub Actions는 워크플로우를 YAML 파일로 정의합니다. 이러한 선언적인 구조는 버전 관리를 용이하게 하며, 더 간편하게 변경 및 공유할 수 있습니다.
또한 이미 구현되어 있는 수많은 액션을 활용하여 간단하게 CI/CD 플로우를 작성할 수 있습니다.
GitHub Actuions의 장점
- 클라우드에서 동작하므로 어떤 설치 과정이나 설정이 필요가 없습니다.
- 비동기 CI/CD가 가능합니다.
- 모든 GitHub 이벤트에 대해 GitHub Actions을 제공하고 있으며 많은 언어와 프레임워크를 지원합니다.
- 모든 환경에서 호환됩니다.
- YAML 파일 작성으로 따로 배포 파이프라인 문법을 학습할 필요가 없습니다.
GitHub Actions의 구성요소
Workflows
GitHub Actions에서 최상위 개념인 워크플로(Workflow)는 자동화 해놓은 작업 과정을 의미합니다.
GitHub에서 이런 워크플로 파일 (.yml) 파일은 소스 코드 Repository 바로 아래에 .github/workfows 경로에 저장가능하며 여러 개의 워크플로를 생성할 수 있습니다.
.github/workflows/example.yml
# Event Trigger
on:
push:
branches: [main]
jobs:
....
Jobs
1. Jobs 속성을 통해 워크플로가 실행되면 수행할 Job을 정의
GitHub Actions에서 작업(Job)이란 독립된 가상 머신(machine) 또는 컨테이너 (Container)입니다.
하나의 워크플로에는 여러 개의 Job을 정의해 줄 수 있는데, 각각의 Job은 다른 Job과는 별개의 독립적인 환경에서 실행됩니다.
jobs:
job1:
# Job1에 대한 세부 내용
job2:
# Job2에 대한 세부 내용
jobs:
runs-on : ubuntu-latest
2. 코드 변경이 감지되면 배포 스크립트를 실행
name: CI/CD using github
# event trigger
on:
push:
branches: ["main", "develop" ]
permissions:
contents: read
3. ubuntu 버전 설정
jobs: # job들을 정의하는 곳
ci: # job의 id
runs-on: ubuntu-latest # 이 job을 수행하는 OS ex. windows-latest , macos-12
사용 가능한 OS들의 목록과 label은 아래 링크를 참고합니다.
4. Action 정의
CI 정의 : actions/checkout Action으로 Repository로 부터 CI를 수행합니다.
JDK 설정 : Github Actions에서 사용될 JDK를 설정합니다.
steps: # job에서 수행하는 step들을 정의하는 곳
- uses: actions/checkout@v3 # Repository로부터 CI 수행 서버로 코드를 내려받는 Action
- name: Set up JDK 11 # step의 이름
uses: actions/setup-java@v3 # jdk를 다운 받고 캐싱해주는 Action
with: # Action에 전달하는 input을 정의하는 곳
java-version: 11
distribution: 'temurin' # jdk를 제공하는 vender사 이름 ex. zulu, adopt, microsoft
Action 사용법
Action을 실행할 때는 uses 키워드를 사용하며 [소유자]/[저장소명]@[참조자]의 형태로 사용할 수 있습니다.
참조자는 보통 Action의 버전을 구분하기 위해 사용되며 위의 예시에서 v3 참조자는 Action 버전 태그를 의미합니다.
참조자에는 Action의 Version과 관련된 SHA 값이나 branch를 적어줄 수도 있습니다.
actions/setup-java
설정한 java-version과 vender 사에 맞는 JDK를 다운받아줍니다. 또 Github 캐시에 캐싱하여 이후에는 더 빠르게 다운받을 수 있도록 해줍니다.
Gradle 빌드 및 의존 라이브러리 캐싱
GitHub Action은 Job을 매 번 새로운 서버에서 실행합니다.
따라서 매 Job마다 서버에 프로젝트가 의존하는 라이브러리들을 새로 받아주어야 합니다.
이는 매우 비효율적이기 때문에 바뀌지 않는 라이브러리들은 GitHub에서 제공하는 Cache에 보관하고 재사용하는 것이 CI 성능에 좋습니다.
아래 코드를 통해 actions/cache Action을 이용하면 gradle을 캐싱할 수 있습니다.
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
path
- 캐싱할 파일이 있는 경로
- 캐싱했던 파일을 다시 복구할 경로
Key
- 이 Key와 함께 파일을 캐싱합니다.
- 이 Key를 통해 캐싱서버로부터 파일을 복구합니다.
${{ }} : expression을 선언하는 곳으로 expression은 변수나 함수등이 있습니다.
runner.os
runner라는 context에 있는 os라는 프로퍼티입니다. 현재 job을 실행하는 서버의 os를 string으로 반환합니다.
hashFiles(path)
path를 파라미터로 받고 이 경로에 존재하는 파일들을 이용해 하나의 해시를 반환하는 함수입니다.
gradle 설정에 변화가 생겼는지 검증하기 위해 이 해시를 추가해 줍니다. 만약 변화가 생긴다면 해시가 바뀌어 새로운 key로 새롭게 캐시 합니다.
5. application.yml 파일 생성 (Spring boot)
# 환경별 yml 파일 생성(1) - application.yml
- name: make application.yml
if: |
contains(github.ref, 'main') ||
contains(github.ref, 'develop')
run: |
mkdir ./src/main/resources # resources 폴더 생성
cd ./src/main/resources # resources 폴더로 이동
touch ./application.yml # application.yml 생성
echo "${{ secrets.YML }}" > ./application.yml # github actions에서 설정한 값을 application.yml 파일에 쓰기
shell: bash
# 환경별 yml 파일 생성(2) - dev
- name: make application-dev.yml
if: contains(github.ref, 'develop')
run: |
cd ./src/main/resources
touch ./application-dev.yml
echo "${{ secrets.YML_DEV }}" > ./application-dev.yml
shell: bash
gradle/gradle-build-action
gradle에서 제공하는 Action으로 Gradle을 사용해서 빌드하는데 도움을 주는 Action입니다.
이 Action은 build 이외에도 위에서 설명한 actions/cache를 이용하여 더 효율적이고 정교한 캐싱을 제공합니다.
따라서 actions/cache를 직접 사용하지 않고 이 Action을 이용합니다.
- name: Grant execute permission for gradlew
run: chmod +x gradlew # gradlew를 실행할 수 있는 권한을 추가
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: build
cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }}
6. Docker build & Push
# docker build & push to production
- name: Docker build & push to prod
if: contains(github.ref, 'main')
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -f Dockerfile-dev -t ${{ secrets.DOCKER_USERNAME }}/session-test .
docker push ${{ secrets.DOCKER_USERNAME }}/session-test
7. Docker run at AWS EC2
EC2 원격 접속에는 appleboy를 사용합니다.
## deploy to production
- name: Deploy to prod
uses: appleboy/ssh-action@master
id: deploy-prod
if: contains(github.ref, 'main')
with:
host: ${{ secrets.HOST_PROD }} # EC2 퍼블릭 IPv4 DNS
username: ubuntu
key: ${{ secrets.PRIVATE_KEY }}
envs: GITHUB_SHA
script: |
sudo docker ps
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/docker-test-prod
sudo docker run -d -p 9000:9000 ${{ secrets.DOCKER_USERNAME }}/session-test
sudo docker image prune -f
appleboy/ssh-action@master
GitHub Actions에서 사용할 수 있는 외부 액션(External Action) 입니다.
GitHub Actions에서 커뮤니티에 의해 공유되며, 다른 사람들이 만든 재사용 가능한 코드 조각들입니다.
원격 서버에 SSH를 통해 접속하여 원하는 명령어를 실행하는 기능을 제공합니다. 이를 사용하면 원격 서버에서 간단한 명령어 실행, 파일 전송, 디렉토리 복사 등과 같은 작업들을 CI 워크플로우에 포함시킬 수 있습니다.
host
- EC2의 public IP 주소 OR DNS
username
- 인스턴스 생성 시 선택한 OS의 기본 사용자 이름 (Ex, ubuntu)
key
- EC2 생성 시 받은 pem key의 내용
8. Secret 등록
application-dev.yml 의 내용과 DB정보, EC2 접속 정보 등을 외부에 공개하지 않기 위해 secret으로 등록할 수 있습니다.
Secrets and variables 하위에 등록할 수 있습니다.
CI/CD 결과
트러블 슈팅
" COPY with more than one source file, the destination must be a directory and end with a / "
gradle 플로그인 2.5 버전부터 gradle 빌드 시 JAR 파일이 2개 생성됩니다.
- 프로젝트 이름-버전-.jar
- 프로젝트 이름-버전-plain.jar
plain.jar에는 추가한 라이브러리 없이 작성한 코드만 들어 있습니다.
build.gradle.kts에서 아래 코드를 추가하면 해결!!
tasks.named("jar") {
enabled = false
}
'DevOps' 카테고리의 다른 글
Redis Sentinel을 이용한 고가용성 +EC2에 Docker-Compose를 이용한 실습 (3) | 2023.03.22 |
---|---|
Redis Replication 구축 (3) | 2023.03.02 |
Docker를 이용한 Nginx 설치 및 config 수정 (0) | 2023.02.03 |