Lilly  Wilson

Lilly Wilson

1660381860

Cómo Implementar Una Aplicación Django En Heroku Con Docker

Este artículo analiza cómo implementar una aplicación Django en Heroku con Docker a través de Heroku Container Runtime.

Objetivos

Al final de este tutorial, podrá:

  1. Explique por qué es posible que desee utilizar Container Runtime de Heroku para ejecutar una aplicación
  2. Dockerizar una aplicación Django
  3. Implemente y ejecute una aplicación Django en un contenedor Docker en Heroku
  4. Configure GitLab CI para implementar imágenes de Docker en Heroku
  5. Administra activos estáticos con WhiteNoise
  6. Configurar Postgres para que se ejecute en Heroku
  7. Cree un Dockerfile de producción que use compilaciones de Docker de varias etapas
  8. Use el Registro de contenedores de Heroku y el Manifiesto de compilación para implementar Docker en Heroku

Tiempo de ejecución del contenedor Heroku

Junto con las implementaciones tradicionales del compilador Git plus slug ( git push heroku master), Heroku también admite implementaciones basadas en Docker, con Heroku Container Runtime.

Implementaciones basadas en Docker

Las implementaciones basadas en Docker tienen muchas ventajas sobre el enfoque tradicional:

  1. Sin límites de slug : Heroku permite un tamaño máximo de slug de 500 MB para las implementaciones tradicionales basadas en Git. Las implementaciones basadas en Docker, por otro lado, no tienen este límite.
  2. Control total sobre el sistema operativo : en lugar de estar limitado por los paquetes instalados por los paquetes de compilación de Heroku , tiene control total sobre el sistema operativo y puede instalar cualquier paquete que desee con Docker.
  3. Mayor paridad de desarrollo/producción: las compilaciones basadas en Docker tienen una mayor paridad entre desarrollo y producción, ya que los entornos subyacentes son los mismos.
  4. Menos bloqueo de proveedores : finalmente, Docker hace que sea mucho más fácil cambiar a un proveedor de alojamiento en la nube diferente, como AWS o GCP.

En general, las implementaciones basadas en Docker le brindan mayor flexibilidad y control sobre el entorno de implementación. Puede implementar las aplicaciones que desee dentro del entorno que desee. Dicho esto, ahora eres responsable de las actualizaciones de seguridad. Con las implementaciones tradicionales basadas en Git, Heroku es responsable de esto. Aplican actualizaciones de seguridad relevantes a sus pilas y migran su aplicación a las nuevas pilas según sea necesario. Mantén esto en mente.

Actualmente, hay dos formas de implementar aplicaciones con Docker en Heroku:

  1. Container Registry : implemente imágenes de Docker preconstruidas en Heroku
  2. Manifiesto de compilación : dado un Dockerfile, Heroku compila e implementa la imagen de Docker

La principal diferencia entre estos dos es que con el último enfoque, por ejemplo, a través del Manifiesto de compilación, tiene acceso a las funciones Pipelines , Review y Release . Por lo tanto, si está convirtiendo una aplicación de una implementación basada en Git a Docker y está usando alguna de esas funciones, debe usar el enfoque Build Manifest.

Tenga la seguridad de que veremos ambos enfoques en este artículo.

En cualquier caso, seguirá teniendo acceso a la CLI de Heroku , a todos los potentes complementos y al panel de control . Todas estas características funcionan con Container Runtime, en otras palabras.

Tipo de implementaciónMecanismo de despliegueActualizaciones de seguridad (quién las maneja)Acceso a Pipelines, Revisión, LiberaciónAcceso a CLI, complementos y panelLímites de tamaño de slug
Compilador Git + SlugEmpuje GitHeroku
Tiempo de ejecución de Docker + contenedorEmpuje de la ventana acoplableNoNo
Docker + Manifiesto de compilaciónEmpuje GitNo

Tenga en cuenta que las implementaciones basadas en Docker están limitadas a las mismas restricciones que las implementaciones basadas en Git. Por ejemplo, los volúmenes persistentes no son compatibles porque el sistema de archivos es efímero y los procesos web solo admiten solicitudes HTTP(S). Para obtener más información sobre esto, revise los comandos y el tiempo de ejecución de Dockerfile .

Docker vs Heroku Concepts

DockerHeroku
DockerfileBuildPack
ImageSlug
ContainerDyno

Project Setup

Make a project directory, create and activate a new virtual environment, and install Django:

$ mkdir django-heroku-docker
$ cd django-heroku-docker

$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django==3.2.9

Feel free to swap out virtualenv and Pip for Poetry or Pipenv. For more, review Modern Python Environments.

Next, create a new Django project, apply the migrations, and run the server:

(env)$ django-admin startproject hello_django .
(env)$ python manage.py migrate
(env)$ python manage.py runserver

Navigate to http://localhost:8000/ to view the Django welcome screen. Kill the server and exit from the virtual environment once done.

Docker

Add a Dockerfile to the project root:

# pull official base image
FROM python:3.10-alpine

# set work directory
WORKDIR /app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBUG 0

# install psycopg2
RUN apk update \
    && apk add --virtual build-essential gcc python3-dev musl-dev \
    && apk add postgresql-dev \
    && pip install psycopg2

# install dependencies
COPY ./requirements.txt .
RUN pip install -r requirements.txt

# copy project
COPY . .

# add and run as non-root user
RUN adduser -D myuser
USER myuser

# run gunicorn
CMD gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

Here, we started with an Alpine-based Docker image for Python 3.10. We then set a working directory along with two environment variables:

  1. PYTHONDONTWRITEBYTECODE: evita que Python escriba archivos pyc en el disco
  2. PYTHONUNBUFFERED: evita que Python almacene en búfer stdout y stderr

A continuación, instalamos dependencias a nivel de sistema y paquetes de Python, copiamos los archivos del proyecto, creamos y cambiamos a un usuario no root ( recomendado por Heroku ) y usamos CMD para ejecutar Gunicorn cuando un contenedor gira en tiempo de ejecución. Toma nota de la $PORTvariable. Esencialmente, cualquier servidor web que se ejecute en Container Runtime debe escuchar el tráfico HTTP en la $PORTvariable de entorno, que Heroku establece en tiempo de ejecución .

Cree un archivo de requisitos.txt :

Django==3.2.9
gunicorn==20.1.0

Luego agregue un archivo .dockerignore :

__pycache__
*.pyc
env/
db.sqlite3

Actualice las variables SECRET_KEY, DEBUGy en settings.py :ALLOWED_HOSTS

SECRET_KEY = os.environ.get('SECRET_KEY', default='foo')

DEBUG = int(os.environ.get('DEBUG', default=0))

ALLOWED_HOSTS = ['localhost', '127.0.0.1']

No olvides la importación:

import os

Para probar localmente, crea la imagen y ejecuta el contenedor, asegurándote de pasar las variables de entorno apropiadas:

$ docker build -t web:latest .
$ docker run -d --name django-heroku -e "PORT=8765" -e "DEBUG=1" -p 8007:8765 web:latest

Asegúrese de que la aplicación se esté ejecutando en http://localhost:8007/ en su navegador. Deténgase y luego retire el contenedor en ejecución una vez que haya terminado:

$ docker stop django-heroku
$ docker rm django-heroku

Agregue un .gitignore :

__pycache__
*.pyc
env/
db.sqlite3

A continuación, creemos una vista rápida de Django para probar fácilmente la aplicación cuando el modo de depuración está desactivado.

Agregue un archivo views.py al directorio "hello_django":

from django.http import JsonResponse


def ping(request):
    data = {'ping': 'pong!'}
    return JsonResponse(data)

A continuación, actualice urls.py :

from django.contrib import admin
from django.urls import path

from .views import ping


urlpatterns = [
    path('admin/', admin.site.urls),
    path('ping/', ping, name="ping"),
]

Prueba esto de nuevo con el modo de depuración desactivado:

$ docker build -t web:latest .
$ docker run -d --name django-heroku -e "PORT=8765" -e "DEBUG=0" -p 8007:8765 web:latest

Verifique que http://localhost:8007/ping/ funcione como se esperaba:

{
  "ping": "pong!"
}

Deténgase y luego retire el contenedor en ejecución una vez que haya terminado:

$ docker stop django-heroku
$ docker rm django-heroku

Ruido blanco

Si desea utilizar WhiteNoise para administrar sus activos estáticos, primero agregue el paquete al archivo requirements.txt :

Django==3.2.9
gunicorn==20.1.0
whitenoise==5.3.0

Actualice el middleware en settings.py así:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # new
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Luego configure el manejo de sus archivos estáticos con STATIC_ROOT:

STATIC_ROOT = BASE_DIR / 'staticfiles'

Finalmente, agregue soporte de compresión y almacenamiento en caché:

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

Agregue el collectstaticcomando al Dockerfile:

# pull official base image
FROM python:3.10-alpine

# set work directory
WORKDIR /app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBUG 0

# install psycopg2
RUN apk update \
    && apk add --virtual build-essential gcc python3-dev musl-dev \
    && apk add postgresql-dev \
    && pip install psycopg2

# install dependencies
COPY ./requirements.txt .
RUN pip install -r requirements.txt

# copy project
COPY . .

# collect static files
RUN python manage.py collectstatic --noinput

# add and run as non-root user
RUN adduser -D myuser
USER myuser

# run gunicorn
CMD gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

Para probar, crea la nueva imagen y haz girar un nuevo contenedor:

$ docker build -t web:latest .
$ docker run -d --name django-heroku -e "PORT=8765" -e "DEBUG=1" -p 8007:8765 web:latest

Debería poder ver los archivos estáticos cuando ejecuta:

$ docker exec django-heroku ls /app/staticfiles
$ docker exec django-heroku ls /app/staticfiles/admin

Deténgase y luego retire el contenedor en ejecución nuevamente:

$ docker stop django-heroku
$ docker rm django-heroku

postgres

Para poner en marcha Postgres, usaremos el paquete dj_database_url para generar el diccionario de configuración de base de datos adecuado para la configuración de Django en función de una DATABASE_URLvariable de entorno.

Agregue la dependencia al archivo de requisitos:

Django==3.2.9
dj-database-url==0.5.0
gunicorn==20.1.0
whitenoise==5.3.0

Luego, realice los siguientes cambios en la configuración para actualizar la configuración de la base de datos si DATABASE_URLestá presente:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

DATABASE_URL = os.environ.get('DATABASE_URL')
db_from_env = dj_database_url.config(default=DATABASE_URL, conn_max_age=500, ssl_require=True)
DATABASES['default'].update(db_from_env)

Por lo tanto, si DATABASE_URLno está presente, se seguirá utilizando SQLite.

Agregue la importación a la parte superior también:

import dj_database_url

Probaremos esto en un momento después de activar una base de datos de Postgres en Heroku.

Configuración de Heroku

Regístrese para obtener una cuenta de Heroku (si aún no tiene una) y luego instale la CLI de Heroku (si aún no lo ha hecho).

Crear una nueva aplicación:

$ heroku create
Creating app... done, ⬢ limitless-atoll-51647
https://limitless-atoll-51647.herokuapp.com/ | https://git.heroku.com/limitless-atoll-51647.git

Agregue la SECRET_KEYvariable de entorno:

$ heroku config:set SECRET_KEY=SOME_SECRET_VALUE -a limitless-atoll-51647

Cambie SOME_SECRET_VALUEa una cadena generada aleatoriamente que tenga al menos 50 caracteres.

Agregue la URL de Heroku anterior a la lista ALLOWED_HOSTSen hello_django/settings.py así:

ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'limitless-atoll-51647.herokuapp.com']

Asegúrese de reemplazar limitless-atoll-51647cada uno de los comandos anteriores con el nombre de su aplicación.

Implementación de la ventana acoplable de Heroku

En este punto, estamos listos para comenzar a implementar imágenes de Docker en Heroku. ¿Decidiste qué enfoque te gustaría tomar?

  1. Container Registry : implemente imágenes de Docker preconstruidas en Heroku
  2. Manifiesto de compilación : dado un Dockerfile, Heroku compila e implementa la imagen de Docker

¿Inseguro? ¡Pruébalos a los dos!

Enfoque #1: Registro de Contenedores

Omita esta sección si está utilizando el enfoque de manifiesto de compilación.

Nuevamente, con este enfoque, puede implementar imágenes de Docker preconstruidas en Heroku.

Inicie sesión en Heroku Container Registry , para indicarle a Heroku que queremos usar el Container Runtime:

$ heroku container:login

Reconstruya la imagen de Docker y etiquétela con el siguiente formato:

registry.heroku.com/<app>/<process-type>

Asegúrese de reemplazar <app>con el nombre de la aplicación Heroku que acaba de crear, ya <process-type>que webserá para un proceso web .

Por ejemplo:

$ docker build -t registry.heroku.com/limitless-atoll-51647/web .

Empuje la imagen al registro:

$ docker push registry.heroku.com/limitless-atoll-51647/web

Suelta la imagen:

$ heroku container:release -a limitless-atoll-51647 web

Esto ejecutará el contenedor. Debería poder ver la aplicación en https://APP_NAME.herokuapp.com . Debería devolver un 404.

Intente ejecutar heroku open -a limitless-atoll-51647para abrir la aplicación en su navegador predeterminado.

Verifica que https://APP_NAME.herokuapp.com/ping también funcione:

{
  "ping": "pong!"
}

También debería poder ver los archivos estáticos:

$ heroku run ls /app/staticfiles -a limitless-atoll-51647
$ heroku run ls /app/staticfiles/admin -a limitless-atoll-51647

Asegúrese de reemplazar limitless-atoll-51647cada uno de los comandos anteriores con el nombre de su aplicación.

Vaya a la sección "Prueba de Postgres" una vez que haya terminado.

Enfoque #2: Manifiesto de compilación

Omita esta sección si está utilizando el enfoque de Container Registry.

Una vez más, con el enfoque de compilación de manifiesto , puede hacer que Heroku construya e implemente imágenes de Docker en función de un archivo de manifiesto heroku.yml .

Establezca la pila de su aplicación en contenedor:

