현대오토에버 모빌리티 SW스쿨 클라우드 3기 팀 프로젝트 CarPayIn의 일부입니다. CarPayIn은 차량에서 QR 인증만으로 로그인하고, 입차부터 요금 결제까지 사람의 개입 없이 자동으로 처리되는 차량 기반 주차 결제 시스템입니다. AWS(퍼블릭 클라우드)와 OpenStack(프라이빗 클라우드)로 구성된 하이브리드 클라우드 구조이며, 이 레포는 그중 Private Cloud 인프라 파트를 담당합니다. AWS 쪽 인프라(ECS, Lambda, ALB 등)는 별도 레포/팀원이 담당한 영역이라 이 레포에는 포함되어 있지 않습니다.
팀 프로젝트이며, 아래는 본인(June, 김준태)이 Private Cloud 인프라 / DevOps / Monitoring 담당자로서
직접 작업한 범위입니다. 서비스 코드(services/*)는 팀 공동
작업물이고, 아래 인프라·운영 영역이 본인 담당입니다.
- Private Cloud 구축: Kolla-Ansible로 OpenStack(2024.2) 설치, Wi-Fi 단일 NIC 제약 환경에서
dummy0가상 인터페이스로 External Network 우회 구성 - 네트워크 설계: 외부 트래픽(
public-net) / DB(private-net) / Vault(secure-net)로 3계층 분리, k3s 노드는 멀티홈 구조로 3개 네트워크에 모두 연결 - k3s 멀티노드 & 로드밸런싱: Octavia(L4, VIP 단일 진입점)와 Traefik(L7)을 이중화해 노드 장애 시에도 트래픽이 우회되도록 구성, HPA로 파드를 6~9개 사이로 자동 확장
- 하이브리드 클라우드 연결: WireGuard로 AWS ↔ OpenStack 간 VPN 터널 구성,
PersistentKeepalive와 iptables DNAT로 NAT 환경에서도 안정적인 연결 유지. VPN 터널로 들어온 트래픽은 Octavia LB VIP (10.0.1.110)로 DNAT되어 mock-card Pod까지 전달 - Self-hosted GitLab + IaC: OpenStack 내부에 GitLab을 직접 구축해 SCM/CI·CD/Registry/Terraform State를
단일 플랫폼으로 통합. Terraform(프로비저닝)과 Ansible(설정/스케일링)로 역할을 분리하고,
terraform apply는 수동 승인 게이트로 사고를 방지 - 모니터링 & 오토스케일링: Prometheus + AlertManager가 CPU 80% 초과 상태를 감지하면(최초 5분 지속 → 이후 1분으로 단축) FastAPI 기반 webhook-adapter를 거쳐 GitLab CI 파이프라인을 트리거하고, Ansible이 VM을 자동으로 추가/제거해 k3s 클러스터에 join/drain. scale-out/in에는 각각 10분/5분 쿨다운을 둬 중복 트리거를 방지
| 이름 | 역할 |
|---|---|
| 조성윤 | PL |
| 김희라 | OpenStack 인프라 |
| 이예은 | AWS 인프라 |
| 조형동 | Mock Card 서비스 / DB |
| June (본인) | Private Cloud 인프라 / DevOps / Monitoring |
flowchart LR
subgraph AWS["AWS · 퍼블릭 클라우드 (팀원 담당, 이 레포 범위 아님)"]
AAOS["AAOS 시뮬레이터"] --> GW["API Gateway / ALB"]
end
GW -- "WireGuard VPN\nPersistentKeepalive" --> VPNSRV
subgraph OS["OpenStack 2024.2 · 프라이빗 클라우드 (본인 담당)"]
VPNSRV["vpn-server\npublic-net"] -- "iptables DNAT" --> OCT["Octavia LB\nVIP · L4 ROUND_ROBIN"]
OCT --> TRAEFIK["Traefik Ingress\nk3s · L7 라우팅"]
TRAEFIK --> MC["mock-card Pod × 6~9\n(HPA, CPU 70%)"]
MC --> PG[("PostgreSQL\nprivate-net")]
MC -. "AppRole 인증" .-> VAULT[["Vault\nsecure-net"]]
end
범위: Private Cloud(OpenStack/k3s/Octavia)에서 실제로 운영되는 서비스는
mock-card하나입니다.carpayin-backend/mock-pg/pms는 AWS 쪽에 배포됩니다.docs/deployment/gitlab-registry.md가 4개 서비스 이미지를 모두 언급하는 것은 SaaS GitLab에서 Self-hosted GitLab으로 분리하기 이전 구성의 잔재이며, 현재 기준으로는 유효하지 않습니다.
- Terraform: OpenStack VM, 네트워크 포트, Octavia 로드밸런서 프로비저닝 (
infra/terraform) - Ansible: k3s 설치/조인, WireGuard 설정, 모니터링 에이전트 배포, 오토스케일링 시 노드 추가/제거
(
infra/ansible) - webhook-adapter: AlertManager → GitLab CI 트리거 사이의 변환/쿨다운 레이어 (
infra/webhook-adapter)
| 영역 | 기술 |
|---|---|
| Private Cloud | OpenStack 2024.2 (Kolla-Ansible), Neutron(OVS), Cinder, Octavia |
| Orchestration | k3s (멀티노드), Traefik Ingress, HPA |
| IaC | Terraform, Ansible |
| CI/CD | Self-hosted GitLab (SCM/CI·CD/Registry/Terraform State) |
| 보안 | HashiCorp Vault (AppRole), WireGuard, 3계층 네트워크 분리 |
| 모니터링 | Prometheus, Grafana, AlertManager |
| 백엔드 | Python (FastAPI), SQLAlchemy, PostgreSQL, Redis |
| 클라이언트 | Android (Kotlin) |
- 단일 NIC 환경에서 OpenStack Neutron 구성: Wi-Fi 단일 NIC만 있는 환경에서 물리 NIC은 내부 통신용으로
유지하고, 가상 인터페이스
dummy0을 만들어 OVS가 관리하는 external network로 사용해 문제를 우회했습니다. 배포 후에도br-exIP 미할당·Flow Table 오염·bridge-nf-call-iptables간섭이 겹친 SSH 장애를 하나씩 추적해 해결했습니다. 자세히 보기 → - Octavia LB가 계속 OFFLINE에 머무는 문제: Amphora 이미지가 잘못된 프로젝트에 올라가 있던 문제, Redis
미배포로
octavia_worker가 크래시 루프를 돌던 문제, LB가PENDING_CREATE에 멈춰 MariaDB에서provisioning_status를 직접ERROR로 바꿔 정리해야 했던 문제를 하나씩 해결한 뒤에도 LB가 OFFLINE에서 벗어나지 않았습니다. 원인은octavia_network_interface가 기본값(Wi-Fi NIC)으로 잡혀 있어 Health Manager가 amphora와 heartbeat(포트 5555)를 주고받는 전용 인터페이스o-hm0(lb-mgmt-net,10.1.0.242)를 못 찾고 있던 것 —/etc/kolla/globals.yml에octavia_network_interface: "o-hm0"을 지정하고kolla-ansible reconfigure --tags octavia,openstack loadbalancer failover로 amphora를 교체해 멤버 3대(10.0.1.81/82/83)가 ONLINE으로 올라오고 VIP(10.0.1.110) 헬스체크(/health)가 정상 응답하는 것까지 확인했습니다. 자세히 보기 → - Terraform이 유발한 VM 삭제 사고: 네트워크 블록을 수정하면 Terraform이 이를
replace로 판단해 VM을 삭제 후 재생성한다는 점을 모른 채plan확인 없이apply를 실행해 GitLab VM이 삭제될 뻔했습니다 (마침 그 VM 위에서 Runner가 돌고 있어 작업이 중단되며 부팅 볼륨 삭제까지는 가지 않았습니다). 이후 부팅 볼륨delete_on_termination = false고정, 주요 변경 전 스냅샷/state pull백업,apply수동 승인 게이트를 표준 절차로 만들었습니다. 자세히 보기 → - Vault AppRole 기반 배포 시크릿 발급: 애플리케이션이 요청마다 Vault를 호출하지 않도록, GitLab CI가
배포 시점에만 AppRole로 로그인해 일회용
secret_id를 조회하고 환경변수로 주입하는 방식을 채택했습니다. 자세히 보기 →
- 1편 — 프로젝트 개요 & 아키텍처 설계
- 2편 — Kolla-Ansible로 OpenStack 구축
- 3편 — OpenStack 단일 NIC 문제 해결
- 4편 — 3계층 네트워크 설계 & k3s 멀티노드 구축
- 5편 — Octavia 설정
- 6편 — HashiCorp Vault & PostgreSQL 보안 설계
- 7편 — Terraform & GitLab CI IaC 파이프라인
- 8편 — Prometheus/AlertManager 오토스케일링 자동화
- 외전 — 컨벤션 기반 팀 AI 협업 & TDD 자동 디버깅
services/
android-app/ 차량 내/로컬 테스트용 Android 클라이언트
carpayin-backend/ 인증/차량/카드/주차/결제 메인 API
mock-card/ 카드사 모방 API (k3s/Octavia 배포 대상)
mock-pg/ PG사 모방 API + 카드 등록 WebView
pms/ 주차 관제 시스템(PMS) 모방 API
infra/ (아래 전체 본인 담당)
terraform/ OpenStack VM / 네트워크 / Octavia LB 프로비저닝
main.tf, variables.tf, outputs.tf, README.md(state import 가이드)
ansible/ k3s, WireGuard, 모니터링 에이전트, 오토스케일링
site.yml, inventory/hosts.yml
roles/{k3s_agent, k3s_monitoring, node_exporter, wireguard}
playbooks/{scale-in, scale-out}.yml
webhook-adapter/ AlertManager → GitLab CI 트리거 어댑터 (FastAPI)
docs/
api/ OpenAPI 명세
DB 스키마/ DB, Redis 키 설계 문서
deployment/ 레지스트리/CI-CD/배포 문서
diagrams/ 시퀀스 다이어그램 (Mermaid)
use-cases/ 유스케이스 명세
시나리오/ 시나리오 흐름 문서
scripts/
build-push-images.ps1 로컬 GitLab Registry 이미지 빌드/푸시
deploy-from-registry.ps1 레지스트리 이미지 pull 후 Docker Compose 실행
start-local-e2e.ps1 로컬 E2E 의존 서비스 기동
stop-local-e2e.ps1 로컬 E2E 의존 서비스 종료
.env.example을 .env로 복사하고 실제 Hyundai OAuth 값을 채워 넣습니다.
PUBLIC_BASE_URL
HYUNDAI_CLIENT_ID
HYUNDAI_CLIENT_SECRET
PG_PUBLIC_BASE_URL
.env나 실제 시크릿은 커밋하지 않습니다.
docker compose up -d --build| 포트 | 서비스 |
|---|---|
| 8000 | carpayin-backend |
| 8001 | pms |
| 8002 | mock-pg |
| 8003 | mock-card |
| 5432–5435 | 서비스별 PostgreSQL |
| 6379 | redis |
cd services/carpayin-backend
python -m pytest tests/unit tests/api -q --import-mode=importlibcd services/android-app
.\gradlew.bat :app:compileDebugKotlinSelf-hosted GitLab(OpenStack 내부, .gitlab-ci.yml)에서 변경된 경로에 따라 파이프라인이 갈라집니다.
| 변경 경로 | 자동 실행 | 수동 승인 필요 |
|---|---|---|
infra/terraform/** |
validate, plan (MR / main) |
apply (main) |
infra/ansible/** |
– | ansible:deploy (main) |
infra/webhook-adapter/** |
– | deploy:webhook-adapter (main, SCP + Docker Compose) |
services/mock-card/** |
test (MR / main), build (main) |
deploy:mock-card (main, kubectl apply + 이미지 롤링) |
- Terraform state는 GitLab HTTP backend로 관리하며(
-backend-config), lock 충돌 재발 방지를 위해apply는 파이프라인 전용 + 수동 승인만 허용합니다. - 오토스케일링은 GitLab Pipeline Trigger token으로 별도 파이프라인(
CI_PIPELINE_SOURCE == "trigger")을 실행해scale:out/scale:injob이 Ansible로 노드를 추가/제거합니다.
docs/deployment/gitlab-registry.md와scripts/build-push-images.ps1은 SaaS GitLab (registry.gitlab.com) 레지스트리로 4개 서비스 이미지를 로컬/수동으로 빌드-푸시하던 절차입니다. Self-hosted GitLab으로 옮기기 전에 쓰던 방식으로, 현재는 위 표의 self-hosted.gitlab-ci.yml파이프라인(10.0.1.162:5050레지스트리)이 기준이며 해당 문서/스크립트는 기록용으로만 남아 있습니다.