Como Implantar Um Aplicativo Django No Heroku Com O Docker

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

Objetivos

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

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

Tempo de execução do contêiner Heroku

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

Implantações baseadas em Docker

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

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

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

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

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

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

Fique tranquilo, veremos as duas abordagens neste artigo.

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

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

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

Conceitos do Docker vs Heroku

Janela de encaixeHeroku
DockerfileBuildPack
Imagemlesma
RecipienteDinâmico

Configuração do projeto

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

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

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

(env)$ pip install django==3.2.9

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

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

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

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

Janela de encaixe

Adicione um Dockerfile à raiz do projeto:

# pull official base image
FROM python:3.10-alpine

# set work directory
WORKDIR /app

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

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

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

# copy project
COPY . .

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

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

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

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

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

Crie um arquivo requirements.txt :

Django==3.2.9
gunicorn==20.1.0

Em seguida, adicione um arquivo .dockerignore :

__pycache__
*.pyc
env/
db.sqlite3

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

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

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

ALLOWED_HOSTS = ['localhost', '127.0.0.1']

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

import os

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

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

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

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

Adicione um .gitignore :

__pycache__
*.pyc
env/
db.sqlite3

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

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

from django.http import JsonResponse


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

Em seguida, atualize urls.py :

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

from .views import ping


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

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

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

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

{
  "ping": "pong!"
}

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

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

Ruído branco

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

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

Atualize o middleware em settings.py assim:

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

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

STATIC_ROOT = BASE_DIR / 'staticfiles'

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

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

Adicione o collectstaticcomando ao Dockerfile:

# pull official base image
FROM python:3.10-alpine

# set work directory
WORKDIR /app

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

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

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

# copy project
COPY . .

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

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

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

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

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

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

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

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

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

Postgres

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

Adicione a dependência ao arquivo de requisitos:

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

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

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

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

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

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

import dj_database_url

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

Configuração do Heroku

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

Crie um novo aplicativo:

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

Adicione a SECRET_KEYvariável de ambiente:

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

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

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

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

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

Implantação do Heroku Docker

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

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

Não tem certeza? Experimente os dois!

Abordagem nº 1: Container Registry

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

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

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

$ heroku container:login

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

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

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

Por exemplo:

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

Envie a imagem para o registro:

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

Solte a imagem:

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

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

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

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

{
  "ping": "pong!"
}

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

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

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

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

Abordagem nº 2: Construir Manifesto

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

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

Defina a pilha do seu aplicativo como contêiner:

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

Adicione um arquivo heroku.yml à raiz do projeto:

build:
  docker:
    web: Dockerfile

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

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

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

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

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

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

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

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

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

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

Em seguida, adicione o controle remoto Heroku:

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

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

$ git push heroku master

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

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

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

{
  "ping": "pong!"
}

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

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

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

Teste Postgres

Crie o banco de dados:

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

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

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

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

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

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

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

# \q

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

GitLab CI

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

Recupere seu token de autenticação Heroku :

$ heroku auth:token

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

configuração do gitlab

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

Abordagem nº 1: Container Registry

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

.gitlab-ci.yml :

image: docker:stable
services:
  - docker:dind

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

stages:
  - build_and_deploy

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

release.sh :

#!/bin/sh


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

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

Aqui, definimos uma única build_and_deploy etapa onde:

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

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

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

Abordagem nº 2: Construir Manifesto

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

.gitlab-ci.yml :

variables:
  HEROKU_APP_NAME: <APP_NAME>

stages:
  - deploy

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

Aqui, definimos uma única deploy etapa onde:

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

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

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

IC avançado

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

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

Abordagem nº 1: Container Registry

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

Atualize .gitlab-ci.yml assim:

stages:
  - build
  - test
  - deploy

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

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

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

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

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

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

No buildpalco, nós:

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

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

No deploypalco, nós:

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

Adicione as novas dependências ao arquivo de requisitos:

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

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

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

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

System check identified no issues (0 silenced).

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

OK

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

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

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

Abordagem nº 2: Construir Manifesto

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