$ heroku stack:set container -a limitless-atoll-51647

Agregue un archivo heroku.yml a la raíz del proyecto:

build:
  docker:
    web: Dockerfile

Aquí, solo le estamos diciendo a Heroku qué Dockerfile debe usar para crear la imagen.

Junto con build, también puede definir las siguientes etapas:

  • setupse usa para definir complementos de Heroku y variables de configuración para crear durante el aprovisionamiento de la aplicación.
  • release is used to define tasks that you'd like to execute during a release.
  • run is used to define which commands to run for the web and worker processes.

Be sure to review the Heroku documentation to learn more about these four stages.

It's worth noting that the gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT command could be removed from the Dockerfile and added to the heroku.yml file under the run stage:

build:
  docker:
    web: Dockerfile
run:
  web: gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

Also, be sure to place the 'collectstatic' command inside your Dockerfile. Don't move it to the release stage. For more on this, review this Stack Overflow question.

Next, install the heroku-manifest plugin from the beta CLI channel:

$ heroku update beta
$ heroku plugins:install @heroku-cli/plugin-manifest

With that, initialize a Git repo and create a commit.

Then, add the Heroku remote:

$ heroku git:remote -a limitless-atoll-51647

Push the code up to Heroku to build the image and run the container:

$ git push heroku master

You should be able to view the app at https://APP_NAME.herokuapp.com. It should return a 404.

Try running heroku open -a limitless-atoll-51647 to open the app in your default browser.

Verify https://APP_NAME.herokuapp.com/ping works as well:

{
  "ping": "pong!"
}

You should also be able to view the static files:

$ heroku run ls /app/staticfiles -a limitless-atoll-51647
$ heroku run ls /app/staticfiles/admin -a limitless-atoll-51647

Make sure to replace limitless-atoll-51647 in each of the above commands with the name of your app.

Postgres Test

Create the database:

$ heroku addons:create heroku-postgresql:hobby-dev -a limitless-atoll-51647

This command automatically sets the DATABASE_URL environment variable for the container.

Once the database is up, run the migrations:

$ heroku run python manage.py makemigrations -a limitless-atoll-51647
$ heroku run python manage.py migrate -a limitless-atoll-51647

Then, jump into psql to view the newly created tables:

$ heroku pg:psql -a limitless-atoll-51647

# \dt
                      List of relations
 Schema |            Name            | Type  |     Owner
--------+----------------------------+-------+----------------
 public | auth_group                 | table | siodzhzzcvnwwp
 public | auth_group_permissions     | table | siodzhzzcvnwwp
 public | auth_permission            | table | siodzhzzcvnwwp
 public | auth_user                  | table | siodzhzzcvnwwp
 public | auth_user_groups           | table | siodzhzzcvnwwp
 public | auth_user_user_permissions | table | siodzhzzcvnwwp
 public | django_admin_log           | table | siodzhzzcvnwwp
 public | django_content_type        | table | siodzhzzcvnwwp
 public | django_migrations          | table | siodzhzzcvnwwp
 public | django_session             | table | siodzhzzcvnwwp
(10 rows)

# \q

Again, make sure to replace limitless-atoll-51647 in each of the above commands with the name of your Heroku app.

GitLab CI

Regístrese para obtener una cuenta de GitLab (si es necesario) y luego cree un nuevo proyecto (nuevamente, si es necesario).

Recupere su token de autenticación de Heroku :

$ heroku auth:token

Luego, guarde el token como una nueva variable llamada HEROKU_AUTH_TOKENdentro de la configuración de CI/CD de su proyecto: Configuración > CI/CD > Variables.

configuración de gitlab

A continuación, debemos agregar un archivo de configuración GitLab CI/CD llamado .gitlab-ci.yml a la raíz del proyecto. El contenido de este archivo variará según el enfoque utilizado.

Enfoque #1: Registro de Contenedores

Omita esta sección si está utilizando el enfoque de manifiesto de compilación.

.gitlab-ci.yml :

image: docker:stable
services:
  - docker:dind

variables:
  DOCKER_DRIVER: overlay2
  HEROKU_APP_NAME: <APP_NAME>
  HEROKU_REGISTRY_IMAGE: registry.heroku.com/${HEROKU_APP_NAME}/web

stages:
  - build_and_deploy

build_and_deploy:
  stage: build_and_deploy
  script:
    - apk add --no-cache curl
    - docker login -u _ -p $HEROKU_AUTH_TOKEN registry.heroku.com
    - docker pull $HEROKU_REGISTRY_IMAGE || true
    - docker build
      --cache-from $HEROKU_REGISTRY_IMAGE
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $HEROKU_REGISTRY_IMAGE
    - chmod +x ./release.sh
    - ./release.sh

liberación.sh :

#!/bin/sh


IMAGE_ID=$(docker inspect ${HEROKU_REGISTRY_IMAGE} --format={{.Id}})
PAYLOAD='{"updates": [{"type": "web", "docker_image": "'"$IMAGE_ID"'"}]}'

curl -n -X PATCH https://api.heroku.com/apps/$HEROKU_APP_NAME/formation \
  -d "${PAYLOAD}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/vnd.heroku+json; version=3.docker-releases" \
  -H "Authorization: Bearer ${HEROKU_AUTH_TOKEN}"

Aquí, definimos una sola build_and_deploy etapa en la que:

  1. Instalar cURL
  2. Inicie sesión en el registro de contenedores de Heroku
  3. Pull the previously pushed image (if it exists)
  4. Build and tag the new image
  5. Push the image up to the registry
  6. Create a new release via the Heroku API using the image ID within the release.sh script

Make sure to replace <APP_NAME> with your Heroku app's name.

With that, initialize a Git repo, commit, add the GitLab remote, and push your code up to GitLab to trigger a new pipeline. This will run the build_and_deploy stage as a single job. Once complete, a new release should automatically be created on Heroku.

Approach #2: Build Manifest

Skip this section if you're using the Container Registry approach.

.gitlab-ci.yml:

variables:
  HEROKU_APP_NAME: <APP_NAME>

stages:
  - deploy

deploy:
  stage: deploy
  script:
    - apt-get update -qy
    - apt-get install -y ruby-dev
    - gem install dpl
    - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_AUTH_TOKEN

Here, we defined a single deploy stage where we:

  1. Instale Ruby junto con una gema llamada dpl
  2. Implemente el código en Heroku con dpl

Asegúrese de reemplazar <APP_NAME>con el nombre de su aplicación Heroku.

Confirme, agregue el control remoto de GitLab y envíe su código a GitLab para activar una nueva canalización . Esto ejecutará el deployescenario como un solo trabajo. Una vez completado, el código debe implementarse en Heroku.

IC avanzado

En lugar de solo crear la imagen de Docker y crear una versión en GitLab CI, también ejecutemos las pruebas de Django, Flake8 , Black e isort .

Una vez más, esto variará según el enfoque que haya utilizado.

Enfoque #1: Registro de Contenedores

Omita esta sección si está utilizando el enfoque de manifiesto de compilación.

Actualice .gitlab-ci.yml así:

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:latest || true
    - docker build
      --cache-from $IMAGE:latest
      --tag $IMAGE:latest
      --file ./Dockerfile
      "."
    - docker push $IMAGE:latest

test:
  stage: test
  image: $IMAGE:latest
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
    HEROKU_APP_NAME: <APP_NAME>
    HEROKU_REGISTRY_IMAGE: registry.heroku.com/${HEROKU_APP_NAME}/web
  script:
    - apk add --no-cache curl
    - docker login -u _ -p $HEROKU_AUTH_TOKEN registry.heroku.com
    - docker pull $HEROKU_REGISTRY_IMAGE || true
    - docker build
      --cache-from $HEROKU_REGISTRY_IMAGE
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $HEROKU_REGISTRY_IMAGE
    - chmod +x ./release.sh
    - ./release.sh

Asegúrese de reemplazar <APP_NAME>con el nombre de su aplicación Heroku.

Entonces, ahora tenemos tres etapas: build, testy deploy.

En el buildescenario, nosotros:

  1. Inicie sesión en el registro de contenedores de GitLab
  2. Tire de la imagen previamente empujada (si existe)
  3. Crea y etiqueta la nueva imagen
  4. Sube la imagen al Registro de contenedores de GitLab

Luego, en la testetapa, configuramos Postgres , configuramos la DATABASE_URLvariable de entorno y luego ejecutamos las pruebas de Django, Flake8, Black e isort usando la imagen que se construyó en la etapa anterior.

En el deployescenario, nosotros:

  1. Instalar cURL
  2. Inicie sesión en el registro de contenedores de Heroku
  3. Tire de la imagen previamente empujada (si existe)
  4. Crea y etiqueta la nueva imagen
  5. Sube la imagen al registro
  6. Cree una nueva versión a través de la API de Heroku usando la ID de la imagen dentro del script release.sh

Agregue las nuevas dependencias al archivo de requisitos:

# prod
Django==3.2.9
dj-database-url==0.5.0
gunicorn==20.1.0
whitenoise==5.3.0

# dev and test
black==21.11b1
flake8==4.0.1
isort==5.10.1

Antes de subir a GitLab, ejecute las pruebas de Django localmente:

$ source env/bin/activate
(env)$ pip install -r requirements.txt
(env)$ python manage.py test

System check identified no issues (0 silenced).

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Asegúrese de que Flake8 sea aprobado y luego actualice el código fuente según las recomendaciones de Black e isort:

(env)$ flake8 hello_django --max-line-length=100
(env)$ black hello_django
(env)$ isort hello_django --profile black

Confirme y presione su código una vez más. Asegúrese de que pasen todas las etapas.

Enfoque #2: Manifiesto de compilación

Omita esta sección si está utilizando el enfoque de Container Registry.

Actualice .gitlab-ci.yml así:

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:latest || true
    - docker build
      --cache-from $IMAGE:latest
      --tag $IMAGE:latest
      --file ./Dockerfile
      "."
    - docker push $IMAGE:latest

test:
  stage: test
  image: $IMAGE:latest
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  variables:
    HEROKU_APP_NAME: <APP_NAME>
  script:
    - apt-get update -qy
    - apt-get install -y ruby-dev
    - gem install dpl
    - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_AUTH_TOKEN

Asegúrese de reemplazar <APP_NAME>con el nombre de su aplicación Heroku.

Entonces, ahora tenemos tres etapas: build, testy deploy.

En el buildescenario, nosotros:

  1. Inicie sesión en el registro de contenedores de GitLab
  2. Tire de la imagen previamente empujada (si existe)
  3. Crea y etiqueta la nueva imagen
  4. Sube la imagen al Registro de contenedores de GitLab

Luego, en la testetapa, configuramos Postgres , configuramos la DATABASE_URLvariable de entorno y luego ejecutamos las pruebas de Django, Flake8, Black e isort usando la imagen que se construyó en la etapa anterior.

En el deployescenario, nosotros:

  1. Instale Ruby junto con una gema llamada dpl
  2. Implemente el código en Heroku con dpl

Agregue las nuevas dependencias al archivo de requisitos:

# prod
Django==3.2.9
dj-database-url==0.5.0
gunicorn==20.1.0
whitenoise==5.3.0

# dev and test
black==21.11b1
flake8==4.0.1
isort==5.10.1

Antes de subir a GitLab, ejecute las pruebas de Django localmente:

$ source env/bin/activate
(env)$ pip install -r requirements.txt
(env)$ python manage.py test

System check identified no issues (0 silenced).

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Asegúrese de que Flake8 sea aprobado y luego actualice el código fuente según las recomendaciones de Black e isort:

(env)$ flake8 hello_django --max-line-length=100
(env)$ black hello_django
(env)$ isort hello_django --profile black

Confirme y presione su código una vez más. Asegúrese de que pasen todas las etapas.

Construcción de Docker de varias etapas

Finalmente, actualice el Dockerfile así para usar una compilación de varias etapas para reducir el tamaño final de la imagen:

FROM python:3.10-alpine AS build-python
RUN apk update && apk add --virtual build-essential gcc python3-dev musl-dev postgresql-dev
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY ./requirements.txt .
RUN pip install -r requirements.txt

FROM python:3.10-alpine
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBUG 0
ENV PATH="/opt/venv/bin:$PATH"
COPY --from=build-python /opt/venv /opt/venv
RUN apk update && apk add --virtual build-deps gcc python3-dev musl-dev postgresql-dev
RUN pip install psycopg2-binary
WORKDIR /app
COPY . .
RUN python manage.py collectstatic --noinput
RUN adduser -D myuser
USER myuser
CMD gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

A continuación, debemos actualizar la configuración de GitLab para aprovechar el almacenamiento en caché de la capa de Docker.

Enfoque #1: Registro de Contenedores

Omita esta sección si está utilizando el enfoque de manifiesto de compilación.

.gitlab-ci.yml :

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}
  HEROKU_APP_NAME: <APP_NAME>
  HEROKU_REGISTRY_IMAGE: registry.heroku.com/${HEROKU_APP_NAME}/web

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:build-python || true
    - docker pull $IMAGE:production || true
    - docker build
      --target build-python
      --cache-from $IMAGE:build-python
      --tag $IMAGE:build-python
      --file ./Dockerfile
      "."
    - docker build
      --cache-from $IMAGE:production
      --tag $IMAGE:production
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $IMAGE:build-python
    - docker push $IMAGE:production

test:
  stage: test
  image: $IMAGE:production
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - apk add --no-cache curl
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:build-python || true
    - docker pull $IMAGE:production || true
    - docker build
      --target build-python
      --cache-from $IMAGE:build-python
      --tag $IMAGE:build-python
      --file ./Dockerfile
      "."
    - docker build
      --cache-from $IMAGE:production
      --tag $IMAGE:production
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $IMAGE:build-python
    - docker push $IMAGE:production
    - docker login -u _ -p $HEROKU_AUTH_TOKEN registry.heroku.com
    - docker push $HEROKU_REGISTRY_IMAGE
    - chmod +x ./release.sh
    - ./release.sh

Asegúrese de reemplazar <APP_NAME>con el nombre de su aplicación Heroku.

