FastAPI 개발 중에 귀신들린 이야기 (feat. VSCode)
FastAPI 개발 중 겪은 일
FastAPI를 활용한 새 웹 프로젝트를 개발하던 중 미스터리한 일을 겪었다. (스포일러: 귀신은 없었다.) 이번 프로젝트에서는 AWS를 사용해 프로덕션을 개발할 생각이었기에, 개발 환경은 AWS EC2로 세팅을 마쳤었다. Python 프로젝트는 여러 번 해 봤지만 FastAPI를 활용하는 경우는 처음이어서 간단한 데모 코드를 가지고 원리를 이해하고 있었다.
당시 개발 중이던 환경은 위 화면과 같았는데, Visual Studio Code에 Remote-SSH 익스텐션을 설치해서 원격으로 EC2 Instance에 접속하였었다.
사진이 작아서 잘 안 보이기는 하지만, 화면을 보면 당시 AWS EC2에 부여된 Public IP가 172.31.3.163
임을 알 수 있다.
FastAPI에서는 서버를 실행하기 위해 fastapi dev my-file.py
와 같은 명령을 실행하는데, 그 결과로 아래와 같은 출력을 얻을 수 있었다.
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
여기서 주목할 점은 FastAPI 서버가 http://127.0.0.1:8000
에 개방되었다는 정보이다.
EC2 보안그룹 정책을 통해 당시 8000번 포트는 접속을 개방한 상태였다.
따라서 내 생각으로는 Public IP가 172.31.3.163
이고, 8000번 포트가 개방되었으니 내 브라우저에서 http://172.31.3.163:8000
으로 접근하면 테스트 서버에 접속할 수 있겠다는 판단이었다.
귀신 들린 컴퓨터
그러나 뜻밖의 일이 일어났다. http://172.31.3.163:8000
은 Timeout이 뜨면서 접속이 되지 않았다.
왜 이러나 싶어서 VPC 설정이 잘못된 건지, 새 프로젝트를 시작하면서 새로 만든 서브넷의 설정이 망가진 건지, 라우팅 테이블은 멀쩡한지 다 뒤져봤다.
아무리 찾아도 문제가 해결되지 않던 중, 혹시나 하는 생각에 http://127.0.0.1:8000
로 접속을 시도해 봤더니…
여기서부터 뭔가 이상해졌다. 내가 설정한 건 SSH지 VPN이 아니었는데, 왜 localhost에서 EC2 인스턴스랑 연결이 되는 거지? 이때 별 생각을 다 하면서 구글링을 했는데, 너무 연관된 주제가 많다 보니 키워드 산정부터 어려움이 있었다. 당시 상황을 종합하면
- AWS EC2랑
- SSH로 연결해
- FastAPI Dev Server를
- Public IP로는 접속이 안 되지만
- Localhost에서는 접속이 됨
이었는데, 연결된 의존성이 많다 보니 검색 키워드도 제대로 나오지 않았다.
이 시점에서 FastAPI 배우기는 우선순위에서 밀려나 버렸고, 나는 지금 당장 이게 왜 작동하는지 알고 싶었다. 이를 해결하고자 이 이상현상의 잠재적 원인은 AWS, SSH, FastAPI 중 한 곳에 있을 것이라는 가설을 세우고 각각 검증하기로 결정했다.
문제의 원인 - 가설
AWS가 문제다?
AWS 설정을 처음부터 다시 다 확인했지만 이상은 없었고, 따지고 보면 AWS가 문제일 가능성은 낮았다. AWS가 잘못되었다면 연결이 아예 안 되었으면 안 되었지, localhost에서 연결이 되는 건 AWS로 설명하기 힘들었다.
SSH가 문제다?
SSH가 문제가 있을 가능성이 높아 보였다. 어찌됐든 Remote와 Local을 잇는 거의 직접적인 bridge이기 때문이었다. 그러나 어떻게 연결이 SSH 밖으로 “샐” 수 있는지는 미지수였고, 따라서 FastAPI와 연관이 있을 것이라고 생각했다.
FastAPI가 문제다?
아무래도 FastAPI 자체에 뭔가 기능이 있는 것 같았다.
이때 내가 세운 가설은 FastAPI의 fastapi dev my-file.py
를 실행하는 과정에서 현재 환경이 SSH인지 아닌지를 판단하여, SSH일 경우 자동으로 추가 연결을 수립하는 숨겨진 매커니즘이 있을 것이라고 생각했다.
UVicorn이 문제다?
FastAPI Document를 뒤지던 중 FastAPI의 서버 구현은 UVicorn이라는 오픈소스에 의존한다는 것을 알게 되었다. 이떄 Uvicorn SSH와 같은 키워드로 검색을 해 보았지만, SSH 환경일 경우에만 발동하는 특별 기능이 있다는 말은 없었다.
뜻밖의 장소에서 얻어낸 힌트
여기까지 오도록 문제가 해결될 기미는 안 보였는데, 그러던 중 우연히 실마리를 하나 찾게 된다.
검색 중에 Visual Studio Code가 아니라 Git Bash로 SSH에 접속해 fastapi dev my-file.py
를 실행했더니, 더 이상 localhost:8000
으로 웹페이지에 접속할 수 없었다.
이상함을 느끼고 다시 Visual Studio Code로 SSH를 연결했더니 이번에는 접속이 되었다.
이 상황을 실마리 삼아 vscode ssh remote localhost
라고 구글에 검색해보게 되었고, 이 시점에서 문제의 원인이 명확해졌다.
문제의 원인 - 정답
2021년에 나와 같은 문제를 겪고 있는 사람이 작성한 블로그에서 정답을 알 수 있었다. Vscode에서 SSH로 접속할 때는 그냥 접속하지 않고 Remote-SSH라는 익스텐션을 사용한다. 이 플러그인에는 Port Forwarding이라는 기능이 내장되어 있는데, 원격 서버의 Port와 로컬 머신의 Port를 바인딩해 주는 기능이다. 사실 기본 SSH에서도 이 기능이 있는데, 가장 큰 차이점은 Remote-SSH는 자동으로 Port를 탐지해 Port forwarding을 수행해 준다.
SSH를 평소에 엄청나게 자주 쓰기 때문에 이런 기능이 존재한다는 것은 들었던 적이 있었다. 하지만 지금까지는 이 기능을 굳이 쓸 일이 없었기에 잊고 있었던 데다, 무엇보다도 Vscode가 자동으로 Port forwarding을 할 줄은 몰랐었다.
Microsoft 개발자가 직접 남긴 구현 코멘트에 따르면 해당 익스텐션은 설정에 따라 두 가지 방법으로 자동으로 포트를 탐지한다.
- 현재 실행 중인 프로세스 중, 열린 포트를 가진 프로세스를 탐지
- 터미널 출력에서 URL + 포트 형식을 가진 출력이 있는지 탐지
실제로 이 사실을 알고 다시 Vscode를 확인해 보니 아래와 같은 탭을 발견할 수 있었다.
사실, 이 문제는 두 가지 문제가 결합한 문제였다. localhost에서 접속이 되는 것도 문제지만, Public IP를 통해서는 접속할 수 없었던 것을 기억하는가? FastAPI dev 서버에서 Public IP를 통해 접속을 허용하려면 사실은 아래와 같은 옵션을 주어야 했다.
fastapi dev my-file.py --host 0.0.0.0
이 옵션을 주고 실행하면, (보안 설정이 문제 없다는 가정 하에) Public IP를 사용해 http://172.31.3.163:8000
로도 접근할 수 있다. ■