Flask로 구현한 API 서버 app.py를 도커 컨테이너에 띄워 실행시키고, 로컬에서 해당 API서버로 접근하는 방법을 정리했습니다.
"0.0.0.0"
으로 명시해야 합니다. 이는 동일한 네트워크 상에서 localhost가 아닌 서버의 IP로 접속을 가능하게 해줍니다.app.run(host='0.0.0.0', port=...)
docker run
을 실행할 때 -p
옵션을 통해 포트 포워딩을 할 수 있습니다. -p 1234:8888
은 호스트 기기의 1234번 포트를 도커 컨테이너의 8888번 포트와 연결한다는 의미입니다.로컬에 웹 서버를 호스팅하고 접근하는 것은 어렵지 않습니다.
python flask를 이용해 아주 간단한 웹 API 서버를 만들고 실행해봅시다!
# app.py
from flask import Flask
app = Flask(__name__)
host_addr = "0.0.0.0"
host_port = 5000
@app.route('/')
def hello():
return "try /ping!"
@app.route('/ping')
def ping():
return {'response': 'pong'}
if __name__ == "__main__":
app.run(debug=True,
host=host_addr,
port=host_port)
로컬 환경에서 위의 app.py를 실행하면 서버 호스팅이 시작됩니다.
서버 호스팅 시작
이 상태에서 웹 브라우저에서 http://localhost:5000 접근하면 다음 결과를 얻을 수 있습니다.
http://localhost:5000
이제 이 API서버를 도커 컨테이너 위에서 실행하려는데...
우선, Docker container를 어떤 환경으로 세팅할 것인지를 정해야합니다. 처음엔 도커 컨테이너에서 jupyter를 사용해야하나 싶어서 jupyter/minimal-notebook을 기반으로 컨테이너를 실행시켰습니다.
docker pull jupyter/minimal-notebook
docker run -p 8888:8888 jupyter/minimal-notebook
실행 후 다음과 같은 메시지를 확인할 수 있습니다.
http://127.0.0.1:8888/lab?token=... 주소를 복사하여 웹 브라우저에서 접속하면 JupyterLab 환경이 설치되어 있는 것을 확인할 수 있습니다.
그런데 생각해보니, 컨테이너 내에서 jupyter를 사용할 이유가 전혀 없어서 다른 도커 이미지를 사용하기로 했습니다.
docker pull python3.10.2-slim # 현재 사용중인 python 버전
이제 본격적으로 컨테이너를 구동하기 위해 Dockerfile을 작성합시다. Dockerfile은 특정 이미지를 베이스로 하여 컨테이너를 생성할 때 미리 명시한 동작들을 수행할 수 있도록 새로운 이미지를 만드는 파일입니다.
FROM python:3.10.2-slim
ADD . /app
WORKDIR /app
RUN pip install flask
ENTRYPOINT python app.py
비교적 간단하게 작성한 Dockerfile입니다. 한 줄씩 살펴보면,
FROM <이미지 이름>:<버전>
어떤 이미지를 기반으로 만들 것인지를 가장 위에 작성합니다. FROM python:3.10.2-slim
ADD <로컬 파일 이름/목록> <컨테이너 내에 저장할 위치>
로컬 디렉토리의 파일을 컨테이너의 특정 위치에 추가합니다. ADD . /app
는 docker run
을 실행하는 디렉토리 내의 모든 파일을, 컨테이너 내부의 /app 위치에 추가한다는 의미입니다. 컨테이너를 생성할 때 로컬 파일을 같이 띄우고 싶을 때 유용합니다.WORKDIR <작업 디렉토리>
컨테이너 내에서 작업할 디렉토리로 이동합니다.RUN <스크립트 명령어>
컨테이너를 생성할 때 실행할 명령어입니다. &&\와 줄바꿈을 통해 여러 개의 명령을 하나의 RUN
커맨드로 묶을 수 있습니다.ENTRYPOINT <스크립트 명령어>
컨테이너를 시작할 때 실행할 명령어입니다. RUN
은 컨테이너가 최초로 생성될 때만 실행되지만, ENTRYPOINT
는 컨테이너를 시작할 때마다 실행됩니다.RUN
에는 패키지 설치 명령을, ENTRYPOINT
에는 해당 컨테이너가 실행할 실행 파일(서버 등)을 명시합니다. Dockerfile을 만들었으면 이를 이용해 도커 이미지를 빌드합니다.
docker build <Dockerfile이 위치한 디렉토리> -t <이미지 이름>:<태그>
태그를 명시하지 않으면 알아서 :latest
(최신 버전) 태그를 붙입니다.
docker build . -t flask-image
이제 도커를 실행할 차례입니다!
docker run -d -p 8888:5000 <이미지>
-d
현재 터미널에 도커 터미널을 띄우지 않고 백그라운드로 컨테이너를 실행합니다.
-p <호스트 포트>:<컨테이너 포트>
포트 포워딩 옵션입니다. 위의 app.py는 5000번 포트로 서버를 열어두는데, 이를 컨테이너 환경에서 실행하기 때문에 컨테이너의 5000번 포트를 연결해야 컨테이너 내의 flask 서버에 접근할 수 있습니다.
Docker 컨테이너 실행 결과. Dockerfile 내의 ENTRYPOINT 덕분에 컨테이너 실행과 동시에 flask 서버가 구동되는 것을 확인할 수 있다.
Docker의 포트 바인딩때문에 몇 시간을 구글링하면서 끙끙댔습니다. 이 문제를 해결하면서 호스트 포트와 컨테이너 포트, 그리고 서버 프로그램에서 사용하는 포트를 어떻게 연결해야 하는지 감을 잡았습니다.
상황에 따라 필요한 이미지를 찾아 pull하고, 적절한 Dockerfile을 작성하여 컨테이너를 생성하는 방법을 배웠습니다.