Revise los cambios por su cuenta. Luego, pruébalo por última vez.

Para obtener más información sobre este patrón de almacenamiento en caché, consulte la sección "Multifase" del artículo Faster CI Builds with Docker Cache .

Enfoque #2: Manifiesto de compilación

Omita esta sección si está utilizando el enfoque de Container Registry.

.gitlab-ci.yml :

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}
  HEROKU_APP_NAME: <APP_NAME>

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:build-python || true
    - docker pull $IMAGE:production || true
    - docker build
      --target build-python
      --cache-from $IMAGE:build-python
      --tag $IMAGE:build-python
      --file ./Dockerfile
      "."
    - docker build
      --cache-from $IMAGE:production
      --tag $IMAGE:production
      --file ./Dockerfile
      "."
    - docker push $IMAGE:build-python
    - docker push $IMAGE:production

test:
  stage: test
  image: $IMAGE:production
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  script:
    - apt-get update -qy
    - apt-get install -y ruby-dev
    - gem install dpl
    - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_AUTH_TOKEN

Asegúrese de reemplazar <APP_NAME>con el nombre de su aplicación Heroku.

Revise los cambios por su cuenta. Luego, pruébalo por última vez.

Para obtener más información sobre este patrón de almacenamiento en caché, consulte la sección "Multifase" del artículo Faster CI Builds with Docker Cache .

Conclusión

En este artículo, analizamos dos enfoques para implementar una aplicación Django en Heroku con Docker: el registro de contenedores y el manifiesto de compilación.

Entonces, ¿cuándo debería pensar en usar Heroku Container Runtime sobre el compilador tradicional Git y slug para implementaciones?

Cuando necesite más control sobre el entorno de implementación de producción.

Ejemplos:

  1. Su aplicación y dependencias superan el límite máximo de slug de 500 MB.
  2. Su aplicación requiere paquetes no instalados por los paquetes de compilación habituales de Heroku.
  3. Desea una mayor seguridad de que su aplicación se comportará de la misma manera en el desarrollo que en la producción.
  4. Realmente disfrutas trabajar con Docker.

Fuente:  https://testdriven.io

#django #heroku #docker 

What is GEEK

Buddha Community

Cómo Implementar Una Aplicación Django En Heroku Con Docker
Lilly  Wilson

Lilly Wilson

1660381860

Cómo Implementar Una Aplicación Django En Heroku Con Docker

Este artículo analiza cómo implementar una aplicación Django en Heroku con Docker a través de Heroku Container Runtime.

Objetivos

Al final de este tutorial, podrá:

  1. Explique por qué es posible que desee utilizar Container Runtime de Heroku para ejecutar una aplicación
  2. Dockerizar una aplicación Django
  3. Implemente y ejecute una aplicación Django en un contenedor Docker en Heroku
  4. Configure GitLab CI para implementar imágenes de Docker en Heroku
  5. Administra activos estáticos con WhiteNoise
  6. Configurar Postgres para que se ejecute en Heroku
  7. Cree un Dockerfile de producción que use compilaciones de Docker de varias etapas
  8. Use el Registro de contenedores de Heroku y el Manifiesto de compilación para implementar Docker en Heroku

Tiempo de ejecución del contenedor Heroku

Junto con las implementaciones tradicionales del compilador Git plus slug ( git push heroku master), Heroku también admite implementaciones basadas en Docker, con Heroku Container Runtime.

Implementaciones basadas en Docker

Las implementaciones basadas en Docker tienen muchas ventajas sobre el enfoque tradicional:

  1. Sin límites de slug : Heroku permite un tamaño máximo de slug de 500 MB para las implementaciones tradicionales basadas en Git. Las implementaciones basadas en Docker, por otro lado, no tienen este límite.
  2. Control total sobre el sistema operativo : en lugar de estar limitado por los paquetes instalados por los paquetes de compilación de Heroku , tiene control total sobre el sistema operativo y puede instalar cualquier paquete que desee con Docker.
  3. Mayor paridad de desarrollo/producción: las compilaciones basadas en Docker tienen una mayor paridad entre desarrollo y producción, ya que los entornos subyacentes son los mismos.
  4. Menos bloqueo de proveedores : finalmente, Docker hace que sea mucho más fácil cambiar a un proveedor de alojamiento en la nube diferente, como AWS o GCP.

En general, las implementaciones basadas en Docker le brindan mayor flexibilidad y control sobre el entorno de implementación. Puede implementar las aplicaciones que desee dentro del entorno que desee. Dicho esto, ahora eres responsable de las actualizaciones de seguridad. Con las implementaciones tradicionales basadas en Git, Heroku es responsable de esto. Aplican actualizaciones de seguridad relevantes a sus pilas y migran su aplicación a las nuevas pilas según sea necesario. Mantén esto en mente.

Actualmente, hay dos formas de implementar aplicaciones con Docker en Heroku:

  1. Container Registry : implemente imágenes de Docker preconstruidas en Heroku
  2. Manifiesto de compilación : dado un Dockerfile, Heroku compila e implementa la imagen de Docker

La principal diferencia entre estos dos es que con el último enfoque, por ejemplo, a través del Manifiesto de compilación, tiene acceso a las funciones Pipelines , Review y Release . Por lo tanto, si está convirtiendo una aplicación de una implementación basada en Git a Docker y está usando alguna de esas funciones, debe usar el enfoque Build Manifest.

Tenga la seguridad de que veremos ambos enfoques en este artículo.

En cualquier caso, seguirá teniendo acceso a la CLI de Heroku , a todos los potentes complementos y al panel de control . Todas estas características funcionan con Container Runtime, en otras palabras.

Tipo de implementaciónMecanismo de despliegueActualizaciones de seguridad (quién las maneja)Acceso a Pipelines, Revisión, LiberaciónAcceso a CLI, complementos y panelLímites de tamaño de slug
Compilador Git + SlugEmpuje GitHeroku
Tiempo de ejecución de Docker + contenedorEmpuje de la ventana acoplableNoNo
Docker + Manifiesto de compilaciónEmpuje GitNo

Tenga en cuenta que las implementaciones basadas en Docker están limitadas a las mismas restricciones que las implementaciones basadas en Git. Por ejemplo, los volúmenes persistentes no son compatibles porque el sistema de archivos es efímero y los procesos web solo admiten solicitudes HTTP(S). Para obtener más información sobre esto, revise los comandos y el tiempo de ejecución de Dockerfile .

Docker vs Heroku Concepts

DockerHeroku
DockerfileBuildPack
ImageSlug
ContainerDyno

Project Setup

Make a project directory, create and activate a new virtual environment, and install Django:

$ mkdir django-heroku-docker
$ cd django-heroku-docker

$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django==3.2.9

Feel free to swap out virtualenv and Pip for Poetry or Pipenv. For more, review Modern Python Environments.

Next, create a new Django project, apply the migrations, and run the server:

(env)$ django-admin startproject hello_django .
(env)$ python manage.py migrate
(env)$ python manage.py runserver

Navigate to http://localhost:8000/ to view the Django welcome screen. Kill the server and exit from the virtual environment once done.

Docker

Add a Dockerfile to the project root:

# pull official base image
FROM python:3.10-alpine

# set work directory
WORKDIR /app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBUG 0

# install psycopg2
RUN apk update \
    && apk add --virtual build-essential gcc python3-dev musl-dev \
    && apk add postgresql-dev \
    && pip install psycopg2

# install dependencies
COPY ./requirements.txt .
RUN pip install -r requirements.txt

# copy project
COPY . .

# add and run as non-root user
RUN adduser -D myuser
USER myuser

# run gunicorn
CMD gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

Here, we started with an Alpine-based Docker image for Python 3.10. We then set a working directory along with two environment variables:

  1. PYTHONDONTWRITEBYTECODE: evita que Python escriba archivos pyc en el disco
  2. PYTHONUNBUFFERED: evita que Python almacene en búfer stdout y stderr

A continuación, instalamos dependencias a nivel de sistema y paquetes de Python, copiamos los archivos del proyecto, creamos y cambiamos a un usuario no root ( recomendado por Heroku ) y usamos CMD para ejecutar Gunicorn cuando un contenedor gira en tiempo de ejecución. Toma nota de la $PORTvariable. Esencialmente, cualquier servidor web que se ejecute en Container Runtime debe escuchar el tráfico HTTP en la $PORTvariable de entorno, que Heroku establece en tiempo de ejecución .

Cree un archivo de requisitos.txt :

Django==3.2.9
gunicorn==20.1.0

Luego agregue un archivo .dockerignore :

__pycache__
*.pyc
env/
db.sqlite3

Actualice las variables SECRET_KEY, DEBUGy en settings.py :ALLOWED_HOSTS

SECRET_KEY = os.environ.get('SECRET_KEY', default='foo')

DEBUG = int(os.environ.get('DEBUG', default=0))

ALLOWED_HOSTS = ['localhost', '127.0.0.1']

No olvides la importación:

import os

Para probar localmente, crea la imagen y ejecuta el contenedor, asegurándote de pasar las variables de entorno apropiadas:

$ docker build -t web:latest .
$ docker run -d --name django-heroku -e "PORT=8765" -e "DEBUG=1" -p 8007:8765 web:latest

Asegúrese de que la aplicación se esté ejecutando en http://localhost:8007/ en su navegador. Deténgase y luego retire el contenedor en ejecución una vez que haya terminado:

$ docker stop django-heroku
$ docker rm django-heroku

Agregue un .gitignore :

__pycache__
*.pyc
env/
db.sqlite3

A continuación, creemos una vista rápida de Django para probar fácilmente la aplicación cuando el modo de depuración está desactivado.

Agregue un archivo views.py al directorio "hello_django":

from django.http import JsonResponse


def ping(request):
    data = {'ping': 'pong!'}
    return JsonResponse(data)

A continuación, actualice urls.py :

from django.contrib import admin
from django.urls import path

from .views import ping


urlpatterns = [
    path('admin/', admin.site.urls),
    path('ping/', ping, name="ping"),
]

Prueba esto de nuevo con el modo de depuración desactivado:

$ docker build -t web:latest .
$ docker run -d --name django-heroku -e "PORT=8765" -e "DEBUG=0" -p 8007:8765 web:latest

Verifique que http://localhost:8007/ping/ funcione como se esperaba:

{
  "ping": "pong!"
}

Deténgase y luego retire el contenedor en ejecución una vez que haya terminado:

$ docker stop django-heroku
$ docker rm django-heroku

Ruido blanco

Si desea utilizar WhiteNoise para administrar sus activos estáticos, primero agregue el paquete al archivo requirements.txt :

Django==3.2.9
gunicorn==20.1.0
whitenoise==5.3.0

Actualice el middleware en settings.py así:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # new
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Luego configure el manejo de sus archivos estáticos con STATIC_ROOT:

STATIC_ROOT = BASE_DIR / 'staticfiles'

Finalmente, agregue soporte de compresión y almacenamiento en caché:

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

Agregue el collectstaticcomando al Dockerfile:

# pull official base image
FROM python:3.10-alpine

# set work directory
WORKDIR /app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBUG 0

# install psycopg2
RUN apk update \
    && apk add --virtual build-essential gcc python3-dev musl-dev \
    && apk add postgresql-dev \
    && pip install psycopg2

# install dependencies
COPY ./requirements.txt .
RUN pip install -r requirements.txt

# copy project
COPY . .

# collect static files
RUN python manage.py collectstatic --noinput

# add and run as non-root user
RUN adduser -D myuser
USER myuser

# run gunicorn
CMD gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

Para probar, crea la nueva imagen y haz girar un nuevo contenedor:

$ docker build -t web:latest .
$ docker run -d --name django-heroku -e "PORT=8765" -e "DEBUG=1" -p 8007:8765 web:latest

Debería poder ver los archivos estáticos cuando ejecuta:

$ docker exec django-heroku ls /app/staticfiles
$ docker exec django-heroku ls /app/staticfiles/admin

Deténgase y luego retire el contenedor en ejecución nuevamente:

$ docker stop django-heroku
$ docker rm django-heroku

postgres

Para poner en marcha Postgres, usaremos el paquete dj_database_url para generar el diccionario de configuración de base de datos adecuado para la configuración de Django en función de una DATABASE_URLvariable de entorno.

Agregue la dependencia al archivo de requisitos:

Django==3.2.9
dj-database-url==0.5.0
gunicorn==20.1.0
whitenoise==5.3.0

Luego, realice los siguientes cambios en la configuración para actualizar la configuración de la base de datos si DATABASE_URLestá presente:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

DATABASE_URL = os.environ.get('DATABASE_URL')
db_from_env = dj_database_url.config(default=DATABASE_URL, conn_max_age=500, ssl_require=True)
DATABASES['default'].update(db_from_env)

Por lo tanto, si DATABASE_URLno está presente, se seguirá utilizando SQLite.

Agregue la importación a la parte superior también:

import dj_database_url

Probaremos esto en un momento después de activar una base de datos de Postgres en Heroku.

Configuración de Heroku

Regístrese para obtener una cuenta de Heroku (si aún no tiene una) y luego instale la CLI de Heroku (si aún no lo ha hecho).

Crear una nueva aplicación:

$ heroku create
Creating app... done, ⬢ limitless-atoll-51647
https://limitless-atoll-51647.herokuapp.com/ | https://git.heroku.com/limitless-atoll-51647.git

Agregue la SECRET_KEYvariable de entorno:

$ heroku config:set SECRET_KEY=SOME_SECRET_VALUE -a limitless-atoll-51647

Cambie SOME_SECRET_VALUEa una cadena generada aleatoriamente que tenga al menos 50 caracteres.

Agregue la URL de Heroku anterior a la lista ALLOWED_HOSTSen hello_django/settings.py así:

ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'limitless-atoll-51647.herokuapp.com']

Asegúrese de reemplazar limitless-atoll-51647cada uno de los comandos anteriores con el nombre de su aplicación.

Implementación de la ventana acoplable de Heroku

En este punto, estamos listos para comenzar a implementar imágenes de Docker en Heroku. ¿Decidiste qué enfoque te gustaría tomar?

  1. Container Registry : implemente imágenes de Docker preconstruidas en Heroku
  2. Manifiesto de compilación : dado un Dockerfile, Heroku compila e implementa la imagen de Docker

