#Docker #Nginx #Flask #Gunicorn #Mysql #Python
Docker위에 nginx, flask, mysql로 웹 페이지를 제작할 일이 있어서 기본적인 연동만 완료된 상태의 소스코드를 기록해둔다.
project_root
├─ .env
├─ docker-compose.yml
└─ services
├─ nginx
│ ├─ Dockerfile
│ └─ nginx.conf
└─ web
├─ Dockerfile
├─ Dockerfile.prod
├─ manage.py
├─ requirements.txt
└─ src
├─ app.py
└─ config.py
.env
: 포트, 패스워드 등 사용자 / 환경에 따라서 자주 바꿔야하는 것들을 환경 변수로 빼놓음docker-compose.yml
: 모든 서비스들을 일괄적으로 관리하기 위한 파일services
: nginx와 web (flask)의 dockerfile을 build하기위한 파일들이 있음
manage.py
: nginx와 flask를 연결하기 위함services/web/src
에 위치하게 된다..env
# --- Common Configs ---
HOME_DIR=/home/app
APP_FOLDER=/home/app/web
FLASK_APP=src/app.py
FLASK_DEBUG=0
DB_ROOT_PASSWORD=123456
DB_NAME=DB_MYAPP
DB_TIMEZONE=Asia/Seoul
# --- Prod configs ---
PROD_FLASK_PORT=5000
PROD_DB_PORT=3306
PROD_PHPMYADMIN_PORT=8081
PROD_NGINX_PORT=80
# --- Developer configs ---
DEV_FLASK_PORT=8003
DEV_DB_PORT=3307
DEV_PHPMYADMIN_PORT=8082
docker-compose.yaml
version: '3.8'
services:
# --------------
# - Flask - Dev
# --------------
flask_dev:
container_name: flask_dev
build: ./services/web
command: python manage.py run -h 0.0.0.0
restart: on-failure
volumes:
- ./services/web/:/usr/src/app/
ports:
- ${DEV_FLASK_PORT}:5000
env_file:
- ./.env
depends_on:
- db_dev
networks:
- net
# --------------
# - Flask - Prod
# --------------
flask_prod:
container_name: flask_prod
build:
context: ./services/web
dockerfile: Dockerfile.prod
command: gunicorn -b 0.0.0.0:5000 manage:app
restart: on-failure
volumes:
- static_volume:/home/app/web/src/static
- media_volume:/home/app/web/src/media
expose:
- ${PROD_FLASK_PORT}
env_file:
- ./.env
depends_on:
- db_prod
networks:
- net
# --------------
# - DB (Dev)
# --------------
db_dev:
container_name: db_dev
image: mysql:8.0.32-debian
platform: linux/amd64
restart: on-failure
expose:
- ${DEV_DB_PORT}
volumes:
- mysql_data-dev:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: ${DB_NAME}
TZ: ${DB_TIMEZONE}
networks:
- net
# --------------
# - DB (Prod)
# --------------
db_prod:
container_name: db_prod
image: mysql:8.0.32-debian
platform: linux/amd64
restart: on-failure
expose:
- ${PROD_DB_PORT}
volumes:
- mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_NAME}
TZ: ${DB_TIMEZONE}
networks:
- net
# --------------
# - Nginx
# --------------
nginx:
container_name: nginx
build: ./services/nginx
restart: on-failure
volumes:
- static_volume:/home/app/web/project/static
- media_volume:/home/app/web/project/media
ports:
- ${PROD_NGINX_PORT}:80
depends_on:
- flask_prod
networks:
- net
# --------------
# - PhpMyAdmin (Dev)
# --------------
phpmyadmin_dev:
container_name: phpmyadmin_dev
image: phpmyadmin/phpmyadmin
restart: on-failure
ports:
- ${DEV_PHPMYADMIN_PORT}:80
networks:
- net
depends_on:
- db_dev
environment:
PMA_HOST: db
PMA_PORT: ${DEV_DB_PORT}
PMA_USER: root
PMA_PASSWORD: ${DB_ROOT_PASSWORD}
PMA_ARBITRARY: 1
# --------------
# - PhpMyAdmin (Prod)
# --------------
phpmyadmin_prod:
container_name: phpmyadmin_prod
image: phpmyadmin/phpmyadmin
restart: on-failure
ports:
- ${PROD_PHPMYADMIN_PORT}:80
networks:
- net
depends_on:
- db_prod
environment:
PMA_HOST: db
PMA_PORT: ${DEV_DB_PORT}
PMA_USER: root
PMA_PASSWORD: ${DB_ROOT_PASSWORD}
PMA_ARBITRARY: 1
volumes:
mysql_data:
mysql_data-dev:
static_volume:
media_volume:
networks:
net:
services/nginx/Dockerfile
FROM nginx:mainline
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
services/nginx/nginx.conf
upstream emoji_flask {
# flask_prod is the name of the docker compose service.
# flask_prod:5000 is the port the service is listening on.
# This is the same as the port you set in the Dockerfile.
server flask_prod:5000;
}
server {
listen 80;
location / {
resolver 127.0.0.11 valid=30s;
resolver_timeout 10s;
set $upstream http://myapp.com;
proxy_pass $upstream;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static/ {
alias /home/app/web/project/static/;
}
location /media/ {
alias /home/app/web/project/media/;
}
}
services/web/Dockerfile
# pull official base image
FROM python:3.10.7-slim-buster
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install system dependencies
RUN apt-get update && apt-get install -y netcat
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r requirements.txt
# copy project
COPY . /usr/src/app/
Dockerfile.prod
###########
# BUILDER #
###########
# pull official base image
FROM python:3.10.7-slim-buster as builder
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install system dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc
# lint
RUN pip install --upgrade pip
RUN pip install flake8==6.0.0
COPY . /usr/src/app/
# RUN flake8 --ignore=E501,F401 .
# install python dependencies
COPY ./requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt
#########
# FINAL #
#########
# pull official base image
FROM python:3.10.7-slim-buster
# create directory for the app user
RUN mkdir -p /home/app
# create the app user
RUN addgroup --system app && adduser --system --group app
# create the appropriate directories
ENV HOME=$HOME_DIR
ENV APP_HOME=$HOME_DIR/web
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
# install dependencies
RUN apt-get update && apt-get install -y --no-install-recommends netcat
COPY --from=builder /usr/src/app/wheels /wheels
COPY --from=builder /usr/src/app/requirements.txt .
RUN pip install --upgrade pip
RUN pip install --no-cache /wheels/*
# copy project
RUN rm -rf $APP_HOME
COPY . $APP_HOME
# chown all the files to the app user
RUN chown -R app:app $APP_HOME
# change to the app user
USER app
services/web/manage.py
from flask.cli import FlaskGroup
from src.app import app
cli = FlaskGroup(app)
if __name__ == "__main__":
cli()
services/web/requirements.txt
Flask==3.0.0
Flask-SQLAlchemy==3.1.1
gunicorn==21.2.0
mysql-connector-python==8.2.0
services/web/src/app.py
from flask import (
Flask,
abort,
jsonify,
redirect,
render_template,
request,
send_from_directory,
session,
)
from sqlalchemy import create_engine
app = Flask(__name__)
app.config.from_object("src.config.Config")
db = create_engine(app.config["SQLALCHEMY_DATABASE_URI"])
@app.route("/")
def index():
return jsonify({"message": "Hello, World!"})
@app.route("/static/<path:filename>")
def staticfiles(filename):
return send_from_directory(app.config["STATIC_FOLDER"], filename)
@app.route("/media/<path:filename>")
def mediafiles(filename):
return send_from_directory(app.config["MEDIA_FOLDER"], filename)
services/web/src/config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite://")
SQLALCHEMY_TRACK_MODIFICATIONS = False
STATIC_FOLDER = f"{os.getenv('APP_FOLDER')}/src/static"
MEDIA_FOLDER = f"{os.getenv('APP_FOLDER')}/src/media"
docker-compose up -d --build
MacOS X 맥에서 독(Dock)에 통통 튀는 것 비활성화 (2) | 2023.12.04 |
---|---|
Python import `잘 하는` 방법 (1) | 2023.12.01 |
시놀로지 SSL Certificates Path (1) | 2023.12.01 |
Top-Level Volume in docker compose (0) | 2023.11.30 |
CouchDB 설치 with docker compose (0) | 2023.11.29 |