Atualize .gitlab-ci.yml assim:

stages:
  - build
  - test
  - deploy

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

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

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

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

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

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

No buildpalco, nós:

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

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

No deploypalco, nós:

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

Adicione as novas dependências ao arquivo de requisitos:

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

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

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

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

System check identified no issues (0 silenced).

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

OK

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

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

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

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

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

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

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

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

Abordagem nº 1: Container Registry

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

.gitlab-ci.yml :

stages:
  - build
  - test
  - deploy

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

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

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

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

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

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

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

Abordagem nº 2: Construir Manifesto

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

.gitlab-ci.yml :

stages:
  - build
  - test
  - deploy

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

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

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

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

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

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

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

Conclusão

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

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

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

Exemplos:

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

Fonte:  https://testdrive.io

#django #heroku #docker 

What is GEEK

Buddha Community

Como Implantar Um Aplicativo Django No Heroku Com O Docker

Como Implantar Um Aplicativo Django No Heroku Com O Docker

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

Objetivos

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

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

Tempo de execução do contêiner Heroku

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

Implantações baseadas em Docker

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

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

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

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

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

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

Fique tranquilo, veremos as duas abordagens neste artigo.

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

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

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

Conceitos do Docker vs Heroku

Janela de encaixeHeroku
DockerfileBuildPack
Imagemlesma
RecipienteDinâmico

Configuração do projeto

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

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

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

(env)$ pip install django==3.2.9

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

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

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

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

Janela de encaixe

Adicione um Dockerfile à raiz do projeto:

# pull official base image
FROM python:3.10-alpine

# set work directory
WORKDIR /app

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

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

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

# copy project
COPY . .

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

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

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

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

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

Crie um arquivo requirements.txt :

Django==3.2.9
gunicorn==20.1.0

Em seguida, adicione um arquivo .dockerignore :

__pycache__
*.pyc
env/
db.sqlite3

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

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

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

ALLOWED_HOSTS = ['localhost', '127.0.0.1']

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

import os

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

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

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

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

Adicione um .gitignore :

__pycache__
*.pyc
env/
db.sqlite3

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

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

from django.http import JsonResponse


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

Em seguida, atualize urls.py :

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

from .views import ping


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

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

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

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

{
  "ping": "pong!"
}

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

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

Ruído branco

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

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

Atualize o middleware em settings.py assim:

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

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

STATIC_ROOT = BASE_DIR / 'staticfiles'

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

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

Adicione o collectstaticcomando ao Dockerfile:

# pull official base image
FROM python:3.10-alpine

# set work directory
WORKDIR /app

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

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

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

# copy project
COPY . .

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

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

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

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

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

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

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

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

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

Postgres

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

Adicione a dependência ao arquivo de requisitos:

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

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

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

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

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

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

import dj_database_url

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

Configuração do Heroku

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

Crie um novo aplicativo:

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

Adicione a SECRET_KEYvariável de ambiente:

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

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

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

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

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

Implantação do Heroku Docker

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

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

Não tem certeza? Experimente os dois!

Abordagem nº 1: Container Registry

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

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

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

$ heroku container:login

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

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

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

Por exemplo:

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

Envie a imagem para o registro:

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

Solte a imagem:

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

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

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

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

{
  "ping": "pong!"
}

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

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

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

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

Abordagem nº 2: Construir Manifesto

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

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

Defina a pilha do seu aplicativo como contêiner:

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

Adicione um arquivo heroku.yml à raiz do projeto:

build:
  docker:
    web: Dockerfile

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

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

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

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

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

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

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

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

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

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

Em seguida, adicione o controle remoto Heroku:

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

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

$ git push heroku master

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

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

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

{
  "ping": "pong!"
}

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

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

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

Teste Postgres

Crie o banco de dados:

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

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

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

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

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

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

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

# \q

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

GitLab CI

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

Recupere seu token de autenticação Heroku :

$ heroku auth:token

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

configuração do gitlab

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

Abordagem nº 1: Container Registry

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