¿Inseguro? ¡Pruébalos a los dos!

Enfoque #1: Registro de Contenedores

Omita esta sección si está utilizando el enfoque de manifiesto de compilación.

Nuevamente, con este enfoque, puede implementar imágenes de Docker preconstruidas en Heroku.

Inicie sesión en Heroku Container Registry , para indicarle a Heroku que queremos usar el Container Runtime:

$ heroku container:login

Reconstruya la imagen de Docker y etiquétela con el siguiente formato:

registry.heroku.com/<app>/<process-type>

Asegúrese de reemplazar <app>con el nombre de la aplicación Heroku que acaba de crear, ya <process-type>que webserá para un proceso web .

Por ejemplo:

$ docker build -t registry.heroku.com/limitless-atoll-51647/web .

Empuje la imagen al registro:

$ docker push registry.heroku.com/limitless-atoll-51647/web

Suelta la imagen:

$ heroku container:release -a limitless-atoll-51647 web

Esto ejecutará el contenedor. Debería poder ver la aplicación en https://APP_NAME.herokuapp.com . Debería devolver un 404.

Intente ejecutar heroku open -a limitless-atoll-51647para abrir la aplicación en su navegador predeterminado.

Verifica que https://APP_NAME.herokuapp.com/ping también funcione:

{
  "ping": "pong!"
}

También debería poder ver los archivos estáticos:

$ heroku run ls /app/staticfiles -a limitless-atoll-51647
$ heroku run ls /app/staticfiles/admin -a limitless-atoll-51647

Asegúrese de reemplazar limitless-atoll-51647cada uno de los comandos anteriores con el nombre de su aplicación.

Vaya a la sección "Prueba de Postgres" una vez que haya terminado.

Enfoque #2: Manifiesto de compilación

Omita esta sección si está utilizando el enfoque de Container Registry.

Una vez más, con el enfoque de compilación de manifiesto , puede hacer que Heroku construya e implemente imágenes de Docker en función de un archivo de manifiesto heroku.yml .

Establezca la pila de su aplicación en contenedor:

$ heroku stack:set container -a limitless-atoll-51647

Agregue un archivo heroku.yml a la raíz del proyecto:

build:
  docker:
    web: Dockerfile

Aquí, solo le estamos diciendo a Heroku qué Dockerfile debe usar para crear la imagen.

Junto con build, también puede definir las siguientes etapas:

  • setupse usa para definir complementos de Heroku y variables de configuración para crear durante el aprovisionamiento de la aplicación.
  • release is used to define tasks that you'd like to execute during a release.
  • run is used to define which commands to run for the web and worker processes.

Be sure to review the Heroku documentation to learn more about these four stages.

It's worth noting that the gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT command could be removed from the Dockerfile and added to the heroku.yml file under the run stage:

build:
  docker:
    web: Dockerfile
run:
  web: gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

Also, be sure to place the 'collectstatic' command inside your Dockerfile. Don't move it to the release stage. For more on this, review this Stack Overflow question.

Next, install the heroku-manifest plugin from the beta CLI channel:

$ heroku update beta
$ heroku plugins:install @heroku-cli/plugin-manifest

With that, initialize a Git repo and create a commit.

Then, add the Heroku remote:

$ heroku git:remote -a limitless-atoll-51647

Push the code up to Heroku to build the image and run the container:

$ git push heroku master

You should be able to view the app at https://APP_NAME.herokuapp.com. It should return a 404.

Try running heroku open -a limitless-atoll-51647 to open the app in your default browser.

Verify https://APP_NAME.herokuapp.com/ping works as well:

{
  "ping": "pong!"
}

You should also be able to view the static files:

$ heroku run ls /app/staticfiles -a limitless-atoll-51647
$ heroku run ls /app/staticfiles/admin -a limitless-atoll-51647

Make sure to replace limitless-atoll-51647 in each of the above commands with the name of your app.

Postgres Test

Create the database:

$ heroku addons:create heroku-postgresql:hobby-dev -a limitless-atoll-51647

This command automatically sets the DATABASE_URL environment variable for the container.

Once the database is up, run the migrations:

$ heroku run python manage.py makemigrations -a limitless-atoll-51647
$ heroku run python manage.py migrate -a limitless-atoll-51647

Then, jump into psql to view the newly created tables:

$ heroku pg:psql -a limitless-atoll-51647

# \dt
                      List of relations
 Schema |            Name            | Type  |     Owner
--------+----------------------------+-------+----------------
 public | auth_group                 | table | siodzhzzcvnwwp
 public | auth_group_permissions     | table | siodzhzzcvnwwp
 public | auth_permission            | table | siodzhzzcvnwwp
 public | auth_user                  | table | siodzhzzcvnwwp
 public | auth_user_groups           | table | siodzhzzcvnwwp
 public | auth_user_user_permissions | table | siodzhzzcvnwwp
 public | django_admin_log           | table | siodzhzzcvnwwp
 public | django_content_type        | table | siodzhzzcvnwwp
 public | django_migrations          | table | siodzhzzcvnwwp
 public | django_session             | table | siodzhzzcvnwwp
(10 rows)

# \q

Again, make sure to replace limitless-atoll-51647 in each of the above commands with the name of your Heroku app.

GitLab CI

Regístrese para obtener una cuenta de GitLab (si es necesario) y luego cree un nuevo proyecto (nuevamente, si es necesario).

Recupere su token de autenticación de Heroku :

$ heroku auth:token

Luego, guarde el token como una nueva variable llamada HEROKU_AUTH_TOKENdentro de la configuración de CI/CD de su proyecto: Configuración > CI/CD > Variables.

configuración de gitlab

A continuación, debemos agregar un archivo de configuración GitLab CI/CD llamado .gitlab-ci.yml a la raíz del proyecto. El contenido de este archivo variará según el enfoque utilizado.

Enfoque #1: Registro de Contenedores

Omita esta sección si está utilizando el enfoque de manifiesto de compilación.

.gitlab-ci.yml :

image: docker:stable
services:
  - docker:dind

variables:
  DOCKER_DRIVER: overlay2
  HEROKU_APP_NAME: <APP_NAME>
  HEROKU_REGISTRY_IMAGE: registry.heroku.com/${HEROKU_APP_NAME}/web

stages:
  - build_and_deploy

build_and_deploy:
  stage: build_and_deploy
  script:
    - apk add --no-cache curl
    - docker login -u _ -p $HEROKU_AUTH_TOKEN registry.heroku.com
    - docker pull $HEROKU_REGISTRY_IMAGE || true
    - docker build
      --cache-from $HEROKU_REGISTRY_IMAGE
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $HEROKU_REGISTRY_IMAGE
    - chmod +x ./release.sh
    - ./release.sh

liberación.sh :

#!/bin/sh


IMAGE_ID=$(docker inspect ${HEROKU_REGISTRY_IMAGE} --format={{.Id}})
PAYLOAD='{"updates": [{"type": "web", "docker_image": "'"$IMAGE_ID"'"}]}'

curl -n -X PATCH https://api.heroku.com/apps/$HEROKU_APP_NAME/formation \
  -d "${PAYLOAD}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/vnd.heroku+json; version=3.docker-releases" \
  -H "Authorization: Bearer ${HEROKU_AUTH_TOKEN}"

Aquí, definimos una sola build_and_deploy etapa en la que:

  1. Instalar cURL
  2. Inicie sesión en el registro de contenedores de Heroku
  3. Pull the previously pushed image (if it exists)
  4. Build and tag the new image
  5. Push the image up to the registry
  6. Create a new release via the Heroku API using the image ID within the release.sh script

Make sure to replace <APP_NAME> with your Heroku app's name.

With that, initialize a Git repo, commit, add the GitLab remote, and push your code up to GitLab to trigger a new pipeline. This will run the build_and_deploy stage as a single job. Once complete, a new release should automatically be created on Heroku.

Approach #2: Build Manifest

Skip this section if you're using the Container Registry approach.

.gitlab-ci.yml:

variables:
  HEROKU_APP_NAME: <APP_NAME>

stages:
  - deploy

deploy:
  stage: deploy
  script:
    - apt-get update -qy
    - apt-get install -y ruby-dev
    - gem install dpl
    - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_AUTH_TOKEN

Here, we defined a single deploy stage where we:

  1. Instale Ruby junto con una gema llamada dpl
  2. Implemente el código en Heroku con dpl

Asegúrese de reemplazar <APP_NAME>con el nombre de su aplicación Heroku.

Confirme, agregue el control remoto de GitLab y envíe su código a GitLab para activar una nueva canalización . Esto ejecutará el deployescenario como un solo trabajo. Una vez completado, el código debe implementarse en Heroku.

IC avanzado

En lugar de solo crear la imagen de Docker y crear una versión en GitLab CI, también ejecutemos las pruebas de Django, Flake8 , Black e isort .

Una vez más, esto variará según el enfoque que haya utilizado.

Enfoque #1: Registro de Contenedores

Omita esta sección si está utilizando el enfoque de manifiesto de compilación.

Actualice .gitlab-ci.yml así:

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:latest || true
    - docker build
      --cache-from $IMAGE:latest
      --tag $IMAGE:latest
      --file ./Dockerfile
      "."
    - docker push $IMAGE:latest

test:
  stage: test
  image: $IMAGE:latest
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
    HEROKU_APP_NAME: <APP_NAME>
    HEROKU_REGISTRY_IMAGE: registry.heroku.com/${HEROKU_APP_NAME}/web
  script:
    - apk add --no-cache curl
    - docker login -u _ -p $HEROKU_AUTH_TOKEN registry.heroku.com
    - docker pull $HEROKU_REGISTRY_IMAGE || true
    - docker build
      --cache-from $HEROKU_REGISTRY_IMAGE
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $HEROKU_REGISTRY_IMAGE
    - chmod +x ./release.sh
    - ./release.sh

Asegúrese de reemplazar <APP_NAME>con el nombre de su aplicación Heroku.

Entonces, ahora tenemos tres etapas: build, testy deploy.

En el buildescenario, nosotros:

  1. Inicie sesión en el registro de contenedores de GitLab
  2. Tire de la imagen previamente empujada (si existe)
  3. Crea y etiqueta la nueva imagen
  4. Sube la imagen al Registro de contenedores de GitLab

Luego, en la testetapa, configuramos Postgres , configuramos la DATABASE_URLvariable de entorno y luego ejecutamos las pruebas de Django, Flake8, Black e isort usando la imagen que se construyó en la etapa anterior.

En el deployescenario, nosotros:

  1. Instalar cURL
  2. Inicie sesión en el registro de contenedores de Heroku
  3. Tire de la imagen previamente empujada (si existe)
  4. Crea y etiqueta la nueva imagen
  5. Sube la imagen al registro
  6. Cree una nueva versión a través de la API de Heroku usando la ID de la imagen dentro del script release.sh

Agregue las nuevas dependencias al archivo de requisitos:

# prod
Django==3.2.9
dj-database-url==0.5.0
gunicorn==20.1.0
whitenoise==5.3.0

# dev and test
black==21.11b1
flake8==4.0.1
isort==5.10.1

Antes de subir a GitLab, ejecute las pruebas de Django localmente:

$ source env/bin/activate
(env)$ pip install -r requirements.txt
(env)$ python manage.py test

System check identified no issues (0 silenced).

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Asegúrese de que Flake8 sea aprobado y luego actualice el código fuente según las recomendaciones de Black e isort:

(env)$ flake8 hello_django --max-line-length=100
(env)$ black hello_django
(env)$ isort hello_django --profile black

Confirme y presione su código una vez más. Asegúrese de que pasen todas las etapas.

Enfoque #2: Manifiesto de compilación

Omita esta sección si está utilizando el enfoque de Container Registry.

Actualice .gitlab-ci.yml así:

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:latest || true
    - docker build
      --cache-from $IMAGE:latest
      --tag $IMAGE:latest
      --file ./Dockerfile
      "."
    - docker push $IMAGE:latest

test:
  stage: test
  image: $IMAGE:latest
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  variables:
    HEROKU_APP_NAME: <APP_NAME>
  script:
    - apt-get update -qy
    - apt-get install -y ruby-dev
    - gem install dpl
    - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_AUTH_TOKEN

Asegúrese de reemplazar <APP_NAME>con el nombre de su aplicación Heroku.

Entonces, ahora tenemos tres etapas: build, testy deploy.

En el buildescenario, nosotros:

  1. Inicie sesión en el registro de contenedores de GitLab
  2. Tire de la imagen previamente empujada (si existe)
  3. Crea y etiqueta la nueva imagen
  4. Sube la imagen al Registro de contenedores de GitLab

Luego, en la testetapa, configuramos Postgres , configuramos la DATABASE_URLvariable de entorno y luego ejecutamos las pruebas de Django, Flake8, Black e isort usando la imagen que se construyó en la etapa anterior.

En el deployescenario, nosotros:

  1. Instale Ruby junto con una gema llamada dpl
  2. Implemente el código en Heroku con dpl

Agregue las nuevas dependencias al archivo de requisitos:

# prod
Django==3.2.9
dj-database-url==0.5.0
gunicorn==20.1.0
whitenoise==5.3.0

# dev and test
black==21.11b1
flake8==4.0.1
isort==5.10.1

Antes de subir a GitLab, ejecute las pruebas de Django localmente:

$ source env/bin/activate
(env)$ pip install -r requirements.txt
(env)$ python manage.py test

System check identified no issues (0 silenced).

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Asegúrese de que Flake8 sea aprobado y luego actualice el código fuente según las recomendaciones de Black e isort:

(env)$ flake8 hello_django --max-line-length=100
(env)$ black hello_django
(env)$ isort hello_django --profile black

Confirme y presione su código una vez más. Asegúrese de que pasen todas las etapas.

Construcción de Docker de varias etapas

Finalmente, actualice el Dockerfile así para usar una compilación de varias etapas para reducir el tamaño final de la imagen:

FROM python:3.10-alpine AS build-python
RUN apk update && apk add --virtual build-essential gcc python3-dev musl-dev postgresql-dev
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY ./requirements.txt .
RUN pip install -r requirements.txt

FROM python:3.10-alpine
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBUG 0
ENV PATH="/opt/venv/bin:$PATH"
COPY --from=build-python /opt/venv /opt/venv
RUN apk update && apk add --virtual build-deps gcc python3-dev musl-dev postgresql-dev
RUN pip install psycopg2-binary
WORKDIR /app
COPY . .
RUN python manage.py collectstatic --noinput
RUN adduser -D myuser
USER myuser
CMD gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

A continuación, debemos actualizar la configuración de GitLab para aprovechar el almacenamiento en caché de la capa de Docker.

Enfoque #1: Registro de Contenedores

Omita esta sección si está utilizando el enfoque de manifiesto de compilación.

.gitlab-ci.yml :

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}
  HEROKU_APP_NAME: <APP_NAME>
  HEROKU_REGISTRY_IMAGE: registry.heroku.com/${HEROKU_APP_NAME}/web

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:build-python || true
    - docker pull $IMAGE:production || true
    - docker build
      --target build-python
      --cache-from $IMAGE:build-python
      --tag $IMAGE:build-python
      --file ./Dockerfile
      "."
    - docker build
      --cache-from $IMAGE:production
      --tag $IMAGE:production
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $IMAGE:build-python
    - docker push $IMAGE:production

test:
  stage: test
  image: $IMAGE:production
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - apk add --no-cache curl
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:build-python || true
    - docker pull $IMAGE:production || true
    - docker build
      --target build-python
      --cache-from $IMAGE:build-python
      --tag $IMAGE:build-python
      --file ./Dockerfile
      "."
    - docker build
      --cache-from $IMAGE:production
      --tag $IMAGE:production
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $IMAGE:build-python
    - docker push $IMAGE:production
    - docker login -u _ -p $HEROKU_AUTH_TOKEN registry.heroku.com
    - docker push $HEROKU_REGISTRY_IMAGE
    - chmod +x ./release.sh
    - ./release.sh

Asegúrese de reemplazar <APP_NAME>con el nombre de su aplicación Heroku.

Revise los cambios por su cuenta. Luego, pruébalo por última vez.

Para obtener más información sobre este patrón de almacenamiento en caché, consulte la sección "Multifase" del artículo Faster CI Builds with Docker Cache .

Enfoque #2: Manifiesto de compilación

Omita esta sección si está utilizando el enfoque de Container Registry.

.gitlab-ci.yml :

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}
  HEROKU_APP_NAME: <APP_NAME>

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:build-python || true
    - docker pull $IMAGE:production || true
    - docker build
      --target build-python
      --cache-from $IMAGE:build-python
      --tag $IMAGE:build-python
      --file ./Dockerfile
      "."
    - docker build
      --cache-from $IMAGE:production
      --tag $IMAGE:production
      --file ./Dockerfile
      "."
    - docker push $IMAGE:build-python
    - docker push $IMAGE:production

test:
  stage: test
  image: $IMAGE:production
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  script:
    - apt-get update -qy
    - apt-get install -y ruby-dev
    - gem install dpl
    - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_AUTH_TOKEN

Asegúrese de reemplazar <APP_NAME>con el nombre de su aplicación Heroku.

Revise los cambios por su cuenta. Luego, pruébalo por última vez.

Para obtener más información sobre este patrón de almacenamiento en caché, consulte la sección "Multifase" del artículo Faster CI Builds with Docker Cache .

Conclusión

En este artículo, analizamos dos enfoques para implementar una aplicación Django en Heroku con Docker: el registro de contenedores y el manifiesto de compilación.

Entonces, ¿cuándo debería pensar en usar Heroku Container Runtime sobre el compilador tradicional Git y slug para implementaciones?

Cuando necesite más control sobre el entorno de implementación de producción.

Ejemplos:

  1. Su aplicación y dependencias superan el límite máximo de slug de 500 MB.
  2. Su aplicación requiere paquetes no instalados por los paquetes de compilación habituales de Heroku.
  3. Desea una mayor seguridad de que su aplicación se comportará de la misma manera en el desarrollo que en la producción.
  4. Realmente disfrutas trabajar con Docker.

Fuente:  https://testdriven.io

#django #heroku #docker 

Ahebwe  Oscar

Ahebwe Oscar

1620177818

Django admin full Customization step by step

Welcome to my blog , hey everyone in this article you learn how to customize the Django app and view in the article you will know how to register  and unregister  models from the admin view how to add filtering how to add a custom input field, and a button that triggers an action on all objects and even how to change the look of your app and page using the Django suit package let’s get started.

Database

Custom Titles of Django Admin

Exclude in Django Admin

Fields in Django Admin

#django #create super user django #customize django admin dashboard #django admin #django admin custom field display #django admin customization #django admin full customization #django admin interface #django admin register all models #django customization

Cómo Implementar Una Aplicación Django En AWS EC2 Con Docker

En este tutorial, implementaremos una aplicación Django en AWS EC2 con Docker. La aplicación se ejecutará detrás de un proxy HTTPS Nginx que utiliza certificados Let's Encrypt SSL. Usaremos AWS RDS para servir nuestra base de datos de Postgres junto con AWS ECR para almacenar y administrar nuestras imágenes de Docker.

Objetivos

Al final de este tutorial, podrá:

  1. Configurar una nueva instancia EC2
  2. Instalar Docker en una instancia EC2
  3. Configurar y usar una dirección IP elástica
  4. Configurar un rol de IAM
  5. Utilice el registro de imágenes de Amazon Elastic Container Registry (ECR) para almacenar imágenes creadas
  6. Configurar AWS RDS para la persistencia de datos
  7. Configurar un grupo de seguridad de AWS
  8. Implemente Django en AWS EC2 con Docker
  9. Ejecute la aplicación Django detrás de un proxy HTTPS Nginx con certificados Let's Encrypt SSL

requisitos previos

Esta publicación se basa en la Dockerización de Django con Postgres, Gunicorn y Nginx y en la protección de una aplicación Django en contenedor con las publicaciones de Let's Encrypt .

Se supone que usted puede:

  1. Contenga una aplicación de Django junto con Postgres, Nginx y Gunicorn.
  2. Proteja una aplicación Django en contenedor que se ejecuta detrás de un proxy HTTPS Nginx con certificados Let's Encrypt SSL.
  3. Use SSH para conectarse a un servidor remoto y SCP para copiar archivos al servidor.

AWS EC2

Primero, cree una cuenta de AWS si aún no tiene una.

A continuación, vaya a la consola de EC2 y haga clic en Iniciar instancia :

Inicio EC2

Use Ubuntu Server 18.04 LTS (HVM) para la imagen del servidor (AMI):

Seleccionar AMI

In the next step, stick with the t2.micro instance. Click on Next: Configure Instance Details:

tipo de instancia EC2

At the Configure Instance Details step, leave everything as it is to keep things simple. Then click Next a few times until you're at the Configure Security Group step.

With Create a new security group selected, set the name and description to django-ec2 and add two rules:

  • HTTP -> Anywhere
  • HTTPS -> Anywhere

EC2 configurar grupos de seguridad

These rules are needed to issue certificates and to access the app.

Security group inbound rules are used to limit access to your instance from the internet. Unless you have some additional security requirements, you'll probably want to allow HTTP and HTTPS traffic from anywhere for instances hosting web apps. SSH must be allowed for you to connect to the instance for set up and deployment.

Click Review and Launch. On the next screen, click Launch.

You'll be prompted to select a key pair. You need it for SSH connection to your instance. Select Create a new key pair and name it djangoletsencrypt. Then click Download key pair. After the key pair is downloaded, click on Launch Instances:

EC2 Agregar par de claves

It will take a few minutes for the instance to spin up.

Configure EC2 Instance

In this section, we'll install Docker on the instance, add an Elastic IP, and configure an IAM role.

Install Docker

Navigate back to the EC2 console, select the newly created instance, and grab the public IP address:

EC2 IP pública

Connect to your EC2 instance using the .pem key that we downloaded in the "AWS EC2" step.

$ ssh -i /path/to/your/djangoletsencrypt.pem ubuntu@public-ip-or-domain-of-ec2-instance

Your .pem was probably downloaded into path like ~/Downloads/djangoletsencrypt.pem. If you're not sure where to store it, move it into the ~/.ssh directory. You may have to also change the permissions -- i.e., chmod 400 -i /path/to/your/djangoletsencrypt.pem.

Start by installing the latest version of Docker and version 1.29.2 of Docker Compose:

$ sudo apt update
$ sudo apt install apt-transport-https ca-certificates curl software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
$ sudo apt update
$ sudo apt install docker-ce
$ sudo usermod -aG docker ${USER}
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

$ docker -v
Docker version 20.10.8, build 3967b7d

$ docker-compose -v
docker-compose version 1.29.2, build 5becea4c

Install AWS CLI

First, install unzip:

$ sudo apt install unzip

Download AWS CLI ZIP:

$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"

Unzip its content:

$ unzip awscliv2.zip

Install AWS CLI:

$ sudo ./aws/install

Verify installation:

$ aws --version

Elastic IP

By default, instances receive new public IP address every time they start and re-start.

Elastic IP allows you to allocate static IPs for your EC2 instances, so the IP stays the same all the time and can be re-associated between instances. It's recommended to use one for your production setup.

Navigate to Elastic IPs and click Allocate Elastic IP address:

IP elástica

Then, click Allocate:

Asignación de IP elástica

Click on Associate this Elastic IP address:

Asociado de IP elástica

Select your instance and click Associate:

Instancia de selección de IP elástica

IAM Role

We'll be using AWS ECR to pull images from AWS ECR to our EC2 instance during deployment. Since we won't be allowing public access to the Docker image on ECR, you'll need to create an IAM role with permissions to pull Docker images from ECR and attach it to your EC2 instance.

Navigate to the IAM console.

Click Roles in the left sidebar and then Create role:

Funciones de gestión de identidades y accesos

Select AWS Service and EC2, then click on Next: Permissions:

Caso de uso de selección de IAM

Enter container in the search box, select the AmazonEC2ContainerRegistryPowerUser policy, and click Next: Tags:

Política de funciones de IAM

Click Next: Review. Use django-ec2 for the name, and click on Create role:

Revisión de roles de IAM

Now you need to attach the new role to your EC2 instance.

Back in the EC2 console, click Instances, and then select your instance. Click the Actions dropdown -> Instance settings -> Attach/Replace IAM Role:

EC2 Adjuntar rol de IAM

Select the django-ec2 role, and then click Apply.

EC2 Seleccionar rol de IAM

Click Close.

Add DNS Record

Add an A record to your DNS, for the domain that you are using, to point to your EC2 instance's public IP.

It's the Elastic IP you've associated to your instance.

AWS ECR

Amazon Elastic Container Registry (ECR) is a fully-managed Docker image registry that makes it easy for developers to store and manage images. For private images, access is managed via IAM users and roles.

Navigate to the ECR console. Click Repositories in the sidebar and then on Create repository:

Repositorios ECR

Set the name to django-ec2 and click on Create repository:

ECR Crear repositorio

AWS RDS

Now we can configure an RDS Postgres database.

While you can run your own Postgres database in a container, since databases are critical services, adding additional layers, such us Docker, adds unnecessary risk in production. To simplify tasks such as minor version updates, regular backups, and scaling, it's recommended to use a managed service. So, we'll use RDS.

Navigate to the RDS console. Click on Create database:

RDS Inicio

Select the latest version of Postgres with the Free tier template:

RDS Crear base de datos

Under Settings, set:

  • DB Instance identifier: djangoec2
  • Master username: webapp
  • Select Auto generate a password

Stick with default settings for:

  • DB instance size
  • Storage
  • Availability & durability

Skip down to the Connectivity section and set the following:

  • Virtual private cloud (VPC): Default
  • Subnet group: default
  • Publicly accessible: No
  • VPC security group: django-ec2
  • Database port: 5432

RDS Crear conectividad de base de datos

Leave Database authentication as it is.

Open Additional configuration and change Initial database name to djangoec2:

RDS Crear nombre de base de datos inicial de la base de datos

Leave the other settings as they are.

Finally, click Create database.

Click on View credentials details to see the generated password for the webapp user:

RDS Ver credenciales

Store this password somewhere safe. You'll need to provide it to the Django application here shortly.

It will take a few of minutes for the instance to spin up. Once up, click on the DB Identifier of the newly created database to see its details. Take note of the database endpoint; you'll need to set it in your Django app.

Detalles de la base de datos RDS

AWS Security Group

Within the EC2 console, click Security Groups in the sidebar. Find and click the ID of the django-ec2 group to edit its details.

Click on Edit inbound rules:

Detalles del grupo de seguridad EC2

Add an inbound rule that will allow Postgres connections to instances inside that Security Group. To do that:

  • click on Add rule
  • select PostgreSQL for the rule type
  • select Custom for the rule source
  • click into the search field and select the django-ec2 Security Group
  • click on Save rules

To limit access to your database, only connections from instances inside the same Security Group are allowed. Our application can connect because we set the same Security Group, django-ec2, for both the RDS and EC2 instances. Instances inside other Security Groups are therefore not allowed to connect.

EC2 Editar reglas de entrada

Project Config

With the AWS infrastructure set up, we now need to configure our Django project locally before deploying it.

First, clone down the contents from the GitHub project repo:

$ git clone https://github.com/testdrivenio/django-on-docker-letsencrypt django-on-docker-letsencrypt-aws
$ cd django-on-docker-letsencrypt-aws

This repository contains everything that you need to deploy a Dockerized Django with Let's Encrypt HTTPS certificates.

Docker Compose

When the app is deployed for the first time, you should follow these two steps to avoid issues with certificates:

  1. Start by issuing the certificates from Let's Encrypt's staging environment
  2. Then, when all is running as expected, switch to Let's Encrypt's production environment

You can read more about Let's Encrypt's limitations on production environments in the Let's Encrypt section from the previous post, Securing a Containerized Django Application with Let's Encrypt.

