상황
Termux 환경에서 FastAPI 또는 Uvicorn 서버를 실행하고 있었다.
예를 들면 다음과 같은 방식이다.
bashpython main.py
또는 main.py 내부에서 다음처럼 8000번 포트를 사용하고 있었다.
pythonuvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=False,
)
그런데 서버를 정상적으로 Ctrl + C로 종료한 것이 아니라, Code-server 자체를 중간에 꺼버렸다.
그 이후 다시 서버를 실행하면 다음과 같은 오류가 발생한다.
bashERROR: [Errno 98] Address already in use
즉, 8000번 포트가 이미 사용 중이라고 나온다.
원인
이 경우 핵심은 다음과 같다.
Code-server 터미널 안에서 실행 중이던 Python/Uvicorn 서버 프로세스가 완전히 종료되지 않고 남아 있을 가능성이 높다.
흐름으로 보면 이렇다.
plainCode-server 터미널에서 python main.py 실행
↓
FastAPI/Uvicorn 서버가 8000번 포트를 점유
↓
Ctrl + C로 정상 종료하지 않고 Code-server를 종료
↓
터미널 화면은 사라졌지만 자식 프로세스가 남을 수 있음
↓
8000번 포트가 계속 점유됨
↓
다시 python main.py 실행 시 Address already in use 발생
즉, 포트가 혼자 살아있는 것이 아니라, 8000번을 잡고 있는 프로세스가 어딘가에 남아 있는 상태다.
확인된 단서
프로세스 조회 결과에 이런 항목이 있었다.
bashu0_a438 26040 ... pts/0 ... /data/data/com.termux/files/home/workspace/my-llm-project/back/venv/bin/python ...
여기서 중요한 부분은 pts/0이다.
현재 새로 사용하는 터미널은 pts/1인데, 이전 Code-server 터미널이 pts/0였을 가능성이 있다.
즉, 이전 터미널에 연결되어 있던 프로세스가 남아 있을 수 있다.
lsof로 보이지 않는 경우
보통 포트 점유 프로세스를 찾을 때는 다음 명령을 사용한다.
bashlsof -nP -iTCP:8000
하지만 Termux나 Android 환경에서는 이 명령으로 아무것도 나오지 않을 수 있다.
bash(venv) ➜ app git:(main) ✗ lsof -nP -iTCP:8000
(venv) ➜ app git:(main) ✗
그런데 다시 서버를 실행하면 여전히 다음 오류가 발생한다.
bashERROR: [Errno 98] Address already in use
이 경우 lsof에 안 보인다고 해서 포트가 비어 있다고 단정하면 안 된다.
Termux 환경에서는 Android 권한이나 프로세스 소유자 문제 때문에 포트를 잡고 있는 프로세스가 제대로 표시되지 않을 수 있다.
1. 이전 터미널에 붙어 있던 프로세스 찾기
먼저 pts/0에 붙어 있는 프로세스를 확인한다.
bashps -A -o pid,ppid,tty,stat,args | grep 'pts/0'
만약 위 명령이 Termux에서 잘 안 되면 간단하게 확인한다.
bashps -A | grep 'pts/0'
여기서 나온 PID가 이전 Code-server 터미널에서 남은 프로세스일 수 있다.
2. 해당 PID 종료하기
예를 들어 PID가 26040이라면 다음처럼 종료한다.
bashkill 26040
안 죽으면 강제 종료한다.
bashkill -9 26040
여러 개가 나오면 한 번에 종료할 수도 있다.
bashkill -9 PID1 PID2 PID3
3. 프로젝트 venv의 Python 프로세스 전체 종료하기
만약 어떤 프로세스가 정확히 서버인지 애매하다면, 현재 프로젝트의 가상환경 Python 프로세스를 전부 종료할 수 있다.
bashpkill -9 -f "/data/data/com.termux/files/home/workspace/my-llm-project/back/venv/bin/python"
이 명령은 현재 프로젝트의 venv/bin/python으로 실행 중인 프로세스를 종료한다.
주의할 점은 Code-server의 Python 확장 기능, 예를 들어 Jedi language server 같은 프로세스도 같이 죽을 수 있다는 것이다.
하지만 보통 큰 문제는 아니다. Code-server나 Python 파일을 다시 열면 다시 실행될 수 있다.
4. Code-server 관련 프로세스까지 확인하기
Code-server, Node, Python, Uvicorn, 이전 터미널과 관련된 프로세스를 넓게 확인하려면 다음 명령을 사용한다.
bashps -A -o pid,ppid,tty,stat,args | grep -E 'code-server|node|python|uvicorn|pts/0'
여기서 의심되는 PID를 찾아 종료한다.
bashkill -9 PID
5. 다시 8000번으로 실행하기
프로세스를 정리한 뒤 다시 실행한다.
bashpython main.py
정상이라면 더 이상 다음 오류가 나오지 않아야 한다.
bashERROR: [Errno 98] Address already in use
6. 8001번은 되는데 8000번만 안 되는 경우
8001번 포트로 실행하면 정상 동작하는데 8000번만 실패한다면, 코드 문제보다는 8000번 포트 점유 문제일 가능성이 높다.
예를 들어 다음 명령은 정상 실행된다.
bashBACKEND_HOST=127.0.0.1 BACKEND_PORT=8001 BACKEND_RELOAD=false python main.py
하지만 8000번에서는 실패한다.
bashBACKEND_HOST=127.0.0.1 BACKEND_PORT=8000 BACKEND_RELOAD=false python main.py
이 경우 8001번으로 우회할 수도 있지만, 기존 프론트엔드나 API 호출 주소가 8000번을 바라보고 있다면 근본적으로 8000번 점유 프로세스를 정리해야 한다.
7. 마지막 수단: Termux 강제 종료
그래도 8000번 포트가 계속 잡혀 있다면 Android 설정에서 Termux 앱을 강제 종료한다.
경로는 보통 다음과 같다.
plainAndroid 설정
→ 앱
→ Termux
→ 강제 종료
이 방법은 Termux UID로 떠 있는 남은 프로세스를 한 번에 정리하기 때문에, 고아 프로세스가 원인일 때 가장 확실하다.
정리
이번 문제는 코드 자체의 문제라기보다는 실행 환경 문제에 가깝다.
특히 다음 조건이 겹치면 자주 발생할 수 있다.
plainTermux
+ Code-server
+ Code-server 내장 터미널
+ FastAPI/Uvicorn 서버
+ 8000번 포트
+ 정상 종료가 아닌 Code-server 강제 종료
해결 순서는 다음과 같다.
bash# 1. 이전 터미널 프로세스 확인
ps -A -o pid,ppid,tty,stat,args | grep 'pts/0'
# 2. 의심 PID 종료
kill -9 PID
# 3. 프로젝트 venv Python 전체 종료
pkill -9 -f "/data/data/com.termux/files/home/workspace/my-llm-project/back/venv/bin/python"
# 4. 다시 실행
python main.py
그래도 안 되면 Termux 앱 자체를 강제 종료하는 것이 가장 확실하다.