CloudStack의 app 서버에서 노드 SSH 연결 모니터링

CloudStack management/app 서버에서 KVM 노드 SSH 연결을 주기적으로 확인하는 스크립트입니다. BatchMode, ConnectTimeout, 대상 파일, 로그, 실패 원인 분리를 정리했습니다.

CloudStack management/app 서버에서 각 KVM 노드로 SSH가 안 되면 agent 점검, 로그 수집, 수동 복구 작업이 지연됩니다. 이 글의 목적은 “SSH 명령 한 번 실행”이 아니라, 여러 노드를 대상으로 연결 성공/실패를 기록하고 타임아웃을 빠르게 감지하는 모니터링 스크립트를 만드는 것입니다.

노드 목록 준비

모니터링 대상은 파일로 관리합니다. 운영 계정, 호스트명, 포트를 명시합니다.

sudo install -d -m 755 /etc/cloudstack-monitor
sudo tee /etc/cloudstack-monitor/node-ssh-targets.conf >/dev/null <<'EOF'
# name user host port
kvm-node-01 root kvm-node-01.example.com 22
kvm-node-02 root kvm-node-02.example.com 22
EOF

CloudStack API를 쓰는 환경이라면 host 목록을 주기적으로 생성해도 됩니다.

cloudmonkey list hosts type=Routing filter=id,name,state,ipaddress

SSH 연결 모니터링 스크립트

자동 모니터링에서는 패스워드 프롬프트가 뜨면 안 됩니다. BatchMode=yes로 비대화형 연결만 허용하고, ConnectTimeout과 전체 timeout을 함께 둡니다.

#!/usr/bin/env bash
set -Eeuo pipefail

TARGET_FILE="${1:-/etc/cloudstack-monitor/node-ssh-targets.conf}"
LOG_FILE="/var/log/cloudstack-node-ssh-monitor.log"
ALERT_TO="cloud-ops@example.com"
SSH_TIMEOUT=5

log() {
  printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "${LOG_FILE}"
}

alert() {
  local subject="$1"
  local body="$2"
  log "ALERT ${subject} ${body}"
  if command -v mailx >/dev/null 2>&1; then
    printf '%s\n' "${body}" | mailx -s "${subject}" "${ALERT_TO}" || true
  fi
}

check_ssh() {
  local name="$1"
  local user="$2"
  local host="$3"
  local port="$4"

  if timeout "$((SSH_TIMEOUT + 2))" ssh \
      -o BatchMode=yes \
      -o ConnectTimeout="${SSH_TIMEOUT}" \
      -o ConnectionAttempts=1 \
      -o StrictHostKeyChecking=accept-new \
      -p "${port}" \
      "${user}@${host}" 'echo ok' >/tmp/cloudstack-ssh-check.$$ 2>&1; then
    log "OK ssh name=${name} host=${host} port=${port}"
    rm -f /tmp/cloudstack-ssh-check.$$
    return 0
  fi

  local detail
  detail="$(tr '\n' ' ' < /tmp/cloudstack-ssh-check.$$ | sed 's/[[:space:]]\\+/ /g' | cut -c1-300)"
  rm -f /tmp/cloudstack-ssh-check.$$
  alert "CloudStack node SSH failed: ${name}" "host=${host} port=${port} detail=${detail}"
  return 1
}

overall=0
while read -r name user host port; do
  [ -z "${name:-}" ] && continue
  [[ "${name}" =~ ^# ]] && continue
  check_ssh "${name}" "${user}" "${host}" "${port}" || overall=1
done < "${TARGET_FILE}"

exit "${overall}"

등록과 로그 확인

sudo install -m 755 cloudstack-node-ssh-monitor.sh /usr/local/sbin/cloudstack-node-ssh-monitor.sh
sudo touch /var/log/cloudstack-node-ssh-monitor.log
sudo chmod 640 /var/log/cloudstack-node-ssh-monitor.log
sudo crontab -e
*/3 * * * * /usr/local/sbin/cloudstack-node-ssh-monitor.sh >/dev/null 2>&1
tail -f /var/log/cloudstack-node-ssh-monitor.log

실패 원인 분리

스크립트가 실패를 기록하면 다음 순서로 분리합니다.

# management/app 서버에서
nc -vz -w 3 kvm-node-01.example.com 22
ssh -vvv -o BatchMode=yes -o ConnectTimeout=5 root@kvm-node-01.example.com 'echo ok'

# 대상 노드 콘솔에서
systemctl status sshd --no-pager
journalctl -u sshd -n 100 --no-pager
ss -lntp | grep ':22'

Permission denied는 키/계정 문제이고, Connection timed out은 네트워크/방화벽 문제입니다. Connection refused는 포트는 도달했지만 sshd가 listen하지 않는 경우가 많습니다.

운영 주의사항

모니터링 전용 SSH 키는 권한을 최소화하고, 가능하면 root 대신 읽기/점검 전용 계정을 사용합니다. 비밀번호 로그인에 의존하면 크론에서 멈추거나 보안상 위험하므로 공개키 기반 비대화형 인증을 기준으로 구성하세요.

BGM EVER