For testing, update the docker-compose.staging.yml. file like so:

version: '3.8'

services:
  web:
    build:
      context: ./app
      dockerfile: Dockerfile.prod
    image: <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:web
    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.staging
  nginx-proxy:
    container_name: nginx-proxy
    build: nginx
    image: <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:nginx-proxy
    restart: always
    ports:
      - 443:443
      - 80:80
    volumes:
      - static_volume:/home/app/web/staticfiles
      - media_volume:/home/app/web/mediafiles
      - certs:/etc/nginx/certs
      - html:/usr/share/nginx/html
      - vhost:/etc/nginx/vhost.d
      - /var/run/docker.sock:/tmp/docker.sock:ro
    depends_on:
      - web
  nginx-proxy-letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    env_file:
      - ./.env.staging.proxy-companion
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - certs:/etc/nginx/certs
      - html:/usr/share/nginx/html
      - vhost:/etc/nginx/vhost.d
      - acme:/etc/acme.sh
    depends_on:
      - nginx-proxy

volumes:
  static_volume:
  media_volume:
  certs:
  html:
  vhost:
  acme:

For the web and nginx-proxy services, update the image properties to use images from ECR (which we'll add shortly).

Examples:

image: 123456789.dkr.ecr.us-east-1.amazonaws.com/django-ec2:web

image: 123456789.dkr.ecr.us-east-1.amazonaws.com/django-ec2:nginx-proxy

The values consist of the repository URL (123456789.dkr.ecr.us-east-1.amazonaws.com) along with the image name (django-ec2) and tags (web and nginx-proxy).

To keep things simple we're using a single registry to store both images. We used the web and nginx-proxy to differentiate between the two. Ideally, you should use two registries: one for web and one for nginx-proxy. Update this on your own if you'd like it.

Other than the image properties, we also removed the db service (and related volume) since we're using RDS rather than managing Postgres in a container.

Environments

It's time to set up the environment files for the web and nginx-proxy-letsencrypt containers.

First, add an .env.staging file for the web container:

DEBUG=0
SECRET_KEY=change_me
DJANGO_ALLOWED_HOSTS=<YOUR_DOMAIN.COM>
SQL_ENGINE=django.db.backends.postgresql
SQL_DATABASE=djangoec2
SQL_USER=webapp
SQL_PASSWORD=<PASSWORD-FROM-AWS-RDS>
SQL_HOST=<DATABASE-ENDPOINT-FROM-AWS-RDS>
SQL_PORT=5432
DATABASE=postgres
VIRTUAL_HOST=<YOUR_DOMAIN.COM>
VIRTUAL_PORT=8000
LETSENCRYPT_HOST=<YOUR_DOMAIN.COM>

Notes:

  1. Change <YOUR_DOMAIN.COM> to your actual domain.
  2. Change SQL_PASSWORD and SQL_HOST to match those created in the RDS section.
  3. Change SECRET_KEY to some long random string.
  4. The VIRTUAL_HOST and VIRTUAL_PORT are needed by nginx-proxy container to auto create the reverse proxy configuration.
  5. LETSENCRYPT_HOST is there so the nginx-proxy-companion can issue Let's Encrypt certificate for your domain.

For testing/debugging purposes you may want to use a * for DJANGO_ALLOWED_HOSTS the first time you deploy to simplify things. Just don't forget to limit the allowed hosts once testing is complete.

Second, add an .env.staging.proxy-companion file, making sure to update the DEFAULT_EMAIL value:

DEFAULT_EMAIL=youremail@yourdomain.com
ACME_CA_URI=https://acme-staging-v02.api.letsencrypt.org/directory
NGINX_PROXY_CONTAINER=nginx-proxy

Build and Push Docker Images

Now we're ready to build the Docker images:

$ docker-compose -f docker-compose.staging.yml build

It may take a few minutes to build. Once done, we're ready to push the images up to ECR.

First, assuming that you have the awscli installed and that you've set your AWS credentials, log in to the ECR Docker repository:

$ aws ecr get-login-password --region <aws-region> | docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com
# aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com

You should see:

Login Succeeded

Then push the images to ECR:

$ docker-compose -f docker-compose.staging.yml push

Open your django-ec2 ECR repository to see the pushed images:

Imágenes ECR

Running the Containers

Everything is set up for deployment.

It's time to move to your EC2 instance.

Assuming you have a project directory created on your instance, like /home/ubuntu/django-on-docker, copy the files and folders over with SCP:

$ scp -i /path/to/your/djangoletsencrypt.pem \
      -r $(pwd)/{app,nginx,.env.staging,.env.staging.proxy-companion,docker-compose.staging.yml} \
      ubuntu@public-ip-or-domain-of-ec2-instance:/path/to/django-on-docker

Next, connect to your instance via SSH and move to the project directory:

$ ssh -i /path/to/your/djangoletsencrypt.pem ubuntu@public-ip-or-domain-of-ec2-instance
$ cd /path/to/django-on-docker

Login to ECR Docker repository.

$ aws ecr get-login-password --region <aws-region> | docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com

Pull the images:

$ docker pull <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:web
$ docker pull <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:nginx-proxy

With that, you're ready to spin up the containers:

$ docker-compose -f docker-compose.staging.yml up -d

Una vez que los contenedores estén en funcionamiento, navegue hasta su dominio en su navegador. Deberías ver algo como:

Certificado HTTPS no seguro

Esto se espera. Esta pantalla se muestra porque el certificado se emitió desde un entorno de ensayo .

¿Cómo saber si todo funciona?

Haga clic en "Avanzado" y luego en "Continuar". Ahora deberías ver tu aplicación. Cargue una imagen y luego asegúrese de que puede ver la imagen en https://yourdomain.com/media/IMAGE_FILE_NAME.

Emitir el Certificado de Producción

Ahora que todo funciona como se esperaba, podemos cambiar al entorno de producción de Let's Encrypt.

Baja los contenedores existentes y sal de tu instancia:

$ docker-compose -f docker-compose.staging.yml down -v
$ exit

De vuelta en su máquina local, abra docker-compose.prod.yml y realice los mismos cambios que hizo para la versión provisional:

  1. Actualice las ìmagepropiedades para que coincidan con sus URL de AWS ECR para los servicios ẁebynginx-proxy
  2. Eliminar el dbservicio junto con el volumen relacionado
version: '3.8'

services:
  web:
    build:
      context: ./app
      dockerfile: Dockerfile.prod
    image: 046505967931.dkr.ecr.us-east-1.amazonaws.com/django-ec2:web
    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
  nginx-proxy:
    container_name: nginx-proxy
    build: nginx
    image: 046505967931.dkr.ecr.us-east-1.amazonaws.com/django-ec2:nginx-proxy
    restart: always
    ports:
      - 443:443
      - 80:80
    volumes:
      - static_volume:/home/app/web/staticfiles
      - media_volume:/home/app/web/mediafiles
      - certs:/etc/nginx/certs
      - html:/usr/share/nginx/html
      - vhost:/etc/nginx/vhost.d
      - /var/run/docker.sock:/tmp/docker.sock:ro
    depends_on:
      - web
  nginx-proxy-letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    env_file:
      - ./.env.prod.proxy-companion
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - certs:/etc/nginx/certs
      - html:/usr/share/nginx/html
      - vhost:/etc/nginx/vhost.d
      - acme:/etc/acme.sh
    depends_on:
      - nginx-proxy

volumes:
  static_volume:
  media_volume:
  certs:
  html:
  vhost:
  acme:

A continuación, cree un archivo .env.prod duplicando el archivo .env.staging . No es necesario que realice ningún cambio en él.

Finalmente, agregue un archivo .env.prod.proxy-companion :

DEFAULT_EMAIL=youremail@yourdomain.com
NGINX_PROXY_CONTAINER=nginx-proxy

Cree y envíe imágenes de nuevo:

$ docker-compose -f docker-compose.prod.yml build
$ aws ecr get-login-password --region <aws-region> | docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com
$ docker-compose -f docker-compose.prod.yml push

Copie los nuevos archivos y carpetas a su instancia con SCP:

$ scp -i /path/to/your/djangoletsencrypt.pem \
      $(pwd)/{.env.prod,.env.prod.proxy-companion,docker-compose.prod.yml} \
      ubuntu@public-ip-or-domain-of-ec2-instance:/path/to/django-on-docker

Como antes, conéctese a su instancia a través de SSH y muévase al directorio del proyecto:

$ ssh -i /path/to/your/djangoletsencrypt.pem ubuntu@public-ip-or-domain-of-ec2-instance
$ cd /path/to/django-on-docker

Vuelva a iniciar sesión en su repositorio ECR Docker:

$ aws ecr get-login-password --region <aws-region> | docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com

Saca las imágenes:

$ docker pull <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:web
$ docker pull <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:nginx-proxy

Y finalmente girar los contenedores:

$ docker-compose -f docker-compose.prod.yml up -d

Navegue a su dominio nuevamente. Ya no debería ver una advertencia.

¡Felicitaciones! Ahora está utilizando un certificado Let's Encrypt de producción para su aplicación Django que se ejecuta en AWS EC2.

Si desea ver el proceso de creación de certificados en acción, consulte los registros:

$ docker-compose -f docker-compose.prod.yml logs nginx-proxy-letsencrypt

Conclusión

En este tutorial, implementó una aplicación Django en contenedor en EC2. La aplicación se ejecuta detrás de un proxy HTTPS Nginx con certificados Let's Encrypt SSL. También usó una instancia de RDS Postgres y almacenó imágenes de Docker en ECR.

Fuente:  https://testdriven.io

#django #aws #docker 

Ahebwe  Oscar

Ahebwe Oscar

1620185280

How model queries work in Django

How model queries work in Django

Welcome to my blog, hey everyone in this article we are going to be working with queries in Django so for any web app that you build your going to want to write a query so you can retrieve information from your database so in this article I’ll be showing you all the different ways that you can write queries and it should cover about 90% of the cases that you’ll have when you’re writing your code the other 10% depend on your specific use case you may have to get more complicated but for the most part what I cover in this article should be able to help you so let’s start with the model that I have I’ve already created it.

**Read More : **How to make Chatbot in Python.

Read More : Django Admin Full Customization step by step

let’s just get into this diagram that I made so in here:

django queries aboutDescribe each parameter in Django querset

we’re making a simple query for the myModel table so we want to pull out all the information in the database so we have this variable which is gonna hold a return value and we have our myModel models so this is simply the myModel model name so whatever you named your model just make sure you specify that and we’re gonna access the objects attribute once we get that object’s attribute we can simply use the all method and this will return all the information in the database so we’re gonna start with all and then we will go into getting single items filtering that data and go to our command prompt.

Here and we’ll actually start making our queries from here to do this let’s just go ahead and run** Python manage.py shell** and I am in my project file so make sure you’re in there when you start and what this does is it gives us an interactive shell to actually start working with our data so this is a lot like the Python shell but because we did manage.py it allows us to do things a Django way and actually query our database now open up the command prompt and let’s go ahead and start making our first queries.

#django #django model queries #django orm #django queries #django query #model django query #model query #query with django

Como Implantar Um Aplicativo Django No Heroku Com O Docker

Este artigo analisa como implantar um aplicativo Django no Heroku com o Docker por meio do Heroku Container Runtime.

Objetivos

Ao final deste tutorial, você será capaz de:

  1. Explique por que você pode querer usar o Container Runtime do Heroku para executar um aplicativo
  2. Dockerize um aplicativo Django
  3. Implante e execute um aplicativo Django em um contêiner do Docker no Heroku
  4. Configure o GitLab CI para implantar imagens do Docker no Heroku
  5. Gerencie ativos estáticos com WhiteNoise
  6. Configure o Postgres para rodar no Heroku
  7. Criar um Dockerfile de produção que usa compilações do Docker de vários estágios
  8. Use o Heroku Container Registry e o Build Manifest para implantar o Docker no Heroku

Tempo de execução do contêiner Heroku

Juntamente com as implantações tradicionais do compilador Git plus slug ( git push heroku master), o Heroku também oferece suporte a implantações baseadas em Docker, com o Heroku Container Runtime.

Implantações baseadas em Docker

As implantações baseadas em Docker têm muitas vantagens sobre a abordagem tradicional:

  1. Sem limites de slug : o Heroku permite um tamanho máximo de slug de 500 MB para as implantações tradicionais baseadas em Git. As implantações baseadas em Docker, por outro lado, não têm esse limite.
  2. Controle total sobre o sistema operacional : em vez de ser limitado pelos pacotes instalados pelos buildpacks do Heroku , você tem controle total sobre o sistema operacional e pode instalar qualquer pacote que desejar com o Docker.
  3. Paridade dev/prod mais forte : compilações baseadas em Docker têm paridade mais forte entre desenvolvimento e produção, pois os ambientes subjacentes são os mesmos.
  4. Menos dependência do fornecedor : Por fim, o Docker facilita muito a mudança para um provedor de hospedagem em nuvem diferente, como AWS ou GCP.

Em geral, as implantações baseadas em Docker oferecem maior flexibilidade e controle sobre o ambiente de implantação. Você pode implantar os aplicativos desejados no ambiente desejado. Dito isso, agora você é responsável pelas atualizações de segurança. Com as implantações tradicionais baseadas em Git, o Heroku é responsável por isso. Eles aplicam atualizações de segurança relevantes em seus Stacks e migram seu aplicativo para os novos Stacks conforme necessário. Mantenha isso em mente.

Atualmente, existem duas maneiras de implantar aplicativos com o Docker no Heroku:

  1. Container Registry : implante imagens pré-criadas do Docker no Heroku
  2. Build Manifest : dado um Dockerfile, o Heroku compila e implanta a imagem do Docker

A principal diferença entre esses dois é que com a última abordagem -- por exemplo, através do Build Manifest -- você tem acesso aos recursos Pipelines , Review e Release . Portanto, se você estiver convertendo um aplicativo de uma implantação baseada em Git para o Docker e estiver usando qualquer um desses recursos, deverá usar a abordagem Build Manifest.

Fique tranquilo, veremos as duas abordagens neste artigo.

Em ambos os casos, você ainda terá acesso à CLI do Heroku, a todos os complementos poderosos e ao painel . Todos esses recursos funcionam com o Container Runtime, em outras palavras.

Tipo de implantaçãoMecanismo de implantaçãoAtualizações de segurança (quem trata)Acesso a pipelines, revisão, liberaçãoAcesso à CLI, complementos e painelLimites de tamanho do slug
Compilador Git + SlugGit PushHerokuSimSimSim
Docker + tempo de execução do contêinerDocker PushVocêNãoSimNão
Docker + Build ManifestGit PushVocêSimSimNão

Tenha em mente que as implantações baseadas em Docker são limitadas às mesmas restrições que as implantações baseadas em Git. Por exemplo, volumes persistentes não são suportados, pois o sistema de arquivos é efêmero e os processos da Web suportam apenas solicitações HTTP(S). Para saber mais sobre isso, revise os comandos do Dockerfile e o tempo de execução .

Conceitos do Docker vs Heroku

Janela de encaixeHeroku
DockerfileBuildPack
Imagemlesma
RecipienteDinâmico

Configuração do projeto

Crie um diretório de projeto, crie e ative um novo ambiente virtual e instale o Django:

$ mkdir django-heroku-docker
$ cd django-heroku-docker

$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django==3.2.9

Sinta-se à vontade para trocar virtualenv e Pip por Poetry ou Pipenv . Para saber mais, revise Ambientes Python Modernos .

Em seguida, crie um novo projeto Django, aplique as migrações e execute o servidor:

(env)$ django-admin startproject hello_django .
(env)$ python manage.py migrate
(env)$ python manage.py runserver

Navegue até http://localhost:8000/ para ver a tela de boas-vindas do Django. Mate o servidor e saia do ambiente virtual assim que terminar.

Janela de encaixe

Adicione um Dockerfile à raiz do projeto:

# pull official base image
FROM python:3.10-alpine

# set work directory
WORKDIR /app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBUG 0

# install psycopg2
RUN apk update \
    && apk add --virtual build-essential gcc python3-dev musl-dev \
    && apk add postgresql-dev \
    && pip install psycopg2

# install dependencies
COPY ./requirements.txt .
RUN pip install -r requirements.txt

# copy project
COPY . .

# add and run as non-root user
RUN adduser -D myuser
USER myuser

# run gunicorn
CMD gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

Aqui, começamos com uma imagem Docker baseada em Alpine para Python 3.10. Em seguida, definimos um diretório de trabalho junto com duas variáveis ​​de ambiente:

  1. PYTHONDONTWRITEBYTECODE: Impede que o Python grave arquivos pyc no disco
  2. PYTHONUNBUFFERED: Impede o Python de armazenar em buffer stdout e stderr

Em seguida, instalamos dependências no nível do sistema e pacotes Python, copiamos os arquivos do projeto, criamos e mudamos para um usuário não root (o que é recomendado pelo Heroku ) e usamos o CMD para executar o Gunicorn quando um contêiner é executado em tempo de execução. Anote a $PORTvariável. Essencialmente, qualquer servidor da Web executado no Container Runtime deve escutar o tráfego HTTP na $PORTvariável de ambiente, que é definida pelo Heroku no runtime .

Crie um arquivo requirements.txt :

Django==3.2.9
gunicorn==20.1.0

Em seguida, adicione um arquivo .dockerignore :

__pycache__
*.pyc
env/
db.sqlite3

Atualize as variáveis SECRET_KEY​​, DEBUGe em settings.py :ALLOWED_HOSTS

SECRET_KEY = os.environ.get('SECRET_KEY', default='foo')

DEBUG = int(os.environ.get('DEBUG', default=0))

ALLOWED_HOSTS = ['localhost', '127.0.0.1']

Não esqueça da importação:

import os

Para testar localmente, crie a imagem e execute o contêiner, certificando-se de passar as variáveis ​​de ambiente apropriadas:

$ docker build -t web:latest .
$ docker run -d --name django-heroku -e "PORT=8765" -e "DEBUG=1" -p 8007:8765 web:latest

Certifique-se de que o aplicativo esteja sendo executado em http://localhost:8007/ em seu navegador. Pare e remova o contêiner em execução assim que terminar:

$ docker stop django-heroku
$ docker rm django-heroku

Adicione um .gitignore :

__pycache__
*.pyc
env/
db.sqlite3

Em seguida, vamos criar uma visualização rápida do Django para testar facilmente o aplicativo quando o modo de depuração estiver desativado.

Adicione um arquivo views.py ao diretório "hello_django" :

from django.http import JsonResponse


def ping(request):
    data = {'ping': 'pong!'}
    return JsonResponse(data)

Em seguida, atualize urls.py :

from django.contrib import admin
from django.urls import path

from .views import ping


urlpatterns = [
    path('admin/', admin.site.urls),
    path('ping/', ping, name="ping"),
]

Teste isso novamente com o modo de depuração desativado:

$ docker build -t web:latest .
$ docker run -d --name django-heroku -e "PORT=8765" -e "DEBUG=0" -p 8007:8765 web:latest

Verifique se http://localhost:8007/ping/ funciona conforme o esperado:

{
  "ping": "pong!"
}

Pare e remova o contêiner em execução assim que terminar:

$ docker stop django-heroku
$ docker rm django-heroku

Ruído branco

Se você quiser usar o WhiteNoise para gerenciar seus ativos estáticos, primeiro adicione o pacote ao arquivo requirements.txt :

Django==3.2.9
gunicorn==20.1.0
whitenoise==5.3.0

Atualize o middleware em settings.py assim:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # new
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Em seguida, configure o manuseio de seus arquivos estáticos com STATIC_ROOT:

STATIC_ROOT = BASE_DIR / 'staticfiles'

Por fim, adicione suporte a compactação e armazenamento em cache:

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

Adicione o collectstaticcomando ao Dockerfile:

# pull official base image
FROM python:3.10-alpine

# set work directory
WORKDIR /app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBUG 0

# install psycopg2
RUN apk update \
    && apk add --virtual build-essential gcc python3-dev musl-dev \
    && apk add postgresql-dev \
    && pip install psycopg2

# install dependencies
COPY ./requirements.txt .
RUN pip install -r requirements.txt

# copy project
COPY . .

# collect static files
RUN python manage.py collectstatic --noinput

# add and run as non-root user
RUN adduser -D myuser
USER myuser

# run gunicorn
CMD gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

Para testar, crie a nova imagem e ative um novo contêiner:

$ docker build -t web:latest .
$ docker run -d --name django-heroku -e "PORT=8765" -e "DEBUG=1" -p 8007:8765 web:latest

Você deve conseguir visualizar os arquivos estáticos ao executar:

$ docker exec django-heroku ls /app/staticfiles
$ docker exec django-heroku ls /app/staticfiles/admin

Pare e remova o contêiner em execução novamente:

$ docker stop django-heroku
$ docker rm django-heroku

Postgres

Para colocar o Postgres em funcionamento, usaremos o pacote dj_database_url para gerar o dicionário de configuração de banco de dados adequado para as configurações do Django com base em uma DATABASE_URLvariável de ambiente.

Adicione a dependência ao arquivo de requisitos:

Django==3.2.9
dj-database-url==0.5.0
gunicorn==20.1.0
whitenoise==5.3.0

Em seguida, faça as seguintes alterações nas configurações para atualizar a configuração do banco de dados, se DATABASE_URLestiver presente:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

DATABASE_URL = os.environ.get('DATABASE_URL')
db_from_env = dj_database_url.config(default=DATABASE_URL, conn_max_age=500, ssl_require=True)
DATABASES['default'].update(db_from_env)

Portanto, se o DATABASE_URLnão estiver presente, o SQLite ainda será usado.

Adicione a importação ao topo também:

import dj_database_url

Testaremos isso daqui a pouco depois de criarmos um banco de dados Postgres no Heroku.

Configuração do Heroku

Inscreva-se na conta Heroku (se você ainda não tiver uma) e instale a CLI do Heroku (se ainda não tiver feito isso).

Crie um novo aplicativo:

$ heroku create
Creating app... done, ⬢ limitless-atoll-51647
https://limitless-atoll-51647.herokuapp.com/ | https://git.heroku.com/limitless-atoll-51647.git

Adicione a SECRET_KEYvariável de ambiente:

$ heroku config:set SECRET_KEY=SOME_SECRET_VALUE -a limitless-atoll-51647

Altere SOME_SECRET_VALUEpara uma string gerada aleatoriamente com pelo menos 50 caracteres.

Adicione o URL do Heroku acima à lista de ALLOWED_HOSTSem hello_django/settings.py assim:

ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'limitless-atoll-51647.herokuapp.com']

