Projects

MovieSir

AI 기반 영화 추천 B2B API 서비스의 인프라 설계 및 운영

2025.12 - 2026.025명인프라 · CI/CD · 보안
KakaoCloudDockerGitHub ActionsNginxPostgreSQLRedisLet's Encryptfail2ban

SBERT + ALS 하이브리드 알고리즘 기반의 영화 추천 서비스. 기업 고객이 B2B Console에서 API Key를 발급받아 자사 서비스에 연동하는 구조로, External API 분리 설계와 Rate Limiting 기반 플랜별 과금 체계를 구축했습니다. 카카오클라우드 2-Tier VPC 아키텍처 설계, 4계층 보안 방어 체계, CI/CD 파이프라인 자동화, Cron 기반 서버 운영 자동화를 담당했습니다.


핵심 성과

5,227 → 0

SSH 공격 차단

A+

SSL Labs 등급

4개 도메인

SSL 단일 서버 운영


인프라 아키텍처

MovieSir 인프라 아키텍처

KakaoCloud VPC10.0.0.0/16

서버Subnet서비스 (포트)

App Server

Bastion Host

Public

10.0.0.0/20

보안그룹

프로토콜출발지포트설명
TCP0.0.0.0/080HTTP
TCP0.0.0.0/0443HTTPS
TCP0.0.0.0/052222SSH

GPU Server

Tesla T4 16GB

Private

10.0.32.0/20

보안그룹

프로토콜출발지포트설명
TCP10.0.1.117/325432PostgreSQL
TCP10.0.1.117/328001AI Service
ICMP10.0.1.117/32ALLPing
TCP10.0.1.117/3222SSH

문제 해결 1: SSH 브루트포스 공격 대응

01문제 발생

서버 운영 2주 만에 lastb 1,107건 SSH 로그인 실패 기록 발견. 자동화된 봇의 브루트포스 공격.

lastb 명령어 실행 결과

02해결 과정

자동 차단

fail2ban 도입 - 3회 실패 시 IP 자동 차단

수동 IP 차단은 비효율적. 로그 기반 자동 차단으로 운영 부담 최소화

포트 은닉

SSH 포트 변경 (22 → 52222)

봇은 기본 포트 22만 스캔. 비표준 포트로 90% 이상 자동 공격 회피

다층 방어

Defense in Depth - UFW + 보안그룹 이중 방화벽

단일 방화벽 설정 실수 시에도 2차 방어선 유지

네트워크 격리

GPU Server Private Subnet 배치 + Public IP 제거

Bastion Host (SSH ProxyJump) 구성

DB/AI 모델 보호. App Server 경유만 허용

보안 아키텍처 변경 전후 비교

03결과

서버대응 전대응 후
App Server1,107건침투 0건 (299 IP 차단)
GPU Server205건공격 시도 0건
GPU Server - Public IP 제거 후

GPU Server - 신규 공격 0건

App Server - 52222 탐지됐으나 차단

App Server - 포트 탐지됐으나 침투 차단


문제 해결 2: 서버 경량화 및 자동화

01문제 발생

App Server 스토리지 10GB SSD. Docker 이미지 1.2GB, 시스템 로그 427MB 누적.

App Server 디스크 사용량

02해결 과정

이미지 경량화

Docker 멀티스테이지 빌드

빌드 환경(node_modules)과 실행 환경 분리. 최종 이미지에 빌드 도구 미포함

런타임 제거

Nginx 정적 서빙으로 전환

SPA는 빌드 후 정적 파일만 필요. Node 런타임 제거로 리소스 절약

자동 정리

Cron 자동화 스크립트 (매주 금요일)

수동 정리는 잊기 쉬움. 주간 스케줄로 디스크 풀 사전 예방

# 매주 금요일 자동 정리
0 17 * * 5 /home/ubuntu/scripts/weekly-cleanup.sh

# journalctl 7일 이전 로그 삭제 + Docker prune

03결과

항목BeforeAfter개선
Docker 이미지1.2GB95MB92% 감소
시스템 로그427MB16MB96% 감소
Weekly Cleanup 실행 로그

freed 0B = 자동화 성공. 주간 정기 실행으로 쌓이기 전에 미리 정리됨.


문제 해결 3: CI/CD 파이프라인 구축

01문제 발생

수동 배포 시 휴먼 에러 발생. GPU 서버가 Private Subnet이라 GitHub Actions에서 직접 접근 불가.

GitHub Actions 배포 실패 - Private Subnet 접근 불가

