[목차여기]
GitHub Actions를 이용하여 CI/CD를 구축하는 방법을 알아보겠습니다. 바로 실습으로 가실 분들은 목차 1번으로 가주세요!
0. CI/CD 란?
CI/CD는 소프트웨어 개발 프로세스에서 중요한 자동화 과정을 의미하며, 'Continuous Integration' (CI)과 'Continuous Deployment' (CD)의 약자입니다. CI는 개발자들이 작업한 코드를 주기적으로 공유 브랜치에 병합하는 것을 말합니다.
Git으로 예시를 들어보겠습니다.
우리는 브랜치를 생성하여 작업을 한 뒤, 버그가 없는 완전한 코드를 main 브랜치에 PR(Pull Request)을 통해 병합합니다. 이 과정에서 중요한 것은 자동화된 빌드와 테스트를 통해 코드 변경 사항이 시스템에 통합되면서도 문제가 없도록 하는 것입니다.
CD는 CI 과정을 통과한 코드를 자동으로 배포하는 단계를 말합니다. 자동화된 테스트를 성공적으로 통과한 코드를 바로 프로덕션 환경에 배포하는 것을 의미하며, 이는 완전한 자동화 상태를 뜻합니다.
CI/CD는 왜 필요할까요?
우리 팀이 ‘학생 관리 시스템’을 개발하고 있다고 가정해 봅시다.
팀원들은 각자 다른 기능을 개발하고 있는데요, 각자의 기능을 개발한 후 이를 Git의 main 브랜치에 병합합니다(CI). 병합된 코드는 코드에 오류가 없는지 자동으로 테스트를 거치게 됩니다. 테스트를 통과하면 코드는 성공적으로 통합된 것입니다.
자 그런데 CD과정이 없다면? 자동 배포가 이루어지지 못하겠죠. 그렇다는 것은 소프트웨어의 배포가 수동으로 이루어져야 합니다. 매번 소프트웨어의 새 버전을 직접 운영 환경에 업로드하고 설정해야 되면 수동으로 배포하는 과정에서 설정을 잘못 입력할 수 있습니다. 그리고 수동 배포는 일관성을 유지하기 어렵기 때문에 예상치 못한 오류도 발생합니다.
그래서 CI 과정으로 자동으로 코드를 병합하고, CD 과정으로 배포를 자동화하여 효율성을 높이고 실수와 시간 지연의 발생 가능성을 낮추는 것입니다!
1. GitHub Actions
GitHub Actions는 GitHub에서 직접 CI/CD 파이프라인을 자동화하기 위한 도구입니다.
이를 통해 소프트웨어 개발 워크플로우에서 테스트, 빌드, 배포와 같은 일련의 작업을 자동으로 실행할 수 있습니다.
GitHub Actions의 주요 특징은 다음과 같습니다. ⬇️
- 이벤트 기반 : 특정 이벤트에 반응하여 자동으로 작업을 시작할 수 있습니다. 예를 들어, 레포지토리에 새로운 코드가 푸시(Push)되거나, 풀 리퀘스트(Pull Request, PR)가 생성될 때 트리거될 수 있습니다.
- 워크플로우 구성 : 워크 플로우는. github/workflows 디렉터리 내에 YAML 파일 형식으로 구성됩니다. 각 워크플로우 파일은 하나 이상의 작업(job)을 정의하고, 각 작업은 하나 이상의 단계(step)로 구성됩니다.
- 재사용 가능: GitHub Actions에서는 특정 작업을 위한 '액션(action)'을 재사용할 수 있습니다. 이 액션들은 공유되어 다른 프로젝트에서도 사용될 수 있으며, GitHub Marketplace에서 찾을 수 있습니다.
- 운영 시스템과 환경 지원: GitHub Actions는 Linux, Windows, macOS 러너(runner)에서 실행될 수 있으며, Docker 컨테이너 내에서도 액션을 실행할 수 있습니다.
- 보안과 비밀 관리: 민감한 정보와 환경 변수는 Secrets로 관리되어 암호화되며, 워크플로우에서 안전하게 사용할 수 있습니다.
2. Docker
Docker는 개발자가 애플리케이션을 컨테이너라는 격리된 환경에서 개발하고 배포할 수 있도록 해주는 오픈 소스 플랫폼입니다.
Docker 컨테이너는 애플리케이션과 그 애플리케이션을 실행하는 데 필요한 모든 종속성과 라이브러리를 포함하고, 이를 어떤 환경에서도 동일하게 실행할 수 있게 해 줍니다.
컨테이너...?
일상생활에서 ‘컨테이너’는 어떤 물건을 안전하게 다른 장소로 운반하기 위해 사용됩니다. 이 컨테이너는 내용물이 무엇이든 간에 동일한 형태와 크기를 가지므로, 세계 어디서나 쉽게 취급할 수 있습니다.
바로, 이 컨테이너를 다루는 것이 Docker입니다. 그리고 컨테이너가 운반하는 어떤 물건이 바로, 이미지(Image)입니다.
이미지는 애플리케이션과 그 실행 환경을 포함하는 불변의 파일입니다. 이미지는 컨테이너를 생성하는 데 사용되고, 주로 `Dockerfile`이라는 스크립트를 통해 정의되고 빌드됩니다.
Docker Hub는 컨테이너들의 ‘창고’라고 할 수 있는데요, Docker Hub에는 애플리케이션을 실행하는 데 필요한 컨테이너 이미지들이 저장됩니다. 창고에서 물건을 다른 사람들과 공유하듯이, Docker Hub를 통해 다른 사람들과 컨테이너 이미지를 공유할 수 있습니다.
즉, Docker Hub는 Docker 이미지를 저장하고 공유하기 위한 클라우드 기반의 서비스입니다.
3. AWS EC2
EC2는 AWS가 제공하는 클라우드 기반의 가상 서버 서비스입니다.
사용자는 EC2를 통해 인터넷을 통해 접근할 수 있는 가상 컴퓨터를 할당받아 사용할 수 있습니다. EC2는 주로 애플리케이션을 호스팅 하고 실행하는 데 사용됩니다. 여기서는 운영 체제, 애플리케이션 서버, 데이터베이스 등의 환경 구성 및 관리가 이루어집니다.
바로 이 EC2가 CI/CD를 구축할 때 애플리케이션의 실행 환경을 제공하는 것입니다.
실습 시땅
(아래 그림처럼 실습을 진행할 것입니다.)
Step1. GitHub Repository 생성
깃허브에서 CI/CD를 적용할 새로운 레포지토리를 생성합니다.
그리고 해당 프로젝트에 `Dockerfile`을 추가해 줍니다.
Dockerfile의 코드는 아래와 같이 작성해 주세요. (그대로 복붙 하시면 됩니다.)
FROM openjdk:17-jdk-slim
ADD /build/libs/*.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
그리고 Actions를 들어가 보면 GitHub가 제공하는 다양한 workflow를 확인할 수 있습니다.
우리는 Java with Gradle을 사용할 것입니다.
마지막 단계에서 여기로 돌아올 것이니, Step2로 먼저 가겠습니다. (Configure 클릭 X)
Step2. Docker Hub Repository 생성
Docker Hub를 사용하기 위해 회원가입 후 로그인을 진행해 주세요.
그다음 새로운 repository를 생성합니다. 공개 여부에 대해서는 저는 테스트 용이기 때문에 Public으로 해주었습니다.
🎯 Repository를 생성했다면 우리가 기억해야 할 것은 3가지입니다.
- 나의 Docker Hub 계정 유저이름 (사진에서는 Namespace)
- 나의 Docker Hub 계정 비밀번호
- 생성한 Repository의 이름 (사진에서는 Repository Name)
Step3. Amazon EC2 생성
AWS를 사용하기 위해 회원가입 후 로그인을 진행해 주세요.
➡️ AWS 바로가기
로그인을 완료했다면 EC2 서비스를 검색합니다.
그리고 Instances에 Launch instances를 클릭합니다. 이때, 지역이 Seoul인 것을 확인합니다. 어떤 region을 선택해도 상관없지만, 뒤에 pem키를 생성하는 과정에서 필요하니 내가 지금 선택한 region을 확인하는 것입니다.
이제 인스턴스를 생성해 주겠습니다. Name을 입력합니다.
Ububtu를 선택해 주시고 Amazon Machine Image는 해당 사진과 똑같이 지정해 주세요. EC2는 가상환경을 만드는 것이기 때문에 Ubuntu 환경을 만들어주신다고 생각하시면 됩니다.
가상환경의 경우에도 어떤 것을 선택해도 상관없으나 가상환경의 종류에 따라 `UserName`이 달라집니다.
- Amazon Linux 2022, Amazon Linux 2 또는 the Amazon Linux AMI의 경우 사용자 이름은 `ec2-user`
- CentOS AMI의 경우 사용자 이름은 `centos` 또는 `ec2-user`
- Debian AMI의 경우 사용자 이름은 `admin`
- Fedora AMI의 경우 사용자 이름은 `fedora` 또는`ec2-user`
- RHEL AMI의 경우 사용자 이름은 ec2-user 또는 `root`
- SUSE AMI의 경우 사용자 이름은 `ec2-user` 또는 `root`
- Ubuntu AMI의 경우 사용자 이름은 `ubuntu`
- Oracle AMI의 경우 사용자 이름은 `ec2-user`
- Bitnami AMI의 경우 사용자 이름은 `bitnami`
해당 region의 key pair를 선택합니다.
만약 key pair가 없다면 새로운 key pair를 생성한 뒤 선택합니다. 저의 경우, Seoul region의 key pair 이름은 aws-myKey-seoul입니다. (region 마다 고유한 key가 있는 것으로, 앞서 제가 region을 확인해야 된다는 이유가 여기서 밝혀집니다.)
⚠️ key pair는 내 로컬 컴퓨터에 잘 저장해 두어야 합니다!
보안 그룹은 사진과 같이 설정해 줍니다.
이제 주황색 버튼을 클릭하면 인스턴스 생성이 완료됩니다.
인스턴스 생성이 성공적으로 이뤄졌다는 것을 확인할 수 있습니다.
Instance Dashboard로 들어오면 생성한 EC2가 잘 작동되고 있습니다. (Running)
지금부터 설명할 `탄력적 IP 설정`은 선택 사항입니다. (비용이 더 추가될 수 있기 때문입니다.)
탄력적 IP 설정을 하지 않는다면 바로 Step3로 넘어가면 됩니다.
탄력적 IP란, 고정된 IPv4 주소를 설정할 수 있는 서비스입니다. 아마도 우리는 인스턴스의 상태를 Run/Stop을 반복할 것인데, (계속 Run 상태면 돈이 나갑니다..) 그때마다 IP주소가 바뀐다면 굉장히 번거로울 것입니다. 그런데, 탄력적 IP도 우리가 한 아이피를 점유하는 것이기 때문에 요금이 부과됩니다.
아무런 설정을 건드리지 않고 바로 Allocate를 클릭합니다.
그러면 사진과 같이 `43.202.123.199`라는 IP 주소가 할당되었습니다.
이제 IP주소를 인스턴스와 연결하겠습니다.
우리가 생성한 인스턴스를 선택한 뒤 연결합니다.
IP 주소가 해당 인스턴스에 잘 연결된 것을 확인할 수 있습니다.
그리고 인스턴스의 보안 그룹을 확인하겠습니다.
보안 그룹을 선택하면 Inbound rules를 확인할 수 있습니다.
Inbound rule이란, 네트워크 보안에서 들어오는(인바운드) 트래픽에 대한 액세스 규칙을 설정하는 것입니다.
아무런 설정도 하지 않는 다면 사진과 같은 기본 Inbound rules가 보일 것입니다. 우리는 기본 규칙을 사용할 것이므로 추가적인 설정을 하지 않겠습니다.
🎯 EC2를 생성했다면 우리가 기억해야 할 것은 4가지입니다.
1. EC2 Host (Public IPv4 DNS - 탄력적 IP를 설정했다면 항상 고정, 아니라면 재부팅할 때마다 달라짐.)
2. EC2 key (pem 키)
3. EC2 Username (ubuntu)
4. Port (8080)
⚠️ Public IPv4 DNS를 Public IPv4 address와 혼동하시면 안 됩니다!
Step4. EC2 접속
이제 생성한 EC2에 Docker를 설치해주어야 합니다.
생성한 인스턴스에 접속하는 방법은 AWS에서 직접 접속하거나, mac 터미널을 이용하는 방법이 있습니다.
1. AWS에서 직접 접속
해당 인스턴스의 Connect 버튼을 클릭합니다.
기본 설정 그대로 다시 한번 Connect를 클릭합니다.
이제 접속이 완료되었습니다. 이곳에 Docker를 설치합니다.
Docker 설치 명령어는 Docker에서 제공하는 공식문서를 이용합니다. 아래 링크를 클릭하여 들어가면 설치 명령어들을 확인할 수 있습니다. 이 글이 오래된다면.. 공식적으로 제공하는 명령어가 달라질 것이기 때문에 공식문서를 첨부합니다.
Docker는 인스턴스 생성 후, 한 번만 설치하면 됩니다.
2. MAC Terminal에서 접속
- 우리가 pem 키를 저장한 폴더로 이동합니다. `cd [폴더경로]`
- 해당 pem 키에 권한을 부여합니다. `chmod 400 [key 이름]`
- 접속을 시도합니다. `sudo ssh -i [key 이름] [username]@[IP주소]`
- 비밀번호까지 입력하고 나면 접속이 완료됩니다. 이때 비밀번호는 내 컴퓨터 비밀번호입니다.
접속하면 해당 화면이 나타날 것이고 마찬가지로 Docker를 설치해 줍니다.
Step5. GitHub Actions : workflow 연결
드디어 마지막 단계입니다!
먼저 환경 변수 세팅이 필요합니다.
Settings > Security > Actions > New repository secret을 클릭하여 환경 변수를 설정할 수 있습니다.
환경변수는 총 7개를 설정합니다.
DOCKERHUB_PASSWORD | 나의 Docker Hub 계정 비밀번호 |
DOCKERHUB_USERNAME | 나의 Docker Hub 계정 유저이름 |
PROJECT_NAME | Docker Hub에서 생성한 Repository의 이름 |
EC2_HOST | Public IPv4 DNS |
EC2_KEY | pem키 |
EC2_USERNAME | ubuntu |
PORT | 8080 |
입력의 예시는 다음과 같습니다.
EC2_KEY에 pem 키를 입력할 때 주의할 점이 있습니다.
해당 내용을 드래그하여 복붙을 하시면 됩니다.
⚠️ 검은색 부분만 복사하는 것이 아니라 반드시 위, 아래의 BEGIN RSA ~~ END RSA ~~ 도 함께 복붙 해주셔야 합니다. (즉, 전체 복붙)
7개를 모두 설정했습니다.
Step 1 끝에서 확인했던 Java with Gradle > Configure를 클릭합니다.
그러면 gradle.yml 파일이 보이실 텐데요 아래의 코드 전체를 내용에 복붙 해준 뒤 저장합니다. 코드의 설명은 주석을 참고해 주세요.
⚠️ 붙여 넣은 뒤 꼭!! tab을 확인해 주세요. 간혹 tab(들여 쓰기)이 꼬이는 경우가 있습니다.
# Workflow 이름
name: Java Project & Gradle CI/CD
# 어떤 이벤트가 발생하면 workflow 실행할 지 명시
on:
# main 브랜치에 push나 pull request 발생 시
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# 위 이벤트 발생 시 실행될 작업들
jobs:
build:
# VM의실행 환경 지정 => 우분투 최신 버전
runs-on: ubuntu-latest
# 실행될 jobs를 순서대로 명시
steps:
- name: Checkout
uses: actions/checkout@v3
# JDK 17 설치
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
# Gradle Build를 위한 권한 부여
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# Gradle Build (test 제외)
- name: Build with Gradle
run: ./gradlew clean build --exclude-task test
# DockerHub 로그인
- name: DockerHub Login
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
# Docker 이미지 빌드
- name: Docker Image Build
run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }} .
# DockerHub Push
- name: DockerHub Push
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}
# EC2 인스턴스 접속 및 애플리케이션 실행
- name: Application Run
uses: appleboy/ssh-action@v0.1.6
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_KEY }}
script: |
sudo docker kill ${{ secrets.PROJECT_NAME }}
sudo docker rm -f ${{ secrets.PROJECT_NAME }}
sudo docker rmi ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}
sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}
sudo docker run -p ${{ secrets.PORT }}:${{ secrets.PORT }} \
--name ${{ secrets.PROJECT_NAME }} \
-d ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}
그러면 gradle.yml이라는 파일이 생성되었습니다.
그리고 다시 Actions를 가면 뭔가가 작동되고 있는 것을 확인하실 수 있습니다.
잠시만 기다리면 초록색 체크 버튼으로 완료된 것을 볼 수 있습니다. 이렇게 되면 CI/CD 구축이 완료된 것입니다.
Step6. 실행 확인하기
직접 주소에 접속하여 구축이 완료된 것을 확인해 보겠습니다.
`http://[IP주소]:[Port번호]`에 접속했더니 제가 만든 프로젝트가 잘 배포된 것을 확인할 수 있습니다.
간혹, 위 사진처럼 Whitelabel Error가 뜰 수도 있습니다.
이것은 배포를 실패한 것이 아니라 배포한 프로젝트의 view가 없기 때문입니다. 프로젝트에 view를 만들어주면 원하는 페이지를 불러올 수 있습니다. (ex. Springboot 프로젝트의 경우 resources/index.html)
'Tutorial' 카테고리의 다른 글
[Springboot] GitHub Actions, NCP, Docker를 이용한 CI/CD 구축하는 법 (1) | 2024.11.27 |
---|---|
[SpringBoot] REST API 카카오 로그인 개발하는 법 - ✅ 글 하나로 끝내기 (코드포함, Spring Security JWT 로그인) (0) | 2024.11.07 |