소개
Docker는 오픈 소스 컨테이너 플랫폼입니다. 이는 물리적 호스트 머신에서 실행되는 다른 소프트웨어와 격리되어 소프트웨어가 실행될 수 있도록 하는 가볍고 가상화된 이식 가능한 소프트웨어 정의 표준화 환경입니다. Docker는 가상 머신에 대한 가벼운 대안을 제공합니다. 동시에 애플리케이션의 이식성, 성능, 민첩성 및 확장성을 제공합니다. 포괄적인 Docker 생태계에 대한 가이드를 보려면 Docker를 사용한 컨테이너화에 대한 자세한 개요를 살펴보세요..
Flask는 다음으로 빌드된 오픈 소스 미니멀 웹 프레임워크입니다: Python. Flask의 훌륭한 특징 중 일부는 가볍고 유연하며 고도로 구조화되어 있다는 점입니다. 또한 실행하는 데 특정 도구나 플러그인이 필요하지 않습니다.
Flask와 Docker를 결합하면 가볍고 유연하며 확장 가능한 애플리케이션을 얻을 수 있습니다. Docker화된 컨테이너의 이식성 덕분에 여러 서버와 인프라에 배포할 수 있습니다. 이 튜토리얼의 초점은 Docker를 사용하여 Flask 애플리케이션을 배포하는 방법을 보여주는 것입니다. 또한 애플리케이션의 향후 업데이트가 제대로 적용되는지 확인하는 방법도 시연합니다.
전제 조건
이 튜토리얼은 실습형 튜토리얼이므로, 따라 할 수 있는 환경을 만들어야 합니다:
- 다음이 필요합니다: Ubuntu 20.04 설치가 초기 운영 환경으로 필요합니다. 또한 다음을 생성해야 합니다: sudo 권한이 있는 non-root 사용자.
- 또한 Docker를 설치해야 합니다. 다음 주제에 대한 튜토리얼이 있습니다: Ubuntu에 Docker를 설치하고 작동하는 방법. 1, 2, 3, 4단계를 따르세요. 이는 모든 Ubuntu 배포판에서 작동할 것입니다.
- 마지막으로, 다음이 설치되어 있어야 합니다: Nginx. 다음 튜토리얼을 따르세요: Ubuntu에 Nginx 설치하기.
이제 시작하겠습니다!
1단계: Flask 애플리케이션 준비
먼저 Flask 애플리케이션을 담을 디렉터리를 생성하는 것부터 시작하겠습니다. 원하는 디렉터리 이름을 선택할 수 있습니다. 하지만 이 튜토리얼에서는 다음과 같이 이름을 지정하겠습니다. flask_demo. 프로젝트 파일은 다음 디렉터리 내에 저장할 것입니다. /var/www 디렉터리이며, 이 디렉터리는 일반적으로 Ubuntu가 기본적으로 공용 인터넷에 대한 액세스를 허용하는 디렉터리입니다. 먼저 다음 명령을 실행하여 디렉터리를 생성하고 해당 디렉터리로 이동합니다:
|
1 2 3 |
sudo mkdir /var/www/flask_demo cd /var/www/flask_demo |
프로젝트의 이 루트 디렉터리 내에 Flask 애플리케이션의 기본 폴더 구조를 생성합니다. 다음으로, 다음 명령을 실행하여 기본 구조를 생성하고, 도중에 모든 상위 폴더를 생성하기 위해 -p 플래그를 추가합니다:
|
1 |
sudo mkdir -p app/static app/templates |
app 폴더에는 다음을 포함하여 Flask 앱과 관련된 모든 파일이 보관됩니다: views 및 blueprints. Views에는 애플리케이션에 도달하는 요청에 응답하기 위해 작성하는 코드가 포함되어 있습니다. Blueprints는 애플리케이션 구성 요소를 만들고 Flask 애플리케이션의 일반적인 패턴을 지원하는 데 도움이 됩니다.
이름에 걸맞게 static 폴더에는 이미지, CSS 및 JavaScript 파일과 같은 정적 자산이 보관됩니다. templates 디렉터리에는 프로젝트의 모든 HTML 템플릿이 보관됩니다.
이제 Flask 애플리케이션을 초기화하는 데 필요한 파일을 작성하기 시작할 수 있습니다. 먼저 다음 이름의 파일을 생성합니다. __init__.py 파일을 app 디렉터리 내에 생성하여 Python 인터프리터에 app 디렉터리가 패키지로 취급되어야 함을 알립니다. 터미널에서 다음 명령을 실행하여 nano 편집기로 파일을 엽니다:
|
1 |
sudo nano app/__init__.py |
Python에서는 패키지를 사용하여 모듈을 논리적 네임스페이스 또는 계층 구조로 그룹화합니다. 모듈화를 통해 코드를 특정 기능을 수행하는 개별적이고 관리하기 쉬운 블록으로 나눌 수 있습니다.
그 후, 편집기에 열려 있는 __init__.py 파일 내에 다음 코드 스니펫을 추가하여 Flask 인스턴스를 시작하고, 다음 단계에서 생성할 views.py에서 로직을 가져옵니다:
|
1 2 3 4 5 |
from flask import Flask app = Flask(__name__) from app import views |
완료되면 Ctrl + O 및 ENTER 파일을 저장한 다음, 다음 키로 닫습니다. Ctrl + X. 다음으로, views.py 파일을 app 디렉터리 안에 생성해 보겠습니다. views.py 파일에는 애플리케이션 로직의 대부분이 포함됩니다:
|
1 |
sudo nano app/views.py |
파일 안에 다음 코드 스니펫을 추가합니다. 이 코드는 사용자가 웹사이트를 방문할 때 앱이 실행 중임을 보여주는 간단한 문자열을 표시합니다:
|
1 2 3 4 5 |
from app import app @app.route('/') def home(): return "Our Flask application is running!" |
이 파일에서는 먼저 Flask 앱 인스턴스를 가져오는 것으로 시작합니다. 그런 다음 경로를 정의하는 줄을 추가해야 합니다: @app.route(/). @app.route(/) 줄은 Flask에서 데코레이터(decorator)라고 합니다. 데코레이터를 사용하여 하나 이상의 함수에 추가 기능을 주입할 수 있습니다. 이 경우, 경로 /에 대한 호출을 home 함수로 전달합니다. 사용자가 이 경로를 방문하면 다음 텍스트가 표시됩니다: "Our Flask application is running!".
다음으로, 애플리케이션의 uwsgi.ini 설정을 저장할 uWSGI 파일을 생성합니다. uWSGI는 Nginx용 배포 옵션으로, 프로토콜 및 애플리케이션 서버 역할을 합니다. 다음 명령을 실행하여 nano 편집기로 프로젝트의 루트 디렉터리에 파일을 생성합니다:
|
1 |
sudo nano uwsgi.ini |
열린 파일 안에 다음 코드 스니펫을 추가합니다:
|
1 2 3 4 |
[uwsgi] module = main callable = app master = true |
이 파일에는 몇 가지 지시문이 포함되어 있습니다. 그 목적은 아래와 같습니다:
- module – Flask 애플리케이션이 서비스될 모듈을 정의합니다. 모듈을 main으로 설정하여 루트 디렉터리의 main.py 파일을 참조하도록 했습니다. 다음 단계에서 이 파일을 생성할 것입니다.
- callable – uWSGI가 애플리케이션에서 내보낸 app 인스턴스를 사용하도록 지시합니다.
- master – 전체 애플리케이션을 다시 로드하는 동안 다운타임을 최소화하기 위해 애플리케이션이 계속 실행되도록 보장합니다.
완료되면 파일을 저장하고 닫습니다.
이제 애플리케이션의 진입점(entry point)을 결정할 main.py 파일을 생성할 수 있습니다. uWSGI 는 이 파일을 읽어 애플리케이션과 상호 작용하는 방법을 파악합니다. 다음 명령을 실행하여 프로젝트의 main.py 디렉터리 내에 nano로 root 디렉터리 파일을 생성합니다:
|
1 |
sudo nano main.py |
파일 안에 애플리케이션 패키지에서 생성된 Flask 인스턴스를 가져오는 다음 줄을 추가합니다:
|
1 |
from app import app |
이 단계에서 마지막으로 할 일은 애플리케이션 실행에 필요한 종속성을 정의하는 것입니다. 이러한 종속성은 dependencies.txt라는 파일 안에 정의할 것입니다. Docker가 애플리케이션의 이미지를 빌드할 때, 종속성을 설치하기 위해 pip (패키지 관리자) 명령을 실행합니다. 다음 명령을 사용하여 루트 디렉터리에서 파일을 엽니다:
|
1 |
sudo nano dependencies.txt |
우리 프로젝트의 이 시점까지는 단 하나의 종속성만 필요합니다: Flask. 따라서 프로젝트에 원하는 올바른 버전의 Flask를 참조하도록 다음 줄을 추가할 수 있습니다:
|
1 |
Flask==2.0.1 |
우리는 종속성으로 Flask 버전 2.0.1을 사용하기로 결정했습니다. 이 튜토리얼을 작성하는 시점의 최신 버전입니다. 다양한 버전에 대한 자세한 내용은 Flask Changes 페이지에서 확인할 수 있습니다. 이것으로 Flask 애플리케이션 설정이 완료되었습니다. 이제 배포를 위한 Docker 설정을 준비해 보겠습니다.
Step 2: Configure Docker
Docker 배포를 설정하기 위해 두 개의 파일, 즉 Dockerfile 및 start.sh을 생성합니다. Dockerfile에는 Docker 이미지를 구성하는 선언적 줄이 포함되어 있습니다. start.sh은 이미지를 빌드하고 Dockerfile에서 컨테이너를 시작하는 기본 셸 스크립트입니다. 프로젝트의 루트 디렉터리에 있는 동안 다음 명령을 실행하여 Dockerfile:
|
1 |
sudo nano Dockerfile |
이 파일에는 Docker 이미지에 필요한 구성이 포함되어 있습니다. 다음으로, 종속성과 이미지를 빌드하는 방법을 지정하는 다음 코드 스니펫을 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 |
FROM tiangolo/uwsgi-nginx-flask:python3.6-alpine3.7 RUN apk --update add bash nano git ENV STATIC_URL /static ENV STATIC_PATH /var/www/app/static COPY ./dependencies.txt /var/www/dependencies.txt RUN pip install -r /var/www/dependencies.txt |
다음 파일의 첫 번째 줄은: Dockerfile이며, 이는 우리가 이미지를 빌드할 기준이 되는 베이스 이미지를 정의합니다. 이 경우, 우리는 다음을 기반으로 빌드할 것입니다: tiangolo/uwsgi-nginx-flask, 사용 가능한 곳: DockerHub. 여러 Python 버전을 지원하기 때문에 이 특정 이미지를 선택했습니다.
또한 이미지를 업데이트하도록 지정합니다. 그런 다음, 다음을 추가해야 합니다: bash 명령어 프로세서 , 그리고 nano 텍스트 편집기, 그리고 git 클라이언트 (다음과 같은 버전 관리 저장소에서 소스 코드를 가져오고 보내기 위한 용도): GitHub, Bitbucket, 또는 Gitlab. ENV가 포함된 줄은 컨테이너에서 사용할 환경 변수를 지정합니다.
The COPY 명령은 종속성을 컨테이너로 복사합니다. RUN 명령은 pip 패키지 관리자를 호출하여 dependencies.txt 파일을 파싱하고 종속성을 설치합니다. 편집이 끝나면 파일을 저장하고 닫으십시오.
다음으로, start.sh 스크립트를 생성합니다. 이 스크립트에는 이미지를 빌드하고 실행하는 Docker 명령이 포함됩니다. 터미널에서 이러한 명령을 점진적으로 실행할 수도 있지만, 쉘 스크립트에 추가하고 터미널에서 하나의 명령으로 호출하는 것이 더 깔끔하다고 생각했습니다.
이 파일의 내용을 정의하기 전에, 먼저 다른 서비스가 사용하지 않는 빈 포트를 확인해야 합니다. 우리는 45644 포트를 사용할 것입니다. 하지만 다른 포트를 선택할 수도 있습니다. 다음 줄을 실행하여 포트가 비어 있는지 확인하십시오:
|
1 |
sudo nc localhost 45644 < /dev/null; echo $? |
선택한 포트에 따라, 위 명령의 출력이 1이면 비어 있는 것입니다. 그렇지 않으면 다른 포트를 선택하고 명령을 다시 시도해야 할 수 있습니다:

