One place for hosting & domains

      Aplicativo

      Como usar o EJS para modelar seu aplicativo Node


      Introdução

      Ao criar aplicativos Node dinamicamente, uma maneira fácil e rápida de modelar seu aplicativo pode ser necessária.

      O Jade é o mecanismo de exibição do Express por padrão, mas a sintaxe do Jade pode ser complexa de mais para muitos casos de uso. O EJS é uma alternativa que cumpre essa função muito bem e é bastante fácil de ser configurado. Vamos dar uma olhada em como criar um aplicativo simples e usar o EJS para incluir partes repetíveis do nosso site (parciais) e passar dados para nossas exibições.

      Como configurar o aplicativo de demonstração

      Estaremos fazendo duas páginas para nosso aplicativo, sendo uma com largura total e a outra com uma barra lateral.

      Obtenha o código: é possível encontrar um repositório git com o código de demonstração completo no GitHub aqui

      Estrutura de arquivos

      Aqui estão os arquivos que serão necessários para o nosso aplicativo. Vamos fazer o modelamento dentro da pasta de exibições e o resto do processo segue o padrão para práticas com o Node.

      - views
      ----- partials
      ---------- footer.ejs
      ---------- head.ejs
      ---------- header.ejs
      ----- pages
      ---------- index.ejs
      ---------- about.ejs
      - package.json
      - server.js
      

      O package.json armazenará as informações do nosso aplicativo Node e as dependências necessárias (express e EJS). O server.js armazenará as configurações do nosso servidor Express. Vamos definir as rotas para nossas páginas aqui.

      Configuração do Node

      Vamos entrar em nosso arquivo package.json e configurar nosso projeto por lá.

      package.json

      {
        "name": "node-ejs",
        "main": "server.js",
        "dependencies": {
          "ejs": "^3.1.5",
          "express": "^4.17.1"
        }
      }
      

      Tudo o que precisamos é o Express e EJS. Agora, precisamos instalar as dependências que acabamos de definir. Vá em frente e execute:

      Com todas as nossas dependências instaladas, vamos configurar o nosso aplicativo para usar o EJS e definir nossas rotas para as duas páginas que precisamos: a página de índica (largura total) e a página “sobre” (barra lateral). Isso tudo será feito dentro do nosso arquivo server.js.

      server.js

      // load the things we need
      var express = require('express');
      var app = express();
      
      // set the view engine to ejs
      app.set('view engine', 'ejs');
      
      // use res.render to load up an ejs view file
      
      // index page
      app.get("https://www.digitalocean.com/", function(req, res) {
          res.render('pages/index');
      });
      
      // about page
      app.get('/about', function(req, res) {
          res.render('pages/about');
      });
      
      app.listen(8080);
      console.log('8080 is the magic port');
      

      Aqui definimos nosso aplicativo e configuramos ele para ser exibido na porta 8080. Também é necessário definir o EJS como o mecanismo de exibição para o nosso aplicativo Express usando app.set('view engine', 'ejs');. Observe como enviamos uma exibição para o usuário usando o res.render(). É importante notar que o res.render() irá procurar em uma pasta de exibições pela exibição. Dessa forma, só é preciso definir pages/index, já que o caminho completo é views/pages/index.

      Iniciar o nosso servidor

      Vá em frente e inicie o servidor usando:

      Agora, podemos ver nosso aplicativo em um navegador em http://localhost:8080 e http://localhost:8080/about. Nosso aplicativo está configurado e precisamos definir nossos arquivos de exibição e ver como o EJS funciona neles.

      Criar as parciais do EJS

      Assim como muitos outras aplicativos que compilamos, grande parte do código será reutilizado. Iremos chamá-los de parciais e definir três arquivos que vamos usar em todo o nosso site: head.ejs, header.ejs e footer.ejs. Vamos criar esses arquivos agora.

      views/partials/head.ejs

      <meta charset="UTF-8">
      <title>EJS Is Fun</title>
      
      <!-- CSS (load bootstrap from a CDN) -->
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.min.css">
      <style>
          body { padding-top:50px; }
      </style>
      

      views/partials/header.ejs

      <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="https://www.digitalocean.com/">EJS Is Fun</a>
        <ul class="navbar-nav mr-auto">
          <li class="nav-item">
            <a class="nav-link" href="https://www.digitalocean.com/">Home</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="http://www.digitalocean.com/about">About</a>
          </li>
        </ul>
      </nav>
      

      views/partials/footer.ejs

      <p class="text-center text-muted">© Copyright 2020 The Awesome People</p>
      

      Adicionar as parciais do EJS às exibições

      As nossas parciais já estão definidas. Tudo o que precisamos fazer é incluí-las em nossas exibições. Vamos entrar em index.ejs e about.ejs e usar a sintaxe include para adicionar as parciais.

      Sintaxe para incluir uma parcial do EJS

      Utilize <%- include('RELATIVE/PATH/TO/FILE') %> para incorporar um parcial do EJS em outro arquivo.

      • O hífen <%- é usado, ao invés de apenas <% para dizer ao EJS para renderizar HTML não processado.
      • O caminho para o parcial é relativo ao arquivo atual.

      views/pages/index.ejs

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <%- include('../partials/head'); %>
      </head>
      <body class="container">
      
      <header>
          <%- include('../partials/header'); %>
      </header>
      
      <main>
          <div class="jumbotron">
              <h1>This is great</h1>
              <p>Welcome to templating using EJS</p>
          </div>
      </main>
      
      <footer>
          <%- include('../partials/footer'); %>
      </footer>
      
      </body>
      </html>
      

      Agora, podemos observar nossa exibição definida no navegador em http://localhost:8080. node-ejs-templating-index

      Para a página “sobre”, também adicionamos uma barra lateral de inicialização para demonstrar como as parciais podem ser estruturadas para reuso em diferentes modelos e páginas.

      views/pages/about.ejs

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <%- include('../partials/head'); %>
      </head>
      <body class="container">
      
      <header>
          <%- include('../partials/header'); %>
      </header>
      
      <main>
      <div class="row">
          <div class="col-sm-8">
              <div class="jumbotron">
                  <h1>This is great</h1>
                  <p>Welcome to templating using EJS</p>
              </div>
          </div>
      
          <div class="col-sm-4">
              <div class="well">
                  <h3>Look I'm A Sidebar!</h3>
              </div>
          </div>
      
      </div>
      </main>
      
      <footer>
          <%- include('../partials/footer'); %>
      </footer>
      
      </body>
      </html>
      

      Se visitarmos http://localhost:8080/about, veremos nossa página “sobre” com uma barra lateral! node-ejs-templating-about

      Agora, podemos começar a usar o EJS para passar dados do nosso aplicativo Node para nossas exibições.

      Passar dados para exibições e parciais

      Vamos definir algumas variáveis básicas e uma lista para passar para nossa página inicial. Volte para seu arquivo server.js e adicione o código a seguir dentro da sua rota app.get("https://www.digitalocean.com/").

      server.js

      // index page
      app.get("https://www.digitalocean.com/", function(req, res) {
          var mascots = [
              { name: 'Sammy', organization: "DigitalOcean", birth_year: 2012},
              { name: 'Tux', organization: "Linux", birth_year: 1996},
              { name: 'Moby Dock', organization: "Docker", birth_year: 2013}
          ];
          var tagline = "No programming concept is complete without a cute animal mascot.";
      
          res.render('pages/index', {
              mascots: mascots,
              tagline: tagline
          });
      });
      

      Acabamos de criar uma lista chamada mascots e uma string simples chamada tagline. Vamos entrar em nosso arquivo index.ejs e usá-los.

      Renderizar uma única variável no EJS

      Para ecoar uma única variável, utilizamos apenas <%= tagline %>. Vamos adicionar isto em nosso arquivo index.ejs:

      views/pages/index.ejs

      ...
      <h2>Variable</h2>
      <p><%= tagline %></p>
      ...
      

      Executar um loop em dados no EJS

      Para executar um loop em nossos dados, vamos utilizar o .forEach. Vamos adicionar isto em nosso arquivo de exibição:

      views/pages/index.ejs

      ...
      <ul>
          <% mascots.forEach(function(mascot) { %>
              <li>
                  <strong><%= mascot.name %></strong>
                  representing <%= mascot.organization %>, born <%= mascot.birth_year %>
              </li>
          <% }); %>
      </ul>
      ...
      

      Agora, podemos ver em nosso navegador que as novas informações foram adicionadas!

      node-ejs-templating-rendered

      Passar dados para uma parcial no EJS

      A parcial do EJS tem acesso aos mesmos dados que a exibição pai. Mas tenha cuidado: caso esteja fazendo referência a uma variável em uma parcial, ela precisa estar definida em todas as exibições que usam a parcial ou ocorrerá um erro.

      Também é possível definir e passar variáveis a uma parcial do EJS na sintaxe include desta forma:

      views/pages/about.ejs

      ...
      <header>
          <%- include('../partials/header', {variant:'compact'}); %>
      </header>
      ...
      

      No entanto, é necessário novamente ter cuidado em assumir que uma variável já está definida.

      Se quiser fazer referência a uma variável em uma parcial que talvez nem sempre esteja definida e atribuir-lhe um valor padrão, faça isso desta forma:

      views/partials/header.ejs

      ...
      <em>Variant: <%= typeof variant != 'undefined' ? variant : 'default' %></em>
      ...
      

      Na linha acima, o código do EJS está renderizando o valor de variant caso esteja definido e default caso contrário.

      Conclusão

      O EJS permite a criação rápida de aplicativos quando não precisamos de nada muito complexo. Usando parciais e tendo a capacidade de passar facilmente variáveis às nossas exibições, somos capazes de criar aplicativos fantásticos rapidamente.

      Para outras referências sobre o EJS, consulte a documentação oficial aqui.



      Source link

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


      Introdução

      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 disponível, ao mesmo tempo em que também aumenta seu rendimento de modo que as solicitações possam ser processadas simultaneamente. Uma maneira de escalar horizontalmente um aplicativo Django é fornecendo servidores de aplicativos adicionais que executem seu aplicativo Django e seu servidor HTTP WSGI (como o Gunicorn ou o uWSGI). Para encaminhar e distribuir pedidos recebidos neste conjunto de servidores de aplicativos, você pode usar um balanceador de carga e um proxy reverso como o Nginx. O Nginx também é capaz de colocar em cache conteúdo estático e encerrar as conexões via protocolo TLS, usadas para providenciar o HTTPS e conexões seguras ao seu aplicativo.

      Executar seu aplicativo Django e o proxy Nginx dentro dos contêineres Docker garante que esses componentes se comportem da mesma maneira, independentemente do ambiente em que estão implantados. Além disso, os contêineres proporcionam muitos recursos que facilitam o empacotamento e a configuração do seu aplicativo.

      Neste tutorial, você irá escalar horizontalmente um Django e um aplicativo Gunicorn Polls em contêiner fornecendo dois servidores de aplicativos que irão cada um executar uma cópia de um contêiner de aplicativos Django e Gunicorn.

      Você também habilitará o HTTPS fornecendo e configurando um terceiro servidor proxy que irá executar um contêiner de proxy reverso Nginx e um contêiner do cliente Certbot. O Certbot irá fornecer certificados TLS para o Nginx a partir da autoridade de certificação Let’s Encrypt. Isso irá garantir que seu site receba uma alta classificação de segurança do SSL Labs. Este servidor proxy receberá todos os pedidos externos do seu aplicativo e se colocará em frente aos dois servidores upstream do aplicativo Django. Por fim, você irá fortalecer esse sistema distribuído restringindo o acesso externo apenas ao servidor de proxy.

      Pré-requisitos

      Para seguir este tutorial, será necessário:

      • Três servidores Ubuntu 18.04:

        • Dois serão os servidores de aplicativo, usados para executar os aplicativos Django e Gunicorn.
        • Um servidor será um servidor proxy, usado para executar o Nginx e o Certbot.
        • Todos devem possuir um usuário não root com privilégios sudo e um firewall ativo. Para saber como configurar isso, consulte este guia de Configuração inicial do servidor.
      • Docker instalado em todos os três servidores. Como orientação na instalação do Docker, siga os Passos 1 e 2 de Como instalar e usar o Docker no Ubuntu 18.04.

      • Um nome de domínio registrado. Este tutorial utilizará o your_domain.com durante todo o processo. Você pode obter um domínio gratuitamente através do Freenom, ou usar o registrador de domínios de sua escolha.

      • Um registro de DNS de tipo A com o your_domain.com apontando para o endereço IP público do seu servidor proxy. Você pode seguir esta introdução para o DNS da DigitalOcean para obter mais detalhes sobre como adicioná-lo a uma conta da DigitalOcean, caso seja o que estiver usando:

      • Um bucket de armazenamento de objetos S3, como um espaço da DigitalOcean para armazenar os arquivos estáticos do seu projeto Django e um conjunto de chaves de acesso para esse espaço. Para aprender como criar um espaço, consulte a documentação de produto Como criar espaços. Para aprender como criar chaves de acesso para espaços, consulte Compartilhando acesso a espaços com chaves de acesso. Com pequenas alterações, você pode usar qualquer serviço de armazenamento de objetos que o plug-in django-storages suporte.

      • Uma instância de servidor PostgreSQL, banco de dados e usuário para seu aplicativo Django. Com pequenas alterações, você pode usar qualquer banco de dados compatível com o Django.

      Passo 1 — Configurando o primeiro servidor do aplicativo Django

      Para começar, vamos clonar o repositório do aplicativo Django no primeiro servidor de aplicativo. Em seguida, vamos configurar e compilar a imagem do aplicativo Docker e então testar o aplicativo executando o contêiner do Django.

      Nota: se estiver continuando a partir de Como construir um aplicativo Django e Gunicorn com o Docker, você já terá completado o Passo 1 e pode seguir direto ao Passo 2 para configurar o segundo servidor de aplicativo.

      Comece fazendo login no primeiro dos dois servidores do aplicativo Django e use o git para clonar a ramificação polls-docker a partir do repositório GitHub do aplicativo Polls de tutorial do Django. Este repositório contém o código para o aplicativo de amostra Polls da documentação do Django A ramificação polls-docker contém uma versão em Docker do aplicativo Polls. Para aprender como o aplicativo Polls foi modificado para funcionar efetivamente em um ambiente em contêiner, consulte Como construir um aplicativo Django e Gunicorn com o Docker.

      • git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git

      Navegue até o diretório django-polls:

      cd django-polls
      

      Esse diretório contém o código Python do aplicativo Django, um Dockerfile que o Docker usará para compilar a imagem do contêiner, bem como um arquivo env que contém uma lista de variáveis de ambiente a serem passadas para o ambiente de execução do contêiner. Inspecione o Dockerfile usando o cat:

      cat Dockerfile
      

      Output

      FROM python:3.7.4-alpine3.10 ADD django-polls/requirements.txt /app/requirements.txt RUN set -ex && apk add --no-cache --virtual .build-deps postgresql-dev build-base && python -m venv /env && /env/bin/pip install --upgrade pip && /env/bin/pip install --no-cache-dir -r /app/requirements.txt && runDeps="$(scanelf --needed --nobanner --recursive /env | awk '{ gsub(/,/, "nso:", $2); print "so:" $2 }' | sort -u | xargs -r apk info --installed | sort -u)" && apk add --virtual rundeps $runDeps && apk del .build-deps ADD django-polls /app WORKDIR /app ENV VIRTUAL_ENV /env ENV PATH /env/bin:$PATH EXPOSE 8000 CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi"]

      Esse Dockerfile usa a imagem Docker oficial do Python 3.7.4 como base e instala os requisitos de pacote Python do Django e do Gunicorn, conforme definido no arquivo django-polls/requirements.txt. Em seguida, ele remove alguns arquivos de compilação desnecessários, copia o código do aplicativo na imagem e define o PATH de execução. Por fim, ele declara que a porta 8000 será usada para aceitar conexões de contêiner recebidas e executa gunicorn com 3 trabalhadores, escutando na porta 8000.

      Para aprender mais sobre cada um dos passos nesse Dockerfile, confira o Passo 6 de Como construir um aplicativo Django e Gunicorn com o Docker.

      Agora, crie a imagem usando o docker build:

      Nós demos o nome de polls para a imagem usando o sinalizador -t e passamos o diretório atual como um contexto de compilação, que é o conjunto de arquivos de referência ao compilar a imagem.

      Depois que o Docker compilar e marcar a imagem, liste as imagens disponíveis usando docker images:

      docker images
      

      Você deve ver a imagem polls listada:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE polls latest 80ec4f33aae1 2 weeks ago 197MB python 3.7.4-alpine3.10 f309434dea3a 8 months ago 98.7MB

      Antes de executarmos o contêiner Django, precisamos configurar seu ambiente de execução usando o arquivo env presente no diretório atual. Esse arquivo será passado para o comando docker run usado para executar contêiner e o Docker irá injetar as variáveis de ambiente configuradas no ambiente de execução do contêiner.

      Abra o arquivo env com o nano ou com o seu editor favorito:

      nano env
      

      Vamos configurar o arquivo dessa forma, e você precisará adicionar alguns valores adicionais, conforme descrito abaixo.

      django-polls/env

      DJANGO_SECRET_KEY=
      DEBUG=True
      DJANGO_ALLOWED_HOSTS=
      DATABASE_ENGINE=postgresql_psycopg2
      DATABASE_NAME=polls
      DATABASE_USERNAME=
      DATABASE_PASSWORD=
      DATABASE_HOST=
      DATABASE_PORT=
      STATIC_ACCESS_KEY_ID=
      STATIC_SECRET_KEY=
      STATIC_BUCKET_NAME=
      STATIC_ENDPOINT_URL=
      DJANGO_LOGLEVEL=info
      

      Preencha os valores que estão faltando para as seguintes chaves:

      • DJANGO_SECRET_KEY: defina isso como um valor único e imprevisível, conforme detalhado na documentação do Django. Um método para gerar essa chave é fornecido em Ajustando as configurações do aplicativo do tutorial sobre o Aplicativo Django escalável.
      • DJANGO_ALLOWED_HOSTS: essa variável protege o aplicativo e impede ataques de cabeçalho de host HTTP. Para fins de teste, defina isso como *, um coringa que irá corresponder a todos os hosts. Na produção, você deve definir isso como your_domain.com. Para aprender mais sobre esse ajuste do Django, consulte as Core Settings da documentação do Django.
      • DATABASE_USERNAME: defina isso como o usuário do banco de dados PostgreSQL criado nos passos pré-requisitos.
      • DATABASE_NAME: defina isso como polls ou o nome do banco de dados PostgreSQL criado nos passos pré-requisitos.
      • DATABASE_PASSWORD: defina isso como a senha do usuário do banco de dados PostgreSQL criada nos passos pré-requisitos.
      • DATABASE_HOST: defina isso como o nome do host do seu banco de dados.
      • DATABASE_PORT: defina isso como a porta do seu banco de dados.
      • STATIC_ACCESS_KEY_ID: defina isso como a chave de acesso do seu bucket S3 ou espaço.
      • STATIC_SECRET_KEY: defina isso como o segredo da chave de acesso do seu bucket S3 ou espaço.
      • STATIC_BUCKET_NAME: defina isso como o nome do seu bucket S3 ou espaço.
      • STATIC_ENDPOINT_URL: defina isso como o URL do ponto de extremidade do bucket S3 ou espaço apropriado, como por exemplo https://space-name.nyc3.digitaloceanspaces.com se seu espaço estiver localizado na região nyc3.

      Assim que terminar a edição, salve e feche o arquivo.

      Agora, usaremos o docker run para substituir o conjunto CMD no Dockerfile e criar o esquema de banco de dados usando os comandos manage.py makemigrations e manage.py migrate:

      docker run --env-file env polls sh -c "python manage.py makemigrations && python manage.py migrate"
      

      Executamos a imagem de contêiner polls:latest, passamos o arquivo de variável de ambiente que acabamos de modificar e substituímos o comando do Dockerfile com sh -c "python manage.py makemigrations && python manage.py migrate", o que irá criar o esquema de banco de dados definido pelo código do aplicativo. Se estiver executando isso pela primeira vez, você deve ver:

      Output

      No changes detected Operations to perform: Apply all migrations: admin, auth, contenttypes, polls, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying polls.0001_initial... OK Applying sessions.0001_initial... OK

      Isso indica que o esquema de banco de dados foi criado com sucesso.

      Se estiver executando migrate uma outra vez, o Django irá cancelar a operação a menos que o esquema de banco de dados tenha sido alterado.

      Em seguida, vamos executar outra instância do contêiner de aplicativo e usar um shell interativo dentro dela para criar um usuário administrativo para o projeto Django.

      docker run -i -t --env-file env polls sh
      

      Isso lhe fornecerá um prompt do shell dentro do contêiner em execução que você pode usar para criar o usuário do Django:

      python manage.py createsuperuser
      

      Digite um nome de usuário, endereço de e-mail e senha para o seu usuário e, depois de criá-lo, pressione CTRL+D para sair do contêiner e encerrá-lo.

      Por fim, vamos gerar os arquivos estáticos para o aplicativo e fazer o upload deles para o espaço da DigitalOcean usando o collectstatic. Observe que esse processo pode demorar um pouco de tempo para ser concluído.

      docker run --env-file env polls sh -c "python manage.py collectstatic --noinput"
      

      Depois que esses arquivos forem gerados e enviados, você receberá a seguinte saída.

      Output

      121 static files copied.

      Agora, podemos executar o aplicativo:

      docker run --env-file env -p 80:8000 polls
      

      Output

      [2019-10-17 21:23:36 +0000] [1] [INFO] Starting gunicorn 19.9.0 [2019-10-17 21:23:36 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) [2019-10-17 21:23:36 +0000] [1] [INFO] Using worker: sync [2019-10-17 21:23:36 +0000] [7] [INFO] Booting worker with pid: 7 [2019-10-17 21:23:36 +0000] [8] [INFO] Booting worker with pid: 8 [2019-10-17 21:23:36 +0000] [9] [INFO] Booting worker with pid: 9

      Aqui, executamos o comando padrão definido no Dockerfile, gunicorn --bind :8000 --workers 3 mysite.wsgi:application e expomos a porta do contêiner 8000 para que a porta 80 no servidor Ubuntu seja mapeada para a porta 8000 do contêiner polls.

      Agora, você deve ser capaz de navegar até o aplicativo polls usando seu navegador Web digitando http://APP_SERVER_1_IP na barra de URL. Como não há nenhuma rota definida para o caminho /, você provavelmente receberá um erro 404 Page Not Found, o que é esperado.

      Aviso: quando se usa o firewall UFW com o Docker, o Docker ignora quaisquer regras configuradas do firewall UFW, conforme documentado neste problema do GitHub. Isso explica por que você tem acesso à porta 80 do seu servidor, mesmo que não tenha criado explicitamente uma regra de acesso no UFW em qualquer passo pré-requisito. No Passo 5, vamos tratar desse problema de segurança corrigindo a configuração do UFW. Se você não estiver usando o UFW e estiver usando os Firewalls em Nuvem da DigitalOcean, você pode ignorar com segurança esse aviso.

      Navegue até http://APP_SERVER_1_IP/polls para ver a interface do aplicativo Polls:

      Interface do aplicativo Polls

      Para visualizar a interface administrativa, visite http://APP_SERVER_1_IP/admin. Você deve ver a janela de autenticação do administrador do aplicativo Polls:

      Página de autenticação de administrador do Polls

      Digite o nome e a senha do usuário administrativo que você criou com o comando createsuperuser.

      Depois de autenticar-se, você pode acessar a interface administrativa do aplicativo Polls:

      Interface administrativa principal do Polls

      Observe que os ativos estáticos para os aplicativos admin e polls estão sendo entregues diretamente do armazenamento de objetos. Para confirmar isso, consulte Testando a entrega de arquivos estáticos de espaços.

      Quando terminar de explorar, aperte CTRL+C na janela do terminal executando o contêiner Docker para encerrar o contêiner.

      Agora que confirmou que o contêiner de aplicativo funciona como esperado, você pode executá-lo em modo separado. Isso irá executá-lo em segundo plano e lhe permitirá fazer logoff da sua sessão SSH:

      docker run -d --rm --name polls --env-file env -p 80:8000 polls
      

      O sinalizador -d instrui o Docker a executar o contêiner em modo separado, o sinalizador -rm limpa o sistema de arquivos do contêiner após a saída do contêiner e damos o nome de polls a ele.

      Faça logoff do primeiro servidor do aplicativo Django e navegue até http://APP_SERVER_1_IP/polls para confirmar se o contêiner está funcionando como esperado.

      Agora que seu primeiro servidor do aplicativo Django está em operação, você pode configurar seu segundo servidor do aplicativo Django.

      Passo 2 — Configurando o segundo servidor do aplicativo Django

      Como muitos dos comandos usados para configurar este servidor serão iguais àqueles do passo anterior, eles serão apresentados aqui de forma abreviada. Por favor, reveja o Passo 1 para obter mais informações sobre qualquer comando em particular neste passo.

      Comece fazendo login no segundo servidor do aplicativo Django.

      Clone a ramificação polls-docker do repositório GitHub django-polls:

      • git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git

      Navegue até o diretório django-polls:

      cd django-polls
      

      Compile a imagem usando o docker build:

      Abra o arquivo env com o nano ou com o seu editor favorito:

      nano env
      

      django-polls/env

      DJANGO_SECRET_KEY=
      DEBUG=True
      DJANGO_ALLOWED_HOSTS=
      DATABASE_ENGINE=postgresql_psycopg2
      DATABASE_NAME=polls
      DATABASE_USERNAME=
      DATABASE_PASSWORD=
      DATABASE_HOST=
      DATABASE_PORT=
      STATIC_ACCESS_KEY_ID=
      STATIC_SECRET_KEY=
      STATIC_BUCKET_NAME=
      STATIC_ENDPOINT_URL=
      DJANGO_LOGLEVEL=info
      

      Preencha os valores que estão faltando como no Passo 1. Quando terminar a edição, salve e feche o arquivo.

      Por fim, execute o contêiner do aplicativo em modo separado:

      docker run -d --rm --name polls --env-file env -p 80:8000 polls
      

      Navegue até http://APP_SERVER_2_IP/polls para confirmar se o contêiner está funcionando como esperado. Você pode fazer logoff com segurança do segundo servidor de aplicativo sem precisar encerrar seu contêiner em execução.

      Com ambos os contêineres do aplicativo Django em operação, você pode seguir para a configuração do contêiner do proxy reverso do Nginx.

      Passo 3 — Configurando o contêiner Docker do Nginx

      O Nginx é um servidor Web versátil que oferece vários recursos, incluindo proxy reverso, balanceamento de carga e cache. Neste tutorial, nós descarregamos os ativos estáticos do Django para um armazenamento de objetos, então não usaremos as capacidades de cache do Nginx. No entanto, usaremos o Nginx como um proxy reverso para nossos dois servidores de backend do aplicativo Django e distribuiremos pedidos recebidos entre eles. Além disso, o Nginx irá realizar a terminação TLS e redirecionamento utilizando um certificado TLS fornecido pelo Certbot. Isso significa que ele irá forçar os clientes a usar o HTTPS, redirecionando solicitações HTTP recebidas para a porta 443. Em seguida, irá descriptografar as solicitações HTTPS e enviá-las via proxy para os servidores upstream do Django.

      Neste tutorial, tomamos a decisão de projeto de dissociar os contêineres do Nginx dos servidores de backend. Dependendo do seu caso de uso, você pode optar por executar o contêiner do Nginx em um dos servidores do aplicativo Django, fazendo o proxy de pedidos localmente, bem como para o outro servidor Django. Outra arquitetura possível seria executar dois contêineres do Nginx, um em cada servidor de backend, com um balanceador de carga em nuvem no frontend. Cada arquitetura apresenta diferentes vantagens de segurança e desempenho, e você deve fazer o teste de carga no seu sistema para descobrir gargalos. A arquitetura flexível descrita neste tutorial permite que você escale tanto a camada backend do aplicativo Django, quanto a camada de proxy do Nginx. Assim que o contêiner do Nginx único se tornar um gargalo, você pode escalar o processo para vários proxies do Nginx e adicionar um balanceador de carga em nuvem ou um balanceador de carga L4 rápido como o HAProxy.

      Com ambos os servidores do aplicativo Django em operação, podemos começar a configurar o servidor de proxy do Nginx. Faça login em seu servidor de proxy e crie um diretório chamado conf:

      mkdir conf
      

      Crie um arquivo de configuração chamado nginx.conf usando o nano ou seu editor favorito:

      nano conf/nginx.conf
      

      Cole nele a seguinte configuração do Nginx:

      conf/nginx.conf

      
      upstream django {
          server APP_SERVER_1_IP;
          server APP_SERVER_2_IP;
      }
      
      server {
          listen 80 default_server;
          return 444;
      }
      
      server {
          listen 80;
          listen [::]:80;
          server_name your_domain.com;
          return 301 https://$server_name$request_uri;
      }
      
      server {
          listen 443 ssl http2;
          listen [::]:443 ssl http2;
          server_name your_domain.com;
      
          # SSL
          ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
          ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
      
          ssl_session_cache shared:le_nginx_SSL:10m;
          ssl_session_timeout 1440m;
          ssl_session_tickets off;
      
          ssl_protocols TLSv1.2 TLSv1.3;
          ssl_prefer_server_ciphers off;
      
          ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
      
          client_max_body_size 4G;
          keepalive_timeout 5;
      
              location / {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                proxy_pass http://django;
              }
      
          location ^~ /.well-known/acme-challenge/ {
              root /var/www/html;
          }
      
      }
      

      Esses blocos upstream, server e location configuram o Nginx para redirecionar solicitações HTTP para HTTPS, além do balanceamento de carga entre eles em dois servidores do aplicativo Django configurados nos Passos 1 e 2. Para aprender mais sobre a estrutura do arquivo de configuração do Nginx, consulte este artigo, Compreendendo a estrutura do arquivo de configuração do Nginx e contextos de configuração. Além dele, este artigo, Compreendendo os algoritmos de seleção do servidor Nginx e do bloco de localização, também pode ser útil.

      Essa configuração foi montada a partir de arquivos de configuração de amostra fornecidos pelo Gunicorn, Cerbot e Nginx e serve como uma configuração mínima do Nginx para tornar essa arquitetura funcional. Ajustar essa configuração do Nginx vai além do escopo deste artigo, mas você pode usar uma ferramenta como o NGINXConfig para gerar arquivos de configuração do Nginx com maior desempenho e segurança para sua arquitetura.

      O bloco upstream define o grupo de servidores usados para fazer proxy de solicitações usando a diretiva proxy_pass:

      conf/nginx.conf

      upstream django {
          server APP_SERVER_1_IP;
          server APP_SERVER_2_IP;
      }
      . . .
      

      Neste bloco, damos o nome de django ao upstream e incluímos os endereços IP de ambos os servidores do aplicativo Django. Se os servidores do aplicativo estiverem em execução na DigitalOcean e possuírem a rede VPC ativada, você deve usar aqui seus endereços IP privados. Para aprender como habilitar a rede VPC na DigitalOcean, consulte Como habilitar a rede VPC em Droplets existentes.

      O primeiro bloco server captura solicitações que não correspondam ao seu domínio e encerra a conexão. Por exemplo, uma solicitação HTTP direta para o endereço IP do seu servidor seria manipulada por este bloco:

      conf/nginx.conf

      . . .
      server {
          listen 80 default_server;
          return 444;
      }
      . . .
      

      O próximo bloco server redireciona as solicitações HTTP para seu domínio para HTTPS usando um redirecionamento HTTP 301. Essas solicitações são então manipuladas pelo bloco server final:

      conf/nginx.conf

      . . .
      server {
          listen 80;
          listen [::]:80;
          server_name your_domain.com;
          return 301 https://$server_name$request_uri;
      }
      . . .
      

      Essas duas diretivas definem os caminhos para o certificado TLS e a chave secreta. Eles serão fornecidos usando o Certbot e montados no contêiner do Nginx no próximo passo.

      conf/nginx.conf

      . . .
      ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
      . . .
      

      Esses parâmetros são os padrões de segurança SSL recomendados pelo Certbot. Para aprender mais sobre eles, consulte Module ngx_http_ssl_module da documentação do Nginx. O Security/Server Side TLS do Mozilla é outro guia útil que você ajuste sua configuração SSL.

      conf/nginx.conf

      . . .
          ssl_session_cache shared:le_nginx_SSL:10m;
          ssl_session_timeout 1440m;
          ssl_session_tickets off;
      
          ssl_protocols TLSv1.2 TLSv1.3;
          ssl_prefer_server_ciphers off;
      
          ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
      . . .
      

      Essas duas diretivas da amostra de configuração do Nginx do Gunicorn definem o tamanho máximo permitido do corpo da solicitação do cliente e atribuem o tempo limite para manutenção de conexão com o cliente. O Nginx irá interromper as conexões com o cliente após keepalive_timeout segundos.

      conf/nginx.conf

      . . .
      client_max_body_size 4G;
      keepalive_timeout 5;
      . . .
      

      O primeiro bloco location instrui o Nginx a redirecionar solicitações via proxy para os servidores upstream django via HTTP. Ele também preserva os cabeçalhos HTTP do cliente que capturam o endereço IP originário, protocolo usado para conexão e host de destino:

      conf/nginx.conf

      . . .
      location / {
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_set_header Host $http_host;
          proxy_redirect off;
          proxy_pass http://django;
      }
      . . .
      

      Para aprender mais sobre essas diretivas, consulte Deploying Gunicorn e Module ngx_http_proxy_module da documentação do Nginx.

      O bloco location final captura as solicitações para o caminho /well-known/acme-challenge/ usado pelo Certbot para HTTP-01 challenges. Ele faz isso para verificar seu domínio com o Let’s Encrypt e fornecer ou renovar os certificados TLS. Para maiores informações sobre o HTTP-01 challenge usado pelo Certbot, consulte Challenge Types da documentação do Let’s Encrypt.

      conf/nginx.conf

      . . .
      location ^~ /.well-known/acme-challenge/ {
              root /var/www/html;
      }
      

      Assim que terminar a edição, salve e feche o arquivo.

      Agora, você pode usar esse arquivo de configuração para executar um contêiner Docker do Nginx. Neste tutorial, usaremos a imagem nginx:1.19.0, versão 1.19.0 da imagem oficial do Docker mantida pelo Nginx.

      Quando executamos o contêiner pela primeira vez, o Nginx irá gerar um erro e falhar, pois ainda não fornecemos os certificados definidos no arquivo de configuração. No entanto, ainda vamos executar o comando para baixar localmente a imagem do Nginx e testar se todo o resto está funcionando corretamente:

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Aqui, chamamos o contêiner de nginx e mapeamos as portas de host 80 e 443 para as respectivas portas do contêiner. O sinalizador -v monta o arquivo de configuração no contêiner do Nginx em /etc/nginx/conf.d/nginx.conf, que é de onde a imagem do Nginx está configurada para carregar. Ele é montado em ro, ou modo “apenas leitura”, para que o contêiner não consiga modificar o arquivo. O diretório root Web /var/www/html também é montado no contêiner. Por fim, o nginx:1.19.0 instrui o Docker a pegar e executar a imagem nginx:1.19.0 do Dockerhub.

      O Docker irá pegar e executar a imagem, e então o Nginx irá gerar um erro quando não encontrar o certificado TLS e a chave secreta configurados. No próximo passo, vamos providenciá-los usando um cliente Certbot em Docker e a autoridade de certificação Let’s Encrypt.

      Passo 4 — Configurando o Certbot e a renovação de certificados do Lets Encrypt

      O Certbot é um cliente do Let’s Encrypt desenvolvido pela Electronic Frontier Foundation. Ele fornece certificados TLS gratuitos da autoridade de certificação Let’s Encrypt, que permite que navegadores verifiquem a identidade de seus servidores Web. Como temos o Docker instalado em nosso servidor de proxy do Nginx, usaremos a imagem Certbot do Docker para fornecer e renovar os certificados TLS.

      Comece garantindo que você tenha um registro DNS do tipo A mapeado para o endereço IP público do servidor proxy. Em seguida, em seu servidor proxy, forneça uma versão de preparo dos certificados usando a imagem certbot do Docker:

      docker run -it --rm -p 80:80 --name certbot 
               -v "/etc/letsencrypt:/etc/letsencrypt" 
               -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
               certbot/certbot certonly --standalone --staging -d your_domain.com
      

      Esse comando executa a imagem certbot do Docker em modo interativo, e encaminha a porta 80 no host para a porta 80 no contêiner. Ele cria e monta dois diretórios de host no contêiner: /etc/letsencrypt/ e /var/lib/letsencrypt/. O certbot é executado no modo standalone, sem o Nginx, e usará os servidores staging — de preparo — do Let’s Encrypt para realizar a validação de domínio.

      Quando solicitado, digite seu endereço de e-mail e concorde com os Termos de serviço. Se a validação de domínio for bem sucedida, você deve ver o seguinte resultado:

      Output

      Obtaining a new certificate Performing the following challenges: http-01 challenge for stubb.dev Waiting for verification... Cleaning up challenges IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain.com/privkey.pem Your cert will expire on 2020-09-15. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal.

      Você pode inspecionar o certificado usando o cat:

      sudo cat /etc/letsencrypt/live/your_domain.com/fullchain.pem
      

      Com o certificado TLS fornecido, podemos testar a configuração do Nginx montada no passo anterior:

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Esse é a mesma execução de comandos do Passo 3, com a adição de ambos os diretórios recém-criados do Let’s Encrypt.

      Assim que o Nginx estiver em funcionamento, navege até http://your_domain.com. Você pode receber um aviso em seu navegador de que a autoridade de certificação é inválida. Isso é esperado, já que fornecemos os certificados de preparo e não os certificados do Let’s Encrypt de produção. Verifique a barra de URL do seu navegador para confirmar se sua solicitação HTTP foi redirecionada para o HTTPS.

      Aperte CTRL+C em seu terminal para sair do Nginx e execute o cliente certbot novamente, mas, desta vez, omitindo o sinalizador --staging:

      docker run -it --rm -p 80:80 --name certbot 
               -v "/etc/letsencrypt:/etc/letsencrypt" 
               -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
               certbot/certbot certonly --standalone -d your_domain.com
      

      Quando solicitado a manter o certificado existente ou renová-lo, aperte 2 para renová-lo e então ENTER para confirmar sua escolha.

      Com o certificado TLS de produção fornecido, execute o servidor Nginx novamente:

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Em seu navegador, navegue até http://your_domain.com. Na barra de URL, confirme se a solicitação HTTP foi redirecionada para o HTTPS. Como o aplicativo Polls não possui nenhum padrão de rota configurado, você deve ver um erro Page not found do Django. Navegue até https://your_domain.com/polls e você verá a interface padrão do aplicativo Polls:

      Interface do aplicativo Polls

      Neste ponto, você já forneceu um certificado TLS de produção usando o cliente Certbot do Docker, e está aplicando um proxy reverso e balanceamento de carga nas solicitações externas de carga para os dois servidores do aplicativo Django.

      Os certificados do Let’s Encrypt expiram a cada 90 dias. Para garantir que seu certificado permaneça válido, você deve renová-lo regularmente antes de sua expiração programada. Com o Nginx em execução, você deve usar o cliente Certbot no modo webroot em vez do modo standalone. Isso significa que o Certbot irá realizar a validação criando um arquivo no diretório /var/www/html/.well-known/acme-challenge/ e as solicitações de validação do Let’s Encrypt para este caminho serão capturadas pela regra location definida na configuração do Nginx no Passo 3. Então, o Certbot irá rotacionar os certificados e você pode recarregar o Nginx para que ele use esse certificado recém-fornecido.

      Existem várias maneiras de automatizar esse procedimento e a renovação automática de certificados TLS vai além do escopo deste tutorial. Para um processo semelhante usando o utilitário de planejamento cron, consulte o Passo 6 de Como proteger um aplicativo Node.js em contêiner com Nginx, Let’s Encrypt e Docker Compose.

      Em seu terminal, aperte CTRL+C para encerrar o contêiner do Nginx. Execute-o novamente em modo separado adicionando o sinalizador -d:

      docker run --rm --name nginx -d -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
        -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Com o Nginx em execução em segundo plano, use o comando a seguir para realizar simulação do procedimento de renovação de certificado:

      docker run -it --rm --name certbot 
          -v "/etc/letsencrypt:/etc/letsencrypt" 
        -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
        -v "/var/www/html:/var/www/html" 
        certbot/certbot renew --webroot -w /var/www/html --dry-run
      

      Usamos o plug-in --webroot, especificamos o caminho do root Web e usamos o sinalizador --dry-run para verificar se tudo está funcionando corretamente sem realmente realizar a renovação de certificado.

      Se a simulação de renovação for bem sucedida, você deve ver o seguinte resultado:

      Output

      Cert not due for renewal, but simulating renewal for dry run Plugins selected: Authenticator webroot, Installer None Renewing an existing certificate Performing the following challenges: http-01 challenge for your_domain.com Using the webroot path /var/www/html for all unmatched domains. Waiting for verification... Cleaning up challenges - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - new certificate deployed without reload, fullchain is /etc/letsencrypt/live/your_domain.com/fullchain.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates below have not been saved.) Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/your_domain.com/fullchain.pem (success) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

      Em uma configuração de produção, após renovar os certificados, é necessário recarregar o Nginx para que as alterações entrem em vigor. Para recarregar o Nginx, execute o seguinte comando:

      docker kill -s HUP nginx
      

      Esse comando enviará um sinal HUP do Unix para o processo do Nginx em execução dentro do contêiner nginx do Docker. Ao receber esse sinal, o Nginx irá recarregar suas configurações e os certificados renovados.

      Com o HTTPS habilitado e todos os componentes dessa arquitetura em operação, o passo final é bloquear a configuração impedindo o acesso externo aos dois servidores backend de aplicativo. Todas as solicitações HTTP devem fluir através do proxy do Nginx.

      Passo 5 — Prevenindo o acesso externo a servidores do aplicativo Django

      Na arquitetura descrita neste tutorial, a terminação SSL ocorre no proxy do Nginx. Isso significa que o Nginx descriptografa a conexão SSL, e os pacotes, não criptografados, são enviados via proxy para os servidores do aplicativo Django. Para muitos casos de uso, esse nível de segurança é o suficiente. Para aplicações envolvendo dados financeiros ou de saúde, pode ser interessante implementar uma criptografia de ponta a ponta. Você pode fazer isso encaminhando pacotes criptografados através do balanceador de carga e descriptografando-os nos servidores do aplicativo, ou criptografando novamente no proxy e mais uma vez descriptografando nos servidores do aplicativo Django. Essas técnicas vão além do escopo deste artigo, mas para aprender mais sobre elas, consulte Criptografia de ponta a ponta.

      O proxy do Nginx age como um gateway entre o tráfego externo e a rede interna. Teoricamente, nenhum cliente externo deve ter acesso direto aos servidores internos do aplicativo, e todas as solicitações devem fluir através do servidor Nginx. A nota no Passo 1 descreve brevemente um problema em aberto com o Docker, onde ele ignora as configurações de firewall do ufw por padrão e abre portas externamente, o que pode não ser seguro. Para lidar com essa preocupação de segurança, é recomendado usar firewalls em nuvem ao trabalhar com servidores que possuam o Docker habilitado. Para obter mais informações sobre a criação de firewalls em nuvem com a DigitalOcean, consulte Como criar firewalls. Você também pode manipular o iptables diretamente em vez de usar o ufw. Para aprender mais sobre como usar o iptables com o Docker, consulte Docker e o iptables.

      Neste passo, vamos modificar a configuração do UFW para bloquear o acesso externo a portas do host abertas pelo Docker. Ao executarmos o Django nos servidores do aplicativo, passamos o sinalizador -p 80:8000 para o docker, que encaminha a porta 80 no host para a porta 8000 no contêiner. Isso também abriu a porta 80 para clientes externos, o que pode ser verificado visitando http://your_app_server_1_IP. Para evitar o acesso direto, vamos modificar a configuração do UFW usando o método descrito no repositório ufw-docker do GitHub.

      Comece fazendo login no primeiro servidor do aplicativo Django. Em seguida, abra o arquivo /etc/ufw/after.rules com privilégios de superusuário, usando o nano ou o seu editor favorito:

      sudo nano /etc/ufw/after.rules
      

      Digite sua senha quando solicitado, e aperte ENTER para confirmar.

      Você deve ver as seguintes regras do ufw:

      /etc/ufw/after.rules

      #
      # rules.input-after
      #
      # Rules that should be run after the ufw command line added rules. Custom
      # rules should be added to one of these chains:
      #   ufw-after-input
      #   ufw-after-output
      #   ufw-after-forward
      #
      
      # Don't delete these required lines, otherwise there will be errors
      *filter
      :ufw-after-input - [0:0]
      :ufw-after-output - [0:0]
      :ufw-after-forward - [0:0]
      # End required lines
      
      # don't log noisy services by default
      -A ufw-after-input -p udp --dport 137 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 138 -j ufw-skip-to-policy-input
      -A ufw-after-input -p tcp --dport 139 -j ufw-skip-to-policy-input
      -A ufw-after-input -p tcp --dport 445 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 67 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 68 -j ufw-skip-to-policy-input
      
      # don't log noisy broadcast
      -A ufw-after-input -m addrtype --dst-type BROADCAST -j ufw-skip-to-policy-input
      
      # don't delete the 'COMMIT' line or these rules won't be processed
      COMMIT
      

      Role até o final e cole o bloco a seguir de regras de configuração do UFW:

      /etc/ufw/after.rules

      . . .
      
      # BEGIN UFW AND DOCKER
      *filter
      :ufw-user-forward - [0:0]
      :DOCKER-USER - [0:0]
      -A DOCKER-USER -j RETURN -s 10.0.0.0/8
      -A DOCKER-USER -j RETURN -s 172.16.0.0/12
      -A DOCKER-USER -j RETURN -s 192.168.0.0/16
      
      -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
      
      -A DOCKER-USER -j ufw-user-forward
      
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
      
      -A DOCKER-USER -j RETURN
      COMMIT
      # END UFW AND DOCKER
      

      Essas regras restringem o acesso público às portas abertas pelo Docker e permitem o acesso dos intervalos de IP privativo 10.0.0.0/8, 172.16.0.0/12 e 192.168.0.0/16. Se estiver usando o VPC com a DigitalOcean, então os Droplets em sua rede VPC terão acesso à porta aberta através da interface de rede privada, mas os clientes externos não terão. Para maiores informações sobre o VPC, consulte a documentação oficial do VPC. Para aprender mais sobre as regras implementadas nesse trecho de código, consulte Como isso funciona? do LEIAME do ufw-docker.

      Se você não estiver usando o VPC com a DigitalOcean, e digitou os endereços IP públicos dos servidores do aplicativo no bloco upstream de sua configuração do Nginx, será necessário modificar explicitamente o firewall UFW para permitir o tráfego do servidor Nginx através da porta 80 nos servidores do aplicativo Django. Para orientação sobre a criação de regras de allow (permissão) com o firewall UFW, consulte Fundamentos do UFW: Regras e comandos comuns do firewall.

      Assim que terminar a edição, salve e feche o arquivo.

      Reinicie o ufw para que a nova configuração entre em vigor:

      sudo systemctl restart ufw
      

      Navegue até http://APP_SERVER_1_IP em seu navegador Web para confirmar se não é mais possível acessar o servidor do aplicativo pela porta 80.

      Repita esse processo no segundo servidor do aplicativo Django.

      Faça logoff do primeiro servidor de aplicativo ou abra outra janela do terminal e faça login no segundo servidor do aplicativo Django. Em seguida, abra o arquivo /etc/ufw/after.rules com privilégios de superusuário, usando o nano ou o seu editor favorito:

      sudo nano /etc/ufw/after.rules
      

      Digite sua senha quando solicitado e aperte ENTER para confirmar.

      Role até o final e cole o bloco a seguir de regras de configuração do UFW:

      /etc/ufw/after.rules

      . . .
      
      # BEGIN UFW AND DOCKER
      *filter
      :ufw-user-forward - [0:0]
      :DOCKER-USER - [0:0]
      -A DOCKER-USER -j RETURN -s 10.0.0.0/8
      -A DOCKER-USER -j RETURN -s 172.16.0.0/12
      -A DOCKER-USER -j RETURN -s 192.168.0.0/16
      
      -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
      
      -A DOCKER-USER -j ufw-user-forward
      
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
      
      -A DOCKER-USER -j RETURN
      COMMIT
      # END UFW AND DOCKER
      

      Assim que terminar a edição, salve e feche o arquivo.

      Reinicie o ufw para que a nova configuração entre em vigor:

      sudo systemctl restart ufw
      

      Navegue até http://APP_SERVER_2_IP em seu navegador Web para confirmar se não é mais possível acessar o servidor do aplicativo pela porta 80.

      Por fim, navegue até https://your_domain_here/polls para confirmar se o proxy do Nginx ainda tem acesso aos servidores upstream do Django. Você deve ver a interface padrão do aplicativo Polls.

      Conclusão

      Neste tutorial, você configurou um aplicativo Polls do Django escalável usando contêineres Docker. À medida que seu tráfego cresce e a carga no sistema aumenta, é possível escalar cada camada separadamente: a camada de proxy do Nginx, a camada backend do aplicativo Django e a camada do banco de dados PostgreSQL.

      Ao construir um sistema distribuído, muitas vezes há várias decisões de projeto que você deve enfrentar, e várias arquiteturas podem satisfazer seu caso de uso. A arquitetura descrita neste tutorial tem o intuito de servir como um planejamento flexível para projetar aplicativos escaláveis com o Django e o Docker.

      Você pode desejar controlar o comportamento dos contêineres quando eles encontram erros, ou executar contêineres automaticamente quando o seu sistema for inicializado. Para fazer isso, você pode usar um gerenciador de processos como o Systemd ou implementar políticas de reinicialização. Para mais informações sobre isso, consulte Iniciar contêineres automaticamente da documentação do Docker.

      Ao trabalhar em escala com vários hosts executando a mesma imagem do Docker, pode ser mais eficiente automatizar passos usando uma ferramenta de gerenciamento de configuração como o Ansible ou o Chef. Para aprender mais sobre o gerenciamento de configuração, consulte Uma introdução ao gerenciamento de configuração e Automatizando a configuração do servidor com o Ansible: um kit de oficina da DigitalOcean.

      Em vez de compilar a mesma imagem em todos os hosts, você também pode simplificar a implantação usando um registro de imagem como o Docker Hub, que compila, armazena e distribui centralmente as imagens do Docker em vários servidores. Junto com um registro de imagem, um pipeline de integração e implantação contínuas pode ajudá-lo a compilar, testar e implantar imagens em seus servidores de aplicativo. Para mais informações sobre CI/CD, consulte Introdução a práticas recomendadas de CI/CD.



      Source link

      Como configurar um aplicativo Node.js para produção no Ubuntu 20.04


      Introdução

      O Node.js é um ambiente de execução de código aberto do JavaScript para construção de aplicativos de rede e do servidor. A plataforma executa nos sistemas operacionais Linux, macOS, FreeBSD e Windows. Embora você possa executar aplicativos Node.js na linha de comando, este tutorial se concentrará em executá-los como um serviço. Isso significa que eles irão reiniciar após reinicialização ou falha, sendo seguros para utilização em um ambiente de produção.

      Neste tutorial, você irá configurar um ambiente Node.js pronto para produção em um único servidor Ubuntu 20.04. Este servidor executará um aplicativo Node.js gerenciado pelo PM2 e fornecerá aos usuários acesso seguro ao aplicativo através de um proxy reverso Nginx. O servidor Nginx oferecerá HTTPS usando um certificado gratuito fornecido pelo Let’s Encrypt.

      Pré-requisitos

      Este guia supõe que você tenha o seguinte:

      Quando você tiver cumprido com os pré-requisitos, você terá um servidor atendendo a página do espaço reservado padrão do seu domínio em https://example.com/.

      Passo 1 — Instalando o Node.js

      Vamos começar instalando a versão mais recente do Node.js com LTS, ou Long-Term Support (Suporte de longo prazo), usando os arquivos do pacote NodeSource.

      Primeiramente, instale o NodeSource PPA para ter acesso ao seu conteúdo. Certifique-se de que você esteja em seu diretório base, e use o curl para recuperar o script de instalação para a versão LTS mais recente do Node.js dos seus arquivos.

      • cd ~
      • curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh

      É possível verificar o conteúdo deste script com o nano ou seu editor de texto preferido:

      Quando terminar de inspecionar o script, execute-o sob o sudo:

      • sudo bash nodesource_setup.sh

      O PPA será adicionado à sua configuração e seu cache de pacotes local será atualizado automaticamente. Após executar o script de configuração do Nodesource, você pode instalar o pacote do Node.js:

      Para verificar qual versão do Node.js você tem instalada após esses passos iniciais, digite:

      Output

      v14.4.0

      Nota: ao instalar a partir do NodeSource com PPA, o arquivo executável Node.js é chamado de nodejs e não node.

      O pacote nodejs contém o binário do nodejs assim como o npm, um gerenciador de pacotes para módulos Node, então não é necessário instalar o npm separadamente.

      O npm usa um arquivo de configuração no seu diretório home para controlar as atualizações. Ele será criado na primeira vez que você executar o npm. Execute este comando para verificar se o npm está instalado e crie o arquivo de configuração:

      Output

      6.14.5

      Para que alguns pacotes npm possam funcionar (os que requerem compilar o código da fonte, por exemplo), será necessário instalar o pacote build-essential:

      • sudo apt install build-essential

      Agora, você tem as ferramentas necessárias para trabalhar com os pacotes npm que requerem a compilação do código da fonte.

      Com o ambiente de execução Node.js instalado, vamos seguir em frente para escrever um aplicativo Node.js.

      Passo 2 — Criando um aplicativo Node.js

      Vamos escrever um aplicativo Hello World que retorna “Hello World” a qualquer pedido de HTTP. Este aplicativo exemplo ajudará você a configurar o Node.js. Você pode substituí-lo pelo seu próprio aplicativo — certifique-se apenas de que você modifique seu aplicativo para escutar os endereços IP e portas apropriadas.

      Primeiramente, vamos criar um aplicativo exemplo chamado hello.js:

      Insira o seguinte código no arquivo:

      ~/hello.js

      const http = require('http');
      
      const hostname="localhost";
      const port = 3000;
      
      const server = http.createServer((req, res) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World!n');
      });
      
      server.listen(port, hostname, () => {
        console.log(`Server running at http://${hostname}:${port}/`);
      });
      

      Salve o arquivo e saia do editor.

      Este aplicativo Node.js escuta no endereço especificado (localhost) e porta (3000) e retorna “Hello World!” com um código de sucesso HTTP 200. Uma vez que estamos escutando no localhost, clientes remotos não poderão se conectar ao nosso aplicativo.

      Para testar seu aplicativo, digite:

      Você receberá o seguinte resultado:

      Output

      Server running at http://localhost:3000/

      Nota: executar um aplicativo Node.js dessa maneira irá bloquear comandos adicionais até que o aplicativo seja encerrado pressionando-se CTRL+C.

      Para testar o aplicativo, abra outra sessão de terminal no seu servidor e conecte-se ao localhost com o curl:

      • curl http://localhost:3000

      Caso obtenha o seguinte resultado, o aplicativo está funcionando corretamente e escutando no endereço e porta corretos:

      Output

      Hello World!

      Caso não obtenha o resultado esperado, certifique-se de que seu aplicativo Node.js esteja funcionando e configurado para escutar no endereço e porta apropriados.

      Assim que tiver certeza certeza de que ele está funcionando, encerre o aplicativo (se você ainda não o tiver feito) pressionando CTRL+C.

      Passo 3 — Instalando o PM2

      Em seguida, vamos instalar o PM2, um gerenciador de processos para aplicativos Node.js. O PM2 torna possível forçar os aplicativos a executarem como daemon para que eles executem em segundo plano como um serviço.

      Utilize o npm para instalar a última versão do PM2 no seu servidor:

      • sudo npm install pm2@latest -g

      A opção -g faz com que o npm instale o módulo globally, para que ele esteja disponível em todo o sistema.

      Vamos usar primeiro o comando pm2 start para executar seu aplicativo, hello.js, em segundo plano:

      Isso também adiciona seu aplicativo na lista de processos do PM2, que é produzida toda vez que você inicia um aplicativo:

      Output

      ... [PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting /home/sammy/hello.js in fork_mode (1 instance) [PM2] Done. ┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐ │ id │ name │ mode │ ↺ │ status │ cpu │ memory │ ├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤ │ 0 │ hello │ fork │ 0 │ online │ 0% │ 25.2mb │ └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

      Assim como indicado acima, o PM2 atribui automaticamente um App name (baseado no nome do arquivo, sem a extensão .js) e um id do PM2. O PM2 também mantém outras informações, como o PID do processo, seu status atual e o uso de memória.

      Os aplicativos que estão funcionando sob o PM2 serão reiniciados automaticamente se o aplicativo falhar ou for encerrado, mas podemos ir um passo além para fazer o aplicativo iniciar na inicialização do sistema usando o subcomando startup. Este subcomando gera e configura um script de inicialização para iniciar o PM2 e seus processos gerenciados nas inicializações do servidor:

      A última linha da saída resultante incluirá um comando para ser executado com privilégios de superusuário para definir o PM2 para iniciar na inicialização:

      Output

      [PM2] Init System found: systemd sammy [PM2] To setup the Startup Script, copy/paste the following command: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Execute o comando a partir do resultado, com o seu nome de usuário no lugar de sammy:

      • sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Como um passo adicional, podemos salvar a lista de processos PM2 e os ambientes correspondentes:

      Agora, você criou uma unidade systemd que executa o pm2 para seu usuário na inicialização. Esta instância pm2, por sua vez, executa o hello.js.

      Inicie o serviço com systemctl:

      • sudo systemctl start pm2-sammy

      Se neste ponto você encontrar um erro, pode ser necessário reinicializar o sistema. Isso pode ser feito com o sudo reboot.

      Verifique o status da unidade systemd:

      • systemctl status pm2-sammy

      Para um panorama detalhado do systemd, veja Systemd Essentials: Working with Services, Units, and the Journal.

      Além daqueles que abordamos, o PM2 fornece muitos subcomandos que permitem que você gerencie ou procure informações sobre seus aplicativos.

      Interrompa um aplicativo com este comando (especifique o App name do PM2 ou id):

      Reinicie um aplicativo:

      • pm2 restart app_name_or_id

      Liste os aplicativos atualmente gerenciados pelo PM2:

      Obtenha informações sobre um aplicativo específico usando seu App name:

      O monitor de processos do PM2 pode ser trazido com o subcomando monit. Isso mostra o status do aplicativo, da CPU, e o uso de memória:

      Note que executar o pm2 sem qualquer argumento também exibirá uma página de ajuda com exemplos de uso.

      Agora que seu aplicativo Node.js está funcionando e é gerenciado pelo PM2, vamos configurar o proxy reverso.

      Passo 4 — Configurando o Nginx como um Servidor de Proxy Reverso

      Seu aplicativo está funcionando e escutando no localhost, mas você precisa configurar uma forma dos seus usuários acesssarem ele. Vamos configurar o servidor Web Nginx como um proxy reverso com esse intuito.

      Nos pré-requisitos do tutorial, você definiu sua configuração do Nginx no arquivo /etc/nginx/sites-available/example.com. Abra este arquivo para edição:

      • sudo nano /etc/nginx/sites-available/example.com

      Dentro do bloco server, você deve ter um bloco location / existente. Substitua o conteúdo desse bloco com a seguinte configuração. Se seu aplicativo for configurado para escutar em uma porta diferente, atualize a parte em destaque com o número de porta correto:

      /etc/nginx/sites-available/example.com

      server {
      ...
          location / {
              proxy_pass http://localhost:3000;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Isso configura o servidor para responder a pedidos em sua raiz. Supondo que nosso servidor esteja disponível em example.com, acessar o https://example.com/ através de um navegador Web enviaria o pedido para hello.js, escutando na porta 3000 do localhost.

      Você pode adicionar blocos location adicionais ao mesmo bloco de servidor para fornecer acesso a outros aplicativos no mesmo servidor. Por exemplo, se você também estivesse executando outro aplicativo Node.js na porta 3001, você poderia adicionar este bloco de localização para permitir o acesso a ele através de https://example.com/app2:

      /etc/nginx/sites-available/example.com — Optional

      server {
      ...
          location /app2 {
              proxy_pass http://localhost:3001;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Assim que terminar de adicionar os blocos de localização para seus aplicativos, salve o arquivo e saia do seu editor.

      Certifique-se de que não tenha introduzido qualquer erro de sintaxe digitando:

      Reinicie o Nginx:

      • sudo systemctl restart nginx

      Supondo que o seu aplicativo Node.js esteja funcionando e o seu aplicativo e as configurações do Nginx estejam corretos, agora você deverá poder acessar seu aplicativo via proxy reverso do Nginx. Teste acessando o URL do seu servidor (seu endereço IP público ou nome de domínio).

      Conclusão

      Parabéns! Agora, você tem seu aplicativo Node.js funcionando atrás de um proxy reverso Nginx em um servidor Ubuntu 20.04. Esta configuração de proxy reverso é suficientemente flexível para fornecer o acesso aos seus usuários a outros aplicativos ou conteúdos Web estáticos que você queira compartilhar.



      Source link