본문 바로가기
프로그래밍

[docker] 도커를 통한 flask 실행

by choihyuunmin 2024. 5. 31.

Docker 컨테이너를 통한 Flask 앱과 nginx 연결을 실습해보겠습니다.

지난번에 올린 포스팅 Django + nginx와 같은 방법으로 실습을 진행합니다. Flask 컨테이너와 nginx 컨테이너를 각각 실행시켜 연결하는 방법 외에 docker-compose를 사용해서 실행하는 방법까지 진행해보겠습니다.

 

1. Flask 설치 및 api 생성

먼저 django와 마찬가지로 flask 라이브러리를 설치합니다. 파이썬 가상환경을 설치, 실행하고 해당 가상환경 안에서 flask를 설치하겠습니다.

저는 도커 실습을 위해 만들어놓은 가상환경에서 진행하도록 하겠습니다.

> cd django-docker
> source bin/activate
> which python
/Users/choi/Workspace/django-docker/bin/python
> which pip
/Users/choi/Workspace/django-docker/bin/pip
> pip install flask
Collecting flask
  Obtaining dependency information for flask from https://files.pythonhosted.org/packages/61/80/ffe1da13ad9300f87c93af113edd0638c75138c42a0994becfacac078c06/flask-3.0.3-py3-none-any.whl.metadata
  Downloading flask-3.0.3-py3-none-any.whl.metadata (3.2 kB)
Collecting Werkzeug>=3.0.0 (from flask)
  Obtaining dependency information for Werkzeug>=3.0.0 from https://files.pythonhosted.org/packages/9d/6e/e792999e816d19d7fcbfa94c730936750036d65656a76a5a688b57a656c4/werkzeug-3.0.3-py3-none-any.whl.metadata
  Downloading werkzeug-3.0.3-py3-none-any.whl.metadata (3.7 kB)
Collecting Jinja2>=3.1.2 (from flask)
  Obtaining dependency information for Jinja2>=3.1.2 from https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl.metadata
  Downloading jinja2-3.1.4-py3-none-any.whl.metadata (2.6 kB)
Collecting itsdangerous>=2.1.2 (from flask)
  Obtaining dependency information for itsdangerous>=2.1.2 from https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl.metadata
  Downloading itsdangerous-2.2.0-py3-none-any.whl.metadata (1.9 kB)
Collecting click>=8.1.3 (from flask)
  Obtaining dependency information for click>=8.1.3 from https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl.metadata
  Using cached click-8.1.7-py3-none-any.whl.metadata (3.0 kB)
Collecting blinker>=1.6.2 (from flask)
  Obtaining dependency information for blinker>=1.6.2 from https://files.pythonhosted.org/packages/bb/2a/10164ed1f31196a2f7f3799368a821765c62851ead0e630ab52b8e14b4d0/blinker-1.8.2-py3-none-any.whl.metadata
  Downloading blinker-1.8.2-py3-none-any.whl.metadata (1.6 kB)
Collecting MarkupSafe>=2.0 (from Jinja2>=3.1.2->flask)
  Obtaining dependency information for MarkupSafe>=2.0 from https://files.pythonhosted.org/packages/11/e7/291e55127bb2ae67c64d66cef01432b5933859dfb7d6949daa721b89d0b3/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl.metadata
  Downloading MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl.metadata (3.0 kB)