.gitlab-ci.yml :

image: docker:stable
services:
  - docker:dind

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

stages:
  - build_and_deploy

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

release.sh :

#!/bin/sh


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

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

Aqui, definimos uma única build_and_deploy etapa onde:

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

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

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

Abordagem nº 2: Construir Manifesto

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

.gitlab-ci.yml :

variables:
  HEROKU_APP_NAME: <APP_NAME>

stages:
  - deploy

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

Aqui, definimos uma única deploy etapa onde:

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

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

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

IC avançado

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

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

Abordagem nº 1: Container Registry

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

Atualize .gitlab-ci.yml assim:

stages:
  - build
  - test
  - deploy

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

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

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

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

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

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

No buildpalco, nós:

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

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

No deploypalco, nós:

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

Adicione as novas dependências ao arquivo de requisitos:

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

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

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

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

System check identified no issues (0 silenced).

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

OK

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

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

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

Abordagem nº 2: Construir Manifesto

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

Atualize .gitlab-ci.yml assim:

stages:
  - build
  - test
  - deploy

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

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

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

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

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

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

No buildpalco, nós:

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

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

No deploypalco, nós:

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

Adicione as novas dependências ao arquivo de requisitos:

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

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

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

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

System check identified no issues (0 silenced).

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

OK

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

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

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

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

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

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

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

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

Abordagem nº 1: Container Registry

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

.gitlab-ci.yml :

stages:
  - build
  - test
  - deploy

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

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

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

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

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

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

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

Abordagem nº 2: Construir Manifesto

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

.gitlab-ci.yml :

stages:
  - build
  - test
  - deploy

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

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

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

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

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

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

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

Conclusão

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

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

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

Exemplos:

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

Fonte:  https://testdrive.io

#django #heroku #docker 

Ahebwe  Oscar

Ahebwe Oscar

1620177818

Django admin full Customization step by step

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

Database

Custom Titles of Django Admin

Exclude in Django Admin

Fields in Django Admin

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

Neil  Morgan

Neil Morgan

1660749420

Como Implantar Um Aplicativo Django No AWS EC2 Com O Docker

Neste tutorial, implantaremos um aplicativo Django no AWS EC2 com o Docker. O aplicativo será executado por trás de um proxy HTTPS Nginx que usa certificados Let's Encrypt SSL. Usaremos o AWS RDS para servir nosso banco de dados Postgres junto com o AWS ECR para armazenar e gerenciar nossas imagens do Docker.

Objetivos

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

  1. Configurar uma nova instância do EC2
  2. Instalar o Docker em uma instância do EC2
  3. Configurar e usar um endereço IP elástico
  4. Configurar uma função do IAM
  5. Utilize o registro de imagem do Amazon Elastic Container Registry (ECR) para armazenar imagens criadas
  6. Configurar o AWS RDS para persistência de dados
  7. Configurar um grupo de segurança da AWS
  8. Implante o Django no AWS EC2 com o Docker
  9. Execute o aplicativo Django por trás de um proxy HTTPS Nginx com certificados SSL Let's Encrypt

Pré-requisitos

Este post se baseia nos posts Dockerizing Django com Postgres, Gunicorn e Nginx e Protegendo um Aplicativo Django Containerizado com Let's Encrypt .

Ele assume que você pode:

  1. Conteinerize um aplicativo Django junto com Postgres, Nginx e Gunicorn.
  2. Proteja um aplicativo Django em contêiner rodando atrás de um proxy HTTPS Nginx com certificados SSL Let's Encrypt .
  3. Use SSH para se conectar a um servidor remoto e SCP para copiar arquivos para o servidor.

AWS EC2

Primeiro, crie uma conta da AWS , caso ainda não tenha uma.

Em seguida, navegue até o console do EC2 e clique em Launch instance :

Início EC2

Use o Ubuntu Server 18.04 LTS (HVM) para a imagem do servidor (AMI):

Selecione AMI

Na próxima etapa, fique com a instância t2.micro . Clique em Avançar: Configurar detalhes da instância :

