📌 한 줄 요약
Terraform으로 AWS EC2 인스턴스를 코드 한 줄로 프로비저닝하고, GitHub Actions CD 파이프라인과 연결해 develop 브랜치 push만으로 전체 마이크로서비스가 EC2에 자동 배포되도록 구성하는 데 성공했다.
🗺️ 전체 인프라 구성
[개발자 로컬]
│
├─ terraform apply
│ ↓
│ [AWS ap-northeast-2 (서울)]
│ ├── EC2 t3.xlarge (Ubuntu 22.04 LTS)
│ │ └── Docker + Docker Compose 자동 설치
│ ├── Security Group (22, 80, 443, 8080 개방)
│ ├── Key Pair (RSA 4096, omc-key.pem 로컬 저장)
│ └── Elastic IP (고정 IP → GitHub Secrets에 등록)
│
└─ git push origin develop
↓
[GitHub Actions CD]
├── 1. Gradle 빌드 (Java 21)
├── 2. Docker 이미지 빌드 & ghcr.io 푸시 (5개 서비스)
└── 3. SSH → EC2에서 docker compose pull & up -d
🏗️ STEP 1 — Terraform으로 AWS 인프라 프로비저닝
1-1. Provider & 버전 고정
terraform {
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
tls = { source = "hashicorp/tls", version = "~> 4.0" }
local = { source = "hashicorp/local", version = "~> 2.0" }
}
}
provider "aws" {
region = "ap-northeast-2" # 서울 리전
profile = "terraform-user-my" # ~/.aws/credentials 프로파일
}
포인트: profile을 명시해서 실수로 다른 계정에 배포하는 사고를 방지했다.
AWS 자격증명 저장 위치
Terraform이 AWS에 접근할 때 내 PC의 ~/.aws/credentials 파일을 읽는다.
# ~/.aws/credentials
[terraform-user-my]
aws_access_key_id = AKIA...
aws_secret_access_key = xxxxxx
# ~/.aws/config
[profile terraform-user-my]
region = ap-northeast-2
이 파일은 AWS IAM 콘솔에서 terraform-user-my 사용자의 액세스 키를 발급받아 직접 저장해 둔 것이다. Terraform은 코드에 키를 하드코딩하지 않고 이 로컬 파일을 참조한다.
1-2. SSH Key 자동 생성 — terraform apply 한 번으로 모두 자동 처리
# 1. RSA 4096 키 쌍 생성
resource "tls_private_key" "omc_key" {
algorithm = "RSA"
rsa_bits = 4096
}
# 2. 공개키 → AWS Key Pair 등록
resource "aws_key_pair" "omc_key" {
key_name = "omc-key"
public_key = tls_private_key.omc_key.public_key_openssh
}
# 3. 비밀키 → 로컬 .pem 파일로 저장 (권한 0400)
resource "local_file" "omc_key_pem" {
content = tls_private_key.omc_key.private_key_pem
filename = "${path.module}/omc-key.pem"
file_permission = "0400"
}
terraform apply 명령을 실행하면 아래 과정이 자동으로 실행된다. 키를 직접 만들거나 AWS 콘솔에서 수동 등록할 필요가 없다.
- RSA 4096 키 쌍 생성
- 비밀키 → 내 PC infra/terraform/omc-key.pem으로 자동 저장 (권한 0400)
- 공개키 → AWS Key Pair omc-key로 자동 등록
- 이후 EC2 SSH 접속 시 내 PC의 .pem 파일 사용
`bash
ssh -i ./omc-key.pem ubuntu@<Elastic IP>
`
.pem 파일과 terraform.tfstate(비밀키가 평문으로 포함됨)는 절대 git 커밋 금지. 이미 .gitignore에 추가되어 있다.
1-3. Security Group
포트 용도
| 22 | SSH 접속 |
| 8080 | Spring Cloud Gateway API |
| 80 | HTTP (Nginx 대비) |
| 443 | HTTPS (Nginx 대비) |
모든 outbound는 전체 허용 (0.0.0.0/0).
1-4. EC2 인스턴스
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical 공식 계정
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
resource "aws_instance" "omc" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.xlarge" # vCPU 4, RAM 16GB
key_name = aws_key_pair.omc_key.key_name
root_block_device {
volume_size = 30 # GB
}
user_data = <<-EOF
#!/bin/bash
# Docker CE 공식 설치 스크립트
apt-get update && apt-get install -y ca-certificates curl gnupg
# ... (Docker 저장소 추가 → apt install)
usermod -aG docker ubuntu
systemctl enable docker && systemctl start docker
EOF
}
포인트: user_data로 EC2 최초 부팅 시 Docker를 자동 설치한다. 인스턴스에 직접 SSH해서 수동 설치할 필요가 없다.
data source 활용: AMI ID를 하드코딩하지 않고 data.aws_ami로 항상 최신 Ubuntu 22.04 LTS AMI를 동적으로 조회한다. 리전마다 AMI ID가 다르기 때문에 이 방식이 이식성이 높다.
1-5. Elastic IP
resource "aws_eip" "omc_eip" {
instance = aws_instance.omc.id
domain = "vpc"
}
output "elastic_ip" {
value = aws_eip.omc_eip.public_ip
}
이유: EC2를 중지/시작하면 Public IP가 바뀐다. Elastic IP를 붙이면 고정 IP가 유지되어 GitHub Secrets의 EC2_HOST를 수정할 필요가 없다.
1-6. terraform apply 실행 순서
# 1. 초기화 (프로바이더 다운로드)
terraform init
# 2. 변경 사항 미리보기
terraform plan
# 3. 실제 프로비저닝
terraform apply
# 완료 후 출력 예시:
# elastic_ip = "x.x.x.x"
# ssh_command = "ssh -i omc-key.pem ubuntu@x.x.x.x"
🐳 STEP 2 — 배포 대상 서비스 구성 (docker-compose.prod.yml)
구분 서비스 이미지 출처 메모리 제한
| 인프라 | PostgreSQL 18 | Docker Hub | 512 MB |
| 인프라 | Redis 7.2 | Docker Hub | 256 MB |
| 인프라 | Zookeeper 7.5.0 | Confluent | 256 MB |
| 인프라 | Kafka 7.5.0 | Confluent | 512 MB |
| 인프라 | Keycloak 26.2 | Quay.io | 1 GB |
| 앱 | eureka-server | ghcr.io | 512 MB |
| 앱 | config-server | ghcr.io | 512 MB |
| 앱 | gateway (8080) | ghcr.io | 512 MB |
| 앱 | user-service | ghcr.io | 512 MB |
| 앱 | drop-service | ghcr.io | 512 MB |
healthcheck 기반 의존성 체인:
postgres healthy → keycloak healthy
postgres healthy → user-service, drop-service
eureka-server healthy → config-server healthy
config-server healthy → gateway, user-service, drop-service
keycloak healthy → gateway, user-service
⚙️ STEP 3 — GitHub Actions CD 파이프라인
트리거
on:
push:
branches:
- develop
develop 브랜치에 push하면 자동으로 CD가 실행된다.
Job 1: Build & Push Images
1. Checkout 코드
2. Java 21 (Temurin) 세팅
3. Gradle 빌드 (5개 서비스 bootJar, 테스트 제외)
- eureka-server
- config-server
- gateway
- user-service
- drop-service
4. ghcr.io 로그인 (GITHUB_TOKEN 사용)
5. 각 서비스 Docker 이미지 빌드 & 푸시
→ ghcr.io/wansix/omc-{서비스명}:latest
Job 2: Deploy to EC2
1. SSH로 EC2 접속 (appleboy/ssh-action)
- host : $EC2_HOST (Elastic IP)
- key : $EC2_SSH_KEY (omc-key.pem 내용)
2. EC2에서 실행:
cd ~/omc
docker login ghcr.io # 프라이빗 이미지 pull 권한
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
docker image prune -f # 미사용 이미지 정리
GitHub Secrets 목록
시크릿 키 값
| EC2_HOST | Terraform output의 Elastic IP |
| EC2_USER | ubuntu |
| EC2_SSH_KEY | omc-key.pem 파일 내용 (-----BEGIN RSA...) |
💡 핵심 배운 점
- IaC(Infrastructure as Code)의 가치: 인프라 설정이 코드로 관리되므로 팀원 누구나 terraform apply 한 번으로 동일한 환경을 재현할 수 있다. 수동 콘솔 클릭 작업이 사라진다.
- tls 프로바이더로 키 관리 자동화: 수동으로 AWS 콘솔에서 Key Pair를 만들고 다운로드하는 번거로운 과정이 없어졌다. Terraform 상태 파일(terraform.tfstate)에 비밀키가 저장되므로 .gitignore에 추가해 절대 커밋하면 안 된다.
- data source로 AMI 동적 조회: AMI ID는 리전마다 다르고 주기적으로 업데이트된다. data.aws_ami를 쓰면 항상 최신 공식 이미지를 사용할 수 있다.
- user_data로 부트스트랩 자동화: EC2 생성과 동시에 Docker 설치까지 완료된다. 별도 Ansible, 수동 SSH 설정 없이도 바로 컨테이너를 실행할 수 있는 상태가 된다.
- Elastic IP의 필요성: 재시작할 때마다 IP가 바뀌면 GitHub Secrets를 매번 수정해야 한다. Elastic IP로 고정하는 것이 CI/CD 운영의 기본이다.
- healthcheck 기반 의존성 설정: depends_on: condition: service_healthy를 설정하지 않으면, 예컨대 Keycloak이 아직 뜨지 않았는데 Gateway가 먼저 시작되어 연결 실패 에러가 발생한다. 특히 Keycloak처럼 초기 기동이 오래 걸리는 서비스는 start_period를 넉넉히(90초) 주는 것이 중요하다.