Artigo original: How to Deploy a React App to Production Using Docker and NGINX with API Proxies

Neste artigo, você vai aprender como implementar (eminglês, deploy) uma aplicação desenvolvida com React em produção. Vamos utilizar o Docker e o NGINX para assegurar nossas chaves de acesso da API e requisições de proxy, a fim de prevenir ataques de Cross-Origin Resource Sharing (CORS).

No final do artigo, você pode conferir o código e um vídeo relacionado.

O que você vai aprender neste artigo

Em todo ciclo de vida do projeto de software, chega a hora de publicá-lo. Porém, nem sempre é óbvio como fazê-lo. O ambiente de produção é diferente do ambiente de desenvolvimento. A maioria das aplicações consome algum tipo de API – por vezes, hospedada em diferentes servidores.

Como desenvolvedores, precisamos resolver potenciais riscos de CORS. Às vezes, acabamos construindo um back-end desnecessariamente. Eu acredito que desenvolvedores devem manter suas aplicações simples, cortando todas as partes redundantes.

Neste artigo, vou mostrar como eu preparo minhas aplicações desenvolvidas em React para implantar em produção.

Eu poderia demonstrar uma aplicação simples, mas isso seria pouco útil. Portanto, decidi criar uma aplicação que consome dados de uma API real, a fornecida pela Divisão de Pesquisa Econômica do Federal Reserve Bank of Saint Louis. A API requer uma chave de acesso para obter dados, e os endpoints estão protegidos contra vulnerabilidades de CORS.

Observação: se sua aplicação é baseada em rederização no servidor (SSR, em inglês, server-side rendering), esta não é a melhor estratégia de implantação. Você, provavelmente, precisará de algum tipo de back-end.

Pré-requisitos

É importante que você tenha conhecimento básico de como construir aplicações em React e conheça os fundamentos de Docker antes de seguir as instruções deste artigo. Caso você precise revisar esses conteúdos, acesse esses conteúdos do FreeCodeCamp (ambos em inglês):

Construindo a aplicação em React

Eu criei uma aplicação simples usando o comando create-react-app. A única funcionalidade da aplicação será exibir um gráfico que representa o produto interno bruto (PIB) dos Estados Unidos da América.

Observação:  a documentação legada do React recomenda o uso do create-react-app para aplicações de página única (SPA– do inglês, single page applications) ou para quem esta aprendendo React.

fredapp

A aplicação obtém dados somente desta API:

https://api.stlouisfed.org/fred/series/observations?series_id=GDPCA&frequency=a&observation_start=1999-04-15&observation_end=2021-01-01&file_type=json&api_key=abcdefghijklmnopqrstuvwxyz123456

Os parametros são:

  • series_id – o identificador das séries. GDPCA se refere à "GDP real".
  • frequency – a agregação do dado. O a se refere à "anual".
  • observation_start – o início do período observado.
  • observation_end – o fim do período observado.
  • file_type – o formato do dado. O padrão é xml.
  • api_key – A chave da API é requerida para obter dados. Você pode solicitar uma chave de acesso aqui: veja uma aqui.

Para mais detalhes, consulte a documentação.

Como nem tudo é perfeito, o projeto da API também não é. Nessa API, é necessário que o desenvolvedor envie a chave de acesso e o retorno vem como parâmetros de URL.

Enviar o código como parâmetro não é um problema para nós. Entretanto, o vazamento da chave da API pode ser um problema! Imagine que alguém pode interceptar a busar da API, realizando procedimentos abusivos. Ninguém quer correr esse risco, certo?

Vamos assumir que a chave da API não é um problema. Ainda assim, é possível abusar da API. A API FRED tem proteção contra requisições interdomínio (do inglês, cross-domain request). Portanto, você obtém os seguintes erros se tentar acessá-la de um domínio externo:

frederror

Muitos desenvolvedores podem sugerir o emprego de uma aplicação intermediária (middleware) para requisitar a API e filtrar conteúdo sensível via proxy. Podem dizer que precisam ampliar as funcionalidades futuras e, de certa maneira, essa é uma boa abordagem.

Entretanto, eu prefiro construir minhas aplicações seguindo um dos princípios da vertente da Programação Extrema (do inglês, Extreme Programming). Um dos tantos princípios se trata de "você não vai precisar disso", que prega evitar a adição de muitas funcionalidades, principalmente as que "você talvez nunca use" (YAGNI way). Portanto, vou evitar construir um back-end até ser necessário, ou no nosso caso, "nunca ser necessário".

Vamos usar o NGINX!

