Neste post, vamos aprender passo a passo como subir uma aplicação Spring Boot conectada a um banco de dados MySQL dentro do Kubernetes.
A ideia é ser didático e acessível, então, se você está começando agora, siga com calma cada etapa. Algumas instruções podem variar de acordo com o sistema operacional. Estou usando macOS, mas com pequenas adaptações, tudo funcionará em outros sistemas.
Pré-requisitos
Antes de começar, certifique-se de ter:
Docker instalado
Kubernetes instalado (Minikube, Docker Desktop ou outro)
Maven instalado (opcional, mas recomendado para build manual)
Postman ou cURL para testar a API
Se não tiver esses itens configurados ainda, recomendo procurar tutoriais específicos de instalação.
Etapa 1: Criando a Aplicação Spring Boot
Nossa aplicação será uma API REST simples para gerenciar pedidos (Order). Ela terá as operações básicas de criar e buscar pedidos. Vamos focar no essencial para que o projeto seja leve e fácil de entender.
Estrutura do Projeto
Vamos começar definindo a estrutura de dados para o nosso pedido.
@Getter
@Setter
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue
private long id;
private String productName;
private int quantity;
public Order(long id, String productName, int quantity) {
this.id = id;
this.productName = productName;
this.quantity = quantity;
}
}
O próximo passo é criar o repositório. Usaremos o JpaRepository, uma interface do Spring Data que já vem com operações básicas de CRUD.
public interface OrderRespository extends JpaRepository<Order, Long> {
}
Por fim, vamos criar o controller, que será responsável por expor os endpoints da API e interagir diretamente com o banco de dados por meio do repositório.
@RestController
@RequestMapping("/orders")
public class OrderController {
private OrderRespository orderRespository;
public OrderController(final OrderRespository orderRespository) {
this.orderRespository = orderRespository;
}
@PostMapping
public ResponseEntity<Order> create(@RequestBody Order order) {
Order response = orderRespository.save(order);
return ResponseEntity.ok(response);
}
@GetMapping("/{id}")
public ResponseEntity<Order> getOrderById(@PathVariable Long id) {
return orderRespository.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
}
Agora, vamos ver as configurações definidas no arquivo application.yaml
, responsável por centralizar as propriedades da aplicação.
spring:
datasource:
url: jdbc:mysql://${DB_HOST}/${DB_NAME}?useSSL=false
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
initialization-fail-timeout: 0 # Disable initialization timeout
jpa:
database-platform: org.hibernate.dialect.MySQLDialect
generate-ddl: true
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
Algumas das informações acima estão parametrizadas com variáveis de ambiente. Vamos configurá-las mais adiante, mas você já pode deixá-las prontas no seu projeto.
Agora, vou compartilhar o conteúdo do pom.xml, que define as dependências e configurações do projeto. Assim, garantimos que todos estejam utilizando o mesmo ambiente e consigam reproduzir os resultados.
Etapa 2 – Criando a imagem Docker
Agora, vou compartilhar o conteúdo do meu Dockerfile. É nele que definimos como a imagem Docker da aplicação será criada e executada.
Embora fosse possível usar uma versão mais leve, como a slim-buster
, optei por manter a imagem padrão para facilitar o entendimento.
# configurando o maven
FROM maven:3.9.6-eclipse-temurin-17 AS build
WORKDIR /kubernetes
COPY . .
RUN mvn clean package -DskipTests
# configurando o java
FROM openjdk:17
EXPOSE 8080
WORKDIR /kubernetes
COPY --from=build /kubernetes/target/*.jar kubernetes-0.0.1-SNAPSHOT.jar
ENTRYPOINT ["java", "-jar", "/kubernetes/kubernetes-0.0.1-SNAPSHOT.jar"]
A imagem Docker foi dividida em duas partes: uma para compilar o código com o Maven e outra para executar a aplicação com Java. Essa separação é útil caso, no futuro, você queira automatizar o processo de build e deploy com ferramentas de CI/CD e Kubernetes.
Antes de continuar, verifique se sua estrutura de pastas está assim:
Etapa 3 – Criando os arquivos do Kubernetes
Agora vamos começar a criar os arquivos de configuração do Kubernetes. O primeiro será o Deployment
, responsável por criar e gerenciar o Pod
onde nossa API será executada.
apiVersion: apps/v1
kind: Deployment
metadata:
name: crud-deployment
spec:
selector:
matchLabels:
app: crud-deployment
replicas: 1
template:
metadata:
labels:
app: crud-deployment
spec:
containers:
- name: crud-deployment
image: springboot-k8s:1.0.8
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: mysql-config
key: dbHost
- name: DB_NAME
valueFrom:
configMapKeyRef:
name: mysql-config
key: dbName
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: mysql-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
Podemos observar que essa configuração define os seguintes parâmetros:
- name: crud-deployment
image: springboot-k8s:1.0.8
imagePullPolicy: IfNotPresent
No campo image
, colocamos o nome da nossa imagem Docker, que será usada para criar o container no Pod. O imagePullPolicy: IfNotPresent
garante que o Kubernetes só irá buscar a imagem no Docker Hub se ela ainda não estiver disponível localmente. Logo abaixo, temos as env
, que são variáveis de ambiente. Elas se conectam com os dados do nosso application.yaml
, mas ainda precisam ser configuradas.
A seguir, vamos criar o recurso Service
, responsável por expor nossa aplicação na porta 8080.
apiVersion: v1
kind: Service
metadata:
name: crud-svc
spec:
selector:
app: crud-deployment
type: NodePort # or LoadBalancer validar o segundo caso
ports:
- port: 8080 # porta do serviço
targetPort: 8080 # porta do container que sera exposta
Finalizamos a parte da API, então agora vamos configurar o banco de dados.
O primeiro passo é criar o Deployment, que define como o Kubernetes deve iniciar e manter o container do MySQL rodando corretamente.
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-deployment
labels:
app: mysql
tier: database
spec:
selector:
matchLabels:
app: mysql
tier: database
replicas: 1
template:
metadata:
labels: # Deve ser igual ao do servico e deployment
app: mysql
tier: database
spec:
containers:
- image: mysql:5.7
imagePullPolicy: IfNotPresent
name: mysql
args:
- "--ignore-db-dir=lost+found" # Ignore lost+found directory
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
- name: MYSQL_DATABASE
valueFrom:
configMapKeyRef:
name: mysql-config
key: dbName
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc
Nesse arquivo, definimos as portas e os volumes necessários para o funcionamento do MySQL, além das variáveis de ambiente que representam as credenciais de acesso, que ainda serão configuradas.
Agora, vamos criar o Service, que irá expor o banco de dados na porta padrão 3306
.
apiVersion: v1
kind: Service
metadata:
name: mysql-svc
labels:
app: mysql
tier: database
spec:
ports:
- port: 3306
targetPort: 3306
protocol: TCP
selector:
app: mysql
tier: database
clusterIP: None # Use None for headless service if needed
Agora vamos configurar o PersistentVolumeClaim (PVC), que será responsável por garantir o armazenamento persistente dos dados do nosso banco de dados.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
labels:
app: mysql
tier: database
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Com a configuração do banco de dados finalizada, agora vamos criar os recursos que irão fornecer os valores para os parâmetros utilizados anteriormente.
Começaremos pelo ConfigMap, que será responsável por armazenar as configurações que não são sensíveis, como o nome do banco e o host.
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
dbHost: mysql-svc
dbName: javatest
Agora vamos configurar a Secret, que será usada para armazenar informações sensíveis, como usuário e senha do banco de dados.
No exemplo abaixo, os dados estão comentados, mas em um ambiente de produção, o ideal seria utilizar um serviço de gerenciamento de segredos, como o AWS Secrets Manager, HashiCorp Vault ou o próprio Sealed Secrets do Kubernetes.
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
data:
username: cm9vdA== # encoded 'root'
password: cm9vdA== # encoded 'root'
Etapa 4 – Executando tudo
Vamos recapitular rapidamente o que foi feito até aqui:
Criamos os arquivos Deployment e Service da nossa API, que serão responsáveis por controlar a criação e exposição dos Pods da aplicação.
Em seguida, configuramos os arquivos de Deployment, Service e PersistentVolumeClaim para o banco de dados, garantindo seu funcionamento e armazenamento persistente.
Por fim, criamos o ConfigMap e a Secret, que armazenam respectivamente as variáveis de ambiente e informações sensíveis (como usuário e senha do banco).
Com tudo configurado, podemos agora executar os arquivos.
O primeiro passo é gerar a imagem da aplicação com base no Dockerfile.
Caso esteja usando o Minikube localmente, execute o seguinte comando no diretório onde está seu Dockerfile:
eval $(minikube docker-env)
Esse comando faz com que o terminal passe a usar o Docker interno do Minikube, tornando sua imagem visível para o cluster. Assim, ao executar os manifests do Kubernetes, o Minikube conseguirá encontrar e rodar sua imagem sem precisar baixá-la de um repositório externo.
Agora vamos executar o comando responsável por gerar a imagem Docker da nossa aplicação, utilizando o Dockerfile que criamos anteriormente. No terminal, dentro do diretório onde está localizado o Dockerfile, execute o seguinte comando:
docker build -t springboot-k8s:1.0.8 .
Neste exemplo, estou utilizando a versão 1.0.8 da imagem, mas você pode começar com a versão 1.0.0 e, futuramente, atualizar a tag no seu arquivo de Deployment do Kubernetes conforme necessário.
Para verificar se a imagem foi criada com sucesso, execute o seguinte comando no terminal:
docker images
Você deverá ver a imagem que acabou de ser gerada listada entre os resultados.
Agora, com tudo pronto, vamos aplicar os arquivos de configuração do Kubernetes para subir nossa aplicação.
kubectl apply -f infra/
Caso o comando para aplicar todos os arquivos de uma vez não funcione corretamente — seja por erro de formatação em algum arquivo ou problema pontual —, você pode aplicar cada um manualmente com o seguinte comando:
kubectl apply -f nome-do-arquivo.yaml
Basta repetir esse comando para cada arquivo de configuração (como deployment.yaml
, service.yaml
, configmap.yaml
, etc.), garantindo que todos os recursos sejam criados corretamente no cluster.
Se tudo ocorreu conforme o esperado, você poderá verificar os pods em execução com o seguinte comando:
kubectl get pods
Esse comando lista todos os pods ativos no cluster e seus respectivos status. Se tudo estiver certo, o status dos pods deve aparecer como Running
.
Etapa 5 – Acessando a aplicação
Com tudo em execução, agora podemos fazer uma requisição para testar nossa API.
No entanto, se você estiver utilizando o Minikube, talvez precise criar um túnel para expor o serviço externamente, dependendo de como o ambiente foi configurado.
Para isso, execute o comando abaixo:
minikube service <nome-do-serviço>
Esse comando abrirá um endpoint acessível via navegador ou ferramentas como o Postman.
Se tiver dúvidas sobre qual nome usar, você pode listar todos os serviços disponíveis com:
minikube service list
A saída será parecida com isso:
Ao executar o comando:
minikube service <nome-do-serviço>
o Minikube criará automaticamente um túnel de conexão, abrindo uma URL no navegador (ou exibindo no terminal) que permite o acesso direto à sua API rodando dentro do cluster Kubernetes.
Esse túnel expõe a porta configurada no Service, facilitando testes locais sem a necessidade de configurar um Ingress ou LoadBalancer manualmente.
Etapa 6 – Testando via Postman
Agora que a aplicação está rodando e exposta corretamente, podemos fazer nossas requisições de teste usando o Postman (ou qualquer cliente HTTP de sua preferência).
Se você quiser remover os recursos criados (como Pods, Services, Deployments, etc.), basta executar o comando abaixo no diretório onde estão os seus arquivos de configuração:
kubectl delete -f infra/
Esse comando irá deletar todos os recursos definidos na pasta infra/
, limpando o ambiente do cluster.
Espero que este post tenha sido claro e que você tenha conseguido executar todos os passos com sucesso.
Caso encontre algum problema ou tenha dúvidas durante o processo, deixe um comentário — ficarei feliz em tentar ajudar!
🚀 Próximo passo
No próximo post, vamos avançar mais um pouco, vamos configurar um Load Balancer
e adicionar o Ingress
à nossa estrutura Kubernetes, permitindo que diferentes aplicações se comuniquem entre si de forma controlada e organizada.
Top comments (0)