본문 바로가기

[배포]

[배포] 🚀 무중단 배포 과정 기록

1️⃣ EC2 서버 준비

  • 목표: GitHub → EC2로 JAR 파일을 수동 배포 가능한 상태 만들기
  • 자동화하려면 우선 수동으로도 배포가 잘 되는지 확인하는 게 기본이다!

🔹 Java 설치 (EC2에서 JAR 실행에 필요)

sudo apt update
sudo apt install openjdk-17-jdk -y
java -version  # 설치 확인

 

🔹 디렉토리 구조 생성

mkdir -p ~/app/deploy
mkdir -p ~/app/logs
  • ~/app/deploy: jar 파일을 배포할 위치
  • ~/app/logs: 실행 로그 저장할 위치

2️⃣ GitHub Actions 자동화 + S3 업로드

🔹 AWS CLI 설치 (로컬/Actions runner에 설치되어 있어야 함)

sudo apt install awscli -y
aws configure
  • AWS Access Key, Secret Key, region, output 설정 필요

GitHub Actions runner 안에서 AWS CLI가 필요하다는 의미

  • GitHub Actions는 내부적으로 Ubuntu 환경의 가상 머신을 띄워서 코드를 빌드하고 배포한다.
  • 이 GitHub Actions 환경에서는 S3나 Secrets Manager 같은 AWS 리소스에 접근하려면 AWS CLI가 필요함.
  • 다행히, GitHub Actions의 기본 Ubuntu runner에는 이미 awscli가 설치돼 있어.
  • 👉 그래서 추가로 설치할 필요는 거의 없다!

🔹 GitHub Secrets 등록

이름 설명
AWS_ACCESS_KEY_ID AWS IAM 사용자 access key (3단계 이후 추가해주세요!)
AWS_SECRET_ACCESS_KEY AWS IAM 사용자 secret key (3단계 이후 추가해주세요!)
AWS_REGION 예: ap-northeast-2
EC2_HOST 예: ec2-user@xxx.xxx.xxx.xxx 탄력적 IP 안 쓰는 경우: (퍼블릭 DNS) 
EC2_KEY PEM 키 파일 전체 문자열 (Base64 인코딩 권장)

GitHub 저장소 > Settings > Secrets → New repository secret 클릭

 

✏️ pem 키 파일 전체 문자열 여는법

 

📁 터미널에서 열기

cat ~/Downloads/your-key.pem

# 형식은 아래와 같음
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAzla1d3J...
...중략...
F8+kxP2LPk3Z5TR7e+5nQA==
-----END RSA PRIVATE KEY-----
  • ~/Downloads/your-key.pem 부분은 저장되어 있는 .pem 파일 경로로 바꿔준다.
  • 이 명령은 .pem 내용 전체를 터미널에 출력해줌.

🔹 .github/workflows/deploy.yml 예시

name: Deploy Spring Boot to AWS

on:
  push:
    branches: [ main ] # main 브랜치에 push 될 때마다 이 워크플로우가 실행됨

