Serverless Framework: Anatomia do arquivo YAML
Tags: ptbr, webdev, serverless, aws
Published: 2019-08-15 13:11:14

Nos últimos anos, a criação de uma infraestrutura serverless tornou-se mais acessível do que nunca. Com as plataformas AWS, Azure e Google Cloud, os desenvolvedores e engenheiros têm recursos quase ilimitados ao implantar sites e aplicativos. O [Serverless Framework](https://serverless.com), uma estrutura popular para criar arquitetura serverless, tornou ainda mais fácil a utilização das maiores plataformas de computação em nuvem disponíveis. Eles fizeram isso abstraindo a configuração da arquitetura de nuvem para uma forma mais direta usando um arquivo [YAML](https://learn.getgrav.org/16/advanced/yaml). Neste artigo, veremos o arquivo `serverless.yml` em mais detalhes para ver como ele é usado para configurar uma arquitetura serverless.

# O arquivo "serverless.yml"

O `serverless.yml` é o coração de um aplicativo serverless. Este arquivo descreve toda a infraestrutura de aplicativos, desde a linguagem de programação até o acesso a recursos.

A seção principal deste arquivo YAML é o `provider`. Na configuração do `serverless.yml`, você tem a opção de usar o AWS, o Google Cloud ou o Microsoft Azure como seu provedor serverless. Você também pode especificar a linguagem de programação que deseja usar. No exemplo abaixo, eu especifico que vou usar o AWS com o Python 3.7.

Exemplo de um `serverless.yml`:

```yml
service: my-first-serverless-app
provider:
  name: aws
  runtime: python3.7
```

O arquivo `serverless.yml` acima é a base do que poderia ser um poder ilimitado de computação, centenas de funções serverless e uma infraestrutura de banco de dados que potencializa seu site ou aplicativo. Vamos nos aprofundar nessa configuração. Eu vou estar usando o AWS e python para o meu exemplo de base.

# Variáveis de Ambiente

Configurar variáveis ​​de ambiente para usar em seu aplicativo é realmente fácil com o arquivo `serverless.yml`. Dentro da chave `provider`, você adiciona uma seção de ambiente e uma lista de valores de chave/valor. Usando o nosso exemplo acima um passo adiante, vamos adicionar uma variável de ambiente que especifica um nome de tabela para uma tabela de usuários.

```yml
provider:
  name: aws
  runtime: python3.7
  environment:
    USER_TABLE: users_table
```

Agora, em qualquer função serverless que utilizamos, a variávei ​​de ambiente `USER_TABLE` estará disponível para uso. Também podemos usar essa variável em outras partes do nosso arquivo `serverless.yml` para nomear recursos ou prefixar caminhos HTTP.

Para usar essa variável de ambiente no `serverless.yml` mais tarde, use a seguinte sintaxe. Observe que é semelhante a percorrer um objeto em outras linguagens de programação.

```yml
${self:provider.environment.USER_TABLE}
```

# Dividindo em múltiplos arquivos

Muitas vezes você precisa separar a configuração de seu aplicativo em múltiplos arquivos. O Serverless Framework possui uma sintaxe muito legal para incluir variáveis ​​de outros arquivos. Digamos, por exemplo, que você queira que a tabela do usuário seja denominada `dev_users_table` para um ambiente de desenvolvimento. Há muitas maneiras para isso, mas para ilustrar o fornecimento de outros arquivos, mostrarei uma maneira.

Você pode ter vários arquivos de configuração que são usados ​​para ambientes específicos. Digamos que você tenha dois arquivos de configuração YAML na pasta raiz do seu projeto:

Exemplo `dev.config.yml`:

```yml
table_name: 'dev_users_table'
```

Exemplo `prod.config.yml`:

```yml
table_name: 'prod_users_table'
```

Seu aplicativo de produção usaria `prod.config.yml`. Para obter esse arquivo em produção, você pode usar a seguinte sintaxe:

```yml
provider:
  name: aws
  runtime: python3.7
  environment:
    USER_TABLE: ${file(./prod.config.yml):table_name}
```

Mas espere, como você implementa este aplicativo em **dev**? Você tem que mudar o arquivo para especificar **dev** certo? Não, podemos atualizar essa configuração para utilizar a variável `stage`. A variável `stage` é uma variável especial na estrutura do arquivo yaml que pode ser usada para especificar qual ambiente você está usando. Por padrão, a variável **stage** é dev.

# Variáveis `stage`

Vamos consertar nosso `serverless.yml` para que possamos usar a configuração dev sem modificar o arquivo manualmente. Para fazer isso, usamos `${opt: stage, self: provider.stage}`.

Exemplo do **serverless.yml** com variável **stage**:

```yml
provider:
  name: aws
  runtime: python3.7
  environment:
    USER_TABLE: ${opt:stage, self:provider.stage}_users_table
```

Agora que estamos usando a variável **stage**. Nós não precisamos do arquivo **prod.config.yml**. Podemos facilmente prefixar todos os nossos serviços com a variável **stage** para que, quando implantarmos nosso aplicativo para testar um novo serviço ou recurso, não afetemos os serviços de produção.

Se nós quiséssemos usar arquivos de configuração separados, poderíamos usar nossa variável **stage** para separar a configuração em arquivos separados como este:

```yml
provider:
  name: aws
  runtime: python3.7
  environment:
    USER_TABLE: ${file(./${opt:stage, self:provider.stage}.config.yml):table_name}
```

Separar suas variáveis ​​de ambiente em arquivos é uma ótima maneira de organizar aplicativos maiores que usam muitos recursos, como buckets do S3, tabelas de banco de dados, caminhos de versão da API etc.

# Declarações de IAM Roles

Se você usou os serviços da AWS, provavelmente precisou criar um usuário do **IAM** ou **Role**. Com o serverless, você pode conceder permissões às suas aplicações para utilizar recursos através do arquivo **serverless.yml**. A configuração de permissão é basicamente o **AWS Cloudformation** escrito em YAML. As permissões vão sob a seção **iamRoleStatements** sob dentro de **provider**.

## Dando acesso ao S3

Se você tiver um bucket S3 chamado **awesome-bucket-name**. O exemplo a seguir dará ao seu aplicativo acesso completo (Read, Write, List, etc) ao bloco especificado.

```yml
provider:
  name: aws
  runtime: python3.7
  environment:
    USER_TABLE: users_table 
 iamRoleStatements:
    -  Effect: "Allow"
       Action:
         - "s3:*"
       Resource:
          - arn:aws:s3:::awesome-bucket-name
```

## Dando acesso ao SES

Se você tem um aplicativo que precisa enviar e-mails. Você pode dar acesso ao Amazon SES da seguinte forma: (Observação: você precisa configurar o SES com um domínio verificado separadamente.) O exemplo a seguir dá ao seu aplicativo acesso para usar todos os recursos do SES para enviar emails.

```yml
provider:
  name: aws
  runtime: python3.7
  environment:
    USER_TABLE: users_table 
 iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "ses:SendEmail"
      Resource: "*"
```

# Funções

Eu tenho escrito sobre esse misterioso "aplicativo", mas não expliquei o que ele é. Em seu arquivo **serverless.yml**, você configura as funções a serem criadas na plataforma serverless do **provider**. Para AWS, é possível dizer ao serverless para criar funções do Lambda que possam ser anexadas a um endpoint HTTP. Vamos dar uma olhada rápida em como definir um [endpoint HTTP](https://en.wikipedia.org/wiki/Web_API) através do arquivo **serverless.yml** .

Sem funções, você não possui um aplicativo. Lembre-se da linguagem de programação que especificamos no **provider**? Isso diz ao nosso **provider** com qual linguagem de programação iremos criar nossas funções. (Nota: as linguagens de programação são determinadas pelo **provider**. Você não pode escolher qualquer idioma.)

Como exemplo, vamos criar um endpoint HTTP que retorne o hash [md5](https://en.wikipedia.org/wiki/MD5) de um endereço de email, juntamente com a URL do [Gravatar](https://en.gravatar.com/) para esse email.

Para definir uma função, precisamos de duas coisas:

1. Um arquivo com a função chamada **getHash**, chamaremos o arquivo de **handler.py**
2. Uma definição de função em **serverless.yml**

Exemplo da definição no serverless.yml:

```yml
functions:
  myCustomFunctionName:
    handler: handler.getHash
```

Na primeira linha, temos **functions**. Esta é uma seção do arquivo YAML que vive na seção do **provider**. Na seção de funções, temos **myCustomFunctionName**. Esse nome será o nome da função Lambda no AWS Lambda, não o usado no código.

Em seguida, definimos o **handler** . O **handler** é o caminho para a função usando a notação de ponto. Essa notação de ponto é semelhante a como as importações funcionam no Python. **handler** é o nome do arquivo e **getHash** é o nome da função nesse arquivo.

Aqui está o nosso **handler.py**:

```python
import hashlib
import json
CORS = {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Credentials': True,
}
def getHash(event, context):
    string = event['pathParameters']['string']
    h = hashlib.md5(string.encode('utf8'))
    h = h.hexdigest()
    return {
        "statusCode": 200,
        "headers": CORS,
        "body": json.dumps({
            "hash": h,
            "avatar": "http://gravatar.com/avatar/{}".format(h)
        })
    }
```

Com o arquivo definido e a função chamada **getHash** dentro, nosso **serverless.yml** está pronto para criar nossa função no AWS Lambda. Antes de implantar isso, precisamos ativar o acesso a essa função via HTTP. Para tornar isso um endpoint HTTP, vamos adicionar uma seção de eventos.

**Exemplo de funções com eventos:**

```yml
functions:
  myCustomFunctionName:
    handler: handler.getHash
    events:
      - http:
          path: md5/{email}
          method: get
          cors: true
```

A seção **events** vincula nossa função ao API Gateway fornecido pela AWS. Nesta seção, especificamos o caminho e o método do endpoint HTTP. Com esta configuração de evento, você poderá acessar sua função do Lambda através do caminho **/md5/some@email.com**. A resposta seria um objeto JSON com o hash md5 e um URL para a imagem gravatar.

Exemplo de saída com meu email:

```json
{
  "hash": "97fbef641c6e4669b6a1ad4ffb3342f5",
  "avatar": "http://gravatar.com/avatar/97fbef641c6e4669b6a1ad4ffb3342f5"
}
```

Você pode ir ainda mais longe com isso, exigindo autenticação e limitação de taxa nesse endpoint. [Mais na documentação](https://serverless.com/framework/docs/providers/aws/events/apigateway/).

Agora que temos nossos eventos, podemos enviar pedidos para essa função a partir de um aplicativo frontend e ela executará nossa função serverless.

# Conclusão

Esta postagem arranha a superfície do arquivo **serverless.yml**. Espero que você entenda um pouco mais sobre serverless e mergulhe mais fundo nessa estrutura e tecnologia. O **Serverless Framework**, junto com as plataformas de nuvem disponíveis, permite que as pessoas criem produtos escaláveis ​​a um baixo custo. Construir a infraestrutura para um aplicativo é muito mais fácil do que nunca e pode ser criado usando um arquivo de configuração simples como **serverless.yml**.

Obrigado pela leitura!

# Créditos

- [Anatomy of a Serverless YAML File](https://blog.richardkeller.net/anatomy-of-a-serverless-yaml-file/), escrito originalmente por [Richard Keller](https://twitter.com/richardkeller14).