Desenvolvimento Local Simplificado com Kubernetes e Tilt.dev
Neste tutorial, vamos aprender como configurar e implantar uma aplicação Kotlin com Spring Boot em um cluster Kubernetes local, utilizando a ferramenta Tilt.
O código completo pode ser encontrado em: dev-with-tilt
1. Overview
Tilt é uma ferramenta projetada para simplificar e acelerar o desenvolvimento local de aplicações e microsserviços que utilizam contêineres ou Kubernetes. Com suporte a clusters K8s como MicroK8s, Kind, Minikube e também Docker Compose, o Tilt permite que desenvolvedores executem projetos localmente de forma eficiente.
A ferramenta automatiza a implantação de aplicações durante o desenvolvimento, oferecendo atualizações em tempo real aos arquivos do projeto através do recurso live_update, o que garante um ciclo de feedback contínuo e ágil. Além disso, o Tilt facilita o gerenciamento e compartilhamento de ambientes de desenvolvimento, automatizando tarefas repetitivas e integrando-se perfeitamente aos fluxos de trabalho existentes.
Recursos oferecidos pela ferramenta:
- Automação de workflows:
- Tilt automatiza a construção, implantação e monitoramento de aplicações em Kubernetes, incluindo a reconstrução automática de contêineres e o redeployment de serviços sempre que há mudanças no código.
- Feedback rápido:
- A ferramenta proporciona feedback quase em tempo real, com logs e eventos centralizados, facilitando a análise e resolução de problemas.
- Configuração simplificada:
- Utiliza um arquivo de configuração chamado Tiltfile, escrito em Starlark (baseado em Python), que descreve como o Tilt deve construir e implantar a aplicação.
- Integração com ferramentas de desenvolvimento:
- Integra-se com diversas ferramentas como Docker, Helm e Kubernetes, oferecendo um ambiente coeso e eficiente para desenvolvimento local.
- Suporte a microsserviços:
- Tilt é ideal para ambientes de microsserviços, permitindo que desenvolvedores trabalhem em múltiplos serviços simultaneamente e observem como eles interagem em tempo real.
- UI interativa:
- Possui uma interface web que mostra o estado atual dos serviços, logs e permite interações manuais como reiniciar serviços ou construir imagens.
2. Instalando e configurando o Tilt com MicroK8s
A instalação do Tilt é simples, porém precisamos nos atentar a alguns requisitos, como:
- Ter um cluster Kubernetes configurado e em execução.
- Docker instalado e em execução na máquina local.
Neste tutorial vamos utilizar o MicroK8s como cluster Kubernetes local, porém o Tilt suporta outros clusters, que estão listados em: Tilt - Supported Clusters
Nota: O Tilt também suporta Docker Compose, caso não tenha um cluster Kubernetes configurado.
Preparando o MicroK8s
Caso não tenha o MicroK8s instalado, siga o tutorial de instalação em:
Com o MicroK8s instalado precisamos habilitar 2 addons:
- DNS
- Implanta o CoreDNS, que é responsável pela resolução de endereços dentro do nosso cluster Kubernetes
- Registry
- Habilita um local registry, que será utilizado pelo Tilt para armazenar as imagens Docker.
Para habilitar os addons, execute o comando:
1
2
sudo microk8s.enable dns && \
sudo microk8s.enable registry
Por fim habilitaremos o MicroK8s como nosso cluster padrão, com o comando:
1
2
3
4
sudo microk8s.kubectl config view --flatten > ~/.kube/microk8s-config && \
KUBECONFIG=~/.kube/microk8s-config:~/.kube/config kubectl config view --flatten > ~/.kube/temp-config && \
mv ~/.kube/temp-config ~/.kube/config && \
kubectl config use-context microk8s
Instalando o Tilt
A instalação do Tilt é feita com o comando:
1
curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
3. Preparando nossa aplicação Kotlin com Spring Boot de exemplo
Para exemplificar o uso do Tilt, vamos criar um projeto em Kotlin com Spring Boot e automatizar o workflow de desenvolvimento.
Nosso exemplo será composto por 1 serviço:
- sample-tilt
- Aplicação gradle + Kotlin + Spring Boot que expõe uma API REST simples.
Para a execução da aplicação com Tilt, precisamos de 3 arquivos de configuração:
- Dockerfile
- Manifesto Kubernetes
- Tiltfile
Estrutura do projeto:
A estrutura do projeto será a seguinte:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
├── build.gradle
├── Dockerfile
├── gradle
├── gradle.properties
├── gradlew
├── gradlew.bat
├── kub-tilt.yaml
├── settings.gradle
├── src
│ ├── main
│ │ ├── kotlin
│ │ │ └── br
│ │ │ └── com
│ │ │ └── sampletilt
│ │ │ ├── SampleController.kt
│ │ │ └── SampleTiltApplication.kt
│ │ └── resources
│ │ ├── application.yml
│ │ └── logbback.xml
│ └── test
│ └── kotlin
│ └── br
│ └── com
│ └── sampletilt
│ └── SampleTilt2ApplicationTests.kt
└── Tiltfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package br.com.sampletilt
import mu.KotlinLogging
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class SampleController {
val logger = KotlinLogging.logger { }
@GetMapping("/hello")
fun hello() = Sample("world").also {
logger.info { "hello: received call to sample" }
}
}
data class Sample(val response: String)
1
2
3
4
5
6
7
8
9
10
11
package br.com.sampletilt
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class SampleTiltApplication
fun main(args: Array<String>) {
runApplication<SampleTiltApplication>(*args)
}
Dockerfile
1
2
3
4
5
6
7
8
FROM openjdk:11.0.11-slim
WORKDIR /app
ADD BOOT-INF/lib /app/lib
ADD META-INF /app/META-INF
ADD BOOT-INF/classes /app
ENTRYPOINT java -cp .:./lib/* br.com.sampletilt.SampleTiltApplicationKt
Manifesto Kubernetes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-tilt
labels:
app: sample-tilt
spec:
selector:
matchLabels:
app: sample-tilt
template:
metadata:
labels:
app: sample-tilt
spec:
hostNetwork: false
containers:
- name: sample-tilt
image: sample-tilt-image
env:
- name: ENV
value: dev
- name: JAVA_OPTS
value: >-
-Duser.timezone=America/Sao_Paulo -Dfile.encoding=UTF8 -Xms512m -Xmx512m
- name: ENV_SBA_ACTIVE
value: 'true'
O Tiltfile
O Tiltfile é o arquivo de configuração que descreve como o Tilt deve construir, implantar e orquestrar a aplicação em nosso cluster Kubernetes.
Para configurar nosso Tiltfile, vamos seguir algumas etapas
Etapa 1 - Pré Configuração
Esta etapa não é obrigatória, mas é útil para definir variáveis que serão utilizadas posteriormente.
application_name
: Nome da aplicação, que será utilizado para identificar os recursos no Tilt.port
: A porta que nossa aplicação spring boot irá escutar e que será utilizada no foward.application_class
: A classe principal da aplicação.project_name
: Nome do projeto, que utilizaremos como label para identificar os recursos no Tilt.java_opts
: Parâmetros para a JVM.entrypoint_var
: Entrypoint para a execução do nosso container
1
2
3
4
5
6
7
# -*- mode: Python -*-
application_name = "sample-tilt"
port = 9082
application_class = 'br.com.sampletilt.SampleTiltApplicationKt'
project_name = 'til-dev-samples'
java_opts = '-Duser.timezone=America/Sao_Paulo -Dfile.encoding=UTF8 -Xms512m -Xmx1024m'
entrypoint_var = ['java', '-noverify', java_opts, '-cp', '.:./lib/*', application_class]
Etapa 2 - Configuração da etapa de compilação
Nesta etapa utilizaremos duas funções do Tilt:
load()
: Função utilizada para carregar recursos externos e extensões do Tilt.local_resource()
- Configura um ou mais comandos shell que serão executados na máquina host (não no cluster k8s).
- Por padrão, o Tilt executa uma atualização do local_resources sempre que um arquivo de dependência é alterado.
1
2
3
4
5
6
7
8
9
load('ext://restart_process', 'docker_build_with_restart')
local_resource(
application_name + '-compile',
"./gradlew" + ' bootJar && ' +
'unzip -o build/libs/*.jar -d build/jar-staging && ' +
'rsync --inplace --checksum -r build/jar-staging/ build/jar',
deps=['src', 'build.gradle'],
labels=project_name + "-compile")
Neste exemplo load('ext://restart_process', 'docker_build_with_restart')
carrega a extensão restart_process
,
que contém a função docker_build_with_restart
, responsável por reconstruir a imagem Docker e reiniciar o container,
nos auxiliando no live update.
Por fim, nosso local_resource
descompacta o jar para build/jar-staging
com o comando:
1
unzip -o build/libs/*.jar -d build/jar-staging`
e em seguida utiliza o rsync --checksum
para copiá-lo para build/jar
com o comando:
1
rsync --inplace --checksum -r build/jar-staging/ build/jar
O live_update do Tilt copiará todos os arquivos que foram modificados, já que rsync --checksum
copia o diretório, mas
não altera nenhum arquivo que não tenha sido modificado.
Etapa 3 - Construção da imagem Docker com live_update
Nesta etapa, utilizaremos a função docker_build_with_restart
que importamos anteriormente, para construir a imagem com
o live_update.
1
2
3
4
5
6
7
8
9
10
11
12
docker_build_with_restart(
application_name + '-image',
'./build/jar',
entrypoint=entrypoint_var,
dockerfile='./Dockerfile',
live_update=[
sync('./build/jar/BOOT-INF/lib', '/app/lib'),
sync('./build/jar/META-INF', '/app/META-INF'),
sync('./build/jar/BOOT-INF/classes', '/app'),
],
)
No docker_build_with_restart
, o primeiro argumento é o nome da imagem, que deve ser o mesmo nome que utilizamos no
nosso
manifesto Kubernetes. O segundo é o contexto, que neste caso será o diretório onde descompactamos nosso jar. O parâmetro
entrypoint
é o comando que será executado quando o container for iniciado. O parâmetro dockerfile
é o caminho para o
Dockerfile que utilizaremos para construir a imagem, e o parâmetro live_update
é uma lista de comandos sync que copiam
as bibliotecas, .class
e outros arquivos compilados do diretório build/jar
para dentro do container.
Etapa 4 - Configurando recursos do Kubernetes
Por fim, configuramos o Kubernetes para implantar nossa aplicação.
1
2
3
k8s_yaml('./kub-tilt.yaml')
k8s_resource(application_name,
resource_deps=[application_name + '-compile'], labels=project_name + "-pods", port_forwards=port)
k8s_yaml('./kub-tilt.yaml')
, carrega o arquivo de manifesto Kubernetes que criamos anteriormente que esta
em ./kub-tilt.yaml
k8s_resource(application_name, resource_deps=[application_name + '-compile'], labels=project_name + "-pods", port_forwards=port)
,
cria um recurso k8s com o nome da aplicação, que depende do recurso de compilação, adicionamos um label para identificar
os
pods no Tilt e configuramos o port_forward para a nossa variável port
.
Após todas as etapas configuradas, nosso Tiltfile deve ficar assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# -*- mode: Python -*-
application_name = "sample-tilt"
port = 9082
application_class = 'br.com.sampletilt.SampleTiltApplicationKt'
project_name = 'til-dev-samples'
java_opts = '-Duser.timezone=America/Sao_Paulo -Dfile.encoding=UTF8 -Xms512m -Xmx1024m'
entrypoint_var = ['java', '-noverify', java_opts, '-cp', '.:./lib/*', application_class]
# Extensão de live update para sincronizar arquivos
# Para mais exemplos de extensões, acesse: https://docs.tilt.dev/extensions.html
load('ext://restart_process', 'docker_build_with_restart')
local_resource(
application_name + '-compile',
"./gradlew" + ' bootJar && ' +
'unzip -o build/libs/*.jar -d build/jar-staging && ' +
'rsync --inplace --checksum -r build/jar-staging/ build/jar',
deps=['src', 'build.gradle'],
labels=project_name + "-compile")
docker_build_with_restart(
application_name + '-image',
'./build/jar',
entrypoint=entrypoint_var,
dockerfile='./Dockerfile',
live_update=[
sync('./build/jar/BOOT-INF/lib', '/app/lib'),
sync('./build/jar/META-INF', '/app/META-INF'),
sync('./build/jar/BOOT-INF/classes', '/app'),
],
)
k8s_yaml('./kub-tilt.yaml')
k8s_resource(application_name,
resource_deps=[application_name + '-compile'], labels=project_name + "-pods", port_forwards=port)
Executando o Tilt
Para executar o Tilt, basta executar o comando tilt up
na raiz do projeto.
1
tilt up
Dica: Caso receba um erro parecido com:
tls: failed to verify client certificate: x509: certificate signed by unknown authority
, execute os comandos abaixo:
1 2 sudo microk8s refresh-certs -e ca.crt microk8s config > ~/.kube/config
A saída do comando será algo parecido com:
1
2
3
4
5
6
7
Tilt started on http://localhost:10350/
v0.33.17, built 2024-06-12
(space) to open the browser
(s) to stream logs (--stream=true)
(t) to open legacy terminal mode (--legacy=true)
(ctrl-c) to exit
Pronto! Agora temos nossa aplicação em execução no Tilt, com live_update e pronto para ser desenvolvida.
Acesse o endereço http://localhost:10350/
no navegador para visualizar o dashboard do Tilt.
Para ter acesso ao overview do projeto, clique no botão Resource Name
e selecione um dos recursos, automaticamente
seremos
redirecionados para a página de Overview, onde podemos visualizar os logs, status e interagir com o serviço.
Para interromper o Tilt, basta pressionar ctrl-c
no terminal e em seguida executar o comando:
1
tilt down
Para mais detalhes sobre a UI do tilt, acesse a documentação oficial em: Tilt - UI
4. Leitura Adicional
Exemplos em outras linguagens
Escolhendo um local dev cluster
Documentação sobre clusters suportados pelo Tilt: Tilt - Choosing Clusters
Referências