빈 포트를 확인했으므로, 이제 다음 명령을 실행하여 프로젝트의 루트 디렉터리 내에 nano로 파일을 생성할 수 있습니다:
|
1 |
sudo nano start.sh |
이 파일 안에 다음 코드 스니펫을 추가합니다:
|
1 2 3 4 5 6 7 |
#!/bin/bash app_name="docker-flask-demo" docker build -t ${app_name} . docker run -d -p 45644:80 --name=${app_name} -v $PWD:/app ${app_name} |
첫 번째 줄은 shebang이라 불리며, 이것이 bash 파일이고 명령어로 실행되어야 함을 지정합니다. 두 번째 줄은 app_name라는 변수를 선언합니다. 우리는 이 변수를 사용하여 이미지 및 컨테이너 이름을 설정합니다. 세 번째 줄은 Docker에게 build 명령을 사용하여 현재 디렉터리의 Dockerfile 정의를 기반으로 이미지를 빌드하도록 지시합니다. 이미지 이름은 변수에 따라 docker-flask-demo가 됩니다.
마지막 줄은 docker-flask-demo라는 이름의 컨테이너를 우리가 정의한 변수에 따라 생성합니다. -d 플래그는 명령 실행이 끝난 후에도 컨테이너가 백그라운드에서 분리된(detached) 상태로 계속 실행되도록 유지합니다. -p 플래그는 서버의 포트를 컨테이너의 특정 포트에 바인딩합니다. 이 경우, 호스트 머신의 45644 포트를 Docker가 컨테이너에서 노출할 80 포트에 마운트합니다.
우리는 -v 플래그를 사용하여 컨테이너에 마운트할 Docker 볼륨을 지정합니다. $PWD 변수는 특정 시점에 사용자가 위치한 현재 디렉터리의 경로를 보유하는 기본 Linux 변수입니다:

우리의 경우, 전체 프로젝트 디렉터리를 컨테이너의 /var/www 디렉터리에 마운트하고 있습니다. 이제 Docker 설정이 완료되었습니다. 다음 명령을 실행하여 이미지를 빌드하고 빌드된 이미지를 기반으로 컨테이너를 시작할 수 있습니다:
|
1 |
sudo bash start.sh |
스크립트 실행이 완료될 때까지 기다린 다음, 다음 Docker 명령을 실행하여 실행 중인 모든 컨테이너를 나열합니다:
|
1 |
sudo docker ps |
출력에 실행 중인 컨테이너가 표시됩니다:

실행 중인 컨테이너 목록에서 이름이 docker-flask-demo 인 컨테이너가 표시되어야 합니다. 서버의 공인 IP 주소를 찾아 브라우저에서 지정된 포트로 접속하세요: http://your-server-public-ip:45644.
다음과 유사한 출력이 표시되어야 합니다:

브라우저에 위와 같이 표시된다면 Flask 애플리케이션이 성공적으로 배포된 것입니다. 다음으로, 파일을 수정하고 템플릿을 통해 사용자에게 콘텐츠를 제공해 보겠습니다.
3단계: 템플릿 파일을 통해 콘텐츠 제공하기
Flask에서 템플릿은 웹사이트 방문자에게 정적 및 동적 콘텐츠를 표시하는 데 사용됩니다. 사용자에게 제공할 HTML 템플릿을 생성하고 사용자가 특정 경로를 방문할 때 이를 제공하는 방법을 보여드리겠습니다. 예를 들어, 이는 홈 페이지나 소개(About) 페이지가 될 수 있습니다.
터미널에서 다음 명령을 실행하여 index.html 파일을 app/templates 디렉터리에 생성합니다:
|
1 |
sudo nano app/templates/index.html |
그런 다음 파일에 다음 코드 스니펫을 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Flask Demo</title> </head> <body> <h2>Your are Home</h2> <p>Welcome to the Flask with Docker Demo Page</p> </body> </html> |
완료되면 파일을 저장하고 닫습니다. 또한 다음 명령을 사용하여 소개(About) 페이지라고 부를 다른 페이지를 생성합니다:
|
1 |
sudo nano app/templates/about.html |
파일에 다음 코드 스니펫을 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>소개 Flask Demo</title> </head> <body> <h2>소개 Page</h2> <p>This was a demo project. It shows how to build a Flask app with Docker and Nginx.</p> <p>You can add as many pages and files as you like</p> </body> </html> |
완료되면 파일을 저장하고 닫습니다. 다음으로, 템플릿과 실제 페이지의 경로를 참조하도록 app/views.py 파일을 수정합니다:
|
1 |
sudo nano app/views.py |
파일이 다음과 같이 보이도록 수정합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from flask import render_template from app import app @app.route('/') def home(): return "Our Flask application is running!" @app.route('/index') def index(): return render_template('index.html') @app.route('/about') def about(): return render_template('about.html') |
작업을 마쳤으면 파일을 저장하고 닫습니다. 변경한 사항은 컨테이너를 중지하고 다시 시작할 때까지 적용되지 않습니다. 다음 Docker 명령을 실행하여 컨테이너를 중지하고 시작하십시오. 앞서 정의한 컨테이너 이름을 기록해 두십시오:
|
1 |
sudo docker stop docker-flask-demo && sudo docker start docker-flask-demo |
컨테이너가 실행되면 홈 페이지와 소개(About) 페이지를 방문하여 새로운 콘텐츠 중 일부를 확인하십시오:
|
1 |
Home page: http://your-server-public-ip:45644/index |

