▶ 개요
배포되고 있는 서비스를 업데이트 하려면 어떤 과정을 거쳐야 할까?
가장 흔한 배포 플로우를 살펴보자.
- 1. 새로운 기능에 대한 코드 작성
- 2.
git commit
하여 브랜치에Merge
를 하고 배포(git push
) - 3. 배포 서버에 원격 접속하여 새로운 코드를 업데이트(
git pull
)하고 애플리케이션 재실행
보통 단순한 서비스를 배포한다면 위와 같은 과정을 거칠 것이다.
하지만, 이 과정을 코드의 수정이 일어날 때마다 수행한다는 것은 너무 귀찮은 일 아닐까.
그래서 이런 반복적인 과정을 해결하기 위해 여러 환경에서의 CI/CD 구축 방법에 대해서 시리즈 형식으로 작성해보려고 한다.
예정되어 있는 포스팅은 다음과 같다.
- 1. 개인 프로젝트에서 많이 쓰는 CI/CD 구축 방법
- 2. 일반 프로젝트에서 많이 쓰는 CI/CD 구축 방법
- 3. 컨테이너 기반(Docker)의 프로젝트에서 많이 쓰이는 CI/CD 구축 방법
위 4단계로 거쳐 각각 상황에 맞는 CI/CD 구축 방법에 대해서 이해해보자.
▶ 전체적인 흐름
▸ 장점
git pull
을 활용하여 변경된 부분의 프로젝트에 대해서만 코드 업데이트를 하기에 CI/CD 속도가 빠름.github actions
만 사용하기 때문에 인프라 구조가 비교적 간단.
▸ 단점
- 빌드 작업을 EC2에서 진행하기 때문에, 빌드 진행 시 운영하고 있는 서버의 성능에 영향을 미침.
- Github 계정 정보가 EC2 서버 내에 저장되기 때문에 보안이 취약.
▸ 이 방법이 쓰이는 이유
- 주로 개인 프로젝트에서 CI/CD를 간단하고 빠르게 적용시키고 싶을 때 사용함.
▶ 프로젝트 생성
https://start.spring.io/ 사이트로 들어가서 간단하게 자바 스프링 프로젝트를 생성
그리고 아래와 같이 간단한 코드를 추가해주자. (생성한 프로젝트명에 따라 경로는 달라질 수 있음.) src/main/java/com/example/instagram_server/
package com.example.instagram_server;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AppController {
@GetMapping("/")
public String home() {
return "Hello, World!";
}
▶ 깃허브에 프로젝트 배포
1. Github repository 생성
Private repository로 생성하자.
2. Github repository에 프로젝트 코드 배포
cd <프로젝트 경로>
git init
git add .
git commit -m "first commit"
git branch -M main
git remoad add origin <github repo url>
git push -u origin main
▸ github 비밀번호 치는 과정 없애기
Private 레포지토리로 pull을 받거나 clone을 하거나 push를 하는 등의 작업을 하려면 해당 레포지토리 소유자의 토큰 값을 입력하여 진행해야 한다.
근데 매번 push
, pull
마다 토큰을 입력하는 것은 CI/CD 취지에 맞지 않다.
그러므로 아래와 같이 계정과 비밀번호를 입력하지 않아도 되게 만들어보자.
git config --global credential.helper store
git pull origin main
# Github 계정 및 비밀번호 입력
git pull origin main # 더 이상 비밀번호를 묻지 않음.
그리고 계정과 토큰 값의 경우 ~/.git-credentials
에서 확인이 가능하다.
▶ EC2 접속하여 기본 환경 구성
- EC2 생성 과정은 생략함.
1. 보안 그룹 8080번 포트 활성화
2. JDK 설치
sudo apt-get update -y && sudo apt-get install -y openjdk-17-jdk
# 잘 설치되었는지 확인
java -version
3. 프로젝트 clone
git clone <git repository clone 주소>
4. EC2에서 해당 프로젝트를 빌드하고 실행하여 애플리케이션이 정상적으로 작동하는지 확인
cd <프로젝트 경로>
chmod u+x gradlew && ./gradlew clean build
cp build/libs/*SNAPSHOP.jar ./app.jar
# 애플리케이션 실행
nohup java -jar app.jar 1>/dev/null 2>&1 &
# 8080번 포트에 Spring boot가 실행 중인지 확인
sudo lsof -i:8080
▶ 실제 코드가 업데이트 될 때 어떤 과정을 거치는지 짚어보기
이제 CI/CD를 왜 사용해야하는지에 대해서 더 명확하게 이해해보기 위해서
위 프로젝트 코드인 Hello, World
를 Hi,World
로 변경하고 다시 실행해보자.
배포 과정을 복기하면 아래와 같다.
- 1. 개발 서버에서 새로운 코드 작성
- 2.
Commit
후 github에 배포 - 3. EC2에 들어가서
git pull
- 4. 빌드 후 재배포하기.
# 2
git commit -m "hello to hi"
git push -u origin main
# 3 (EC2 접속 후 git pull)
ssh -i ~/.ssh/ec2.pem ubuntu@x.x.x.x
cd <프로젝트 경로>
git pull origin main
# 4. 빌드 후 배포
sudo fuser -k -n tcp 8080 # 8080번 포트에서 실행되고 있는 프로세스 종료
./gradlew clean build
cp build/libs/*SNAPSHOP.jar ./app.jar
nohup java -jar app.jar 1>/dev/null 2>&1 & # 애플리케이션 실행
▶ 지금까지의 배포 과정 자동화하기
이제 위 과정을 CI/CD를 도입하여 자동화해보자.
먼저 프로젝트 최상단에서 아래와 같은 폴더와 파일을 만들어주자.
mkdir -p .github/workflows
touch .github/workflows/deploy.yaml
.yaml
파일의 이름은 아무거나 지정해도 상관없지만 위 폴더 경로는 지켜져야 한다.
아래와 같이 deploy.yaml
을 작성하자.
name: Deploy To EC2
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: "1. SSH로 EC2에 접속"
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.EC2_HOST }} # EC2의 주소
username: ${{ secrets.EC2_USERNAME }} # EC2 접속 username
key: ${{ secrets.EC2_PRIVATE_KEY }} # EC2의 Key 파일의 내부 텍스트
script_stop: true # 아래 script 중 실패하는 명령이 하나라도 있으면 실패로 처리
script: |
cd /home/ubuntu/cicd-practice # 여기 경로는 자신의 EC2에 맞는 경로로 재작성
git pull origin main
./gradlew clean build
sudo fuser -k -n tcp 8080 || true # || true를 붙인 이유는 8080에 종료시킬 프로세스가 없더라도 실패로 처리하지 않기 위해서이다.
# jar 파일을 실행시키는 명령어이다. 그리고 발생하는 로그들을 ./output.log 파일에 남기는 명령어이다.
nohup java -jar build/libs/*SNAPSHOT.jar > ./output.log 2>&1 &
▸ Secret 값 주입하기
▸ 민감한 정보 처리 방법
Spring Boot에서는 민감한 값을 application.yaml
또는 application.properties
라는 파일로 분리하는 경우가 많다.
말 그대로 민감한 값이기에 .gitignore
에 추가하여 application.yaml
가 버전 관리 되지 않도록 설정을 하는데,
이 때문에 배포할 때 application.yaml
파일을 따로 서버에 주입 해줘야 하는 수동 작업이 포함되게 된다.
그 이유는 git pull
시 버전 관리에서 제외된 파일(application.yaml
)이 업데이트 되지 않기 때문이다.
그래서 이 과정을 자동화 시켜보려고 한다.
1. .gitignore에 application.yaml 추가
.gitignore
...
application.yml
2. application.yml 파일 만들기
src/main/resources/application.yml
# 아래 값은 테스트를 위해 임의로 넣은 값임.
aws:
access-key: xxxx
secret-key: xxxx
3. Github에 resources 폴더가 push 되도록 임의의 파일 하나 만들기
touch src/main/resources/any.txt
4. Github Actions 코드 수정하기
.github/workflows/deploy.yaml
name: Deploy To EC2
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: SSH로 EC2에 접속하기
uses: appleboy/ssh-action@v1.0.3
env:
APPLICATION_PROPERTIES: ${{ secrets.APPLICATION_PROPERTIES }}
with:
host: ${{ secrets.EC2_HOST }} # EC2의 주소
username: ${{ secrets.EC2_USERNAME }} # EC2 접속 username
key: ${{ secrets.EC2_PRIVATE_KEY }} # EC2의 Key 파일의 내부 텍스트
envs: APPLICATION_PROPERTIES
script_stop: true # 아래 script 중 실패하는 명령이 하나라도 있으면 실패로 처리
script: |
cd /home/ubuntu/instagram-server # 여기 경로는 자신의 EC2에 맞는 경로로 재작성하기
rm -rf src/main/resources/application.yml
git pull origin main
echo "$APPLICATION_PROPERTIES" > src/main/resources/application.yml
./gradlew clean build
sudo fuser -k -n tcp 8080 || true # || true를 붙인 이유는 8080에 종료시킬 프로세스가 없더라도 실패로 처리하지 않기 위해서이다.
nohup java -jar build/libs/*SNAPSHOT.jar > ./output.log 2>&1 &
5. github에 Secret 값(APPLICATION_PROPERTIES) 주입하기
6. 실제 EC2에 application.yaml 파일도 같이 배포 되었는지 확인하기
▸ Github Actions 테스트
이제 작성한 deploy.yaml
을 github로 push해서 Github Actions가 정상적으로 작동하는지 확인해뵈자.
git add .
git commit -m "feat: 테스트"
git push -u origin main