Como criar um serviço sem servidor em 15 minutos

A palavra “serverless” tem sido popular por um bom tempo. Quando a Amazon lançou o serviço AWS Lambda em 2015, muitas ferramentas surgiram para ajudar as pessoas a criar serviços sem servidor com apenas alguns comandos. Em comparação com os serviços tradicionais sempre ativos, os serviços sem servidor são muito fáceis de desenvolver, implantar e manter. Eles também são extremamente rentáveis, especialmente para aqueles serviços simples que não têm muito tráfego.

Então, o que é serverless?

Como o próprio nome indica, “serverless” significa que você executa um serviço sem um servidor. Bem, tecnicamente, ainda há um servidor executando o serviço, mas você, como proprietário do serviço, não precisa se preocupar com esse servidor.

Por exemplo, o AWS Lambda permite implantar uma “função” para lidar com as solicitações. A AWS tem um servidor para executar todas as funções quando elas são solicitadas. Mas você não precisa se preocupar sobre como esse servidor funciona ou como fazer seu código funcionar com esse servidor. Tudo o que você precisa saber é que você escreve uma função e depois a envia para o serviço Lambda.

E a parte mais doce é que é muito barato. O Amazon Lambda oferece solicitações gratuitas de 1 milhão e tempo de computação livre de 400.000 GB por segundo por mês (o que significa que seu cálculo pode usar 1 GB de memória por 400.000 segundos), o que é suficiente para a maioria dos serviços pequenos. Comparado ao EC2, onde uma instância nano custaria US $ 0,0058 por hora (que é US $ 0,14 por dia), o Lambda é muito mais barato.

O que faremos aqui

Neste post, mostrarei como construir um pequeno site pessoal sem servidor usando o AWS. O site tem as seguintes características:

  • Inclui front-end e back-end
  • Principalmente estático ou pesado front-end
  • Solicitações de API – são raras, mas necessárias
  • O back-end não requer muita memória ou CPU (por exemplo, um contador da web simples que requer apenas um acesso ao banco de dados)

Nosso serviço será implantado nos seguintes domínios (usei domínios falsos nesta postagem):

Uma solução sem servidor é perfeita tanto tecnicamente quanto do ponto de vista de custo. Usaremos os seguintes serviços da AWS:

  • Lambda + API Gateway + S3, para o servidor de API
  • DynamoDB, para armazenamento de dados
  • S3, para hospedagem na web estática
  • Cloudfront, para CDN distribuído
  • AWS Certificate Manager (ACM), para gerar certificados para o nosso site https

Para o servidor da API, usaremos uma combinação Python + Flask e Zappa como o kit de ferramentas sem servidor.

Configurando o ambiente do AWS

Primeiro, precisamos configurar o ambiente do AWS para que possamos acessar o AWS a partir do nosso código e do zappa . Isso leva dois passos:

  1. Precisamos criar um usuário da AWS para acesso programático
  2. Precisamos configurar um ambiente local da AWS para usar para esse usuário

Crie um usuário da AWS

Faça o login no AWS e escolha o serviço “IAM” para gerenciar as credenciais do usuário.

Crie um usuário chamado “myservice-admin” (ou qualquer outro nome de usuário que você gostaria de usar), e não se esqueça de marcar a opção ” Acesso programático “.

Na próxima tela, clique no botão ” Anexar políticas existentes diretamente ” e adicione ” AdministratorAccess ” ao usuário.

Nota: De uma perspectiva de segurança, esta não é a melhor prática. Mas, para fins de demonstração, este post não cobre os detalhes de restrição de permissões.

Clique no botão “next” e depois no botão “Create User”, e o usuário myservice-admin será criado. Na última tela, a ID da chave de acesso e a chave de acesso secreta são exibidas. Certifique-se de copiar e colá-los em um arquivo local. Estas são as credenciais da API que vamos usar na próxima etapa.

