Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 디스크i/o
- Transaction
- model
- string
- 공통규약
- MVC요청플로우
- SEQUENCE
- SpringLegacy
- MVC
- 커뮤니티서버
- OOP
- 로그백
- 클린아키텍처
- springsecurity
- JPA
- TDD
- 이벤트핸들러등록
- index
- 공통기술
- 설정세팅
- JDBC
- 사면초가
- 이벤트핸들러this
- DDD
- 단위테스트
- AOP
- 냄새라도
- 전전긍긍
- 문제선택근거
- Ajax
Archives
- Today
- Total
개발이군고구마
빌드(CI)와 배포(CD) 본문
728x90
CI/CD OJT GUIDE v2.0
빌드 & 배포 프로세스
Jenkins · GitLab · AWS EC2 · S3 · Launch Template PREV → PRD A/B-SET 무중단배포
00
전체 흐름 한눈에 보기
전체 배포 파이프라인 — 2단계 구조
⬡ 1단계 · 프리뷰 배포 (PREV)
1
프리뷰 브랜치 생성 PREV_20250101 배포 담당자가 GitLab에서 직접 생성
↓
2
개발자 브랜치 → PREV 머지 feature/login → PREV_20250101 feature/payment → PREV_20250101
↓
3
Jenkins 수동 클릭 http://jenkins.mycompany.com Job: deploy-prev / Set: B 선택
↓
4
GitLab에서 Jenkinsfile 로드 scripts/Jenkinsfile-prev
↓
5
PREV 브랜치 체크아웃 & 빌드 → app-pc.jar, app-mo.jar, app-admin.jar
↓
6
빌드 결과물 S3 업로드 (zip) s3://myco-deploy/PREV_20250101/
↓
7
Launch Template → EC2 신규 생성 B-SET / PC·MO·Admin 각각
↓
8
EC2 User Data 자동 실행 S3 → 다운로드 → 앱 실행
↓
✓
QA 검증 완료 → 운영배포 진행
→
⬢ 2단계 · 운영 배포 (PRD)
1
운영 브랜치 생성 PRD_20250101 PREV 내용 기반으로 생성
↓
2
Jenkins 수동 클릭 Job: deploy-prd / 신세트: B 선택
↓
3
운영 전용 Jenkinsfile 로드 scripts/Jenkinsfile-prd ⚠ 빌드 없음 — S3 파일 재사용
↓
4
B-SET 스케일 아웃 PC / MO / Admin EC2 각각 신규 생성 User Data → S3에서 prod 파일 실행
↓
5
B-SET 스모크 테스트 확인
↓
6
구세트 A 절체 시작 ① WEB(Nginx) 먼저 내림 ② WAS(앱) 내림 ③ ALB 트래픽 → B-SET 전환
↓
7
배치 작업 완료 대기 (수 시간) A-SET에서 돌던 배치 Job 종료 확인
↓
✓
A-SET 스케일 인 (EC2 종료) 다음 배포 때 A가 신세트가 됨
01
1단계: 프리뷰 배포 상세
브랜치 생성 & 머지
▶
브랜치 구조
main
└── PRD_20250101 ← 운영 브랜치
└── PREV_20250101 ← 프리뷰 브랜치
├── feature/login
├── feature/payment
└── feature/mypage
└── PRD_20250101 ← 운영 브랜치
└── PREV_20250101 ← 프리뷰 브랜치
├── feature/login
├── feature/payment
└── feature/mypage
머지 대상
이번 배포에 포함될 개발 브랜치들을
배포 담당자가 PREV_20250101로 직접 머지하거나,
각 개발자에게 PR 머지를 요청해요.
배포 담당자가 PREV_20250101로 직접 머지하거나,
각 개발자에게 PR 머지를 요청해요.
Jenkins 파이프라인 실행
▶
Checkout
PREV_20250101
브랜치 받아오기
브랜치 받아오기
→
Build
gradlew build
jar 3개 생성
jar 3개 생성
→
S3 Upload
zip 압축 후
S3 업로드
S3 업로드
→
Deploy
Launch Template
→ EC2 생성
→ EC2 생성
Jenkins가 "어느 Jenkinsfile을 사용할지"는 Job 설정에 저장되어 있어요. 담당자가 수동으로 파일을 고르는 게 아니라, Job 자체가 GitLab의 특정 브랜치에서 Jenkinsfile을 자동으로 읽어옵니다.
02
Jenkinsfile 파일 구조 & 예시
GitLab 저장소 안에 배포 스크립트들이 별도 브랜치 또는 폴더로 관리돼요. 프리뷰용과 운영용 스크립트는 파일이 다릅니다.
이 문서에서 설명하는 배포 구조는 Blue/Green + ASG 혼합 방식이에요. Jenkins는 직접 서버를 만드는 역할이 아니라, 새 버전을 빌드하고, S3에 올리고, ASG가 새 인스턴스를 교체·운영하도록 지시하는 오케스트레이터에 가깝습니다.
이 문서에서 설명하는 배포 구조는 Blue/Green + ASG 혼합 방식이에요. Jenkins는 직접 서버를 만드는 역할이 아니라, 새 버전을 빌드하고, S3에 올리고, ASG가 새 인스턴스를 교체·운영하도록 지시하는 오케스트레이터에 가깝습니다.
이 섹션의 핵심: Jenkinsfile의 목적은 "새 버전 배포를 시작하는 것"이고, 실제로 새 EC2가 뜨고 교체되는 일은 ASG + Launch Template가 담당합니다. 그래서 sec3에서 이어서 보게 될 Launch Template / User Data는 바로 이 Jenkins 배포 흐름의 다음 단계라고 이해하면 됩니다.
| 파일 경로 | 용도 | 빌드 | S3 업로드 | 배포(ASG) | 절체 |
|---|---|---|---|---|---|
| scripts/Jenkinsfile-prev | 프리뷰 전용 | ✔ 있음 | ✔ 있음 | ✔ 있음 | — 없음 |
| scripts/Jenkinsfile-prd | 운영 전용 | — 없음 | — 없음 | ✔ 있음 | ✔ 있음 |
| scripts/Jenkinsfile-build | PR 머지 시 자동 검증 | ✔ 있음 | — 없음 | — 없음 | — 없음 |
Jenkinsfile이 배포 전체에서 맡는 역할
▶
🧭 배포 흐름 한눈에 보기 flow
1. Jenkins가 소스 코드를 체크아웃한다 2. Jenkins가 애플리케이션을 빌드한다 (프리뷰만) 3. Jenkins가 결과물을 S3에 업로드한다 4. Jenkins가 Blue/Green 중 새 세트(New Set)에 연결된 ASG를 갱신한다 5. ASG가 Launch Template 기준으로 새 EC2를 순차적으로 생성한다 6. 새 EC2는 부팅하면서 User Data를 실행한다 7. 애플리케이션이 정상 기동되고 헬스 체크를 통과한다 8. ALB가 트래픽을 새 세트(New Set)로 전환한다 9. 이전 세트(Old Set)는 드레이닝 후 축소 또는 종료한다
왜 Jenkinsfile 설명에서 ASG를 먼저 잡고 가는가?
sec3에서 보게 될 Launch Template과 User Data는 단독으로 움직이지 않기 때문이에요. Jenkins가 배포를 시작하면, 그다음 실제 서버 생성과 부팅은 ASG → Launch Template → EC2 → User Data 순서로 이어집니다. 즉, sec2는 "배포를 누가 시작하느냐"를 설명하고, sec3는 "새 서버가 어떻게 태어나고 부팅하느냐"를 설명하는 구조예요.
scripts/Jenkinsfile-prev — 프리뷰 배포 스크립트
▶
프리뷰 배포의 목적은 새 버전을 먼저 검증 가능한 세트에 올려보는 것이에요. Jenkins는 코드를 빌드하고 S3에 배포 파일을 올린 뒤, 프리뷰용 ASG가 새 인스턴스를 교체하도록 refresh를 시작합니다.
📄 scripts/Jenkinsfile-prev Groovy
// Jenkins Job 설정에서 이 파일을 GitLab에서 읽어옴 // GitLab: https://gitlab.mycompany.com/myteam/myrepo.git // Branch: scripts Script Path: scripts/Jenkinsfile-prev pipeline { agent any parameters { // 배포 담당자가 Jenkins 화면에서 입력 string(name: 'BRANCH', defaultValue: 'PREV_20250101') choice(name: 'TARGET_SET', choices: ['A-SET', 'B-SET']) } environment { S3_BUCKET = 'myco-deploy' AWS_REGION = 'ap-northeast-2' // 프리뷰용 ASG PREV_PC_ASG = 'prev-pc-asg' PREV_MO_ASG = 'prev-mo-asg' PREV_ADM_ASG = 'prev-admin-asg' } stages { stage('Checkout') { steps { // 여기서 PREV_20250101 브랜치를 체크아웃 git branch: "${params.BRANCH}", credentialsId: 'gitlab-jenkins-token', url: 'https://gitlab.mycompany.com/myteam/myrepo.git' } } stage('Build') { steps { sh ''' ./gradlew :pc-server:build -x test ./gradlew :mo-server:build -x test ./gradlew :admin-server:build -x test ''' // 결과: build/pc-server/app-pc.jar // build/mo-server/app-mo.jar // build/admin-server/app-admin.jar } } stage('S3 Upload') { steps { sh """ zip -j deploy.zip \ build/pc-server/app-pc.jar \ build/mo-server/app-mo.jar \ build/admin-server/app-admin.jar aws s3 cp deploy.zip \ s3://${S3_BUCKET}/${params.BRANCH}/deploy.zip \ --region ${AWS_REGION} """ // S3 경로: s3://myco-deploy/PREV_20250101/deploy.zip } } stage('Refresh Preview ASG') { steps { sh """ # 프리뷰 세트에 연결된 ASG가 새 인스턴스를 순차적으로 교체하도록 refresh 시작 aws autoscaling start-instance-refresh \ --auto-scaling-group-name ${PREV_PC_ASG} \ --preferences MinHealthyPercentage=50,InstanceWarmup=120 \ --region ${AWS_REGION} aws autoscaling start-instance-refresh \ --auto-scaling-group-name ${PREV_MO_ASG} \ --preferences MinHealthyPercentage=50,InstanceWarmup=120 \ --region ${AWS_REGION} aws autoscaling start-instance-refresh \ --auto-scaling-group-name ${PREV_ADM_ASG} \ --preferences MinHealthyPercentage=50,InstanceWarmup=120 \ --region ${AWS_REGION} """ } } stage('Wait for Preview Health Check') { steps { sh """ echo "Waiting for preview instances to pass health checks..." sleep 60 """ } } } }
프리뷰에서 왜 ASG를 쓰는가?
운영만 ASG로 하고 프리뷰는 다르게 가면, 두 환경의 동작 방식이 달라져요. 프리뷰도 ASG 기반으로 맞춰두면 운영과 최대한 비슷한 조건에서 검증할 수 있어서, 배포 안정성이 훨씬 좋아집니다.
scripts/Jenkinsfile-prd — 운영 배포 스크립트
▶
운영 배포는 프리뷰 때 이미 검증된 S3 파일을 그대로 씁니다. 빌드를 다시 하지 않는 이유는 "검증한 결과물 그대로 운영에 반영한다"는 원칙을 지키기 위해서예요.
운영에서는 새 세트(New Set)의 ASG를 먼저 갱신해 충분히 준비시킨 뒤, 마지막에 ALB가 트래픽을 넘기는 방식으로 절체가 일어납니다.
운영에서는 새 세트(New Set)의 ASG를 먼저 갱신해 충분히 준비시킨 뒤, 마지막에 ALB가 트래픽을 넘기는 방식으로 절체가 일어납니다.
📄 scripts/Jenkinsfile-prd Groovy
pipeline {
agent any
parameters {
string(name: 'PREV_BRANCH', defaultValue: 'PREV_20250101')
string(name: 'PRD_BRANCH', defaultValue: 'PRD_20250101')
choice(name: 'NEW_SET', choices: ['B-SET', 'A-SET'])
choice(name: 'OLD_SET', choices: ['A-SET', 'B-SET'])
}
environment {
S3_BUCKET = 'myco-deploy'
AWS_REGION = 'ap-northeast-2'
// 운영용 ASG
PRD_PC_ASG = 'prd-pc-asg'
PRD_MO_ASG = 'prd-mo-asg'
PRD_ADM_ASG = 'prd-admin-asg'
// 운영용 Launch Template
PC_LT_NAME = 'prd-pc-server-template'
MO_LT_NAME = 'prd-mo-server-template'
ADM_LT_NAME = 'prd-admin-server-template'
}
stages {
// Build 단계 없음 — 프리뷰에서 검증된 산출물 그대로 사용
stage('Promote Artifact Metadata') {
steps {
sh """
# 실제 환경에서는 S3 copy, parameter store, launch template versioning 등을 함께 사용 가능
echo "Promote artifact from ${params.PREV_BRANCH} to ${params.PRD_BRANCH}"
"""
}
}
stage('Create Launch Template Version') {
steps {
sh """
# 새 버전 설명만 예시로 표현
# 실제로는 새 AMI ID 또는 User Data 변경분을 반영할 수 있음
aws ec2 create-launch-template-version \
--launch-template-name ${PC_LT_NAME} \
--source-version '$Latest' \
--version-description "${params.PRD_BRANCH}" \
--region ${AWS_REGION}
aws ec2 create-launch-template-version \
--launch-template-name ${MO_LT_NAME} \
--source-version '$Latest' \
--version-description "${params.PRD_BRANCH}" \
--region ${AWS_REGION}
aws ec2 create-launch-template-version \
--launch-template-name ${ADM_LT_NAME} \
--source-version '$Latest' \
--version-description "${params.PRD_BRANCH}" \
--region ${AWS_REGION}
"""
}
}
stage('Update New Set ASG') {
steps {
sh """
# 새 세트(New Set)에 연결된 ASG가 새 Launch Template 버전을 사용하도록 갱신
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name ${PRD_PC_ASG}-${params.NEW_SET} \
--launch-template LaunchTemplateName=${PC_LT_NAME},Version='$Latest' \
--region ${AWS_REGION}
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name ${PRD_MO_ASG}-${params.NEW_SET} \
--launch-template LaunchTemplateName=${MO_LT_NAME},Version='$Latest' \
--region ${AWS_REGION}
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name ${PRD_ADM_ASG}-${params.NEW_SET} \
--launch-template LaunchTemplateName=${ADM_LT_NAME},Version='$Latest' \
--region ${AWS_REGION}
"""
}
}
stage('Refresh New Set') {
steps {
sh """
aws autoscaling start-instance-refresh \
--auto-scaling-group-name ${PRD_PC_ASG}-${params.NEW_SET} \
--preferences MinHealthyPercentage=75,InstanceWarmup=180 \
--region ${AWS_REGION}
aws autoscaling start-instance-refresh \
--auto-scaling-group-name ${PRD_MO_ASG}-${params.NEW_SET} \
--preferences MinHealthyPercentage=75,InstanceWarmup=180 \
--region ${AWS_REGION}
aws autoscaling start-instance-refresh \
--auto-scaling-group-name ${PRD_ADM_ASG}-${params.NEW_SET} \
--preferences MinHealthyPercentage=50,InstanceWarmup=180 \
--region ${AWS_REGION}
"""
}
}
stage('Wait for Health Check') {
steps {
sh """
# 실제 운영에서는 서비스별 target group 상태를 개별 확인
echo "Waiting for new set instances to pass health checks..."
sleep 120
"""
}
}
stage('ALB Traffic Switch') {
steps {
sh """
# ALB가 기본 트래픽을 새 세트(New Set)로 넘김
aws elbv2 modify-listener \
--listener-arn arn:aws:elasticloadbalancing:ap-northeast-2:... \
--default-actions Type=forward,TargetGroupArn=arn:...:targetgroup/${params.NEW_SET}
"""
}
}
stage('Drain Old Set') {
steps {
sh """
# 이전 세트(Old Set)는 즉시 종료하지 않고 드레이닝 후 축소
echo "Old set ${params.OLD_SET} is now draining..."
"""
}
}
// 스케일 인은 운영 확인 후 수동 또는 별도 Job으로 처리 가능
}
}
운영 배포에서 가장 중요한 포인트는?
핵심은 기존 세트를 바로 건드리지 않고, 새 세트를 먼저 완성한 뒤 마지막에 트래픽만 전환한다는 점이에요. 이게 Blue/Green의 안정성과 ASG의 자동 교체 능력을 함께 쓰는 방식이고, 그래서 운영 배포가 더 안전해집니다.
sec3로 자연스럽게 이어지는 포인트
▶
여기까지가 "Jenkins가 배포를 어떻게 시작하느냐"에 대한 설명이었다면, 다음 sec3는 "ASG가 새 EC2를 만들 때 정확히 무엇을 참고하느냐"를 설명하는 파트예요.
즉, sec2의 마지막 장면은 ASG Refresh 시작이고, sec3의 첫 장면은 Launch Template을 참고해 EC2가 생성되고, 그 안에서 User Data가 실행되는 순간이라고 보면 됩니다.
즉, sec2의 마지막 장면은 ASG Refresh 시작이고, sec3의 첫 장면은 Launch Template을 참고해 EC2가 생성되고, 그 안에서 User Data가 실행되는 순간이라고 보면 됩니다.
03
Launch Template & User Data — EC2가 스스로 부팅하는 원리
핵심 원리: Launch Template = EC2의 "설계도". 그 안의 User Data = EC2가 처음 켜질 때 AWS가 자동으로 실행해주는 shell 스크립트. Jenkins는 "이 설계도로 EC2 만들어줘" 라고 AWS에 API 요청만 할 뿐, 이후는 EC2가 스스로 다 처리합니다.
정확한 순서는 (1) Jenkins가 ASG에 배포/교체 요청 → (2) ASG가 Launch Template을 참고해 EC2 생성 → (3) EC2가 부팅하면서 User Data 실행 입니다.
정확한 순서는 (1) Jenkins가 ASG에 배포/교체 요청 → (2) ASG가 Launch Template을 참고해 EC2 생성 → (3) EC2가 부팅하면서 User Data 실행 입니다.
Launch Template 목록 (예시)
prev-pc-server-template
prev-mo-server-template
prev-admin-server-template
prd-pc-server-template
prd-mo-server-template
prd-admin-server-template
prev-mo-server-template
prev-admin-server-template
prd-pc-server-template
prd-mo-server-template
prd-admin-server-template
Launch Template이 담고 있는 정보
AMI ID (OS 이미지)
인스턴스 타입 (c5.2xlarge 등)
IAM Role (S3 접근 권한)
보안 그룹 ID
서브넷 설정
User Data (부팅 스크립트)
즉, "이 사양의 서버를 이렇게 부팅해"라는 생성 규격 묶음
인스턴스 타입 (c5.2xlarge 등)
IAM Role (S3 접근 권한)
보안 그룹 ID
서브넷 설정
User Data (부팅 스크립트)
즉, "이 사양의 서버를 이렇게 부팅해"라는 생성 규격 묶음
Jenkins → ASG → Launch Template → EC2 → User Data 흐름
▶
이렇게 이해하면 정확합니다: Jenkins가 "새 버전으로 배포해"라고 요청하면, ASG가 Launch Template을 기준으로 EC2를 만들고, 새 EC2는 부팅하면서 User Data를 실행해 애플리케이션을 서비스 가능한 상태로 올립니다.
🗺 동작 순서도 flow
┌─────────┐
│ Jenkins │
└────┬────┘
│
│ 배포 시작 / ASG 갱신 / Instance Refresh 요청
▼
┌────────────────────┐
│ Auto Scaling Group │
└────┬───────────────┘
│
│ "어떤 사양으로 새 서버를 만들까?"
▼
┌──────────────────┐
│ Launch Template │
└────┬─────────────┘
│
│ AMI / 타입 / IAM / SG / User Data 제공
▼
┌──────────────────┐
│ EC2 Instance │
└────┬─────────────┘
│
│ 첫 부팅
▼
┌──────────────────┐
│ User Data │
│ - 패키지 설치 │
│ - S3에서 파일 다운로드 │
│ - jar 실행 │
└────┬─────────────┘
│
▼
┌──────────────────┐
│ 서비스 가능 상태 │
└──────────────────┘
그럼 User Data가 EC2를 만드는 건가?
아닙니다. User Data는 이미 만들어진 EC2 안에서 실행되는 초기화 스크립트예요. EC2 생성은 ASG가 Launch Template을 참고해서 하고, User Data는 그 EC2가 처음 켜질 때 실행됩니다.
Auto Scaling Group과의 관계
▶
Launch Template만 있으면 EC2가 자동으로 생기나?
아니에요. Launch Template은 설계도일 뿐, 스스로 EC2를 만들지 않습니다. 누군가 이 설계도를 사용해야 하는데, 그 대표적인 주체가 ASG입니다.
Launch Template
"어떤 스펙으로 만들지?"를 정의
예: AMI, 인스턴스 타입, 보안 그룹, User Data
예: AMI, 인스턴스 타입, 보안 그룹, User Data
Auto Scaling Group
"몇 대를 유지할지?"를 관리
필요하면 Launch Template을 참고해 새 EC2 생성
필요하면 Launch Template을 참고해 새 EC2 생성
Jenkins
배포의 시작점
ASG 설정 변경, desired capacity 조절, Instance Refresh 트리거
ASG 설정 변경, desired capacity 조절, Instance Refresh 트리거
실무에서는 Jenkins가 직접 EC2를 하나하나 만드는 것보다, ASG의 Launch Template 버전을 교체하거나 Instance Refresh를 시작해서 새 인스턴스가 순차적으로 교체되도록 만드는 경우가 많습니다.
배포 시 실제로 벌어지는 일
▶
📦 배포 흐름 예시 deploy
1. Jenkins가 새 버전을 빌드한다 2. Jenkins가 결과물을 S3에 업로드하거나 새 AMI를 만든다 3. Jenkins가 ASG에 새 Launch Template 버전을 사용하도록 설정한다 4. Jenkins가 Instance Refresh를 시작한다 5. ASG가 기존 인스턴스를 순서대로 교체한다 6. 새 EC2가 Launch Template 기준으로 생성된다 7. 새 EC2는 부팅하면서 User Data를 실행한다 8. 앱이 정상 기동되고 헬스 체크를 통과하면 트래픽을 받는다
왜 이 구조가 좋은가?
새 서버가 완전히 준비된 뒤에만 서비스에 붙일 수 있어서 무중단에 가까운 롤링 배포가 가능하고, 기존 서버에 문제가 생겨도 ASG가 다시 같은 설계도로 새 서버를 만들어 복구 자동화가 가능해집니다.
prd-pc-server-template — User Data (PC 서버용)
▶
EC2는 "나는 PC 서버야"라는 걸 어떻게 알지?
Launch Template 자체가 역할별로 따로 존재해요. prd-pc-server-template으로 만들어진 EC2는 User Data에 PC 전용 jar를 받아오도록 이미 쓰여 있어요. MO 서버용 Template은 MO jar를, Admin용은 Admin jar를 받아오죠. EC2는 태어날 때부터 역할이 정해져 있습니다.
⚡ User Data — prd-pc-server-template bash
#!/bin/bash # ===================================================== # 이 스크립트는 EC2 첫 부팅 시 AWS가 자동으로 실행합니다 # Launch Template: prd-pc-server-template # 역할: PC 웹 서버 (Spring Boot) # ===================================================== # 1. 환경 변수 설정 DEPLOY_BRANCH="PRD_20250101" # Jenkins에서 넘겨받거나 고정값 S3_BUCKET="myco-deploy" APP_DIR="/opt/myapp" LOG_DIR="/var/log/myapp" # 2. 필수 패키지 설치 yum update -y yum install -y java-17-amazon-corretto awscli unzip # 3. 디렉토리 생성 mkdir -p $APP_DIR $LOG_DIR # 4. S3에서 빌드 결과물 다운로드 # Jenkins가 올려둔 경로: s3://myco-deploy/PRD_20250101/deploy.zip aws s3 cp s3://${S3_BUCKET}/${DEPLOY_BRANCH}/deploy.zip $APP_DIR/deploy.zip # 5. 압축 해제 unzip -o $APP_DIR/deploy.zip -d $APP_DIR/ # 6. PC 서버 전용 jar 실행 # ↑ MO 서버라면 app-mo.jar, Admin이라면 app-admin.jar를 실행 java -jar $APP_DIR/app-pc.jar \ --spring.profiles.active=prod \ --server.port=8080 \ --logging.file.name=$LOG_DIR/pc-server.log \ > $LOG_DIR/startup.log 2>&1 & echo "PC Server started. PID: $!"
prd-mo-server-template — User Data (MO 서버용)
▶
⚡ User Data — prd-mo-server-template bash
#!/bin/bash DEPLOY_BRANCH="PRD_20250101" S3_BUCKET="myco-deploy" APP_DIR="/opt/myapp" yum install -y java-17-amazon-corretto awscli unzip mkdir -p $APP_DIR aws s3 cp s3://${S3_BUCKET}/${DEPLOY_BRANCH}/deploy.zip $APP_DIR/deploy.zip unzip -o $APP_DIR/deploy.zip -d $APP_DIR/ # ↓ PC 서버와 다른 점: app-mo.jar 실행 + MO 전용 포트 java -jar $APP_DIR/app-mo.jar \ --spring.profiles.active=prod \ --server.port=8081 \ > /var/log/myapp/mo-server.log 2>&1 &
S3 버킷 구조
▶
☁ s3://myco-deploy/ S3
s3://myco-deploy/ ├── PREV_20241201/ # 이전 프리뷰 (롤백 보관용) │ └── deploy.zip ├── PREV_20250101/ # 이번 프리뷰 빌드 결과물 │ └── deploy.zip # ← app-pc.jar + app-mo.jar + app-admin.jar 포함 ├── PRD_20241201/ # 이전 운영 (롤백 시 재사용 가능) │ └── deploy.zip └── PRD_20250101/ # 이번 운영 배포 (PREV와 동일 파일) └── deploy.zip # ← 프리뷰 때 업로드한 파일 그대로 사용
날짜별로 폴더가 나뉘어 있어서, 운영 배포 후 문제가 생기면 이전 버전 경로의 파일로 User Data를 수정하고 EC2를 다시 띄우는 것만으로 롤백이 가능해요.
마지막으로 꼭 구분해야 하는 것
▶
맞는 이해
Jenkins가 ASG에 배포/교체를 요청한다
ASG가 Launch Template을 참고해 EC2를 만든다
EC2는 부팅하면서 User Data를 실행한다
ASG가 Launch Template을 참고해 EC2를 만든다
EC2는 부팅하면서 User Data를 실행한다
틀리기 쉬운 이해
User Data가 EC2를 만든다
Launch Template이 혼자 EC2를 만든다
Jenkins가 모든 서버 내부 작업까지 직접 수행한다
Launch Template이 혼자 EC2를 만든다
Jenkins가 모든 서버 내부 작업까지 직접 수행한다
04
2단계: 운영 배포 상세
B-SET 스케일 아웃 — 신세트 EC2 생성
▶
🔵 A-SET (현재 운영 중)
pc-server × 4대 (구버전 운영)
mo-server × 4대 (구버전 운영)
admin-server × 2대 (구버전 운영)
실 사용자 트래픽 처리 중
배포 중 건드리지 않음
🟣 B-SET (신규 생성 중)
prd-pc-server-template → 4대 생성
prd-mo-server-template → 4대 생성
prd-admin-server-template → 2대 생성
User Data 실행 → S3 다운로드 → 앱 기동
헬스체크 통과 후 트래픽 전환 대기
05
절체(WEB→WAS 순서) & 스케일 인
절체 — WEB 먼저, WAS 나중에 내리는 이유
▶
WAS 먼저 내리면? 사용자 요청 → Nginx(살아있음) → WAS(죽음) → 502 Bad Gateway 에러가 사용자에게 노출돼요!
WEB 먼저 내리면? Nginx가 꺼지면서 이 서버로 신규 요청이 들어오지 않아요. 이미 처리 중인 요청만 WAS가 마저 처리하고, 그 후 WAS를 내리면 에러 없이 깔끔하게 종료됩니다.
절체 순서 — 구세트 A 순서도
① WEB(Nginx) 종료 systemctl stop nginx # A-SET WEB 서버 전체 → 이 시점부터 신규 요청은 B-SET으로만 감 (ALB가 A제외) ② WAS(Spring Boot 앱) 종료 systemctl stop app # A-SET WAS 서버 전체 → 처리 중이던 요청 graceful shutdown 후 종료 ③ ALB 리스너 Target Group 변경 A-SET Target Group → B-SET Target Group → 사용자는 이제 전부 신버전(B) 서비스 이용 ④ 배치 작업 완료 대기 A-SET에서 돌던 스케줄러/배치 Job이 끝날 때까지 EC2는 살려둠 예: 매일 새벽 2시 정산 배치가 돌고 있다면 그게 끝날 때까지 대기 ⑤ A-SET 스케일 인 aws ec2 terminate-instances --instance-ids i-0abc123... i-0def456... → A-SET EC2 전부 종료 (비용 절감)
A/B-SET 다음 배포 때 역할이 바뀌는 구조
▶
배포 회차별 A/B 역할 변화 흐름
# 1차 배포 A-SET: 구버전 운영 → 절체 후 종료 B-SET: 신규 생성 → 운영 중 # 2차 배포 (다음 배포) B-SET: 현재 운영 중 → 절체 후 종료 A-SET: 신규 생성 → 운영 중 # 3차 배포 A-SET: 현재 운영 중 → 절체 후 종료 B-SET: 신규 생성 → 운영 중 # → 이렇게 번갈아 쓰는 전략 = Blue/Green 배포 # 항상 한 쪽이 살아서 트래픽을 받고 있어서 서비스 중단이 없음
06
Q&A 정리
Jenkins 스크립트는 스크립트 파일이 있는 깃 브랜치를 "호출"한다는 게 무슨 뜻?
Jenkins Job 설정 화면에 "SCM(소스 코드 관리)" 항목이 있어요. 여기에 GitLab 주소 + 브랜치 + Jenkinsfile 경로가 저장되어 있어요. 배포 담당자가 버튼을 클릭하면 Jenkins가 그 설정을 보고 GitLab에서 Jenkinsfile을 가져와서 실행하는 구조예요.
# Jenkins Job 설정 (UI에서 저장된 값) Pipeline script from SCM SCM: Git Repository URL: https://gitlab.mycompany.com/myteam/myrepo.git Credentials: gitlab-jenkins-token Branch: */scripts ← 스크립트 전용 브랜치 Script Path: scripts/Jenkinsfile-prev ← Jenkinsfile 위치
브랜치(PREV_20250101)는 어떻게 체크아웃 받는 거지? 이것도 Job에 써있나?
맞아요, 전부 Jenkinsfile 안에 명시되어 있어요. Jenkinsfile의 Checkout stage에서
git branch: "${params.BRANCH}" 로 받아오는데, 이 BRANCH 값은 Jenkins 실행 시 파라미터로 입력돼요. 사람이 수동으로 git clone하는 게 아니에요.운영 배포 때 "스크립트 배포 Job이 실행된다 — 따로 스크립트 작성 없이?"
배포 로직이 이미 Jenkinsfile-prd의 Deploy Stage 안에 작성되어 있어요. 담당자는 버튼 하나만 클릭하면 되고, 그 이후 EC2 생성 → 절체 → ALB 전환까지 자동으로 실행돼요. 새로운 스크립트를 그때그때 작성하는 게 아니라, 미리 만들어둔 스크립트가 파라미터(어떤 브랜치? 몇 세트?) 만 받아서 실행되는 구조예요.
EC2가 S3 파일 받고 부팅하는 것도 다 Job에 써있는 건가?
반반이에요. EC2 생성 명령은 Jenkinsfile에 있고, EC2가 부팅하면서 S3에서 파일 받고 앱을 실행하는 로직은 Launch Template의 User Data에 있어요. Jenkins가 EC2에 직접 SSH 접속해서 명령하는 게 아니라, EC2가 부팅되면서 AWS 인프라가 User Data를 자동으로 실행해줍니다.
각 EC2가 PC/MO/Admin 중 어떤 앱을 받아와야 하는지 어떻게 알지?
Launch Template이 역할별로 따로 있어요. prd-pc-server-template의 User Data에는 app-pc.jar를 받아서 실행하는 스크립트가, prd-mo-server-template에는 app-mo.jar를 받는 스크립트가 각각 저장되어 있어요. EC2는 어떤 Template으로 만들어졌느냐에 따라 역할이 결정돼요.
프리뷰와 운영 배포 스크립트(Jenkinsfile)는 다 다른 건가?
맞아요. 목적이 다르기 때문에 파일 자체가 달라요. 프리뷰(Jenkinsfile-prev)는 빌드 + S3 업로드 + EC2 생성까지 다 포함하고, 운영(Jenkinsfile-prd)은 빌드 없이 이미 S3에 올라간 파일을 쓰면서 EC2 생성 + 절체 + ALB 전환까지 담당해요. 환경 변수(spring.profiles.active)도 preview vs prod로 달라요.
'Cloud Deploy' 카테고리의 다른 글
| Git의 원리: 객체, 브랜치, HEAD, 병합, 원격 저장소 (0) | 2026.03.21 |
|---|---|
| Gitlab MR 병목현상 (0) | 2026.03.15 |
| [AWS Networking] DNS와 보안 (Route53 & Security) (0) | 2026.01.27 |
| [AWS Networking] 로드밸런싱의 모든 것ELB (0) | 2026.01.24 |
| [AWS Networking] VPC &Subnet (0) | 2026.01.23 |