02해결 과정

워크플로우 분리

Path 기반 트리거 (frontend/backend/ai)

폴더별 독립 배포. 불필요한 빌드 방지

Private 서버 배포

SSH ProxyJump 설정

App Server를 점프 호스트로 활용. GPU 서버 Public IP 없이 배포

시크릿 관리

GitHub Secrets로 환경변수 분리

SSH 키, DB 비밀번호 등 민감 정보. 코드에 하드코딩 제거

CI/CD 파이프라인 구조

03결과

deploy-frontend.yml

frontend/** 변경 → npm build → rsync → /var/www/

deploy-backend.yml

backend/** 변경 → SSH → docker compose up -d --build

deploy-gpu.yml

ai/** 변경 → SSH (ProxyJump) → GPU 서버 배포

dev/main push만으로 자동 배포. 수동 배포 대비 배포 시간 단축, 휴먼 에러 제거.

Before - 배포 시간 10분대

Before - 10m 49s

After - 배포 시간 2분 미만

After - 1m 43s (83% 단축)


문제 해결 4: Nginx SSL/HTTPS 보안 설정

01문제 발생

4개 도메인(Landing, Demo, Console, API)을 단일 서버에서 HTTPS로 서빙해야 하는 상황. HTTP 평문 통신으로 인한 보안 취약점과, HTTPS 전환 시 SPA 라우팅 404, CORS 중복 헤더 등 복합적인 문제 발생.

02해결 과정

SSL 인증서

Let's Encrypt 와일드카드 인증서로 4개 도메인 통합 관리

도메인별 개별 인증서 대신 와일드카드로 갱신 포인트 단일화. systemd timer 자동 갱신

TLS 강화

TLS 1.2/1.3만 허용, 구버전 프로토콜 차단

HSTS max-age 2년 설정으로 브라우저 레벨에서 HTTPS 강제

보안 헤더

X-Content-Type-Options, X-Frame-Options, Referrer-Policy 적용

MIME 스니핑, 클릭재킹, 정보 유출 방지. Nginx add_header 상속 이슈 해결

리버스 프록시

도메인별 라우팅 분리 (정적 서빙 + API 프록시)

SPA try_files 폴백, 정적 파일 1년 캐시 + index.html no-cache 전략

03결과

SSL Labs A+ 등급 달성

SSL Labs A+ 등급

SSL Labs 상세 결과

TLS 1.3, HSTS 적용 확인

moviesir.cloud 랜딩 페이지

moviesir.cloud - Landing Page

demo.moviesir.cloud Demo App

demo.moviesir.cloud - Demo App

console.moviesir.cloud B2B Console

console.moviesir.cloud - B2B Console

api.moviesir.cloud External API

api.moviesir.cloud - External API

항목설정
SSL Labs 등급A+
TLS 프로토콜1.2 / 1.3
HSTSmax-age=63072000 (2년)
도메인4개 서브도메인 단일 서버 운영
인증서 갱신자동 (systemd timer)

회고

배운 점

서버 2대라는 제한된 자원으로 VPC 설계부터 CI/CD 자동화까지 프로덕션 수준의 인프라를 직접 구축했다. 제약 조건 안에서 최적의 아키텍처를 설계하는 과정에서, 클라우드 인프라는 "완성"이 아니라 "운영하며 개선하는 것"이라는 걸 체감했다.

SSH 브루트포스 5,227건을 탐지하고 단계적으로 대응하면서, 보안은 한 번의 설정이 아니라 계층별로 쌓아야 한다는 걸 배웠다. fail2ban → 포트 변경 → 이중 방화벽 → Private Subnet 격리까지 Defense in Depth 전략을 실전에서 경험했다.

Cron 자동화와 SSL 설정을 직접 하면서, 수동 작업을 반복하기보다 자동화 스크립트로 만드는 습관이 운영 안정성에 직결된다는 걸 느꼈다.

개선하고 싶은 점

현재 서버 상태를 수동으로 확인하고 있는데, Prometheus + Grafana 기반 모니터링을 도입하여 CPU, 메모리, 디스크 사용량을 시각화하고 임계치 알림을 자동화하고 싶다.

Docker Compose 기반 배포를 Kubernetes로 전환하여 오토스케일링과 무중단 배포를 경험해보고 싶다.

Terraform으로 현재 수동 구성한 카카오클라우드 인프라를 코드로 관리(IaC)하여, 환경 재현성과 변경 이력 추적을 개선하고 싶다.


관련 글