|
1 |
소개 page: http://your-server-public-ip:45644/about |

지금까지 웹사이트 방문자에게 콘텐츠를 제공할 수 있는 Flask 애플리케이션을 만들었습니다. 프로젝트의 파일 구조는 다음과 같습니다:

새로운 변경 사항을 적용하려면 Docker 컨테이너를 다시 시작해야 한다는 점을 눈치채셨을 것입니다. 다음 단계에서는 다운타임을 줄이기 위해 이를 자동화할 것입니다.
4단계: 애플리케이션 파일 업데이트가 자동으로 다시 로드되도록 구성
우리는 로직이나 사용자 인터페이스를 개선하거나 일부 종속성을 추가하기 위해 수시로 애플리케이션을 변경합니다. 이러한 변경 사항이 적용되려면 Docker 컨테이너를 다시 시작해야 할 수 있습니다. 다행히도, uWSGI 에는 컨테이너를 다시 시작하지 않고 Python 스크립트를 다시 로드하기 위한 touch-reload 기능이 있습니다.
기본적으로 Python에는 전체 파일 시스템의 변경 사항을 감시하고 변경이 발생할 때 애플리케이션을 새로 고치는 auto-reloading 기능이 있습니다. 자동 재로드는 다운타임을 최소화하는 데 좋지만 리소스를 많이 소모할 수 있습니다. 따라서 프로덕션 환경에는 권장되지 않습니다.
특정 파일의 변경 사항을 감시하고 변경 사항이 있을 때 애플리케이션을 다시 로드하기 위해 어떻게 touch-reload를 사용할 수 있는지 알아보겠습니다. nano 편집기로 uwsgi.ini 파일을 수정합니다:
|
1 |
sudo nano uwsgi.ini |
강조 표시된 줄을 추가하여 다음과 같이 만듭니다:
|
1 2 3 4 5 |
[uwsgi] module = main callable = app master = true touch-reload = /app/uwsgi.ini |
작업을 마쳤으면 파일을 저장하고 닫습니다. 추가된 줄은 애플리케이션의 재로드를 트리거하기 위해 수정될 파일을 지정했습니다. 그러나 향후 수정 시 이 조건이 활성화되려면 먼저 컨테이너를 다시 시작해야 합니다:
|
1 |
sudo docker stop docker-flask-demo && sudo docker start docker-flask-demo |
이제 app/views.py 파일을 수정하여 자동 재로드가 어떻게 작동하는지 시연해 보겠습니다:
|
1 |
sudo nano app/views.py |
home 함수가 반환하는 문자열을 강조 표시된 대로 변경합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from flask import render_template from app import app @app.route('/') def home(): return "<h3>자동 재로드를 위해 Flask 애플리케이션에 몇 가지 변경 사항을 적용했습니다!</h3>" @app.route('/index') def index(): return render_template('index.html') @app.route('/about') def about(): return render_template('about.html') |
완료되면 파일을 저장하고 닫습니다.
브라우저에서 애플리케이션의 홈 페이지를 엽니다: http://your-server-public-ip:45644.
아직 변경 사항이 표시되지 않습니다. 이는 touch-reload 조건이 uwsgi.ini 파일의 변경 사항을 감시하기 때문입니다. touch 를 사용하여 조건을 활성화함으로써 다음 명령으로 전체 애플리케이션을 다시 로드할 수 있습니다:
|
1 |
sudo touch uwsgi.ini |
이제 홈 페이지를 새로 고침하면 새로운 변경 사항이 표시되는 것을 확인할 수 있습니다:

앞으로 변경 사항이 있을 경우 다음 명령만 실행하면 됩니다. sudo touch uwsgi.ini 그리고 전체 애플리케이션이 더 적은 다운타임으로 다시 로드됩니다. 이것으로 이번 튜토리얼을 마칩니다.
결론
이 튜토리얼에서는 Docker 이미지와 컨테이너를 사용하여 Flask 애플리케이션을 구현하고 배포했습니다. 컨테이너를 다시 시작할 필요가 없도록 하여 다운타임을 최소화하기 위해 귀하는 touch-reload가 특정 파일의 변경 사항을 감지하고 전체 애플리케이션을 자동으로 다시 로드하도록 설정했습니다. 마지막으로 브라우저에서 이 모든 것을 테스트하여 제대로 작동하는지 확인했습니다.
Docker는 더 빠른 배포를 보장하고 애플리케이션을 쉽게 확장할 수 있도록 합니다. 다양한 Docker 명령어에 대해 자세히 알아보려면 다음 튜토리얼을 참조하세요. Ubuntu에서 Docker를 설치하고 사용하는 방법.
Docker에 관한 더 많은 리소스를 저희 블로그에서 확인하시려면, 다음을 참고해 보세요:
- 컨테이너화 기술: CloudSigma PaaS 플랫폼에서의 다양한 컨테이너 유형 및 용도
- Docker 컨테이너와 호스트 간에 데이터를 공유하는 방법
- CentOS 7에 Docker 설치 및 설정하기
- Docker Compose를 사용하여 Laravel, Nginx 및 MySQL 배포하기
- Docker 리소스 정리하기 – 이미지, 컨테이너 및 볼륨
즐거운 컴퓨팅 되세요!
댓글
아직 댓글이 없습니다. 첫 번째로 작성해 보세요.