Certifique-se de substituir limitless-atoll-51647cada um dos comandos acima pelo nome do seu aplicativo.

Implantação do Heroku Docker

Neste ponto, estamos prontos para começar a implantar imagens do Docker no Heroku. Você decidiu qual abordagem gostaria de adotar?

  1. Container Registry : implante imagens pré-criadas do Docker no Heroku
  2. Build Manifest : dado um Dockerfile, o Heroku compila e implanta a imagem do Docker

Não tem certeza? Experimente os dois!

Abordagem nº 1: Container Registry

Pule esta seção se estiver usando a abordagem Build Manifest.

Novamente, com essa abordagem, você pode implantar imagens do Docker pré-criadas no Heroku.

Faça login no Heroku Container Registry , para indicar ao Heroku que queremos usar o Container Runtime:

$ heroku container:login

Reconstrua a imagem do Docker e marque-a com o seguinte formato:

registry.heroku.com/<app>/<process-type>

Certifique-se de substituir <app>pelo nome do aplicativo Heroku que você acabou de criar e <process-type>com webo qual isso será para um processo da web .

Por exemplo:

$ docker build -t registry.heroku.com/limitless-atoll-51647/web .

Envie a imagem para o registro:

$ docker push registry.heroku.com/limitless-atoll-51647/web

Solte a imagem:

$ heroku container:release -a limitless-atoll-51647 web

Isso executará o contêiner. Você deve conseguir visualizar o aplicativo em https://APP_NAME.herokuapp.com . Deve retornar um 404.

Tente executar heroku open -a limitless-atoll-51647para abrir o aplicativo em seu navegador padrão.

Verifique se https://APP_NAME.herokuapp.com/ping também funciona:

{
  "ping": "pong!"
}

Você também deve poder visualizar os arquivos estáticos:

$ heroku run ls /app/staticfiles -a limitless-atoll-51647
$ heroku run ls /app/staticfiles/admin -a limitless-atoll-51647

Certifique-se de substituir limitless-atoll-51647cada um dos comandos acima pelo nome do seu aplicativo.

Pule para a seção "Teste Postgres" uma vez feito.

Abordagem nº 2: Construir Manifesto

Ignore esta seção se estiver usando a abordagem do Container Registry.

Novamente, com a abordagem Build Manifest , você pode fazer com que o Heroku crie e implante imagens do Docker com base em um arquivo de manifesto heroku.yml .

Defina a pilha do seu aplicativo como contêiner:

$ heroku stack:set container -a limitless-atoll-51647

Adicione um arquivo heroku.yml à raiz do projeto:

build:
  docker:
    web: Dockerfile

Aqui, estamos apenas dizendo ao Heroku qual Dockerfile usar para construir a imagem.

Junto com build, você também pode definir as seguintes etapas:

  • setupé usado para definir complementos e variáveis ​​de configuração do Heroku a serem criados durante o provisionamento do aplicativo.
  • releaseé usado para definir tarefas que você gostaria de executar durante uma versão.
  • runé usado para definir quais comandos executar para os processos da Web e do trabalhador.

Certifique-se de revisar a documentação do Heroku para saber mais sobre esses quatro estágios.

Vale a pena notar que o gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORTcomando pode ser removido do Dockerfile e adicionado ao arquivo heroku.ymlrun no estágio:

build:
  docker:
    web: Dockerfile
run:
  web: gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

Além disso, certifique-se de colocar o comando 'collectstatic' dentro do seu Dockerfile. Não o mova para o releasepalco. Para saber mais sobre isso, revise esta pergunta do Stack Overflow .

Em seguida, instale o heroku-manifestplug-in do canal CLI beta:

$ heroku update beta
$ heroku plugins:install @heroku-cli/plugin-manifest

Com isso, inicialize um repositório Git e crie um commit.

Em seguida, adicione o controle remoto Heroku:

$ heroku git:remote -a limitless-atoll-51647

Envie o código para o Heroku para construir a imagem e executar o contêiner:

$ git push heroku master

Você deve conseguir visualizar o aplicativo em https://APP_NAME.herokuapp.com . Deve retornar um 404.

Tente executar heroku open -a limitless-atoll-51647para abrir o aplicativo em seu navegador padrão.

Verifique se https://APP_NAME.herokuapp.com/ping também funciona:

{
  "ping": "pong!"
}

Você também deve poder visualizar os arquivos estáticos:

$ heroku run ls /app/staticfiles -a limitless-atoll-51647
$ heroku run ls /app/staticfiles/admin -a limitless-atoll-51647

Certifique-se de substituir limitless-atoll-51647cada um dos comandos acima pelo nome do seu aplicativo.

Teste Postgres

Crie o banco de dados:

$ heroku addons:create heroku-postgresql:hobby-dev -a limitless-atoll-51647

Este comando define automaticamente a DATABASE_URLvariável de ambiente para o contêiner.

Quando o banco de dados estiver ativo, execute as migrações:

$ heroku run python manage.py makemigrations -a limitless-atoll-51647
$ heroku run python manage.py migrate -a limitless-atoll-51647

Em seguida, entre no psql para visualizar as tabelas recém-criadas:

$ heroku pg:psql -a limitless-atoll-51647

# \dt
                      List of relations
 Schema |            Name            | Type  |     Owner
