Skip to main content

서버 프로비저닝 (IaaS + Ansible)

IaaS에서 서버를 생성하고 Ansible로 표준 상태까지 자동 셋업하는 전체 과정입니다. 아래 Phase 1은 Vultr를 예시로 하지만, DigitalOcean / Linode / Hetzner / AWS EC2 등 어떤 IaaS에서도 동일한 흐름이 적용됩니다. 핵심은 "Ubuntu 24.04 LTS 서버 + 운영자 SSH 공개키 + 초기 IP"만 확보하면 Phase 2 이후는 완전히 동일합니다.

사전 요구사항

항목필수 여부비고
로컬에 Ansible 2.15+ 설치pip install ansible 또는 apt install ansible
SSH 키 쌍 (운영자용)ssh-keygen -t ed25519
IaaS 계정 + SSH 공개키 등록각 IaaS Dashboard의 SSH Keys 메뉴 (Vultr / DigitalOcean / Linode 등 공통)
Tailscale 계정 + pre-auth keyAdmin Console → Settings → Keys → Auth keys
ansible-vault 비밀번호Secret 관리용

Phase 1: IaaS 서버 생성 (Vultr 예시)

다른 IaaS를 사용하는 경우

아래는 Vultr 대시보드 기준 예시입니다. DigitalOcean, Linode, Hetzner, AWS EC2 등에서도 동일한 핵심 4가지만 준비하면 됩니다:

  1. Ubuntu 24.04 LTS x64 이미지
  2. 운영자 SSH 공개키 등록
  3. 역할 기반 hostname (예: msk-app-01)
  4. 생성된 공인 IP 주소 확보

이후 Phase 2~5는 IaaS와 무관하게 모두 같습니다.

Vultr Dashboard에서 (예시)

  1. Deploy New Server 클릭
  2. 설정:
    • Server Type: Cloud Compute (또는 High Performance)
    • Location: Tokyo/Seoul/Singapore 등 가까운 리전
    • Server Image: Ubuntu 24.04 LTS x64
    • Server Size: 최소 2GB RAM 이상 권장 (앱에 따라 조정)
    • SSH Keys: 사전 등록한 운영자 공개키 선택 (필수)
    • Firewall Group: 기본 (Ansible이 UFW로 재구성)
    • Hostname: 역할 기반 (예: msk-npm-01, msk-app-quant)
  3. Deploy Now
  4. 생성 완료 후 IP 주소 복사
cloud-init 자동화 (선택)

대부분의 IaaS(Vultr, DigitalOcean, AWS EC2 등)는 User Data(cloud-init)를 지원합니다. 첫 부팅 시 기본 패키지 설치까지 자동화하려면:

#cloud-config
package_update: true
package_upgrade: true
packages:
- curl
- git

단, 복잡한 설정은 Ansible이 담당하므로 cloud-init은 최소만 두는 것이 관리 단순화 측면에서 좋습니다.

Phase 2: Inventory 등록

workspace/_infra/ansible/inventory/hosts.ini에 신규 서버 추가:

# Phase 1: 정적 inventory
[npm_servers]
msk-npm-01 ansible_host=141.164.xx.xx ansible_user=root

[app_servers]
msk-app-quant ansible_host=64.176.xx.xx

[db_servers]
msk-db-01 ansible_host=108.61.xx.xx

[monitoring_servers]
msk-monitor-01 ansible_host=95.179.xx.xx

