这是一个分步教程,详细介绍了如何配置 Flask 在 Postgres 上运行于 Docker。 对于生产环境,我们将添加 Nginx 和Gunicorn。 我们还将研究如何通过 Nginx 提供静态和用户上传的媒体文件。
主要依赖:
- Flask v1.1.1
- Docker v19.03.4
- Python v3.8.2
建立项目
$ mkdir flask-on-docker && cd flask-on-docker$ mkdir services && cd services$ mkdir web && cd web$ mkdir project$ python -m venv env$ source env/bin/activate(env)$ pip install flask
创建 Flask 应用:
# ./services/web/project/__init__.pyfrom flask import Flask, jsonifyapp = Flask(__name__)@app.route("/")def hello_world():return jsonify(hello="world")
然后,要配置Flask CLI工具以从命令行运行和管理应用程序,请将 manage.py 文件添加到 web 目录中:
# ./services/web/manage.pyfrom flask.cli import FlaskGroupfrom project import appcli = FlaskGroup(app)if __name__ == "__main__":cli()
在这里,我们创建了一个新的 FlaskGroup 实例,以使用与Flask应用相关的命令扩展普通的 CLI。
从 web 目录运行服务器:
(env)$ export FLASK_APP=project/__init__.py(env)$ python manage.py run
导航到 http://localhost:5000/ 。您应该看到:
{"hello": "world"}
在 web 目录中创建一个 requirements.txt 文件,并将Flask添加为依赖项:
Flask==1.1.1
您的项目结构应该是这样的:
└── services
└── web
├── manage.py
├── project
│ └── __init__.py
└── requirements.txt
Docker
安装 Docker,如果你还没有它,那么添加一个Dockerfile到 web 目录:
# pull official base image
FROM python:3.8-slim-buster
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# 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/
接下来,将 docker-compose.yml 文件添加到项目根目录:
services:
web:
build: ./services/web
command: python manage.py run -h 0.0.0.0
volumes:
- ./services/web/:/usr/src/app/
ports:
- 5000:5000
env_file:
- ./.env.dev
然后,在项目根目录中创建一个 .env.dev 文件,以存储用于开发的环境变量:
FLASK_APP=project/__init__.py
FLASK_ENV=development
构建镜像:
$ docker-compose build
生成映像后,运行容器:
$ docker-compose up -d
导航到 http://localhost:5000/ 再次看到相同结果。
如果不工作,可以通过
docker-compose logs -f检查日志中的错误。
Postgres
要配置 Postgres,我们需要向 docker-compose.yml 文件中添加新服务,设置 flask-sqlalchemy ,然后安装 Psycopg2。
首先,将一个名为 db 的新服务添加到 docker-compose.yml 中:
version: '3.7'
services:
web:
build: ./services/web
command: python manage.py run -h 0.0.0.0
volumes:
- ./services/web/:/usr/src/app/
ports:
- 5000:5000
env_file:
- ./.env.dev
depends_on:
- db
db:
image: postgres:12-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=hello_flask
- POSTGRES_PASSWORD=hello_flask
- POSTGRES_DB=hello_flask_dev
volumes:
postgres_data:
为了在容器的使用生命周期之外保留数据,我们配置了一个数据卷。 此配置会将 postgres_data 绑定到容器中的 /var/lib/postgresql/data/ 目录。
我们还添加了一个环境密钥来定义默认数据库的名称,并设置用户名和密码。
查看 Postgres Docker Hub page 页面的“环境变量”部分以获得更多信息。
然后将 DATABASE_URL 环境变量添加到 .env.dev :
FLASK_APP=project/__init__.py
FLASK_ENV=development
DATABASE_URL=postgresql://hello_flask:hello_flask@db:5432/hello_flask_dev
然后,将一个名为 config.py 的新文件添加到 project 目录,在该目录中,我们将定义特定于环境的配置变量:
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
在这里,数据库是根据我们刚刚定义的 DATABASE_URL 环境变量配置的。 注意默认值。
更新 __init__.py 以获取配置:
from flask import Flask, jsonify
app = Flask(__name__)
app.config.from_object("project.config.Config")
@app.route("/")
def hello_world():
return jsonify(hello="world")
添加 Flask-SQLAlchemy and Psycopg2 依赖到 requirements.txt 文件:
Flask==1.1.1
Flask-SQLAlchemy==2.4.1
psycopg2-binary==2.8.4
再次更新 __init__.py 文件,以创建一个新的 SQLAlchemy 实例并定义一个数据库模型:
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_object("project.config.Config")
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(128), unique=True, nullable=False)
active = db.Column(db.Boolean(), default=True, nullable=False)
def __init__(self, email):
self.email = email
@app.route("/")
def hello_world():
return jsonify(hello="world")
最后更新 manage.py 文件:
from flask.cli import FlaskGroup
from project import app, db
cli = FlaskGroup(app)
@cli.command("create_db")
def create_db():
db.drop_all()
db.create_all()
db.session.commit()
if __name__ == "__main__":
cli()
这会将一个新命令 create_db 注册到 CLI,以便我们可以从命令行运行它,稍后将使用该模型将模型应用于数据库。构建新镜像:
$ docker-compose up -d --build
创建数据库:
$ docker-compose exec web python manage.py create_db
确保创建了 users 表:
$ docker-compose exec db psql --username=hello_flask --dbname=hello_flask_dev
psql (12.2)
Type "help" for help.
hello_flask_dev=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------------+-------------+----------+------------+------------+-----------------------------
hello_flask_dev | hello_flask | UTF8 | en_US.utf8 | en_US.utf8 |
postgres | hello_flask | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | hello_flask | UTF8 | en_US.utf8 | en_US.utf8 | =c/hello_flask +
| | | | | hello_flask=CTc/hello_flask
template1 | hello_flask | UTF8 | en_US.utf8 | en_US.utf8 | =c/hello_flask +
| | | | | hello_flask=CTc/hello_flask
(4 rows)
hello_flask_dev=# \c hello_flask_dev
You are now connected to database "hello_flask_dev" as user "hello_flask".
hello_flask_dev=# \dt
List of relations
Schema | Name | Type | Owner
--------+-------+-------+-------------
public | users | table | hello_flask
(1 row)
hello_flask_dev=# \q
您可以通过运行以下命令检查该数据卷是否也已创建:
$ docker volume inspect flask-on-docker_postgres_data
您应该看到类似以下内容:
[
{
"CreatedAt": "2020-02-24T13:39:47Z",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "flask-on-docker",
"com.docker.compose.version": "1.24.1",
"com.docker.compose.volume": "postgres_data"
},
"Mountpoint": "/var/lib/docker/volumes/flask-on-docker_postgres_data/_data",
"Name": "flask-on-docker_postgres_data",
"Options": null,
"Scope": "local"
}
]
接下来,在创建数据库表和运行 Flask 开发服务器之前,将 entrypoint.sh 文件添加到 web 目录以验证Postgres 是否正常运行:
#!/bin/sh
if [ "$DATABASE" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $SQL_HOST $SQL_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
python manage.py create_db
exec "$@"
注意环境变量。然后在本地更新文件权限:
$ chmod +x services/web/entrypoint.sh
然后,更新 Dockerfile 以安装 Netcat,复制 entrypoint.sh 文件,然后将该文件作为 Docker entrypoint命令运行:
# pull official base image
FROM python:3.8.1-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/
# run entrypoint.sh
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
将 entrypoint.sh 脚本的 SQL_HOST , SQL_PORT 和 DATABASE 环境变量添加到 .env.dev :
FLASK_APP=project/__init__.py
FLASK_ENV=development
DATABASE_URL=postgresql://hello_flask:hello_flask@db:5432/hello_flask_dev
SQL_HOST=db
SQL_PORT=5432
DATABASE=postgres
测试一下:
- 重建图像
- 运行容器
- 打开 http://localhost:5000/
我们还为 CLI 添加一个 seed_db 命令,用于将示例用户添加到 manage.py 中的 users 表中:
from flask.cli import FlaskGroup
from project import app, db, User
cli = FlaskGroup(app)
@cli.command("create_db")
def create_db():
db.drop_all()
db.create_all()
db.session.commit()
@cli.command("seed_db")
def seed_db():
db.session.add(User(email="michael@mherman.org"))
db.session.commit()
if __name__ == "__main__":
cli()
尝试运行:
$ docker-compose exec web python manage.py seed_db
$ docker-compose exec db psql --username=hello_flask --dbname=hello_flask_dev
psql (12.0)
Type "help" for help.
hello_flask_dev=# \c hello_flask_dev
You are now connected to database "hello_flask_dev" as user "hello_flask".
hello_flask_dev=# select * from users;
id | email | active
----+---------------------+--------
1 | michael@mherman.org | t
(1 row)
hello_flask_dev=# \q
尽管添加了 Postgres,我们仍然可以通过不设置 DATABASE_URL 环境变量来为 Flask 创建一个独立的Docker映像。 要进行测试,请生成一个新映像,然后运行一个新容器:
$ docker build -f ./services/web/Dockerfile -t hello_flask:latest ./services/web
$ docker run -p 5001:5000 \
-e "FLASK_APP=project/__init__.py" -e "FLASK_ENV=development" \
hello_flask python /usr/src/app/manage.py run -h 0.0.0.0
您应该能够在 http://localhost:5001 上查看 hello world 健全性检查。
Gunicorn
在生产环境中,让我们将 Gunicorn(生产级别 WSGI 服务器)添加到需求文件中:
Flask==1.1.1
Flask-SQLAlchemy==2.4.1
gunicorn==20.0.4
psycopg2-binary==2.8.4
由于我们仍要在开发中使用 Flask 的内置服务器,因此创建一个名为 docker-compose.prod.yml 的新撰写文件进行生产:
version: '3.7'
services:
web:
build: ./services/web
command: gunicorn --bind 0.0.0.0:5000 manage:app
ports:
- 5000:5000
env_file:
- ./.env.prod
depends_on:
- db
db:
image: postgres:12-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
env_file:
- ./.env.prod.db
volumes:
postgres_data:
如果您有多个环境,则可能需要使用 docker-compose.override.yml 配置文件。 使用这种方法,您可以将基本配置添加到
docker-compose.yml文件,然后使用docker-compose.override.yml文件根据环境覆盖这些配置设置。
注意默认命令。 我们正在运行 Gunicorn,而不是 Flask 开发服务器。 我们还从Web 服务中删除了该卷,因为在生产中不需要它。 最后,我们使用单独的环境变量文件来定义两个服务的环境变量,这些环境变量将在运行时传递给容器。
# .env.prod
FLASK_APP=project/__init__.py
FLASK_ENV=production
DATABASE_URL=postgresql://hello_flask:hello_flask@db:5432/hello_flask_prod
SQL_HOST=db
SQL_PORT=5432
DATABASE=postgres
# .env.prod.db
POSTGRES_USER=hello_flask
POSTGRES_PASSWORD=hello_flask
POSTGRES_DB=hello_flask_prod
将两个文件添加到项目根目录。 您可能想让它们脱离版本控制,因此将它们添加到 .gitignore 文件中。运行命令:
$ docker-compose down -v
$ docker-compose -f docker-compose.prod.yml up -d --build
验证是否已与 users 表一起创建了 hello_flask_prod 数据库。 测试 http://localhost:5000/。
生产环境
您是否注意到我们仍在运行 create_db 命令,该命令会在每次运行容器时删除所有现有表,然后从模型中创建表? 这在开发中很好,但是让我们为生产创建一个新的入口点文件。
文件 entrypoint.prod.sh:
#!/bin/sh
if [ "$DATABASE" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $SQL_HOST $SQL_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
exec "$@"
或者,您可以像这样更改现有的文件,而不是创建新的入口点文件:
#!/bin/sh
if [ "$DATABASE" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $SQL_HOST $SQL_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
if [ "$FLASK_ENV" = "development" ]
then
echo "Creating the database tables..."
python manage.py create_db
echo "Tables created"
fi
exec "$@"
在本地更新文件权限:
$ chmod +x services/web/entrypoint.prod.sh
要使用此文件,请创建一个名为 Dockerfile.prod 的新 Dockerfile 以用于生产版本:
###########
# BUILDER #
###########
# pull official base image
FROM python:3.8.1-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
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.8.1-slim-buster
# create directory for the app user
RUN mkdir -p /home/app
# create the app user
RUN addgroup -S app && adduser -S app -G app
# create the appropriate directories
ENV HOME=/home/app
ENV APP_HOME=/home/app/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 entrypoint-prod.sh
COPY ./entrypoint.prod.sh $APP_HOME
# copy project
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
# run entrypoint.prod.sh
ENTRYPOINT ["/home/app/web/entrypoint.prod.sh"]
在这里,我们使用了 Docker 多阶段构建来减小最终映像的大小。 本质上, builder 是用于构建 Python Wheel 文件的临时映像。 然后将构建生成的 Wheel 文件复制到最终生产镜像中,并丢弃builder 镜像。
您可以进一步采用多阶段构建方法,并使用单个 Dockerfile 而不是创建两个 Dockerfile 文件。 考虑在两个不同的文件上使用此方法的利弊。
您是否注意到我们创建了非 root 用户? 默认情况下,Docker 作为容器内部的根运行容器进程。 这是一个不好的做法,因为如果攻击者设法突破容器,他们可以获得对Docker主机的根访问权限。 如果您是容器的 root 用户,那么您将是主机的root用户。
更新 docker-compose.prod.yml 文件中的 web 服务以使用 Dockerfile.prod 进行构建:
web:
build:
context: ./services/web
dockerfile: Dockerfile.prod
command: gunicorn --bind 0.0.0.0:5000 manage:app
ports:
- 5000:5000
env_file:
- ./.env.prod
depends_on:
- db
尝试运行一下命令:
$ docker-compose -f docker-compose.prod.yml down -v
$ docker-compose -f docker-compose.prod.yml up -d --build
$ docker-compose -f docker-compose.prod.yml exec web python manage.py create_db
Nginx
接下来,让我们将 Nginx 添加到组合中,以充当Gunicorn的反向代理,以处理客户端请求以及提供静态文件。
将服务添加到 docker-compose.prod.yml 中:
nginx:
build: ./services/nginx
ports:
- 1337:80
depends_on:
- web
然后,在“services”目录中,创建以下文件和文件夹:
└── nginx
├── Dockerfile
└── nginx.conf
Dockerfile:
FROM nginx:1.17-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
nginx.conf:
upstream hello_flask {
server web:5000;
}
server {
listen 80;
location / {
proxy_pass http://hello_flask;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
有关如何配置Nginx与Flask一起使用的更多信息,请参见如何为Flask Web应用程序配置NGINX。
然后,在 docker-compose.prod.yml 中更新 web 服务,将 ports 替换为 expose :
web:
build:
context: ./services/web
dockerfile: Dockerfile.prod
command: gunicorn --bind 0.0.0.0:5000 manage:app
expose:
- 5000
env_file:
- ./.env.prod
depends_on:
- db
现在,端口 5000 仅在内部公开给其他 Docker 服务。 该端口将不再发布到主机。
有关
ports与expose的更多信息,请查看 Stack Overflow 问题。
再次测试:
$ docker-compose -f docker-compose.prod.yml down -v
$ docker-compose -f docker-compose.prod.yml up -d --build
$ docker-compose -f docker-compose.prod.yml exec web python manage.py create_db
确保该应用程序已启动并在http://localhost:1337上运行。您的项目结构现在应如下所示:
├── .env.dev
├── .env.prod
├── .env.prod.db
├── .gitignore
├── docker-compose.prod.yml
├── docker-compose.yml
└── services
├── nginx
│ ├── Dockerfile
│ └── nginx.conf
└── web
├── Dockerfile
├── Dockerfile.prod
├── entrypoint.prod.sh
├── entrypoint.sh
├── manage.py
├── project
│ ├── __init__.py
│ └── config.py
└── requirements.txt
完成后将容器删除:
$ docker-compose -f docker-compose.prod.yml down -v
由于 Gunicorn 是应用程序服务器,所以它将不提供静态文件。 因此,在此特定配置中应如何处理静态文件和媒体文件?
静态文件
首先在 services/web/project 文件夹中创建以下文件和文件夹:
└── static
└── hello.txt
添加一些文本到 hello.txt:
hi!
向 __init__.py 添加新的路由处理程序:
from flask import Flask, jsonify, send_from_directory
@app.route("/static/<path:filename>")
def staticfiles(filename):
return send_from_directory(app.config["STATIC_FOLDER"], filename)
不要忘记导入send_from_directory。最后, 添加 STATIC_FOLDER 配置到 services/web/project/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')}/project/static"
开发环境
将 APP_FOLDER 环境变量添加到 .env.dev :
FLASK_APP=project/__init__.py
FLASK_ENV=development
DATABASE_URL=postgresql://hello_flask:hello_flask@db:5432/hello_flask_dev
SQL_HOST=db
SQL_PORT=5432
DATABASE=postgres
APP_FOLDER=/usr/src/app
要进行测试,请先重新构建图像,然后按常规创建新容器。 完成后确保http://localhost:5000/static/hello.txt 正确提供了文件
生产环境
为了进行生产,请在 docker-compose.prod.yml 中向 web 和 nginx 服务添加一个数据卷,以便每个容器将共享一个名为 static 的目录:
version: '3.7'
services:
web:
build:
context: ./services/web
dockerfile: Dockerfile.prod
command: gunicorn --bind 0.0.0.0:5000 manage:app
volumes:
- static_volume:/home/app/web/project/static
expose:
- 5000
env_file:
- ./.env.prod
depends_on:
- db
db:
image: postgres:12-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
env_file:
- ./.env.prod.db
nginx:
build: ./services/nginx
volumes:
- static_volume:/home/app/web/project/static
ports:
- 1337:80
depends_on:
- web
volumes:
postgres_data:
static_volume:
接下来,更新Nginx配置以将静态文件请求路由到 static 文件夹:
upstream hello_flask {
server web:5000;
}
server {
listen 80;
location / {
proxy_pass http://hello_flask;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /static/ {
alias /home/app/web/project/static/;
}
}
将 APP_FOLDER 环境变量添加到 .env.prod 文件:
FLASK_APP=project/__init__.py
FLASK_ENV=production
DATABASE_URL=postgresql://hello_flask:hello_flask@db:5432/hello_flask_prod
SQL_HOST=db
SQL_PORT=5432
DATABASE=postgres
APP_FOLDER=/home/app/web
然后删除旧容器并重新创建和运行容器:
$ docker-compose down -v
$ docker-compose -f docker-compose.prod.yml up -d --build
再次,请求 http://localhost:1337/static/* 服务将由 static 目录提供。
打开 http://localhost:1337/static/hello.txt,并确保正确加载了静态文件。
您还可以在日志中通过 docker-compose -f docker-compose.prod.ymllogs -f 验证是否已通过Nginx成功处理了对静态文件的请求:
nginx_1 | 172.23.0.1 - - [24/Feb/2020:17:01:24 +0000] "GET /static/hello.txt HTTP/1.1" 200 4 "-"
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:73.0) Gecko/20100101 Firefox/73.0" "-"
完成后删除容器:
$ docker-compose -f docker-compose.prod.yml down -v
媒体文件
要测试对用户上传的媒体文件的处理,请在 __init__.py 中添加两个新的路由处理程序:
@app.route("/media/<path:filename>")
def mediafiles(filename):
return send_from_directory(app.config["MEDIA_FOLDER"], filename)
@app.route("/upload", methods=["GET", "POST"])
def upload_file():
if request.method == "POST":
file = request.files["file"]
filename = secure_filename(file.filename)
file.save(os.path.join(app.config["MEDIA_FOLDER"], filename))
return f"""
<!doctype html>
<title>upload new File</title>
<form action="" method=post enctype=multipart/form-data>
<p><input type=file name=file><input type=submit value=Upload>
</form>
"""
同时更新导入:
import os
from werkzeug.utils import secure_filename
from flask import (
Flask,
jsonify,
send_from_directory,
request,
redirect,
url_for
)
from flask_sqlalchemy import SQLAlchemy
添加 MEDIA_FOLDER 到配置文件 services/web/project/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')}/project/static"
MEDIA_FOLDER = f"{os.getenv('APP_FOLDER')}/project/media"
最后,在“project”文件夹中创建一个名为“media”的新文件夹。
开发环境
测试:
$ docker-compose up -d --build
您应该能够在 http://localhost:5000/upload 上载图像,然后在 http://localhost:5000/media/IMAGE_FILE_NAME上查看图像。
生产环境
对于生产,将另一个数据卷添加到 web 和 nginx 服务:
version: '3.7'
services:
web:
build:
context: ./services/web
dockerfile: Dockerfile.prod
command: gunicorn --bind 0.0.0.0:5000 manage:app
volumes:
- static_volume:/home/app/web/project/static
- media_volume:/home/app/web/project/media
expose:
- 5000
env_file:
- ./.env.prod
depends_on:
- db
db:
image: postgres:12-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
env_file:
- ./.env.prod.db
nginx:
build: ./services/nginx
volumes:
- static_volume:/home/app/web/project/static
- media_volume:/home/app/web/project/media
ports:
- 1337:80
depends_on:
- web
volumes:
postgres_data:
static_volume:
media_volume:
接下来,更新Nginx配置以将媒体文件请求路由到“ media”文件夹:
server web:5000;
}
server {
listen 80;
location / {
proxy_pass http://hello_flask;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /static/ {
alias /home/app/web/project/static/;
}
location /media/ {
alias /home/app/web/project/media/;
}
}
重新构建镜像:
$ docker-compose down -v
$ docker-compose -f docker-compose.prod.yml up -d --build
$ docker-compose -f docker-compose.prod.yml exec web python manage.py create_db
最后一次测试:
- 在 http://localhost:5000/upload 上传一个图像。
在 http://localhost:5000/media/IMAGE_FILE_NAME 上查看图像。
总结
在本教程中,我们逐步介绍了如何使用 Postgres 将 Flask 应用程序容器化以进行开发。 我们还创建了可用于生产的 Docker Compose 文件,该文件将 Gunicorn 和 Nginx 添加到混合文件中以处理静态文件和媒体文件。 您现在可以在本地测试生产设置。
就实际部署到生产环境而言,您可能需要使用:- 使用非 root 用户运行
db和nginx服务。
您可以在 flask-on-docker 存储库中找到代码。
谢谢阅读!
