Skip to main content

서버 관리 개요

multi-saas-kit은 클라우드 서버를 코드로 관리(IaC) 하는 표준 체계를 제공합니다. Ansible로 OS 프로비저닝/소프트웨어 배포를, Tailscale로 서버 간 보안 네트워크를 구성합니다. IaaS 제공자는 선택 가능 — Vultr, DigitalOcean, Linode, Hetzner, AWS EC2 등 Ubuntu LTS를 제공하는 어떤 클라우드도 동일한 방식으로 동작합니다.

클라우드 중립

이 가이드의 예시에서 Vultr가 등장한다면, 그것은 하나의 참고 예시일 뿐입니다. 각 운영자가 선호하는 IaaS로 자유롭게 교체할 수 있으며, Ansible 코드는 수정 없이 재사용됩니다. 변경되는 것은 inventory/hosts.ini의 IP와 초기 VM 생성 단계뿐입니다.

설계 배경

이 체계의 의사결정 과정과 대안 비교는 ADR-018 Ansible 기반 서버 프로비저닝 채택을 참조하세요.

왜 이 조합인가

도구역할선택 이유
IaaS 제공자VM 인프라운영자 선호 (Vultr, DigitalOcean, Linode, Hetzner, AWS EC2 등). API·cloud-init·root-only 초기 상태 등 공통 요건만 충족하면 가능
AnsibleOS 프로비저닝 + 배포Agentless(SSH만), 선언적, 멱등성, 5~100대 스윗스팟, IaaS 중립
Tailscale서버 간 사내망제로 설정 VPN, ACL 기반 권한, 방화벽 대체
Docker Compose애플리케이션 레이어기존 생태계와 호환, 앱 배포 단위

아키텍처 한눈에

┌──────────────────────────────────────────────────────────────┐
│ 로컬 Control Node (운영자 PC 또는 CI) │
│ workspace/_infra/ansible/ │
│ ├── inventory/ # 서버 목록 │
│ ├── roles/ # 역할별 설정 │
│ ├── playbooks/ # 실행 시나리오 │
│ └── vault/ # 암호화된 Secret │
└──────────────────┬───────────────────────────────────────────┘
│ SSH (Tailscale)

┌──────────────────────────────────────────────────────────────┐
│ IaaS 서버들 (Tailscale Mesh 100.64.0.0/10) │
│ │
│ NPM 서버 ─▶ App 서버 1 ─▶ DB 서버 │
│ │ App 서버 2 │
│ └─▶ DevTools (pgAdmin, RedisInsight, Grafana) │
│ │
│ 모든 서버: Docker + Tailscale + node_exporter + promtail │
└──────────────────────────────────────────────────────────────┘

설계 원칙

원칙의미
선언적"이 상태여야 한다"만 기술 — 절차 X, 결과 O
멱등성같은 Playbook을 반복 실행해도 결과 동일
최소 권한root 직접 로그인 차단, deploy 유저 + sudo
비밀 분리Secret은 ansible-vault로 암호화 저장
역할 분리공통부(common) + 역할별(role) 조합
점진 도입5대 → Ansible 단독 / 10대+ → Terraform 추가

AI 협업과 IaC drift 관리

AI 에이전트(예: Claude Code)로 서버를 운영할 때 가장 큰 위험은 configuration drift — 에이전트나 사람이 호스트에서 직접 apt install·수동 설정을 하면, 그 변경이 플레이북(SSOT)에 없어 서버의 실제 상태와 코드가 어긋나는 현상입니다. drift 가 쌓이면 "새 서버를 코드로 찍어도 기존 서버와 달라져" 재현성과 서버 이전 가능성이 무너집니다.

multi-saas-kit 은 이를 "AI 를 막는다"가 아니라 구조적으로 drift 가 일어나기 어렵게, 일어나도 드러나게 만드는 3가지 방식으로 다룹니다.

전략방법효과
① 변경 표면 축소 (2계층 격리)호스트(OS·Docker·네트워크·드라이버)는 Ansible 이 관리하는 거의 불변 베이스로 유지하고, 변동성 큰 개발 의존성(파이썬/모델 SDK 등)은 전부 컨테이너·uv·pipx 로 격리에이전트가 무엇을 설치하든 컨테이너에서 끝나 호스트가 오염되지 않음
② 호스트 변경은 코드 경유호스트 레벨 변경이 필요하면 직접 설치 대신 ansible role 에 추가 → 적용. AI 지침에 "호스트 직접 apt/pip 금지"를 명문화에이전트의 변경이 곧 SSOT 변경이 되어 drift 가 생기지 않음
③ drift 자동 탐지컨트롤 노드에서 정기적으로 ansible-playbook --check --diff 를 cron 실행 → 차이가 있으면 알림(Discord 등)drift 가 항상 보이는 상태 — 발견되면 코드에 반영(흡수)하거나 되돌림
핵심 발상

"완벽한 관리"는 drift 가 0 인 상태가 아니라 drift 가 항상 드러나는 상태입니다. 변동성을 컨테이너로 몰아넣어 호스트를 단순하게 유지하면, Ansible 이 관리할 표면이 작아져 자동으로 관리가 쉬워집니다. AI 에이전트를 여러 개 동시에 운영할수록 이 격리·탐지 전략이 더 중요해집니다.

재시작 유발 변경 주의

이미 운영 중인 호스트의 설정을 코드와 정합화할 때, Docker daemon.json 처럼 변경 시 데몬 재시작을 유발하는 파일은 무검증 적용이 위험합니다(실행 중 컨테이너 영향). 먼저 --check --diff 로 영향을 확인하고, 실제 상태와 내용이 정확히 일치하도록 변수화(parameterize) 하여 재시작 없이 SSOT 만 맞추는 방식을 권장합니다.