Tipo de instância EC2

Na etapa Configure Instance Details , deixe tudo como está para manter as coisas simples. Em seguida, clique em Avançar algumas vezes até chegar à etapa Configurar Grupo de Segurança .

Com Criar um novo grupo de segurança selecionado, defina o nome e a descrição django-ec2e adicione duas regras:

  • HTTP -> Em qualquer lugar
  • HTTPS -> Em qualquer lugar

EC2 configurar grupos de segurança

Essas regras são necessárias para emitir certificados e acessar o aplicativo.

As regras de entrada do grupo de segurança são usadas para limitar o acesso à sua instância pela Internet. A menos que você tenha alguns requisitos de segurança adicionais, provavelmente desejará permitir tráfego HTTP e HTTPS de qualquer lugar para instâncias que hospedam aplicativos da web. O SSH deve ser permitido para você se conectar à instância para configuração e implantação.

Clique em Revisar e Iniciar . Na próxima tela, clique em Iniciar .

Você será solicitado a selecionar um par de chaves. Você precisa dele para conexão SSH com sua instância. Selecione Criar um novo par de chaves e nomeie-o djangoletsencrypt. Em seguida, clique em Baixar par de chaves . Após o download do par de chaves, clique em Launch Instances :

EC2 Adicionar par de chaves

Levará alguns minutos para a instância girar.

Configurar instância do EC2

Nesta seção, instalaremos o Docker na instância, adicionaremos um IP elástico e configuraremos uma função do IAM.

Instalar o Docker

Navegue de volta ao console do EC2 , selecione a instância recém-criada e pegue o endereço IP público:

IP público EC2

Conecte-se à sua instância do EC2 usando a .pemchave que baixamos na etapa "AWS EC2".

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

Seu .pemprovavelmente foi baixado em um caminho como ~/Downloads/djangoletsencrypt.pem. Se você não tiver certeza de onde armazená-lo, mova-o para o diretório ~/.ssh . Você também pode ter que alterar as permissões -- ou seja, chmod 400 -i /path/to/your/djangoletsencrypt.pem.

Comece instalando a versão mais recente do Docker e a versão 1.29.2 do 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

Instalar AWS CLI

Primeiro, instale o descompacte:

$ sudo apt install unzip

Faça download do ZIP da AWS CLI:

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

Descompacte seu conteúdo:

$ unzip awscliv2.zip

Instale a AWS CLI:

$ sudo ./aws/install

Verifique a instalação:

$ aws --version

IP elástico

Por padrão, as instâncias recebem um novo endereço IP público sempre que são iniciadas e reiniciadas.

O IP elástico permite alocar IPs estáticos para suas instâncias do EC2, para que o IP permaneça o mesmo o tempo todo e possa ser reassociado entre as instâncias. É recomendável usar um para sua configuração de produção.

Navegue até Elastic IPs e clique em Allocate Elastic IP address :

IP elástico

Em seguida, clique em Alocar :

Alocação de IP elástico

Clique em Associar este endereço IP elástico :

Associado de IP elástico

Selecione sua instância e clique em Associar :

Instância de seleção de IP elástico

Função do IAM

Usaremos o AWS ECR para extrair imagens do AWS ECR para nossa instância do EC2 durante a implantação. Como não permitiremos acesso público à imagem do Docker no ECR, você precisará criar uma função do IAM com permissões para extrair imagens do Docker do ECR e anexá-las à sua instância do EC2.

Navegue até o console do IAM .

Clique em Funções na barra lateral esquerda e depois em Criar função :

Funções do IAM

Selecione AWS Service e EC2 e clique em Next: Permissions :

Caso de uso de seleção de IAM

Digite containerna caixa de pesquisa, selecione a política AmazonEC2ContainerRegistryPowerUser e clique em Next: Tags :

Política de função do IAM

Clique em Avançar: Revisar . Use django-ec2para o nome e clique em Criar função :

Revisão do papel do IAM

Agora você precisa anexar a nova função à sua instância do EC2.

