순서대로만... 따라하시면... 될겁니다...
학습 목표
- 컨테이너화의 필요성과 Docker 활용법
- 클라우드 인프라스트럭처의 구성 요소
- CI/CD 파이프라인의 설계와 구현
- 운영 환경에서의 서비스 배포 전략
선수 지식
- Spring Boot 기반 웹 애플리케이션 개발
- Git을 통한 버전 관리
- 리눅스 기본 명령어
- 네트워킹 기초 (포트, IP 개념)
Chapter 1: 현재 상황 분석
1.1 로컬 개발 환경의 특성과 한계
현재 상황: 개발자의 로컬 머신에서만 실행되는 Spring Boot 애플리케이션|

로컬 환경의 구성요소:
- Spring Boot 애플리케이션 (localhost:8080)
- 로컬 데이터베이스 (MySQL/H2)
- 개발자 PC의 특정 환경 (Java 버전, OS, 설정 등)
핵심 문제점:
- 접근성 제약: 개발자 PC에서만 접근 가능
- 가용성 문제: PC 종료 시 서비스 중단
- 환경 의존성: "내 컴퓨터에서는 됩니다" 문제
- 확장성 부족: 사용자 증가 시 대응 불가
1.2 목표: 전체 배포 로드맵 이해
최종 목표: 코드 푸시만으로 자동 배포되는 24시간 운영 서비스

4단계 로드맵:
1단계: 로컬 개발 (현재 상황)
- Spring Boot 애플리케이션 완성
- 로컬 DB 연동
- localhost:8080에서 정상 동작
2단계: 컨테이너화
- Dockerfile 작성으로 환경 독립성 확보
- Docker 이미지 빌드
- 컨테이너로 실행하여 환경 문제 해결
3단계: AWS 인프라 구축
- EC2 인스턴스 (실제 서버)
- RDS 데이터베이스 (관리형 DB)
- 보안 그룹 설정 (네트워크 보안)
4단계: CI/CD 자동화
- GitHub Actions으로 자동 배포
- 코드 푸시 → 자동 테스트 → 자동 배포
- 완전 자동화된 개발 workflow
각 단계별 핵심 질문:
- "왜 로컬에서 잘 되는데 도커로 만들어야 할까?"
- "AWS가 정확히 어떤 역할을 하는 거지?"
- "CI/CD가 없어도 배포는 되는 거 아닌가?"
- "테스트 코드와 CI는 어떤 관계인가?"
Chapter 2: 컨테이너화 (Containerization)
2.1 왜 컨테이너화가 필요한가?
환경 의존성 문제:
개발 환경 vs 운영 환경 불일치:

