Serverless Framework: Gerenciando Variáveis de Ambiente e Stages
Tags: ptbr, webdev, aws, serverless
Published: 2019-12-09 01:39:58

Geralmente me encontro criando quatro ambientes para cada projeto [Serverless Framework](https://serverless.com/) que trabalho, eles são: `dev`, `staging`, `prod` e `local`. Obviamente, os três primeiros devem ser implantados na nuvem, mas o último, `local`, deve executar e testar interações com recursos locais. Também é ótimo ter uma versão offline (como quando você está em um avião  ✈ ou tem um wifi terrível em algum lugar). Além disso, o desenvolvimento é muito mais rápido, porque você não está esperando requisições de ida e volta ao servidor. 😉

Um recurso realmente excelente do Serverless Framework é a capacidade de configurar variáveis ​​de AMBIENTE no arquivo `serverless.yml`. Isso permite armazenar informações globais importantes, como nomes de bancos de dados, API de serviços e muito mais. Podemos até fazer referência a senhas com segurança usando o [AWS Service Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html) e decodificar senhas criptografadas na hora do deploy, mantendo elas protegidas contra desenvolvedores e repositórios git. 😬 Basta referenciar a variável `${ssm:/myapp/my-secure-value~true}` no seu arquivo de configuração.

# Usando variáveis ​​STAGES e de AMBIENTE juntos pode criar um fluxo de trabalho realmente poderoso para você e sua equipe de desenvolvimento

Eu acho que `sls invoke local -f myFunction -p /path/to/event.json` é um dos comandos mais úteis na minha caixa de ferramentas. Não apenas você pode testar funções localmente simulando eventos, mas também manipular completamente o ambiente passando a opção `-s` com o nome do stage desejado.

Por exemplo, se eu estivesse escrevendo um script que interaja com um banco de dados (talvez consultando dados para um relatório), provavelmente criaria um banco de dados local e apontaria meu `MYSQL_HOST` para localhost (junto com outras configurações). Agora, a execução `sls invoke local -f myDBFunction -p /path/to/event.json -s local` executaria minha consulta na versão local. No entanto, se eu mudar meu `-s` para `dev` desejo que meu código acesse a versão "dev" do meu banco de dados (que talvez esteja na nuvem). Isso é útil para testar alterações de consulta e compatibilidade.

Isso também é ótimo para permitir que você altere outros recursos com base em STAGE, como SQS, S3 buckets, tabelas Dynamo DB, etc.

# Como configuramos nosso serverless.yml para fazer isso?

Outro ótimo recurso do Serverless Framework é "auto-referenciar" variáveis dentro do arquivo `serverless.yml`. Isso nos permite usar valores estáticos (ou mesmo referenciados recursivamente) para definir outros valores. Tenho certeza que você usou isso ao nomear funções, por exemplo, `name: ${opt:stage}-myFunction`, você também pode definir um valor padrão, caso a referência não exista, por exemplo `stage: ${opt:stage,'dev'}`, o que é incrivelmente útil. 👍

No nosso caso, queremos fornecer uma lista de opções possíveis com base no STAGE fornecido. Isso pode ser realizado de várias maneiras. [A documentação ainda oferece o exemplo](https://serverless.com/framework/docs/providers/aws/guide/variables/) de inclusão de um arquivo separado com base no nome STAGE, o que deixa tudo mais fácil. Tudo que você precisa fazer é criar um objeto dentro de `custom:` que forneça um valor para cada stage:

```yml
# Custom Variables
custom:
  mysqlHost:
    local: localhost
    dev: devdb.example.com
    staging: ${ssm:/myapp/staging/mysql-host} # pegando do ssm
    prod: ${ssm:/myapp/prod/mysql-host} # pegando do ssm
```

Agora basta auto-referenciar a chave correta do objeto na seção `environment:`.

```yml
# Environment Variables
environment:
  MYSQL_HOST: ${self:custom.mysqlHost.${self:provider.stage}}
```

E é isso! Agora, sempre que você usar a opção `-s local`, o host do seu banco de dados será "localhost". Quando você altera o valor do stage, o valor do host também será alterado!

Abaixo um exemplo mais completo:

```yml
# Serverless Config
service: myapp

# Provider
provider:
  name: aws
  runtime: nodejs8.10
  stage: ${opt:stage,'dev'}
  ...

  # Environment Variables
  environment:
    MYSQL_HOST: ${self:custom.mysqlHost.${self:provider.stage}}
    MYSQL_USER: ${self:custom.mysqlUser.${self:provider.stage}}
    MYSQL_PASSWORD: ${self:custom.mysqlPassword.${self:provider.stage}}
    MYSQL_DATABASE: ${self:custom.mysqlDatabase.${self:provider.stage}}
    MYSQL_PORT: ${self:custom.mysqlPort.${self:provider.stage}}

# Custom Variables
custom:
  stages:
    - dev
    - staging
    - prod
  mysqlHost:
    local: localhost
    dev: devdb.example.com
    staging: ${ssm:/myapp/staging/mysql-host} #get from ssm
    prod: ${ssm:/myapp/prod/mysql-host} #get from ssm
  mysqlUser:
    local: root
    dev: myapp_devuser
    staging: myapp_stag
    prod: myapp
  mysqlPassword:
    local: root
    dev: ${ssm:/myapp/dev/mysql-password~true} #get from ssm (secure)
    staging: ${ssm:/myapp/staging/mysql-password~true} #get from ssm (secure)
    prod: ${ssm:/myapp/prod/mysql-password~true} #get from ssm (secure)
  mysqlDatabase:
    local: myapp_testdb
    dev: myapp_dev
    staging: myapp_staging
    prod: myapp_prod
  mysqlPort:
    local: '8889'
    dev: '3306'
    staging: '3306'
    prod: '3306'

# Plugins
plugins:
  - serverless-stage-manager

...
```

# Para onde vamos daqui?

Essa técnica também funciona para sistemas de CI / CD. Se o seu ambiente de produção estiver em uma conta separada, o acesso a segredos compartilhados permanecerá seguro.

Se você deseja acessar serviços em nuvem que estão em uma VPC, sempre pode criar estágios adicionais, como `dev_local`. Em seguida, você pode acessar recursos remotos por meio de uma VPN ou usar [túneis SSH para acessar recursos atrás de uma VPC](https://www.jeremydaly.com/access-aws-vpc-based-elasticsearch-cluster-locally/). Você pode usar o encaminhamento de porta, por exemplo, para direcionar o tráfego do MySQL para o host local através da sua instância do VPC RDS.

Se você quiser se salvar de digitar nomes errados de stages, confira o [Serverless Stage Manager](https://www.npmjs.com/package/serverless-stage-manager). Isso permite restringir os nomes de stages usados ​​para implementar stacks completas e funções.

Espero que você tenha achado isso útil. Boa sorte e vá serverless! 🤘🏻

# Créditos

- [How To: Manage Serverless Environment Variables Per Stage](https://www.jeremydaly.com/how-to-manage-serverless-environment-variables-per-stage/), escrito originalmente por [Jeremy Daly](https://twitter.com/jeremy_daly)