Nota: Este é o único lugar onde você pode ver as chaves de acesso secreto ! Se você não conseguir fazer uma cópia deles, terá que ir para a tela de detalhes do usuário e gerar um novo par de chaves de acesso e segredo.

Configure seu ambiente local da AWS

Precisamos criar um ambiente local para usar o AWS localmente.

Primeiro, vamos instalar a ferramenta awscli , que nos ajudará a configurar o ambiente:

 $ sudo apt instala o awscli

Após a instalação, vamos configurar o AWS usando o comando aws configure :

 $ aws configure 
 ID da chave de acesso da AWS [Nenhum]: ****** 
 Chave de acesso secreto da AWS [Nenhum]: ****** 
 Nome da região padrão [Nenhum]: us-east-1 
 Formato de saída padrão [None]: json

Aqui, precisamos digitar a ID da chave de acesso e a chave de acesso secreta que recebemos da última etapa. Em termos de região padrão, usei o us-east-1 . Você pode escolher qualquer região que desejar, mas outras regiões podem causar alguns problemas ao configurar o CloudFront.

Criar uma tabela no DynamoDB

Para armazenar o valor do contador de visitantes do site no DynamoDB, precisamos de um armazenamento persistente. Então, precisamos criar uma tabela e preencher um valor inicial dentro dela.

No console da AWS, escolha o serviço DynamoDB. Em seguida, clique no botão ” Criar tabela “. Na tela “Criar tabela do DynamoDB”, preencha o nome da tabela com myservice-dev e o campo da chave primária com o id e clique no botão Criar tabela .

Após alguns segundos, a tabela deve ser criada. Selecione a tabela recém-criada, escolha a guia Itens no painel direito, clique no botão Criar item e crie um item com id='counter' e counter_value=0 .

Observação: você precisa clicar no sinal de mais no lado esquerdo para adicionar o atributo counter_value e não se esqueça de definir o tipo de counter_value como Number .

Crie um serviço de API

Em seguida, criaremos o serviço da API. Para fins de demonstração, esse serviço de API fornecerá uma API de contador que aumentará um valor de contador quando clicado. O valor do contador será armazenado no DynamoDB. Os pontos de extremidade da API são:

  • POST /counter/increase aumenta o contador e retorna o valor do contador
  • GET /counter retorna o valor atual do contador

Codificando o Serviço de API com Python e Flask

Vamos começar com a criação de um ambiente virtual Python e instalar os pacotes necessários:

 $ mkdir myservice && cd myservice 
 $ python3 -m venv .env 
 $ source .env / bin / active 
 (.env) $ pip instale o frasco boto3 simplejson

flask é o framework web e o pacote boto3 é necessário para acessar o DynamoDB. simplejson pode nos ajudar a lidar com alguns problemas de conversão do JSON. Vamos criar o serviço criando um arquivo myservice.py com o conteúdo abaixo:

 import boto3 
 do balão de importação do frasco, jsonify
 app = Flask (__ name__)
 # Inicialize o acesso ao dinamodb 
 dynamodb = boto3.resource ('dynamodb') 
 db = dynamodb.Table ('myservice-dev')
 @ app.route ('/ counter', métodos = ['GET']) 
 def counter_get (): 
 res = db.get_item (Key = {'id': 'contador'}) 
 return jsonify ({'contador': res ['Item'] ['contador_valor']})
 @ app.route ('/ counter / increase', métodos = ['POST']) 
 def counter_increase (): 
 res = db.get_item (Key = {'id': 'contador'}) 
 value = res ['Item'] ['counter_value'] + 1 
 res = db.update_item ( 
 Key = {'id': 'contador'}, 
 UpdateExpression = 'set counter_value =: value', 
 ExpressionAttributeValues ??= {': value': value}, 
 ) 
 return jsonify ({'contador': valor})

Crie um arquivo run.py para testar este serviço de API localmente:

 do aplicativo de importação myservice 
 se __name__ == '__main__': 
 app.run (debug = True, host = '127.0.0.1', porta = 8000)

