One place for hosting & domains

      Aplicativo

      Como implantar um aplicativo Django escalável e seguro com o Kubernetes


      Introdução

      Neste tutorial, você irá implantar um aplicativo de pesquisa do Django em contêiner em um cluster do Kubernetes.

      O Django é um framework Web poderoso, que pode ajudar a acelerar a implantação do seu aplicativo Python. Ele inclui diversas funcionalidades convenientes, como um mapeador relacional do objeto, autenticação de usuário e uma interface administrativa personalizável para seu aplicativo. Ele também inclui um framework de cache e encoraja o design de aplicativos organizados através do seu URL Dispatcher e Sistema de modelos.

      Em Como construir um aplicativo Django e Gunicorn com o Docker, o aplicativo Polls de tutorial do Django foi modificado de acordo com a metodologia do Twelve-Factor para a construção de aplicativos Web escaláveis e nativos na nuvem. Essa configuração em contêiner foi dimensionada e protegida com um proxy reverso do Nginx e autenticada pelo Let’s Encrypt com certificados TLS seguindo Como dimensionar e proteger um aplicativo Django com o Docker, Nginx e Let’s Encrypt. Neste tutorial final da série De contêineres ao Kubernetes com o Django, o aplicativo modernizado de pesquisa do Django será implantado em um cluster do Kubernetes.

      O Kubernetes é um orquestrador de contêineres de código aberto poderoso, que automatiza a implantação, dimensionamento e gerenciamento de aplicativos em contêiner. Os objetos do Kubernetes como os ConfigMaps e Segredos permitem centralizar e desacoplar a configuração dos seus contêineres, enquanto os controladores como Deployments reiniciam automaticamente contêineres que falharam e habilitam o dimensionamento rápido de réplicas de contêineres. A criptografia TLS é habilitada com um objeto Ingress e o Controlador Ingress de código aberto ingress-nginx. O add-on cert-manager do Kubernetes renova e emite certificados usando a autoridade de certificação gratuita Let’s Encrypt.

      Pré-requisitos

      Para seguir este tutorial, será necessário:

      • Um cluster do Kubernetes 1.15+ com controle de acesso baseado na função (RBAC) habilitado. Essa configuração irá usar um cluster do Kubernetes da DigitalOcean, mas você pode criar um cluster usando outro método.
      • A ferramenta kubectl de linha de comando instalada em sua máquina local e configurada para se conectar ao seu cluster. Você pode ler mais sobre como instalar o kubectl na documentação oficial. Se você estiver usando um cluster do Kubernetes da DigitalOcean, consulte Como se conectar a um cluster do Kubernetes da DigitalOcean para aprender como se conectar ao seu cluster usando o kubectl.
      • Um nome de domínio registrado. Este tutorial usará your_domain.com do início ao fim. Você pode obter um domínio gratuitamente através do Freenom, ou usar o registrador de domínios de sua escolha.
      • Um Controlador Ingress ingress-nginx e o gerenciador de certificados TLS cert-manager instalados no seu cluster e configurados para emitir certificados TLS. Para aprender como instalar e configurar um Ingress com o cert-manager, consulte Como configurar um Nginx Ingress com Cert-Manager no Kubernetes da plataforma DigitalOcean.
      • Um registro de DNS A com your_domain.com apontando para o endereço IP público do Balanceador de carga do Ingress. Se estiver usando a DigitalOcean para gerenciar os registros de DNS do seu domínio, consulte Como gerenciar os registros de DNS para aprender como criar um registro A.
      • 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.
      • Uma conta do Docker Hub e repositório público. Para obter mais informações sobre a criação deles, consulte Repositórios da documentação do Docker.
      • O mecanismo Docker instalado em sua máquina local. Consulte Como instalar e usar o Docker no Ubuntu 18.04 para aprender mais.

      Assim que tiver esses componentes configurados, tudo estará pronto para seguir o guia.

      Passo 1 — Clonando e configurando o aplicativo

      Neste passo, vamos clonar o código do aplicativo do GitHub e definir algumas configurações como as credenciais de banco de dados e chaves de armazenamento de objetos.

      O código do aplicativo e o Dockerfile podem ser encontrados na ramificação polls-docker do repositório GitHub do aplicativo Polls de tutorial do Django. Esse repositório contém códigos para o aplicativo de amostra Polls da documentação do Django, que ensina como construir um aplicativo de pesquisa a partir do zero.

      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.

      Comece usando o git para clonar o branch polls-docker do repositório GitHub do aplicativo Polls do tutorial do Django em sua máquina local:

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

      Navegue até o diretório 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. Verifique o 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:

      Você deve ver a imagem polls listada:

      OutputREPOSITORY          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 o 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:

      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, isso deve ser definido 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 espaço ou armazenamento de objetos.
      • STATIC_SECRET_KEY: defina isso como o segredo da chave de acesso do seu espaço ou armazenamento de objetos.
      • STATIC_BUCKET_NAME: defina isso como seu nome de espaço ou bucket de armazenamento de objetos.
      • STATIC_ENDPOINT_URL: defina isso como o URL do ponto de extremidade do espaço apropriado ou armazenamento de objetos, como https://your_space_name.nyc3.digitaloceanspaces.com se o espaço estiver localizado na região nyc3.

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

      No próximo passo, vamos executar o contêiner configurado localmente e criar o esquema de banco de dados. Além disso, vamos carregar ativos estáticos como folhas de estilos e imagens para o armazenamento de objetos.

      Passo 2 — Criando o esquema de banco de dados e carregando ativos para o armazenamento de objetos

      Com o contêiner construído e configurado, use 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 na sua máquina local 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://localhost 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.

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

      Interface do aplicativo Polls

      Para visualizar a interface administrativa, visite http://localhost/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.

      Com a imagem Docker do aplicativo Django testada, ativos estáticos carregados no armazenamento de objetos e o esquema de banco de dados configurado e pronto para ser usado com seu aplicativo, tudo está pronto para carregar sua imagem do aplicativo Django em um registro de imagem como o Docker Hub.

      Passo 3 — Enviando a imagem do aplicativo Django para o Docker Hub

      Para implementar seu aplicativo no Kubernetes, sua imagem de aplicativo deve ser carregada em um registro como o Docker Hub. O Kubernetes irá puxar a imagem do aplicativo do seu repositório e implantá-la em seu cluster.

      É possível usar um registro privado do Docker, como o registro do contêiner da DigitalOcean, atualmente gratuito em acesso antecipado, ou um registro público do Docker como o Docker Hub. O Docker Hub também permite criar repositórios privados do Docker. Um repositório público permite que qualquer pessoa veja e puxe as imagens do contêiner, enquanto um repositório privado permite restringir o acesso a você e seus membros de equipe.

      Neste tutorial, vamos enviar a imagem do Django ao repositório público do Docker Hub criado nos pré-requisitos. Também é possível enviar sua imagem a um repositório privado, mas puxar imagens de um repositório privado está além do escopo deste artigo. Para aprender mais sobre a autenticação do Kubernetes com o Docker Hub e puxar imagens privadas, consulte Puxar uma imagem de um registro privado dos documentos do Kubernetes.

      Comece fazendo login no Docker Hub em sua máquina local:

      Output

      Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username:

      Digite seu nome de usuário e senha do Docker Hub para fazer login.

      A imagem do Django possui atualmente o sinalizador polls:latest. Para enviá-la ao seu repositório do Docker Hub, sinalize novamente a imagem com seu nome de usuário e nome de repositório do Docker Hub:

      • docker tag polls:latest your_dockerhub_username/your_dockerhub_repo_name:latest

      Carregue a imagem no repositório:

      • docker push sammy/sammy-django:latest

      Neste tutorial, o nome de usuário do Docker Hub é sammy e o nome do repositório é sammy-django. Você deve substituir esses valores pelo seu nome de usuário e nome de repositório do Docker Hub.

      Você verá um resultado que se atualiza conforme as camadas da imagem são enviadas ao Docker Hub.

      Agora que sua imagem está disponível no Kubernetes no Docker Hub, você pode começar a implantá-la em seu cluster.

      Passo 4 — Configurando o ConfigMap

      Quando executamos o contêiner do Django localmente, passamos o arquivo env ao docker run para injetar variáveis de configuração no ambiente de tempo de execução. No Kubernetes, as variáveis de configuração podem ser injetadas usando o ConfigMaps e Segredos.

      Os ConfigMaps devem ser usados para armazenar informações de configuração não confidenciais, como as configurações do aplicativo, enquanto os Segredos devem ser usados para informações confidenciais como chaves de API e credenciais de banco de dados. Ambos são injetados em contêineres de maneira similar, mas os Segredos têm recursos de controle de acesso e segurança adicionais, como a criptografia em repouso. Os Segredos também armazenam dados em base64, enquanto os ConfigMaps armazenam dados em texto simples.

      Para começar, crie um diretório chamado yaml no qual vamos armazenar nossos manifestos do Kubernetes. Navegue até o diretório.

      Abra um arquivo chamado polls-configmap.yaml no nano ou seu editor de texto preferido:

      • nano polls-configmap.yaml

      Cole o seguinte manifesto do ConfigMap:

      polls-configmap.yaml

      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: polls-config
      data:
        DJANGO_ALLOWED_HOSTS: "*"
        STATIC_ENDPOINT_URL: "https://your_space_name.space_region.digitaloceanspaces.com"
        STATIC_BUCKET_NAME: "your_space_name"
        DJANGO_LOGLEVEL: "info"
        DEBUG: "True"
        DATABASE_ENGINE: "postgresql_psycopg2"
      

      As configurações não confidenciais foram extraídas do arquivo env modificado no Passo 1 e coladas em um manifesto do ConfigMap. O objeto do ConfigMap chama-se polls-config. Copie os mesmos valores inseridos no arquivo env no passo anterior.

      Por motivos de teste, deixe o DJANGO_ALLOWED_HOSTS como * para desativar a filtragem baseada em cabeçalhos de host. Em um ambiente de produção, isso deve ser definido como o domínio do seu aplicativo.

      Quando terminar de editar o arquivo, salve e feche-o.

      Crie o ConfigMap em seu cluster usando o kubectl apply:

      • kubectl apply -f polls-configmap.yaml

      Output

      configmap/polls-config created

      Com o ConfigMap criado, vamos criar o Segredo usado pelo nosso aplicativo no próximo passo.

      Passo 5 — Configurando o Segredo

      Os valores do Segredo devem ser codificados em base64, o que significa que criar objetos Segredo em seu cluster é ligeiramente mais complicado do que criar ConfigMaps. É possível repetir o processo do passo anterior, manualmente codificar os valores do Segredo em base64 e colá-los em um arquivo de manifesto. Também é possível criá-los usando um arquivo de variável de ambiente, kubectl create e o sinalizador --from-env-file, o que será feito neste passo.

      Novamente, vamos usar o arquivo env do Passo 1, removendo variáveis inseridas no ConfigMap. Faça uma cópia do arquivo env chamado polls-secrets no diretório yaml:

      • cp ../env ./polls-secrets

      Edite o arquivo em seu editor de preferência:

      polls-secrets

      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
      

      Exclua todas as variáveis inseridas no manifesto do ConfigMap. Quando terminar, ele deve ficar parecido com isto:

      polls-secrets

      DJANGO_SECRET_KEY=your_secret_key
      DATABASE_NAME=polls
      DATABASE_USERNAME=your_django_db_user
      DATABASE_PASSWORD=your_django_db_user_password
      DATABASE_HOST=your_db_host
      DATABASE_PORT=your_db_port
      STATIC_ACCESS_KEY_ID=your_space_access_key
      STATIC_SECRET_KEY=your_space_access_key_secret
      

      Certifique-se de usar os mesmos valores usados no Passo 1. Quando terminar, salve e feche o arquivo.

      Crie o Segredo em seu cluster usando o kubectl create secret:

      • kubectl create secret generic polls-secret --from-env-file=poll-secrets

      Output

      secret/polls-secret created

      Aqui, criamos um objeto Segredo chamado polls-secret e o passamos no arquivo de Segredos que acabamos de criar.

      Verifique o Segredo usando o kubectl describe:

      • kubectl describe secret polls-secret

      Output

      Name: polls-secret Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== DATABASE_PASSWORD: 8 bytes DATABASE_PORT: 5 bytes DATABASE_USERNAME: 5 bytes DJANGO_SECRET_KEY: 14 bytes STATIC_ACCESS_KEY_ID: 20 bytes STATIC_SECRET_KEY: 43 bytes DATABASE_HOST: 47 bytes DATABASE_NAME: 5 bytes

      Neste ponto, você armazenou a configuração do seu aplicativo em seu cluster do Kubernetes usando os tipos de objeto Segredo e ConfigMap. Agora, estamos prontos para implantar o aplicativo no cluster.

      Passo 6 — Implementando o aplicativo do Django usando uma Implantação

      Neste passo, você criará um Deployment (implantação) para seu aplicativo do Django. Uma Implantação do Kubernetes é um controlador que pode ser usado para gerenciar aplicativos sem estado em seu cluster. Um controlador é um loop de controle que regula cargas de trabalho aumentando ou diminuindo-as. Os controladores também reiniciam e limpam contêineres com falhas.

      As Implantações controlam um ou mais Pods, a menor unidade implantável em um cluster do Kubernetes. Os Pods incluem um ou mais contêineres. Para aprender mais sobre os diferentes tipos de cargas de trabalho que você pode inicializar, consulte Uma introdução ao Kubernetes.

      Inicie abrindo um arquivo chamado polls-deployment.yaml no seu editor de texto favorito:

      • nano polls-deployment.yaml

      Cole o manifesto de Implantação a seguir:

      polls-deployment.yaml

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: polls-app
        labels:
          app: polls
      spec:
          replicas: 2
        selector:
          matchLabels:
            app: polls
        template:
          metadata:
            labels:
              app: polls
          spec:
            containers:
              - image: your_dockerhub_username/app_repo_name:latest
                name: polls
                envFrom:
                - secretRef:
                    name: polls-secret
                - configMapRef:
                    name: polls-config
                ports:
                  - containerPort: 8000
                    name: gunicorn
      

      Preencha o nome apropriado da imagem do contêiner, referenciando a imagem do Polls do Django que você enviou para o Docker Hub no Passo 2.

      Aqui, definimos uma Implantação do Kubernetes chamada polls-app e a rotulamos com o par de chave-valor app: polls. Especificamos que queremos executar duas réplicas do Pod definido abaixo do campo template.

      Usando o envFrom com o secretRef e o configMapRef, especificamos que todos os dados do Segredo polls-secret e do ConfigMap polls-config devem ser injetados nos contêineres como variáveis de ambiente. As chaves ConfigMap e Segredo tornam-se os nomes das variáveis de ambiente.

      Por fim, expomos a containerPort 8000 e a nomeamos gunicorn.

      Para aprender mais sobre como configurar as Implantações do Kubernetes, consulte Deployments na documentação do Kubernetes.

      Quando terminar de editar o arquivo, salve e feche-o.

      Crie uma Implantação no seu cluster usando o kubectl apply -f:

      • kubectl apply -f polls-deployment.yaml
      • deployment.apps/polls-app created

      Verifique se a Implantação foi implantada corretamente usando o kubectl get:

      • kubectl get deploy polls-app

      Output

      NAME READY UP-TO-DATE AVAILABLE AGE polls-app 2/2 2 2 6m38s

      Se encontrar um erro ou algo não estiver funcionando, use o kubectl describe para verificar o Deployment que falhou:

      Verifique os dois Pods usando o kubectl get pod:

      Output

      NAME READY STATUS RESTARTS AGE polls-app-847f8ccbf4-2stf7 1/1 Running 0 6m42s polls-app-847f8ccbf4-tqpwm 1/1 Running 0 6m57s

      Agora, duas réplicas do seu aplicativo Django estão em funcionamento no cluster. Para acessar o aplicativo, é necessário criar um Service (serviço) do Kubernetes, que vamos fazer em seguida.

      Passo 7 — Permitindo o acesso externo usando um Serviço

      Neste passo, você irá criar um Serviço para seu aplicativo Django. Um Serviço do Kubernetes é uma abstração que permite expor um conjunto de Pods em execução como um serviço de rede. Ao usar um Serviço, é possível criar um ponto de extremidade estável para seu aplicativo que não muda à medida que os Pods são destruídos e recriados.

      Existem vários tipos de Serviço, incluindo os Serviços de ClusterIP, que expõem o Serviço em um IP interno do cluster, os Serviços de NodePort, que expõem o Serviço em cada nó em uma porta estática chamada NodePort, além de os Serviços de LoadBalancer, que fornecem um balanceador de carga em nuvem para direcionar o tráfego externo aos Pods no seu cluster (através dos NodePorts, criados por ele automaticamente). Para aprender mais sobre isso, consulte Service nos documentos do Kubernetes.

      Em nossa configuração final, vamos usar um Serviço de ClusterIP que é exposto usando um Ingress e um Controlador Ingress configurados nos pré-requisitos deste guia. Por enquanto, para testar se tudo está funcionando corretamente, vamos criar um Serviço de NodePort temporário para acessar o aplicativo Django.

      Inicie criando um arquivo chamado polls-svc.yaml usando seu editor favorito:

      Cole o manifesto de Serviço a seguir:

      polls-svc.yaml

      apiVersion: v1
      kind: Service
      metadata:
        name: polls
        labels:
          app: polls
      spec:
        type: NodePort
        selector:
          app: polls
        ports:
          - port: 8000
            targetPort: 8000
      

      Aqui, criamos um Serviço de NodePort chamado polls e damos a ele o rótulo app: polls. Em seguida, selecionamos os Pods de backend com o rótulo app: polls e miramos em suas portas 8000.

      Quando terminar de editar o arquivo, salve e feche-o.

      Implemente o Serviço usando o kubectl apply:

      • kubectl apply -f polls-svc.yaml

      Output

      service/polls created

      Confirme se seu Serviço foi criado usando o kubectl get svc:

      Output

      NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE polls NodePort 10.245.197.189 <none> 8000:32654/TCP 59s

      Esse resultado mostra o IP interno do cluster do Serviço e o NodePort (32654). Para nos conectar ao serviço, precisamos dos endereços IP externos para nossos nós de cluster:

      Output

      NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME pool-7no0qd9e0-364fd Ready <none> 27h v1.18.8 10.118.0.5 203.0.113.1 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9 pool-7no0qd9e0-364fi Ready <none> 27h v1.18.8 10.118.0.4 203.0.113.2 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9 pool-7no0qd9e0-364fv Ready <none> 27h v1.18.8 10.118.0.3 203.0.113.3 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9

      Em seu navegador Web, visite seu aplicativo Polls usando um endereço IP externo de qualquer nó e o NodePort. De acordo com o resultado acima, a URL do aplicativo seria: http://203.0.113.1:32654/polls.

      Você deve ver a mesma interface do aplicativo Polls que você acessou localmente no Passo 1:

      Interface do aplicativo Polls

      É possível repetir o mesmo teste usando a rota /admin: http://203.0.113.1:32654/admin. Você deve ver a mesma interface de administrador que antes:

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

      Neste estágio, você já implantou duas réplicas do contêiner do aplicativo Polls do Django usando uma Implantação. Você também criou um ponto de extremidade de rede estável para essas duas réplicas, e o tornou externamente acessível usando um Serviço de NodePort.

      O passo final neste tutorial é proteger o tráfego externo para seu aplicativo usando HTTPS. Para fazer isso, vamos usar o Controlador Ingress ingress-nginx instalado nos pré-requisitos e criar um objeto Ingress para rotear o tráfego externo para o Serviço polls do Kubernetes.

      Passo 8 — Configurando o HTTPS usando o Nginx Ingress e o cert-manager

      Os Ingresses do Kubernetes permitem o roteamento do tráfego externo ao cluster do seu Kubernetes de maneira flexível para os Serviços dentro de seu cluster. Isso é alcançado usando os objetos do Ingress, que definem as regras para rotear o tráfego HTTP e HTTPS para os Serviços do Kubernetes e para os Controladores do Ingress, os quais implementam as regras fazendo o balanceamento da carga do tráfego e o seu roteamento para os Serviços de backend apropriados.

      Nos pré-requisitos, você instalou o Controlador Ingress ingress-nginx e o add-on de automação de certificados TLS cert-manager. Você também definiu a preparação e a produção de ClusterIssuers para seu domínio usando a autoridade de certificação Let’s Encrypt e criou um Ingress para testar a emissão de certificados e a criptografia TLS em dois Serviços de backend fictícios. Antes de continuar com este passo, deve-se excluir o Ingress echo-ingress criado no tutorial pré-requisito:

      • kubectl delete ingress echo-ingress

      Se desejar, também pode excluir os Serviços e Implantações fictícios usando o kubectl delete svc e o kubectl delete deploy, mas isso não é essencial para completar este tutorial.

      Você também deve ter criado um registro de DNS A com your_domain.com apontando para o endereço IP público do balanceador de carga do Ingress. Se estiver usando um balanceador de carga da DigitalOcean, é possível encontrar esse endereço IP na seção Load Balancers do Painel de controle. Se estiver usando a DigitalOcean para gerenciar os registros de DNS do seu domínio, consulte Como gerenciar os registros de DNS para aprender como criar registros A.

      Se estiver usando o Kubernetes da DigitalOcean, também certifique-se de ter implementado a solução descrita no Passo 5 de Como configurar um Nginx Ingress com o Cert-Manager no Kubernetes da DigitalOcean.

      Assim que tiver um registro A apontando para o balanceador de carga do Ingress, crie um Ingress para your_domain.com e o Serviço polls.

      Abra um arquivo chamado polls-ingress.yaml no seu editor favorito:

      Cole o manifesto de Ingress a seguir:

      [polls-ingress.yaml]
      apiVersion: networking.k8s.io/v1beta1
      kind: Ingress
      metadata:
        name: polls-ingress
        annotations:
          kubernetes.io/ingress.class: "nginx"
          cert-manager.io/cluster-issuer: "letsencrypt-staging"
      spec:
        tls:
        - hosts:
          - your_domain.com
          secretName: polls-tls
        rules:
        - host: your_domain.com
          http:
            paths:
            - backend:
                serviceName: polls
                servicePort: 8000
      

      Criamos um objeto Ingress chamado polls-ingress e o anotamos para instruir o plano de controle para usar o Controlador Ingress ingress-nginx e o ClusterIssuer de preparo. Também habilitamos o TLS para your_domain.com e armazenamos o certificado e a chave privada em um segredo chamado polls-tls. Por fim, definimos uma regra para rotear o tráfego para o host your_domain.com para o Serviço polls na porta 8000.

      Quando terminar de editar o arquivo, salve e feche-o.

      Crie o Ingress no seu cluster usando o kubectl apply:

      • kubectl apply -f polls-ingress.yaml

      Output

      ingress.networking.k8s.io/polls-ingress created

      É possível usar o kubectl describe para rastrear o estado do Ingress que acabou de ser criado:

      • kubectl describe ingress polls-ingress

      Output

      Name: polls-ingress Namespace: default Address: workaround.your_domain.com Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>) TLS: polls-tls terminates your_domain.com Rules: Host Path Backends ---- ---- -------- your_domain.com polls:8000 (10.244.0.207:8000,10.244.0.53:8000) Annotations: cert-manager.io/cluster-issuer: letsencrypt-staging kubernetes.io/ingress.class: nginx Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 51s nginx-ingress-controller Ingress default/polls-ingress Normal CreateCertificate 51s cert-manager Successfully created Certificate "polls-tls" Normal UPDATE 25s nginx-ingress-controller Ingress default/polls-ingress

      Também é possível executar um describe no certificado polls-tls para confirmar ainda mais se sua criação foi bem-sucedida:

      • kubectl describe certificate polls-tls

      Output

      . . . Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Issuing 3m33s cert-manager Issuing certificate as Secret does not exist Normal Generated 3m32s cert-manager Stored new private key in temporary Secret resource "polls-tls-v9lv9" Normal Requested 3m32s cert-manager Created new CertificateRequest resource "polls-tls-drx9c" Normal Issuing 2m58s cert-manager The certificate has been successfully issued

      Isso confirma que o certificado TLS foi emitido com sucesso e a criptografia do HTTPS agora está ativa para your_domain.com.

      Como usamos o ClusterIssuer de preparo, a maior parte dos navegadores Web não irá confiar no certificado falso do Let’s Encrypt que ele emitiu, de forma que navegar até your_domain.com irá resultar em uma página de erro.

      Para enviar um pedido de teste, vamos usar o wget a partir da linha de comando:

      • wget -O - http://your_domain.com/polls

      Output

      . . . ERROR: cannot verify your_domain.com's certificate, issued by ‘CN=Fake LE Intermediate X1’: Unable to locally verify the issuer's authority. To connect to your_domain.com insecurely, use `--no-check-certificate'.

      Vamos usar o sinalizador sugerido --no-check-certificate para ignorar a validação de certificados:

      • wget --no-check-certificate -q -O - http://your_domain.com/polls

      Output

      <link rel="stylesheet" type="text/css" href="https://your_space.nyc3.digitaloceanspaces.com/django-polls/static/polls/style.css"> <p>No polls are available.</p>

      Esse resultado mostra o HTML para a página de interface de /polls, o que também confirma que a folha de estilos está sendo exibida a partir do armazenamento de objetos.

      Agora que você testou com sucesso a emissão de certificados usando o ClusterIssuer de preparo, modifique o Ingress para usar o ClusterIssuer de produção.

      Abra o polls-ingress.yaml para editar mais uma vez:

      Modifique a anotação do cluster-issuer:

      [polls-ingress.yaml]
      apiVersion: networking.k8s.io/v1beta1
      kind: Ingress
      metadata:
        name: polls-ingress
        annotations:
          kubernetes.io/ingress.class: "nginx"
          cert-manager.io/cluster-issuer: "letsencrypt-prod"
      spec:
        tls:
        - hosts:
          - your_domain.com
          secretName: polls-tls
        rules:
        - host: your_domain.com
          http:
            paths:
            - backend:
                serviceName: polls
                servicePort: 8000
      

      Quando terminar, salve e feche o arquivo. Atualize o Ingress usando o kubectl apply:

      • kubectl apply -f polls-ingress.yaml

      Output

      ingress.networking.k8s.io/polls-ingress configured

      É possível usar o kubectl describe certificate polls-tls e o kubectl describe ingress polls-ingress para rastrear o status da emissão de certificados:

      • kubectl describe ingress polls-ingress

      Output

      . . . Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 23m nginx-ingress-controller Ingress default/polls-ingress Normal CreateCertificate 23m cert-manager Successfully created Certificate "polls-tls" Normal UPDATE 76s (x2 over 22m) nginx-ingress-controller Ingress default/polls-ingress Normal UpdateCertificate 76s cert-manager Successfully updated Certificate "polls-tls"

      O resultado acima confirma que o novo certificado de produção foi emitido com sucesso e armazenado no Secredo polls-tls.

      Navegue até your_domain.com/polls no seu navegador Web para confirmar se a criptografia do HTTPS está habilitada e tudo está funcionando como esperado. Você deve ver a interface do aplicativo Polls:

      Interface do aplicativo Polls

      Verifique se a criptografia do HTTPS está ativa no seu navegador Web. Se estiver usando o Google Chrome, chegar na página acima sem erros confirma que tudo está funcionando corretamente. Além disso, você deve ver um cadeado na barra de URL. Clicar no cadeado permitirá verificar os detalhes do certificado do Let’s Encrypt.

      Como uma tarefa de limpeza final, você pode alterar opcionalmente o tipo de Serviço polls do NodePort para o tipo exclusivamente interno ClusterIP.

      Modifique o polls-svc.yaml usando seu editor:

      Altere o type de NodePort para ClusterIP:

      polls-svc.yaml

      apiVersion: v1
      kind: Service
      metadata:
        name: polls
        labels:
          app: polls
      spec:
        type: ClusterIP
        selector:
          app: polls
        ports:
          - port: 8000
            targetPort: 8000
      

      Quando terminar de editar o arquivo, salve e feche-o.

      Implemente as alterações usando o kubectl apply:

      • kubectl apply -f polls-svc.yaml --force

      Output

      service/polls configured

      Confirme se seu Serviço foi modificado usando o kubectl get svc:

      Output

      NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE polls ClusterIP 10.245.203.186 <none> 8000/TCP 22s

      Esse resultado mostra que o tipo de Serviço é agora ClusterIP. A única maneira de acessá-lo é através do seu domínio e do Ingress criados neste passo.

      Conclusão

      Neste tutorial, você implantou um aplicativo Django dimensionável e seguro via HTTPS em um cluster do Kubernetes. O conteúdo estático é servido diretamente do armazenamento de objetos, e o número de Pods em execução pode ser aumentado ou reduzido rapidamente usando o campo replicas no manifesto de Implantação polls-app.

      Se estiver usando um espaço da DigitalOcean, também é possível habilitar a entrega de ativos estáticos através de uma rede de entrega de conteúdo e criar um subdomínio personalizado para seu espaço. Por favor, consulte Habilitando o CDN de Como configurar um aplicativo Django dimensionável com os bancos de dados e espaços gerenciados da DigitalOcean para aprender mais.

      Para revisar o resto da série, visite nossa página da série De contêineres ao Kubernetes com o Django.



      Source link

      Como habilitar a renderização do servidor para um aplicativo React


      Introdução

      A renderização do servidor (do inglês, SSR) é uma técnica popular para renderizar um aplicativo de página única (do inglês, SPA) no servidor e então enviar uma página totalmente renderizada ao cliente. Isso permite que componentes dinâmicos sejam servidos como uma marcação HTML estática.

      Essa abordagem pode ser útil para o motor de otimização de busca (do inglês, SEO) quando a indexação não manipular o JavaScript corretamente. Ela também pode ser benéfica em situações em que baixar um pacote grande do JavaScript for um processo difícil por conta de uma rede lenta.

      Neste tutorial, você irá inicializar um aplicativo React usando o Create React App e então modificar o projeto para habilitar a renderização do servidor.

      No final deste tutorial, você terá um projeto funcional com um aplicativo React do lado do cliente e um aplicativo Express do lado do servidor.

      Nota: De maneira alternativa, o Next.js oferece uma abordagem moderna para criar aplicativos estáticos e renderizados no servidor desenvolvidos com o React.

      Pré-requisitos

      Para completar este tutorial, você precisará de:

      Este tutorial foi verificado com o Node v14.4.0 e o npm v6.14.5.

      Passo 1 — Criando o aplicativo React e modificando o componente do aplicativo

      Primeiro, vamos usar o npx para iniciar um novo aplicativo React usando a versão mais recente do Create React App.

      Vamos chamar nosso aplicativo my-ssr-app:

      Em seguida, vamos usar o cd para entrar no novo diretório:

      cd my-ssr-app
      

      Por fim, iniciamos nosso novo aplicativo de cliente para verificar a instalação:

      Você deve ver um exemplo de aplicativo do React em exibição na janela do seu navegador.

      Agora, vamos criar um componente <Home>:

      Depois, adicione o código a seguir ao arquivo Home.js:

      src/Home.js

      import React from 'react';
      
      export default props => {
        return <h1>Hello {props.name}!</h1>;
      };
      

      Isso irá criar um cabeçalho <h1> com uma mensagem "Hello" dirigida a um nome.

      Em seguida, vamos renderizar o <Home> no componente <App>. Abra o arquivo App.js:

      Depois disso, substitua as linhas de código existentes por essas novas linhas:

      src/App.js

      import React from 'react';
      import Home from './Home';
      
      export default () => {
        return <Home name="Sammy" />;
      };
      

      Isso passa adiante um name ao componente <Home>, de forma que a mensagem que esperamos receber é "Hello Sammy!".

      No arquivo index.js do nosso aplicativo, vamos usar o método hydrate do ReactDOM ao invés do render, para indicar ao renderizador DOM que estamos reabastecendo o aplicativo após um render do servidor.

      Vamos abrir o arquivo index.js:

      Em seguida, substitua o conteúdo do arquivo index.js pelo seguinte código:

      index.js

      import React from 'react';
      import ReactDOM from 'react-dom';
      import App from './App';
      
      ReactDOM.hydrate(<App />, document.getElementById('root'));
      

      Aqui termina a configuração do lado do cliente. Podemos seguir agora para a configuração do lado do servidor.

      Passo 2 — Criando um servidor Express e renderizando o componente do aplicativo

      Agora que temos nosso aplicativo instalado, vamos configurar um servidor que irá enviar uma versão renderizada. Vamos usar o Express para o nosso servidor. Vamos adicioná-lo ao projeto inserindo o seguinte comando em sua janela de terminal:

      • npm install express@4.17.1

      Ou, usando o yarn:

      Em seguida, crie um diretório server próximo ao diretório src do aplicativo:

      Depois disso, crie um novo arquivo index.js que irá conter o código do servidor Express:

      Adicione as importações necessárias e defina algumas constantes:

      server/index.js

      import path from 'path';
      import fs from 'fs';
      
      import React from 'react';
      import express from 'express';
      import ReactDOMServer from 'react-dom/server';
      
      import App from '../src/App';
      
      const PORT = process.env.PORT || 3006;
      const app = express();
      

      Em seguida, adicione o código do servidor com algum gerenciamento de erro:

      server/index.js

      // ...
      
      app.get('/', (req, res) => {
        const app = ReactDOMServer.renderToString(<App />);
      
        const indexFile = path.resolve('./build/index.html');
        fs.readFile(indexFile, 'utf8', (err, data) => {
          if (err) {
            console.error('Something went wrong:', err);
            return res.status(500).send('Oops, better luck next time!');
          }
      
          return res.send(
            data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
          );
        });
      });
      
      app.use(express.static('./build'));
      
      app.listen(PORT, () => {
        console.log(`Server is listening on port ${PORT}`);
      });
      

      Como pode ver, podemos importar nosso componente <App> do aplicativo cliente diretamente do servidor.

      Três coisas importantes estão acontecendo aqui:

      • Dizemos ao Express para servir conteúdo do diretório build como arquivos estáticos.
      • Usamos um método do ReactDOMServer, o renderToString, para renderizar nosso aplicativo em uma string HTML estática.
      • Depois disso, lemos o arquivo index.html do aplicativo cliente construído, injetamos o conteúdo estático do nosso aplicativo no <div> com um id de "root" e enviamos isso como a resposta à solicitação.

      Passo 3 — Configurando o webpack, Babel e os scripts npm

      Para que o código do nosso servidor funcione, vamos precisar empacotar e transcompila-lo usando o webpack e o Babel. Para fazer isso, vamos adicionar as dependências de desenvolvimento ao projeto inserindo o seguinte comando em sua janela de terminal:

      • npm install webpack@4.42.0 webpack-cli@3.3.12 webpack-node-externals@1.7.2 @babel/core@7.10.4 babel-loader@8.1.0 @babel/preset-env@7.10.4 @babel/preset-react@7.10.4 --save-dev

      Ou, usando o yarn:

      • yarn add webpack@4.42.0 webpack-cli@3.3.12 webpack-node-externals@1.7.2 @babel/core@7.10.4 babel-loader@8.1.0 @babel/preset-env@7.10.4 @babel/preset-react@7.10.4 --dev

      Nota: uma versão anterior deste tutorial instalava o babel-core, babel-preset-env e babel-preset-react-app. Esses pacotes foram arquivados desde então e, ao invés deles, as versões de repositório único são utilizadas.

      Em seguida, crie um arquivo de configuração do Babel:

      Depois disso, adicione as predefinições do env e do react-app:

      .babelrc.json

      {
        "presets": [
          "@babel/preset-env",
          "@babel/preset-react"
        ]
      }
      

      Nota: uma versão anterior deste tutorial usava um arquivo .babelrc (sem extensão de arquivo .json). Esse era um arquivo de configuração para o Babel 6, o que não é mais o caso para o Babel 7.<

      Agora, vamos criar uma configuração do webpack para o servidor que usa o Babel Loader para transcompilar o código. Comece criando o arquivo:

      Depois, adicione as configurações a seguir ao arquivo webpack.server.js:

      webpack.server.js

      const path = require('path');
      const nodeExternals = require('webpack-node-externals');
      
      module.exports = {
        entry: './server/index.js',
      
        target: 'node',
      
        externals: [nodeExternals()],
      
        output: {
          path: path.resolve('server-build'),
          filename: 'index.js'
        },
      
        module: {
          rules: [
            {
              test: /.js$/,
              use: 'babel-loader'
            }
          ]
        }
      };
      

      Com essa configuração, nosso pacote de servidor transcompilado será enviado para a pasta server-build em um arquivo chamado index.js.

      Observe o uso de target: 'node' e externals: [nodeExternals()] de webpack-node-externals, que irá omitir os arquivos de node_modules no pacote. O servidor pode acessar esses arquivos diretamente.

      Isso completa a instalação de dependência e webpack, assim como a configuração do Babel.

      Agora, vamos revisitar o package.json para adicionar scripts auxiliares do npm:

      Vamos adicionar os scripts dev:build-server, dev:start, e dev ao arquivo package.json para compilar e servir nosso aplicativo SSR facilmente:

      package.json

      "scripts": {
        "dev:build-server": "NODE_ENV=development webpack --config webpack.server.js --mode=development -w",
        "dev:start": "nodemon ./server-build/index.js",
        "dev": "npm-run-all --parallel build dev:*",
        ...
      },
      

      Usamos o nodemon para reiniciar o servidor quando fazemos alterações nele. E usamos o npm-run-all para executar vários comandos em paralelo.

      Vamos instalar esses pacotes agora inserindo os seguintes comandos em sua janela de terminal:

      • npm install nodemon@2.0.4 npm-run-all@4.1.5 --save-dev

      Ou, usando o yarn:

      • yarn add nodemon@2.0.4 npm-run-all@4.1.5 --dev

      Com isso no lugar, é possível executar o seguinte para compilar o aplicativo do cliente, empacotar e transcompilar o código do servidor e iniciar o servidor em :3006:

      Ou, usando o yarn:

      Nossa configuração do webpack do servidor irá monitorar alterações e nosso servidor irá reiniciar nas alterações. No entanto, para o aplicativo cliente, ainda precisamos compilá-lo cada vez que fizermos alterações. Há um problema aberto para isso aqui.

      Agora, abra http://localhost:3006/ no seu navegador Web e você verá seu aplicativo do servidor renderizado.

      Anteriormente, o código fonte revelava:

      Output

      <div id="root"></div>

      Mas agora, com as alterações feitas, o código fonte revela:

      Output

      <div id="root"><h1 data-reactroot="">Hello <!-- -->Sammy<!-- -->!</h1></div>

      O renderizador do servidor converteu o componente <App> em HTML com sucesso.

      Conclusão

      Neste tutorial, você inicializou um aplicativo React e habilitou a renderização do servidor.

      Com este post, apenas abordamos o mais básico dentro de tudo aquilo que é possível. As coisas tendem a ficar um pouco mais complicadas quando o encaminhamento, coleta de dados, ou o Redux também precisam fazer parte de um aplicativo renderizado no servidor.

      Um dos principais benefícios de usar o SSR é ter um aplicativo que pode ser rastreado por seu conteúdo, mesmo por rastreadores que não executam código JavaScript. Isso pode ajudar com a otimização de mecanismos de busca (SEO) e fornecendo metadados aos canais de mídia social.

      O SSR também pode ajudar com o desempenho, pois um aplicativo totalmente carregado é enviado do servidor na primeira solicitação. Para aplicativos não triviais, a efetividade desse método pode variar, pois o SSR requer uma configuração que pode ficar um tanto complicada e cria uma carga maior no servidor. Saber quando usar a renderização do servidor para o seu aplicativo React depende de suas necessidades específicas e quais recursos fazem mais sentido para o seu caso de uso.

      Se quiser aprender mais sobre o React, dê uma olhada em nossa série Como programar no React.js, ou confira nossa página do tópico React para exercícios e projetos de programação.



      Source link

      Como adicionar uma autenticação ao seu aplicativo com o Flask-Login


      Introdução

      Permitir que usuários façam login em seu aplicativo é um dos recursos mais comuns a ser adicionado ao seu aplicativo Web. Este artigo irá abordar como adicionar uma autenticação ao seu aplicativo Flask com o pacote Flask-Login.

      Gif animado do aplicativo Flask e caixa de login

      Construiremos algumas páginas de inscrição e login que permitem que os usuários façam login e acessem páginas protegidas que usuários não autenticados são incapazes de visualizar. Coletaremos informações do modelo de usuário e as exibiremos em nossas páginas protegidas quando o usuário fizer login, de forma a simular como seria um perfil.

      Iremos abordar os seguintes assuntes neste artigo:

      • Como usar a biblioteca Flask-Login para o gerenciamento de sessão
      • Como usar o utilitário integrado do Flask para o hash de senhas
      • Como adicionar páginas protegidas ao nosso aplicativo acessíveis somente por usuários autenticados
      • Como usar o Flask-SQLAlchemy para criar um modelo de usuário
      • Como criar formulários de inscrição e login para que nossos usuários criem contas e façam login
      • Como exibir mensagens de erro aos usuários quando algo der errado
      • Como usar informações da conta do usuário para exibi-las na página do perfil

      O código fonte para este projeto está disponível no GitHub.

      Pré-requisitos

      Para concluir este tutorial, você precisará do seguinte:

      Nosso aplicativo irá utilizar os padrões de fábrica do aplicativo Flask com blueprints. Teremos um blueprint que processa tudo que está relacionado à autenticação e outro para nossas rotas regulares, que incluem o índice e a página de perfil protegida. Em um aplicativo real, é possível desmembrar as funcionalidades da maneira que achar melhor, mas a solução abordada aqui irá funcionar bem para este tutorial.

      Aqui está um diagrama para entendermos melhor como ficará a estrutura de arquivos do seu projeto após completar o tutorial:

      .
      └── flask_auth_app
          └── project
              ├── __init__.py       # setup our app
              ├── auth.py           # the auth routes for our app
              ├── db.sqlite         # our database
              ├── main.py           # the non-auth routes for our app
              ├── models.py         # our user model
              └── templates
                  ├── base.html     # contains common layout and links
                  ├── index.html    # show the home page
                  ├── login.html    # show the login form
                  ├── profile.html  # show the profile page
                  └── signup.html   # show the signup form
      

      À medida que avançarmos neste tutorial, iremos criar esses diretórios e arquivos.

      Passo 1 — Instalando os pacotes

      Existem três pacotes principais que precisamos para o nosso projeto:

      • Flask
      • Flask-Login: para processar as sessões de usuário após a autenticação
      • Flask-SQLAlchemy: para representar o modelo de usuário e interagir com nosso banco de dados

      Iremos utilizar o SQLite para evitar a instalação de dependências extras para o banco de dados.

      Primeiro, começaremos criando o diretório de projeto:

      Em seguida, precisamos navegar até o diretório de projeto:

      Crie um ambiente Python caso não tenha um. Dependendo da forma como o Python foi instalado em sua máquina, seus comandos serão semelhantes a:

      • python3 -m venv auth
      • source auth/bin/activate

      Nota: consulte o tutorial relevante ao seu ambiente de trabalho para configurar o venv.

      Execute os comandos a seguir a partir do seu ambiente virtual para instalar os pacotes necessários:

      • pip install flask flask-sqlalchemy flask-login

      Agora que você instalou os pacotes, tudo está pronto para a criação do arquivo principal do aplicativo.

      Passo 2 — Criando o arquivo principal do aplicativo

      Vamos começar criando um diretório project:

      O primeiro arquivo no qual trabalharemos será o arquivo __init__.py para o nosso projeto:

      Esse arquivo será responsável pela criação do nosso aplicativo, inicializando o banco de dados e registrando nossos blueprints. Neste momento, aparentemente nada irá mudar. No entanto, isso será necessário para o resto do nosso aplicativo. Precisamos inicializar o SQLAlachemy, definir alguns valores de configuração e registrar nossos blueprints aqui.

      project/__init__.py

      from flask import Flask
      from flask_sqlalchemy import SQLAlchemy
      
      # init SQLAlchemy so we can use it later in our models
      db = SQLAlchemy()
      
      def create_app():
          app = Flask(__name__)
      
          app.config['SECRET_KEY'] = 'secret-key-goes-here'
          app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
      
          db.init_app(app)
      
          # blueprint for auth routes in our app
          from .auth import auth as auth_blueprint
          app.register_blueprint(auth_blueprint)
      
          # blueprint for non-auth parts of app
          from .main import main as main_blueprint
          app.register_blueprint(main_blueprint)
      
          return app
      

      Agora que temos o arquivo principal do aplicativo, podemos começar a adicionar nossas rotas.

      Passo 3 — Adicionando rotas

      Para nossas rotas, iremos utilizar dois blueprints. Para o nosso blueprint principal, teremos uma página inicial (/) e uma página de perfil (/profile) para depois da sessão ser iniciada. Se o usuário tentar acessar a página de perfil sem estar autenticado, ele será enviado para a rota de login.

      Para o nosso blueprint de autenticação, teremos rotas para recuperar tanto a página de login (/login) quanto a página de inscrição (/sign-up). Também teremos rotas para lidar com as solicitações POST de ambas as rotas. Por fim, teremos uma rota de logoff (/logout) para efetuar o logoff de um usário ativo.

      Por enquanto, iremos definir login, signup e logout com devoluções simples. Iremos revisitá-los em um passo posterior e atualizá-los com a funcionalidade desejada.

      Primeiro, crie o main.py para o seu main_blueprint:

      project/main.py

      from flask import Blueprint
      from . import db
      
      main = Blueprint('main', __name__)
      
      @main.route('/')
      def index():
          return 'Index'
      
      @main.route('/profile')
      def profile():
          return 'Profile'
      

      Em seguida, crie o auth.py para o seu auth_blueprint:

      project/auth.py

      from flask import Blueprint
      from . import db
      
      auth = Blueprint('auth', __name__)
      
      @auth.route('/login')
      def login():
          return 'Login'
      
      @auth.route('/signup')
      def signup():
          return 'Signup'
      
      @auth.route('/logout')
      def logout():
          return 'Logout'
      

      Em um terminal, defina os valores FLASK_APP e FLASK_DEBUG:

      • export FLASK_APP=project
      • export FLASK_DEBUG=1

      A variável de ambiente FLASK_APP instrui o Flask sobre como carregar o aplicativo. Ela deve apontar para o local onde o create_app está localizado. Para o nosso caso, iremos apontar para o diretório project.

      A variável de ambiente FLASK_DEBUG é habilitada quando definida em 1. Isso irá habilitar um depurador que exibirá erros do aplicativo no navegador.

      Certifique-se de estar no diretório flask_auth_app e então execute o projeto:

      Agora, em um navegador, deve ser possível navegar até as cinco URLs possíveis e ver o texto retornado que foi definido em auth.py e main.py.

      Por exemplo, visitar o localhost:5000/profile exibe Profile:

      Captura de tela do projeto em localhost porta 5000 no navegador

      Agora que verificamos que nossas rotas estão se comportando conforme esperado, podemos seguir em frente para a criação de modelos.

      Passo 4 — Criando modelos

      Vamos continuar e criar os modelos usados em nosso aplicativo. Este é o primeiro passo antes de podermos implementar de fato a funcionalidade de login. Nosso aplicativo irá utilizar quatro modelos:

      • index.html
      • profile.html
      • login.html
      • signup.html

      Além disso, teremos um modelo base que terá partes de código comuns a cada uma das páginas. Neste caso, o modelo base terá links de navegação e o layout geral da página. Vamos criá-los agora.

      Primeiro, crie um diretório templates no seu diretório project:

      • mkdir -p project/templates

      Em seguida, crie o base.html:

      • nano project/templates/base.html

      Depois, adicione o código a seguir ao arquivo base.html:

      project/templates/base.html

      <!DOCTYPE html>
      <html>
      
      <head>
          <meta charset="utf-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <title>Flask Auth Example</title>
          <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css" />
      </head>
      
      <body>
          <section class="hero is-primary is-fullheight">
      
              <div class="hero-head">
                  <nav class="navbar">
                      <div class="container">
      
                          <div id="navbarMenuHeroA" class="navbar-menu">
                              <div class="navbar-end">
                                  <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("main.index') }}" class="navbar-item">
                                      Home
                                  </a>
                                  <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("main.profile') }}" class="navbar-item">
                                      Profile
                                  </a>
                                  <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.login') }}" class="navbar-item">
                                      Login
                                  </a>
                                  <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.signup') }}" class="navbar-item">
                                      Sign Up
                                  </a>
                                  <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.logout') }}" class="navbar-item">
                                      Logout
                                  </a>
                              </div>
                          </div>
                      </div>
                  </nav>
              </div>
      
              <div class="hero-body">
                  <div class="container has-text-centered">
                     {% block content %}
                     {% endblock %}
                  </div>
              </div>
          </section>
      </body>
      
      </html>
      

      Esse código irá criar uma série de links de menu para cada página do aplicativo e uma área onde o conteúdo irá aparecer.

      Nota: nos bastidores, estamos usando o Bulma para lidar com a estilização e o layout. Para entender mais sobre o Bulma, leia a documentação oficial do Bulma.

      Em seguida, crie o templates/index.html:

      • nano project/templates/index.html

      Adicione o código a seguir ao arquivo recém-criado para adicionar conteúdo à página:

      project/templates/index.html

      {% extends "base.html" %}
      
      {% block content %}
      <h1 class="title">
        Flask Login Example
      </h1>
      <h2 class="subtitle">
        Easy authentication and authorization in Flask.
      </h2>
      {% endblock %}
      

      Esse código irá criar um uma página básica de índice com um título e um subtítulo.

      Em seguida, crie o templates/login.html:

      • nano project/templates/login.html

      Esse código gera uma página de login com campos para E-mail e Senha. Há também uma caixa de seleção para “lembrar” uma sessão iniciada.

      project/templates/login.html

      {% extends "base.html" %}
      
      {% block content %}
      <div class="column is-4 is-offset-4">
          <h3 class="title">Login</h3>
          <div class="box">
              <form method="POST" action="/login">
                  <div class="field">
                      <div class="control">
                          <input class="input is-large" type="email" name="email" placeholder="Your Email" autofocus="">
                      </div>
                  </div>
      
                  <div class="field">
                      <div class="control">
                          <input class="input is-large" type="password" name="password" placeholder="Your Password">
                      </div>
                  </div>
                  <div class="field">
                      <label class="checkbox">
                          <input type="checkbox">
                          Remember me
                      </label>
                  </div>
                  <button class="button is-block is-info is-large is-fullwidth">Login</button>
              </form>
          </div>
      </div>
      {% endblock %}
      

      Em seguida, crie o templates/signup.html:

      • nano project/templates/signup.html

      Adicione o código a seguir para criar uma página de inscrição com campos para e-mail, nome e senha:

      project/templates/signup.html

      {% extends "base.html" %}
      
      {% block content %}
      <div class="column is-4 is-offset-4">
          <h3 class="title">Sign Up</h3>
          <div class="box">
              <form method="POST" action="/signup">
                  <div class="field">
                      <div class="control">
                          <input class="input is-large" type="email" name="email" placeholder="Email" autofocus="">
                      </div>
                  </div>
      
                  <div class="field">
                      <div class="control">
                          <input class="input is-large" type="text" name="name" placeholder="Name" autofocus="">
                      </div>
                  </div>
      
                  <div class="field">
                      <div class="control">
                          <input class="input is-large" type="password" name="password" placeholder="Password">
                      </div>
                  </div>
      
                  <button class="button is-block is-info is-large is-fullwidth">Sign Up</button>
              </form>
          </div>
      </div>
      {% endblock %}
      

      Em seguida, crie o templates/profile.html:

      • nano project/templates/profile.html

      Adicione este código para criar uma página simples com um título embutido em código para dar boas vindas ao Anthony:

      project/templates/profile.html

      {% extends "base.html" %}
      
      {% block content %}
      <h1 class="title">
        Welcome, Anthony!
      </h1>
      {% endblock %}
      

      Posteriormente, iremos adicionar um código para saudar dinamicamente qualquer usuário.

      Assim que os modelos forem adicionados, podemos atualizar as declarações de retorno em cada uma de nossas rotas para que retornem os modelos ao invés do texto.

      Em seguida, atualize o main.py alterando a linha de importação e as rotas para o index e profile:

      project/main.py

      from flask import Blueprint, render_template
      ...
      @main.route('/')
      def index():
          return render_template('index.html')
      
      @main.route('/profile')
      def profile():
          return render_template('profile.html')
      

      Agora, você irá atualizar o auth.py modificando a linha de importação e rotas para login e signup:

      project/auth.py

      from flask import Blueprint, render_template
      ...
      @auth.route('/login')
      def login():
          return render_template('login.html')
      
      @auth.route('/signup')
      def signup():
          return render_template('signup.html')
      

      Assim que as alterações forem feitas, a página de inscrição ficará desta forma, caso navegue até /sign-up:

      Página de inscrição em /signup

      Também deve ser possível ver as páginas para /, /login e /profile.

      Não iremos alterar /logout por enquanto, pois ela não irá exibir um modelo quando for finalizada.

      Passo 5 — Criando modelos de usuário

      Nosso modelo de usuário representa o que significa para o nosso aplicativo ter um usuário. Teremos campos para um endereço de e-mail, senha e nome. Em seu aplicativo, fique a vontade para decidir se quer que mais informações sejam armazenadas por usuário. É possível adicionar coisas como aniversário, imagem de perfil, localização ou qualquer preferência do usuário.

      Os modelos criados no Flask-SQLAlchemy são representados por classes que então se traduzem em tabelas em um banco de dados. Os atributos dessas classes transformam-se então em colunas para essas tabelas.

      Vamos continuar e criar o modelo de usuário:

      Este código cria um modelo de usuário com colunas para um id, email, password e name:

      project/models.py

      from . import db
      
      class User(db.Model):
          id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy
          email = db.Column(db.String(100), unique=True)
          password = db.Column(db.String(100))
          name = db.Column(db.String(1000))
      

      Agora que um modelo de usuário foi criado, continue para a configuração do seu banco de dados.

      Passo 6 — Configurando o banco de dados

      Como mencionado nos pré-requisitos, iremos utilizar um banco de dados SQLite. Poderíamos criar um banco de dados SQLite por conta própria, mas vamos deixar o Flask-SQLAlchemy fazer isso por nós. Nós já temos o caminho do banco de dados especificado no arquivo __init__.py. Portanto, precisamos apenas dizer ao Flask-SQLAlchemy para criar o banco de dados no REPL do Python.

      Se você parar o aplicativo e abrir um REPL do Python, seremos capazes de criar o banco de dados usando o método create_all no objeto db. Certifique-se de que você ainda esteja no ambiente virtual e no diretório flask_auth_app.

      • from project import db, create_app
      • db.create_all(app=create_app()) # pass the create_app result so Flask-SQLAlchemy gets the configuration.

      Nota: caso usar o interpretador do Python seja algo novo para você, consulte a documentação oficial.

      Agora, você verá um arquivo db.sqlite em seu diretório de projeto. Esse banco de dados terá a nossa tabela de usuário em seu interior.

      Passo 7 — Configurando a função de autorização

      Para a nossa função de inscrição, enviaremos os dados que o usuário digita no formulário ao nosso banco de dados. Antes de adicioná-la, precisamos garantir que o usuário não exista no banco de dados. Se ele não existir, então precisamos nos certificar que a senha passará pelo hash antes de colocá-la no banco de dados, pois não queremos que nossas senhas sejam armazenadas em texto simples.

      Vamos começar adicionando uma segunda função para lidar com os dados do formulário POST. Nesta função, coletaremos dados passados pelo usuário primeiro.

      Crie a função e adicione um redirecionamento no final. Isso proporcionará uma experiência de usuário de uma inscrição bem-sucedida e direcionamento à Página de login.

      Atualize o auth.py alterando a linha de importação e implementando o signup_post:

      project/auth.py

      from flask import Blueprint, render_template, redirect, url_for
      ...
      @auth.route('/signup', methods=['POST'])
      def signup_post():
          # code to validate and add user to database goes here
          return redirect(url_for('auth.login'))
      

      Agora, vamos adicionar o resto do código necessário para a inscrição de um usuário.

      Para começar, será necessário usar o objeto de solicitação para obter os dados do formulário.

      Continue atualizando o auth.py adicionando as importações e implementando o signup_post:

      auth.py

      from flask import Blueprint, render_template, redirect, url_for, request
      from werkzeug.security import generate_password_hash, check_password_hash
      from .models import User
      from . import db
      ...
      @auth.route('/signup', methods=['POST'])
      def signup_post():
          email = request.form.get('email')
          name = request.form.get('name')
          password = request.form.get('password')
      
          user = User.query.filter_by(email=email).first() # if this returns a user, then the email already exists in database
      
          if user: # if a user is found, we want to redirect back to signup page so user can try again
              return redirect(url_for('auth.signup'))
      
          # create a new user with the form data. Hash the password so the plaintext version isn't saved.
          new_user = User(email=email, name=name, password=generate_password_hash(password, method='sha256'))
      
          # add the new user to the database
          db.session.add(new_user)
          db.session.commit()
      
          return redirect(url_for('auth.login'))
      

      Nota: armazenar senhas em texto simples é considerada uma prática de segurança ruim. De maneira geral, é vantajoso utilizar um algoritmo de hash complexo e um valor de sal de senha para manter as senhas em segurança.

      Passo 8 — Testando o método de inscrição

      Agora que o método de inscrição está finalizado, devemos ser capazes de criar um novo usuário. Use o formulário para criar um usuário.

      Existem duas maneiras de verificar se a inscrição foi bem-sucedida: usando o visualizador do banco de dados para observar a linha que foi adicionada à sua tabela, ou tentando inscrever-se com o mesmo e-mail novamente e, caso receba um erro, saberá que o primeiro e-mail foi salvo corretamente. Portanto, vamos tomar esta segunda abordagem.

      Podemos adicionar um código para informar ao usuário que o e-mail já existe e pedir para que vá à página de login. Ao chamar a função flash, iremos enviar uma mensagem para a próxima solicitação, que, neste caso, é o redirecionamento. Dessa forma, a página em que chegarmos terá acesso a essa mensagem no modelo.

      Primeiro, adicionamos o flash antes de redirecionarmos o processo de volta à nossa página de inscrição.

      project/auth.py

      from flask import Blueprint, render_template, redirect, url_for, request, flash
      ...
      @auth.route('/signup', methods=['POST'])
      def signup_post():
          ...
          if user: # if a user is found, we want to redirect back to signup page so user can try again
              flash('Email address already exists')
              return redirect(url_for('auth.signup'))
      

      Para o recebimento da mensagem de flash no modelo, podemos adicionar este código acima do formulário. Isso fará com que a mensagem seja exibida diretamente acima do formulário.

      project/templates/signup.html

      ...
      {% with messages = get_flashed_messages() %}
      {% if messages %}
          <div class="notification is-danger">
              {{ messages[0] }}. Go to <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.login') }}">login page</a>.
          </div>
      {% endif %}
      {% endwith %}
      <form method="POST" action="/signup">
      

      Caixa de inscrição exibindo uma mensagem de que o

      Passo 9 — Adicionando o método de login

      O método de login é semelhante à função de inscrição, já que também iremos pegar informações do usuário para fazer algo com elas. Neste caso, iremos analisar o endereço de e-mail inserido para checar se ele está presente no banco de dados. Caso esteja, iremos testar a senha fornecida pelo usuário utilizando o hash na senha passada pelo usuário e comparando-a com a senha já com hash no banco de dados. Sabemos que o usuário digitou a senha correta quando ambas as senhas com hash correspondem.

      Assim que o usuário passar pela checagem de senha, sabemos que ele possui as credenciais corretas e podemos autenticá-los usando o Flask-Login. Ao chamar o login_user, o Flask-Login irá criar uma sessão para aquele usuário que irá persistir enquanto o usuário permanecer conectado. Isso permitirá que o usuário visualize páginas protegidas.

      Podemos começar com uma nova rota para o manuseio dos dados em POST. Redirecionaremos o usuário para a página de perfil quando o login for realizado com sucesso:

      project/auth.py

      ...
      @auth.route('/login', methods=['POST'])
      def login_post():
          # login code goes here
          return redirect(url_for('main.profile'))
      

      Agora, é necessário verificar se o usuário possui as credenciais corretas:

      project/auth.py

      ...
      @auth.route('/login', methods=['POST'])
      def login_post():
          email = request.form.get('email')
          password = request.form.get('password')
          remember = True if request.form.get('remember') else False
      
          user = User.query.filter_by(email=email).first()
      
          # check if the user actually exists
          # take the user-supplied password, hash it, and compare it to the hashed password in the database
          if not user or not check_password_hash(user.password, password):
              flash('Please check your login details and try again.')
              return redirect(url_for('auth.login')) # if the user doesn't exist or password is wrong, reload the page
      
          # if the above check passes, then we know the user has the right credentials
          return redirect(url_for('main.profile'))
      

      Vamos adicionar o bloco de código no modelo para que o usuário possa ver a mensagem em flash. Assim como no formulário de inscrição, vamos adicionar a mensagem de erro em potencial diretamente acima do formulário:

      project/templates/login.html

      ...
      {% with messages = get_flashed_messages() %}
      {% if messages %}
          <div class="notification is-danger">
              {{ messages[0] }}
          </div>
      {% endif %}
      {% endwith %}
      <form method="POST" action="/login">
      

      Agora, somos capazes de dizer que um usuário foi conectado com sucesso, mas não há nada para fazer o login do usuário. É aqui onde trazemos o Flask-Login para o gerenciamento de sessões de usuário.

      Antes de iniciarmos, precisamos de algumas coisas para que o Flask-Login funcione. Comece adicionando o UserMixin ao seu Modelo de usuário. O UserMixin irá adicionar atributos do Flask-Login ao modelo para que o Flask-Login seja capaz de trabalhar com ele.

      models.py

      from flask_login import UserMixin
      from . import db
      
      class User(UserMixin, db.Model):
          id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy
          email = db.Column(db.String(100), unique=True)
          password = db.Column(db.String(100))
          name = db.Column(db.String(1000))
      

      Em seguida, é necessário especificar nosso carregador de usuário. Um carregador de usuário informa ao Flask-Login como encontrar um usuário específico a partir do ID armazenado em seu cookie de sessão. Podemos adicionar isso em nossa função create_app, juntamente com o código init para o Flask-Login:

      project/__init__.py

      ...
      from flask_login import LoginManager
      ...
      def create_app():
          ...
          db.init_app(app)
      
          login_manager = LoginManager()
          login_manager.login_view = 'auth.login'
          login_manager.init_app(app)
      
          from .models import User
      
          @login_manager.user_loader
          def load_user(user_id):
              # since the user_id is just the primary key of our user table, use it in the query for the user
              return User.query.get(int(user_id))
      

      Por fim, adicionamos a função login_user um pouco antes de redirecionarmos o usuário para a página de perfil para criar a sessão:

      project/auth.py

      from flask_login import login_user
      from .models import User
      ...
      @auth.route('/login', methods=['POST'])
      def login_post():
          ...
          # if the above check passes, then we know the user has the right credentials
          login_user(user, remember=remember)
          return redirect(url_for('main.profile'))
      

      Com o Flask-Login configurado, podemos usar a rota /login. Quando tudo estiver no lugar, você verá a página de perfil.

      Página de perfil com

      Passo 10 — Protegendo as páginas

      Se o seu nome não é Anthony, então verá que a mensagem está errada. O que queremos é que o perfil exiba o nome presente no banco de dados. Portanto, primeiro, é necessário proteger a página e então acessar os dados do usuário para obter o seu nome.

      Para proteger uma página ao usar o Flask-Login, adicionamos o decorador @login_requried entre a rota e a função. Isso impede que um usuário que não esteja conectado veja a rota. Se o usuário não estiver conectado, ele será redirecionado para a página de login, conforme a configuração do Flask-Login.

      Com as rotas decoradas com o decorador @login_required, somos capazes de usar o objeto current_user dentro da função. O current_user representa o usuário do banco de dados e podemos acessar todos os atributos desse usuário com uma notação de ponto. Por exemplo, current_user.email, current_user.password, current_user.name e current_user.id irão retornar os valores reais armazenados no banco de dados para o usuário conectado.

      Vamos usar o nome do usuário atual e enviá-lo ao modelo. Em seguida, usaremos esse nome e exibiremos seu valor.

      project/main.py

      from flask_login import login_required, current_user
      ...
      @main.route('/profile')
      @login_required
      def profile():
          return render_template('profile.html', name=current_user.name)
      

      Depois disso, no arquivo profile.html, atualize a página para exibir o valor name:

      project/templates/profile.html

      ...
      <h1 class="title">
        Welcome, {{ name }}!
      </h1>
      

      Ao acessarmos nossa página de perfil, vemos agora que o nome do usuário aparece.

      Página de boas-vindas com o nome do usuário atualmente conectado

      A última coisa que podemos fazer é atualizar a visualização de logoff. Podemos chamar a função logout_user em uma rota para o logoff. Adicionamos o decorador @login_required porque não faz sentido fazer logoff de um usuário que não esteja conectado em primeiro lugar.

      project/auth.py

      from flask_login import login_user, logout_user, login_required
      ...
      @auth.route('/logout')
      @login_required
      def logout():
          logout_user()
          return redirect(url_for('main.index'))
      

      Depois de fazer o logoff, se tentarmos visualizar a página de perfil novamente, vemos que uma mensagem de erro aparece. Isso ocorre porque o Flask-Login exibe uma mensagem quando o usuário não é autorizado a acessar uma página.

      Página de login com uma mensagem mostrando que o usuário deve fazer login para acessar a página

      Uma última coisa que podemos fazer é colocar declarações if nos modelos para que apenas os links relevantes ao usuário sejam exibidos. Assim, antes do usuário fazer login, haverá a opção de fazer login ou se inscrever. Após ter feito o login, é possível ir ao perfil ou fazer logoff:

      templates/base.html

      ...
      <div class="navbar-end">
          <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("main.index') }}" class="navbar-item">
              Home
          </a>
          {% if current_user.is_authenticated %}
          <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("main.profile') }}" class="navbar-item">
              Profile
          </a>
          {% endif %}
          {% if not current_user.is_authenticated %}
          <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.login') }}" class="navbar-item">
              Login
          </a>
          <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.signup') }}" class="navbar-item">
              Sign Up
          </a>
          {% endif %}
          {% if current_user.is_authenticated %}
          <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.logout') }}" class="navbar-item">
              Logout
          </a>
          {% endif %}
      </div>
      

      Página inicial com as opções Home, Login e Sign Up no topo da tela

      Com isso, você terminou de construir seu aplicativo de autenticação com sucesso.

      Conclusão

      Utilizamos neste tutorial o Flask-Login e o Flask-SQLAlchemy para construir um sistema de login para o nosso aplicativo e abordamos como autenticar um usuário. Primeiro, criamos um modelo de usuário e armazenamos suas informações. Em seguida, foi necessário verificar se a senha do usuário estava correta utilizando o hash na senha do formulário e comparando-a com a armazenada no banco de dados. Por fim, adicionamos a autorização ao nosso aplicativo usando o decorador @login_required em uma página de perfil para que apenas usuários conectados possam ver essa página.

      O que criamos neste tutorial será o suficiente para aplicativos menores, mas se você quiser ter mais funcionalidades logo no início, considere usar as bibliotecas Flask-User ou Flask-Security, que são desenvolvidas com base na biblioteca Flask-Login.



      Source link