프로덕션 수준 CI/CD 파이프라인 구축: Terraform, GitHub Actions, Ansible을 활용한 인프라 자동화

발행: (2025년 12월 10일 오전 06:15 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

프로젝트 목표 및 개요

이 프로젝트의 주요 목표는 다중 서비스 TODO 애플리케이션에 대해 완전 자동화된 배포 파이프라인을 구축하고 인프라 자동화를 전면에 구현하는 것이었습니다. 솔루션은 다음과 같은 핵심 요구 사항을 충족해야 했습니다:

핵심 요구 사항

  • 코드 커밋부터 프로덕션 배포까지 전 과정 자동화
  • 선언적 구성을 이용한 인프라 프로비저닝
  • 일관된 서버 설정을 위한 자동화된 구성 관리
  • SSL/TLS 종료를 통한 무중단 배포
  • 인프라 일관성을 유지하기 위한 드리프트 감지
  • 마이크로서비스 간 상호작용 디버깅을 위한 분산 추적
  • 암호화된 비밀 관리와 최소 공격 표면을 갖춘 보안 우선 접근 방식

선택된 기술 스택

  • Infrastructure as Code: AWS 리소스 프로비저닝을 위한 Terraform
  • Configuration Management: 서버 구성 및 애플리케이션 배포를 위한 Ansible
  • CI/CD Orchestration: 워크플로 자동화를 위한 GitHub Actions
  • Containerization: 서비스 격리를 위한 Docker 및 Docker Compose
  • Reverse Proxy: 라우팅, 로드 밸런싱 및 자동 SSL을 위한 Traefik
  • Observability: 분산 요청 추적을 위한 Zipkin
  • Message Queue: 비동기 로그 처리를 위한 Redis

최종 목표는 인프라 변경 및 애플리케이션 업데이트를 git push 하나로 배포할 수 있는 시스템을 구축하는 것이었으며, 드리프트 감지, 이메일 알림, 프로덕션 환경에 대한 수동 승인 게이트와 같은 안전 메커니즘을 내장했습니다.

시스템 아키텍처 및 설계

애플리케이션은 각각의 책임에 가장 적합한 언어로 구현된 7개의 독립 서비스로 구성된 마이크로서비스 패턴을 따릅니다.

아키텍처 다이어그램

Microservice Architecture

그림 1: 모든 서비스, 기술 스택 및 데이터 흐름을 보여주는 전체 마이크로서비스 아키텍처.

서비스 책임

#서비스언어 / 플랫폼주요 책임
1프론트엔드 서비스Vue.jsSPA UI, 백엔드 API와 통신, Zipkin 클라이언트, 정적 자산 제공
2Auth APIGo인증 및 인가, JWT 처리, Users API를 통한 자격 증명 검증
3Todos APINode.jsTODO 항목 CRUD, Redis에 이벤트 발행, JWT 검증
4Users APISpring Boot (Java)사용자 프로필 관리, Auth 서비스용 읽기 전용 조회
5로그 메시지 프로세서PythonRedis 메시지 소비, 모니터링을 위한 이벤트 로그
6Redis인메모리 메시지 큐 (pub/sub)
7Zipkin분산 추적 수집기 및 UI
8Traefik리버스 프록시, 자동 서비스 디스커버리, Let’s Encrypt SSL, 라우팅 및 대시보드

네트워크 아키텍처

Docker Network Architecture

그림 2: Traefik만 외부 게이트웨이로 두고 app-network라는 격리된 Docker 네트워크를 보여줌.

모든 서비스는 app-network라는 전용 Docker 브리지 네트워크를 통해 통신하며, 다음을 제공합니다:

  • 호스트 시스템으로부터 격리
  • 컨테이너 이름(DNS)을 통한 서비스 간 통신
  • Traefik을 통한 포트만 외부에 노출
  • 외부 클라이언트와 Traefik 사이의 트래픽 암호화

Terraform을 이용한 인프라스트럭처 코드

Terraform은 선언적 구성, 상태 관리 및 성숙한 AWS 프로바이더 덕분에 선택되었습니다.

프로비저닝된 AWS 리소스

1. EC2 인스턴스

# Data source ensures we always use the latest Ubuntu AMI
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"] # Canonical's AWS account

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

# EC2 Instance resource definition
resource "aws_instance" "todo_app" {
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = var.instance_type
  key_name               = aws_key_pair.deployer.key_name
  vpc_security_group_ids = [aws_security_group.todo_app.id]

  tags = {
    Name        = "todo-app-server-v2"
    Environment = "production"
    Project     = "hngi13-stage6"
  }
}

2. 보안 그룹

resource "aws_security_group" "todo_app" {
  name        = "todo-app-sg"
  description = "Security group for TODO application"

  # HTTP access for initial Let's Encrypt challenges
  ingress {
    description = "HTTP"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # HTTPS access
  ingress {
    description = "HTTPS"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # SSH access (restricted)
  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["YOUR.TRUSTED.IP/32"]
  }

  # Allow all outbound traffic
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
Back to Blog

관련 글

더 보기 »

DevOps란 무엇인가?

소개 온라인에서 “What is DevOps?”를 검색하면 복잡한 정의가 많이 나옵니다. 이 기사에서는 DevOps를 기초부터 설명합니다. DevOps = De...