Agora execute o serviço:

 (.env) $ python run.py

E podemos testar esse serviço com os seguintes comandos (abra outro terminal para digitar esses comandos):

 $ curl localhost: 8000 / contador 
 { 
 "contador": 0 
 } 
 $ curl -X POST localhost: 8000 / contador / aumento 
 { 
 "contador": 1 
 } 
 $ curl -X POST localhost: 8000 / contador / aumento 
 { 
 "contador": 2 
 } 
 $ curl localhost: 8000 / contador 
 { 
 "contador": 2 
 }

Podemos ver que nosso código está funcionando e está aumentando com sucesso o contador!

Implantando nosso código no Lambda com o Zappa

Implantar nossa API no Lambda é extremamente fácil com o zappa. Primeiro, precisamos instalar o zappa:

 (.env) $ pip zappa de instalação

Em seguida, inicialize o ambiente zappa init com zappa init . Ele fará algumas perguntas, mas geralmente você pode usar as respostas padrão para todas as perguntas:

 (.env) $ zappa init 
 ... 
 O que você deseja chamar esse ambiente (padrão 'dev'): 
 ... 
 O que você quer chamar seu balde? (padrão 'zappa-ab7dd70x5'):
 Parece que este é um aplicativo Flask. 
 Qual é o caminho modular para a função do seu aplicativo? 
 Isso provavelmente será algo como 'your_module.app'. 
 Nós descobrimos: myservice.app 
 Onde está a função do seu aplicativo? (padrão 'myservice.app'): 
 ...
 Você gostaria de implantar esse aplicativo globalmente? (padrão 'n') [y / n / (p) rimary]:
 Ok, aqui está o seu zappa_settings.json:
 { 
 "dev": { 
 "app_function": "myservice.app", 
 "aws_region": "us-east-1", 
 "profile_name": "default", 
 "project_name": "myservice", 
 "runtime": "python3.6", 
 "s3_bucket": "zappa-ab7dd70x5" 
 } 
 }
 Isso parece bem? (padrão 'y') [y / n]: 
 ...

Após a inicialização, podemos ver o arquivo zappa_settings.json gerado. Então podemos começar a implantar nosso serviço:

 (.env) $ zappa deploy dev 
 Chamando implantar para o estágio dev .. 
 ... 
 Implantação completa !: https://2ks1n5nrxh.execute-api.us-east-1.amazonaws.com/dev

Ótimo! Nosso serviço está online. Você pode testar este serviço com o curl também:

 (.env) $ curl https://2ks1n5nrxh.execute-api.us-east-1.amazonaws.com/dev/counter 
 {"contador": 2} 
 (.env) $ curl -X POST https://2ks1n5nrxh.execute-api.us-east-1.amazonaws.com/dev/counter/increase 
 {"contador": 3} 
 (.env) $ curl https://2ks1n5nrxh.execute-api.us-east-1.amazonaws.com/dev/counter 
 {"contador": 3}

Configurar um domínio personalizado para o serviço de API

No entanto, há um problema com o serviço da API. O endpoint da API gerado automaticamente 2ks1n5nrxh.execute-api.us-east-1.amazonaws.com é muito difícil de ler ou usar para consumo humano. Felizmente, podemos vincular um nome de domínio personalizado a esse endpoint da API.

Usaremos o domínio personalizado https://myservice-api.example.com para este serviço de API. Como queremos atendê-lo com https, precisamos primeiro obter um certificado. A AWS fornece um certificado gratuito com o serviço “Gerenciador de certificados” e é muito fácil de usar.

Depois que o certificado é gerado, podemos usá-lo para configurar um domínio personalizado para o nosso serviço no serviço AWS API Gateway.

Candidate-se a um certificado

Mude para o serviço ACM no console de gerenciamento da AWS (o serviço é, na verdade, chamado de Gerenciador de certificados, mas você pode digitar “ACM” para pesquisá-lo). Clique em Solicitar um botão de certificado e, em seguida, escolha a opção Solicitar um certificado público na próxima tela. O certificado é gratuito, desde que você escolha um certificado público aqui.