Downloading flask-3.0.3-py3-none-any.whl (101 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 101.7/101.7 kB 5.3 MB/s eta 0:00:00
Downloading blinker-1.8.2-py3-none-any.whl (9.5 kB)
Using cached click-8.1.7-py3-none-any.whl (97 kB)
Downloading itsdangerous-2.2.0-py3-none-any.whl (16 kB)
Downloading jinja2-3.1.4-py3-none-any.whl (133 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 133.3/133.3 kB 6.3 MB/s eta 0:00:00
Downloading werkzeug-3.0.3-py3-none-any.whl (227 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 227.3/227.3 kB 6.9 MB/s eta 0:00:00
Downloading MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl (18 kB)
Installing collected packages: MarkupSafe, itsdangerous, click, blinker, Werkzeug, Jinja2, flask
Successfully installed Jinja2-3.1.4 MarkupSafe-2.1.5 Werkzeug-3.0.3 blinker-1.8.2 click-8.1.7 flask-3.0.3 itsdangerous-2.2.0

[notice] A new release of pip is available: 23.2.1 -> 24.0
[notice] To update, run: pip install --upgrade pip

 

flask를 설치했으니 실행시켜보겠습니다.

flask를 실행하기 위해서는 endpoint를 정의하는 파이썬 코드를 작성해야합니다. 간단한 api를 작성해보겠습니다.

 

from flask import Flask

app = Flask(__name__)

@app.route('/')
def root():
    return 'hello world'


if __name__ == '__main__':
    app.run(host='0.0.0.0', port='8081')

 

0.0.0.0:8081 주소의 /(루트) 로 접근하는 클라이언트에게 hello world라는 문자열을 반환하는 간단한 코드입니다.

 

> python main.py
* Serving Flask app 'main'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:8081
 * Running on http://192.168.45.58:8081
Press CTRL+C to quit

 

python 명령어로 위에서 생성한 main.py 파일을 실행시키면 서버가 정상적으로 작동하게 됩니다.

웹브라우저로 요청을 보내보겠습니다.

localhost의 8081 포트로 접속하면 위에서 반환한 'hello world'가 웹브라우저에 나타나게 됩니다.

그리고 파일을 실행시킨 터미널에서는 아래와 같은 api 로그를 확인할 수 있습니다.

 

127.0.0.1 - - [03/Jun/2024 21:29:56] "GET / HTTP/1.1" 200 -

 

2. Flask 컨테이너 실행

위에서 만들었던 flask 앱을 도커 컨테이너 형태로 실행시키겠습니다.

우선 더 다양한 테스트를 위해 간단한 api endpoint를 추가하겠습니다.

from flask import Flask

app = Flask(__name__)

users = {
    "kim": '30',
    "park": '20',
    "choi": '25'
}
    

@app.route('/')
def root():
    return 'hello world'

@app.route('/items', methods=['GET'])
def get_all():
    return users

@app.route('/items/<name>', methods=['GET'])
def get(name):
    return users.get(name, "유저 없음")

if __name__ == '__main__':
    app.run(host='0.0.0.0', port='18081')

 

nginx와 연결해서 테스트할 flask는 두개의 endpoint를 추가했습니다. users라는 딕셔너리를 추가해서 클라이언트에서 확인할 수 있는 api를 만들었습니다. 로컬과 차이점을 두며 테스트하기 위해 포트번호도 8081에서 18081로 변경했습니다.

이제 Dockerfile을 생성하겠습니다.

FROM python:3.11.7

WORKDIR /usr/src/app

COPY . .

RUN python -m pip install --upgrade pip
RUN pip install -r requirements.txt

WORKDIR ./myapp

CMD gunicorn --bind 0.0.0.0:18081 main:app

EXPOSE 18081

 

로컬에서 실행하는 것과 차이를 두기 위해 연결을 위한 포트는 18081번으로 변경했습니다.

requirement.txt를 생성하고 Dockerfile을 통해 flask 이미지를 빌드하겠습니다.

 

> pip freeze > requirements.txt
> docker image build . -t flask
[+] Building 8.4s (11/11) FINISHED                                                                                                              docker:desktop-linux
 => [internal] load .dockerignore                                                                                                                               0.0s
 => => transferring context: 2B                                                                                                                                 0.0s
 => [internal] load build definition from Dockerfile                                                                                                            0.0s
 => => transferring dockerfile: 241B                                                                                                                            0.0s
 => [internal] load metadata for docker.io/library/python:3.11.7                                                                                                0.8s
 => [1/6] FROM docker.io/library/python:3.11.7@sha256:63bec515ae23ef6b4563d29e547e81c15d80bf41eff5969cb43d034d333b63b8                                          0.0s
 => [internal] load build context                                                                                                                               0.0s
 => => transferring context: 421B                                                                                                                               0.0s
 => CACHED [2/6] WORKDIR /usr/src/app                                                                                                                           0.0s
 => [3/6] COPY . .                                                                                                                                              0.0s
 => [4/6] RUN python -m pip install --upgrade pip                                                                                                               2.1s
 => [5/6] RUN pip install -r requirements.txt                                                                                                                   4.9s
 => [6/6] WORKDIR ./myapp                                                                                                                                       0.0s
 => exporting to image                                                                                                                                          0.5s
 => => exporting layers                                                                                                                                         0.5s
 => => writing image sha256:58d41850072a4dc21c9108a161175a52dc98bc8f8a5884d872fe84277745ba56                                                                    0.0s
 => => naming to docker.io/library/flask                                                                                                                        0.0s
 
> docker image ls
flask                                                     latest                                                                       58d41850072a   25 seconds ago   1.17GB

 

flask 이미지가 생성되었습니다. 이제 nginx와 연결해보도록 하겠습니다.

3. nginx 컨테이너 실행

여기서부터는 지난 포스팅의 django와 같습니다. nginx 설정파일을 생성해주고 설정파일 안에서 flask와 연결해주면 됩니다.

 

upstream myweb {
    server flask:18081;
}

server {
    listen 81;
    server_name localhost;

    location / {
        proxy_pass http://myweb;
    }
}

 

nginx 포트를 81번으로 설정하고 upstream은 위에서 flask의 포트를 18081로 변경했으니, 거기에 맞춰 설정 파일을 수정해줍니다.

 

> docker build . -t nginx
 => [internal] load build definition from Dockerfile                                                                                                            0.0s
 => => transferring dockerfile: 207B                                                                                                                            0.0s
 => [internal] load .dockerignore                                                                                                                               0.0s
 => => transferring context: 2B                                                                                                                                 0.0s
 => [internal] load metadata for docker.io/library/nginx:1.25.3                                                                                                 2.9s
 => [auth] library/nginx:pull token for registry-1.docker.io                                                                                                    0.0s
 => [1/3] FROM docker.io/library/nginx:1.25.3@sha256:c7a6ad68be85142c7fe1089e48faa1e7c7166a194caa9180ddea66345876b9d2                                           0.0s
 => [internal] load build context                                                                                                                               0.0s
 => => transferring context: 232B                                                                                                                               0.0s
 => CACHED [2/3] RUN rm /etc/nginx/conf.d/default.conf                                                                                                          0.0s
 => [3/3] COPY default.conf /etc/nginx/conf.d/                                                                                                                  0.0s
 => exporting to image                                                                                                                                          0.0s
 => => exporting layers                                                                                                                                         0.0s
 => => writing image sha256:f5321ebc877fc6d3c66eaee632a53ee021e4601d3dfb9b4c9746e66dce097e3c                                                                    0.0s
 => => naming to docker.io/library/nginx                                                                                                                        0.0s

 

flask와 nginx의 이미지를 모두 생성했습니다. 이제 flask와 nginx를 컨테이너를 실행하면서 연결해주겠습니다.

 

4. Flask + nginx 연결 및 접속 테스트

두 컨테이너를 연결하기 전에 먼저 네트워크를 생성하겠습니다.

> docker network create flaskapp
80bac38df911a086b9557ae2b7da8f9616a052eaa97f9b29fc83b0ad7b422958

 

생성된 네트워크를 통해 컨테이너를 실행시키고 묶어줍니다.

 

> docker container run -d --name flask --network flaskapp flask
9ce7aae75e1fcc3462a85f8091dfeebc30802f19d16027b696d97ec5816a5ce6
> docker container run -d --name nginx --network flaskapp -p 81:81 nginx
916392fe3b02f2bc21a3574b3f97098a7fefccedc74f19d217400da3f033c3f4

 

docker container ps 명령어로 실행중임을 확인한 후 브라우저를 통해 접속해보겠습니다.

 

변경한 flask의 코드도 적용된 것을 볼 수 있습니다.

이제 마지막으로 docker-compose를 통해 컨테이너를 실행시켜보겠습니다.

 

version: "3"

services:
  flask:
    build: ./docker
    networks:
      - flaskapp
    restart: always

  nginx:
    build: ./front
    networks:
      - flaskapp
    ports:
      - "81:81"
    depends_on:
      - flask
  
    restart: always

networks:
  flaskapp:

 

기존에 실행중이던 컨테이너와 이미지를 모두 삭제하고 docker-compose를 실행시키겠습니다.

 

> docker compose up -d --build
 ✔ Network api_flaskapp   Created                                                                                                                               0.0s
 ✔ Container api-flask-1  Started                                                                                                                               0.2s
 ✔ Container api-nginx-1  Started

 

정상적으로 실행된 것을 확인할 수 있습니다. 브라우저로 접속해보면 아까와 똑같은 결과가 반환됩니다.

compose up으로 생성된 이미지와 컨테이너는 compose down 명령어로 종료할 수 있습니다.

 

> docker compose down
[+] Running 3/3
 ✔ Container api-nginx-1  Removed                                                                                                                               0.1s
 ✔ Container api-flask-1  Removed                                                                                                                              10.1s
 ✔ Network api_flaskapp   Removed                                                                                                                               0.1s