별책부록 6편
systemd + pm2 + Cloudflare Tunnel
SSH를 끊어도 봇은 24/7 작동합니다
📑 이 챕터에서 다룰 내용
운영자가 SSH를 끊어도 봇은 24/7 작동합니다. 1탄 v2 메타 원칙 "Day 0 시스템 자동 운영"의 봇 적용이에요.
📘 6편 개요
- 예상 시간: 1시간
- 핵심: 자동 재시작 + 영구 운영 + (선택) 외부 노출
- 도구: pm2 (Node.js 봇) + systemd (Python 봇) + Cloudflare Tunnel (선택)
pm2 설치
💻 터미널에 입력하세요
# 글로벌 설치 sudo npm install -g pm2 # 확인 pm2 --version
pm2 ecosystem 설정
💻 ecosystem.config.cjs 생성
cd ~/tsv-bot
# ecosystem 파일 생성
cat > ecosystem.config.cjs << 'EOF'
module.exports = {
apps: [{
name: 'tsv-discord-bot',
script: './dist/bot.js',
instances: 1,
exec_mode: 'fork',
// 자동 재시작
autorestart: true,
watch: false,
max_memory_restart: '500M',
// 환경 변수
env: {
NODE_ENV: 'production',
},
// 로그
out_file: './logs/pm2-out.log',
error_file: './logs/pm2-error.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
merge_logs: true,
// 재시작 정책
min_uptime: '10s',
max_restarts: 10,
restart_delay: 5000, // 5초 대기
}]
};
EOF
# 빌드 후 시작
npm run build
pm2 start ecosystem.config.cjs
# 상태 확인
pm2 status
pm2 logs tsv-discord-bot
# 부팅 시 자동 시작
pm2 startup
# 안내된 명령어 복사 + sudo 실행
pm2 save
pm2 운영 명령
💻 pm2 주요 명령
pm2 status # 상태 pm2 logs tsv-discord-bot # 실시간 로그 pm2 restart tsv-discord-bot # 재시작 pm2 stop tsv-discord-bot # 중지 pm2 delete tsv-discord-bot # 삭제 pm2 monit # CPU·메모리 monitor
systemd service 파일
💻 /etc/systemd/system/tsv-tg-bot.service 생성
# /etc/systemd/system/tsv-tg-bot.service sudo tee /etc/systemd/system/tsv-tg-bot.service > /dev/null << 'EOF' [Unit] Description=TSV Telegram Bot (Python) After=network.target [Service] Type=simple User=ubuntu WorkingDirectory=/home/ubuntu/tsv-tg-bot Environment="PATH=/home/ubuntu/tsv-tg-bot/.venv/bin" # 가상환경 Python 사용 ExecStart=/home/ubuntu/tsv-tg-bot/.venv/bin/python /home/ubuntu/tsv-tg-bot/src/bot.py # 자동 재시작 Restart=always RestartSec=10 # 보안 NoNewPrivileges=true PrivateTmp=true # 메모리 제한 (24GB 중 500MB) MemoryMax=500M # 로그 StandardOutput=append:/home/ubuntu/tsv-tg-bot/logs/systemd-out.log StandardError=append:/home/ubuntu/tsv-tg-bot/logs/systemd-error.log [Install] WantedBy=multi-user.target EOF # systemd 재로드 sudo systemctl daemon-reload # 서비스 활성화 + 시작 sudo systemctl enable tsv-tg-bot sudo systemctl start tsv-tg-bot # 상태 확인 sudo systemctl status tsv-tg-bot
systemd 운영 명령
💻 systemd 주요 명령
sudo systemctl status tsv-tg-bot # 상태 sudo systemctl restart tsv-tg-bot # 재시작 sudo systemctl stop tsv-tg-bot # 중지 sudo systemctl disable tsv-tg-bot # 부팅 시 자동 시작 X # 로그 (실시간) sudo journalctl -u tsv-tg-bot -f # 로그 (최근 100줄) sudo journalctl -u tsv-tg-bot -n 100
언제 필요한가
📘 Cloudflare Tunnel 필요 여부
필요한 경우
- Telegram webhook 사용 시 (polling X → webhook)
- 봇 외에 web admin 페이지 운영 시
- 외부 사용자 접근 시 (HTTPS 필요)
필요하지 않은 경우
- 봇만 운영 (polling으로 충분)
- Discord·Telegram 봇은 outbound만 — Tunnel 의무 X
설치 + 설정
💻 Cloudflare Tunnel 설치 및 설정
# 1. cloudflared 설치 (ARM64)
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64.deb
sudo dpkg -i cloudflared.deb
rm cloudflared.deb
# 2. Cloudflare 로그인
cloudflared tunnel login
# 브라우저에서 인증 → 도메인 선택
# 3. Tunnel 생성
cloudflared tunnel create tsv-bot-tunnel
# Tunnel ID 출력 (안전 보관)
# 예: 1234abcd-...
# 4. DNS 라우팅
cloudflared tunnel route dns tsv-bot-tunnel bot.example.com
# 5. 설정 파일
mkdir -p ~/.cloudflared
cat > ~/.cloudflared/config.yml << 'EOF'
tunnel: 1234abcd-...
credentials-file: /home/ubuntu/.cloudflared/1234abcd-....json
ingress:
- hostname: bot.example.com
service: http://localhost:3000
- service: http_status:404
EOF
# 6. 서비스 등록
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
# 7. 상태 확인
sudo systemctl status cloudflared
Telegram webhook 전환 (선택)
💻 src/bot.py — Polling → Webhook 전환
# src/bot.py 수정
# Polling → Webhook
import os
from telegram.ext import Application
WEBHOOK_URL = os.environ.get("WEBHOOK_URL") # https://bot.example.com
PORT = int(os.environ.get("PORT", 3000))
def main():
token = os.environ["TELEGRAM_TOKEN"]
app = Application.builder().token(token).build()
app.add_handler(CommandHandler("ask", ask_command))
if WEBHOOK_URL:
# Webhook 모드 (외부 노출)
app.run_webhook(
listen="0.0.0.0",
port=PORT,
url_path=token,
webhook_url=f"{WEBHOOK_URL}/{token}",
)
else:
# Polling 모드 (기본)
app.run_polling()
모니터링 명령
💻 전체 모니터링
# === Node.js 봇 === pm2 status pm2 logs tsv-discord-bot --lines 50 # === Python 봇 === sudo systemctl status tsv-tg-bot sudo journalctl -u tsv-tg-bot -n 50 # === 시스템 === htop # CPU·메모리 df -h # 디스크 free -h # RAM sudo ufw status # 방화벽 sudo fail2ban-client status # 차단 IP # === Cloudflare Tunnel (사용 시) === sudo systemctl status cloudflared
자동 재시작 검증
💻 강제 종료 후 자동 재시작 확인
# Node.js (pm2) pm2 stop tsv-discord-bot sleep 5 pm2 status # 자동으로 'stopped' 상태 (수동 stop이라 재시작 X) # 강제 kill 시 자동 복구 pm2 start tsv-discord-bot ps aux | grep node sudo kill -9 <pid> sleep 10 pm2 status # 자동 'online' 복구 ✅ # Python (systemd) — 동일 검증 sudo systemctl start tsv-tg-bot ps aux | grep python sudo kill -9 <pid> sleep 15 sudo systemctl status tsv-tg-bot # 자동 'active (running)' ✅
로그 회전 설정
💻 로그 회전 설정
# pm2 로그 회전
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress true
# Python 봇 로그 (logrotate)
sudo tee /etc/logrotate.d/tsv-tg-bot > /dev/null << 'EOF'
/home/ubuntu/tsv-tg-bot/logs/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
su ubuntu ubuntu
}
EOF
# 강제 회전 테스트
sudo logrotate -f /etc/logrotate.d/tsv-tg-bot
💻 최종 검증 — ssh 끊어도 봇이 살아있는지 확인
# 모든 setup 완료 후 검증 # 1. 두 봇 작동 확인 pm2 status # tsv-discord-bot: online sudo systemctl status tsv-tg-bot # active (running) # 2. ssh 종료 exit # 또는 ctrl-D # 3. 다시 ssh 접속 ssh ubuntu@your-ip # 4. 봇 여전히 작동 확인 pm2 status # ★ 여전히 online ✅ sudo systemctl status tsv-tg-bot # ★ 여전히 active ✅ # 5. Discord / Telegram 에서 봇 응답 테스트 # !ask 안녕 / /ask 안녕
🎉 운영 상태 확인
- Node.js 디스코드 봇: pm2 (자동 재시작 + 로그 회전)
- Python Telegram 봇: systemd (자동 재시작 + 메모리 보호)
- (선택) Cloudflare Tunnel: 외부 노출 + HTTPS 무료
- 서버 재부팅 → 두 봇 자동 시작
- 강제 kill → 5~10초 안에 자동 복구
- ssh 종료 → 봇 영향 X
⚠️ 주요 함정 5가지
[함정 1] pm2 startup 후 sudo 명령 실행 X
- 증상: 서버 재부팅 시 봇 자동 시작 X
- 해결: pm2 startup 출력 명령 그대로 sudo 실행 + pm2 save
[함정 2] systemd User 잘못 지정
- 증상: Permission denied 에러
- 해결: User=ubuntu (또는 본인 사용자) 정확 명시 + 권한 확인
[함정 3] 가상환경 path 오류
- 증상: ModuleNotFoundError
- 해결: ExecStart 절대 경로 + Environment PATH 명시
[함정 4] Cloudflare Tunnel ARM64 패키지
- 증상: x86_64 .deb 설치 실패
- 해결: cloudflared-linux-arm64.deb 명시 다운로드
[함정 5] 메모리 누수
- 증상: 시간 지나면 메모리 폭증 + OOM kill
- 해결: max_memory_restart (pm2) 또는 MemoryMax (systemd) 설정 → 자동 재시작으로 복구
📌 6편 정리
- 1️⃣ pm2: Node.js 봇(디스코드) 자동 재시작 + 로그 회전 + 부팅 시 자동 시작
- 2️⃣ systemd: Python 봇(Telegram) 자동 재시작 + 메모리 보호 + 부팅 시 자동 시작
- 3️⃣ Cloudflare Tunnel: webhook 모드 또는 web admin 필요 시 선택 적용
- 4️⃣ 자동 재시작 검증: 강제 kill → 5~10초 안에 자동 복구 ✅
- 5️⃣ 로그 회전: pm2-logrotate 10M/7일 + logrotate 일별/7일
- 6️⃣ 메모리 보호: pm2 max_memory_restart 500M + systemd MemoryMax=500M
- 7️⃣ ssh 종료 검증: exit 후 재접속 → 두 봇 여전히 작동 ✅
📘 3 도구 분담 정리
- pm2: Node.js 봇 (디스코드)
- systemd: Python 봇 (Telegram)
- Cloudflare Tunnel: 외부 노출 (선택)
pm2 + systemd + Cloudflare Tunnel = SSH를 끊어도 봇은 안 죽는 자리 ✅
🎉 1탄 v2 메타 원칙 일관
"Day 0 시스템" — 운영자 휴식 동안 시스템 자체 안정 운영 → 봇 적용 완료
다층 보호 (pm2 + systemd + max_memory + logrotate) = E1 다층 패턴 일관
다음 편: 07편 — Position C 5차원 강화 (베팅·픽 자동 검출 + /about·/terms·/privacy)