Na próxima tela, insira o nome de domínio para o qual deseja aplicar o certificado e clique em Avançar . Aqui eu *.example.com que significa que o certificado pode ser usado por todos os subdomínios em example.com . Dessa forma, podemos usar o mesmo certificado para nosso front end em myfrontend.example.com sem precisar solicitar um novo.

Na próxima etapa, precisamos provar que somos os proprietários deste nome de domínio. Como eu solicitei esse nome de domínio do Google Domains, escolherei a validação do DNS . Clique no botão Revisar e, em seguida, clique em Confirmar e Solicitar .

A solicitação de certificado será criada e uma tela de validação será exibida. As instruções mostram como validar este nome de domínio:

Com base nas instruções, precisamos adicionar um CNAME e atribuir a ele o valor determinado. No meu caso, vou abrir o Google Domains, encontrar o nome do meu domínio example.com e adicionar o registro CNAME especificado:

Nota: Eu adicionei apenas a sequência aleatória _2adee19a0967c7dd5014b81110387d11 no campo Nome, sem digitar a parte .example.com . Isso é para evitar que o sufixo .example.com seja duplicado.

Agora, precisamos aguardar cerca de 10 minutos até que o AWS Certificate Manager valide esse nome de domínio. Uma vez validada, a coluna “Status” no certificado exibirá “Emitido” em verde.

Agora que temos o certificado pronto, podemos começar a vincular nosso nome de domínio personalizado à nossa API.

Configurando um domínio personalizado para nosso serviço de API

Vá para o serviço “API Gateway”. A partir das “APIs” no painel esquerdo, podemos ver que nossa API myservice-dev já foi criada pelo zappa.

Clique em “ Custom Domain Names ” no painel esquerdo, clique no botão Create Custom Domain Name no painel direito e preencha os campos necessários.

Aqui, quero que meu serviço de API seja exposto via CloudFront para que possa ser acessado com velocidade ideal em todo o mundo. Então, escolhi o Edge Optimized nessa configuração. Você pode escolher Regional se não precisar do CloudFront.

Clique no link ” Adicionar mapeamento ” abaixo, selecione nossa API myservice-dev como o destino e escolha dev para a caixa mais à direita. Desta forma, nossa API não irá expor o nome do ambiente dev no URL. Deixe o campo Caminho vazio.

Depois de clicar no botão Salvar , nossa ligação de domínio personalizada será criada. A ligação de domínio real requer até 40 minutos para inicializar, mas podemos configurar as configurações de DNS agora.

A partir da imagem acima, podemos ver que o nome do domínio atual é dgt9opldriaup.cloudfront.net . Precisamos configurar um CNAME em nosso DNS, apontando myservice-api.example.com para o subdomínio do CloudFront dgt9opldriaup.cloudfront.net .

Vá para o Google Domains e adicione o CNAME às configurações de DNS:

Após essa etapa, aguarde cerca de 40 minutos até que o “Inicializando…” no serviço de gateway da API desapareça.

Agora experimente nosso novo serviço de API!

 (.env) $ curl https://myservice-api.example.com/counter 
 {"contador": 3} 
 (.env) $ curl -X POST https://myservice-api.example.com/counter/increase 
 {"contador": 4} 
 (.env) $ curl https://myservice-api.example.com/counter 
 {"contador": 4}

Site estático para front end

Para a próxima tarefa, criaremos um front end para nosso novo serviço de API. Para fins de demonstração, criaremos uma página simples com um botão que aciona a chamada da API /counter/increase .

Codificando o front end

Vamos criar um novo diretório chamado myfrontend :

 $ mkdir myfrontend && cd myfrontend

