1661105160
Это пошаговое руководство, в котором подробно описано, как настроить Masonite, веб-фреймворк на основе Python, для работы в Docker с Postgres. Для производственных сред мы добавим Nginx и Gunicorn. Мы также рассмотрим, как обслуживать статические и загруженные пользователем медиафайлы через Nginx.
Зависимости :
Создайте каталог проекта, установите Masonite и создайте новый проект Masonite:
$ mkdir masonite-on-docker && cd masonite-on-docker
$ python3.10 -m venv env
$ source env/bin/activate
(env)$ pip install masonite==4.16.2
(env)$ project start web
(env)$ cd web
(env)$ project install
(env)$ python craft serve
Перейдите по адресу http://localhost:8000/ , чтобы просмотреть экран приветствия Masonite. Убейте сервер и выйдите из виртуальной среды, как только закончите. Идите вперед и удалите виртуальную среду. Теперь у нас есть простой проект Masonite для работы.
Далее, прежде чем добавлять Docker, давайте немного подчистим структуру проекта:
Теперь структура вашего проекта должна выглядеть так:
├── .env.dev
└── web
├── .env.testing
├── Kernel.py
├── app
│ ├── __init__.py
│ ├── controllers
│ │ ├── WelcomeController.py
│ │ └── __init__.py
│ ├── middlewares
│ │ ├── AuthenticationMiddleware.py
│ │ ├── VerifyCsrfToken.py
│ │ └── __init__.py
│ ├── models
│ │ └── User.py
│ └── providers
│ ├── AppProvider.py
│ └── __init__.py
├── config
│ ├── __init__.py
│ ├── application.py
│ ├── auth.py
│ ├── broadcast.py
│ ├── cache.py
│ ├── database.py
│ ├── exceptions.py
│ ├── filesystem.py
│ ├── mail.py
│ ├── notification.py
│ ├── providers.py
│ ├── queue.py
│ ├── security.py
│ └── session.py
├── craft
├── databases
│ ├── migrations
│ │ ├── 2021_01_09_033202_create_password_reset_table.py
│ │ └── 2021_01_09_043202_create_users_table.py
│ └── seeds
│ ├── __init__.py
│ ├── database_seeder.py
│ └── user_table_seeder.py
├── makefile
├── package.json
├── pyproject.toml
├── requirements.txt
├── resources
│ ├── css
│ │ └── app.css
│ └── js
│ ├── app.js
│ └── bootstrap.js
├── routes
│ └── web.py
├── setup.cfg
├── storage
│ ├── .gitignore
│ └── public
│ ├── favicon.ico
│ ├── logo.png
│ └── robots.txt
├── templates
│ ├── __init__.py
│ ├── base.html
│ ├── errors
│ │ ├── 403.html
│ │ ├── 404.html
│ │ └── 500.html
│ ├── maintenance.html
│ └── welcome.html
├── tests
│ ├── TestCase.py
│ ├── __init__.py
│ └── unit
│ └── test_basic_testcase.py
├── webpack.mix.js
└── wsgi.py
Установите Docker , если у вас его еще нет, затем добавьте файл Dockerfile в каталог «web»:
# pull official base image
FROM python:3.10.5-alpine
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV TZ=UTC
# install system dependencies
RUN apk update && apk --no-cache add \
libressl-dev libffi-dev gcc musl-dev python3-dev openssl-dev cargo
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
# copy project
COPY . .
Итак, мы начали с образа Docker на основе Alpine для Python 3.10.5. Затем мы устанавливаем рабочий каталог вместе с тремя переменными среды:
PYTHONDONTWRITEBYTECODE
: запрещает Python записывать файлы pyc на диск (эквивалентно python -B
опции )PYTHONUNBUFFERED
: запрещает Python буферизовать stdout и stderr (эквивалентно python -u
опции )TZ=UTC
устанавливает часовой пояс в контейнере в UTC, что необходимо для ведения журналаЗатем мы установили некоторые зависимости системного уровня, необходимые для Python. Обратите внимание на зависимости openssl-dev
и . cargo
Это необходимо, так как библиотека криптографии теперь зависит от Rust . Подробнее см. в статье Построение криптографии в Linux .
Наконец, мы обновили Pip, скопировали файл requirements.txt , установили зависимости и скопировали само приложение Masonite.
Ознакомьтесь с рекомендациями Docker для разработчиков Python , чтобы узнать больше о структурировании файлов Docker, а также о некоторых рекомендациях по настройке Docker для разработки на основе Python.
Затем добавьте файл docker-compose.yml в корень проекта:
version: '3.8'
services:
web:
build: ./web
command: python craft serve -p 8000 -b 0.0.0.0
volumes:
- ./web/:/usr/src/app/
ports:
- 8000:8000
env_file:
- .env.dev
Просмотрите справочник по файлу Compose, чтобы узнать, как работает этот файл.
Давайте упростим .env.dev , удалив все неиспользуемые переменные:
APP_DEBUG=True
APP_ENV=local
APP_KEY=zWDMwC0aNfVk8Ao1NyVJC_LiGD9tHJtVn_uCPeaaTNY=
APP_URL=http://localhost:8000
HASHING_FUNCTION=bcrypt
MAIL_DRIVER=terminal
DB_CONNECTION=sqlite
SQLITE_DB_DATABASE=masonite.sqlite3
DB_HOST=127.0.0.1
DB_USERNAME=root
DB_PASSWORD=root
DB_DATABASE=masonite
DB_PORT=3306
DB_LOG=True
QUEUE_DRIVER=async
Создайте образ:
$ docker-compose build
После сборки образа запустите контейнер:
$ docker-compose up -d
Перейдите по адресу http://localhost:8000/ , чтобы снова просмотреть экран приветствия.
Проверьте наличие ошибок в журналах, если это не работает через
docker-compose logs -f
.
Чтобы протестировать автоматическую перезагрузку, сначала откройте журналы Docker -- docker-compose logs -f
-- и затем внесите изменения в файл web/routes/web.py локально:
from masonite.routes import Route
ROUTES = [
Route.get("/", "WelcomeController@show"),
Route.get("/sample", "WelcomeController@show")
]
Как только вы сохраните, вы должны увидеть, как приложение перезагружается в вашем терминале, например:
* Detected change in '/usr/src/app/routes/web.py', reloading
* Restarting with watchdog (inotify)
Убедитесь , что http://localhost:8000/sample работает должным образом.
Чтобы настроить Postgres, нам нужно добавить новую службу в файл docker-compose.yml , обновить переменные среды и установить Psycopg2 .
Во- первых, добавьте новую службу с именем db
в docker-compose.yml :
version: '3.8'
services:
web:
build: ./web
command: python craft serve -p 8000 -b 0.0.0.0
volumes:
- ./web/:/usr/src/app/
ports:
- 8000:8000
env_file:
- .env.dev
depends_on:
- db
db:
image: postgres:14.4-alpine
volumes:
- postgres_data_dev:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=hello_masonite
- POSTGRES_PASSWORD=hello_masonite
- POSTGRES_DB=hello_masonite_dev
volumes:
postgres_data_dev:
Чтобы сохранить данные после окончания срока службы контейнера, мы настроили том. Эта конфигурация будет привязана postgres_data_dev
к каталогу «/var/lib/postgresql/data/» в контейнере.
Мы также добавили ключ среды для определения имени базы данных по умолчанию и установки имени пользователя и пароля.
Дополнительные сведения см. в разделе «Переменные среды» на странице Postgres Docker Hub .
Также обновите следующие переменные среды, связанные с базой данных, в файле .env.dev :
DB_CONNECTION=postgres
DB_HOST=db
DB_PORT=5432
DB_DATABASE=hello_masonite_dev
DB_USERNAME=hello_masonite
DB_PASSWORD=hello_masonite
Просмотрите файл web/config/database.py для получения информации о том, как база данных настроена на основе определенных переменных среды для проекта Masonite.
Обновите Dockerfile, чтобы установить соответствующие пакеты, необходимые для Psycopg2:
# pull official base image
FROM python:3.10.5-alpine
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV TZ=UTC
# install system dependencies
RUN apk update && apk --no-cache add \
libressl-dev libffi-dev gcc musl-dev python3-dev openssl-dev cargo \
postgresql-dev
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
# copy project
COPY . .
Добавьте Psycopg2 в web/requirements.txt :
masonite>=4.0,<5.0
masonite-orm>=2.0,<3.0
psycopg2-binary==2.9.3
Просмотрите этот выпуск GitHub для получения дополнительной информации об установке Psycopg2 в образе Docker на основе Alpine.
Создайте новый образ и запустите два контейнера:
$ docker-compose up -d --build
Примените миграции (из папки "web/databases/migrations"):
$ docker-compose exec web python craft migrate
Тебе следует увидеть:
Migrating: 2021_01_09_033202_create_password_reset_table
Migrated: 2021_01_09_033202_create_password_reset_table (0.01s)
Migrating: 2021_01_09_043202_create_users_table
Migrated: 2021_01_09_043202_create_users_table (0.02s)
Убедитесь, что users
таблица создана:
$ docker-compose exec db psql --username=hello_masonite --dbname=hello_masonite_dev
psql (14.4)
Type "help" for help.
hello_masonite_dev=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
--------------------+----------------+----------+------------+------------+-----------------------------------
hello_masonite_dev | hello_masonite | UTF8 | en_US.utf8 | en_US.utf8 |
postgres | hello_masonite | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | hello_masonite | UTF8 | en_US.utf8 | en_US.utf8 | =c/hello_masonite +
| | | | | hello_masonite=CTc/hello_masonite
template1 | hello_masonite | UTF8 | en_US.utf8 | en_US.utf8 | =c/hello_masonite +
| | | | | hello_masonite=CTc/hello_masonite
(4 rows)
hello_masonite_dev=# \c hello_masonite_dev
You are now connected to database "hello_masonite_dev" as user "hello_masonite".
hello_masonite_dev=# \dt
List of relations
Schema | Name | Type | Owner
--------+-----------------+-------+----------------
public | migrations | table | hello_masonite
public | password_resets | table | hello_masonite
public | users | table | hello_masonite
(3 rows)
hello_masonite_dev=# \q
Вы также можете проверить, что том был создан, запустив:
$ docker volume inspect masonite-on-docker_postgres_data_dev
Вы должны увидеть что-то похожее на:
[
{
"CreatedAt": "2022-07-22T20:07:50Z",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "masonite-on-docker",
"com.docker.compose.version": "2.6.1",
"com.docker.compose.volume": "postgres_data_dev"
},
"Mountpoint": "/var/lib/docker/volumes/masonite-on-docker_postgres_data_dev/_data",
"Name": "masonite-on-docker_postgres_data_dev",
"Options": null,
"Scope": "local"
}
]
Затем добавьте файл entrypoint.sh в каталог «web», чтобы убедиться, что Postgres запущен и работоспособен , прежде чем применять миграции и запускать сервер разработки Masonite:
#!/bin/sh
if [ "$DB_CONNECTION" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $DB_HOST $DB_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
python craft migrate:refresh # you may want to remove this
python craft migrate
exec "$@"
Обратите внимание на переменные окружения.
Затем обновите Dockerfile, чтобы запустить файл entrypoint.sh в качестве команды точки входа Docker :
# pull official base image
FROM python:3.10.5-alpine
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV TZ=UTC
# install system dependencies
RUN apk update && apk --no-cache add \
libressl-dev libffi-dev gcc musl-dev python3-dev openssl-dev cargo \
postgresql-dev
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
# copy project
COPY . .
# run entrypoint.sh
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
Обновите права доступа к файлу локально:
$ chmod +x web/entrypoint.sh
Проверьте еще раз:
Хотите посеять несколько пользователей?
$ docker-compose exec web python craft seed:run
$ docker-compose exec db psql --username=hello_masonite --dbname=hello_masonite_dev
psql (14.4)
Type "help" for help.
hello_masonite_dev=# \c hello_masonite_dev
You are now connected to database "hello_masonite_dev" as user "hello_masonite".
hello_masonite_dev=# select count(*) from users;
count
-------
1
(1 row)
hello_masonite_dev=# \q
Двигаясь дальше, для производственных сред давайте добавим Gunicorn , сервер WSGI производственного уровня, в файл требований:
masonite>=4.0,<5.0
masonite-orm>=2.0,<3.0
psycopg2-binary==2.9.3
gunicorn==20.1.0
Поскольку мы по-прежнему хотим использовать встроенный сервер Masonite в разработке, создайте новый файл компоновки с именем docker-compose.prod.yml для производства:
version: '3.8'
services:
web:
build: ./web
command: gunicorn --bind 0.0.0.0:8000 wsgi:application
ports:
- 8000:8000
env_file:
- .env.prod
depends_on:
- db
db:
image: postgres:14.4-alpine
volumes:
- postgres_data_prod:/var/lib/postgresql/data/
env_file:
- .env.prod.db
volumes:
postgres_data_prod:
Если у вас несколько сред, вы можете рассмотреть использование файла конфигурации docker-compose.override.yml . При таком подходе вы добавляете свою базовую конфигурацию в файл docker-compose.yml , а затем используете файл docker-compose.override.yml для переопределения этих параметров конфигурации в зависимости от среды.
Обратите внимание на значение по умолчанию command
. Мы используем Gunicorn, а не сервер разработки Masonite. Мы также удалили том из web
службы, так как он нам не нужен в рабочей среде. Наконец, мы используем отдельные файлы переменных среды для определения переменных среды для обеих служб, которые будут переданы в контейнер во время выполнения.
.env.prod :
APP_DEBUG=False
APP_ENV=prod
APP_KEY=GM28x-FeI1sM72tgtsgikLcT-AryyVOiY8etOGr7q7o=
APP_URL=http://localhost:8000
HASHING_FUNCTION=bcrypt
MAIL_DRIVER=terminal
DB_CONNECTION=postgres
DB_HOST=db
DB_PORT=5432
DB_DATABASE=hello_masonite_prod
DB_USERNAME=hello_masonite
DB_PASSWORD=hello_masonite
DB_LOG=True
QUEUE_DRIVER=async
.env.prod.db :
POSTGRES_USER=hello_masonite
POSTGRES_PASSWORD=hello_masonite
POSTGRES_DB=hello_masonite_prod
Добавьте два файла в корень проекта. Вы, вероятно, захотите сохранить их вне контроля версий, поэтому добавьте их в файл .gitignore .
Снесите контейнеры разработки (и связанные с ними тома с -v
флагом):
$ docker-compose down -v
Затем создайте производственные образы и разверните контейнеры:
$ docker-compose -f docker-compose.prod.yml up -d --build
Убедитесь, что hello_masonite_prod
база данных была создана вместе с users
таблицей. Проверьте http://localhost:8000/ .
Опять же, если контейнер не запускается, проверьте наличие ошибок в логах через
docker-compose -f docker-compose.prod.yml logs -f
.
Вы заметили, что мы по-прежнему выполняем команды migrate:refresh (которое очищает базу данных) и migrate при каждом запуске контейнера? Это нормально в разработке, но давайте создадим новый файл точки входа для производства.
точка входа.prod.sh :
#!/bin/sh
if [ "$DB_CONNECTION" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $DB_HOST $DB_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
exec "$@"
В качестве альтернативы, вместо создания нового файла точки входа вы можете изменить существующий следующим образом:
#!/bin/sh if [ "$DB_CONNECTION" = "postgres" ] then echo "Waiting for postgres..." while ! nc -z $DB_HOST $DB_PORT; do sleep 0.1 done echo "PostgreSQL started" fi if [ "$APP_ENV" = "local" ] then echo "Refreshing the database..." craft migrate:refresh # you may want to remove this echo "Applying migrations..." craft migrate echo "Tables created" fi exec "$@"
Чтобы использовать этот файл, создайте новый файл Dockerfile с именем Dockerfile.prod для использования с производственными сборками:
###########
# BUILDER #
###########
# pull official base image
FROM python:3.10.5-alpine as builder
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV TZ=UTC
# install system dependencies
RUN apk update && apk --no-cache add \
libressl-dev libffi-dev gcc musl-dev python3-dev openssl-dev cargo \
postgresql-dev
# lint
RUN pip install --upgrade pip
RUN pip install flake8==4.0.1
COPY . .
RUN flake8 --ignore=E501,F401,E303,E402 .
# 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.5-alpine
# 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
# set environment variables
ENV TZ=UTC
# install dependencies
RUN apk update && apk --no-cache add \
libressl-dev libffi-dev gcc musl-dev python3-dev openssl-dev cargo \
postgresql-dev
COPY --from=builder /usr/src/app/wheels /wheels
COPY --from=builder /usr/src/app/requirements.txt .
RUN pip install --no-cache /wheels/*
# copy project
COPY . $APP_HOME
RUN chmod +x /home/app/web/entrypoint.prod.sh
# 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"]
Here, we used a Docker multi-stage build to reduce the final image size. Essentially, builder
is a temporary image that's used for building the Python wheels. The wheels are then copied over to the final production image and the builder
image is discarded.
You could take the multi-stage build approach a step further and use a single Dockerfile instead of creating two Dockerfiles. Think of the pros and cons of using this approach over two different files.
Вы заметили, что мы создали пользователя без полномочий root? По умолчанию Docker запускает процессы контейнера как root внутри контейнера. Это плохая практика, поскольку злоумышленники могут получить root-доступ к хосту Docker, если им удастся выйти из контейнера. Если вы root в контейнере, вы будете root на хосте.
Обновите web
службу в файле docker-compose.prod.yml для сборки с помощью Dockerfile.prod :
web:
build:
context: ./web
dockerfile: Dockerfile.prod
command: gunicorn --bind 0.0.0.0:8000 wsgi:application
ports:
- 8000:8000
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 craft migrate
Затем давайте добавим Nginx, который будет выступать в качестве обратного прокси -сервера для Gunicorn для обработки клиентских запросов, а также для обслуживания статических файлов.
Добавьте сервис в docker-compose.prod.yml :
nginx:
build: ./nginx
ports:
- 1337:80
depends_on:
- web
Затем в локальном корне проекта создайте следующие файлы и папки:
└── nginx
├── Dockerfile
└── nginx.conf
Докерфайл :
FROM nginx:1.23.1-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
nginx.conf :
upstream hello_masonite {
server web:8000;
}
server {
listen 80;
location / {
proxy_pass http://hello_masonite;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
Ознакомьтесь с разделом «Понимание структуры файла конфигурации Nginx и контекстов конфигурации» , чтобы получить дополнительную информацию о файле конфигурации Nginx.
Затем обновите web
службу в docker-compose.prod.yml , заменив ports
на expose
:
web:
build:
context: ./web
dockerfile: Dockerfile.prod
command: gunicorn --bind 0.0.0.0:8000 wsgi:application
expose:
- 8000
env_file:
- .env.prod
depends_on:
- db
Теперь порт 8000 открыт только внутри, для других служб Docker. Порт больше не будет опубликован на хост-компьютере.
Чтобы узнать больше о портах и экспонировании, просмотрите этот вопрос о переполнении стека.
Проверьте это снова.
$ 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 craft migrate
Убедитесь, что приложение запущено и работает по адресу http://localhost:1337 .
Теперь структура вашего проекта должна выглядеть так:
├── .env.dev
├── .env.prod
├── .env.prod.db
├── .gitignore
├── docker-compose.prod.yml
├── docker-compose.yml
├── nginx
│ ├── Dockerfile
│ └── nginx.conf
└── web
├── .env.testing
├── Dockerfile
├── Dockerfile.prod
├── Kernel.py
├── app
│ ├── __init__.py
│ ├── controllers
│ │ ├── WelcomeController.py
│ │ └── __init__.py
│ ├── middlewares
│ │ ├── AuthenticationMiddleware.py
│ │ ├── VerifyCsrfToken.py
│ │ └── __init__.py
│ ├── models
│ │ └── User.py
│ └── providers
│ ├── AppProvider.py
│ └── __init__.py
├── config
│ ├── __init__.py
│ ├── application.py
│ ├── auth.py
│ ├── broadcast.py
│ ├── cache.py
│ ├── database.py
│ ├── exceptions.py
│ ├── filesystem.py
│ ├── mail.py
│ ├── notification.py
│ ├── providers.py
│ ├── queue.py
│ ├── security.py
│ └── session.py
├── craft
├── databases
│ ├── migrations
│ │ ├── 2021_01_09_033202_create_password_reset_table.py
│ │ └── 2021_01_09_043202_create_users_table.py
│ └── seeds
│ ├── __init__.py
│ ├── database_seeder.py
│ └── user_table_seeder.py
├── entrypoint.prod.sh
├── entrypoint.sh
├── makefile
├── package.json
├── pyproject.toml
├── requirements.txt
├── resources
│ ├── css
│ │ └── app.css
│ └── js
│ ├── app.js
│ └── bootstrap.js
├── routes
│ └── web.py
├── setup.cfg
├── storage
│ ├── .gitignore
│ └── public
│ ├── favicon.ico
│ ├── logo.png
│ └── robots.txt
├── templates
│ ├── __init__.py
│ ├── base.html
│ ├── errors
│ │ ├── 403.html
│ │ ├── 404.html
│ │ └── 500.html
│ ├── maintenance.html
│ └── welcome.html
├── tests
│ ├── TestCase.py
│ ├── __init__.py
│ └── unit
│ └── test_basic_testcase.py
├── webpack.mix.js
└── wsgi.py
Когда закончите, опустите контейнеры:
$ docker-compose -f docker-compose.prod.yml down -v
Поскольку Gunicorn является сервером приложений, он не будет обслуживать статические файлы. Итак, как следует обрабатывать как статические, так и мультимедийные файлы в этой конкретной конфигурации?
Сначала обновите STATICFILES
конфигурацию в web/config/filesystem.py :
STATICFILES = {
# folder # template alias
"storage/static": "static/",
"storage/compiled": "static/",
"storage/uploads": "uploads/",
"storage/public": "/",
}
По сути, все статические файлы, хранящиеся в каталогах «storage/static» (обычные файлы CSS и JS) и «storage/compiled» (файлы SASS и LESS), будут обслуживаться по /static/
URL-адресу.
Чтобы включить компиляцию ресурсов, при условии, что у вас установлен NPM , установите зависимости:
$ cd web
$ npm install
Затем, чтобы скомпилировать активы, запустите:
$ npm run dev
Чтобы узнать больше об усложнении ассетов, ознакомьтесь с компиляцией ассетов из документации Masonite.
Затем, чтобы протестировать обычный статический ресурс, добавьте текстовый файл hello.txt в «web/storage/static»:
hi!
Для проверки сначала пересоберите образы и запустите новые контейнеры, как обычно. После этого убедитесь, что следующие статические ресурсы загружаются правильно:
Для производства добавьте том к службам web
and в файле docker-compose.prod.yml , чтобы каждый контейнер имел общий каталог «storage»:nginx
version: '3.8'
services:
web:
build:
context: ./web
dockerfile: Dockerfile.prod
command: gunicorn --bind 0.0.0.0:8000 wsgi:application
volumes:
- storage_volume:/home/app/web/storage
expose:
- 8000
env_file:
- .env.prod
depends_on:
- db
db:
image: postgres:14.4-alpine
volumes:
- postgres_data_prod:/var/lib/postgresql/data/
env_file:
- .env.prod.db
nginx:
build: ./nginx
volumes:
- storage_volume:/home/app/web/storage
ports:
- 1337:80
depends_on:
- web
volumes:
postgres_data_prod:
storage_volume:
Затем обновите конфигурацию Nginx, чтобы направлять запросы статических файлов в соответствующую папку:
upstream hello_masonite {
server web:8000;
}
server {
listen 80;
location /static/ {
alias /home/app/web/storage/static/;
}
location ~ ^/(favicon.ico|robots.txt)/ {
alias /home/app/web/storage/public/;
}
location / {
proxy_pass http://hello_masonite;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
Теперь запросы к статическим файлам будут обслуживаться соответствующим образом:
URL-адрес запроса | Папка |
---|---|
/статический/* | "хранилище/статическое", "хранилище/скомпилированное" |
/favicon.ico, /robots.txt | "хранение/общественное" |
Раскрутите контейнеры разработки:
$ docker-compose down -v
Тест:
$ docker-compose -f docker-compose.prod.yml up -d --build
Опять же, убедитесь, что следующие статические ресурсы загружены правильно:
Вы также можете проверить в журналах — через docker-compose -f docker-compose.prod.yml logs -f
— что запросы к статическим файлам успешно обрабатываются через Nginx:
nginx_1 | 172.28.0.1 - - [2022-07-20:01:39:43 +0000] "GET /robots.txt HTTP/1.1" 200 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36" "-"
nginx_1 | 172.28.0.1 - - [2022-07-20:01:39:52 +0000] "GET /static/hello.txt HTTP/1.1" 200 4 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36" "-"
nginx_1 | 172.28.0.1 - - [2022-07-20:01:39:59 +0000] "GET /static/css/app.css HTTP/1.1" 200 649 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36" "-"
Когда закончите, принесите контейнеры:
$ docker-compose -f docker-compose.prod.yml down -v
Чтобы проверить обработку загружаемых пользователем медиафайлов, обновите блок контента в шаблоне web/templates/welcome.html :
{% block content %}
<html>
<body>
<form action="/" method="POST" enctype="multipart/form-data">
{{ csrf_field }}
<input type="file" name="image_upload">
<input type="submit" value="submit" />
</form>
{% if image_url %}
<p>File uploaded at: <a href="{{ image_url }}">{{ image_url }}</a></p>
{% endif %}
</body>
</html>
{% endblock %}
Добавьте новый метод, вызываемый в upload
web /app/controllers/WelcomeController.py :WelcomeController
def upload(self, storage: Storage, view: View, request: Request):
filename = storage.disk("local").put_file("image_upload", request.input("image_upload"))
return view.render("welcome", {"image_url": f"/framework/filesystem/{filename}"})
Не забывайте об импорте:
from masonite.filesystem import Storage
from masonite.request import Request
Затем подключите контроллер к новому маршруту в web/routes/web.py :
from masonite.routes import Route
ROUTES = [
Route.get("/", "WelcomeController@show"),
Route.get("/sample", "WelcomeController@show"),
Route.post("/", "WelcomeController@upload"),
]
Тест:
$ docker-compose up -d --build
Вы должны иметь возможность загрузить изображение по адресу http://localhost:8000/ , а затем просмотреть его по адресу http://localhost:8000/uploads/IMAGE_FILE_NAME .
Для производства обновите конфигурацию Nginx, чтобы направлять запросы медиафайлов в папку «uploads»:
upstream hello_masonite {
server web:8000;
}
server {
listen 80;
location /static/ {
alias /home/app/web/storage/static/;
}
location ~ ^/(favicon.ico|robots.txt)/ {
alias /home/app/web/storage/public/;
}
location /uploads/ {
alias /home/app/web/storage/framework/filesystem/image_upload/;
}
location / {
proxy_pass http://hello_masonite;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
Перестроить:
$ docker-compose down -v
$ docker-compose -f docker-compose.prod.yml up -d --build
Проверьте это в последний раз:
В этом руководстве мы рассмотрели, как контейнеризировать приложение Masonite с помощью Postgres для разработки. Мы также создали готовый к работе файл Docker Compose, который добавляет в смесь Gunicorn и Nginx для обработки статических и мультимедийных файлов. Теперь вы можете протестировать производственную установку локально.
С точки зрения фактического развертывания в производственной среде вы, вероятно, захотите использовать:
db
иnginx
Вы можете найти код в репозитории masonite-on-docker .
Спасибо за чтение!
Источник: https://testdriven.io
#docker #postgres #gunicorn #nginx
1661105160
Это пошаговое руководство, в котором подробно описано, как настроить Masonite, веб-фреймворк на основе Python, для работы в Docker с Postgres. Для производственных сред мы добавим Nginx и Gunicorn. Мы также рассмотрим, как обслуживать статические и загруженные пользователем медиафайлы через Nginx.
Зависимости :
Создайте каталог проекта, установите Masonite и создайте новый проект Masonite:
$ mkdir masonite-on-docker && cd masonite-on-docker
$ python3.10 -m venv env
$ source env/bin/activate
(env)$ pip install masonite==4.16.2
(env)$ project start web
(env)$ cd web
(env)$ project install
(env)$ python craft serve
Перейдите по адресу http://localhost:8000/ , чтобы просмотреть экран приветствия Masonite. Убейте сервер и выйдите из виртуальной среды, как только закончите. Идите вперед и удалите виртуальную среду. Теперь у нас есть простой проект Masonite для работы.
Далее, прежде чем добавлять Docker, давайте немного подчистим структуру проекта:
Теперь структура вашего проекта должна выглядеть так:
├── .env.dev
└── web
├── .env.testing
├── Kernel.py
├── app
│ ├── __init__.py
│ ├── controllers
│ │ ├── WelcomeController.py
│ │ └── __init__.py
│ ├── middlewares
│ │ ├── AuthenticationMiddleware.py
│ │ ├── VerifyCsrfToken.py
│ │ └── __init__.py
│ ├── models
│ │ └── User.py
│ └── providers
│ ├── AppProvider.py
│ └── __init__.py
├── config
│ ├── __init__.py
│ ├── application.py
│ ├── auth.py
│ ├── broadcast.py
│ ├── cache.py
│ ├── database.py
│ ├── exceptions.py
│ ├── filesystem.py
│ ├── mail.py
│ ├── notification.py
│ ├── providers.py
│ ├── queue.py
│ ├── security.py
│ └── session.py
├── craft
├── databases
│ ├── migrations
│ │ ├── 2021_01_09_033202_create_password_reset_table.py
│ │ └── 2021_01_09_043202_create_users_table.py
│ └── seeds
│ ├── __init__.py
│ ├── database_seeder.py
│ └── user_table_seeder.py
├── makefile
├── package.json
├── pyproject.toml
├── requirements.txt
├── resources
│ ├── css
│ │ └── app.css
│ └── js
│ ├── app.js
│ └── bootstrap.js
├── routes
│ └── web.py
├── setup.cfg
├── storage
│ ├── .gitignore
│ └── public
│ ├── favicon.ico
│ ├── logo.png
│ └── robots.txt
├── templates
│ ├── __init__.py
│ ├── base.html
│ ├── errors
│ │ ├── 403.html
│ │ ├── 404.html
│ │ └── 500.html
│ ├── maintenance.html
│ └── welcome.html
├── tests
│ ├── TestCase.py
│ ├── __init__.py
│ └── unit
│ └── test_basic_testcase.py
├── webpack.mix.js
└── wsgi.py
Установите Docker , если у вас его еще нет, затем добавьте файл Dockerfile в каталог «web»:
# pull official base image
FROM python:3.10.5-alpine
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV TZ=UTC
# install system dependencies
RUN apk update && apk --no-cache add \
libressl-dev libffi-dev gcc musl-dev python3-dev openssl-dev cargo
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
# copy project
COPY . .
Итак, мы начали с образа Docker на основе Alpine для Python 3.10.5. Затем мы устанавливаем рабочий каталог вместе с тремя переменными среды:
PYTHONDONTWRITEBYTECODE
: запрещает Python записывать файлы pyc на диск (эквивалентно python -B
опции )PYTHONUNBUFFERED
: запрещает Python буферизовать stdout и stderr (эквивалентно python -u
опции )TZ=UTC
устанавливает часовой пояс в контейнере в UTC, что необходимо для ведения журналаЗатем мы установили некоторые зависимости системного уровня, необходимые для Python. Обратите внимание на зависимости openssl-dev
и . cargo
Это необходимо, так как библиотека криптографии теперь зависит от Rust . Подробнее см. в статье Построение криптографии в Linux .
Наконец, мы обновили Pip, скопировали файл requirements.txt , установили зависимости и скопировали само приложение Masonite.
Ознакомьтесь с рекомендациями Docker для разработчиков Python , чтобы узнать больше о структурировании файлов Docker, а также о некоторых рекомендациях по настройке Docker для разработки на основе Python.
Затем добавьте файл docker-compose.yml в корень проекта:
version: '3.8'
services:
web:
build: ./web
command: python craft serve -p 8000 -b 0.0.0.0
volumes:
- ./web/:/usr/src/app/
ports:
- 8000:8000
env_file:
- .env.dev
Просмотрите справочник по файлу Compose, чтобы узнать, как работает этот файл.
Давайте упростим .env.dev , удалив все неиспользуемые переменные:
APP_DEBUG=True
APP_ENV=local
APP_KEY=zWDMwC0aNfVk8Ao1NyVJC_LiGD9tHJtVn_uCPeaaTNY=
APP_URL=http://localhost:8000
HASHING_FUNCTION=bcrypt
MAIL_DRIVER=terminal
DB_CONNECTION=sqlite
SQLITE_DB_DATABASE=masonite.sqlite3
DB_HOST=127.0.0.1
DB_USERNAME=root
DB_PASSWORD=root
DB_DATABASE=masonite
DB_PORT=3306
DB_LOG=True
QUEUE_DRIVER=async
Создайте образ:
$ docker-compose build
После сборки образа запустите контейнер:
$ docker-compose up -d
Перейдите по адресу http://localhost:8000/ , чтобы снова просмотреть экран приветствия.
Проверьте наличие ошибок в журналах, если это не работает через
docker-compose logs -f
.
Чтобы протестировать автоматическую перезагрузку, сначала откройте журналы Docker -- docker-compose logs -f
-- и затем внесите изменения в файл web/routes/web.py локально:
from masonite.routes import Route
ROUTES = [
Route.get("/", "WelcomeController@show"),
Route.get("/sample", "WelcomeController@show")
]
Как только вы сохраните, вы должны увидеть, как приложение перезагружается в вашем терминале, например:
* Detected change in '/usr/src/app/routes/web.py', reloading
* Restarting with watchdog (inotify)
Убедитесь , что http://localhost:8000/sample работает должным образом.
Чтобы настроить Postgres, нам нужно добавить новую службу в файл docker-compose.yml , обновить переменные среды и установить Psycopg2 .
Во- первых, добавьте новую службу с именем db
в docker-compose.yml :
version: '3.8'
services:
web:
build: ./web
command: python craft serve -p 8000 -b 0.0.0.0
volumes:
- ./web/:/usr/src/app/
ports:
- 8000:8000
env_file:
- .env.dev
depends_on:
- db
db:
image: postgres:14.4-alpine
volumes:
- postgres_data_dev:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=hello_masonite
- POSTGRES_PASSWORD=hello_masonite
- POSTGRES_DB=hello_masonite_dev
volumes:
postgres_data_dev:
Чтобы сохранить данные после окончания срока службы контейнера, мы настроили том. Эта конфигурация будет привязана postgres_data_dev
к каталогу «/var/lib/postgresql/data/» в контейнере.
Мы также добавили ключ среды для определения имени базы данных по умолчанию и установки имени пользователя и пароля.
Дополнительные сведения см. в разделе «Переменные среды» на странице Postgres Docker Hub .
Также обновите следующие переменные среды, связанные с базой данных, в файле .env.dev :
DB_CONNECTION=postgres
DB_HOST=db
DB_PORT=5432
DB_DATABASE=hello_masonite_dev
DB_USERNAME=hello_masonite
DB_PASSWORD=hello_masonite
Просмотрите файл web/config/database.py для получения информации о том, как база данных настроена на основе определенных переменных среды для проекта Masonite.
Обновите Dockerfile, чтобы установить соответствующие пакеты, необходимые для Psycopg2:
# pull official base image
FROM python:3.10.5-alpine
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV TZ=UTC
# install system dependencies
RUN apk update && apk --no-cache add \
libressl-dev libffi-dev gcc musl-dev python3-dev openssl-dev cargo \
postgresql-dev
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
# copy project
COPY . .
Добавьте Psycopg2 в web/requirements.txt :
masonite>=4.0,<5.0
masonite-orm>=2.0,<3.0
psycopg2-binary==2.9.3
Просмотрите этот выпуск GitHub для получения дополнительной информации об установке Psycopg2 в образе Docker на основе Alpine.
Создайте новый образ и запустите два контейнера:
$ docker-compose up -d --build
Примените миграции (из папки "web/databases/migrations"):
$ docker-compose exec web python craft migrate
Тебе следует увидеть:
Migrating: 2021_01_09_033202_create_password_reset_table
Migrated: 2021_01_09_033202_create_password_reset_table (0.01s)
Migrating: 2021_01_09_043202_create_users_table
Migrated: 2021_01_09_043202_create_users_table (0.02s)
Убедитесь, что users
таблица создана:
$ docker-compose exec db psql --username=hello_masonite --dbname=hello_masonite_dev
psql (14.4)
Type "help" for help.
hello_masonite_dev=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
--------------------+----------------+----------+------------+------------+-----------------------------------
hello_masonite_dev | hello_masonite | UTF8 | en_US.utf8 | en_US.utf8 |
postgres | hello_masonite | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | hello_masonite | UTF8 | en_US.utf8 | en_US.utf8 | =c/hello_masonite +
| | | | | hello_masonite=CTc/hello_masonite
template1 | hello_masonite | UTF8 | en_US.utf8 | en_US.utf8 | =c/hello_masonite +
| | | | | hello_masonite=CTc/hello_masonite
(4 rows)
hello_masonite_dev=# \c hello_masonite_dev
You are now connected to database "hello_masonite_dev" as user "hello_masonite".
hello_masonite_dev=# \dt
List of relations
Schema | Name | Type | Owner
--------+-----------------+-------+----------------
public | migrations | table | hello_masonite
public | password_resets | table | hello_masonite
public | users | table | hello_masonite
(3 rows)
hello_masonite_dev=# \q
Вы также можете проверить, что том был создан, запустив:
$ docker volume inspect masonite-on-docker_postgres_data_dev
Вы должны увидеть что-то похожее на:
[
{
"CreatedAt": "2022-07-22T20:07:50Z",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "masonite-on-docker",
"com.docker.compose.version": "2.6.1",
"com.docker.compose.volume": "postgres_data_dev"
},
"Mountpoint": "/var/lib/docker/volumes/masonite-on-docker_postgres_data_dev/_data",
"Name": "masonite-on-docker_postgres_data_dev",
"Options": null,
"Scope": "local"
}
]
Затем добавьте файл entrypoint.sh в каталог «web», чтобы убедиться, что Postgres запущен и работоспособен , прежде чем применять миграции и запускать сервер разработки Masonite:
#!/bin/sh
if [ "$DB_CONNECTION" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $DB_HOST $DB_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
python craft migrate:refresh # you may want to remove this
python craft migrate
exec "$@"
Обратите внимание на переменные окружения.
Затем обновите Dockerfile, чтобы запустить файл entrypoint.sh в качестве команды точки входа Docker :
# pull official base image
FROM python:3.10.5-alpine
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV TZ=UTC
# install system dependencies
RUN apk update && apk --no-cache add \
libressl-dev libffi-dev gcc musl-dev python3-dev openssl-dev cargo \
postgresql-dev
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
# copy project
COPY . .
# run entrypoint.sh
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
Обновите права доступа к файлу локально:
$ chmod +x web/entrypoint.sh
Проверьте еще раз:
Хотите посеять несколько пользователей?
$ docker-compose exec web python craft seed:run
$ docker-compose exec db psql --username=hello_masonite --dbname=hello_masonite_dev
psql (14.4)
Type "help" for help.
hello_masonite_dev=# \c hello_masonite_dev
You are now connected to database "hello_masonite_dev" as user "hello_masonite".
hello_masonite_dev=# select count(*) from users;
count
-------
1
(1 row)
hello_masonite_dev=# \q
Двигаясь дальше, для производственных сред давайте добавим Gunicorn , сервер WSGI производственного уровня, в файл требований:
masonite>=4.0,<5.0
masonite-orm>=2.0,<3.0
psycopg2-binary==2.9.3
gunicorn==20.1.0
Поскольку мы по-прежнему хотим использовать встроенный сервер Masonite в разработке, создайте новый файл компоновки с именем docker-compose.prod.yml для производства:
version: '3.8'
services:
web:
build: ./web
command: gunicorn --bind 0.0.0.0:8000 wsgi:application
ports:
- 8000:8000
env_file:
- .env.prod
depends_on:
- db
db:
image: postgres:14.4-alpine
volumes:
- postgres_data_prod:/var/lib/postgresql/data/
env_file:
- .env.prod.db
volumes:
postgres_data_prod:
Если у вас несколько сред, вы можете рассмотреть использование файла конфигурации docker-compose.override.yml . При таком подходе вы добавляете свою базовую конфигурацию в файл docker-compose.yml , а затем используете файл docker-compose.override.yml для переопределения этих параметров конфигурации в зависимости от среды.
Обратите внимание на значение по умолчанию command
. Мы используем Gunicorn, а не сервер разработки Masonite. Мы также удалили том из web
службы, так как он нам не нужен в рабочей среде. Наконец, мы используем отдельные файлы переменных среды для определения переменных среды для обеих служб, которые будут переданы в контейнер во время выполнения.
.env.prod :
APP_DEBUG=False
APP_ENV=prod
APP_KEY=GM28x-FeI1sM72tgtsgikLcT-AryyVOiY8etOGr7q7o=
APP_URL=http://localhost:8000
HASHING_FUNCTION=bcrypt
MAIL_DRIVER=terminal
DB_CONNECTION=postgres
DB_HOST=db
DB_PORT=5432
DB_DATABASE=hello_masonite_prod
DB_USERNAME=hello_masonite
DB_PASSWORD=hello_masonite
DB_LOG=True
QUEUE_DRIVER=async
.env.prod.db :
POSTGRES_USER=hello_masonite
POSTGRES_PASSWORD=hello_masonite
POSTGRES_DB=hello_masonite_prod
Добавьте два файла в корень проекта. Вы, вероятно, захотите сохранить их вне контроля версий, поэтому добавьте их в файл .gitignore .
Снесите контейнеры разработки (и связанные с ними тома с -v
флагом):
$ docker-compose down -v
Затем создайте производственные образы и разверните контейнеры:
$ docker-compose -f docker-compose.prod.yml up -d --build
Убедитесь, что hello_masonite_prod
база данных была создана вместе с users
таблицей. Проверьте http://localhost:8000/ .
Опять же, если контейнер не запускается, проверьте наличие ошибок в логах через
docker-compose -f docker-compose.prod.yml logs -f
.
Вы заметили, что мы по-прежнему выполняем команды migrate:refresh (которое очищает базу данных) и migrate при каждом запуске контейнера? Это нормально в разработке, но давайте создадим новый файл точки входа для производства.
точка входа.prod.sh :
#!/bin/sh
if [ "$DB_CONNECTION" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $DB_HOST $DB_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
exec "$@"
В качестве альтернативы, вместо создания нового файла точки входа вы можете изменить существующий следующим образом:
#!/bin/sh if [ "$DB_CONNECTION" = "postgres" ] then echo "Waiting for postgres..." while ! nc -z $DB_HOST $DB_PORT; do sleep 0.1 done echo "PostgreSQL started" fi if [ "$APP_ENV" = "local" ] then echo "Refreshing the database..." craft migrate:refresh # you may want to remove this echo "Applying migrations..." craft migrate echo "Tables created" fi exec "$@"
Чтобы использовать этот файл, создайте новый файл Dockerfile с именем Dockerfile.prod для использования с производственными сборками:
###########
# BUILDER #
###########
# pull official base image
FROM python:3.10.5-alpine as builder
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV TZ=UTC
# install system dependencies
RUN apk update && apk --no-cache add \
libressl-dev libffi-dev gcc musl-dev python3-dev openssl-dev cargo \
postgresql-dev
# lint
RUN pip install --upgrade pip
RUN pip install flake8==4.0.1
COPY . .
RUN flake8 --ignore=E501,F401,E303,E402 .
# 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.5-alpine
# 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
# set environment variables
ENV TZ=UTC
# install dependencies
RUN apk update && apk --no-cache add \
libressl-dev libffi-dev gcc musl-dev python3-dev openssl-dev cargo \
postgresql-dev
COPY --from=builder /usr/src/app/wheels /wheels
COPY --from=builder /usr/src/app/requirements.txt .
RUN pip install --no-cache /wheels/*
# copy project
COPY . $APP_HOME
RUN chmod +x /home/app/web/entrypoint.prod.sh
# 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"]
Here, we used a Docker multi-stage build to reduce the final image size. Essentially, builder
is a temporary image that's used for building the Python wheels. The wheels are then copied over to the final production image and the builder
image is discarded.
You could take the multi-stage build approach a step further and use a single Dockerfile instead of creating two Dockerfiles. Think of the pros and cons of using this approach over two different files.
Вы заметили, что мы создали пользователя без полномочий root? По умолчанию Docker запускает процессы контейнера как root внутри контейнера. Это плохая практика, поскольку злоумышленники могут получить root-доступ к хосту Docker, если им удастся выйти из контейнера. Если вы root в контейнере, вы будете root на хосте.
Обновите web
службу в файле docker-compose.prod.yml для сборки с помощью Dockerfile.prod :
web:
build:
context: ./web
dockerfile: Dockerfile.prod
command: gunicorn --bind 0.0.0.0:8000 wsgi:application
ports:
- 8000:8000
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 craft migrate
Затем давайте добавим Nginx, который будет выступать в качестве обратного прокси -сервера для Gunicorn для обработки клиентских запросов, а также для обслуживания статических файлов.
Добавьте сервис в docker-compose.prod.yml :
nginx:
build: ./nginx
ports:
- 1337:80
depends_on:
- web
Затем в локальном корне проекта создайте следующие файлы и папки:
└── nginx
├── Dockerfile
└── nginx.conf
Докерфайл :
FROM nginx:1.23.1-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
nginx.conf :
upstream hello_masonite {
server web:8000;
}
server {
listen 80;
location / {
proxy_pass http://hello_masonite;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
Ознакомьтесь с разделом «Понимание структуры файла конфигурации Nginx и контекстов конфигурации» , чтобы получить дополнительную информацию о файле конфигурации Nginx.
Затем обновите web
службу в docker-compose.prod.yml , заменив ports
на expose
:
web:
build:
context: ./web
dockerfile: Dockerfile.prod
command: gunicorn --bind 0.0.0.0:8000 wsgi:application
expose:
- 8000
env_file:
- .env.prod
depends_on:
- db
Теперь порт 8000 открыт только внутри, для других служб Docker. Порт больше не будет опубликован на хост-компьютере.
Чтобы узнать больше о портах и экспонировании, просмотрите этот вопрос о переполнении стека.
Проверьте это снова.
$ 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 craft migrate
Убедитесь, что приложение запущено и работает по адресу http://localhost:1337 .
Теперь структура вашего проекта должна выглядеть так:
├── .env.dev
├── .env.prod
├── .env.prod.db
├── .gitignore
├── docker-compose.prod.yml
├── docker-compose.yml
├── nginx
│ ├── Dockerfile
│ └── nginx.conf
└── web
├── .env.testing
├── Dockerfile
├── Dockerfile.prod
├── Kernel.py
├── app
│ ├── __init__.py
│ ├── controllers
│ │ ├── WelcomeController.py
│ │ └── __init__.py
│ ├── middlewares
│ │ ├── AuthenticationMiddleware.py
│ │ ├── VerifyCsrfToken.py
│ │ └── __init__.py
│ ├── models
│ │ └── User.py
│ └── providers
│ ├── AppProvider.py
│ └── __init__.py
├── config
│ ├── __init__.py
│ ├── application.py
│ ├── auth.py
│ ├── broadcast.py
│ ├── cache.py
│ ├── database.py
│ ├── exceptions.py
│ ├── filesystem.py
│ ├── mail.py
│ ├── notification.py
│ ├── providers.py
│ ├── queue.py
│ ├── security.py
│ └── session.py
├── craft
├── databases
│ ├── migrations
│ │ ├── 2021_01_09_033202_create_password_reset_table.py
│ │ └── 2021_01_09_043202_create_users_table.py
│ └── seeds
│ ├── __init__.py
│ ├── database_seeder.py
│ └── user_table_seeder.py
├── entrypoint.prod.sh
├── entrypoint.sh
├── makefile
├── package.json
├── pyproject.toml
├── requirements.txt
├── resources
│ ├── css
│ │ └── app.css
│ └── js
│ ├── app.js
│ └── bootstrap.js
├── routes
│ └── web.py
├── setup.cfg
├── storage
│ ├── .gitignore
│ └── public
│ ├── favicon.ico
│ ├── logo.png
│ └── robots.txt
├── templates
│ ├── __init__.py
│ ├── base.html
│ ├── errors
│ │ ├── 403.html
│ │ ├── 404.html
│ │ └── 500.html
│ ├── maintenance.html
│ └── welcome.html
├── tests
│ ├── TestCase.py
│ ├── __init__.py
│ └── unit
│ └── test_basic_testcase.py
├── webpack.mix.js
└── wsgi.py
Когда закончите, опустите контейнеры:
$ docker-compose -f docker-compose.prod.yml down -v
Поскольку Gunicorn является сервером приложений, он не будет обслуживать статические файлы. Итак, как следует обрабатывать как статические, так и мультимедийные файлы в этой конкретной конфигурации?
Сначала обновите STATICFILES
конфигурацию в web/config/filesystem.py :
STATICFILES = {
# folder # template alias
"storage/static": "static/",
"storage/compiled": "static/",
"storage/uploads": "uploads/",
"storage/public": "/",
}
По сути, все статические файлы, хранящиеся в каталогах «storage/static» (обычные файлы CSS и JS) и «storage/compiled» (файлы SASS и LESS), будут обслуживаться по /static/
URL-адресу.
Чтобы включить компиляцию ресурсов, при условии, что у вас установлен NPM , установите зависимости:
$ cd web
$ npm install
Затем, чтобы скомпилировать активы, запустите:
$ npm run dev
Чтобы узнать больше об усложнении ассетов, ознакомьтесь с компиляцией ассетов из документации Masonite.
Затем, чтобы протестировать обычный статический ресурс, добавьте текстовый файл hello.txt в «web/storage/static»:
hi!
Для проверки сначала пересоберите образы и запустите новые контейнеры, как обычно. После этого убедитесь, что следующие статические ресурсы загружаются правильно:
Для производства добавьте том к службам web
and в файле docker-compose.prod.yml , чтобы каждый контейнер имел общий каталог «storage»:nginx
version: '3.8'
services:
web:
build:
context: ./web
dockerfile: Dockerfile.prod
command: gunicorn --bind 0.0.0.0:8000 wsgi:application
volumes:
- storage_volume:/home/app/web/storage
expose:
- 8000
env_file:
- .env.prod
depends_on:
- db
db:
image: postgres:14.4-alpine
volumes:
- postgres_data_prod:/var/lib/postgresql/data/
env_file:
- .env.prod.db
nginx:
build: ./nginx
volumes:
- storage_volume:/home/app/web/storage
ports:
- 1337:80
depends_on:
- web
volumes:
postgres_data_prod:
storage_volume:
Затем обновите конфигурацию Nginx, чтобы направлять запросы статических файлов в соответствующую папку:
upstream hello_masonite {
server web:8000;
}
server {
listen 80;
location /static/ {
alias /home/app/web/storage/static/;
}
location ~ ^/(favicon.ico|robots.txt)/ {
alias /home/app/web/storage/public/;
}
location / {
proxy_pass http://hello_masonite;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
Теперь запросы к статическим файлам будут обслуживаться соответствующим образом:
URL-адрес запроса | Папка |
---|---|
/статический/* | "хранилище/статическое", "хранилище/скомпилированное" |
/favicon.ico, /robots.txt | "хранение/общественное" |
Раскрутите контейнеры разработки:
$ docker-compose down -v
Тест:
$ docker-compose -f docker-compose.prod.yml up -d --build
Опять же, убедитесь, что следующие статические ресурсы загружены правильно:
Вы также можете проверить в журналах — через docker-compose -f docker-compose.prod.yml logs -f
— что запросы к статическим файлам успешно обрабатываются через Nginx:
nginx_1 | 172.28.0.1 - - [2022-07-20:01:39:43 +0000] "GET /robots.txt HTTP/1.1" 200 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36" "-"
nginx_1 | 172.28.0.1 - - [2022-07-20:01:39:52 +0000] "GET /static/hello.txt HTTP/1.1" 200 4 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36" "-"
nginx_1 | 172.28.0.1 - - [2022-07-20:01:39:59 +0000] "GET /static/css/app.css HTTP/1.1" 200 649 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36" "-"
Когда закончите, принесите контейнеры:
$ docker-compose -f docker-compose.prod.yml down -v
Чтобы проверить обработку загружаемых пользователем медиафайлов, обновите блок контента в шаблоне web/templates/welcome.html :
{% block content %}
<html>
<body>
<form action="/" method="POST" enctype="multipart/form-data">
{{ csrf_field }}
<input type="file" name="image_upload">
<input type="submit" value="submit" />
</form>
{% if image_url %}
<p>File uploaded at: <a href="{{ image_url }}">{{ image_url }}</a></p>
{% endif %}
</body>
</html>
{% endblock %}
Добавьте новый метод, вызываемый в upload
web /app/controllers/WelcomeController.py :WelcomeController
def upload(self, storage: Storage, view: View, request: Request):
filename = storage.disk("local").put_file("image_upload", request.input("image_upload"))
return view.render("welcome", {"image_url": f"/framework/filesystem/{filename}"})
Не забывайте об импорте:
from masonite.filesystem import Storage
from masonite.request import Request
Затем подключите контроллер к новому маршруту в web/routes/web.py :
from masonite.routes import Route
ROUTES = [
Route.get("/", "WelcomeController@show"),
Route.get("/sample", "WelcomeController@show"),
Route.post("/", "WelcomeController@upload"),
]
Тест:
$ docker-compose up -d --build
Вы должны иметь возможность загрузить изображение по адресу http://localhost:8000/ , а затем просмотреть его по адресу http://localhost:8000/uploads/IMAGE_FILE_NAME .
Для производства обновите конфигурацию Nginx, чтобы направлять запросы медиафайлов в папку «uploads»:
upstream hello_masonite {
server web:8000;
}
server {
listen 80;
location /static/ {
alias /home/app/web/storage/static/;
}
location ~ ^/(favicon.ico|robots.txt)/ {
alias /home/app/web/storage/public/;
}
location /uploads/ {
alias /home/app/web/storage/framework/filesystem/image_upload/;
}
location / {
proxy_pass http://hello_masonite;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
Перестроить:
$ docker-compose down -v
$ docker-compose -f docker-compose.prod.yml up -d --build
Проверьте это в последний раз:
В этом руководстве мы рассмотрели, как контейнеризировать приложение Masonite с помощью Postgres для разработки. Мы также создали готовый к работе файл Docker Compose, который добавляет в смесь Gunicorn и Nginx для обработки статических и мультимедийных файлов. Теперь вы можете протестировать производственную установку локально.
С точки зрения фактического развертывания в производственной среде вы, вероятно, захотите использовать:
db
иnginx
Вы можете найти код в репозитории masonite-on-docker .
Спасибо за чтение!
Источник: https://testdriven.io
1660704900
Это пошаговое руководство, в котором подробно описано, как настроить Django для работы в Docker с Postgres. Для производственных сред мы добавим Nginx и Gunicorn. Мы также рассмотрим, как обслуживать статические и мультимедийные файлы Django через Nginx.
Зависимости :
Создайте новый каталог проекта вместе с новым проектом Django:
$ mkdir django-on-docker && cd django-on-docker
$ mkdir app && cd app
$ python3.9 -m venv env
$ source env/bin/activate
(env)$
(env)$ pip install django==3.2.6
(env)$ django-admin.py startproject hello_django .
(env)$ python manage.py migrate
(env)$ python manage.py runserver
Перейдите по адресу http://localhost:8000/ , чтобы просмотреть экран приветствия Django. Убейте сервер, как только закончите. Затем выйдите из виртуальной среды и удалите ее. Теперь у нас есть простой проект Django для работы.
Создайте файл requirements.txt в каталоге «app» и добавьте Django в качестве зависимости:
Django==3.2.6
Поскольку мы будем переходить на Postgres, удалите файл db.sqlite3 из каталога «app».
Каталог вашего проекта должен выглядеть так:
└── app
├── hello_django
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
└── requirements.txt
Установите Docker , если у вас его еще нет, затем добавьте Dockerfile в каталог «app»:
# pull official base image
FROM python:3.9.6-alpine
# 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 .
RUN pip install -r requirements.txt
# copy project
COPY . .
Итак, мы начали с образа Docker на основе Alpine для Python 3.9.6. Затем мы устанавливаем рабочий каталог вместе с двумя переменными среды:
PYTHONDONTWRITEBYTECODE
: запрещает Python записывать файлы pyc на диск (эквивалентно python -B
опции )PYTHONUNBUFFERED
: запрещает Python буферизовать stdout и stderr (эквивалентно python -u
опции )Наконец, мы обновили Pip, скопировали файл requirements.txt , установили зависимости и скопировали сам проект Django.
Просмотрите Docker для разработчиков Python , чтобы узнать больше о структурировании Dockerfiles, а также о некоторых рекомендациях по настройке Docker для разработки на основе Python.
Затем добавьте файл docker-compose.yml в корень проекта:
version: '3.8'
services:
web:
build: ./app
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./app/:/usr/src/app/
ports:
- 8000:8000
env_file:
- ./.env.dev
Просмотрите справочник по файлу Compose, чтобы узнать, как работает этот файл.
Обновите переменные SECRET_KEY
, DEBUG
и в settings.py :ALLOWED_HOSTS
SECRET_KEY = os.environ.get("SECRET_KEY")
DEBUG = int(os.environ.get("DEBUG", default=0))
# 'DJANGO_ALLOWED_HOSTS' should be a single string of hosts with a space between each.
# For example: 'DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]'
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS").split(" ")
Не забудьте добавить импорт вверху:
import os
Затем создайте файл .env.dev в корне проекта для хранения переменных среды для разработки:
DEBUG=1
SECRET_KEY=foo
DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
Создайте образ:
$ docker-compose build
После сборки образа запустите контейнер:
$ docker-compose up -d
Перейдите по адресу http://localhost:8000/ , чтобы снова просмотреть экран приветствия.
Проверьте наличие ошибок в журналах, если это не работает через
docker-compose logs -f
.
Чтобы настроить Postgres, нам нужно добавить новый сервис в файл docker-compose.yml , обновить настройки Django и установить Psycopg2 .
Во- первых, добавьте новую службу с именем db
в docker-compose.yml :
version: '3.8'
services:
web:
build: ./app
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./app/:/usr/src/app/
ports:
- 8000:8000
env_file:
- ./.env.dev
depends_on:
- db
db:
image: postgres:13.0-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=hello_django
- POSTGRES_PASSWORD=hello_django
- POSTGRES_DB=hello_django_dev
volumes:
postgres_data:
Чтобы сохранить данные после окончания срока службы контейнера, мы настроили том. Эта конфигурация будет привязана postgres_data
к каталогу «/var/lib/postgresql/data/» в контейнере.
Мы также добавили ключ среды для определения имени базы данных по умолчанию и установки имени пользователя и пароля.
Дополнительные сведения см. в разделе «Переменные среды» на странице Postgres Docker Hub .
Нам также понадобятся некоторые новые переменные среды для web
службы, поэтому обновите .env.dev следующим образом:
DEBUG=1
SECRET_KEY=foo
DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
SQL_ENGINE=django.db.backends.postgresql
SQL_DATABASE=hello_django_dev
SQL_USER=hello_django
SQL_PASSWORD=hello_django
SQL_HOST=db
SQL_PORT=5432
Обновите DATABASES
dict в settings.py :
DATABASES = {
"default": {
"ENGINE": os.environ.get("SQL_ENGINE", "django.db.backends.sqlite3"),
"NAME": os.environ.get("SQL_DATABASE", BASE_DIR / "db.sqlite3"),
"USER": os.environ.get("SQL_USER", "user"),
"PASSWORD": os.environ.get("SQL_PASSWORD", "password"),
"HOST": os.environ.get("SQL_HOST", "localhost"),
"PORT": os.environ.get("SQL_PORT", "5432"),
}
}
Здесь база данных настроена на основе переменных среды, которые мы только что определили. Обратите внимание на значения по умолчанию.
Обновите Dockerfile, чтобы установить соответствующие пакеты, необходимые для Psycopg2:
# pull official base image
FROM python:3.9.6-alpine
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install psycopg2 dependencies
RUN apk update \
&& apk add postgresql-dev gcc python3-dev musl-dev
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
# copy project
COPY . .
Добавьте Psycopg2 в файл requirements.txt :
Django==3.2.6
psycopg2-binary==2.9.1
Просмотрите этот выпуск GitHub для получения дополнительной информации об установке Psycopg2 в образе Docker на основе Alpine.
Создайте новый образ и запустите два контейнера:
$ docker-compose up -d --build
Запустите миграции:
$ docker-compose exec web python manage.py migrate --noinput
Получить следующую ошибку?
django.db.utils.OperationalError: FATAL: база данных «hello_django_dev» не существует
Запустите
docker-compose down -v
, чтобы удалить тома вместе с контейнерами. Затем пересоберите образы, запустите контейнеры и примените миграции.
Убедитесь, что таблицы Django по умолчанию созданы:
$ docker-compose exec db psql --username=hello_django --dbname=hello_django_dev
psql (13.0)
Type "help" for help.
hello_django_dev=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
------------------+--------------+----------+------------+------------+-------------------------------
hello_django_dev | hello_django | UTF8 | en_US.utf8 | en_US.utf8 |
postgres | hello_django | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | hello_django | UTF8 | en_US.utf8 | en_US.utf8 | =c/hello_django +
| | | | | hello_django=CTc/hello_django
template1 | hello_django | UTF8 | en_US.utf8 | en_US.utf8 | =c/hello_django +
| | | | | hello_django=CTc/hello_django
(4 rows)
hello_django_dev=# \c hello_django_dev
You are now connected to database "hello_django_dev" as user "hello_django".
hello_django_dev=# \dt
List of relations
Schema | Name | Type | Owner
--------+----------------------------+-------+--------------
public | auth_group | table | hello_django
public | auth_group_permissions | table | hello_django
public | auth_permission | table | hello_django
public | auth_user | table | hello_django
public | auth_user_groups | table | hello_django
public | auth_user_user_permissions | table | hello_django
public | django_admin_log | table | hello_django
public | django_content_type | table | hello_django
public | django_migrations | table | hello_django
public | django_session | table | hello_django
(10 rows)
hello_django_dev=# \q
Вы также можете проверить, что том был создан, запустив:
$ docker volume inspect django-on-docker_postgres_data
Вы должны увидеть что-то похожее на:
[
{
"CreatedAt": "2021-08-23T15:49:08Z",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "django-on-docker",
"com.docker.compose.version": "1.29.2",
"com.docker.compose.volume": "postgres_data"
},
"Mountpoint": "/var/lib/docker/volumes/django-on-docker_postgres_data/_data",
"Name": "django-on-docker_postgres_data",
"Options": null,
"Scope": "local"
}
]
Затем добавьте файл entrypoint.sh в каталог «app», чтобы убедиться, что Postgres исправен, прежде чем применять миграции и запускать сервер разработки Django:
#!/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 flush --no-input
python manage.py migrate
exec "$@"
Обновите права доступа к файлу локально:
$ chmod +x app/entrypoint.sh
Затем обновите файл Dockerfile, скопировав файл entrypoint.sh , и запустите его как команду точки входа Docker :
# pull official base image
FROM python:3.9.6-alpine
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install psycopg2 dependencies
RUN apk update \
&& apk add postgresql-dev gcc python3-dev musl-dev
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
# copy entrypoint.sh
COPY ./entrypoint.sh .
RUN sed -i 's/\r$//g' /usr/src/app/entrypoint.sh
RUN chmod +x /usr/src/app/entrypoint.sh
# copy project
COPY . .
# run entrypoint.sh
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
Добавьте DATABASE
переменную окружения в .env.dev :
DEBUG=1
SECRET_KEY=foo
DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
SQL_ENGINE=django.db.backends.postgresql
SQL_DATABASE=hello_django_dev
SQL_USER=hello_django
SQL_PASSWORD=hello_django
SQL_HOST=db
SQL_PORT=5432
DATABASE=postgres
Проверьте еще раз:
Во-первых, несмотря на добавление Postgres, мы по-прежнему можем создать независимый образ Docker для Django, если для DATABASE
переменной среды не задано значение postgres
. Для проверки создайте новый образ и запустите новый контейнер:
$ docker build -f ./app/Dockerfile -t hello_django:latest ./app
$ docker run -d \
-p 8006:8000 \
-e "SECRET_KEY=please_change_me" -e "DEBUG=1" -e "DJANGO_ALLOWED_HOSTS=*" \
hello_django python /usr/src/app/manage.py runserver 0.0.0.0:8000
Вы должны увидеть страницу приветствия по адресу http://localhost:8006.
Во-вторых, вы можете закомментировать команды очистки и миграции базы данных в сценарии entrypoint.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
# python manage.py flush --no-input
# python manage.py migrate
exec "$@"
Вместо этого вы можете запускать их вручную после того, как контейнеры раскрутятся, например:
$ docker-compose exec web python manage.py flush --no-input
$ docker-compose exec web python manage.py migrate
Двигаясь дальше, для производственных сред давайте добавим Gunicorn , сервер WSGI производственного уровня, в файл требований:
Django==3.2.6
gunicorn==20.1.0
psycopg2-binary==2.9.1
Интересуетесь WSGI и Gunicorn? Просмотрите главу WSGI из курса « Создание собственной веб-инфраструктуры Python» .
Поскольку мы по-прежнему хотим использовать встроенный сервер Django в процессе разработки, создайте новый файл компоновки с именем docker-compose.prod.yml для производства:
version: '3.8'
services:
web:
build: ./app
command: gunicorn hello_django.wsgi:application --bind 0.0.0.0:8000
ports:
- 8000:8000
env_file:
- ./.env.prod
depends_on:
- db
db:
image: postgres:13.0-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 для переопределения этих параметров конфигурации в зависимости от среды.
Обратите внимание на значение по умолчанию command
. Мы используем Gunicorn, а не сервер разработки Django. Мы также удалили том из web
службы, так как он нам не нужен в рабочей среде. Наконец, мы используем отдельные файлы переменных среды для определения переменных среды для обеих служб, которые будут переданы в контейнер во время выполнения.
.env.prod :
DEBUG=0
SECRET_KEY=change_me
DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
SQL_ENGINE=django.db.backends.postgresql
SQL_DATABASE=hello_django_prod
SQL_USER=hello_django
SQL_PASSWORD=hello_django
SQL_HOST=db
SQL_PORT=5432
DATABASE=postgres
.env.prod.db :
POSTGRES_USER=hello_django
POSTGRES_PASSWORD=hello_django
POSTGRES_DB=hello_django_prod
Добавьте два файла в корень проекта. Вы, вероятно, захотите сохранить их вне контроля версий, поэтому добавьте их в файл .gitignore .
Снесите контейнеры разработки (и связанные тома с -v
флагом):
$ docker-compose down -v
Затем создайте производственные образы и разверните контейнеры:
$ docker-compose -f docker-compose.prod.yml up -d --build
Убедитесь, что hello_django_prod
база данных была создана вместе с таблицами Django по умолчанию. Проверьте страницу администратора по адресу http://localhost:8000/admin . Статические файлы больше не загружаются. Это ожидается, поскольку режим отладки выключен. Мы исправим это в ближайшее время.
Опять же, если контейнер не запускается, проверьте наличие ошибок в логах через
docker-compose -f docker-compose.prod.yml logs -f
.
Вы заметили, что мы по-прежнему выполняем очистку базы данных ( которая очищает базу данных) и команды переноса при каждом запуске контейнера? Это нормально в разработке, но давайте создадим новый файл точки входа для производства.
точка входа.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 "$@"
Обновите права доступа к файлу локально:
$ chmod +x app/entrypoint.prod.sh
Чтобы использовать этот файл, создайте новый файл Dockerfile с именем Dockerfile.prod для использования с производственными сборками:
###########
# BUILDER #
###########
# pull official base image
FROM python:3.9.6-alpine as builder
# set work directory
WORKDIR /usr/src/app
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install psycopg2 dependencies
RUN apk update \
&& apk add postgresql-dev gcc python3-dev musl-dev
# lint
RUN pip install --upgrade pip
RUN pip install flake8==3.9.2
COPY . .
RUN flake8 --ignore=E501,F401 .
# install 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.9.6-alpine
# 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 apk update && apk add libpq
COPY --from=builder /usr/src/app/wheels /wheels
COPY --from=builder /usr/src/app/requirements.txt .
RUN pip install --no-cache /wheels/*
# copy entrypoint.prod.sh
COPY ./entrypoint.prod.sh .
RUN sed -i 's/\r$//g' $APP_HOME/entrypoint.prod.sh
RUN chmod +x $APP_HOME/entrypoint.prod.sh
# 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. Затем колеса копируются в окончательный производственный образ, и этот builder
образ удаляется.
You could take the multi-stage build approach a step further and use a single Dockerfile instead of creating two Dockerfiles. Think of the pros and cons of using this approach over two different files.
Did you notice that we created a non-root user? By default, Docker runs container processes as root inside of a container. This is a bad practice since attackers can gain root access to the Docker host if they manage to break out of the container. If you're root in the container, you'll be root on the host.
Update the web
service within the docker-compose.prod.yml file to build with Dockerfile.prod:
web:
build:
context: ./app
dockerfile: Dockerfile.prod
command: gunicorn hello_django.wsgi:application --bind 0.0.0.0:8000
ports:
- 8000:8000
env_file:
- ./.env.prod
depends_on:
- db
Try it out:
$ 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 migrate --noinput
Затем давайте добавим Nginx, который будет выступать в качестве обратного прокси -сервера для Gunicorn для обработки клиентских запросов, а также для обслуживания статических файлов.
Добавьте сервис в docker-compose.prod.yml :
nginx:
build: ./nginx
ports:
- 1337:80
depends_on:
- web
Затем в локальном корне проекта создайте следующие файлы и папки:
└── nginx
├── Dockerfile
└── nginx.conf
Докерфайл :
FROM nginx:1.21-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
nginx.conf :
upstream hello_django {
server web:8000;
}
server {
listen 80;
location / {
proxy_pass http://hello_django;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
Ознакомьтесь с разделом Использование NGINX и NGINX Plus в качестве шлюза приложений с uWSGI и Django , чтобы получить дополнительные сведения о настройке Nginx для работы с Django.
Затем обновите web
службу в docker-compose.prod.yml , заменив ports
на expose
:
web:
build:
context: ./app
dockerfile: Dockerfile.prod
command: gunicorn hello_django.wsgi:application --bind 0.0.0.0:8000
expose:
- 8000
env_file:
- ./.env.prod
depends_on:
- db
Теперь порт 8000 открыт только внутри, для других служб Docker. Порт больше не будет опубликован на хост-компьютере.
Чтобы узнать больше о портах и экспонировании, просмотрите этот вопрос о переполнении стека.
Проверьте это снова.
$ 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 migrate --noinput
Убедитесь, что приложение запущено и работает по адресу http://localhost:1337 .
Теперь структура вашего проекта должна выглядеть так:
├── .env.dev
├── .env.prod
├── .env.prod.db
├── .gitignore
├── app
│ ├── Dockerfile
│ ├── Dockerfile.prod
│ ├── entrypoint.prod.sh
│ ├── entrypoint.sh
│ ├── hello_django
│ │ ├── __init__.py
│ │ ├── asgi.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── manage.py
│ └── requirements.txt
├── docker-compose.prod.yml
├── docker-compose.yml
└── nginx
├── Dockerfile
└── nginx.conf
Когда закончите, опустите контейнеры:
$ docker-compose -f docker-compose.prod.yml down -v
Поскольку Gunicorn является сервером приложений, он не будет обслуживать статические файлы. Итак, как следует обрабатывать как статические, так и мультимедийные файлы в этой конкретной конфигурации?
Обновить settings.py :
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
Теперь любой запрос http://localhost:8000/static/*
будет обслуживаться из каталога «staticfiles».
Для проверки сначала пересоберите образы и запустите новые контейнеры, как обычно. Убедитесь, что статические файлы по-прежнему корректно обслуживаются по адресу http://localhost:8000/admin .
Для рабочей среды добавьте том в службы web
and в файле docker-compose.prod.yml , чтобы каждый контейнер имел общий каталог с именем «staticfiles»:nginx
version: '3.8'
services:
web:
build:
context: ./app
dockerfile: Dockerfile.prod
command: gunicorn hello_django.wsgi:application --bind 0.0.0.0:8000
volumes:
- static_volume:/home/app/web/staticfiles
expose:
- 8000
env_file:
- ./.env.prod
depends_on:
- db
db:
image: postgres:13.0-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
env_file:
- ./.env.prod.db
nginx:
build: ./nginx
volumes:
- static_volume:/home/app/web/staticfiles
ports:
- 1337:80
depends_on:
- web
volumes:
postgres_data:
static_volume:
Нам также нужно создать папку «/home/app/web/staticfiles» в Dockerfile.prod :
...
# create the appropriate directories
ENV HOME=/home/app
ENV APP_HOME=/home/app/web
RUN mkdir $APP_HOME
RUN mkdir $APP_HOME/staticfiles
WORKDIR $APP_HOME
...
Почему это необходимо?
Docker Compose обычно монтирует именованные тома как root. И поскольку мы используем пользователя без полномочий root, мы получим ошибку отказа в разрешении при collectstatic
запуске команды, если каталог еще не существует.
Чтобы обойти это, вы можете:
Мы использовали первое.
Затем обновите конфигурацию Nginx, чтобы направлять запросы статических файлов в папку «staticfiles»:
upstream hello_django {
server web:8000;
}
server {
listen 80;
location / {
proxy_pass http://hello_django;
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/staticfiles/;
}
}
Раскрутите контейнеры разработки:
$ 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 migrate --noinput
$ docker-compose -f docker-compose.prod.yml exec web python manage.py collectstatic --no-input --clear
Опять же, запросы http://localhost:1337/static/*
будут обслуживаться из каталога «staticfiles».
Перейдите по адресу http://localhost:1337/admin и убедитесь, что статические ресурсы загружаются правильно.
Вы также можете проверить в журналах — через docker-compose -f docker-compose.prod.yml logs -f
— что запросы к статическим файлам успешно обрабатываются через Nginx:
nginx_1 | 192.168.144.1 - - [23/Aug/2021:20:11:00 +0000] "GET /admin/ HTTP/1.1" 302 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" "-"
nginx_1 | 192.168.144.1 - - [23/Aug/2021:20:11:00 +0000] "GET /admin/login/?next=/admin/ HTTP/1.1" 200 2214 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" "-"
nginx_1 | 192.168.144.1 - - [23/Aug/2021:20:11:00 +0000] "GET /static/admin/css/base.css HTTP/1.1" 304 0 "http://localhost:1337/admin/login/?next=/admin/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" "-"
nginx_1 | 192.168.144.1 - - [23/Aug/2021:20:11:00 +0000] "GET /static/admin/css/nav_sidebar.css HTTP/1.1" 304 0 "http://localhost:1337/admin/login/?next=/admin/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" "-"
nginx_1 | 192.168.144.1 - - [23/Aug/2021:20:11:00 +0000] "GET /static/admin/css/responsive.css HTTP/1.1" 304 0 "http://localhost:1337/admin/login/?next=/admin/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" "-"
nginx_1 | 192.168.144.1 - - [23/Aug/2021:20:11:00 +0000] "GET /static/admin/css/login.css HTTP/1.1" 304 0 "http://localhost:1337/admin/login/?next=/admin/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" "-"
nginx_1 | 192.168.144.1 - - [23/Aug/2021:20:11:00 +0000] "GET /static/admin/js/nav_sidebar.js HTTP/1.1" 304 0 "http://localhost:1337/admin/login/?next=/admin/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" "-"
nginx_1 | 192.168.144.1 - - [23/Aug/2021:20:11:00 +0000] "GET /static/admin/css/fonts.css HTTP/1.1" 304 0 "http://localhost:1337/static/admin/css/base.css" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" "-"
nginx_1 | 192.168.144.1 - - [23/Aug/2021:20:11:00 +0000] "GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1" 304 0 "http://localhost:1337/static/admin/css/fonts.css" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" "-"
nginx_1 | 192.168.144.1 - - [23/Aug/2021:20:11:00 +0000] "GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1" 304 0 "http://localhost:1337/static/admin/css/fonts.css" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" "-"
Когда закончите, принесите контейнеры:
$ docker-compose -f docker-compose.prod.yml down -v
Чтобы проверить работу с медиафайлами, начните с создания нового приложения Django:
$ docker-compose up -d --build
$ docker-compose exec web python manage.py startapp upload
Добавьте новое приложение в INSTALLED_APPS
список в settings.py :
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"upload",
]
приложение/загрузить/views.py :
from django.shortcuts import render
from django.core.files.storage import FileSystemStorage
def image_upload(request):
if request.method == "POST" and request.FILES["image_file"]:
image_file = request.FILES["image_file"]
fs = FileSystemStorage()
filename = fs.save(image_file.name, image_file)
image_url = fs.url(filename)
print(image_url)
return render(request, "upload.html", {
"image_url": image_url
})
return render(request, "upload.html")
Добавьте каталог «templates» в каталог «app/upload», а затем добавьте новый шаблон с именем upload.html :
{% block content %}
<form action="{% url "upload" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="image_file">
<input type="submit" value="submit" />
</form>
{% if image_url %}
<p>File uploaded at: <a href="{{ image_url }}">{{ image_url }}</a></p>
{% endif %}
{% endblock %}
приложение/hello_django/urls.py :
from django.contrib import admin
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
from upload.views import image_upload
urlpatterns = [
path("", image_upload, name="upload"),
path("admin/", admin.site.urls),
]
if bool(settings.DEBUG):
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
приложение/hello_django/settings.py :
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "mediafiles"
Тест:
$ docker-compose up -d --build
Вы должны иметь возможность загрузить изображение по адресу http://localhost:8000/ , а затем просмотреть его по адресу http://localhost:8000/media/IMAGE_FILE_NAME .
Для производства добавьте еще один том в web
and nginx
services:
version: '3.8'
services:
web:
build:
context: ./app
dockerfile: Dockerfile.prod
command: gunicorn hello_django.wsgi:application --bind 0.0.0.0:8000
volumes:
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/mediafiles
expose:
- 8000
env_file:
- ./.env.prod
depends_on:
- db
db:
image: postgres:13.0-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
env_file:
- ./.env.prod.db
nginx:
build: ./nginx
volumes:
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/mediafiles
ports:
- 1337:80
depends_on:
- web
volumes:
postgres_data:
static_volume:
media_volume:
Создайте папку «/home/app/web/mediafiles» в Dockerfile.prod :
...
# create the appropriate directories
ENV HOME=/home/app
ENV APP_HOME=/home/app/web
RUN mkdir $APP_HOME
RUN mkdir $APP_HOME/staticfiles
RUN mkdir $APP_HOME/mediafiles
WORKDIR $APP_HOME
...
Снова обновите конфигурацию Nginx:
upstream hello_django {
server web:8000;
}
server {
listen 80;
location / {
proxy_pass http://hello_django;
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/staticfiles/;
}
location /media/ {
alias /home/app/web/mediafiles/;
}
}
Перестроить:
$ 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 migrate --noinput
$ docker-compose -f docker-compose.prod.yml exec web python manage.py collectstatic --no-input --clear
Проверьте это в последний раз:
Если вы видите
413 Request Entity Too Large
ошибку, вам нужно увеличить максимально допустимый размер тела запроса клиента в контексте сервера или местоположения в конфигурации Nginx.Пример:
location / { proxy_pass http://hello_django; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_redirect off; client_max_body_size 100M; }
В этом руководстве мы рассмотрели, как контейнеризировать веб-приложение Django с помощью Postgres для разработки. Мы также создали готовый к работе файл Docker Compose, который добавляет в смесь Gunicorn и Nginx для обработки статических и мультимедийных файлов. Теперь вы можете протестировать производственную установку локально.
С точки зрения фактического развертывания в производственной среде вы, вероятно, захотите использовать:
db
иnginx
Чтобы узнать о других советах по производству, ознакомьтесь с этим обсуждением .
Вы можете найти код в репозитории django-on-docker .
Источник: https://testdriven.io
1595822100
This is a step-by-step tutorial that details how to configure Masonite to run on Docker with Postgres. For production environments, we’ll add on Nginx and Gunicorn. We’ll also take a look at how to serve static and user-uploaded media files via Nginx.
Masonite is a modern, developer centric, batteries included Python web framework. If you’re new to the framework check out the 5 reasons why people are choosing Masonite over Django blog post.
Dependencies:
Create a project directory, install the Masonite CLI (craft), and create a new Masonite project:
$ mkdir masonie-on-docker && cd masonie-on-docker
$ python3.8 -m venv env
$ source env/bin/activate
(env)$ pip install masonite-cli==2.2.2
(env)$ craft new web
(env)$ cd web
(env)$ craft install
(env)$ craft serve
Feel free to swap out virtualenv and Pip for Pipenv if that’s your tool of choice.
Navigate to >http://localhost:8000/ to view the Masonite welcome screen. Kill the server and exit from the virtual environment once done. We now have a simple Masonite project to work with.
Next, before adding Docker, let’s clean up the project structure a bit.
Remove the following files from the “web” directory:
Move the .env file to the project folder and rename it to .env.dev.
Your project structure should look like:
#dockerizing #postgres #gunicorn #nginx #docker #masonite
1595249460
Following the second video about Docker basics, in this video, I explain Docker architecture and explain the different building blocks of the docker engine; docker client, API, Docker Daemon. I also explain what a docker registry is and I finish the video with a demo explaining and illustrating how to use Docker hub
In this video lesson you will learn:
#docker #docker hub #docker host #docker engine #docker architecture #api
1615787193
Descargue el MBOX al convertidor PST y convierta los archivos MBOX al formato PST. Con esta aplicación, los archivos se convierten a gran velocidad sin ningún problema. Para conocer la aplicación el usuario puede instalar la versión demo de esta aplicación y así conocer la aplicación y su funcionamiento. Con una alta velocidad de compatibilidad, la aplicación convierte todos los archivos MBOX en formato PST.
Esta aplicación avanzada funciona en un orden específico para convertir los archivos MBOX a formato PST. Por lo tanto, a continuación se muestran algunos de los puntos que hablan sobre la aplicación y ver si la aplicación cumple con todas las expectativas del usuario.
Por lo tanto, la aplicación ofrece estas funciones avanzadas que permiten que el software funcione de manera avanzada.
Los usuarios pueden convertir el archivo en unos pocos pasos sin asistencia técnica. Siga estos pasos para convertir su archivo MBOX al formato PST de Outlook:
Paso 1: descargue el convertidor MBOX a PST
Paso 2- Inicie el convertidor
Paso 3- Seleccione los archivos MBOX que desea convertir
Paso 4- Ahora, elija el tipo que desea exportar los archivos.
Paso 5- Elija la ubicación donde desea guardar el archivo
Paso 6- Finalmente, haga clic derecho en el botón “Convertir ahora”.
Estos pasos pueden ser realizados por cualquier usuario novato.
Analicemos las funciones inteligentes de este convertidor que se indican a continuación:
Esta herramienta convierte archivos MBOX de cualquier tipo desde Thunderbird a Apple Mail. Este es un convertidor avanzado.
Los usuarios pueden convertir cualquier cantidad de archivos de datos sin ningún obstáculo. No importa cuál sea el tamaño del archivo MBOX, la conversión procede.
Los archivos que selecciona el usuario se convierten de archivos MBOX al formato PST de Outlook. Los resultados convertidos son los deseados por los usuarios.
El usuario puede guardar el archivo en cualquier ubicación donde el usuario quiera guardarlo. En una ubicación adecuada, se guardan los datos convertidos.
El usuario proporciona una interfaz fácil de usar que ayuda al usuario a convertir los archivos sin problemas y sin ningún obstáculo.
El resultado proporcionado por la aplicación es 100% exacto. La calidad del resultado sigue siendo impecable.
La aplicación da todos los resultados adecuados después de la conversión. Con una alta velocidad de compatibilidad, la tarea de conversión es procesada por la aplicación sin ningún error. Descargue la versión de demostración gratuita del convertidor MBOX a PST para ver si funciona.
Más información:- https://www.datavare.com/ru/конвертер-mbox-в-pst.html
#конвертер mbox в pst #mbox в импортер pst #преобразование mbox в pst #mbox в экспортер pst #конвертировать mbox в pst #импортировать mbox в pst