Как настроить Masonite для работы в Docker с помощью Postgres

Это пошаговое руководство, в котором подробно описано, как настроить Masonite, веб-фреймворк на основе Python, для работы в Docker с Postgres. Для производственных сред мы добавим Nginx и Gunicorn. Мы также рассмотрим, как обслуживать статические и загруженные пользователем медиафайлы через Nginx.

Зависимости :

  1. Мазонит v4.16.2
  2. Докер v20.10.17
  3. Питон v3.10.5

Настройка проекта

Создайте каталог проекта, установите 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, давайте немного подчистим структуру проекта:

  1. Удалите файлы .env-example и .gitignore из каталога «web».
  2. Переместите файл .env в корень проекта и переименуйте его в .env.dev .

Теперь структура вашего проекта должна выглядеть так:

├── .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. Затем мы устанавливаем рабочий каталог вместе с тремя переменными среды:

  1. PYTHONDONTWRITEBYTECODE: запрещает Python записывать файлы pyc на диск (эквивалентно python -B опции )
  2. PYTHONUNBUFFERED: запрещает Python буферизовать stdout и stderr (эквивалентно python -u опции )
  3. 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

Проверьте еще раз:

  1. Восстановить изображения
  2. Запуск контейнеров
  3. Попробуйте http://локальный:8000/

Хотите посеять несколько пользователей?

$ 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.

Рабочий Dockerfile

Вы заметили, что мы по-прежнему выполняем команды 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

Затем давайте добавим 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!

Разработка

Для проверки сначала пересоберите образы и запустите новые контейнеры, как обычно. После этого убедитесь, что следующие статические ресурсы загружаются правильно:

  1. http://localhost:8000/robots.txt ( корневой статический актив)
  2. http://localhost:8000/static/hello.txt (обычный статический ресурс)
  3. http://localhost:8000/static/css/app.css (скомпилированный статический ресурс)

Производство

Для производства добавьте том к службам weband в файле 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

Опять же, убедитесь, что следующие статические ресурсы загружены правильно:

  1. http://локальный:1337/robots.txt
  2. http://локальный:1337/static/hello.txt
  3. http://локальный:1337/статический/css/app.css

Вы также можете проверить в журналах — через 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 %}

Добавьте новый метод, вызываемый в uploadweb /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

Проверьте это в последний раз:

  1. Загрузите изображение по адресу http://localhost:1337 .
  2. Затем просмотрите изображение по адресу http://localhost:1337/uploads/IMAGE_FILE_NAME .

Вывод

В этом руководстве мы рассмотрели, как контейнеризировать приложение Masonite с помощью Postgres для разработки. Мы также создали готовый к работе файл Docker Compose, который добавляет в смесь Gunicorn и Nginx для обработки статических и мультимедийных файлов. Теперь вы можете протестировать производственную установку локально.

С точки зрения фактического развертывания в производственной среде вы, вероятно, захотите использовать:

  1. Полностью управляемая служба базы данных, такая как RDS или Cloud SQL , вместо управления собственным экземпляром Postgres в контейнере.
  2. Пользователь без полномочий root для служб dbиnginx

Вы можете найти код в репозитории masonite-on-docker .

Спасибо за чтение!

Источник:  https://testdriven.io

#docker #postgres #gunicorn #nginx 

What is GEEK

Buddha Community

Как настроить Masonite для работы в Docker с помощью Postgres

Как настроить Masonite для работы в Docker с помощью Postgres

Это пошаговое руководство, в котором подробно описано, как настроить Masonite, веб-фреймворк на основе Python, для работы в Docker с Postgres. Для производственных сред мы добавим Nginx и Gunicorn. Мы также рассмотрим, как обслуживать статические и загруженные пользователем медиафайлы через Nginx.

Зависимости :

  1. Мазонит v4.16.2
  2. Докер v20.10.17
  3. Питон v3.10.5

Настройка проекта

Создайте каталог проекта, установите 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, давайте немного подчистим структуру проекта:

  1. Удалите файлы .env-example и .gitignore из каталога «web».
  2. Переместите файл .env в корень проекта и переименуйте его в .env.dev .

Теперь структура вашего проекта должна выглядеть так:

├── .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. Затем мы устанавливаем рабочий каталог вместе с тремя переменными среды:

  1. PYTHONDONTWRITEBYTECODE: запрещает Python записывать файлы pyc на диск (эквивалентно python -B опции )
  2. PYTHONUNBUFFERED: запрещает Python буферизовать stdout и stderr (эквивалентно python -u опции )
  3. 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

Проверьте еще раз:

  1. Восстановить изображения
  2. Запуск контейнеров
  3. Попробуйте http://локальный:8000/

Хотите посеять несколько пользователей?

$ 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.

Рабочий Dockerfile

Вы заметили, что мы по-прежнему выполняем команды 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

Затем давайте добавим 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!

Разработка

Для проверки сначала пересоберите образы и запустите новые контейнеры, как обычно. После этого убедитесь, что следующие статические ресурсы загружаются правильно:

  1. http://localhost:8000/robots.txt ( корневой статический актив)
  2. http://localhost:8000/static/hello.txt (обычный статический ресурс)
  3. http://localhost:8000/static/css/app.css (скомпилированный статический ресурс)

Производство

Для производства добавьте том к службам weband в файле 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

Опять же, убедитесь, что следующие статические ресурсы загружены правильно:

  1. http://локальный:1337/robots.txt
  2. http://локальный:1337/static/hello.txt
  3. http://локальный:1337/статический/css/app.css

Вы также можете проверить в журналах — через 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 %}

Добавьте новый метод, вызываемый в uploadweb /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

