Acelere seu ambiente de desenvolvimento local com o Tilt
Passamos horas e horas desenvolvendo aplicações nas nossas máquinas, cada vez com mais requisitos e complexidade. Qualquer aplicação moderna facilmente conta com vários containers, microsserviços, deploys em diferentes ambientes, diversas stacks, etc. Então qualquer ferramenta que possa tornar nosso fluxo mais ágil é de grande utilidade.
Neste post quero apresentar uma ferramenta muito poderosa, que pode fazê-lo economizar bastante tempo no seu processo de desenvolvimento. Trata-se do Tilt, que recentemente foi adquirida pela Docker.
Para demonstrar um pouco do que é possível fazer com o Tilt eu vou usar este meu repositório que usei em uma palestra sobre microsserviços. Os exemplos vão ser feitos em Go, mas na documentação oficial é possível visualizar como usá-lo com outras tecnologias e cenários.
Instalação
O primeiro passo é instalar o aplicativo de linha de comando. Para isso, no meu macOS eu executei:
curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
Na documentação é possível ver como fazer a instalação em outros sistemas operacionais.
Primeiros passos
O Tilt funciona lendo um arquivo chamado Tiltfile
na raiz do seu projeto. Ele possui uma sintaxe que lembra bastante Python
e a documentação é bem detalhada, mostrando todas as opções que podemos configurar.
O conteúdo do arquivo Tiltfile
ficou desta forma:
local_resource('auth', cmd='cd auth; go build -o bin/auth main.go',
serve_cmd='auth/bin/auth', deps=['auth/main.go', 'auth/security', 'auth/user', 'pkg'])
local_resource('feedbacks', cmd='cd feedbacks; go build -o bin/feedbacks main.go',
serve_cmd='feedbacks/bin/feedbacks', deps=['feedbacks/main.go', 'feedbacks/feedback', 'pkg'])
local_resource('votes', cmd='cd votes; go build -o bin/votes main.go',
serve_cmd='votes/bin/votes', deps=['votes/main.go', 'votes/vote', 'pkg'])
A função local_resource
configura comandos que vão ser executados na sua máquina local. O primeiro parâmetro é o nome que estamos dando para o recurso, que deve ser único dentro do Tiltfile
. O parâmetro cmd
contém o comando que vai ser executado. O comando contido dentro do parâmetro serve_cmd
vai ser executado pelo Tilt e é esperado que ele não termine. Ou seja, é o comando que vai executar nosso serviço.
O último parâmetro, o deps
é um dos mais interessantes. Ele indica quais diretórios do projeto o Tilt vai observar, e se acontecerem alterações ele vai automaticamente executar o processo. Isso significa que se acontecer qualquer alteração em auth/main.go
, auth/security
, auth/user
, ou pkg
o serviço auth
vai ser recompilado e executado novamente. Tratando-se de uma linguagem compilada como Go, isso é uma grande ajuda porque basta alterar o arquivo e ele já vai ser automaticamente gerado.
Como o nosso projeto é composto por três microsserviços, o restante do Tiltfile
configura o mesmo comportamento para todos.
Para executar o Tilt basta abrir um terminal e digitar:
tilt up
A seguinte ela é apresentada:
Pressionando a barra de espaço é nos apresentada a interface gráfica do Tilt, onde vamos passar bastante tempo:
Nesta tela podemos verificar o log de compilação de cada aplicação, bem como executar novamente o passo desejado. Ela também agrega os logs da aplicação, e nos permite fazer pesquisas neles:
Erros de compilação também aparecem nessa tela:
Somente estas funcionalidades que apresentei até agora já devem ser o suficiente para colocar o Tilt na sua lista de ferramentas a testar, certo? Mas vamos aprofundar um pouco mais.
Containers
Vamos agora aprimorar nosso ambiente. Ao invés de executarmos os binários localmente, vamos adicionar o recurso da criação e atualização automática de containers dos nossos microsserviços. Afinal, eles devem ser executados desta forma no ambiente de produção.
A nova versão do Tiltfile
ficou assim:
local_resource(
'auth-compile',
cmd='cd auth; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/auth main.go',
deps=['auth/main.go', 'auth/security', 'auth/user', 'pkg'],
)
docker_build(
'auth-image',
'./auth',
dockerfile='auth/Dockerfile',
)
local_resource(
'feedbacks-compile',
cmd='cd feedbacks; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/feedbacks main.go',
deps=['feedbacks/main.go', 'feedbacks/feedback', 'pkg'],
)
docker_build(
'feedbacks-image',
'./feedbacks',
dockerfile='feedbacks/Dockerfile',
)
local_resource(
'votes-compile',
cmd='cd votes; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/votes main.go',
deps=['votes/main.go', 'votes/vote', 'pkg'],
)
docker_build(
'votes-image',
'./votes',
dockerfile='votes/Dockerfile',
)
docker_compose('./docker-compose.yml')
Adicionei a função docker_build
que, como o nome sugere, faz a geração da imagem do container. Para isso foi preciso criar um Dockerfile
para cada microsserviço. Por exemplo, a do serviço auth
ficou desta forma:
FROM alpine
ADD bin/auth /
EXPOSE 8081
CMD ["/auth"]
A dos demais serviços ficou bem semelhante, só alterando o nome do executável e da porta: feedbacks
roda na porta 8082
e votes
na 8083
.
Ao fazer essa alteração o Tilt vai avisar que é necessário ter alguma forma de deploy dos containers, senão ele não vai funcionar. Uma forma de se fazer isso é criando um docker-compose.yml
e usá-lo na função docker_compose
. Seu conteúdo ficou assim:
version: "3"
services:
auth:
image: auth-image
ports:
- "8081:8081"
container_name: auth
feedbacks:
image: feedbacks-image
ports:
- "8082:8082"
container_name: feedbacks
votes:
image: votes-image
ports:
- "8083:8083"
container_name: votes
Uma última alteração no código foi necessária. No trecho de código que faz a comunicação entre os serviços, foi preciso alterar o endereço do serviço auth
.
O novo conteúdo ficou:
//@TODO get address from environment variables
req, err := http.Post("http://auth:8081/v1/validate-token", "text/plain", strings.NewReader(payload))
Observação: isso só foi preciso fazer porque o endereço estava fixo no código e o correto era estar em uma variável de ambiente, como é possível ver no @TODO
que nunca foi implementado :D
Com estas alterações o Tilt agora observa mudanças nos códigos do projeto e caso aconteçam ele faz a compilação, geração dos conteiners e atualização do ambiente!
Kubernetes!!
Agora vamos tornar a coisa um pouco mais séria! Vamos fazer com que o Tilt faça o deploy da nossa aplicação em um cluster Kubernetes. Para isso vou usar o minikube, solução que faz a instalação de um cluster local para desenvolvimento.
No macOS bastou executar:
brew install minikube
E depois de instalado foi só executar o comando:
minikube start
Agora que temos nosso cluster configurado vamos alterar o nosso Tiltfile
para refletir o novo ambiente:
load('ext://restart_process', 'docker_build_with_restart')
local_resource(
'auth-compile',
cmd='cd auth; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/auth main.go',
deps=['auth/main.go', 'auth/security', 'auth/user', 'pkg'],
)
docker_build_with_restart(
'auth-image',
'./auth',
dockerfile='auth/Dockerfile',
entrypoint=['/auth'],
live_update=[
sync('./auth/bin/auth', '/auth'),
],
)
k8s_yaml('auth/kubernetes.yaml')
k8s_resource('ms-auth', port_forwards=8081,
resource_deps=['auth-compile'])
local_resource(
'feedbacks-compile',
cmd='cd feedbacks; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/feedbacks main.go',
deps=['feedbacks/main.go', 'feedbacks/feedback', 'pkg'],
)
docker_build_with_restart(
'feedbacks-image',
'./feedbacks',
dockerfile='feedbacks/Dockerfile',
entrypoint=['/feedbacks'],
live_update=[
sync('./feedbacks/bin/feedbacks', '/feedbacks'),
],
)
k8s_yaml('feedbacks/kubernetes.yaml')
k8s_resource('ms-feedbacks', port_forwards=8082,
resource_deps=['feedbacks-compile'])
local_resource(
'votes-compile',
cmd='cd votes; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/votes main.go',
deps=['votes/main.go', 'votes/vote', 'pkg'],
)
docker_build_with_restart(
'votes-image',
'./votes',
dockerfile='votes/Dockerfile',
entrypoint=['/votes'],
live_update=[
sync('./votes/bin/votes', '/votes'),
],
)
k8s_yaml('votes/kubernetes.yaml')
k8s_resource('ms-votes', port_forwards=8083,
resource_deps=['votes-compile'])
Aqui temos um bom número de novidades.
A primeira é a função load
que faz o carregamento de extensions do Tilt. É uma forma de expandir as funcionalidades da ferramenta, e existem várias disponíveis. Aqui estamos usando a docker_build_with_restart
que vai fazer o trabalho de atualizar o container executando dentro do nosso cluster Kubernetes.
Outra mudança é relacionada às configurações do deploy das aplicações dentro do Kubernetes. A função k8s_yaml
indica qual é o arquivo que contém a “receita” que vai ser usada para o deploy. E a função k8s_resource
é usada aqui para fazer o redirecionamento da porta do cluster para o nosso ambiente local, facilitando os testes.
O conteúdo do arquivo auth/kubernetes.yaml
é:
apiVersion: v1
kind: Service
metadata:
labels:
app: ms-auth
name: ms-auth
spec:
ports:
- port: 8081
name: http
protocol: TCP
targetPort: 8081
selector:
app: ms-auth
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ms-auth
labels:
app: ms-auth
spec:
selector:
matchLabels:
app: ms-auth
template:
metadata:
labels:
app: ms-auth
spec:
containers:
- name: ms-auth
image: auth-image
ports:
- containerPort: 8081
Os demais arquivos são praticamente iguais, somente mudando o nome do binário e da porta.
Agora o Tilt faz todo o trabalho pesado para nós:
Para conferir se nossos microsserviços estão sendo executados no cluster podemos usar o comando:
kubectl get pods -n default
NAME READY STATUS RESTARTS AGE
ms-auth-7446897869-89r2j 1/1 Running 0 81s
ms-feedbacks-b5df67d6-wzbj2 1/1 Running 0 81s
ms-votes-76565ddc9c-nkkt7 1/1 Running 0 81s
Conclusões
Não sei se consegui com esse post demonstrar o quão empolgado estou com essa ferramenta! Venho usando o Tilt fazem algumas semanas em um projeto bem complexo, a criação de um Controller para o Kubernetes e graças a toda essa automação eu posso me concentrar apenas na lógica da aplicação, enquanto que o restante é feito automaticamente. E isso economiza muito tempo.
Um agradecimento ao meu colega Felipe Paes de Oliveira que me apresentou essa incrível ferramenta. E se você quer ver o Tilt sendo demonstrado pela grande Ellen Korbes, que trabalha lá, confere nesse vídeo.