
클로드(Claude)와 쳇지피티(ChatGPT)로부터 얻은 답변을 바탕으로, Docker, Apache, Gunicorn을 사용하여 Flask 애플리케이션을 배포하는 방법을 단계별로 설명합니다. 동작에는 성공했지만 보통 쓰는 방법과 다른 점도 있을듯합니다.
최초작성 2025. 3. 6
각 구성 요소는 다음과 같은 역할을 합니다:
Docker
Flask 앱과 그 실행에 필요한 모든 의존성을 하나의 컨테이너(상자)에 패키징하여, 어떤 환경에서도 동일하게 실행할 수 있도록 도와줍니다. 이를 통해 환경 간의 차이로 인한 문제를 줄이고 배포를 단순화합니다.
Apache
Apache는 사용자가 웹 서비스에 접속할 때 가장 먼저 만나는 웹 서버로, 클라이언트의 요청을 받아 안전하게 처리하거나 Flask 애플리케이션(또는 Gunicorn)에 요청을 전달하는 리버스 프록시 역할을 수행합니다.
Gunicorn
Gunicorn은 Flask 앱을 실제로 구동하는 WSGI 서버입니다. 여러 개의 워커(worker)를 사용하여 동시 다발적인 요청을 효율적으로 처리할 수 있게 해줍니다.
전체적인 작동 흐름은 아래와 같습니다:
클라이언트 요청 → Apache (리버스 프록시) → Gunicorn (WSGI 서버) → Flask 애플리케이션
.
VirtualBox에 Ubuntu 22.04를 설치한 환경에서 진행했습니다.
설치 방법은 다음 포스트를 참고하세요.
Windows에 설치된 VirtualBox를 사용하여 Ubuntu 설치하기
https://webnautes.tistory.com/2118
이제 Docker, Apache, Gunicorn을 설치해봅니다.
사전 준비작업으로 우분투에 설치되어 있는 패키지를 업데이트 해줍니다.
sudo apt-get update && sudo apt-get upgrade -y |
도커 설치
1. 도커를 사용하기 위해 필요한 패키지를 설치합니다.
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release |
공식 Docker 저장소의 패키지 무결성을 검증하기 위해 GPG 키를 추가하고, Ubuntu에 Docker 저장소 정보를 등록합니다.
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null |
2. Docker 패키지를 설치하고, 데몬을 시작 및 부팅 시 자동 시작하도록 설정합니다.
또한, 현재 사용자를 Docker 그룹에 추가하여 루트 권한 없이 Docker 명령을 사용할 수 있게 합니다.
# Docker 설치 sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io # Docker 서비스 시작 및 자동 실행 설정 sudo systemctl start docker sudo systemctl enable docker # 현재 사용자를 docker 그룹에 추가 sudo usermod -aG docker $USER newgrp docker |
3. Docker 설치를 확인합니다.
docker run hello-world 명령을 실행하여 Docker 컨테이너가 제대로 실행되는지 테스트합니다.
이 과정에서는 Docker 클라이언트가 데몬에 요청하고, 데몬이 Docker Hub에서 "hello-world" 이미지를 가져와 컨테이너를 생성 및 실행하는 과정을 거칩니다.
docker run hello-world |
문제 없다면 다음과 같은 로그가 출력됩니다. 중간에 노란색으로 표시한 Hello from Docker! 메시지가 출력됩니다.
Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world e6590344b1a5: Pull complete Digest: sha256:bfbb0cc14f13f9ed1ae86abc2b9f11181dc50d779807ed3a3c5e55a6936dbdd5 Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/ |
Apache 설치
Apache는 외부 클라이언트의 요청을 받아 내부 애플리케이션(Flask)을 호출하는 리버스 프록시 역할을 합니다.
1. Apache를 설치합니다.
sudo apt-get install -y apache2 apache2-dev |
2. 리버스 프록시 기능을 사용하기 위해 proxy와 proxy_http 모듈을 활성화합니다.
sudo a2enmod proxy sudo a2enmod proxy_http |
3. 새로운 사이트 설정 파일을 만들어 Apache가 Flask 애플리케이션으로 요청을 전달하도록 구성합니다.
/etc/apache2/sites-available/file-share.conf 파일 생성하여
sudo nano /etc/apache2/sites-available/file-share.conf |
다음 내용을 추가합니다. nano 편집기를 사용했다면 Ctrl + O를 눌러 저장하고 Ctrl + X를 눌러 nano 편집기에서 빠져나오면 됩니다.
<VirtualHost *:80> ServerName localhost ServerAdmin webmaster@localhost DocumentRoot /var/www/html ProxyPass / http://127.0.0.1:8000/ ProxyPassReverse / http://127.0.0.1:8000/ ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost> |
ProxyPass, ProxyPassReverse: Apache가 들어오는 요청을 내부의 Gunicorn 서버(127.0.0.1:8000)로 전달하도록 설정합니다.
로그 설정: 에러 및 접근 로그를 지정하여 추후 문제 발생 시 디버깅에 도움을 줍니다.
4. 새 설정 파일을 활성화하고 기본 사이트 설정을 비활성화한 후 Apache를 재시작합니다.
sudo a2ensite file-share.conf sudo a2dissite 000-default.conf sudo systemctl restart apache2 |
5. Apache 서비스 상태와 설정 테스트를 통해 올바르게 동작하는지 확인합니다.
아파치 서비스 상태 확인을 합니다.
sudo systemctl status apache2 |
문제 없으면 다음처럼 보입니다. q를 누르면 터미널로 빠져나옵니다.
webnautes@webnautes-VirtualBox:~$ sudo systemctl status apache2 ● apache2.service - The Apache HTTP Server Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor prese> Active: active (running) since Mon 2025-03-03 22:29:22 KST; 8s ago Docs: https://httpd.apache.org/docs/2.4/ Process: 5923 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SU> Main PID: 5930 (apache2) Tasks: 55 (limit: 19099) Memory: 5.6M CPU: 42ms CGroup: /system.slice/apache2.service ├─5930 /usr/sbin/apache2 -k start ├─5931 /usr/sbin/apache2 -k start └─5932 /usr/sbin/apache2 -k start 3월 03 22:29:22 webnautes-VirtualBox systemd[1]: Starting The Apache HTTP Serv> 3월 03 22:29:22 webnautes-VirtualBox apachectl[5929]: AH00558: apache2: Could > 3월 03 22:29:22 webnautes-VirtualBox systemd[1]: Started The Apache HTTP Serve> |
아파치 설정을 테스트합니다.
sudo apache2ctl configtest |
다음처럼 보이면 문제 없다 보면 됩니다. 마지막줄에 Syntax OK 메시지가 출력되어 있습니다.
webnautes@webnautes-VirtualBox:~$ sudo apache2ctl configtest AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message Syntax OK |
Python 및 Gunicorn 설치
1. Python 가상환경 설정프로젝트 디렉토리를 생성하고, Python 가상환경을 만들어 활성화합니다.
# 필요한 패키지 설치 sudo apt-get install -y python3-venv # 프로젝트 디렉토리 생성 mkdir ~/myapp cd ~/myapp # 가상환경 생성 및 활성화 python3 -m venv venv source venv/bin/activate |
webnautes@webnautes-VirtualBox:~/myapp$ python3 -m venv venv
webnautes@webnautes-VirtualBox:~/myapp$ source venv/bin/activate
(venv) webnautes@webnautes-VirtualBox:~/myapp$
2. 가상환경 안에서 Flask 애플리케이션과 Gunicorn을 설치합니다.
pip install flask gunicorn |
3. 테스트에 사용할 Flask 앱을 생성합니다.
현재 위치에 app.py 파일 생성합니다.
nano app.py |
다음 내용을 저장합니다.
from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello, World! Docker + Apache + Gunicorn is working!' if __name__ == '__main__': app.run() |
애플리케이션은 기본 경로 / 에 접속 시 간단한 인사말을 반환합니다.
4. Gunicorn 설정 파일인 gunicorn.conf.py 파일을 생성하여 Gunicorn의 동작 방식을 설정합니다.
현재 위치에 gunicorn.conf.py 파일을 생성합니다.
nano gunicorn.conf.py |
다음 내용을 저장합니다.
bind = '127.0.0.1:8000' # Gunicorn이 리스닝할 주소와 포트 workers = 4 # 동시에 처리할 요청 수를 위한 워커 프로세스 개수 worker_class = 'sync' # 동기식 워커 타입 (필요에 따라 async 등으로 변경 가능) worker_connections = 1000 # 각 워커당 처리 가능한 최대 연결 수 accesslog = 'access.log' # 접근 로그 파일 경로 errorlog = 'error.log' # 에러 로그 파일 경로 |
Docker 설정
Docker 컨테이너 내에서 Apache와 Gunicorn을 함께 실행하여 Flask 애플리케이션을 제공할 수 있도록 Dockerfile 및 관련 파일들을 생성합니다.
1. 현재 위치에 Dockerfile을 생성합니다. Dockerfile은 컨테이너 이미지를 빌드하기 위한 레시피입니다.
nano Dockerfile |
다음 내용을 저장합니다.
FROM python:3.9 WORKDIR /app # OS 패키지 업데이트와 Apache 설치 RUN apt-get update && apt-get install -y \ apache2 \ apache2-dev # Python 의존성 설치를 위해 requirements.txt 복사 및 설치 COPY requirements.txt . RUN pip install -r requirements.txt # 프로젝트 파일 전체를 컨테이너의 작업 디렉토리로 복사 COPY . . # Apache 사이트 설정 파일 교체 COPY apache.conf /etc/apache2/sites-available/000-default.conf RUN a2enmod proxy proxy_http RUN service apache2 restart # 컨테이너 외부와 통신할 포트 지정 EXPOSE 80 # 시작 스크립트 복사 및 실행 권한 부여 COPY start.sh . RUN chmod +x start.sh # 컨테이너 실행 시 시작 스크립트 실행 CMD ["./start.sh"] |
각 단계는 다음과 같은 의미를 갖습니다:
베이스 이미지: Python 3.9 이미지를 사용하여 Python 환경을 구성합니다.
Apache 설치: 컨테이너 내에서 Apache를 설치하여 리버스 프록시 역할을 수행합니다.
파일 복사 및 의존성 설치: 필요한 파일들을 복사하고, requirements.txt에 명시된 Python 패키지를 설치합니다.
포트 노출: 외부에서 80번 포트를 통해 접근할 수 있도록 설정합니다.
시작 스크립트: 컨테이너 실행 시 Apache와 Gunicorn을 함께 실행하는 스크립트를 지정합니다.
2. 현재 위치에 requirements.txt 파일을 생성하여 필요한 Python 패키지들을 명시합니다.
nano requirements.txt |
다음 내용을 저장합니다.
flask gunicorn |
3. 현재 위치에 start.sh 파일을 생성합니다. 컨테이너 시작 시 실행되는 스크립트입니다. Apache를 시작한 후 Gunicorn으로 Flask 애플리케이션을 구동합니다.
nano start.sh |
다음 내용을 저장합니다.
#!/bin/bash service apache2 start gunicorn -c gunicorn.conf.py app:app |
service apache2 start: 컨테이너 내부에서 Apache를 시작합니다.
gunicorn -c gunicorn.conf.py app:app: Gunicorn을 설정 파일을 기반으로 실행하여 Flask 애플리케이션을 구동합니다.`
5.4 현재 위치에 apache.conf 파일을 생성합니다. 컨테이너 내 Apache의 리버스 프록시 설정을 지정하는 파일입니다.
nano apache.conf |
다음 내용을 저장합니다.
<VirtualHost *:80> ServerName localhost ProxyPass / http://127.0.0.1:8000/ ProxyPassReverse / http://127.0.0.1:8000/ </VirtualHost> |
이 파일은 호스트의 포트 80으로 들어온 요청을 내부의 8000번 포트(즉, Gunicorn이 실행되는 곳)로 전달합니다.
빌드 및 실행
1. 현재 디렉토리의 Dockerfile을 기준으로 도커 이미지를 빌드합니다. 빌드하는데 시간이 좀 걸립니다.
docker build -t myapp . |
2. 호스트 시스템에 이미 설치된 Apache와의 포트 충돌을 피하기 위해 기존 Apache 서버를 중지한 후, Docker 컨테이너를 실행합니다.
# 기존 Apache 서버 중지 sudo systemctl stop apache2 # Docker 컨테이너 실행 (호스트 80번 포트와 컨테이너의 80번 포트를 연결) docker run -d -p 80:80 --name myapp_container myapp |
테스트 및 검증
배포한 서비스가 정상 동작하는지 확인하는 단계입니다.
1. 서비스 상태 확인
Docker 컨테이너 상태 확인: 실행 중인 컨테이너 목록과 상태를 확인합니다.
docker ps docker logs myapp_container |
(venv) webnautes@webnautes-VirtualBox:~/myapp$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a2127b484be7 myapp "./start.sh" 8 seconds ago Up 7 seconds 0.0.0.0:80->80/tcp, [::]:80->80/tcp myapp_container
(venv) webnautes@webnautes-VirtualBox:~/myapp$ docker logs myapp_container
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
Starting Apache httpd web server: apache2.
Apache 및 Gunicorn 로그 확인:
컨테이너 내부에서 Apache와 Gunicorn 로그를 확인하여 에러 여부를 점검할 수 있습니다.
docker exec myapp_container tail -f /var/log/apache2/error.log docker exec myapp_container tail -f /app/error.log |
2. 웹 서비스 테스트
로컬에서 테스트하여 서비스가 올바르게 응답하는지 확인합니다. Hello, World! Docker + Apache + Gunicorn is working! 메시지가 출력되는지 확인합니다.
curl 테스트
curl http://localhost |
(venv) webnautes@webnautes-VirtualBox:~/myapp$ curl http://localhost
Hello, World! Docker + Apache + Gunicorn is working!(venv) webnautes@webnautes-VirtualBox:~/myapp$
웹 브라우저 테스트
웹브라우저에서 http://localhost에 접속하여 "Hello, World! Docker + Apache + Gunicorn is working!" 메시지를 확인합니다.
호스트 운영체제인 윈도우에서 테스트하려면 NAT 네트워크 환경에서 외부(호스트)에서 컨테이너에 접근할 수 있도록 포트 포워딩 설정이 필요합니다.
가상 머신 창의 메뉴에서 머신 설정을 선택하고 왼쪽 메뉴에서 네트워크를 선택합니다.
어댑터 1 탭에 보이는 포트 포워딩 버튼을 클릭합니다.
+ 아이콘 클릭후, 다음처럼 포트 포워딩 규칙을 추가합니다.
이름: WebService
프로토콜: TCP
호스트 IP: 127.0.0.1 (또는 비워두어도 됨)
호스트 포트: 8080 (또는 원하는 포트)
게스트 IP: 비워둠
게스트 포트: 8080 (또는 Docker 컨테이너에서 사용하는 포트)
다음처럼 입력했습니다.
Windows 등 다른 호스트 환경에서 웹브라우저를 열고 http://localhost:8080/에 접속하여 메시지를 확인합니다:
윈도우의 명령 프롬프트에서 ipconfig 명령으로 IP를 확인한후, 다음처럼 모바일에서 접속해서도 동작합니다.
아이피가 192.168.45.245인 경우 http://192.168.45.245:8080로 접속하면 다음처럼 보입니다.
테스트 종료 후 정리
서비스 테스트가 완료된 후, Docker 리소스를 정리합니다.
# 컨테이너 중지 docker stop myapp_container # 컨테이너 제거 docker rm myapp_container # 이미지 제거 (필요 시) docker rmi myapp |
'WEB > WEB 개발환경' 카테고리의 다른 글
Windows에서 웹 개발 환경 만들기 ( WampServer : MariaDB, PHP, MySQL, PhpMyAdmin ) (76) | 2024.04.26 |
---|---|
AWS p2.xlarge (NVIDIA Tesla K80 GPU) 엔비디아 그래픽 카드 드라이버 설치 방법 (0) | 2023.10.13 |
AWS 프리 티어 가입부터 EC2 생성 및 SSH 접속까지 (1) | 2021.05.03 |
Windows 10에서 Node.js 사용해보기 (0) | 2021.03.22 |
Ubuntu 16.04에 LAMP ( Apache2, MySQL , PHP7) 설치하는 방법 (57) | 2018.10.02 |