De volta ao console do EC2 , clique em Instâncias e selecione sua instância. Clique no menu suspenso Actions -> Instance settings -> Attach/Replace IAM Role :

EC2 Anexar Função do IAM

Selecione a função django-ec2 e clique em Aplicar .

EC2 Selecione a função do IAM

Clique em Fechar .

Adicionar registro DNS

Adicione um registro A ao seu DNS, para o domínio que você está usando, para apontar para o IP público da sua instância do EC2.

É o IP elástico que você associou à sua instância.

AWS ECR

O Amazon Elastic Container Registry (ECR) é um registro de imagem totalmente gerenciado do Docker que facilita o armazenamento e o gerenciamento de imagens para os desenvolvedores. Para imagens privadas, o acesso é gerenciado por meio de usuários e funções do IAM.

Navegue até o console do ECR . Clique em Repositórios na barra lateral e depois em Criar repositório :

Repositórios ECR

Defina o nome django-ec2e clique em Criar repositório :

ECR Criar repositório

AWS RDS

Agora podemos configurar um banco de dados RDS Postgres.

Embora você possa executar seu próprio banco de dados Postgres em um contêiner, já que bancos de dados são serviços críticos , adicionar camadas adicionais, como o Docker, adiciona riscos desnecessários na produção. Para simplificar tarefas como atualizações de versões secundárias, backups regulares e dimensionamento, é recomendável usar um serviço gerenciado. Então, vamos usar RDS .

Navegue até o console do RDS . Clique em Criar banco de dados :

Página inicial do RDS

Selecione a versão mais recente do Postgres com o modelo de nível gratuito :

RDS Criar banco de dados

Em Configurações , defina:

  • Identificador da instância de banco de dados:djangoec2
  • Nome de usuário mestre:webapp
  • Selecione Gerar uma senha automaticamente

Fique com as configurações padrão para:

  • Tamanho da instância de banco de dados
  • Armazenar
  • Disponibilidade e durabilidade

Pule para a seção Conectividade e defina o seguinte:

  • Nuvem privada virtual (VPC): padrão
  • Grupo de sub-rede: padrão
  • Acessível ao público: Não
  • Grupo de segurança da VPC:django-ec2
  • Porta do banco de dados: 5432

RDS Criar conectividade de banco de dados

Deixe a autenticação do banco de dados como está.

Abra a configuração adicional e altere o nome do banco de dados inicial para djangoec2:

RDS Criar nome de banco de dados inicial do banco de dados

Deixe as outras configurações como estão.

Por fim, clique em Criar banco de dados .

Clique em Exibir detalhes das credenciais para ver a senha gerada para o usuário do webapp :

Credenciais do RDS View

Guarde esta senha em algum lugar seguro. Você precisará fornecê-lo ao aplicativo Django aqui em breve.

Levará alguns minutos para a instância girar. Uma vez ativado, clique no DB Identifier do banco de dados recém-criado para ver seus detalhes. Anote o endpoint do banco de dados; você precisará configurá-lo em seu aplicativo Django.

Detalhes do banco de dados RDS

Grupo de segurança da AWS

No console do EC2 , clique em Grupos de segurança na barra lateral. Encontre e clique no ID do grupo django-ec2 para editar seus detalhes.

Clique em Editar regras de entrada :

Detalhes do grupo de segurança do EC2

Adicione uma regra de entrada que permitirá conexões Postgres com instâncias dentro desse grupo de segurança. Fazer isso:

  • clique em Adicionar regra
  • selecione PostgreSQL para o tipo de regra
  • selecione Personalizado para a origem da regra
  • clique no campo de pesquisa e selecione o grupo de segurança django-ec2
  • clique em Salvar regras

Para limitar o acesso ao seu banco de dados, somente conexões de instâncias dentro do mesmo Security Group são permitidas. Nosso aplicativo pode se conectar porque definimos o mesmo grupo de segurança, django-ec2 , para as instâncias RDS e EC2. Instâncias dentro de outros grupos de segurança, portanto, não têm permissão para se conectar.