--------+----------------------------+-------+----------------
 public | auth_group                 | table | siodzhzzcvnwwp
 public | auth_group_permissions     | table | siodzhzzcvnwwp
 public | auth_permission            | table | siodzhzzcvnwwp
 public | auth_user                  | table | siodzhzzcvnwwp
 public | auth_user_groups           | table | siodzhzzcvnwwp
 public | auth_user_user_permissions | table | siodzhzzcvnwwp
 public | django_admin_log           | table | siodzhzzcvnwwp
 public | django_content_type        | table | siodzhzzcvnwwp
 public | django_migrations          | table | siodzhzzcvnwwp
 public | django_session             | table | siodzhzzcvnwwp
(10 rows)

# \q

Novamente, certifique-se de substituir limitless-atoll-51647cada um dos comandos acima pelo nome do seu aplicativo Heroku.

GitLab CI

Inscreva-se para uma conta do GitLab (se necessário) e crie um novo projeto (novamente, se necessário).

Recupere seu token de autenticação Heroku :

$ heroku auth:token

Em seguida, salve o token como uma nova variável chamada HEROKU_AUTH_TOKENnas configurações de CI/CD do seu projeto: Configurações > CI/CD > Variáveis.

configuração do gitlab

Em seguida, precisamos adicionar um arquivo de configuração GitLab CI/CD chamado .gitlab-ci.yml à raiz do projeto. O conteúdo deste arquivo irá variar de acordo com a abordagem utilizada.

Abordagem nº 1: Container Registry

Pule esta seção se estiver usando a abordagem Build Manifest.

.gitlab-ci.yml :

image: docker:stable
services:
  - docker:dind

variables:
  DOCKER_DRIVER: overlay2
  HEROKU_APP_NAME: <APP_NAME>
  HEROKU_REGISTRY_IMAGE: registry.heroku.com/${HEROKU_APP_NAME}/web

stages:
  - build_and_deploy

build_and_deploy:
  stage: build_and_deploy
  script:
    - apk add --no-cache curl
    - docker login -u _ -p $HEROKU_AUTH_TOKEN registry.heroku.com
    - docker pull $HEROKU_REGISTRY_IMAGE || true
    - docker build
      --cache-from $HEROKU_REGISTRY_IMAGE
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $HEROKU_REGISTRY_IMAGE
    - chmod +x ./release.sh
    - ./release.sh

release.sh :

#!/bin/sh


IMAGE_ID=$(docker inspect ${HEROKU_REGISTRY_IMAGE} --format={{.Id}})
PAYLOAD='{"updates": [{"type": "web", "docker_image": "'"$IMAGE_ID"'"}]}'

curl -n -X PATCH https://api.heroku.com/apps/$HEROKU_APP_NAME/formation \
  -d "${PAYLOAD}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/vnd.heroku+json; version=3.docker-releases" \
  -H "Authorization: Bearer ${HEROKU_AUTH_TOKEN}"

Aqui, definimos uma única build_and_deploy etapa onde:

  1. Instalar cURL
  2. Faça login no Heroku Container Registry
  3. Puxe a imagem enviada anteriormente (se existir)
  4. Crie e marque a nova imagem
  5. Empurre a imagem para o registro
  6. Crie uma nova versão por meio da API Heroku usando o ID da imagem no script release.sh

Certifique-se de substituir <APP_NAME>pelo nome do seu aplicativo Heroku.

Com isso, inicialize um repositório Git, confirme, adicione o controle remoto do GitLab e envie seu código para o GitLab para acionar um novo pipeline . Isso executará o build_and_deployestágio como um único trabalho. Depois de concluído, uma nova versão deve ser criada automaticamente no Heroku.

Abordagem nº 2: Construir Manifesto

Ignore esta seção se estiver usando a abordagem do Container Registry.

.gitlab-ci.yml :

variables:
  HEROKU_APP_NAME: <APP_NAME>

stages:
  - deploy

deploy:
  stage: deploy
  script:
    - apt-get update -qy
    - apt-get install -y ruby-dev
    - gem install dpl
    - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_AUTH_TOKEN

Aqui, definimos uma única deploy etapa onde:

  1. Instale Ruby junto com uma gem chamada dpl
  2. Implante o código no Heroku com dpl

Certifique-se de substituir <APP_NAME>pelo nome do seu aplicativo Heroku.

Confirme, adicione o controle remoto do GitLab e envie seu código para o GitLab para acionar um novo pipeline . Isso executará o deployestágio como um único trabalho. Depois de concluído, o código deve ser implantado no Heroku.

IC avançado

Em vez de apenas construir a imagem do Docker e criar uma versão no GitLab CI, vamos também executar os testes do Django, Flake8 , Black e isort .

Novamente, isso irá variar dependendo da abordagem que você usou.

Abordagem nº 1: Container Registry

Pule esta seção se estiver usando a abordagem Build Manifest.

Atualize .gitlab-ci.yml assim:

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:latest || true
    - docker build
      --cache-from $IMAGE:latest
      --tag $IMAGE:latest
      --file ./Dockerfile
      "."
    - docker push $IMAGE:latest

test:
  stage: test
  image: $IMAGE:latest
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
    HEROKU_APP_NAME: <APP_NAME>
    HEROKU_REGISTRY_IMAGE: registry.heroku.com/${HEROKU_APP_NAME}/web
  script:
    - apk add --no-cache curl
    - docker login -u _ -p $HEROKU_AUTH_TOKEN registry.heroku.com
    - docker pull $HEROKU_REGISTRY_IMAGE || true
    - docker build
      --cache-from $HEROKU_REGISTRY_IMAGE
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $HEROKU_REGISTRY_IMAGE
    - chmod +x ./release.sh
    - ./release.sh

Certifique-se de substituir <APP_NAME>pelo nome do seu aplicativo Heroku.

Então, agora temos três estágios: build, test, e deploy.

No buildpalco, nós:

  1. Faça login no GitLab Container Registry
  2. Puxe a imagem enviada anteriormente (se existir)
  3. Crie e marque a nova imagem
  4. Envie a imagem para o GitLab Container Registry

Então, no testestágio nós configuramos o Postgres , configuramos a DATABASE_URLvariável de ambiente, e então rodamos os testes do Django, Flake8, Black, e isort usando a imagem que foi construída no estágio anterior.

No deploypalco, nós:

  1. Instalar cURL
  2. Faça login no Heroku Container Registry
  3. Puxe a imagem enviada anteriormente (se existir)
  4. Crie e marque a nova imagem
  5. Empurre a imagem para o registro
  6. Crie uma nova versão por meio da API Heroku usando o ID da imagem no script release.sh

Adicione as novas dependências ao arquivo de requisitos:

# prod
Django==3.2.9
dj-database-url==0.5.0
gunicorn==20.1.0
whitenoise==5.3.0

# dev and test
black==21.11b1
flake8==4.0.1
isort==5.10.1

Antes de enviar para o GitLab, execute os testes do Django localmente:

$ source env/bin/activate
(env)$ pip install -r requirements.txt
(env)$ python manage.py test

System check identified no issues (0 silenced).

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Certifique-se de que o Flake8 seja aprovado e atualize o código-fonte com base nas recomendações Black e isort:

(env)$ flake8 hello_django --max-line-length=100
(env)$ black hello_django
(env)$ isort hello_django --profile black

Confirme e envie seu código novamente. Certifique-se de que todos os estágios passem.

Abordagem nº 2: Construir Manifesto

Ignore esta seção se estiver usando a abordagem do Container Registry.

Atualize .gitlab-ci.yml assim:

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:latest || true
    - docker build
      --cache-from $IMAGE:latest
      --tag $IMAGE:latest
      --file ./Dockerfile
      "."
    - docker push $IMAGE:latest

test:
  stage: test
  image: $IMAGE:latest
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  variables:
    HEROKU_APP_NAME: <APP_NAME>
  script:
    - apt-get update -qy
    - apt-get install -y ruby-dev
    - gem install dpl
    - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_AUTH_TOKEN

Certifique-se de substituir <APP_NAME>pelo nome do seu aplicativo Heroku.

Então, agora temos três estágios: build, test, e deploy.

No buildpalco, nós:

  1. Faça login no GitLab Container Registry
  2. Puxe a imagem enviada anteriormente (se existir)
  3. Crie e marque a nova imagem
  4. Envie a imagem para o GitLab Container Registry

Então, no testestágio nós configuramos o Postgres , configuramos a DATABASE_URLvariável de ambiente, e então rodamos os testes do Django, Flake8, Black, e isort usando a imagem que foi construída no estágio anterior.

No deploypalco, nós:

  1. Instale Ruby junto com uma gem chamada dpl
  2. Implante o código no Heroku com dpl

Adicione as novas dependências ao arquivo de requisitos:

# prod
Django==3.2.9
dj-database-url==0.5.0
gunicorn==20.1.0
whitenoise==5.3.0

# dev and test
black==21.11b1
flake8==4.0.1
isort==5.10.1

Antes de enviar para o GitLab, execute os testes do Django localmente:

$ source env/bin/activate
(env)$ pip install -r requirements.txt
(env)$ python manage.py test

System check identified no issues (0 silenced).

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Certifique-se de que o Flake8 seja aprovado e atualize o código-fonte com base nas recomendações Black e isort:

(env)$ flake8 hello_django --max-line-length=100
(env)$ black hello_django
(env)$ isort hello_django --profile black

Confirme e envie seu código novamente. Certifique-se de que todos os estágios passem.

Compilação do Docker em vários estágios

Por fim, atualize o Dockerfile dessa forma para usar uma compilação de vários estágios para reduzir o tamanho final da imagem:

FROM python:3.10-alpine AS build-python
RUN apk update && apk add --virtual build-essential gcc python3-dev musl-dev postgresql-dev
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY ./requirements.txt .
RUN pip install -r requirements.txt

FROM python:3.10-alpine
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBUG 0
ENV PATH="/opt/venv/bin:$PATH"
COPY --from=build-python /opt/venv /opt/venv
RUN apk update && apk add --virtual build-deps gcc python3-dev musl-dev postgresql-dev
RUN pip install psycopg2-binary
WORKDIR /app
COPY . .
RUN python manage.py collectstatic --noinput
RUN adduser -D myuser
USER myuser
CMD gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT

Em seguida, precisamos atualizar a configuração do GitLab para aproveitar o cache de camada do Docker.

Abordagem nº 1: Container Registry

Pule esta seção se estiver usando a abordagem Build Manifest.

.gitlab-ci.yml :

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}
  HEROKU_APP_NAME: <APP_NAME>
  HEROKU_REGISTRY_IMAGE: registry.heroku.com/${HEROKU_APP_NAME}/web

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:build-python || true
    - docker pull $IMAGE:production || true
    - docker build
      --target build-python
      --cache-from $IMAGE:build-python
      --tag $IMAGE:build-python
      --file ./Dockerfile
      "."
    - docker build
      --cache-from $IMAGE:production
      --tag $IMAGE:production
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $IMAGE:build-python
    - docker push $IMAGE:production

test:
  stage: test
  image: $IMAGE:production
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - apk add --no-cache curl
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:build-python || true
    - docker pull $IMAGE:production || true
    - docker build
      --target build-python
      --cache-from $IMAGE:build-python
      --tag $IMAGE:build-python
      --file ./Dockerfile
      "."
    - docker build
      --cache-from $IMAGE:production
      --tag $IMAGE:production
      --tag $HEROKU_REGISTRY_IMAGE
      --file ./Dockerfile
      "."
    - docker push $IMAGE:build-python
    - docker push $IMAGE:production
    - docker login -u _ -p $HEROKU_AUTH_TOKEN registry.heroku.com
    - docker push $HEROKU_REGISTRY_IMAGE
    - chmod +x ./release.sh
    - ./release.sh

Certifique-se de substituir <APP_NAME>pelo nome do seu aplicativo Heroku.

Revise as alterações por conta própria. Em seguida, teste-o uma última vez.

Para saber mais sobre esse padrão de cache, revise a seção "Multi-stage" do artigo Faster CI Builds with Docker Cache .

Abordagem nº 2: Construir Manifesto

Ignore esta seção se estiver usando a abordagem do Container Registry.

.gitlab-ci.yml :

stages:
  - build
  - test
  - deploy

variables:
  IMAGE: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}
  HEROKU_APP_NAME: <APP_NAME>

build:
  stage: build
  image: docker:stable
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $IMAGE:build-python || true
    - docker pull $IMAGE:production || true
    - docker build
      --target build-python
      --cache-from $IMAGE:build-python
      --tag $IMAGE:build-python
      --file ./Dockerfile
      "."
    - docker build
      --cache-from $IMAGE:production
      --tag $IMAGE:production
      --file ./Dockerfile
      "."
    - docker push $IMAGE:build-python
    - docker push $IMAGE:production

test:
  stage: test
  image: $IMAGE:production
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: runner
    POSTGRES_PASSWORD: ""
    DATABASE_URL: postgresql://runner@postgres:5432/test
  script:
    - python manage.py test
    - flake8 hello_django --max-line-length=100
    - black hello_django --check
    - isort hello_django --check --profile black

deploy:
  stage: deploy
  script:
    - apt-get update -qy
    - apt-get install -y ruby-dev
    - gem install dpl
    - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_AUTH_TOKEN

Certifique-se de substituir <APP_NAME>pelo nome do seu aplicativo Heroku.

Revise as alterações por conta própria. Em seguida, teste-o uma última vez.

Para saber mais sobre esse padrão de cache, revise a seção "Multi-stage" do artigo Faster CI Builds with Docker Cache .

Conclusão

Neste artigo, percorremos duas abordagens para implantar um aplicativo Django no Heroku com o Docker -- o Container Registry e o Build Manifest.

Então, quando você deve pensar em usar o Heroku Container Runtime sobre o tradicional compilador Git e slug para implantações?

Quando você precisar de mais controle sobre o ambiente de implementação de produção.

Exemplos:

  1. Seu aplicativo e dependências excedem o limite máximo de slug de 500 MB.
  2. Seu aplicativo requer pacotes não instalados pelos buildpacks regulares do Heroku.
  3. Você deseja maior garantia de que seu aplicativo se comportará da mesma forma no desenvolvimento e na produção.
  4. Você realmente gosta de trabalhar com o Docker.

Fonte:  https://testdrive.io

#django #heroku #docker