[all:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_ssh_private_key_file=~/.ssh/id_ed25519

Phase 2.5: 계정(Account) 준비

multi-saas-kit은 멀티 계정(tenant) overlay 구조를 따릅니다 (ADR-020). 각 운영자(자사 포함)·고객사는 별도 계정 폴더를 가집니다.

cd workspace/_infra

# 자사 계정 생성 (최초 1회)
cp -r ansible-accounts/_example ansible-accounts/self
cp ansible-accounts/self/inventory/hosts.ini.example ansible-accounts/self/inventory/hosts.ini
cp ansible-accounts/self/vault/secrets.yml.example ansible-accounts/self/vault/secrets.yml
vi ansible-accounts/self/inventory/hosts.ini # 실제 서버 IP 입력
vi ansible-accounts/self/vault/secrets.yml # tailscale_authkey, ssh 공개키 등
ansible-vault encrypt ansible-accounts/self/vault/secrets.yml

# 고객사 관리가 필요하면 동일 절차로 추가
cp -r ansible-accounts/_example ansible-accounts/customer-a
계정 격리 보장

실행은 반드시 scripts/ansible-account.sh <account> <playbook> 형식을 사용합니다. wrapper가 ANSIBLE_INVENTORY, ANSIBLE_VAULT_PASSWORD_FILE, ANSIBLE_ROLES_PATH를 계정별로 자동 주입하여 타 계정 서버 사고를 방지합니다.

Phase 3: Bootstrap (최초 1회)

대부분의 IaaS는 생성 직후 root만 제공하므로 최초 1회는 root로 접속하여 deploy 유저를 만들고, 이후부터는 deploy로만 접근합니다. (AWS EC2처럼 ubuntu 유저가 기본 제공되는 경우에도 -u ubuntu로 접속 후 동일한 bootstrap 결과를 얻습니다.)

cd workspace/_infra

# 드라이런으로 먼저 확인
scripts/ansible-account.sh self bootstrap \
--limit msk-npm-01 \
--check --diff \
-u root

# 실제 실행
scripts/ansible-account.sh self bootstrap \
--limit msk-npm-01 \
-u root

Bootstrap이 수행하는 작업

순서작업Role
1기본 패키지 업데이트common
2deploy 유저 생성 + SSH 키 주입user
3sudo NOPASSWD 설정user
4PermitRootLogin nohardening
5PasswordAuthentication no (SSH 키 인증만)hardening
6UFW 활성화 (22/80/443만)hardening
7fail2ban 설치hardening
8unattended-upgrades 활성화hardening
9Tailscale 설치 + 가입 (pre-auth key)tailscale

완료 확인

# deploy 유저로 SSH 접속 가능한가
ssh deploy@{서버IP}

# root 접속은 차단되어야 함
ssh root@{서버IP} # → 거부되어야 정상

# Tailscale 상태
ssh deploy@{서버IP} 'tailscale status'

Phase 4: 표준 셋업 (site.yml)

Bootstrap 이후에는 deploy 유저로 반복 가능한 표준 셋업을 실행합니다.

cd workspace/_infra

# 드라이런
scripts/ansible-account.sh self site \
--limit msk-npm-01 \
--check --diff

# 실제 실행
scripts/ansible-account.sh self site --limit msk-npm-01

site.yml이 수행하는 작업

범주작업
DockerDocker Engine + Compose v2 설치 + deploy 유저를 docker 그룹에 추가
관측성node_exporter (메트릭) + promtail (로그 → Loki)
역할별inventory의 그룹 변수에 따라 npm / laravel_app / postgres / grafana_lgtm role 실행

Phase 5: 역할별 배포

각 역할은 별도 playbook으로도 실행 가능합니다.

NPM 서버

scripts/ansible-account.sh self npm --limit npm_servers

수행 내용:

  • /opt/npm/ 에 docker-compose.yml 배포
  • data/, letsencrypt/ 볼륨 권한 설정
  • docker compose up -d 실행

App 서버 (Laravel)

scripts/ansible-account.sh self app \
--limit msk-app-quant \
--extra-vars "project=quant.how branch=main"

DB 서버 (PostgreSQL)

scripts/ansible-account.sh self db --limit db_servers

모니터링 서버 (Grafana LGTM)

scripts/ansible-account.sh self monitoring --limit monitoring_servers

검증 체크리스트

Bootstrap + site.yml 완료 후 다음을 확인:

  • deploy 유저로 SSH 접속 가능
  • root 원격 로그인 차단됨 (ssh root@... 거부)
  • sudo ufw status → active, 22/80/443만 허용
  • sudo systemctl status fail2ban → active
  • tailscale status → 다른 서버들과 연결
  • docker compose version → v2
  • systemctl status node_exporter → active (Tailscale 내부에서 :9100 접근)
  • systemctl status promtail → active
  • 역할별 서비스 동작 (docker ps, 웹 접속 등)

일반적인 함정

문제원인해결
"Permission denied (publickey)"root SSH 키 미등록각 IaaS Dashboard에서 SSH Key 추가 후 서버 재배포
Tailscale 가입 실패pre-auth key 만료Tailscale Admin에서 새 key 발급
UFW가 SSH 차단22번 포트 허용 누락hardening role이 22/tcp 명시적 허용하는지 확인
Docker group 권한 재로그인 필요그룹 추가는 새 세션부터 적용한 번 로그아웃 후 재접속
Playbook 중간 실패네트워크/패키지 저장소 불안정재실행 (멱등성 덕에 안전). 로그에서 실패 task 확인

Secret 관리

ansible-vault로 비밀번호 암호화

# 신규 생성
ansible-vault create vault/secrets.yml

# 편집
ansible-vault edit vault/secrets.yml

# Playbook에서 사용
ansible-playbook ... --ask-vault-pass
# 또는
ansible-playbook ... --vault-password-file ~/.ansible-vault-pass

vault/secrets.yml 예시

tailscale_authkey: tskey-auth-xxxxx
deploy_user_ssh_public_key: "ssh-ed25519 AAAA..."
grafana_admin_password: t0eUHKk1FITgr3yaylofGpTvYFkVsG
postgres_password: xxxxx
cloudflare_api_token: xxxxx
vault 비밀번호 관리

ansible-vault 비밀번호를 잃으면 암호화된 모든 secret을 복구할 수 없습니다. 1Password / Bitwarden 같은 비밀번호 관리자에 보관하고, 팀이라면 공유 vault에 저장하세요.

Phase 2 이후: Terraform 추가

서버가 10대 이상으로 늘어나고 자주 생성/파괴되기 시작하면 Terraform을 추가하여 VM 생성 자체도 IaC화합니다. Terraform은 IaaS별 provider가 별도로 제공되므로, 사용 중인 클라우드의 provider를 선택하면 됩니다.

Vultr 예시 (다른 IaaS는 provider 블록과 리소스명만 교체):

# workspace/_infra/terraform/main.tf (향후)
resource "vultr_instance" "app" {
count = 3
label = "msk-app-${count.index + 1}"
plan = "vc2-2c-4gb"
region = "nrt"
os_id = 1743 # Ubuntu 24.04
ssh_key_ids = [data.vultr_ssh_key.ops.id]
hostname = "msk-app-${count.index + 1}"
}

동등한 DigitalOcean 예시:

resource "digitalocean_droplet" "app" {
count = 3
name = "msk-app-${count.index + 1}"
size = "s-2vcpu-4gb"
region = "sgp1"
image = "ubuntu-24-04-x64"
ssh_keys = [data.digitalocean_ssh_key.ops.id]
}

핵심 원칙: Terraform이 생성 → Ansible이 셋업의 2단계 파이프라인. 이 역할 분리는 어떤 IaaS를 선택해도 동일합니다.

관련 문서