Configurando um ambiente de staging com Kubernetes e feature flags

Guilherme Garnier
Jusbrasil Tech
Published in
5 min readJun 5, 2020

Aqui no Jusbrasil temos uma cultura forte de desenvolvimento: todo código que é escrito passa por um processo de code review, e passa por muitos testes antes de ser liberado para nossos usuários. Mas mesmo após estas etapas, é importante fazer uma última validação antes de subir o código para produção (o ambiente que os usuários tem acesso). Por isso é importante termos um ambiente de staging.

O ambiente de staging é uma réplica do ambiente de produção. A ideia é que ele seja o mais parecido possível com este, pois no ambiente local, onde desenvolvemos e fazemos os primeiros testes, nem sempre conseguimos reproduzir o mesmo ambiente — alguns serviços são difíceis de executar localmente, outros possuem muitas dependências, e mesmo os que conseguimos executar localmente podem não funcionar da mesma forma em diferentes arquiteturas ou sistemas operacionais.

Configurando a infra-estrutura

Para criar um ambiente de staging, precisamos inicialmente preparar a infra-estrutura com configurações semelhantes às de produção. A arquitetura da nossa infra-estrutura, de forma simplificada, é como mostra a figura abaixo:

Arquitetura simplificada

Quando um usuário acessa jusbrasil.com.br, a requisição passa por um servidor nginx (que roda dentro de um cluster Kubernetes), e daí é encaminhada para um Service. Este componente seleciona os Pods que respondem pela app, e encaminha a requisição para um deles, como um balanceador de carga.

A arquitetura que desejamos montar se assemelha à da figura a seguir:

Arquitetura com ambiente de staging

Podemos perceber na imagem que agora temos um segundo Service e um segundo conjunto de Pods. Estes novos recursos responderão pelo ambiente de staging. Segue um exemplo fictício de como criá-los:

Deployment e Service da myapp-web em produção:

apiVersion: v1
kind: Deployment
metadata:
name: myapp-web
labels:
app: myapp
env: production
spec:
replicas: 10
# ...
---
apiVersion: v1
kind: Service
metadata:
name: myapp-web
labels:
app: myapp
env: production
spec:
selector:
app: myapp
env: production
ports:
- port: 5000
# ...

Deployment e Service da myapp-web em staging:

apiVersion: v1
kind: Deployment
metadata:
name: myapp-web-staging
labels:
app: myapp
env: staging
spec:
replicas: 2
# ...
---
apiVersion: v1
kind: Service
metadata:
name: myapp-web-staging
labels:
app: myapp
env: staging
spec:
selector:
app: myapp
env: staging
ports:
- port: 5000
# ...

As diferenças entre os Deployments de produção e staging estão destacadas em negrito nos YAMLs acima. Além dos nomes diferentes, adicionamos a label env com os valores production e staging em cada objeto. O uso dessa label no seletor dos Services permite distinguir as instâncias por ambiente.

Mudamos também o número de réplicas: como o ambiente de produção é acessado por todos os nossos usuários, precisa de um número maior de réplicas do que o de staging, que só é usado por nossos usuários internos, portanto recebe um tráfego muito menor.

Feitas as configurações acima, já conseguimos distinguir os dois ambientes: o Service myapp-web encaminha requisições para os Pods de produção, e o myapp-web-staging, para os Pods de staging. Agora precisamos expor o acesso a esses Services para fora do cluster Kubernetes.

Usamos o nginx para expor o acesso aos Services no Kubernetes. Uma opção seria criar uma URL diferente para o ambiente de staging, como um sub-domínio (algo como staging.meudominio.com.br, por exemplo). Porém, isso exigiria configurações mais complexas nos nossos serviços internos, como login, por exemplo. Por isso, optamos por seguir uma abordagem mais simples: mapear diferentes ambientes no mesmo domínio com o uso de cookies. Para que isso funcione, precisamos adicionar uma configuração como esta no nosso nginx:

upstream myapp-web {
server myapp-web:5000 max_fails=0;
}
upstream myapp-web-staging {
server myapp-web-staging:5000 max_fails=0;
}
map $cookie_staging_env $myapp_upstream {
default http://myapp-web;
"true" http://myapp-web-staging;
}
server {
listen 443;
server_name meudominio.com.br;
location /myapp {
proxy_pass $myapp_upstream;
}
}

Na configuração acima, inicialmente definimos um upstream para cada Service — como o próprio nginx roda no mesmo cluster Kubernetes, ele consegue acessar os Services pelos seus nomes (myapp-web e myapp-web-staging). Em seguida, criamos uma variável $myapp_upstream, cujo valor depende do cookie staging_env: caso ele esteja definido e com o valor true, o conteúdo da variável será http://myapp-web-staging; caso contrário, http://myapp-web. Finalmente, passamos o valor dessa variável para um proxy pass, dentro da location /myapp.

Feita essa configuração, se acessarmos o path /myapp do nginx, a requisição será encaminhada por padrão para o ambiente de produção. Porém, se a requisição incluir o cookie staging_env=true, ela será encaminhada para staging. Podemos fazer esse teste usando a ferramenta curl:

# requisição para produção
curl https://meudominio.com.br/myapp
# requisição para staging
curl -H "Cookie: staging_env=true" https://meudominio.com.br/myapp

Para acessar staging via browser, precisamos abrir o inspector (disponível em todos os browsers modernos) e digitar essa linha no console:

document.cookie = "staging_env=true; domain=.meudominio.com.br; path=/";

A partir desse momento, as requisições para https://meudominio.com.br/myapp serão direcionadas para o ambiente de staging. Ao remover esse cookie, as requisições posteriores serão direcionadas para produção. Mesma URL, dois ambientes diferentes!

Acessando via browser

Como acabamos de ver, agora basta usar um cookie para acessar o ambiente de staging. Porém, manipular cookies via console do browser não é prático, principalmente se o usuário não estiver habituado com esta ferramenta.

Para facilitar o acesso a staging por nossos usuários internos, criamos um item no menu do usuário para ligar ou desligar o acesso a esse ambiente via feature flag. Quando um usuário do Jusbrasil está logado como administrador da plataforma, ele tem acesso a um item Ambiente de staging no menu:

Feature flag para acessar o ambiente de staging

Ativando esta opção, o cookie é automaticamente injetado na página, e o usuário passa a acessar o ambiente de staging. Desativando esta opção, ele retorna ao ambiente de produção.

Um problema que acontecia eventualmente era alguns de nossos usuários internos ativarem o ambiente de staging e esquecerem se tinham desativado ou não — ou seja, não sabiam se estavam acessando produção ou staging. Para tornar visível qual é o ambiente atual, adicionamos um elemento na topbar, que só fica visível em staging:

Ambiente de staging ativado

Fazendo deploys para staging

Finalmente, para facilitar o deploy de código para o ambiente de staging, configuramos um pipeline específico no nosso servidor de integração contínua — o pipeline padrão faz deploy de todo código da branch master para produção. No novo pipeline, todo código enviado para a branch staging faz o deploy para este ambiente. Dessa forma, se quisermos testar um código de uma branch local em staging, precisamos fazer git push origin minha_branch:staging.

Com isso, conseguimos ter um ambiente para testar features antes de liberá-las em produção, de forma segura e simples.

--

--