
Se você não leu o posts Kubernetes Sem Enrolação que é onde tudo começa e Mão na massa com K3d e Spring Boot onde fizemos o deploy de uma aplicação Spring Boot no K3d então corre para ler e volta aqui, que dar tempo. Agora, vamos evoluir para um cenário mais realista e comum em ambientes de produção: o uso de volumes persistentes, bancos de dados com estado (como o PostgreSQL) e o uso do StatefulSet.
🧠 O que vamos abordar nessa parte:
- Diferença entre
PersistentVolume (PV)ePersistentVolumeClaim (PVC) - O que é um
StatefulSete quando usar - O que é um
Headless Service - Criar o deploy de um banco PostgreSQL com volume persistente
- Conectar a aplicação Spring Boot ao PostgreSQL
- Fazer uma consulta simples no banco a partir da aplicação
📦 PV vs PVC – Qual a diferença?
Essa parte confunde muita gente no início, mas pensa assim:
- PersistentVolume (PV): é o recurso físico (um volume em disco disponível no cluster).
- PersistentVolumeClaim (PVC): é um pedido de uso de um volume. O Pod não fala com o PV diretamente, ele fala com o PVC.
O PVC é tipo “quero 1GB de disco”, e o Kubernetes vai alocar um PV compatível com esse pedido.
🧱 O que é um StatefulSet?
Ao contrário do Deployment, o StatefulSet mantém a identidade de cada pod, mesmo quando são reiniciados.
Isso é útil para bancos de dados, como PostgreSQL, MySQL e Filas, porque:
- Cada pod recebe um nome fixo:
postgres-0,postgres-1, etc. - Cada pod mantém seu volume específico: mesmo que o pod caia, o volume é reusado
🌐 O que é um Headless Service?
Quando você usa um Service normal no Kubernetes, ele cria um LoadBalancer interno apontando para os pods. Com o Headless Service (clusterIP: None), o serviço apenas cria entradas DNS diretas para os pods, sem balanceamento. Isso é crucial para o StatefulSet, onde você precisa saber exatamente com qual pod está falando (ex: réplicas de um banco).
🐘 Deploy do PostgreSQL com volume
Vamos usar um StatefulSet simples para subir um PostgreSQL com volume persistente:
statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: "postgres"
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: myappdb
- name: POSTGRES_USER
value: myuser
- name: POSTGRES_PASSWORD
value: dev123
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: postgres-storage
spec:
storageClassName: local-path
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
service.yaml
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
ports:
- port: 5432
clusterIP: None
selector:
app: postgres
Agora que os manifestos já foram criados vamos fazer os deploys dos mesmos:
Observação: Precisamos estar com nosso ambiente pronto, seja com o K3d, Kind ou até mesmo com o Kubernetes disponibilizado por algum dos provedores de nuvem, como AWS, Azure, OCI, entre outros.
kubectl apply -f statefulset.yaml kubectl apply -f service.yaml
Eicha, será que está tudo ok? podemos checar
kubectl get pv kubectl get pvc kubectl get pods kubectl get svc
🧪 Testar a conexão com o banco
Vamos verificar se o PostgreSQL está rodando corretamente:
kubectl exec -it postgres-0 -- psql -U myuser -d myappdb
Dentro do terminal interativo, rode:
CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT);
INSERT INTO users (name) VALUES ('Maddytec');
SELECT * FROM users;
🔄 Conectando a aplicação Spring Boot
No application.yaml, altere:
spring:
datasource:
url: jdbc:postgresql://postgres:5432/myappdb
username: myuser
password: dev123
jpa:
hibernate:
ddl-auto: update
Já no pom.xm, adicione:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.36</version> </dependency>
Altere o endpoint de teste para retornar o nome do usuário que foi salvo da base de dados Postgres criando as seguintes classes:
User.java
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "users")
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
Aqui faça as alterações para buscar na base de dados o nome do usuário:
TestController.java
@RequiredArgsConstructor
@RestController
@RequestMapping("/test")
public class TestController {
private final UserRepository userRepository;
@GetMapping
public String test() {
User user = userRepository.findById(1L).orElse(null);
return "Username: " + user.getName();
}
}
Faça o rebuild sua aplicação com jib, e suba no K3d. A aplicação vai se conectar ao PostgreSQL dentro do cluster, caso você não lembre os passo pode relembrar no post Kubernetes: Mão na massa com K3d e Spring Boot
🎯 Conclusão
Agora você entende:
- A diferença entre PV e PVC
- Como o StatefulSet mantém a identidade dos pods com volumes
- Como o Headless Service permite comunicação direta com cada réplica
- Como subir um PostgreSQL com volume persistente
- Como conectar a aplicação Spring Boot ao banco de dados
💭 Ficou com alguma dúvida?
Deixa um comentário aqui — vai ser massa trocar ideia sobre isso!
👨💻👩💻 Conhece algum dev que tá de olho no Kubernetes?
Compartilha com ele(a), pode ser justamente o empurrãozinho que faltava pra começar! 🚀
💡 Ah, quase ia esquecendo!
Aqui está o link da live onde mostro na prática todos os passos apresentados neste artigo:
🎥 Kubernetes: StatefulSet de PostgreSQL no K3d
E claro… não esquece: Bora praticar! 🚀
☕ Me paga um café?
Se curtiu o conteúdo e quer me dar aquela moral, escaneia aí e me paga um cafezinho pra eu continuar compartilhando conhecimento de forma leve e prática com você 😄
Valeu demais pela força! 🙌