예제 & 사용 사례
완전한 설정과 함께 살펴보는 실전 배포 시나리오입니다.
Spring Boot 마이크로서비스
API 서버, 배치 프로세서, 인프라 서비스로 구성된 전형적인 백엔드입니다.
project:
name: my-platform
version: 1.0.0
base_dir: /app
defaults:
ssh_user: ec2-user
ssh_key_path: ~/.ssh/deploy.pem
health_check:
timeout: 120
interval: 5
environments:
dev:
ssh_key_path: ~/.ssh/dev.pem
branch: develop
servers:
- number: 1
host: 10.0.1.10
prod:
ssh_key_path: ~/.ssh/prod.pem
branch_policy:
allowed: [main, hotfix/*]
servers:
- number: 1
host: 52.78.100.1
modules: [api-server, admin-server]
- number: 2
host: 52.78.100.2
modules: [api-server]
- number: 3
host: 52.78.100.3
modules: [batch-server, kafka, redis]
modules:
api-server:
type: springboot
port: 8080
build_cmd: ./gradlew :api-server:bootJar -x test
artifact_path: api-server/build/libs/api-server-*.jar
config_dir: config
health_check:
type: http
target: http://localhost:8080/actuator/health
variables:
JAVA_OPTS: -Xms512m -Xmx2048m -XX:+UseG1GC
hooks:
pre_build: ./gradlew :api-server:test
post_deploy: |
curl -X POST ${SLACK_WEBHOOK} \
-H 'Content-type: application/json' \
-d '{"text":"api-server deployed to prod"}'
admin-server:
type: springboot
port: 8081
build_cmd: ./gradlew :admin-server:bootJar
health_check:
type: http
target: http://localhost:8081/actuator/health
batch-server:
type: springboot
port: 8082
build_cmd: ./gradlew :batch-server:bootJar
health_check:
type: log
target: "Started BatchApplication in"
kafka:
type: kafka
port: 9092
data_dirs: [kafka-logs]
redis:
type: redis
port: 6379
data_dirs: [redis-data]
배포 명령어
# Deploy API server to all prod servers
tow auto -e prod -m api-server
# Deploy only to prod server 1
tow auto -e prod -m api-server -s 1
# Deploy all modules to dev
tow auto -e dev
# Check status across production
tow status -e prod -m api-server
# Rollback API server if something goes wrong
tow rollback -e prod -m api-server
Node.js / NestJS 애플리케이션
project:
name: my-api
base_dir: /app
defaults:
ssh_user: ubuntu
ssh_key_path: ~/.ssh/deploy.pem
environments:
staging:
branch: develop
servers:
- number: 1
host: 10.0.1.20
prod:
branch: main
servers:
- number: 1
host: api-1.example.com
- number: 2
host: api-2.example.com
modules:
api:
type: node
port: 3000
build_cmd: npm ci && npm run build
start_cmd: node dist/main.js
artifact_path: build/api.tar.gz
package_includes:
- dist/
- node_modules/
- package.json
health_check:
type: http
target: http://localhost:3000/health
timeout: 60
variables:
NODE_ENV: production
PM2_INSTANCES: 4
Python / FastAPI 애플리케이션
project:
name: ml-service
base_dir: /opt/services
defaults:
ssh_user: deploy
ssh_key_path: ~/.ssh/ml-deploy.pem
environments:
prod:
branch: main
servers:
- number: 1
host: gpu-server-1.internal
- number: 2
host: gpu-server-2.internal
modules:
inference-api:
type: python
port: 8000
build_cmd: pip install -r requirements.txt && python -m pytest
start_cmd: |
cd current && \
gunicorn app.main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000
package_includes:
- app/
- requirements.txt
- models/
health_check:
type: http
target: http://localhost:8000/health
timeout: 180 # Models take time to load
data_dirs:
- model-cache
- uploads
Go 마이크로서비스
project:
name: gateway
base_dir: /opt/services
defaults:
ssh_user: deploy
ssh_key_path: ~/.ssh/deploy.pem
environments:
prod:
branch: main
servers:
- number: 1
host: edge-1.example.com
- number: 2
host: edge-2.example.com
modules:
gateway:
type: go
port: 8080
build_cmd: CGO_ENABLED=0 GOOS=linux go build -o bin/gateway ./cmd/gateway
start_cmd: bin/gateway --config conf/config.yaml
artifact_path: bin/gateway
package_includes:
- bin/gateway
- conf/
health_check:
type: http
target: http://localhost:8080/healthz
timeout: 30
커스텀 패키지 레이아웃
package_layout을 사용해서 배포 패키지 안에 파일이 배치되는 방식을 정확히 제어할 수 있습니다. 레거시 호환이나 비표준 프로젝트 구조에 유용합니다.
NSM 레거시 호환
bin/, lib/, conf/ 구조를 기대하는 레거시 셸 스크립트 배포에서 마이그레이션합니다:
modules:
nsm-api:
type: springboot
port: 8080
build_cmd: ./gradlew :nsm-api:bootJar
package_layout:
"script/nsm-api/": "bin/"
"nsm-api/build/libs/*.jar": "lib/"
"config/${ENV}/": "conf/"
"config/logback-${ENV}.xml": "conf/"
health_check:
type: http
target: http://localhost:8080/actuator/health
결과 패키지는 다음과 같은 모습입니다:
nsm-api.tar.gz
├── bin/
│ ├── env.sh
│ └── server
├── lib/
│ └── nsm-api-1.2.3.jar
└── conf/
├── application.yml
└── logback-prod.xml
커스텀 레이아웃을 사용한 Node.js
Next.js standalone 빌드를 깔끔한 디렉토리 구조로 패키징합니다:
modules:
web-app:
type: node
port: 3000
build_cmd: npm ci && npm run build
package_layout:
".next/standalone/": "./"
".next/static/": ".next/static/"
"public/": "public/"
hooks:
post_install: |
cd current && npm rebuild --arch=x64 --platform=linux
health_check:
type: http
target: http://localhost:3000/api/health
계층형 설정을 사용한 다중 환경
환경별, 서버별로 다른 설정이 필요한 프로젝트를 위한 예제입니다:
config/
├── application.yml # Shared defaults
├── dev/
│ └── application.yml # Dev overrides (debug logging, local DB)
├── staging/
│ └── application.yml # Staging (test DB, reduced resources)
├── prod/
│ └── application.yml # Prod (production DB, full resources)
├── prod-1/
│ └── application.yml # Prod server 1 (primary node config)
└── prod-2/
└── application.yml # Prod server 2 (secondary node config)
modules:
api-server:
type: springboot
port: 8080
config_dir: config # Enable hierarchical config resolution
api-server를 prod 서버 1에 배포할 때:
config/application.yml이 복사됩니다 (base)config/prod/application.yml이 오버레이됩니다 (environment)config/prod-1/application.yml이 오버레이됩니다 (server-specific)
CI/CD 연동
GitHub Actions
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Install Tow
run: go install github.com/neurosamAI/tow-cli/cmd/tow@latest
- name: Setup SSH Key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy.pem
chmod 600 ~/.ssh/deploy.pem
ssh-keyscan ${{ secrets.PROD_SERVER_1 }} >> ~/.ssh/known_hosts
ssh-keyscan ${{ secrets.PROD_SERVER_2 }} >> ~/.ssh/known_hosts
- name: Deploy to Production
env:
PROD_SERVER_1: ${{ secrets.PROD_SERVER_1 }}
PROD_SERVER_2: ${{ secrets.PROD_SERVER_2 }}
run: tow auto -e prod -m api-server
- name: Verify Deployment
run: tow status -e prod -m api-server
- name: Cleanup
if: always()
run: rm -f ~/.ssh/deploy.pem
GitLab CI
deploy-prod:
stage: deploy
image: golang:1.21
only:
- main
before_script:
- go install github.com/neurosamAI/tow-cli/cmd/tow@latest
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/deploy.pem
- chmod 600 ~/.ssh/deploy.pem
- ssh-keyscan $PROD_SERVER_1 >> ~/.ssh/known_hosts
script:
- tow auto -e prod -m api-server
- tow status -e prod -m api-server
after_script:
- rm -f ~/.ssh/deploy.pem
점진적 롤아웃 (서버별)
한 번에 서버 하나씩 배포하고 계속 진행하기 전에 확인합니다:
# Deploy to server 1 first
tow auto -e prod -m api-server -s 1
# Verify it's healthy
tow status -e prod -m api-server -s 1
tow logs -e prod -m api-server -s 1 -f "ERROR"
# If looks good, deploy to server 2
tow auto -e prod -m api-server -s 2
# Verify
tow status -e prod -m api-server -s 2
어느 단계에서든 문제가 생기면:
# Instant rollback on the affected server
tow rollback -e prod -m api-server -s 1
인프라 서비스 (28개 플러그인)
Tow는 YAML 플러그인을 통해 애플리케이션 서비스와 함께 인프라도 관리합니다. Go 코드가 필요 없습니다 — type을 플러그인 이름으로 설정하기만 하면 됩니다.
modules:
# Message broker
kafka:
type: kafka
port: 9092
data_dirs: [kafka-logs]
# Cache
redis:
type: redis
port: 6379
data_dirs: [redis-data]
# Database
postgres:
type: postgresql
port: 5432
data_dirs: [pgdata]
# Monitoring
prometheus:
type: prometheus
port: 9090
grafana:
type: grafana
port: 3000
# Search
elasticsearch:
type: elasticsearch
port: 9200
data_dirs: [es-data]
# Secret management
vault:
type: vault
port: 8200
# Deploy infrastructure
tow auto -e prod -m kafka
tow auto -e prod -m redis
tow auto -e prod -m postgres
tow status -e prod # Check everything
# Deploy Redis
tow auto -e prod -m redis -s 1
tow status -e prod -m redis
자동 롤백을 사용한 롤링 배포
프로덕션에 배포하는 가장 안전한 방법 — 한 번에 서버 하나씩, 문제가 생기면 자동으로 되돌립니다:
# Deploy one server at a time, verify health before moving to next
tow auto -e prod -m api-server --rolling --auto-rollback -y
동작 과정:
- 로컬에서 빌드하고 패키징
- 모든 서버에 업로드
- 모든 서버에 설치
- 모든 서버 중지
- server-1 시작 → 헬스체크 대기 → 통과 ✓
- server-2 시작 → 헬스체크 대기 → 통과 ✓
- 어떤 서버든 헬스체크에 실패하면 → 모든 서버를 이전 버전으로 자동 롤백
알림 (Slack / Discord / 웹훅)
배포 이벤트에 대한 알림을 받으세요:
# In tow.yaml
notifications:
- type: slack
url: ${SLACK_WEBHOOK_URL}
- type: webhook
url: https://my-monitoring.com/deploy-events
자동으로 전송되는 이벤트: deploy_start, deploy_success, deploy_failed, auto_rollback.
보존 정책을 사용한 자동 정리
디스크 공간을 관리하세요:
# In tow.yaml
retention:
keep: 5 # Keep 5 most recent deployments
auto_cleanup: true # Clean up automatically after each deploy
또는 수동으로 정리:
tow cleanup -e prod -m api-server --keep 3
AI 에이전트 연동
Claude Code / MCP
AI 연동과 함께 초기화하세요:
tow init --with-ai
다음이 생성됩니다:
.claude/skills/tow-deploy.md— Claude Code가 Tow 명령어를 이해하게 됩니다.claude/settings.json— MCP 서버가 자동으로 설정됩니다
그런 다음 Claude에게 물어보세요: “api-server를 staging에 배포해줘” 또는 “prod 상태를 확인하고 에러 로그를 보여줘”.
수동 MCP 설정
{
"mcpServers": {
"tow": {
"command": "tow",
"args": ["mcp-server"]
}
}
}
프리셋을 사용한 멀티서버 로그 조회
클러스터 전체를 디버깅할 때, 여러 서버의 로그를 한 번에 보면 시간이 절약됩니다:
# View logs from all Kafka brokers simultaneously
tow logs -e prod -m kafka --all
# View logs from specific brokers only
tow logs -e prod -m kafka -s kafka-1,kafka-3
# Follow logs in real time with an error filter
tow logs -e prod -m kafka --all -F -f "ERROR"
# Save a frequently used log query as a preset
tow logs -e prod -m api-server -f "OutOfMemoryError" --save-preset oom-check
# Replay the saved preset later
tow logs --preset oom-check
# Manage presets
tow logs --list-presets
tow logs --delete-preset oom-check
멀티모듈 로그 조회
관련 서비스를 함께 모니터링하세요:
tow logs -e prod -m noriter-kafka,noriter-zookeeper --all -n 3
[noriter-kafka/kafka-1] 2026-03-30 14:35:49 GC Pause Young 765M→702M 17ms
[noriter-kafka/kafka-2] 2026-03-30 14:35:55 GC Pause Young 340M→292M 18ms
[noriter-zookeeper/zk-1] 2026-03-30 14:36:01 Session established
배포 메트릭
tow metrics -e prod --days 7
Deployments (last 7 days):
Total: 7
By action:
deploy 4
auto 2
rollback 1
By module:
api-server ████████████████████ 4
admin █████ 1
kafka █████ 1
market █████ 1
tow ssh로 애드혹 명령 실행
인터랙티브 세션을 열지 않고 여러 서버에서 명령을 실행합니다:
# Check memory on all Kafka brokers
tow ssh -e prod -m kafka --all -- "free -h"
# Check disk usage on API servers
tow ssh -e prod -m api-server --all -- "df -h"
# View Java version on a specific server
tow ssh -e prod -m api-server -s 1 -- "java -version"
# Restart a system service (requires appropriate permissions)
tow ssh -e prod -m kafka -s kafka-2 -- "sudo systemctl restart kafka"
# Run on selected servers
tow ssh -e prod -m kafka -s kafka-1,kafka-3 -- "cat /etc/os-release"
tow config로 설정 관리
CLI에서 직접 서버, 모듈, 할당을 추가하고 제거합니다:
# Add a new server to the prod environment
tow config server add -e prod --name api-3 --host 10.0.1.30
# List all servers in prod
tow config server list -e prod
# Add a new module
tow config module add --name payment-api --type springboot --port 8083
# Assign the new module to the new server
tow config assign -e prod --server api-3 --module payment-api
# Verify the assignment
tow config server list -e prod
# Remove the assignment and clean up
tow config unassign -e prod --server api-3 --module payment-api
tow config server remove -e prod --name api-3
tow config module remove --name payment-api
tow diff로 배포된 버전과 로컬 비교
배포하기 전에 원격 서버에서 정확히 무엇이 바뀔지 확인하세요:
# Compare local build against what's currently deployed
tow diff -e prod -m api-server
# Check a specific server
tow diff -e prod -m api-server -s 1
출력 예시:
Comparing api-server: local vs prod/api-1 (deployed 2026-03-28T10:15:00Z)
Modified: lib/api-server-1.0.1.jar (size: 42.1MB → 42.3MB)
Added: conf/new-feature.properties
Unchanged: bin/server, conf/application.yml (2 files)
Summary: 1 modified, 1 added, 2 unchanged
예상한 변경 사항만 배포되는지 확인해서, 프로덕션에 도달하기 전에 예상치 못한 것들을 잡아낼 수 있습니다.
프로덕션 이슈 디버깅
# Check status on all prod servers (human-readable)
tow status -e prod -m api-server
# Machine-readable for scripts/CI
tow status -e prod -m api-server -o json
# Stream logs with error filter
tow logs -e prod -m api-server -f "ERROR"
tow logs -e prod -m api-server -f "OutOfMemoryError"
# Download logs to local for deeper analysis
tow download -e prod -m api-server logs/std.log
# SSH into a specific server
tow login -e prod -m api-server -s 1
# List all deployed versions
tow list deployments -e prod -m api-server
# Rollback to a known-good version
tow rollback -e prod -m api-server -t 20240625-091500