- Published on
CI/CD
- Authors
- Name
- ywj9811
CI/CD
CI/CD란? - 지속적 통합(Continuos Integration)/지속적 배포(Continous Deployment)
CI란?
CI는 간단히 말하자면 빌드/테스트 자동화 과정을 말하는 것이다.
CI를 성공적으로 구현하게 되면 애플리케이션에 대한 새로운 코드 변경 사항이 정기적으로 빌드 및 테스트되어 공유 레포지토리에 통합되어 여러 명의 개발자가 동시에 애플리케이션 개발과 관련된 코드 작업을 할 경우 충돌 위험성을 해결할 수 있다.
이를 통해 코드/버전 관리에 대한 변경 사항을 정기적으로 커밋하여 팀원들이 동일한 작업 기반으로 진행할 수 있도록 하는 것이다.
이러한 지속적 통합은 CI/CD 파이프라인 구현하기 위한 첫 단계이다.
CD란?
CD는 간단히 말하면 배포 자동화 과정을 말하는 것이다.
즉, 이는 지속적인 배포를 의미하는 것으로 빌드, 테스트 및 배포 단계를 자동화하는 DevOps 방식을 논리적 극한까지 끌어올린다.
코드 변경이 파이프라인의 이전 단계를 모두 성공적으로 통과하면 수동 개입 없이 해당 변경 사항이 프로덕션에 자동으로 배포될 수 있다.
CI/CD 종류
- Jenkins
- CircleCI
- TravisCI
- Github Actions
- etc…
이렇게 여러가지 종류가 존재하는데 이 중에서 Github Actions에 대해서 알아보도록 할 것이다.
Spring Boot + Docker + Github Action 자동 배포
📌 DockFile 작성
우선 DockFile을 작성하여 Docker 이미지를 빌드하기 위해 지시문을 넣어준다.
FROM openjdk:11-jdk
ARG JAR_FILE=./build/libs/dashboardback-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
📌 EC2 인스턴스에서 작업에 필요한 Docker를 설치하도록 한다.
- Ubuntu 버전 -
- 우선 패키지 인덱스를 업데이트하고 새 HTTPS 리포지토리를 추가하는데 필요한 종속성을 설치한다.
sudo apt update
sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
- curl 명령어를 사용하여 리포지토리의 GPG 키를 가져온다.
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
- Docker APT 리포지토리를 시스템에 추가한다.
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
이제 도커 저장소가 실행되었으므로 저장소에 있는 모든 도커 버전을 설치할 수 있다
- 최신 버전 도커 설치
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
⚠️ 본인은 여기서 오류가 발생하였는데, 해당 오류 경로를 찾아가 vi편집기로 확인하자
- 설치 확인
sudo systemctl status docker
# ● docker.service - Docker Application Container Engine
# Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
# Active: active (running) since Thu 2020-05-21 14:47:34 UTC; 42s ago
# ...
결과가 주석과 같이 나오면 성공한 것이다.
- 루트가 아닌 사용자로 도커 실행 권한 부여
sudo usermod -aG docker $USER
이는 현재 사용중인 사용자에게 권한을 부여하는 것으로 $USER
가 현 사용자를 의미한다.
이때 주의할 점은 한번 껏다가 다시 실행하도록 하자 (권한 부여가 그제서야 됨)
- Linux 버전 -
📌 Docker 설치
# yum으로 Docker 설치
sudo yum install docker -y
# Docker 실행
sudo service docker start
# Docker 그룹에 sudo 추가 (인스턴스 접속 후 도커 바로 제어할 수 있도록)
sudo usermod -aG docker ec2-user
# 인스턴스 재접속(putty 껐다 킴) 후 Docker 명령어 실행해보기
docker run hello-world
🖥️ GitHub-Actions 스크립트 파일 생성하기
배포할 때 사용할 GitHub-Actions 스크립트 파일을 생성해 보도록 하자
GitHub 레포지토리 → Actions → Continuous integration 의 Java With Gradle 의 Configure 클릭
클릭하게 되면 아래와 같이 ~/.github/workflow/gradle.yml 파일이 생성된다.
이때, gradle.yml이라는 이름은 본인이 원하는 대로 변경해도 문제가 없으며 이는 현재 gradle build 전용 스크립트 파일이다.
📌 gradle.yml (현재 파일 이름) 작성 → 상황에 맞게 변경해서 사용
name: Java CI with Gradle
on:
push:
branches: ['master']
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
env:
working-directory: ./
APPLICATION: ${{ secrets.APPLICATION }}
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
- name: Cache Gradle packages
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- uses: actions/checkout@v2
- run: |
mkdir ./src/main/resources
cd ./src/main/resources
touch ./application.yml
echo "${{env.APPLICATION}}" > ./application.yml
- uses: actions/upload-artifact@v2
with:
name: application.yml
path: ./src/main/resources/application.yml
- name: Grant execute permission for gradlew
run: chmod +x gradlew
working-directory: ${{ env.working-directory }}
- name: Build with Gradle
run: ./gradlew build
working-directory: ${{ env.working-directory }}
- name: Cleanup Gradle Cache
if: ${{ always() }}
run: |
rm -f ~/.gradle/caches/modules-2/modules-2.lock
rm -f ~/.gradle/caches/modules-2/gc.properties
- name: Docker build
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t ${{ secrets.PROJECT_NAME }} .
docker tag ${{ secrets.PROJECT_NAME }} ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7}
docker push ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7}
- name: Deploy
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.EC2_SERVER_HOST }}
username: ec2-user
key: ${{ secrets.PRIVATE_KEY }}
envs: GITHUB_SHA
script: |
docker rmi $(docker images -q)
docker pull ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7}
docker tag ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7} ${{ secrets.PROJECT_NAME }}
docker stop ${{ secrets.PROJECT_NAME }}
docker rm ${{ secrets.PROJECT_NAME }}
docker run -d --name ${{ secrets.PROJECT_NAME }} -p 80:8080 ${{ secrets.PROJECT_NAME }}
이 과정은 GitHub 레포지토리 main 브랜치에 push가 될 때 AWS EC2 인스턴스에 배포가 되는 과정이다.
참고로 이때 노출되어서는 안되는 properties와 같은 파일은 깃허브에 올리면 안되기 때문에 GitHub-Actions의 스크립트에 직접 작성해야한다.
이제, 위 코드를 하나씩 분석해보도록 하자.
🔗 on: push: branch:
이는 해당 브랜치에 push가 되었을 때 Workflow를 Trigger 실행한다는 뜻이다.
name: Java CI with Gradle
on:
push:
branches:
- main
permissions:
contents: read
🔗 jobs:
GitHub-Actions의 Workflow는 다양한 Job으로 구성되며 Job은 다시 Steps로 구성이 된다.
- GitHub-Actions에서 사용될 JDK를 세팅
- **Java-Version으로 11을 사용하고, distribution으로 ‘temurin’**을 사용
jobs:
build:
runs-on: ubuntu-latest
# Ubuntu로 실행할 것이다.
env:
working-directory: ./
APPLICATION: ${{ secrets.APPLICATION }}
# 작업경로는 여기로 지정한다.
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
# 자바는 이것으로 사용할 것이다.
🔗 Gradle Caching
Gradle을 캐싱하는 코드이다.
- name: Cache Gradle packages
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
⚠️ 필수 아님!! 사용하면 빌드 시간이 단축된다고 함!
🔗 application.yml 등록
파일을 생성하는 부분 (여러개의 환경이 있다면 여러개 작성하면 된다)
(민감한 정보가 있기 때문에 직접 GitHub-Actions에서 작업)
- uses: actions/checkout@v2
- run: |
mkdir ./src/main/resources
cd ./src/main/resources
touch ./application.yml
echo "${{env.APPLICATION}}" > ./application.yml
- uses: actions/upload-artifact@v2
with:
name: application.yml
path: ./src/main/resources/application.yml
이때 GitHub-Actions에서 설정한 값을 properties에 쓰기 위해서는 다음과 같은 과정이 필요하다.
레포지토리의 Settings → Secrets → Actions → New repository secret 버튼
(한번 만들면 확인이 불가능함, 변경만 가능)
APPLICATION | application.yml 파일 |
---|---|
DOCKER_HUB_REPO | 도커 허브 계정 id/project name |
DOCKER_PASSWORD | 도커 허브 계정 비번 |
DOCKER_USERNAME | 도커 허브 계정 id |
EC2_SERVER_HOST | AWS 인스턴스 (EC2) 퍼블릭 IP |
PRIVATE_KEY | AWS 인스턴스 Key(.ppm형태) |
PROJECT_NAME | 프로젝트 이름(이건 별로 안중요) |
➕등등과 같이 본인이 필요한 내용을 상황에 따라 추가하면 된다.
→ Name에 변수 명을 Secret에 값을 복사하여 넣어주면 된다.
🔗 Gradle Build - Docker Build & Push
Gradle Build 및 Docker Build 및 Push 과정이다.
- name: Grant execute permission for gradlew
run: chmod +x gradlew
working-directory: ${{ env.working-directory }}
- name: Build with Gradle
run: ./gradlew build
working-directory: ${{ env.working-directory }}
- name: Cleanup Gradle Cache
if: ${{ always() }}
run: |
rm -f ~/.gradle/caches/modules-2/modules-2.lock
rm -f ~/.gradle/caches/modules-2/gc.properties
- name: Docker build
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t ${{ secrets.PROJECT_NAME }} .
docker tag ${{ secrets.PROJECT_NAME }} ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7}
docker push ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7}
Gradle 빌드를 하며 동시에 도커 로그인 및 jar파일 빌드(이미지화) 하여 도커 허브에 푸시를 부탁하는 부분이다.
🔗 EC2 연결
- name: Deploy
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.EC2_SERVER_HOST }}
username: ec2-user
key: ${{ secrets.PRIVATE_KEY }}
envs: GITHUB_SHA
script: |
docker rmi $(docker images -q)
docker pull ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7}
docker tag ${{ secrets.DOCKER_HUB_REPO }}:${GITHUB_SHA::7} ${{ secrets.PROJECT_NAME }}
docker stop ${{ secrets.PROJECT_NAME }}
docker rm ${{ secrets.PROJECT_NAME }}
docker run -d --name ${{ secrets.PROJECT_NAME }} -p 80:8080 ${{ secrets.PROJECT_NAME }}
순서대로
도커 허브에서 푸쉬된 내용을 pull, 기존에 실행하던 내용 stop 후 삭제하고 새로 받은 것을 실행한다.
참고 : https://minsu20.tistory.com/23 , https://jjeongil.tistory.com/1968