데이터(운영 DB)는 Ansible 의 관리 대상이 아닙니다 — 끊임없이 변하는 가변 상태라 멱등성 개념이 성립하지 않습니다. 서버 이전 시에는 pg_dump → 전송 → 복원 → 검증 절차를 Ansible 로 오케스트레이션할 수 있으나, 데이터 자체의 원천은 어디까지나 백업본과 운영 DB 입니다.

서버 역할 분류

Ansible inventory는 그룹 단위로 서버를 분류합니다.

그룹역할예시 서버
npm_serversNginx Proxy Manager (reverse proxy + SSL)1대
app_serversLaravel SaaS 앱N대 (프로젝트별)
bot_servers메신저 봇 호스트 (사설망/VPN 전용 노출)1~N대
db_serversPostgreSQL1~N대
monitoring_serversGrafana LGTM (로그/메트릭)1대
devtools_serverspgAdmin, RedisInsight 등1대
bot_fleet_managers봇 Fleet 중앙 제어 평면1대
bot_nodes봇 Fleet Agent 설치 대상1~N대

공통 그룹 all — 모든 서버에 기본 role 적용: common / user / hardening / tailscale / docker / node_exporter / promtail

관리 평면과 실행 평면 분리

봇 Fleet 관련 두 그룹은 권한을 일부러 반대로 갖습니다.

중앙 제어 평면 (bot_fleet_managers)Agent (bot_nodes)
Docker socket·저장소❌ 없음✅ 있음 (실제 작업 수행)
외부에서 접속✅ 리버스 프록시 경유❌ 없음 (내보내는 연결만)

관리 UI 가 곧 호스트 권한이 되는 구조를 피하기 위한 배치입니다. 한쪽이 뚫려도 다른 쪽 권한으로 곧장 이어지지 않습니다. 두 역할 모두 기본 비활성이라, 사용하지 않는 서버에는 아무 영향이 없습니다.

단계별 도입 로드맵

Phase시기도입 범위서버 수
1지금Ansible 단독 (정적 inventory), 기본 role 3~5개5대
2향후+ Terraform (IaaS별 provider), dynamic inventory10대+
3Secret 복잡SOPS + age 마이그레이션 검토
4팀 규모↑CI에서 Playbook 실행 (GitHub Actions), 승인 워크플로50대+

지금 권장: Phase 1만 시작. Terraform은 VM이 자주 생성/파괴될 때 가치 — 초기엔 각 IaaS의 대시보드 UI로도 충분.

저장소 구조 (멀티 계정 Overlay)

multi-saas-kit은 공통 코드계정(tenant)별 데이터를 폴더 수준으로 분리합니다. 스타터킷을 구매한 개발사는 자사 서버 + 자기 고객사 서버까지 한 저장소로 관리할 수 있습니다(MSP 패턴).

workspace/_infra/
├── ansible/ ← 스타터킷 공통 (판매 대상)
│ ├── ansible.cfg # account-aware (환경변수 override)
│ ├── playbooks/{bootstrap,site}.yml
│ ├── roles/{common,user,hardening,tailscale,docker, …}
│ └── docs/ # 내부 세부 가이드

├── ansible-accounts/ ← 계정 루트
│ ├── _example/ ← 템플릿 (판매 포함)
│ ├── self/ ← 자사 계정 (gitignore)
│ ├── customer-a/ ← 고객사 A (gitignore)
│ └── customer-b/ ← 고객사 B (gitignore)

└── scripts/
└── ansible-account.sh ← 계정 선택 실행 wrapper

계정 격리 장치 3가지

장치효과
계정별 .vault-pass 분리다른 계정의 secret을 실수로 복호화 불가
ANSIBLE_INVENTORY 강제 주입ansible all -m ... 명령도 해당 계정 서버만 대상
계정 폴더 존재 검증오타(예: customer-A vs customer-a) 즉시 에러

실행 예시

cd workspace/_infra

# 자사 서버
scripts/ansible-account.sh self site

# 고객사 서버
scripts/ansible-account.sh customer-a bootstrap --limit npm-01
scripts/ansible-account.sh customer-b site --tags hardening --check --diff

설계 배경: ADR-020 멀티 계정 Overlay

네트워크 설계

Public → Tailscale → Private 3단 구조

Public Internet
│ *.codebase.how (DNS: Cloudflare)

NPM 서버 (80/443 only)
│ SSL 종료 + reverse proxy

Tailscale Mesh (100.64.0.0/10)
├─ App 서버들
├─ DB 서버
└─ DevTools 어드민

방화벽 정책

포트외부 공개Tailscale 내부
22 (SSH)✅ Tailscale IP로만
80 (HTTP)NPM만 ✅
443 (HTTPS)NPM만 ✅
앱/DB/Redis✅ 내부만

주요 자동화 범위

Ansible이 담당하는 작업:

범주작업
OS 기본hostname, timezone, apt update, 필수 패키지
계정deploy 유저 생성, SSH 키 주입, sudo NOPASSWD
보안SSH hardening, UFW 규칙, fail2ban, unattended-upgrades
네트워크Tailscale 가입 + ACL 태그
컨테이너Docker Engine + Compose v2 설치
관측node_exporter + promtail 설치·설정
앱 배포git clone + docker compose up -d + 환경변수 주입
역할별NPM / Laravel / PostgreSQL / Grafana LGTM 배포

다음 단계

관련 문서