Eu sou fã do NGINX porque ele traz a simplicidade de uso. O NGINX permite que você prepare um servidor web de nível de produção, com funcionalidade HTTP2, compressão, TLS etc.

Podemos chegar a configurações robustas com poucas linhas de código. Veja o exemplo abaixo:

...

http {
    ...

    server {
        ...

        location /api {
            set         $args   $args&&file_type=json&api_key=abcdefghijklmnopqrstuvwxyz123456;
            proxy_pass  https://api.stlouisfed.org/fred/series;
        }
    }
}

Essas quatro linhas de código são todo o necessário para ocultar nossa chave API e suprimir errros de CORS. Agora, todo o tráfego HTTP para /api será enviado para a FRED API e apenas a nossa aplicação esta habilitada a consumir estes dados. Todas as outras requisições externas vão obter erro de CORS.

Para se livrar dos excessos, eu troquei todo o conteúdo do arquivo por .... Você pode encontrar a versão completa no meu GitHub ou no vídeo (links abaixo).

Agora, nosso ponto de acesso (endpoint) é:

/api/observations?series_id=GDPCA&frequency=a&observation_start=1999-04-15&observation_end=2021-01-01

Agora não é necessário passar os parâmetros api_key nem file_type para obter dados. Além disso, ninguém pode acessar a chave de acesso a partir do URL, o que torna o processo seguro.

Screen-Shot-2021-03-14-at-12.49.55

O Docker prefere o NGINX!

A maneira mais conveniente de executar o NGINX na nuvem é usando o Docker. Para essa parte, assumo que você saiba o que é o Docker (mas se não souber, leia o artigo vinculado nos pré-requisitos).
Antes, precisamos criar um Dockerfile com o seguinte conteúdo:

FROM nginx

COPY container /
COPY build /usr/share/nginx/html

Agora só restam esses três passos:

  1. Construir a aplicação em React. Esse processo gera o diretório build/, contendo os arquivos estáticos.
  2. Construir a imagem do Docker. Isso criará uma imagem do Docker executável.
  3. Publicar a imagem do Docker em algum repositório ou executá-la localmente.

Por enquanto, vamos tentar executá-la em nossa máquina.

$ yarn install
$ yarn build
$ docker build -t msokola/fred-app:latest .
$ docker run -p 8081:80 -it msokola/fred-app:latest

A porta 8081 é uma porta local e em sua máquina. Isso significa que a aplicação estará disponível no seguinte URL: http://localhost:8081.

Após abrir esse URL no navegador, você deve ver registros como estes em seu terminal:

0.0.0.1 - - [11/Mar/2021:18:57:50 +0000] "GET / HTTP/1.1" 200 1556 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36" "-"
...
0.0.0.1 - - [11/Mar/2021:18:57:51 +0000] "GET /api/observations?series_id=GDPCA&frequency=a&observation_start=1999-04-15&observation_end=2021-01-01 HTTP/1.1" 200 404 "http://localhost:8081/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36" "-"

Observe os códigos 200, pois eles representam o status HTTP OK. Se você vir um 400 ao lado da solicitação da API, significa que algo está errado com sua chave de API. O código 304 também é válido (isso significa que os dados foram armazenados em cache).

Como fazer o deploy do contêiner na AWS

O contêiner está funcionando, então podemos implementá-lo. Nessa parte do artigo, vou mostrar como executar sua aplicação na Amazon Web Services (AWS). A AWS é uma das plataformas de nuvem mais populares. Se você deseja usar o Microsoft Azure ou qualquer outra plataforma, os passos serão semelhantes, mas a sintaxe dos comandos será diferente.

Observação: gravei um vídeo no YouTube para que você possa me ver passando por todo o processo de implantação. Se você ficar preso ou encontrar problemas, pode verificar se obtemos os mesmos resultados a cada etapa. Se quiser assistir ao vídeo, clique aqui (vídeo em inglês).

1. Instale as ferramentas de CLI da AWS

Antes de começarmos, você precisará instalar as ferramentas de CLI da AWS para poder digitar comandos em sua nuvem. A AWS oferece assistentes de instalação para todos os sistemas operacionais, então vou pular essa seção. Após uma instalação bem-sucedida, você precisará fazer login digitando o seguinte comando:

$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-east-2
Default output format [None]: json

Para gerar chaves de acesso, você precisa fazer login no Console da AWS. Lá, clique em seu nome de usuário e selecione My Security Credentials (em português, "Minhas Credenciais de Segurança").

Screenshot-2021-03-16-at-22.27.53-1

