1660381860
Este artículo analiza cómo implementar una aplicación Django en Heroku con Docker a través de Heroku Container Runtime.
Al final de este tutorial, podrá:
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.
Las implementaciones basadas en Docker tienen muchas ventajas sobre el enfoque tradicional:
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:
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ón | Mecanismo de despliegue | Actualizaciones de seguridad (quién las maneja) | Acceso a Pipelines, Revisión, Liberación | Acceso a CLI, complementos y panel | Límites de tamaño de slug |
---|---|---|---|---|---|
Compilador Git + Slug | Empuje Git | Heroku | Sí | Sí | Sí |
Tiempo de ejecución de Docker + contenedor | Empuje de la ventana acoplable | Tú | No | Sí | No |
Docker + Manifiesto de compilación | Empuje Git | Tú | Sí | Sí | No |
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 | Heroku |
---|---|
Dockerfile | BuildPack |
Image | Slug |
Container | Dyno |
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.
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:
PYTHONDONTWRITEBYTECODE
: evita que Python escriba archivos pyc en el discoPYTHONUNBUFFERED
: evita que Python almacene en búfer stdout y stderrA 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 $PORT
variable. Esencialmente, cualquier servidor web que se ejecute en Container Runtime debe escuchar el tráfico HTTP en la $PORT
variable 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
, DEBUG
y 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
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 collectstatic
comando 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
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_URL
variable 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_URL
está 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_URL
no 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.
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_KEY
variable de entorno:
$ heroku config:set SECRET_KEY=SOME_SECRET_VALUE -a limitless-atoll-51647
Cambie
SOME_SECRET_VALUE
a una cadena generada aleatoriamente que tenga al menos 50 caracteres.
Agregue la URL de Heroku anterior a la lista ALLOWED_HOSTS
en hello_django/settings.py así:
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'limitless-atoll-51647.herokuapp.com']
Asegúrese de reemplazar
limitless-atoll-51647
cada uno de los comandos anteriores con el nombre de su aplicación.
En este punto, estamos listos para comenzar a implementar imágenes de Docker en Heroku. ¿Decidiste qué enfoque te gustaría tomar?
¿Inseguro? ¡Pruébalos a los dos!
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 web
será 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-51647
para 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-51647
cada 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.
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:
setup
se 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 therun
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.
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.
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_TOKEN
dentro de la configuración de CI/CD de su proyecto: Configuración > CI/CD > Variables.
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.
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:
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.
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:
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 deploy
escenario como un solo trabajo. Una vez completado, el código debe implementarse en Heroku.
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.
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
, test
y deploy
.
En el build
escenario, nosotros:
Luego, en la test
etapa, configuramos Postgres , configuramos la DATABASE_URL
variable 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 deploy
escenario, nosotros:
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.
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
, test
y deploy
.
En el build
escenario, nosotros:
Luego, en la test
etapa, configuramos Postgres , configuramos la DATABASE_URL
variable 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 deploy
escenario, nosotros:
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.
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.
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 .
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 .
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:
Fuente: https://testdriven.io
1660381860
Este artículo analiza cómo implementar una aplicación Django en Heroku con Docker a través de Heroku Container Runtime.
Al final de este tutorial, podrá:
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.
Las implementaciones basadas en Docker tienen muchas ventajas sobre el enfoque tradicional:
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:
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ón | Mecanismo de despliegue | Actualizaciones de seguridad (quién las maneja) | Acceso a Pipelines, Revisión, Liberación | Acceso a CLI, complementos y panel | Límites de tamaño de slug |
---|---|---|---|---|---|
Compilador Git + Slug | Empuje Git | Heroku | Sí | Sí | Sí |
Tiempo de ejecución de Docker + contenedor | Empuje de la ventana acoplable | Tú | No | Sí | No |
Docker + Manifiesto de compilación | Empuje Git | Tú | Sí | Sí | No |
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 | Heroku |
---|---|
Dockerfile | BuildPack |
Image | Slug |
Container | Dyno |
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.
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:
PYTHONDONTWRITEBYTECODE
: evita que Python escriba archivos pyc en el discoPYTHONUNBUFFERED
: evita que Python almacene en búfer stdout y stderrA 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 $PORT
variable. Esencialmente, cualquier servidor web que se ejecute en Container Runtime debe escuchar el tráfico HTTP en la $PORT
variable 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
, DEBUG
y 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
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 collectstatic
comando 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
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_URL
variable 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_URL
está 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_URL
no 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.
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_KEY
variable de entorno:
$ heroku config:set SECRET_KEY=SOME_SECRET_VALUE -a limitless-atoll-51647
Cambie
SOME_SECRET_VALUE
a una cadena generada aleatoriamente que tenga al menos 50 caracteres.
Agregue la URL de Heroku anterior a la lista ALLOWED_HOSTS
en hello_django/settings.py así:
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'limitless-atoll-51647.herokuapp.com']
Asegúrese de reemplazar
limitless-atoll-51647
cada uno de los comandos anteriores con el nombre de su aplicación.
En este punto, estamos listos para comenzar a implementar imágenes de Docker en Heroku. ¿Decidiste qué enfoque te gustaría tomar?
¿Inseguro? ¡Pruébalos a los dos!
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 web
será 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-51647
para 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-51647
cada 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.
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:
setup
se 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 therun
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.
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.
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_TOKEN
dentro de la configuración de CI/CD de su proyecto: Configuración > CI/CD > Variables.
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.
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:
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.
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:
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 deploy
escenario como un solo trabajo. Una vez completado, el código debe implementarse en Heroku.
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.
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
, test
y deploy
.
En el build
escenario, nosotros:
Luego, en la test
etapa, configuramos Postgres , configuramos la DATABASE_URL
variable 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 deploy
escenario, nosotros:
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.
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
, test
y deploy
.
En el build
escenario, nosotros:
Luego, en la test
etapa, configuramos Postgres , configuramos la DATABASE_URL
variable 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 deploy
escenario, nosotros:
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.
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.
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 .
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 .
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:
Fuente: https://testdriven.io
1620177818
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.
#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
1660756800
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.
Al final de este tutorial, podrá:
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:
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 :
Use Ubuntu Server 18.04 LTS (HVM) para la imagen del servidor (AMI):
In the next step, stick with the t2.micro instance. Click on Next: Configure Instance Details:
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:
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:
It will take a few minutes for the instance to spin up.
In this section, we'll install Docker on the instance, add an Elastic IP, and configure an IAM role.
Navigate back to the EC2 console, select the newly created instance, and grab the public IP address:
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
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
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:
Then, click Allocate:
Click on Associate this Elastic IP address:
Select your instance and click Associate:
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:
Select AWS Service and EC2, then click on Next: Permissions:
Enter container
in the search box, select the AmazonEC2ContainerRegistryPowerUser policy, and click Next: Tags:
Click Next: Review. Use django-ec2
for the name, and click on Create role:
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:
Select the django-ec2 role, and then click Apply.
Click Close.
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.
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:
Set the name to django-ec2
and click on Create repository:
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:
Select the latest version of Postgres with the Free tier template:
Under Settings, set:
djangoec2
webapp
Stick with default settings for:
Skip down to the Connectivity section and set the following:
django-ec2
Leave Database authentication as it is.
Open Additional configuration and change Initial database name to djangoec2
:
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:
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.
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:
Add an inbound rule that will allow Postgres connections to instances inside that Security Group. To do that:
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.
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.
When the app is deployed for the first time, you should follow these two steps to avoid issues with certificates:
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
andnginx-proxy
to differentiate between the two. Ideally, you should use two registries: one forweb
and one fornginx-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.
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:
<YOUR_DOMAIN.COM>
to your actual domain.SQL_PASSWORD
and SQL_HOST
to match those created in the RDS section.SECRET_KEY
to some long random string.VIRTUAL_HOST
and VIRTUAL_PORT
are needed by nginx-proxy
container to auto create the reverse proxy configuration.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
*
forDJANGO_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
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:
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:
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
.
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:
ìmage
propiedades para que coincidan con sus URL de AWS ECR para los servicios ẁeb
ynginx-proxy
db
servicio junto con el volumen relacionadoversion: '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
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
1620185280
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:
Describe 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
1660374600
Este artigo analisa como implantar um aplicativo Django no Heroku com o Docker por meio do Heroku Container Runtime.
Ao final deste tutorial, você será capaz de:
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.
As implantações baseadas em Docker têm muitas vantagens sobre a abordagem tradicional:
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:
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ção | Mecanismo de implantação | Atualizações de segurança (quem trata) | Acesso a pipelines, revisão, liberação | Acesso à CLI, complementos e painel | Limites de tamanho do slug |
---|---|---|---|---|---|
Compilador Git + Slug | Git Push | Heroku | Sim | Sim | Sim |
Docker + tempo de execução do contêiner | Docker Push | Você | Não | Sim | Não |
Docker + Build Manifest | Git Push | Você | Sim | Sim | Nã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 .
Janela de encaixe | Heroku |
---|---|
Dockerfile | BuildPack |
Imagem | lesma |
Recipiente | Dinâmico |
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.
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:
PYTHONDONTWRITEBYTECODE
: Impede que o Python grave arquivos pyc no discoPYTHONUNBUFFERED
: Impede o Python de armazenar em buffer stdout e stderrEm 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 $PORT
variável. Essencialmente, qualquer servidor da Web executado no Container Runtime deve escutar o tráfego HTTP na $PORT
variá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
, DEBUG
e 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
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 collectstatic
comando 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
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_URL
variá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_URL
estiver 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_URL
nã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.
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_KEY
variável de ambiente:
$ heroku config:set SECRET_KEY=SOME_SECRET_VALUE -a limitless-atoll-51647
Altere
SOME_SECRET_VALUE
para uma string gerada aleatoriamente com pelo menos 50 caracteres.
Adicione o URL do Heroku acima à lista de ALLOWED_HOSTS
em hello_django/settings.py assim:
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'limitless-atoll-51647.herokuapp.com']
Certifique-se de substituir
limitless-atoll-51647
cada um dos comandos acima pelo nome do seu aplicativo.
Neste ponto, estamos prontos para começar a implantar imagens do Docker no Heroku. Você decidiu qual abordagem gostaria de adotar?
Não tem certeza? Experimente os dois!
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 web
o 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-51647
para 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-51647
cada um dos comandos acima pelo nome do seu aplicativo.
Pule para a seção "Teste Postgres" uma vez feito.
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:$PORT
comando 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
release
palco. Para saber mais sobre isso, revise esta pergunta do Stack Overflow .
Em seguida, instale o heroku-manifest
plug-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-51647
para 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-51647
cada um dos comandos acima pelo nome do seu aplicativo.
Crie o banco de dados:
$ heroku addons:create heroku-postgresql:hobby-dev -a limitless-atoll-51647
Este comando define automaticamente a
DATABASE_URL
variá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-51647
cada um dos comandos acima pelo nome do seu aplicativo Heroku.
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_TOKEN
nas configurações de CI/CD do seu projeto: Configurações > CI/CD > Variáveis.
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.
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:
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_deploy
estágio como um único trabalho. Depois de concluído, uma nova versão deve ser criada automaticamente no Heroku.
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:
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 deploy
estágio como um único trabalho. Depois de concluído, o código deve ser implantado no Heroku.
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.
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 build
palco, nós:
Então, no test
estágio nós configuramos o Postgres , configuramos a DATABASE_URL
variá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 deploy
palco, nós:
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.
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 build
palco, nós:
Então, no test
estágio nós configuramos o Postgres , configuramos a DATABASE_URL
variá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 deploy
palco, nós:
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.
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.
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 .
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 .
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:
Fonte: https://testdrive.io