Windows Task Scheduler로 Python 봇 실행 중 포트 바인딩 실패 — 해결 과정
Task Scheduler로 Python 대시보드 봇을 재시작했는데 포트가 안 열림. 5번 재시도 후 포기하는 로직, 수동 실행으로 복구한 과정.
상황
여러 Python 봇을 Windows Task Scheduler로 관리하고 있습니다. 그 중 대시보드 봇이 포트 8902를 사용합니다.
어느 날 대시보드에 접속이 안 됩니다.
ERR_CONNECTION_REFUSED
Task Scheduler에서 작업 기록을 보니 실행은 되고 있는데, 실제로 포트가 안 열려 있습니다.
원인 파악
start_all.py를 보니 재시작 로직이 있었습니다:
MAX_RESTART = 5
for attempt in range(MAX_RESTART):
proc = start_bot(bot_name)
time.sleep(3)
if proc.poll() is None:
break # 성공
# 실패 시 다음 시도
else:
log(f"{bot_name} — {MAX_RESTART}번 재시작 실패, 포기")
5번 시도 후 포기하는 구조입니다. 언제부터인가 대시보드 봇이 5번 연속 실패해서 포기 상태가 됐고, 이후 Task Scheduler가 다시 실행해도 이미 give_up 상태로 남아 있었던 것입니다.
왜 5번 다 실패했나
포트가 이미 점유돼 있었습니다.
netstat -ano | Select-String ":8902"
이전에 비정상 종료된 프로세스가 포트를 물고 있었습니다. 새로 시작하려는 봇은 포트를 못 열어서 계속 실패, 5번 후 포기.
해결
포트 점유 프로세스를 찾아서 종료:
# 8902 포트 사용 중인 PID 찾기
netstat -ano | Select-String ":8902"
# 결과: TCP 0.0.0.0:8902 ... 12345
# 해당 PID 종료
Stop-Process -Id 12345 -Force
이후 봇 수동 실행:
Start-Process -FilePath "python" -ArgumentList "D:\bots\dashboard.py" -WindowStyle Hidden
포트 열림 확인:
netstat -ano | Select-String ":8902"
# TCP 0.0.0.0:8902 LISTENING
재발 방지
start_all.py에 포트 점유 확인 로직을 추가했습니다:
import socket
def is_port_free(port):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
return s.connect_ex(('localhost', port)) != 0
def kill_port(port):
import subprocess
result = subprocess.run(
f'netstat -ano | findstr :{port}',
shell=True, capture_output=True, text=True
)
for line in result.stdout.splitlines():
parts = line.split()
if parts:
pid = parts[-1]
subprocess.run(f'taskkill /PID {pid} /F', shell=True)
# 봇 시작 전
if not is_port_free(8902):
kill_port(8902)
정리
| 증상 | 원인 |
|---|---|
| 봇이 실행돼도 포트가 안 열림 | 이전 프로세스가 포트 점유 중 |
| 재시작 5번 후 포기 | MAX_RESTART 초과로 give up 상태 |
| Task Scheduler 재실행해도 안 됨 | give up 상태가 유지됨 |
Task Scheduler로 봇을 관리할 때는 포트 점유 해제 로직이 없으면 이런 상황이 생깁니다. 특히 비정상 종료가 잦은 환경에서.