2. Crie um Elastic Container Registry (ECR)

Assim que as ferramentas da CLI estiverem configuradas, precisaremos criar um espaço onde podemos armazenar os executáveis de nossa aplicação. Usamos o Docker, então nossos executáveis serão imagens do Docker que serão executadas em máquinas virtuais.
A AWS oferece um serviço dedicado para armazenar imagens chamado Elastic Container Registry. O comando a seguir criará um para nós:

aws ecr create-repository --repository-name react-to-aws --region us-east-2

Aqui estão os parâmetros:

  • ecr – acrônimo para "Elastic Container Registry".
  • repository-name – o nome do nosso registro. Observe que utilizaremos esse nome posteriormente.
  • region – código da região. Você pode encontrar a região mais próxima da sua para reduzir a laência da comunicação. Aqui há uma lista de todas as regiões.

Para mais detalhes, consulte a documentação.

Aqui está a saída do console:

{
    "repository": {
        "repositoryArn": "arn:aws:ecr:us-east-2:1234567890:repository/react-to-aws2",
        "registryId": "1234567890",
        "repositoryName": "react-to-aws",
        "repositoryUri": "1234567890.dkr.ecr.us-east-2.amazonaws.com/react-to-aws2",
        "createdAt": "2021-03-16T22:50:23+04:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": false
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}

3. Envie as imagens do Docker para a nuvem

Nesta etapa, vamos enviar nossas imagens do Docker para a nuvem. Podemos fazer isso copiando os comandos de envio de nosso Console AWS.
Vamos abrir o Console AWS no navegador e clicar em Elastic Container Registry na lista All Services - Containers (Todos os Serviços - Contêineres). Se você não alterou sua região, pode simplesmente clicar aqui. Você verá a lista completa de seus repositórios:

Screenshot-2021-03-16-at-23.00.24-1

Agoram você precisa selecionar o repositório react-to-aws e, em seguida, clicar em View push commands ("Visualizar comandos de envio") no menu (marcado com círculos vermelhos na imagem acima). Você verá a seguinte janela:

Screenshot-2021-03-16-at-23.08.49

Você precisa copiar todos comandos do modal no terminal. Não copie diretamente da caixa abaixo, pois não funcionarão:

$ aws ecr get-login-password --region us-east-2 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-2.amazonaws.com
Login Succeeded

$ docker build -t react-to-aws .
[+] Building 0.6s (8/8) FINISHED
...

$ docker tag react-to-aws:latest 123465789.dkr.ecr.us-east-2.amazonaws.com/react-to-aws:latest

$ docker push 123456789.dkr.ecr.us-east-2.amazonaws.com/react-to-aws:latest
The push refers to repository [123456789.dkr.ecr.us-east-2.amazonaws.com/react-to-aws:latest]
...
latest: digest: sha256:3921262a91fd85d2fccab1d7dbe7adcff84f405a3dd9c0e510a20d744e6c3f74 size: 1988

Agora, feche o modal e clique no nome do repositório (react-to-aws) para listar as imagens disponíveis. Você deve ver essa tela:

Screenshot-2021-03-16-at-23.17.56-1

Sua aplicação está no repositório, pronta para implementação! Agora, clique em Copy URI (Copiar URI) e mantenha isso na sua 'área de transferência' (clipboard), ou copie em um arquivo de texto, pois precisaremos digitá-lo mais tarde!

4. Configurar a aplicação

Nossa imagem está disponível na nuvem. Agora, precisamos configurá-la.
Para executarmos máquinas virtuais, devemos definir algumas instruções, como portas abertas, variáveis de ambiente e assim por diante. A AWS chama isso de definição de tarefa (task definition).
Abra o console da AWS e clique em Elastic Container Service (ECS) na lista All Services - Containers ("Todos os Serviços - Contêineres"). Se você não alterou sua região, pode clicar aqui.
Agora selecione Task Definitions ("Definições de Tarefas") e clique em Create New Task Definition ("Criar Definição de Tarefa"), conforme imagem abaixo:

Screenshot-2021-03-17-at-00.07.54-1

Temos duas opções para executar as tarefas: FARGATE e EC2. Escolha FARGATE e avance para o próximo passo.

Screenshot-2021-03-17-at-00.09.53

No próximo passo, você precisa preencher o formulário com os seguintes valores:

  • Nomeclatura (Task Definition Name) react-to-aws-task.
  • Papel/Nível (Task Role)none.
  • Memória (Task memory) em GB 0.5GB (a menor).
  • Recurso Processamento (Task CPU) em vCPU0.25 vCPU (a menor).

Assim que você chegar em Container Definitions ("Definições de container"), clique em Add container ("Adicionar container"):

Screenshot-2021-03-17-at-00.18.19

Preencha o formulário com os seguintes valores:

  • Nome do container (Container Name) react-to-aws.
  • Imagem (Image) – o URI do passo anterior. Você colou os dados em um bloco de notas, lembra?
  • Limite de memória (Memory Limits) em MiB Soft limit 128.
  • Mapeamento de portas (Port mappings)80 – a porta HTTP.

Outras opções não são relevantes para nós. Agora, clique no botão Add ("Adicionar") para adicionar um contêiner e finalize a definição de tarefa clicando em Create ("Criar"). Você deve ver a seguinte tela e clicar em View Task Definition ("Visualizar definição de tarefa").

Screenshot-2021-03-17-at-00.24.56

5. Vamos executar!

Agora podemos criar o cluster para executar nossa aplicação na nuvem. Você pode selecionar Clusters a partir do menu ao lado esquerdo e clicar em Create Cluster ("Criar Cluster"), conforme a imagem abaixo:

Screenshot-2021-03-17-at-00.29.46

Agora, temos três opções: Networking only, EC2 Linux + Networking e EC2 Windows + Networking. Selecione a primeira, Networking only ("Rede apenas") e avance para o próximo passo, onde você deve ver a seguinte tela:

Screenshot-2021-03-17-at-00.34.35

Digite react-to-aws como nome do cluster e crie no botão Create. Você deve ver uma mensagem indicando sucesso. Pode parecer similar à imagem abaixo (quando criamos a nossa tarefa). Agora, clique em View Cluster ("Ver cluster").

Screenshot-2021-03-17-at-00.39.42-1

Agora, clique na aba Tasks ("Tarefas") e clique em Run new Task ("Executar nova tarefa"). Parabéns! Chegamos ao último formulário! 😀

Screenshot-2021-03-17-at-00.41.10

Preencha seu último formulário com os seguintes valores:

  • Tipo de lançamento (Launch type) FARGATE.
  • Cluster VPC – a primeira opção.
  • Subrede (Subnet) – a primeira opção.

Mantenha os demais valores com o padrão e clique no botão Run task ("Executar tarefa"). Você deve ver essa tela:

Screenshot-2021-03-17-at-00.46.45-1

Precisaremos aguardar cerca de um minuto até que o "Último status" (Last Status) mude para "EXECUTANDO" (RUNNING). Lembre-se de que você precisa clicar no botão "Atualizar" (Refresh) para atualizar a lista. Assim que o status da tarefa estiver em execução, clique no nome da tarefa.

Screenshot-2021-03-17-at-00.50.52

Na seção "Rede" (Network), você pode encontrar o IP público (Public IP) do seu contêiner. Abra-o no navegador para visualizar sua aplicação.

Resumo

Se você está no início de sua carreira – talvez nunca tenha implementado uma aplicação por conta própria – mas é bom aprender essa habilidade, porque um dia precisará fazer isso.
Todo projeto precisa chegar aos usuários. Caso contrário, não terá chance de ser bem-sucedido e nunca será útil e/ou rentável.
O processo de configuração pode ser um pouco tedioso, mas a boa notícia é que você só precisa fazer isso uma vez! 🙂
Depois de configurar tudo, suas implantações futuras serão mais simples. Você só precisa enviar a nova imagem e reiniciar a tarefa para implantar uma nova versão de sua aplicação.
Se você estiver interessado em se aprofundar na AWS, o FreeCodeCamp oferece um tutorial gratuito sobre o assunto (cerca de 14 horas).
Você pode encontrar uma gravação em vídeo deste tutorial (17 minutos) em meu canal no YouTube. Estou no início de minha jornada no YouTube – pelo menos semanalmente, faço upload de um vídeo sobre programação. Significaria muito para mim se você assistisse à minha gravação em vídeo, se inscrevesse no canal e clicasse no botão de curtir.

Você pode encontrar todo o código neste repositório do GitHub: https://github.com/mateuszsokola/react-to-aws

Você pode enviar mensagens diretas ao autor (em inglês) pelo Twitter: @msokola

Pode enviar mensagens diretas ao tradutor (em português ou inglês) pelo Twitter: @drcrescencio

É isso, pessoal! Tenham um bom dia! 🙂

vidar-nordli-mathisen-xgP0GNl9Gzg-unsplash
Foto: Vidar Nordli-Mathisen, extraída do Unsplash