jobs:
  build-and-upload: # 작업(Job) 이름

    runs-on: ubuntu-latest # GitHub에서 제공하는 최신 Ubuntu 가상환경에서 실행

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        # 현재 리포지토리의 코드를 체크아웃 (가져오기) 해서 다음 단계에서 사용할 수 있게 함

      - name: Set up JDK
        uses: actions/setup-java@v3
        with:
          distribution: 'temurin' # OpenJDK 배포판 중 하나인 Temurin 사용
          java-version: '17'      # Java 17 버전 설치

      - name: Give gradlew permission
        run: chmod +x ./gradlew

      - name: Build with Gradle
        run: ./gradlew clean build
        # Gradle 빌드 실행 (clean: 기존 빌드 파일 삭제, build: 새로 빌드 생성)
        # 결과물은 보통 build/libs 디렉토리에 생성됨

      - name: Rename jar for deployment
        run: mv build/libs/*.jar build/libs/app.jar
        # S3에 업로드할 때 파일명을 고정하기

      - name: Upload jar to S3
        uses: jakejarvis/s3-sync-action@master
        with:
          args: --acl private --follow-symlinks
          # --acl private: S3 객체를 비공개로 업로드
          # --follow-symlinks: 심볼릭 링크도 따라가서 업로드
        env:
          AWS_S3_BUCKET: ${{ secrets.S3_BUCKET_NAME }}
          # S3 버킷 이름 (예: my-app-deploy-bucket) — GitHub Secrets에 저장되어 있어야 함

          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          # AWS IAM 사용자의 액세스 키 ID (보안상 Secrets에 저장)

          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          # AWS IAM 사용자의 비밀 액세스 키 (보안상 Secrets에 저장)

          AWS_REGION: ${{ secrets.AWS_REGION }}
          # S3 버킷이 있는 AWS 리전 (예: ap-northeast-2)

          SOURCE_DIR: build/libs
          # S3에 업로드할 파일들이 있는 디렉토리 (예: 빌드된 .jar 파일)

 

빌드시 plain.jar 생기지 않게 설정하기(build.gradle에 다음 옵션을 추가)

jar {
    enabled = false
}

 

main 브랜치에 push 발생 시

  • ✅ 코드 checkout
  • ✅ JDK 17 설정
  • ✅ gradlew 실행 권한 부여
  • ✅ Gradle 빌드 수행 (./gradlew clean build)
  • ✅ 빌드된 JAR 파일을 S3 버킷에 업로드

3️⃣ S3 연동 및 빌드 파일 업로드 자동화 (GitHub Actions에서 실행)

🤔 S3 왜 해야 할까?

  • GitHub Actions에서 생성한 .jar 파일을 EC2에 직접 복사하지 않고,
  • S3 버킷에 먼저 업로드하고, EC2 인스턴스에서는 그걸 받아서 실행하게 할 수 있다.
  • → 무중단 배포 스크립트에서 이 방식이 깔끔하고 보안적으로도 좋다.

📦 준비물

  1. S3 버킷 생성 (한 번만)
  2. IAM 사용자의 Access Key / Secret Key
  3. GitHub Secrets에 등록
  4. Actions에서 aws s3 cp 명령어로 업로드

🧱 S3 버킷 생성 (한 번만 하면 됨)

  1. S3 콘솔 접속
  2. “버킷 만들기” 클릭
  3. 이름: your-app-deploy-bucket (원하는 이름)
  4. 리전은 EC2와 동일한 리전 선택 (예: ap-northeast-2)
  5. 나머지는 기본 설정 → “버킷 만들기” 클릭

🛠️ GitHub에 AWS 인증 정보 등록

1. IAM → 사용자 추가

 

2. 이름: github-deploy-user

 

3. 권한 정책 추가

  • 정책: AmazonEC2FullAccess, AmazonS3FullAccess, SecretsManagerReadWrite

이름 설명
AmazonEC2FullAccess EC2 인스턴스에 접근 및 명령 실행
AmazonS3FullAccess S3 버킷에서 .jar 파일 다운로드 등
SecretsManagerReadWrite Secrets Manager에 저장된 비밀을 읽기/쓰기 가능

 

4. 사용자 만들고 Access Key 생성

 

 

  • 상단 탭에서 "보안 자격 증명(Security credentials)" 클릭
  • 아래로 내려서 "액세스 키" 섹션으로 이동
  • "새 액세스 키 만들기(Create access key)" 버튼 클릭

 

  • 사용 사례 → "명령줄 인터페이스(CLI), SDK, 코드" 선택
  • 다음 → 생성하면 Access Key ID / Secret Access Key 가 나옴

 

5. GitHub → 저장소 Settings → Secrets and variables → Actions

  • AWS_ACCESS_KEY_ID = 발급받은 액세스 키
  • AWS_SECRET_ACCESS_KEY = 발급받은 시크릿 키
  • AWS_REGION = ex. ap-northeast-2

⚠️ Secret Access Key는 다시 볼 수 없으니까 꼭 복사해서 따로 저장하거나 바로 Secrets에 추가해줘야 함.

 


🧱 S3 버킷 생성

🤔 왜 S3 버킷을 쓰는가?

  • GitHub Actions → EC2로 직접 파일 전송하려면 복잡할 수 있음.
  • 대신 GitHub Actions → S3 업로드 → EC2에서 다운로드 방식으로 간단하고 깔끔하게 파일을 전달할 수 있다.
  • 그리고 S3는 안전하고 빠름.

1. 버킷 생성

  • 🔒 [모두 차단] 체크 상태 유지 (기본값) → 나중에 EC2에서 IAM 권한으로 접근하게 설정할 거라서 퍼블릭 오픈은 안 해도 됨
  • 버전 관리, 암호화, 태그 등은 기본값 그대로 둬도 됨

2. GitHub → 저장소 Settings → Secrets and variables → Actions

 

  • S3_BUCKET_NAME = 아까 만든 버킷 이름

🧑‍💻 EC2에서 접근할 수 있도록 IAM 역할 만들기 (선택)

EC2에서 S3에 접근하려면 “나 이 버킷에 접근해도 되지?” 하고 허락이 있어야 한다. 그걸 IAM 역할로 설정해줘야 함.

IAM 역할은 이번 자동 배포엔 필요 없음, 하지만 EC2에서 AWS 자원 직접 쓸 땐 고려할 것!

  • 정책: AmazonEC2FullAccess, AmazonS3FullAccess, SecretsManagerReadWrite

  • 역할 이름: ec2-s3-access-role (예시)

  • EC2 콘솔 이동 → EC2 인스턴스 선택
  • [작업] → [보안] → [IAM 역할 수정]
  • 아까 만든 역할(github-deploy-user) 선택 → 저장

 IAM 사용자 vs 역할 차이

구분 IAM 사용자 IAM 역할
용도 사람이 직접 로그인하거나
프로그램(GitHub 등)이 사용할 계정
EC2, Lambda 같은 AWS 리소스나,
외부 서비스가 잠깐 빌려쓰는 권한
인증 방식 Access Key & Secret Key 사용
(GitHub Actions 등에서 사용)
EC2 등 AWS 서비스에 자동으로 붙음
(권한을 위임받아 쓰기 때문에 Access Key 없이도 가능)
EC2에 붙일 수
있는가?
❌ 못 붙임 ✅ 가능
상황 GitHub Actions → EC2 자동 배포
(키가 필요하니까)
EC2 안의 앱 → S3 접근
(키 없이 안전하게 접근하려고)

 

🚨 예시 상황

  • EC2에 배포한 애플리케이션이 로그 파일이나 이미지를 S3에 저장하려고 함.
  • 애플리케이션 안에서 AWS SDK를 써서 putObject() 같은 걸 호출하는 등.

💡 해결법

  • 애플리케이션이 S3에 접근하려면 AWS 인증 정보가 필요함.
  • 여기에 IAM 사용자 Access Key를 넣으면 되긴 하는데… ❌ 보안에 매우 취약함.
  • 대신! EC2 인스턴스에 역할(Role) 을 붙여서,
    • AWS가 자동으로 해당 EC2 인스턴스에 권한을 줌
    • 키 파일 없이도 S3Client 같은 걸 쓸 수 있음!

4️⃣  GitHub Actions 스크립트 작성 – EC2에 배포 자동화

EC2에 자동 배포 (CD 단계)

  • EC2에 JAR을 자동 다운로드 후 재시작하는 과정은 아직 없다.
  • 이건 두 가지 방식 중 택할 수 있다:
    1. S3에 업로드된 파일을 EC2에서 감지해서 가져오는 방식
    2. GitHub Actions에서 EC2에 SSH 접속해서 직접 배포하는 방식(선택)

1. GitHub Actions에서 EC2에 SSH 접속해서 배포하는 방식

[GitHub Action]
     |
     | SSH로 접속
     ↓
[EC2 서버]  ← GitHub가 직접 명령어 실행 (예: java -jar ~)

📦 GitHub Actions → EC2 직접 SSH 접속 → 빌드된 파일 복사 & 실행

 

장점

  • 빠르고 단순하다 (셋업 쉬움)
  • GitHub에서 PR 머지 → 바로 EC2에 적용됨
  • 실시간 로그 보기 쉬움 (nohup, log.txt 등)

⚠️ 단점

  • EC2에 SSH 키를 GitHub Secrets에 저장해야 함 (보안주의)
  • EC2에 직접 접근하는 것이기 때문에 보안적으로 신중해야 함
  • SSH 연결이 실패하면 배포가 중단됨

🧰 많이 쓰는 툴

  • appleboy/ssh-action: 가장 널리 쓰이는 GitHub Action

2. EC2에서 S3를 감지하거나 주기적으로 pull해서 배포하는 방식

[GitHub Action]
     |
     | S3에 빌드된 zip 업로드
     ↓
  [S3 Bucket]

     ↑
     | 서버가 주기적/수동으로 deploy.sh 실행
[EC2 서버] → zip 다운로드 → 실행

📦 GitHub Actions → S3 업로드 → EC2에서 감지 또는 크론탭으로 pull

 

장점

 

 오토스케일링/동적 IP 대응 가능

  • EC2 인스턴스는 껐다 켜면 IP가 바뀌어.
  • SSH 방식은 GitHub에서 EC2의 고정 IP를 알아야 접속할 수 있어.
  • 그런데 IP가 바뀌면 GitHub Actions에서 연결 불가 ❌

✅ 반대로 Pull 방식은 EC2가 S3를 보고 스스로 가져오기 때문에,

  • IP가 바뀌든,
  • 인스턴스가 새로 생기든 (오토스케일링),
  • 전혀 상관 없음

⚠️ 단점

  • 감지 로직이나 크론탭 등을 EC2에서 설정해야 함 (조금 복잡)
  • GitHub Actions만으로는 배포가 완료되지 않음 (EC2 설정 따로 필요)

EC2에 배포 스크립트 만들기

📌 목표

  • EC2 인스턴스가 GitHub Actions가 올린 파일을 S3에서 받아서 배포하는 쉘 스크립트 작성
  • 보통 다음 경로에 저장함: /home/ec2-user/deploy/deploy.sh

1. EC2에 SSH 접속

ssh ec2-user@<EC2_PUBLIC_IP>

 

2. 배포 스크립트 작성

vim ~/app/deploy/deploy.sh

 

✏️ deploy.sh

#!/bin/bash

# -----------------------
# 환경 설정
# -----------------------
APP_NAME=study-app
JAR_NAME=app.jar
DEPLOY_PATH=/home/ubuntu/app/deploy
LOG_PATH=/home/ubuntu/app/logs
S3_BUCKET=!!!!자신의 버킷명으로 바꿔주세요!!!!
S3_KEY=app.jar

echo "⬇️ S3에서 최신 JAR 다운로드"
aws s3 cp s3://$S3_BUCKET/$S3_KEY $DEPLOY_PATH/$JAR_NAME

if [ $? -ne 0 ]; then
  echo "❗ JAR 다운로드 실패 - S3에 파일이 없습니다: $S3_BUCKET/$S3_KEY"
  exit 1
fi

if [ -f "$DEPLOY_PATH/$JAR_NAME" ]; then
  echo "✅ JAR 다운로드 성공: $JAR_NAME"
else
  echo "❗ JAR 파일이 존재하지 않습니다 (경로: $DEPLOY_PATH/$JAR_NAME)"
  exit 1
fi

echo "🛑 기존 프로세스 종료 (있다면)"
PID=$(pgrep -f $JAR_NAME)
if [ -n "$PID" ]; then
  kill -9 $PID
  echo "✅ 프로세스 종료 완료 (PID: $PID)"
else
  echo "ℹ️ 종료할 프로세스가 없습니다"
fi

echo "🚀 새 버전 실행"
nohup java -jar $DEPLOY_PATH/$JAR_NAME > $LOG_PATH/app.log 2>&1 &

NEW_PID=$(pgrep -f $DEPLOY_PATH/$JAR_NAME | head -n 1)
if [ -n "$NEW_PID" ]; then
  echo "✅ 새 버전 실행 완료 (PID: $NEW_PID)"
else
  echo "❗ 실행 실패 - 프로세스가 시작되지 않았습니다"
  exit 1
fi

 

3. 실행 권한 주기

chmod +x ~/app/deploy/deploy.sh

 

4. AWS CLI 설치하기

sudo apt update
sudo apt install awscli -y

 

🚨 에러 상황

  • EC2 인스턴스에서 awscli를 설치하려고 했는데, apt로 설치할 수 없다는 에러가 떴다.
  • 이건 EC2에 따라 기본 저장소에서 AWS CLI를 못 찾는 경우가 있어서 그렇다.
  • 그래서 수동 설치 방식으로 AWS CLI를 설치해야 함!

✅ AWS CLI v2 수동 설치 (Ubuntu 기준)

 

1. 먼저 EC2에 접속한 상태에서 최신 버전 다운로드

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"

 

2. 압축 해제

unzip awscliv2.zip

만약 unzip 명령어가 없다고 하면 먼저 설치

sudo apt update
sudo apt install unzip -y
3. 설치
sudo ./aws/install
4. 설치 확인
aws --version

 

5. 이렇게 나오면 성공

 

6. 실행해보기

sh ~/app/deploy/deploy.sh

 

🧠 현재 상태 요약

항목 상태
Spring Boot 빌드 자동화 ✅ 완료
JAR S3 업로드 ✅ 완료
EC2 수동 배포 스크립트 ✅ 완료
배포 스크립트 검증 ✅ 성공
Secrets Manager 적용 🟡 준비 중
CodeDeploy 연동 🟡 준비 중
완전 자동화 🔜 곧 가능!

🔐 AWS Secrets Manager 적용

1. AWS 콘솔 접속 → Secrets Manager로 이동

2. "새 보안 암호 저장" 클릭

 

 

키 값 예시:

{
  "spring.datasource.url": "jdbc:mysql://localhost:3306/devdb",
  "spring.datasource.username": "devuser",
  "spring.datasource.password": "devpass"
}

 

3. 비밀 키-값 추가

 

 

4. 보안 암호 이름 정하기

 

  • 나머지 설정은 기본값으로 두고 저장

5. Gradle 의존성 추가

// AWS secret manager (Spring Cloud AWS)
implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.0.1")
implementation 'io.awspring.cloud:spring-cloud-aws-starter-secrets-manager'

 

6. AWS CLI 설치 및 설정

aws configure
  • Access Key ID: 기존에 발급했던 키
  • Secret Access Key: 기존에 발급했던 키
  • 리전: ap-northeast-2
  • 출력 형식: json

7. Java 코드 작성 및 테스트

@Service
public class SecretsManagerService {

    public static void main(String[] args) {
        getSecret();
    }

    private final SecretsManagerClient secretsClient;
    private final ObjectMapper objectMapper;

    public SecretsManagerService() {
        this.secretsClient = SecretsManagerClient.builder()
                .region(Region.AP_NORTHEAST_2) // 서울 리전
                .build();
        this.objectMapper = new ObjectMapper();
    }

    public static void getSecret() {

        String secretName = "prod/switching";
        Region region = Region.of("ap-northeast-2");

        // Create a Secrets Manager client
        SecretsManagerClient client = SecretsManagerClient.builder()
                .region(region)
                .build();

        GetSecretValueRequest getSecretValueRequest = GetSecretValueRequest.builder()
                .secretId(secretName)
                .build();

        GetSecretValueResponse getSecretValueResponse;

        try {
            getSecretValueResponse = client.getSecretValue(getSecretValueRequest);
        } catch (Exception e) {
            // For a list of exceptions thrown, see
            // https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
            throw e;
        }

        String secret = getSecretValueResponse.secretString();

        // Your code goes here.
        System.out.println(secret);
    }
}

  • 위 코드를 통해 Secret 값을 정상적으로 받아오는 데 성공!

8. yml 파일 수정 🌟

spring:
  config:
    import: aws-secretsmanager:secretswitching_${spring.profiles.active}
    
  profiles:
    active: dev

이 설정만 있으면, secretswitching_dev 라는 이름의 secret을 AWS Secrets Manager에서 자동으로 불러오고,
그 안의 key-value(JSON)가 application.yml처럼 속성으로 적용돼.

 

yml 파일 전체 예시

## applicaton-prod.yml
spring:
  config:
    activate:
      on-profile: prod
    #이름의 secret을 AWS Secrets Manager에서 자동으로 불러옴
    import: aws-secretsmanager:${spring.profiles.active}/switching

  datasource:
    url: ${spring.datasource.url}
    username: ${spring.datasource.username}
    password: ${spring.datasource.password}
    driver-class-name: com.mysql.cj.jdbc.Driver

  jpa:
    hibernate:
      ddl-auto: validate
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQLDialect
        # show_sql: true
        format_sql: true

logging.level:
  org.hibernate.SQL: debug
  # org.hibernate.type: trace

custom:
  secret:
    name: prod/switching

server:
  port: 9090
# application.yml
spring:
  application:
    name: study-matching-site

  profiles:
    active: prod

  jwt:
    secret: ${spring.jwt.secret}

springdoc:
  swagger-ui:
    path: /swagger-ui
  api-docs:
    path: /v3/api-docs

server:
  port: 8080  # 기본 포트 (환경별로 덮어쓰기 가능)

 

9. 로컬에서 테스트

Secrets Manager에 접근하려면 AWS 자격증명이 필요함.(로컬은 이미 6번에서 했음)

aws configure
# AWS Access Key ID [None]: YOUR_KEY
# AWS Secret Access Key [None]: YOUR_SECRET
# Default region name [None]: ap-northeast-2

  • 스프링 애플리케이션 로그에서 다음 메시지가 보이면 성공

10. EC2 or ECS or Lambda 같은 AWS 환경

 

AWS 콘솔 → IAM → 역할(Roles) → 역할 생성(Create role)

 

신뢰할 수 있는 엔터티 선택: EC2 선택

 

권한 정책 추가에서 아래 정책을 선택

 

  • 역할 이름 예시: EC2SecretsManagerRole
  • 역할 생성 완료

AWS CodeDeploy를 써서 EC2 인스턴스에 자동 배포를 설정

✅ CodeDeploy란?

AWS CodeDeploy는 애플리케이션을 EC2, Lambda, 또는 온프레미스 서버에 자동으로 배포해주는 서비스이다.
특히 EC2에서 사용할 경우에는 앱이 수정되었을 때 자동으로 EC2에 배포하고, 지정된 스크립트를 실행할 수 있다.

 

💡 기본 개념

1. AppSpec 파일

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ec2-user/app

hooks:
  AfterInstall:
    - location: scripts/deploy.sh
      timeout: 180
      runas: ec2-user
  • 배포 중 어떤 스크립트를 언제 실행할지 정의한 YAML 파일 (appspec.yml)을 작성해야 한다.

2. CodeDeploy 애플리케이션 만들기

 

 

  • 애플리케이션 이름: 아무 이름이나 가능하지만, 예: my-ec2-app
  • 컴퓨팅 플랫폼 선택: EC2/온프레미스
  • 💡 Amazon ECS는 Docker 컨테이너 기반의 서비스에 배포할 때 선택하는 옵션이다.

3. CodeDeploy 서비스 역할 만들기

 

  • 왼쪽 메뉴에서 역할 클릭
  • 역할 만들기(Create role) 클릭

 

 

  • 신뢰할 엔터티 유형: AWS 서비스 선택
  • 사용 사례 선택: 검색창에 codedeploy 입력 후 CodeDeploy 선택

 

  • 기본 정책에서 AWSCodeDeployRole 또는 AWSCodeDeployRoleForEC2 선택 (둘 중 하나면 됨)

  • 역할 이름 지정: CodeDeployServiceRole (예시)
서비스 역할은 IAM에서 미리 만들어 둬야 CodeDeploy 설정 시 선택할 수 있다.
CodeDeploy는 EC2 인스턴스에 접근해서 배포 명령을 실행해야 하니까, 그 권한을 가진 역할이 필요하다.

 

 

3. 배포 그룹 만들기

✅ CodeDeploy 배포 그룹이란?

배포할 EC2 인스턴스, 배포 방식, 그리고 사용할 배포 스크립트(AppSpec 파일) 등을 설정하는 곳

 

  •  애플리케이션 상세 페이지에서 상단에 "배포 그룹 생성" 클릭

 

  • 태그: 이건 CodeDeploy가 "어떤 EC2 인스턴스에 배포할지"를 식별하는 데 꼭 필요
  • CodeDeploy는 배포 그룹에서 "태그 기준으로 EC2를 찾는다"고 되어 있다
  • 그래서 우리가 EC2에 특정 태그를 직접 붙여줘야 CodeDeploy가 이 인스턴스를 찾아서 배포해준다.

🧩 태그

  • Key/Value는 자유롭게 정해도 돼, 꼭 Name이 아니어도 된다.
  • 하지만 배포 그룹과 EC2가 정확히 같은 키-값을 가져야 매칭됨.
  • 여러 인스턴스에 같은 태그 붙이면 동시에 여러 대에 배포 가능함.

  • AllAtOnce, HalfAtATime, OneAtATime 등은 CodeDeploy의 배포 방식 옵션이다.
  • 배포를 어떻게, 어떤 속도로 EC2 인스턴스에 적용할지를 결정

💡 배포 구성(Deployment Configuration) 종류

배포 방식 설명 장점 단점 적합한 상황
AllAtOnce 모든 인스턴스에 동시에 배포함 - 가장 빠름
- 설정 간단
- 오류 발생 시 전체 장애
- 롤백 어려움
- 개발/테스트 서버
- 트래픽 없는 환경
HalfAtATime 전체 인스턴스의 절반에 먼저 배포
→ 성공 시 나머지 절반에 배포
- 부분 장애에 대비 가능
- 일정 속도와 안정성 균형
- 속도는 느리고 완전한 안전도는 아님 - 스테이징 서버
- 안정성과 속도의 균형 필요 시
OneAtATime 한 번에 하나의 인스턴스에 순차적으로 배포 - 가장 안전
- 오류 발생 시 빠른 대응 가능
- 가장 느림
- 시간이 오래 걸림
- 프로덕션 서버
- 실시간 서비스

 

항목 설정 예시 및 설명
배포 그룹 이름 my-ec2-deploy-group (원하는 이름)
서비스 역할 IAM에서 만들어진 CodeDeployRole (예: CodeDeploy-EC2-Role)
* 역할은 IAM에서 미리 만들어 둬야 CodeDeploy 설정 시 선택(위 처럼)
배포 유형 인플레이스 배포 (기본값 유지)
환경 구성 태그 또는 인스턴스 직접 선택 가능. 보통 태그로 EC2를 찾음
대상 선택 방법 예: Key=Name, Value=MyEC2Instance (EC2 인스턴스에 붙은 태그 기준)
로드 밸런서 설정 안 써도 됨 (단일 EC2인 경우 생략 가능)

 

4. appspec.yml 파일 작성

version: 0.0
os: linux
files:                       👈 어떤 파일들을 EC2 어디에 복사할지?
  - source: /
    destination: /home/ubuntu/app/deploy
hooks:                       👈 복사 후 어떤 작업을 할지 정의 (예: 스크립트 실행)
  AfterInstall:
    - location: deploy.sh    👈 파일 복사 후 실행될 작업 (여기서 deploy.sh)
      timeout: 180
      runas: ubuntu
  • CodeDeploy가 어떤 스크립트를 실행할지 정의하는 파일

 

5. git actions 스크립트 수정

name: Deploy Spring Boot to AWS

on:
  push:
    branches: [ main ]  # main 브랜치에 push 될 때마다 이 워크플로우가 실행됨

jobs:
  build-and-upload:  # 작업(Job) 이름

    runs-on: ubuntu-latest  # GitHub에서 제공하는 최신 Ubuntu 가상환경에서 실행

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        # 현재 리포지토리의 코드를 체크아웃 (가져오기) 해서 다음 단계에서 사용할 수 있게 함

      - name: Set up JDK
        uses: actions/setup-java@v3
        with:
          distribution: 'temurin'  # OpenJDK 배포판 중 하나인 Temurin 사용
          java-version: '17'       # Java 17 버전 설치

      - name: Give gradlew permission
        run: chmod +x ./gradlew

      - name: Build with Gradle
        run: ./gradlew clean build
        # Gradle 빌드 실행 (clean: 기존 빌드 파일 삭제, build: 새로 빌드 생성)
        # 결과물은 보통 build/libs 디렉토리에 생성됨

      - name: Rename jar for deployment
        run: mv build/libs/*.jar build/libs/app.jar
        # S3에 업로드할 때 파일명을 고정하기

      - name: Create deploy-package directory  # 배포 디렉토리 생성
        run: mkdir -p deploy-package
        # CodeDeploy용 디렉토리 생성

      - name: Copy deploy files to deploy-package  # deploy-package에 파일 복사
        run: |
          cp appspec.yml deploy-package/        # 루트에서 appspec.yml 복사
          cp deploy.sh deploy-package/          # 루트에서 deploy.sh 복사
          cp build/libs/app.jar deploy-package/  # 빌드된 app.jar 복사

      - name: Upload deploy package to S3
        uses: jakejarvis/s3-sync-action@master
        with:
          args: --acl private --follow-symlinks
          # --acl private: S3 객체를 비공개로 업로드
          # --follow-symlinks: 심볼릭 링크도 따라가서 업로드
        env:
          AWS_S3_BUCKET: ${{ secrets.S3_BUCKET_NAME }}  # S3 버킷 이름
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}  # AWS IAM 액세스 키
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  # AWS IAM 비밀 키
          AWS_REGION: ${{ secrets.AWS_REGION }}  # S3 버킷이 있는 AWS 리전
          SOURCE_DIR: deploy-package  # S3에 업로드할 디렉토리
  • 현재 GitHub Actions는 JAR 파일 하나만 S3에 업로드하고 있다.
  • 그런데 CodeDeploy는 appspec.yml 파일을 기준으로 동작하기 때문에, appspec.yml과 deploy.sh가 같이 있어야 배포 스크립트가 자동 실행된다.

💡 git actions 가 동작한 후 디렉토리 상황

my-spring-app/             👈 "프로젝트 루트"
├── deploy-package/        👈 ⭐ CodeDeploy용 디렉토리 (S3로 업로드할 폴더)
│   ├── appspec.yml        ✅ CodeDeploy 설정
│   ├── deploy.sh          ✅ 배포 스크립트
│   └── app.jar            ✅ 빌드된 JAR 파일
├── src/
├── build.gradle
  • 이 세 개를 하나의 디렉토리에 같이 두고 S3에 업로드해야 CodeDeploy가 인식 가능하므로 git actions 스크립트 수정

6. EC2 인스턴스에 CodeDeploy Agent 설치 및 구성

ssh -i your-key.pem ubuntu@<EC2_PUBLIC_IP>
  • SSH로 EC2 인스턴스에 접속
sudo apt update
sudo apt install ruby wget
# 실행하기 전 I AM 권한 추가 필요!
# AWS CodeDeploy 에이전트 설치 스크립트를 다운로드([ ]자신의 버킷 이름 입력)
sudo wget https://[switching-bucket-202504].s3.amazonaws.com/latest/install
sudo wget https://aws-codedeploy-ap-northeast-2.s3.amazonaws.com/latest/install

# 설치 스크립트 실행
sudo chmod +x ./install
sudo ./install auto 

# CodeDeploy Agent 상태 확인
sudo service codedeploy-agent status
  • CodeDeploy Agent 설치: EC2 인스턴스에 접속한 후, 위 명령어로 CodeDeploy Agent를 설치

실행하기 전 IAM 권한 추가 필요❗️

 

EC2 인스턴스에 S3 액세스 권한을 부여❗️

  • EC2 인스턴스 역할에도 S3 액세스 권한을 추가해야 한다.
  • EC2 인스턴스에서 실행 중인 애플리케이션이나 명령어가 S3에 접근할 때, 해당 EC2 인스턴스에 할당된 IAM 역할이 권한을 가져야 하기 때문이다.

 

S3에 install 파일 업로드 

  • AWS 콘솔에서 S3 서비스를 열고, 해당 버킷을 선택

  • 자신의 버킷에 latest/install 폴더 생성

  • install.sh 파일 업로드

install.sh

#!/bin/bash
# AWS CodeDeploy Agent 설치 스크립트

yum update -y
yum install -y ruby
yum install -y wget
cd /home/ec2-user
wget https://github.com/aws/aws-codedeploy-agent/releases/download/latest/install
chmod +x install
sudo ./install auto
  • 이제 install.sh 파일을 S3에 업로드한 후, EC2 인스턴스에서 그 파일을 다운로드하여 실행하면 된다.
  • 다시 말해, install.sh 파일을 latest 폴더에 업로드하고, EC2 인스턴스에서 해당 파일을 다운로드한 뒤 실행하는 방식이다.

💡 버킷 정책을 사용하여 퍼블릭 액세스 허용 설정

  • 권한 (Permissions) 탭을 선택하고, 버킷 정책 (Bucket Policy)을 클릭
  • 버킷 정책에 아래와 같은 JSON 정책을 추가
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::switching-bucket-202504/latest/install/*"
        }
    ]
}

# AWS CodeDeploy 에이전트 설치 스크립트를 다운로드([ ]자신의 버킷 이름 입력)
sudo wget https://[switching-bucket-202504].s3.amazonaws.com/latest/install

# 설치 스크립트 실행
sudo chmod +x ./install
sudo ./install auto 

# CodeDeploy Agent 상태 확인
sudo service codedeploy-agent status

 

  • 서비스 상태: active (running)
  • 프로세스 ID: 84289 (실행 중인 codedeploy-agent 프로세스)
  • 시간: 2025-04-14 11:56:45부터 실행 중

✅ GitHub Actions에서 CodeDeploy를 직접 트리거

 

업로드 끝난 직후, CodeDeploy에 API 요청을 보내서 배포 시작하는 방식이다.

 

  • 지금 상황 보니까 .jar, appspec.yml, deploy.sh 파일이 전부 S3에 개별 파일로 올라가 있다.
  • CodeDeploy는 하나의 압축 파일(zip) 안에 모든 배포 리소스가 들어 있어야 동작하기 때문에 S3에 배포용 zip 파일 만들기어야 한다. 

📁 압축할 구조 예시

deploy/
├── appspec.yml
├── deploy.sh
└── app.jar  (혹은 study-matching-site-0.0.1-SNAPSHOT.jar)
  • zip으로 묶는 건 GitHub Actions 스크립트에서 자동으로 처리해야 CodeDeploy가 사용할 수 있다.
  • 지금까지 스크립트에서 deploy-package 디렉토리까지 잘 만들고 있고, 그 안에 필요한 파일들만 잘 들어가게 해서 zip으로 묶고 S3에 올리기만 추가하면 된다.
name: Deploy Spring Boot to AWS

on:
  push:
    branches: [ main ]  # main 브랜치에 push 될 때만 실행

jobs:
  build-and-upload:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4  # GitHub repo의 소스 코드 가져오기

      - name: Set up JDK
        uses: actions/setup-java@v3
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: Grant execute permission to Gradle wrapper
        run: chmod +x ./gradlew

      - name: Build with Gradle
        run: ./gradlew clean build  # 프로젝트 빌드

      - name: Rename JAR for deployment
        run: mv build/libs/*.jar build/libs/app.jar  # app.jar로 고정 (CodeDeploy에서 사용하기 쉽게)

      - name: Prepare deploy package
        run: |
          mkdir -p deploy-package
          cp build/libs/app.jar deploy-package/
          cp appspec.yml deploy-package/
          cp deploy.sh deploy-package/

      - name: Zip the deploy package
        run: |
          cd deploy-package
          zip -r deploy.zip .  # deploy.zip 생성

      # AWS 자격 증명 설정 (Secrets에 저장된 값 사용)
      - name: Configure AWS credentials
        run: |
          aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws configure set default.region ${{ secrets.AWS_REGION }}

      # S3에 deploy.zip 업로드
      - name: Upload to S3
        run: aws s3 cp deploy-package/deploy.zip s3://switching-bucket-202504/app.zip

 

🚀 배포 트리거 설정 (CodeDeploy)

  • 우리는 이미 배포 그룹과 애플리케이션을 생성한 상태이다.
  • S3에 app.zip 파일이 업로드된 후 AWS CodeDeploy를 사용해 배포를 시작하려면, 배포를 트리거해야 한다.
  • 이를 위해 배포를 수동으로 트리거하거나 자동화된 트리거를 사용할 수 있다.

GitHub Actions 워크플로우에서 배포 트리거하기

- name: Trigger CodeDeploy Deployment
  run: |
    aws deploy create-deployment \
      --application-name switching-ec2-app \
      --deployment-group-name switching-deploy-group \
      --revision revisionType=S3,s3Location={bucket=${{ secrets.S3_BUCKET_NAME }},key=app.zip,bundleType=zip} \
      --deployment-config-name CodeDeployDefault.OneAtATime \
      --region ${{ secrets.AWS_REGION }} \
      --no-wait
  • --application-name : 배포할 애플리케이션 이름 (switching-ec2-app).
  • --deployment-group-name : 배포 그룹 이름 (switching-deploy-group).
  • --revision : S3 버킷에서 배포할 파일(app.zip).
  • --deployment-config-name : 배포 구성 (CodeDeployDefault.OneAtATime - 하나씩 배포).
  • --region : 배포할 리전 (AWS_REGION).

🚨 IAM CodeDeployFullAccess 권한 추가 ❗️

📄 전체 스크립트

name: Deploy Spring Boot to AWS  # 워크플로우 이름 설정

# main 브랜치에 push가 발생할 때마다 이 워크플로우가 실행됩니다.
on:
  push:
    branches: [ main ]  # main 브랜치에 변경 사항이 있을 때 실행

jobs:
  build-and-upload:  # 작업 이름 설정 (이 작업은 빌드 후 S3에 업로드하는 역할)
    
    runs-on: ubuntu-latest  # GitHub에서 제공하는 최신 Ubuntu 환경에서 실행
    
    steps:
      - name: Checkout code  # 코드 체크아웃
        uses: actions/checkout@v4
        # GitHub 리포지토리의 코드를 현재 워크플로우 환경에 다운로드

      - name: Set up JDK  # Java 환경 설정
        uses: actions/setup-java@v3
        with:
          distribution: 'temurin'  # OpenJDK 배포판 중 하나인 Temurin 사용
          java-version: '17'       # Java 17 버전 설치

      - name: Give gradlew permission  # gradlew 실행 권한 부여
        run: chmod +x ./gradlew
        # 프로젝트 내 gradlew 파일에 실행 권한을 부여합니다.

      - name: Build with Gradle  # Gradle 빌드 실행
        run: ./gradlew clean build
        # clean: 기존 빌드를 삭제하고, build: 새로 빌드를 생성

      - name: Rename jar for deployment  # 빌드된 jar 파일 이름 변경
        run: mv build/libs/*.jar build/libs/app.jar
        # 빌드된 JAR 파일 이름을 고정하여 S3에 업로드할 때 동일한 이름으로 사용

      - name: Create deploy-package directory  # 배포 디렉토리 생성
        run: mkdir -p deploy-package
        # 배포 파일들을 저장할 디렉토리 생성

      - name: Copy deploy files to deploy-package  # 배포 파일들 복사
        run: |
          cp appspec.yml deploy-package/        # appspec.yml 복사
          cp deploy.sh deploy-package/          # deploy.sh 복사
          cp build/libs/app.jar deploy-package/  # 빌드된 app.jar 복사

      - name: Upload deploy package to S3  # S3에 배포 파일 업로드
        uses: jakejarvis/s3-sync-action@master
        with:
          args: --acl private --follow-symlinks  # --acl private: S3에 비공개로 업로드
          # --follow-symlinks: 심볼릭 링크도 따라가서 업로드
        env:
          AWS_S3_BUCKET: ${{ secrets.S3_BUCKET_NAME }}  # S3 버킷 이름 (GitHub Secrets에서 설정)
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}  # AWS IAM 액세스 키
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  # AWS IAM 비밀 키
          AWS_REGION: ${{ secrets.AWS_REGION }}  # AWS 리전
          SOURCE_DIR: deploy-package  # 배포 파일을 포함한 디렉토리

      - name: Trigger CodeDeploy Deployment  # AWS CodeDeploy 배포 트리거
        run: |
          aws deploy create-deployment \  # 배포를 트리거하는 AWS CLI 명령어
            --application-name switching-ec2-app \  # CodeDeploy 애플리케이션 이름
            --deployment-group-name switching-deploy-group \  # 배포 그룹 이름
            --revision revisionType=S3,s3Location={bucket=${{ secrets.S3_BUCKET_NAME }},key=app.zip,bundleType=zip} \  # S3 위치와 배포 파일 설정
            --deployment-config-name CodeDeployDefault.OneAtATime \  # 하나씩 배포하는 기본 설정
            --region ${{ secrets.AWS_REGION }} \  # 리전 정보
            --no-wait  # 배포 완료까지 기다리지 않고 즉시 종료 (백그라운드에서 진행)

하지만 막상 실행했더니 아래 오류가 생기게 됐다...

이 오류는 AWS CodeDeploy가 deploy.sh 스크립트를 배포하려고 할 때, 해당 위치에 이미 동일한 파일이 존재하기 때문에 발생하는 문제였다. 기본적으로 AWS CodeDeploy는 배포 시 파일을 덮어쓰지 않으므로, 이미 존재하는 파일에 대해 덮어쓰기를 시도할 때 문제가 생긴 것이다.

 

해결 방법

  1. 파일 삭제 후 배포하는 방식으로 진행했다.
  2. 배포 전에 deploy.sh와 같은 기존 파일을 명시적으로 삭제하는 절차를 추가하는 방법이다.
  3. 이를 위해 BeforeInstall 훅에서 deploy.sh 파일을 삭제하도록 스크립트를 수정할 수 있다.

1단계: clean-up.sh 파일 만들기

#!/bin/bash
echo "Checking for existing deploy.sh file..."

# 기존 deploy.sh 파일이 존재하면 삭제
if [ -f /home/ubuntu/app/deploy/deploy.sh ]; then
    echo "Found existing deploy.sh, removing it..."
    rm -f /home/ubuntu/app/deploy/deploy.sh
else
    echo "No deploy.sh found, proceeding..."
fi

 

2단계: start.sh 파일 만들기

#!/bin/bash

# -----------------------
# 환경 설정
# -----------------------
APP_NAME=study-app
JAR_NAME=app.jar
DEPLOY_PATH=/home/ubuntu/app/deploy
LOG_PATH=/home/ubuntu/app/logs
S3_BUCKET=switching-bucket-202504
S3_KEY=app.zip  # 이제 zip 파일을 다운로드

echo "⬇️ S3에서 최신 배포 파일 다운로드"
aws s3 cp s3://$S3_BUCKET/$S3_KEY $DEPLOY_PATH/app.zip

if [ $? -ne 0 ]; then
  echo "❗ ZIP 파일 다운로드 실패 - S3에 파일이 없습니다: $S3_BUCKET/$S3_KEY"
  exit 1
fi

echo "✅ ZIP 파일 다운로드 성공: $S3_KEY"

echo "📦 ZIP 파일 풀기"
unzip -o $DEPLOY_PATH/app.zip -d $DEPLOY_PATH  # -o는 기존 파일 덮어쓰기를 허용

if [ $? -ne 0 ]; then
  echo "❗ ZIP 파일 압축 해제 실패"
  exit 1
fi

if [ ! -f "$DEPLOY_PATH/$JAR_NAME" ]; then
  echo "❗ JAR 파일이 존재하지 않습니다 (경로: $DEPLOY_PATH/$JAR_NAME)"
  exit 1
fi

echo "✅ JAR 파일 압축 해제 완료: $JAR_NAME"

echo "🛑 기존 프로세스 종료 (있다면)"
PID=$(pgrep -f "java.*$JAR_NAME")
if [ -n "$PID" ]; then
  kill -9 $PID
  echo "✅ 프로세스 종료 완료 (PID: $PID)"
else
  echo "ℹ️ 종료할 프로세스가 없습니다"
fi

echo "🚀 새 버전 실행"
nohup java -jar $DEPLOY_PATH/$JAR_NAME > $LOG_PATH/app.log 2>&1 &

NEW_PID=$(pgrep -f "java.*$JAR_NAME" | head -n 1)
if [ -n "$NEW_PID" ]; then
  echo "✅ 새 버전 실행 완료 (PID: $NEW_PID)"
else
  echo "❗ 실행 실패 - 프로세스가 시작되지 않았습니다"
  exit 1
fi
  • 기존에 있던 deploy.sh 를 복사해서 이름만 바꿔줬다.

3단계: appspec.yml 수정

version: 0.0
os: linux
files:
  - source: .
    destination: /home/ubuntu/app/deploy
permissions:
  - object: /home/ubuntu/app/deploy
    owner: ubuntu
    group: ubuntu

hooks:
  BeforeInstall:
    - location: clean-up.sh  # clean-up.sh 파일 경로
      timeout: 180

  AfterInstall:
    - location: start.sh  # 기존 start.sh 파일 경로
      timeout: 180

 

4단계: git actions 스크립트 변경

name: Deploy Spring Boot to AWS

on:
  push:
    branches: [ main ]  # main 브랜치에 push 될 때마다 이 워크플로우가 실행됨

jobs:
  build-and-upload:
    runs-on: ubuntu-latest  # GitHub에서 제공하는 최신 Ubuntu 가상환경에서 실행

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        # 현재 리포지토리의 코드를 체크아웃 (가져오기) 해서 다음 단계에서 사용할 수 있게 함

      - name: Set up JDK
        uses: actions/setup-java@v3
        with:
          distribution: 'temurin'  # OpenJDK 배포판 중 하나인 Temurin 사용
          java-version: '17'       # Java 17 버전 설치

      - name: Give gradlew permission
        run: chmod +x ./gradlew

      - name: Build with Gradle
        run: ./gradlew clean build
        # Gradle 빌드 실행 (clean: 기존 빌드 파일 삭제, build: 새로 빌드 생성)
        # 결과물은 보통 build/libs 디렉토리에 생성됨

      - name: Rename jar for deployment
        run: mv build/libs/*.jar build/libs/app.jar
        # S3에 업로드할 때 파일명을 고정하기

      - name: Create deploy-package directory  # 배포 디렉토리 생성
        run: mkdir -p deploy-package
        # CodeDeploy용 디렉토리 생성

      - name: Copy deploy files to deploy-package  # deploy-package에 파일 복사
        run: |
          cp appspec.yml deploy-package/        # 루트에서 appspec.yml 복사
          cp clean-up.sh deploy-package/  # scripts 폴더에서 clean-up.sh 복사
          cp start.sh deploy-package/    # scripts 폴더에서 start.sh 복사
          cp build/libs/app.jar deploy-package/  # 빌드된 app.jar 복사

      - name: Zip deploy package
        run: zip -r app.zip deploy-package

      - name: Upload to S3
        run: aws s3 cp app.zip s3://switching-bucket-202504/app.zip

        env:
          AWS_S3_BUCKET: ${{ secrets.S3_BUCKET_NAME }}  # S3 버킷 이름
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}  # AWS IAM 액세스 키
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  # AWS IAM 비밀 키
          AWS_REGION: ${{ secrets.AWS_REGION }}  # S3 버킷이 있는 AWS 리전
          SOURCE_DIR: deploy-package  # S3에 업로드할 디렉토리

      - name: Trigger CodeDeploy deployment
        run: |
          aws deploy create-deployment \
            --application-name "switching-ec2-app" \
            --deployment-group-name "switching-deploy-group" \
            --revision "revisionType=S3,s3Location={bucket=switching-bucket-202504,key=app.zip,bundleType=zip}" \
            --deployment-config-name "CodeDeployDefault.AllAtOnce"
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}  # AWS IAM 액세스 키
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  # AWS IAM 비밀 키
          AWS_REGION: ${{ secrets.AWS_REGION }}  # AWS 리전
  • deploy.sh는 더 이상 필요하지 않으므로 관련된 부분을 삭제

  • 배포 압축 구조가 잘못되어 있었음
app.zip
└── deploy-package/
    ├── app.jar
    ├── start.sh
    ├── clean-up.sh
    └── appspec.yml

 

CodeDeploy는 압축을 풀었을 때 appspec.yml과 스크립트들이 최상위(root)에 있어야 작동한다고 함.

app.zip
├── app.jar
├── start.sh
├── clean-up.sh
└── appspec.yml

 

git actions 스크립트 수정

name: Deploy Spring Boot to AWS  # 워크플로우 이름

on:
  push:
    branches: [ main ]  # main 브랜치에 push가 발생할 때 실행됨

jobs:
  build-and-upload:
    runs-on: ubuntu-latest  # GitHub에서 제공하는 Ubuntu 최신 환경에서 실행

    steps:
      - name: Checkout code
        uses: actions/checkout@v4  # GitHub 레포지토리 코드를 가져옴

      - name: Set up JDK
        uses: actions/setup-java@v3  # Java 환경 설정
        with:
          distribution: 'temurin'    # OpenJDK 배포판
          java-version: '17'        # Java 17 사용

      - name: Give gradlew permission
        run: chmod +x ./gradlew  # gradlew에 실행 권한 부여

      - name: Build with Gradle
        run: ./gradlew clean build  # Gradle로 빌드 (clean + build)

      - name: Rename jar for deployment
        run: mv build/libs/*.jar build/libs/app.jar  # 빌드된 JAR 파일 이름을 app.jar로 변경

      - name: Prepare deploy-package directory
        run: |
          rm -rf deploy-package                       # 기존 디렉토리 삭제 (있다면)
          mkdir -p deploy-package                     # 새 디렉토리 생성
          cp appspec.yml deploy-package/              # CodeDeploy 스펙 복사
          cp start.sh deploy-package/                 # 시작 스크립트 복사
          cp clean-up.sh deploy-package/              # 종료 스크립트 복사
          cp build/libs/app.jar deploy-package/       # 빌드된 JAR 복사

      - name: Zip for deployment
        run: |
          cd deploy-package                           # 패키징 디렉토리로 이동
          zip -r ../app.zip ./*                       # 전체 파일을 app.zip으로 압축

      - name: Upload to S3
        run: aws s3 cp app.zip s3://switching-bucket-202504/app.zip  # 압축파일 S3에 업로드
        env:
          AWS_S3_BUCKET: ${{ secrets.S3_BUCKET_NAME }}                # S3 버킷 이름 (Secrets에서 가져옴)
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}        # AWS 인증 키
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: ${{ secrets.AWS_REGION }}

      - name: Trigger CodeDeploy deployment
        run: |
          aws deploy create-deployment \
            --application-name "switching-ec2-app" \                     # CodeDeploy 애플리케이션 이름
            --deployment-group-name "switching-deploy-group" \          # 배포 그룹 이름
            --revision "revisionType=S3,s3Location={bucket=switching-bucket-202504,key=app.zip,bundleType=zip}" \  # 배포할 파일 정보
            --deployment-config-name CodeDeployDefault.AllAtOnce \      # 한 번에 전체 배포
            --description "GitHub Actions Deployment"                   # 배포 설명
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}          # AWS 인증 정보
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: ${{ secrets.AWS_REGION }}

 

🚀 지금까지의 GitHub Actions → S3 → EC2 (CodeDeploy) 배포 흐름

👨‍💻 You push to `main`
        │
        ▼
🌐 GitHub Actions (CI/CD)
────────────────────────────────────────────────────────────
1. ✅ Checkout 소스 코드
2. ✅ Gradle로 빌드 → app.jar 생성
3. ✅ 필요한 파일 모으기:
     - app.jar
     - appspec.yml
     - start.sh
     - clean-up.sh
4. ✅ ZIP으로 압축 → app.zip
5. ✅ app.zip 을 S3 버킷에 업로드
6. ✅ CodeDeploy에게 배포 요청
────────────────────────────────────────────────────────────
        │
        ▼
📦 S3 Bucket
switching-bucket-202504/
└── app.zip
    ├── app.jar
    ├── appspec.yml
    ├── start.sh
    └── clean-up.sh
        │
        ▼
🏃 AWS CodeDeploy
────────────────────────────────────────────
1. EC2 인스턴스에 app.zip 다운로드
2. 압축 해제 → /home/ubuntu/app/deploy/
3. `appspec.yml` 기준으로:
   - BeforeInstall: clean-up.sh 실행
   - AfterInstall: start.sh 실행
────────────────────────────────────────────
        │
        ▼
🖥️ EC2 인스턴스
/home/ubuntu/app/deploy/
├── app.jar              ◀── 실행 대상
├── appspec.yml
├── start.sh             ◀── 새 버전 실행
└── clean-up.sh          ◀── 이전 프로세스 종료

 

'[배포]' 카테고리의 다른 글

[AWS] EC2에 MySQL 설치 및 접속  (0) 2025.04.05
[AWS] 🔐 보안 그룹 설정  (0) 2025.04.05
[GitHub Actions] GitHub Actions 명령어  (0) 2025.03.14
[배포] 배포 방식  (0) 2025.02.04
[DevOps] GitHub Actions  (0) 2025.01.18