
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
StatefulSet
e 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
Se ficou com dúvidas ou curtiu essa parte, compartilha com alguém que está começando com Kubernetes.
💡 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! 🙌