Em seguida, crie um arquivo HTML simples index.html :

 <html> 
 <body> 
 <h1> Bem-vindo à minha página inicial! </ h1> 
 <p> Contador: <span id = "counter"> </ span> </ p> 
 <button id = "increase"> Aumentar Contador </ button> 
 <script> 
 const setCounter = (counter_value) => { 
 document.querySelector ('# counter'). innerHTML = counter_value; 
 };
 const api = ' https://myservice-api.idv2.com' ; 
 buscar (api + '/ contador') 
 .then (res => res.json ()) 
 .then (result => setCounter (resultado.contador));
 document.querySelector ('# increase') 
 .addEventListener ('click', () => { 
 buscar (api + '/ counter / increase', {método: 'POST'}) 
 .then (res => res.json ()) 
 .then (result => setCounter (resultado.contador)); 
 } 
 ); 
 </ script> 
 </ body> 
 </ html>

Publique o front end no AWS S3

Para criar um site estático com o S3, precisamos criar um bloco com o mesmo nome do nosso nome de domínio.

Observação: se você está acompanhando este tutorial, o nome do intervalo myfrontend.example.com pode não estar disponível, pois os nomes dos buckets são exclusivos no mundo todo. Além disso, você precisará criar um nome de intervalo com base no seu domínio público. Por exemplo, myfrontend. [yourdomain] .com

Mude para o serviço S3 no console de gerenciamento da AWS. Como queremos hospedar o website estático em myfrontend.example.com , criaremos um bloco com esse nome. Clique no botão Criar bucket e preencha o nome do bucket e continue clicando em Next até que o bucket seja criado.

Em seguida, precisamos ativar a hospedagem na web estática a partir desse intervalo. Abra esse intervalo, escolha a guia Propriedades e escolha Hospedagem da Web estática . Na caixa de diálogo, selecione Usar este intervalo para hospedar um site e digite index.html no campo “Documento de índice”. Clique em Salvar quando terminar.

Nota: O link “Endpoint” mostrado na caixa de diálogo acima. Vamos testar o nosso site estático com este URL mais tarde.

A última coisa que precisamos fazer é permitir o acesso público no balde. Isso pode ser feito adicionando uma política de buckets. Abra esse intervalo, escolha a guia Permissões e clique no botão Bucket Policy .

Digite o seguinte conteúdo como política e clique no botão Salvar (não se esqueça de substituir myservice.example.com pelo seu nome de domínio).

 { 
 "Versão": "2012-10-17", 
 "Declaração": [ 
 { 
 "Sid": "PublicReadGetObject", 
 "Efeito": "Permitir", 
 "Diretor": "*", 
 "Ação": "s3: GetObject", 
 "Recurso": "arn: aws: s3 ::: myfrontend.example.com/*" 
 } 
 ] 
 }

Depois de salvar, poderemos ver um sinal laranja “público” no botão Política de balde e na guia Permissões , o que indica que nosso balde está acessível publicamente.

Agora o bucket é criado, mas ainda está vazio. Precisamos fazer o upload de nossos arquivos de código front-end para esse intervalo. Certifique-se de que estamos no diretório myfrontend recém-criado e digite o seguinte comando:

 # Certifique-se de estar no diretório `myfrontend` ... 
 $ aws s3 sync. s3: //myfrontend.example.com

O comando acima copia todos os arquivos da corrente . diretório para S3.