EC2 Editar regras de entrada

Configuração do projeto

Com a infraestrutura da AWS configurada, agora precisamos configurar nosso projeto Django localmente antes de implantá-lo.

Primeiro, clone o conteúdo do repositório do projeto GitHub:

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

Este repositório contém tudo o que você precisa para implantar um Django Dockerizado com certificados Let's Encrypt HTTPS.

Composição do Docker

Quando o aplicativo for implantado pela primeira vez, você deve seguir estas duas etapas para evitar problemas com certificados:

  1. Comece emitindo os certificados do ambiente de teste do Let's Encrypt
  2. Então, quando tudo estiver funcionando como esperado, mude para o ambiente de produção do Let's Encrypt

Você pode ler mais sobre as limitações do Let's Encrypt em ambientes de produção na seção Let's Encrypt do post anterior, Protegendo um aplicativo Django em contêiner com o Let's Encrypt .

Para testar, atualize o docker-compose.staging.yml. arquivo assim:

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:

Para os serviços webe nginx-proxy, atualize as imagepropriedades para usar imagens do ECR (que adicionaremos em breve).

Exemplos:

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

Os valores consistem na URL do repositório ( 123456789.dkr.ecr.us-east-1.amazonaws.com) junto com o nome da imagem ( django-ec2) e as tags ( webe nginx-proxy).

Para simplificar, estamos usando um único registro para armazenar as duas imagens. Usamos o webe nginx-proxypara diferenciar entre os dois. Idealmente, você deve usar dois registros: um para webe outro para nginx-proxy. Atualize isso por conta própria, se desejar.

Além das imagepropriedades, também removemos o dbserviço (e o volume relacionado), pois estamos usando o RDS em vez de gerenciar o Postgres em um contêiner.

Ambientes

É hora de configurar os arquivos de ambiente para os contêineres webe nginx-proxy-letsencrypt.

Primeiro, adicione um arquivo .env.staging para o webcontêiner:

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>

Notas:

  1. Mude <YOUR_DOMAIN.COM>para o seu domínio real.
  2. Altere SQL_PASSWORDe SQL_HOSTpara corresponder aos criados na seção RDS.
  3. Mude SECRET_KEYpara alguma string aleatória longa.
  4. O VIRTUAL_HOSTe VIRTUAL_PORTsão necessários pelo nginx-proxycontêiner para criar automaticamente a configuração do proxy reverso.
  5. LETSENCRYPT_HOSTexiste para que nginx-proxy-companionpossa emitir o certificado Let's Encrypt para o seu domínio.

Para fins de teste/depuração, você pode usar a *pela DJANGO_ALLOWED_HOSTSprimeira vez para simplificar as coisas. Só não se esqueça de limitar os hosts permitidos assim que o teste for concluído.

Em segundo lugar, adicione um arquivo .env.staging.proxy-companion , certificando-se de atualizar o DEFAULT_EMAILvalor:

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

Criar e enviar imagens do Docker

Agora estamos prontos para construir as imagens do Docker:

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

Pode levar alguns minutos para construir. Uma vez feito, estamos prontos para enviar as imagens para o ECR.

Primeiro, supondo que você tenha o awscli instalado e que tenha definido suas credenciais da AWS , faça login no repositório 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
# aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com

Você deveria ver:

Login Succeeded

Em seguida, envie as imagens para o ECR:

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

Abra seu repositório ECR django-ec2 para ver as imagens enviadas:

Imagens ECR

Executando os contêineres

Tudo está configurado para implantação.

É hora de migrar para sua instância do EC2.

Supondo que você tenha um diretório de projeto criado em sua instância, como /home/ubuntu/django-on-docker , copie os arquivos e pastas com o 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

Em seguida, conecte-se à sua instância via SSH e vá para o diretório do projeto:

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

Faça login no repositório 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

Puxe as imagens:

$ 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

Com isso, você está pronto para ativar os contêineres:

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

Quando os contêineres estiverem funcionando, navegue até seu domínio no navegador. Você deve ver algo como:

Certificado HTTPS não seguro

Isso é esperado. Essa tela é exibida porque o certificado foi emitido de um ambiente de teste .

Como saber se tudo funciona?

Clique em "Avançado" e depois em "Continuar". Agora você deve ver seu aplicativo. Carregue uma imagem e, em seguida, certifique-se de que pode visualizar a imagem em https://yourdomain.com/media/IMAGE_FILE_NAME.

Emitir o Certificado de Produção

Agora que tudo funciona como esperado, podemos passar para o ambiente de produção do Let's Encrypt.

Desative os contêineres existentes e saia da sua instância:

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

De volta à sua máquina local, abra docker-compose.prod.yml e faça as mesmas alterações que você fez para a versão de teste:

  1. Atualize as ìmagepropriedades para corresponder aos URLs do AWS ECR para os serviços ẁebenginx-proxy
  2. Remova o dbserviço junto com o volume relacionado
version: '3.8'

services:
  web:
    build:
      context: ./app
      dockerfile: Dockerfile.prod
    image: 046505967931.dkr.ecr.us-east-1.amazonaws.com/django-ec2:web
    command: gunicorn hello_django.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - static_volume:/home/app/web/staticfiles
      - media_volume:/home/app/web/mediafiles
    expose:
      - 8000
    env_file:
      - ./.env.prod
  nginx-proxy:
    container_name: nginx-proxy
    build: nginx
    image: 046505967931.dkr.ecr.us-east-1.amazonaws.com/django-ec2:nginx-proxy
    restart: always
    ports:
      - 443:443
      - 80:80
    volumes:
      - static_volume:/home/app/web/staticfiles
      - media_volume:/home/app/web/mediafiles
      - certs:/etc/nginx/certs
      - html:/usr/share/nginx/html
      - vhost:/etc/nginx/vhost.d
      - /var/run/docker.sock:/tmp/docker.sock:ro
    depends_on:
      - web
  nginx-proxy-letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    env_file:
      - ./.env.prod.proxy-companion
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - certs:/etc/nginx/certs
      - html:/usr/share/nginx/html
      - vhost:/etc/nginx/vhost.d
      - acme:/etc/acme.sh
    depends_on:
      - nginx-proxy

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

Em seguida, crie um arquivo .env.prod duplicando o arquivo .env.staging . Você não precisa fazer nenhuma alteração nele.

Por fim, adicione um arquivo .env.prod.proxy-companion :

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

Crie e envie imagens novamente:

$ 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 os novos arquivos e pastas para sua instância com 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, conecte-se à sua instância via SSH e vá para o diretório do projeto:

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

Faça login no seu repositório ECR Docker novamente:

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

Puxe as imagens:

$ 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

E, finalmente, gire os contêineres:

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

Navegue até o seu domínio novamente. Você não deve mais ver um aviso.

Parabéns! Agora você está usando um certificado Let's Encrypt de produção para seu aplicativo Django em execução no AWS EC2.

Quer ver o processo de criação do certificado em ação, confira os logs:

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

Conclusão

Neste tutorial, você implantou um aplicativo Django em contêiner no EC2. O aplicativo está sendo executado por trás de um proxy HTTPS Nginx com certificados Let's Encrypt SSL. Você também usou uma instância do RDS Postgres e armazenou imagens do Docker no ECR.

Fonte:  https://testdrive.io

#django #aws #docker 

Ahebwe  Oscar

Ahebwe Oscar

1620185280

How model queries work in Django

How model queries work in Django

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

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

Read More : Django Admin Full Customization step by step

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

django queries aboutDescribe each parameter in Django querset

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

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

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

Trystan  Doyle

Trystan Doyle

1598610840

Como escalar e proteger um aplicativo Django com o Docker, Nginx e Let's Encrypt

Em ambientes baseados em nuvem, existem várias maneiras de dimensionar e proteger um aplicativo Django. Ao escalar horizontalmente e executar várias cópias de seu aplicativo, você pode construir um sistema mais tolerante e altamente

#docker #nginx #django