이전에 CI/CD를 위해서 서버 세팅과 프로그램 설치까지 마쳤습니다
이제는 CI/CD를 구축하여 자동으로 빌드 -> 배포까지 할 수 있는 시스템을 만들어 보겠습니다
CI/CD 배포 순서
- 코드 수정 후 GitHub에 push (master 브랜치)
- GitHub Actions 작동 → Gradle로 빌드 (.jar 생성)
- Docker 이미지 생성 → DockerHub에 push
- GitHub Actions가 EC2에 SSH로 접속
- EC2에서 deploy.sh 실행 → Docker 이미지 pull & 컨테이너 재시작
- Spring Boot 앱이 EC2에서 실행됨 (8080 포트)
🐳 DockerHub 가입 및 설정
해당 링크로 접속해서 회원 가입후 로그인
Docker Hub Container Image Library | App Containerization
Increase your reach and adoption on Docker Hub With a Docker Verified Publisher subscription, you'll increase trust, boost discoverability, get exclusive data insights, and much more.
hub.docker.com
Access Token 생성 (Github에서 로그인 용도)
1. Accocunt Settings > Security 에서 New Access Token 생성
접근 권한은 Push/Pull이 가능해야 하기 때문에 읽기 및 쓰기 권한으로 설정

2. 생성된 토큰은 복사해서 저장 (생성 직후 한번만 표시하기 때문에 따로 저장해둬야 함)
3. 추후 GitHub Secrets에 작성하여 관리
인스턴스에 배포 스크립트 작성
애플리케이션을 서버에 자동으로 배포 하는 명령어 파일을 만들어 GitHub Actions에서 실행하도록 만들어 줍니다
먼저 인스턴스에 접속하여 아래 명령어로 파일을 생성하고 nano 에디터로 작성해줍니다
nano ~/deploy.sh
nano 에디터가 열리면 배포 스크립트 내용을 작성
#!/bin/bash
# 컨테이너 이름과 이미지 정의
IMAGE=mydockerid/spring-app
CONTAINER_NAME=spring-container
# 기존 컨테이너 종료 및 삭제
docker stop $CONTAINER_NAME || true
docker rm $CONTAINER_NAME || true
# 최신 이미지 가져오기
docker pull $IMAGE
# 새 컨테이너 실행 (GitHub Actions에서 환경변수 주입)
docker run -d --name $CONTAINER_NAME \
-p 8080:8080 \
-e SPRING_DATASOURCE_URL=$SPRING_DATASOURCE_URL \
-e SPRING_DATASOURCE_USERNAME=$SPRING_DATASOURCE_USERNAME \
-e SPRING_DATASOURCE_PASSWORD=$SPRING_DATASOURCE_PASSWORD \
$IMAGE
Docker 컨테이너 이름과 이미지를 정의하고 기존에 실행되고 있는 컨테이너를 종료 후 삭제하고
새로운 최신 이미지(Spring Boot)를 가져와 실행
여기서 DB 접속 정보 및 Docker 컨테이너,이미지 이름은 모두 GitHub Secrets에 저장하여 외부에 노출되지 않도록 관리하고
GitHub Actions에서 주입받아 사용하여 보안적인 측면을 고려했습니다
Spring Boot 애플리케이션 DB 환경 설정 및 Dockerfile 작성
인스턴스에 올려서 운영할 애플리케이션 DB 설정을 하고 Dockerfile을 작성하여 자동으로 빌드하여 실행하도록 자동화합니다
1. application.properties 설정
기존에 .env 파일로 민감 정보를 관리한 것처럼 배포 스크립트 GitHub에서 민감정보를 주입받아 사용하도록 변경
spring.datasource.url=${SPRING_DATASOURCE_URL}
spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
다음 처럼 배포 스크립트의 환경 변수 이름과 동일하게 작성
2. Dockerfile 생성
자동으로 빌드하고 실행하는 코드를 작성하여 repositroy에 Push (최상위 경로에 만들기)
# 기반 이미지 설정 (jdk 17)
FROM openjdk:17-jdk-slim
# 빌드된 .jar 파일 경로를 지정합니다 (Gradle 빌드 결과물)
ARG JAR_FILE=build/libs/*.jar
# .jar 파일을 컨테이너 내부로 복사
COPY ${JAR_FILE} app.jar
# 컨테이너가 실행될 때 실행할 기본 명령어
ENTRYPOINT ["java", "-jar", "/app.jar"]
- jdk 17 버전 이미지를 기반으로 컨테이너를 생성
- "build/libs 폴더에 있는 .jar (빌드 파일) 파일을 JAR_FILE가 참조하도록 지정
- JAR_FILE가 참조하는 파일을 컨테이너 내부로 복사
- Docker 컨테이너가 실행되면 자동 실행할 명령어 (빌드 파일을 실행)
GitHub Secrets 생성
민감 정보는 GitHub Actions에서 모두 주입받아 사용하도록 만들기 위해서 repository에 Secrets를 생성합니다
repository - Settings - Secrets and variables - Actions 에서 New repository secret를 눌러 생성

Name에는 GitHub Actions에 작성할 변수명 Secret는 변수가 참조할 값을 작성하여 추가합니다

생성한 GitHub Secrets 목록
| Secret 이름 | 설명 | 예시 값 |
| EC2_HOST | EC2 퍼블릭 IP 주소 | 13.125.123.123 |
| EC2_KEY | EC2 접속용 프라이빗 키 (.pem 파일의 내용 전체) | -----BEGIN RSA PRIVATE KEY----- ... |
| DOCKERHUB_USERNAME | Docker Hub 사용자 이름 | yourdockerid |
| DOCKERHUB_TOKEN | Docker Hub Access Token | ghp_xxx 또는 생성한 토큰 |
| SPRING_DATASOURCE_URL | Spring Boot에서 사용할 DB URL | jdbc:mysql://rds-endpoint:3306/dbname |
| SPRING_DATASOURCE_USERNAME | DB 사용자 이름 | admin |
| SPRING_DATASOURCE_PASSWORD | DB 비밀번호 | yourpassword123 |

사전 작업은 완료됐습니다
이제 작업한 걸 바탕으로 모든 작업을 자동으로 실행할 수 있도록 GitHub Actions를 작성하여 CI/CD 구축을 해보겠습니다
GitHub Actions로 CI/CD 구축하기
repository 상단 메뉴에서 Actions에 접속

set up a workflow yourself를 클릭하여 워크플로우 작성

workflow 작성
workflow 이름 설정
name: Docker CI/CD # 워크플로우 이름 (GitHub Actions 탭에서 표시됨)
GitHub Actions 탭에 표시될 이름을 지정합니다
workflow 실행 트리거 설정
on:
push:
branches: [ "master" ] # master 브랜치에 push될 때 자동 실행됨
어떤 이벤트가 발생했을 때 워크 플로우를 실행하는지 트리거를 지정합니다
master 브랜치에 push 작업이 이루어지면 자동 실행되도록 설정
작업 실행 운영체제(OS) 설정
jobs:
deploy:
runs-on: ubuntu-latest
jobs: workflow에서 실행할 작업을 정의
deploy: 작업의 이름 (다른 이름으로 설정 가능 즉 예약어가 아님)
runs-on: 작업을 실행할 OS (최신 우분투 리눅스 OS를 사용하도록 설정)
GitHub Actions 가상 머신에 리포지토리의 최신 코드를 클론
steps:
- name: Checkout code
uses: actions/checkout@v3 # GitHub 저장소 코드를 현재 워크플로우로 가져옴
리눅스 머신에 작업할 리포지토리 최신 코드를 가져옵니다
GitHub Actions 가상 머신에 Java 설치
- name: Set up JDK
uses: actions/setup-java@v3 # Java 개발 환경 설정
with:
java-version: '17' # Java 17 사용
distribution: 'temurin'
Spring Boot 프로젝트를 빌드하기 위해서 jdk 설치를 진행합니다 버전은 Spring Boot에 사용된 버전인 17 버전으로 설치
💢 distribution를 정의하지 않으면 에러 발생 여기서 'temurin' 으로 지정
프로젝트 빌드 설정
- name: Grant execute permission for Gradle wrapper # ./gradlew 실행 권한 부여
run: chmod +x ./gradlew
- name: Build JAR without tests # 빌드 파일 삭제후 새로운 빌드 (테스트 생략)
run: ./gradlew clean build -x test --no-daemon
Linux 환경에서 실행 권한이 없으면 에러 발생으로 "./gradlew" 파일의 권한 부여
빌드 파일 삭제 후 빌드 진행
테스트 생략은 만들었던 Spring Boot 프로젝트의 테스트 환경의 DB 설정이 운영 DB 설정과 동일하게 사용되어 테스트 진행에 오류가 발생하여 임시로 테스트는 진행하지 않도록 설정하였습니다 테스트가 실패하면 CI/CD가 실패하게 됩니다...
추후에 테스트 DB 설정을 따로 해서 관리해야 할 것 같습니다
DockerHub에 로그인
- name: Log in to DockerHub
uses: docker/login-action@v2 # DockerHub 로그인
with:
username: ${{ secrets.DOCKERHUB_USERNAME }} # GitHub Secrets에 저장된 Docker ID
password: ${{ secrets.DOCKERHUB_TOKEN }} # GitHub Secrets에 저장된 Docker Access Token
DockerHub에 Access Token으로 로그인하는 작업입니다
Docker 이미지를 push 하기 위해서 먼저 로그인을 해야 하기 때문에 로그인을 진행합니다
여기서 환경 변수는 이전에 추가했던 GitHub Secrets로 관리하고 있습니다
Docker 이미지 빌드 및 Push
- name: Build and Push Docker Image
# Docker 이미지 빌드
# DockerHub에 이미지 푸시
run: |
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/spring-app .
docker push ${{ secrets.DOCKERHUB_USERNAME }}/spring-app
Spring Boot 프로젝트에 작성한 Dockerfile을 기반으로 애플리케이션 이미지를 생성하고 DockerHub 저장소에 이미지를 업로드하는 작업을 진행합니다
EC2 서버 접속 후 배포 스크립트 실행
- name: Deploy to EC2
uses: appleboy/ssh-action@v1.0.0 # EC2에 SSH 접속 후 명령 실행
with:
host: ${{ secrets.EC2_HOST }} # EC2 퍼블릭 IP
username: ubuntu # EC2 사용자명 (Amazon Linux는 ec2-user, Ubuntu는 ubuntu)
key: ${{ secrets.EC2_KEY }} # EC2 접속용 개인 키 (PEM 파일 내용 전체)
# GitHub Secrets에서 환경변수 주입 (DB 연결 정보 등)
envs: SPRING_DATASOURCE_URL,SPRING_DATASOURCE_USERNAME,SPRING_DATASOURCE_PASSWORD
# EC2 접속 후 실행할 스크립트
# 배포 스크립트 실행 권한 부여
# 배포 스크립트 실행 → Docker pull + run
script: |
export SPRING_DATASOURCE_URL="${{ secrets.SPRING_DATASOURCE_URL }}"
export SPRING_DATASOURCE_USERNAME="${{ secrets.SPRING_DATASOURCE_USERNAME }}"
export SPRING_DATASOURCE_PASSWORD="${{ secrets.SPRING_DATASOURCE_PASSWORD }}"
chmod +x ~/deploy.sh
~/deploy.sh
EC2에 SSH 접속하여 배포 스크립트에 환경 변수를 주입하고 실행하는 스크립트입니다
여기서 원래 EC2 보안 그룹 SSH의 접속 허용을 내 IP로 설정했는데 접속하는 과정에서 접속이 안 되는 이슈가 있어서 모든 IP를 허용으로 변경했습니다
GitHub Actions의 우분투에서 SSH로 접속하기 때문에 GitHub Actions IP로 접속해서 막히는 거 같습니다

master 브랜치에 push 작업이 이루어지면 실행되는 작업 순서
1. GitHub에서 제공하는 우분투 환경을 가져와 사용
2. 현재 리포지토리의 내용을 우분투 OS에 가져옴
3. JDK 설치 (17 버전)
4. 리포지토리 Spring Boot 프로젝트 빌드
5. Docker Image 생성 후 Docker Hub 로그인하여 업로드
6. EC2 서버에 SSH 방식으로 접속하여 배포 스크립트 실행
최종 workflow 코드
name: Docker CI/CD # 워크플로우 이름 (GitHub Actions 탭에서 표시됨)
on:
push:
branches: [ "master" ] # main 브랜치에 push될 때 자동 실행됨
jobs:
deploy:
runs-on: ubuntu-latest # GitHub이 제공하는 우분투 환경에서 실행
steps:
- name: Checkout code
uses: actions/checkout@v3 # GitHub 저장소 코드를 현재 워크플로우로 가져옴
- name: Set up JDK
uses: actions/setup-java@v3 # Java 개발 환경 설정
with:
java-version: '17' # Java 17 사용
distribution: 'temurin'
- name: Grant execute permission for Gradle wrapper # ./gradlew 실행 권한 부여
run: chmod +x ./gradlew
- name: Build JAR without tests # 빌드 파일 삭제후 새로운 빌드 (테스트 생략)
run: ./gradlew clean build -x test --no-daemon
- name: Log in to DockerHub
uses: docker/login-action@v2 # DockerHub 로그인
with:
username: ${{ secrets.DOCKERHUB_USERNAME }} # GitHub Secrets에 저장된 Docker ID
password: ${{ secrets.DOCKERHUB_TOKEN }} # GitHub Secrets에 저장된 Docker Access Token
- name: Build and Push Docker Image
# Docker 이미지 빌드
# DockerHub에 이미지 푸시
run: |
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/spring-app .
docker push ${{ secrets.DOCKERHUB_USERNAME }}/spring-app
- name: Deploy to EC2
uses: appleboy/ssh-action@v1.0.0 # EC2에 SSH 접속 후 명령 실행
with:
host: ${{ secrets.EC2_HOST }} # EC2 퍼블릭 IP
username: ubuntu # EC2 사용자명 (Amazon Linux는 ec2-user, Ubuntu는 ubuntu)
key: ${{ secrets.EC2_KEY }} # EC2 접속용 개인 키 (PEM 파일 내용 전체)
# GitHub Secrets에서 환경변수 주입 (DB 연결 정보 등)
envs: SPRING_DATASOURCE_URL,SPRING_DATASOURCE_USERNAME,SPRING_DATASOURCE_PASSWORD
# EC2 접속 후 실행할 스크립트
# 배포 스크립트 실행 권한 부여
# 배포 스크립트 실행 → Docker pull + run
script: |
export SPRING_DATASOURCE_URL="${{ secrets.SPRING_DATASOURCE_URL }}"
export SPRING_DATASOURCE_USERNAME="${{ secrets.SPRING_DATASOURCE_USERNAME }}"
export SPRING_DATASOURCE_PASSWORD="${{ secrets.SPRING_DATASOURCE_PASSWORD }}"
chmod +x ~/deploy.sh
~/deploy.sh
작성 후 Commit changes...를 눌러 커밋하고 Push

Push를 진행하면 자동으로 Actions가 실행되는데 에러 나는 게 없는지 확인하고 없다면 성공적으로 배포가 완료
(생각보다 에러 나는게 많아서 고치는데 시간이 많이 걸렸습니다)

평균적으로 1분 30초 정도 걸리는 것 같습니다
CI/CD 및 EC2 서버 배포 확인
EC2 SSH 접속해서 docker 로그 확인
docker logs -f spring-container
Spring 서버가 잘 돌아가는 걸 볼 수 있습니다
여기서도 에러가 없는지 확인해 줍니다 분명 CI/CD는 문제없이 되는데 접속해 보면 응답 없는 경우 Spring 서버가 에러 나서 서버가 멈춘 상태였습니다 그러니 로그를 확인하는 게 좋을 거 같습니다

직접 ip 주소로 접속하여 확인하기
"http://(EC2 퍼블릭 IP):8080/swagger-ui/index.html#/" URL을 브라우저에서 접속하여 확인합니다
저는 Swagger를 사용해서 간단하게 API 문서를 확인할 수 있었습니다

CI/CD 구축에 사용된 리포지토리
GitHub - tuioe5679/OpenBoard: Spring Boot 기반의 간단한 게시판 API입니다.
Spring Boot 기반의 간단한 게시판 API입니다. . Contribute to tuioe5679/OpenBoard development by creating an account on GitHub.
github.com
'DevOps' 카테고리의 다른 글
| [CI/CD] GitHub Actions와 Docker로 AWS EC2서버 CI/CD 구축 (우분투 서버 세팅 및 RDS 연결) (1) | 2025.05.09 |
|---|---|
| [CI/CD] GitHub Actions CI/CD 구축을 위해 AWS 프리티어 계정 생성 및 EC2 인스턴스 생성하여 SSH 원격연결 (0) | 2025.05.02 |