Проверьте это в последний раз:

  1. Загрузите изображение по адресу http://localhost:1337 .
  2. Затем просмотрите изображение по адресу http://localhost:1337/uploads/IMAGE_FILE_NAME .

Вывод

В этом руководстве мы рассмотрели, как контейнеризировать приложение Masonite с помощью Postgres для разработки. Мы также создали готовый к работе файл Docker Compose, который добавляет в смесь Gunicorn и Nginx для обработки статических и мультимедийных файлов. Теперь вы можете протестировать производственную установку локально.

С точки зрения фактического развертывания в производственной среде вы, вероятно, захотите использовать:

  1. Полностью управляемая служба базы данных, такая как RDS или Cloud SQL , вместо управления собственным экземпляром Postgres в контейнере.
  2. Пользователь без полномочий root для служб dbиnginx

Вы можете найти код в репозитории masonite-on-docker .

Спасибо за чтение!

Источник:  https://testdriven.io

#docker #postgres #gunicorn #nginx 

Как настроить Django для работы в Docker с Postgres

Это пошаговое руководство, в котором подробно описано, как настроить Django для работы в Docker с Postgres. Для производственных сред мы добавим Nginx и Gunicorn. Мы также рассмотрим, как обслуживать статические и мультимедийные файлы Django через Nginx.

Зависимости :

  1. Джанго v3.2.6
  2. Докер v20.10.8
  3. Питон v3.9.6

Настройка проекта

Создайте новый каталог проекта вместе с новым проектом 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. Затем мы устанавливаем рабочий каталог вместе с двумя переменными среды:

  1. PYTHONDONTWRITEBYTECODE: запрещает Python записывать файлы pyc на диск (эквивалентно python -B опции )
  2. 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

Обновите DATABASESdict в 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

Проверьте еще раз:

  1. Восстановить изображения
  2. Запуск контейнеров
  3. Попробуйте http://локальный:8000/

Заметки

Во-первых, несмотря на добавление 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.

Рабочий Dockerfile

Вы заметили, что мы по-прежнему выполняем очистку базы данных ( которая очищает базу данных) и команды переноса при каждом запуске контейнера? Это нормально в разработке, но давайте создадим новый файл точки входа для производства.

точка входа.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

Затем давайте добавим 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 .

Производство

Для рабочей среды добавьте том в службы weband в файле 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запуске команды, если каталог еще не существует.

Чтобы обойти это, вы можете:

  1. Создайте папку в Dockerfile ( источник )
  2. Изменить права доступа к каталогу после его монтирования ( источник )

Мы использовали первое.

Затем обновите конфигурацию 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 .

Производство

Для производства добавьте еще один том в weband nginxservices:

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

Проверьте это в последний раз:

  1. Загрузите изображение по адресу http://localhost:1337/ .
  2. Затем просмотрите изображение по адресу http://localhost:1337/media/IMAGE_FILE_NAME .

Если вы видите 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 для обработки статических и мультимедийных файлов. Теперь вы можете протестировать производственную установку локально.

С точки зрения фактического развертывания в производственной среде вы, вероятно, захотите использовать:

  1. Полностью управляемая служба базы данных, такая как RDS или Cloud SQL , вместо управления собственным экземпляром Postgres в контейнере.
  2. Пользователь без полномочий root для служб dbиnginx

Чтобы узнать о других советах по производству, ознакомьтесь с этим обсуждением .

Вы можете найти код в репозитории django-on-docker .

Источник:  https://testdriven.io

#django #docker #nginx #gunicorn #postgres 

Turner  Crona

Turner Crona

1595822100

Dockerizing Masonite with Postgres, Gunicorn, and Nginx

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:

  1. Masonite v2.2
  2. Docker v19.03.5
  3. Python v3.8.0

Project Setup

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:

  • .env-example
  • .gitignore
  • .travis.yml

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

Iliana  Welch

Iliana Welch

1595249460

Docker Explained: Docker Architecture | Docker Registries

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:

  • What is Docker Host
  • What is Docker Engine
  • Learn about Docker Architecture
  • Learn about Docker client and Docker Daemon
  • Docker Hub and Registries
  • Simple demo to understand using images from registries

#docker #docker hub #docker host #docker engine #docker architecture #api

joe biden

1615787193

Kонвертер MBOX в PST - Бесплатный MBOX в PST для конвертации файла MBOX в файл PST

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.

Conozca el funcionamiento de la aplicación.

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.

  • Los usuarios pueden convertir archivos MBOX a granel y sin problemas.
  • Con la ubicación especificada por el usuario, los datos se convierten rápidamente.
  • La aplicación proporciona una conversión directa.
  • De forma avanzada, se realiza el proceso de conversión.
  • La aplicación proporciona una conversión rápida con solo un clic.
  • La aplicación funciona en cualquier aplicación de Windows, incluidos XP o Vista.
  • Cualquier archivo MBOX de correo electrónico se convierte en este convertidor inteligente.
  • La aplicación guarda el archivo localmente.

Por lo tanto, la aplicación ofrece estas funciones avanzadas que permiten que el software funcione de manera avanzada.

¿Cómo convertir archivos MBOX a PST?

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.

Algunos de los atributos de este convertidor inteligente

Analicemos las funciones inteligentes de este convertidor que se indican a continuación:

  1. Convierta cualquier archivo MBOX

Esta herramienta convierte archivos MBOX de cualquier tipo desde Thunderbird a Apple Mail. Este es un convertidor avanzado.

  1. Conversión masiva de archivos MBOX

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.

  1. Solo se convierten los archivos seleccionados

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.

  1. Ubicación personalizada

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.

  1. Buena compatibilidad

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.

  1. Excelente precisión

El resultado proporcionado por la aplicación es 100% exacto. La calidad del resultado sigue siendo impecable.

Conclusión

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