Tudo feito! Agora podemos testar este site estático com o URL exibido anteriormente. Abra essa URL com qualquer navegador (no meu caso, http://myfrontend.example.com.s3-website-us-east-1.amazonaws.com/) e veja o resultado!

Opa! O contador não é exibido de todo. ?

E parece que temos algum erro de JavaScript. Podemos ver o seguinte erro no console:

 Falha ao carregar https://myservice-api.example.com/counter: Nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Origem 'http://myfrontend.example.com.s3-website-us-east-1.amazonaws.com', portanto, não tem permissão de acesso. Se uma resposta opaca atender às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com o CORS desativado.

Aparentemente, precisamos definir o cabeçalho CORS para que esse script funcione, já que a API de back-end está localizada em outro domínio. Mas como vamos configurar um domínio personalizado para o frontend, o URL será alterado, então nos preocuparemos com o CORS posteriormente.

Configurando o CloudFront para nosso site estático

O último passo é configurar o CloudFront para o nosso front end. Como já criamos um certificado para *.example.com , essa etapa será muito fácil.

Alterne para o serviço CloudFront no console de gerenciamento da AWS. Clique no botão Criar Distribuição e, em seguida, clique no botão Iniciar na seção “Web”.

Na tela “Criar distribuição”, precisamos fazer cinco alterações:

  • Clique na caixa de entrada de nome de domínio de origem e selecione nosso balde S3 myfrontend.example.com.s3.amazonaws.com .
  • Em seguida, altere a Política do Protocolo do Visualizador para “Redirecionar HTTP para HTTPS” para “forçar https”.
  • Na caixa Nomes de domínio alternativos , digite nosso domínio personalizado. Nesse caso, myfrontend.example.com .
  • Role para baixo até a seção Certificado SSL , escolha “Certificado SSL personalizado” e selecione nosso certificado *.example.com .
  • Altere o Objeto Raiz Padrão para index.html .

Depois que a distribuição é criada, podemos ver o domínio do CloudFront na lista de distribuição.

Embora o status ainda seja “Em andamento”, podemos configurar nosso registro DNS agora. Vá para o Google Domains e adicione um CNAME para este domínio:

Agora abra seu navegador e tente acessar myfrontend.example.com . Podemos ver exatamente o mesmo site estático!

Corrija o problema do CORS

Agora, o único problema que resta é o CORS. Como estamos usando um nome de domínio diferente no backend e no frontend, precisamos adicionar suporte ao CORS.

O compartilhamento de recursos de origem cruzada (CORS) é um mecanismo que permite que recursos restritos (por exemplo, fontes) em uma página da Web sejam solicitados de outro domínio fora do domínio do qual o primeiro recurso foi exibido. – Wikipedia

Volte para o diretório da minha API ( myservice ) e ative o ambiente Python. Em seguida, instale o pacote flask_cors .

 $ cd myservice 
 $ source .env / bin / activate 
 (.env) $ pip install flask_cors

Em seguida, edite myservice.py e adicione as seguintes linhas (em negrito):

 import boto3 
 do balão de importação do frasco, jsonify 
 de flask_cors import CORS
 app = Flask (__ name__) 
 CORS (app, origins = [' https://myfrontend1.idv2.com' ])

Empurre o serviço atualizado para o AWS Lambda:

 (.env) $ zappa update dev

Agora tente atualizar nosso navegador. Podemos ver que o contador é exibido corretamente. Clicar no botão “Aumentar Contador” também pode aumentar o contador.

Conclusão

Nesta postagem, exploramos vários serviços da AWS necessários para criar um serviço simples sem servidor. Você pode achar que há muitos serviços da AWS se não estiver familiarizado com a AWS, mas a maioria dos serviços da AWS que usamos aqui são para uso único. Uma vez que eles estão configurados, não precisamos tocá-los em todos os desenvolvimentos. Tudo o que você precisa fazer é executar o zappa update e o aws s3 sync .

Além disso, isso é muito mais fácil do que configurar um VPS privado, instalar servidores da web e criar um trabalho Jenkins para implantação contínua.

Em resumo, aqui estão as principais conclusões deste post:

  • O Lambda pode executar um serviço simples. Este serviço pode ser exposto pelo API Gateway.
  • O zappa é uma ótima ferramenta se você quiser escrever serviços sem servidor no Python.
  • O bucket do S3 pode ser usado para hospedagem estática.
  • Inscreva-se para obter um certificado do AWS ACM, se desejar usar https.
  • O API Gateway e o CloudFront suportam nomes de domínio personalizados.

Espero que você goste deste post e não hesite em aplaudir ? para mim se você fez! Siga-me se você quiser ler mais sobre desenvolvimento web.