Docker의 해결책: 컨테이너는 애플리케이션과 모든 의존성을 하나의 패키지로 묶어서, 어떤 환경에서든 동일하게 실행되도록 보장합니다.
Docker 컨테이너에 포함되는 것들:
- Java 17 Runtime (내장)
- Spring Boot 애플리케이션 (내장)
- 필요한 라이브러리 (내장)
- 설정 파일 (내장)
- 환경 변수 (내장)
2.2 Dockerfile 작성 및 이해
Dockerfile의 구조: 레이어 기반 시스템
# 1. 베이스 이미지 설정 (OS + Java Runtime)
FROM openjdk:17-jre-slim
# 2. 메타데이터 추가
LABEL maintainer="student@university.edu"
LABEL version="1.0"
# 3. 작업 디렉토리 설정
WORKDIR /app
# 4. 애플리케이션 파일 복사
COPY build/libs/*.jar app.jar
# 5. 포트 노출 (문서화 목적)
EXPOSE 8080
# 6. 시작 명령어 정의
ENTRYPOINT ["java", "-jar", "app.jar"]
각 명령어 설명:
- FROM: 기본이 되는 운영체제와 런타임 설정
- WORKDIR: 컨테이너 내부의 작업 디렉토리
- COPY: 로컬 파일을 컨테이너로 복사
- EXPOSE: 사용할 포트 번호 명시
- ENTRYPOINT: 컨테이너 시작 시 실행할 명령어
레이어 시스템 이해: Docker는 각 명령어마다 새로운 레이어를 생성합니다:
- Layer 1: Ubuntu + Java 17 Runtime
- Layer 2: 메타데이터 + 작업 디렉토리
- Layer 3: Spring Boot JAR 파일
- Layer 4: 포트 설정 + 시작 명령어
실습: 빌드 과정
# Step 1: 애플리케이션 빌드
./gradlew clean build
# Step 2: Docker 이미지 생성
docker build -t my-spring-app .
# Step 3: 컨테이너 실행
docker run -p 8080:8080 my-spring-app
# Step 4: 테스트
curl http://localhost:8080/health
2.3 Docker Compose를 통한 Multi-container 환경
왜 Docker Compose가 필요한가? 실제 애플리케이션은 보통 여러 서비스로 구성됩니다:
- 애플리케이션 서버
- 데이터베이스
- 캐시 서버 (Redis)
- 등등...
docker-compose.yml 작성:
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=docker
- SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/myapp
depends_on:
- db
networks:
- app-network
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: myapp
MYSQL_USER: appuser
MYSQL_PASSWORD: apppassword
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
networks:
- app-network
volumes:
db_data:
networks:
app-network:
driver: bridge
Docker Compose의 장점:
- 서비스 간 네트워킹: 컨테이너들이 서로 통신 가능
- 의존성 관리: depends_on으로 시작 순서 제어
- 환경 변수 관리: 각 서비스별 설정 분리
- 볼륨 관리: 데이터 영속성 보장
실행 명령어:
# 모든 서비스 시작
docker-compose up -d
# 로그 확인
docker-compose logs
# 서비스 중지 및 삭제
docker-compose down
Chapter 3: AWS 클라우드 인프라스트럭처
3.1 Docker Hub vs AWS: 역할 차이 이해
나의 오해: "Docker Hub에 올리면 배포 완료 아닌가?"
Docker Hub = 창고 (저장소):
- 이미지를 저장만 함
- 실제로 실행되지 않음
- 다운로드만 가능
AWS = 실제 컴퓨터 (실행 환경):
- 이미지를 다운받아서 실행
- 24시간 가동되는 서버
- 외부에서 접근 가능
전체 배포 과정:
1. 로컬에서 이미지 빌드
↓
2. Docker Hub에 업로드 (저장)
↓
3. AWS EC2에서 이미지 다운로드
↓
4. EC2에서 컨테이너 실행 (실제 서비스)
↓
5. 외부 사용자들이 접속 가능
3.2 AWS 아키텍처 설계
목표 아키텍처:
Internet
│
▼
┌──────────────────────────────────────┐
│ AWS Cloud │
│ ┌─────────────────────────────────┐ │
│ │ EC2 Instance │ │ ← 애플리케이션 서버
│ │ Docker Container 실행 │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ RDS MySQL │ │ ← 데이터베이스
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ S3 Bucket │ │ ← 파일 저장소
│ └─────────────────────────────────┘ │
└──────────────────────────────────────┘
각 서비스의 역할:
EC2 (Elastic Compute Cloud):
- 가상 서버 역할
- Docker 컨테이너 실행 환경
- 24시간 가동
- 외부 접근 가능한 Public IP
RDS (Relational Database Service):
- 관리형 데이터베이스 서비스
- MySQL 8.0 엔진 사용
- 자동 백업, 보안 패치
- EC2에서만 접근 가능 (보안)
S3 (Simple Storage Service):
- 파일 저장소
- 프로필 이미지, 정적 파일 저장
- 전세계 어디서든 접근 가능
- 무제한 용량
3.3 AWS 서비스 설정 실습
3.3.1 EC2 인스턴스 생성:
인스턴스 설정:
- AMI: Ubuntu Server 22.04 LTS
- 인스턴스 타입: t3.micro (1 vCPU, 1GB RAM)
- 스토리지: 8GB gp3
- 키 페어: 새로 생성 (.pem 파일 다운로드)
보안 그룹 설정:
Inbound Rules:
- SSH (22): My IP (SSH 접속용)
- HTTP (80): 0.0.0.0/0 (웹 트래픽)
- Custom TCP (8080): 0.0.0.0/0 (Spring Boot)
탄력적 IP 할당:
- EC2 재시작 시에도 IP 고정
- 도메인 연결 시 필요
3.3.2 RDS 설정:
데이터베이스 설정:
- 엔진: MySQL 8.0
- 템플릿: 프리 티어
- 인스턴스 클래스: db.t3.micro
- 스토리지: 20GB gp2
네트워크 보안:
RDS Security Group:
- Type: MySQL/Aurora (3306)
- Source: EC2 Security Group ID
- Description: MySQL access from EC2
3.3.3 S3 버킷 생성:
- 버킷 이름: unique-bucket-name-12345
- 리전: ap-northeast-2 (서울)
- 퍼블릭 액세스: 읽기만 허용
3.4 수동 배포 테스트
EC2에 SSH 접속:
# pem 키 권한 설정
chmod 400 my-key.pem
# EC2 접속
ssh -i my-key.pem ec2-user@[EC2-PUBLIC-IP]
EC2에서 Docker 설치:
# Docker 설치
sudo yum update -y
sudo yum install -y docker
sudo service docker start
sudo usermod -a -G docker ec2-user
# 재접속 후 Docker 테스트
docker --version
애플리케이션 배포:
# Docker Hub에서 이미지 다운로드
docker pull your-username/my-spring-app:latest
# 컨테이너 실행
docker run -d --name my-app -p 80:8080 \
-e SPRING_DATASOURCE_URL=jdbc:mysql://[RDS-ENDPOINT]:3306/myapp \
-e SPRING_DATASOURCE_USERNAME=appuser \
-e SPRING_DATASOURCE_PASSWORD=apppassword \
your-username/my-spring-app:latest
# 실행 확인
docker ps
curl localhost:80/health
외부 접속 테스트:
- 브라우저에서 http://[EC2-PUBLIC-IP] 접속
- API 테스트: http://[EC2-PUBLIC-IP]/api/test
Chapter 4: CI/CD 파이프라인
4.1 CI/CD와 테스트 코드의 관계

4.2 GitHub Actions 워크플로우 구조
GitHub Actions의 구조:
Workflow (전체 자동화 과정)
├── Job 1: 테스트
│ ├── Step 1: 코드 다운로드
│ ├── Step 2: Java 설치
│ └── Step 3: 테스트 실행
└── Job 2: 배포 (테스트 성공 시에만)
├── Step 1: 도커 빌드
├── Step 2: AWS 업로드
└── Step 3: 서비스 재시작
워크플로우 실행 과정:
1단계: 트리거 감지
- 개발자가 git push origin main 실행
- GitHub이 main 브랜치 변경 감지
- .github/workflows/deploy.yml 파일 실행
2단계: 가상머신 생성
- GitHub이 ubuntu-latest 가상머신 생성
- 완전히 깨끗한 환경에서 시작
3단계: 테스트 Job 실행 (2-5분 소요)
- 코드 다운로드 (checkout)
- Java 17 설치
- 의존성 다운로드
- 테스트 실행 (./gradlew test)
4단계: 배포 Job 실행 (3-10분 소요, 테스트 성공 시에만)
- Docker 이미지 빌드
- AWS에 업로드
- EC2에서 컨테이너 재시작
5단계: 완료 또는 실패
- 성공: 새 버전이 자동 배포됨
- 실패: 즉시 중단, 오류 알림 발송
4.3 GitHub Actions 설정 실습
파일 구조:
프로젝트 루트/
├── .github/
│ └── workflows/
│ └── deploy.yml
├── Dockerfile
├── docker-compose.yml
└── src/
deploy.yml 작성:
name: Spring Boot CI/CD
on:
push:
branches: [main] # main 브랜치 푸시 시 실행
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Run tests
run: ./gradlew test
- name: Build application
run: ./gradlew build
deploy:
needs: test # 테스트 성공 후에만 실행
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Build application
run: ./gradlew build
- name: Build Docker image
run: docker build -t ${{ secrets.DOCKER_USERNAME }}/my-app:latest .
- name: Login to Docker Hub
run: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
- name: Push Docker image
run: docker push ${{ secrets.DOCKER_USERNAME }}/my-app:latest
- name: Deploy to EC2
uses: appleboy/ssh-action@v0.1.5
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
script: |
docker pull ${{ secrets.DOCKER_USERNAME }}/my-app:latest
docker stop my-app || true
docker rm my-app || true
docker run -d --name my-app -p 80:8080 \
-e SPRING_DATASOURCE_URL=${{ secrets.DB_URL }} \
-e SPRING_DATASOURCE_USERNAME=${{ secrets.DB_USERNAME }} \
-e SPRING_DATASOURCE_PASSWORD=${{ secrets.DB_PASSWORD }} \
${{ secrets.DOCKER_USERNAME }}/my-app:latest
GitHub Secrets 설정: Repository Settings → Secrets and variables → Actions에서 다음 값들 설정:
DOCKER_USERNAME: Docker Hub 사용자명
DOCKER_PASSWORD: Docker Hub 비밀번호
EC2_HOST: EC2 퍼블릭 IP
EC2_USER: ec2-user
EC2_PRIVATE_KEY: .pem 파일 내용 전체
DB_URL: jdbc:mysql://[RDS-ENDPOINT]:3306/myapp
DB_USERNAME: appuser
DB_PASSWORD: apppassword
Chapter 5: 종합 실습 가이드
5.1 단계별 체크리스트
Phase 1: 로컬 개발 완료
- Spring Boot 애플리케이션 개발 완료
- 로컬에서 정상 실행 확인 (localhost:8080)
- 테스트 코드 작성 및 통과
- 데이터베이스 연동 확인
- ./gradlew build 성공
Phase 2: 컨테이너화
- Dockerfile 작성
- docker build -t my-app . 성공
- docker run으로 로컬 테스트
- docker-compose.yml 작성 (선택)
- 컨테이너 환경에서 정상 동작 확인
Phase 3: AWS 인프라 구축
- AWS 계정 생성
- EC2 인스턴스 생성 (t3.micro, Ubuntu)
- 보안 그룹 설정 (SSH, HTTP, 8080)
- 탄력적 IP 할당
- RDS MySQL 인스턴스 생성
- S3 버킷 생성
- IAM 사용자 및 정책 설정
Phase 4: 수동 배포 테스트
- EC2에 Docker 설치
- 로컬 이미지를 Docker Hub에 푸시
- EC2에서 이미지 다운로드
- 컨테이너 실행 및 포트 연결
- 외부에서 접속 테스트
- RDS 연결 확인
- Health Check API 구현
Phase 5: CI/CD 자동화
- .github/workflows/deploy.yml 생성
- GitHub Repository Secrets 설정
- 워크플로우 테스트 (테스트 → 빌드 → 배포)
- 푸시 시 자동 배포 확인
Phase 6: 운영 준비 완료
- 도메인 연결 (선택)
- HTTPS 인증서 설정 (선택)
- 모니터링 설정
- 로그 관리 시스템
- 성능 테스트
- 서비스 런칭!
5.2 자주 발생하는 문제와 해결책
Docker 관련 문제:
문제: "컨테이너가 즉시 종료됨" 해결:
- docker logs [컨테이너명]으로 로그 확인
- 보통 JAR 파일 경로 오류: COPY build/libs/*.jar app.jar 경로 재확인
- 포트 충돌: docker run -p 8081:8080 다른 포트 시도
AWS 연결 문제:
문제: "EC2에 SSH 접속 안됨" 해결:
- 보안 그룹에서 SSH(22) 포트 오픈 확인
- pem 키 파일 권한: chmod 400 key.pem
- 올바른 사용자명 사용: ssh -i key.pem ec2-user@퍼블릭IP
데이터베이스 연결 문제:
문제: "RDS 연결 실패" 해결:
- RDS 보안 그룹에서 MySQL(3306) 포트 오픈
- Source를 EC2 보안 그룹 ID로 설정
- application.yml에서 RDS 엔드포인트 URL 확인
네트워크 문제:
문제: "외부에서 접속 안됨" 해결:
- 보안 그룹에서 HTTP(80), Custom(8080) 포트 오픈
- Source: 0.0.0.0/0 (전체 허용)
- EC2 내부에서 curl localhost:8080 테스트 먼저
5.3 핵심 명령어 요약
로컬 개발:
# 빌드 및 테스트
./gradlew clean build test
# Docker 이미지 빌드 및 실행
docker build -t my-app .
docker run -p 8080:8080 my-app
Docker Hub 업로드:
# 태그 및 푸시
docker tag my-app username/my-app:latest
docker push username/my-app:latest
AWS 배포:
# EC2 접속
ssh -i key.pem ec2-user@server-ip
# 배포
docker pull username/my-app:latest
docker stop my-app || true
docker rm my-app || true
docker run -d --name my-app -p 80:8080 username/my-app:latest
디버깅 명령어:
# Docker 디버깅
docker logs my-app
docker exec -it my-app bash
docker ps -a
# 네트워크 디버깅
curl localhost:8080/health
netstat -tlnp | grep 8080
ping rds-endpoint
# 시스템 모니터링
top
free -h
df -h
'Docker + CI,CD' 카테고리의 다른 글
| AWS 배포 순서 (파일 & 작업 순서) (0) | 2025.06.23 |
|---|