Skip to content

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Garimpo Musical — Docker e Kubernetes

Aplicação web em três camadas (frontend, backend e banco) containerizada com Docker e implantada em Kubernetes local (Minikube) via Helm Chart, entregue como trabalho da disciplina de DevOps.

Sobre

O Garimpo Musical é uma plataforma para descobrir artistas independentes: mostra os lançamentos atuais, sugere artistas a partir da localização do usuário e tem perfil próprio para cada artista, com músicas e link para o Spotify. O acesso é protegido por uma tela de login, com cadastro próprio e escolha de avatar. Cada usuário pode seguir artistas e ver depois sua lista em uma aba "Seguindo".

O foco do trabalho não é a aplicação em si, e sim empacotá-la em contêineres, isolando frontend, backend e banco em camadas independentes que sobem com um único comando.

O que mudou desde a primeira entrega

A primeira versão era só leitura: a home listava álbuns e artistas vindos do banco e o perfil do artista mostrava as músicas. Para a segunda entrega o projeto ganhou duas funcionalidades novas, ambas com gravação no banco:

  • Autenticação completa: tela de login com cadastro próprio (nome, email, senha com indicador de força e escolha de avatar), sessão guardada no Postgres em vez de JWT, senhas com BCrypt e logout que apaga a sessão.
  • Seguir artistas: o botão "Seguir" no perfil do artista virou funcional (alterna para "Deixar de seguir" quando já segue) e foi adicionada uma nova aba "Seguindo" no menu lateral, listando os artistas seguidos.

Junto disso, as credenciais do banco e a porta do frontend foram movidas para um .env (com .env.example versionado).

Nesta terceira entrega, o projeto foi migrado do Docker Compose para Kubernetes (rodando localmente no Minikube), com um Helm Chart para orquestrar os três componentes, Ingress publicando o site em http://k8s.local e as imagens exportadas para o Docker Hub. A base com Docker Compose continua funcional em paralelo — a seção "Rodando no Kubernetes (Minikube)" mais abaixo explica tudo em detalhes.

Tecnologias

O frontend é uma aplicação React (Create React App) servida por Nginx. O mesmo Nginx atua como reverse proxy, encaminhando as chamadas /api/* para o backend.

O backend é um serviço Spring Boot 3.5 em Java 21, com Spring Data JPA sobre Hibernate. O build é feito com Maven dentro de um Dockerfile multi-stage, de modo que a imagem final só carrega o JAR e a JDK.

O banco é PostgreSQL 16 (postgres:16-alpine), com volume nomeado (postgres_data) para persistência dos dados.

A orquestração é feita pelo Docker Compose v2, no arquivo compose.yml da raiz do repositório.

Arquitetura

A decisão de design mais importante foi expor apenas uma porta ao host: a do frontend (localhost:3000). Backend e banco vivem dentro de uma rede Docker privada (garimpo-net) e não são acessíveis de fora.

Quando o navegador abre o site, o Nginx do frontend devolve o build do React. O JavaScript faz chamadas relativas para /api/..., que o Nginx encaminha internamente para http://backend:8080. O backend, por sua vez, fala com o banco em postgres:5432. Esses hostnames são resolvidos pelo DNS interno do Docker, sem precisar configurar IPs.

Esse arranjo dá três benefícios práticos: reduz a superfície de ataque (apenas uma porta exposta), elimina a necessidade de configurar CORS no backend (tudo vem do mesmo origin do ponto de vista do navegador) e permite usar URLs relativas no React, sem variável de ambiente.

A ordem de inicialização também está garantida: o Postgres tem um healthcheck com pg_isready, e o backend só sobe depois que o healthcheck passa.

Pré-requisitos

Apenas Docker Desktop (ou Docker Engine + Compose v2). Não é preciso ter Node, Java, Maven nem Postgres na máquina.

Configuração (variáveis de ambiente)

As credenciais do banco e a porta exposta do frontend são lidas de um arquivo .env na raiz do projeto. O repositório traz um .env.example com os valores padrão; basta copiá-lo:

cp .env.example .env

O .env está no .gitignore e não é versionado — só o .env.example, que serve de referência.

Como rodar

git clone https://github.com/digo-smithh/pratica-devops-garimpo-musical.git
cd pratica-devops-garimpo-musical
cp .env.example .env
docker compose up --build

Esse comando baixa a imagem do Postgres, constrói as imagens do backend e do frontend, cria a rede e o volume e sobe os três contêineres na ordem certa. Quando os logs estabilizarem (o Spring Boot vai imprimir Started BackendJavaApplication), abra http://localhost:3000.

Para parar, docker compose down. Para parar e apagar o volume com os dados do banco, docker compose down -v.

Acesso ao banco

O Postgres não é exposto no host (só vive na rede garimpo-net), então não dá para conectar com localhost:5432. Para inspecionar o banco, use docker exec direto no contêiner:

docker exec -it garimpo-postgres psql -U postgres -d garimpo-db

Credenciais padrão (definidas no .env):

  • host (interno do compose): postgres
  • porta: 5432
  • database: garimpo-db
  • usuário: postgres
  • senha: password

Login da aplicação

Ao abrir http://localhost:3000 o site cai numa tela de login. Há duas opções para entrar:

Usar a conta de demonstração (criada automaticamente pelo seed):

  • email: demo@garimpo.test
  • senha: demo123

Criar uma conta nova pelo botão "Criar conta", informando nome, email, senha (com indicador de força) e escolhendo um dos seis avatares disponíveis.

A autenticação é baseada em sessão guardada no banco: o POST /api/auth/login gera um token UUID, persistido na tabela session com expiração de 7 dias, e retornado para o frontend, que o armazena em localStorage. Cada chamada subsequente envia Authorization: Bearer <token>. Ao clicar em "Sair", o backend apaga a linha da session.

As senhas são guardadas com BCrypt na coluna password_hash da tabela app_user.

Endpoints da API

Todas as chamadas passam pelo Nginx do frontend (/api/...). Não há acesso direto ao backend a partir do host.

Públicos (sem token):

  • GET /api/home/albums — lista os álbuns em destaque.
  • GET /api/home/artists?city=&state= — lista artistas, com filtro opcional por cidade e estado.
  • GET /api/home/artists/{id} — detalhes de um artista, com as músicas.

Autenticação (escrevem no banco):

  • POST /api/auth/register — cria um app_user e já abre uma session.
  • POST /api/auth/login — verifica a senha (BCrypt) e abre uma nova session.
  • POST /api/auth/logout — apaga a session correspondente ao token enviado.
  • GET /api/auth/me — retorna o usuário do token (sem gravar nada).

Seguindo artistas (também autenticados, escrevem no banco):

  • GET /api/me/following — lista os artistas que o usuário segue.
  • GET /api/me/following/ids — só os IDs, usado pelo frontend para marcar o botão "Seguir/Deixar de seguir".
  • POST /api/me/following/{artistId} — começa a seguir um artista (retorna 201 se for novo, 204 se já seguia, 404 se o artista não existe).
  • DELETE /api/me/following/{artistId} — para de seguir (idempotente, sempre 204).

Os endpoints autenticados esperam o header Authorization: Bearer <token>, onde <token> é o UUID retornado por /login ou /register.

Persistência no banco

A aplicação não é só leitura — várias operações escrevem no Postgres em tempo real, em três tabelas criadas pelo Hibernate no startup:

  • app_user — uma linha por usuário cadastrado (nome, email, hash BCrypt da senha, avatar e data de criação).
  • session — uma linha por login ativo (token UUID, FK para o usuário, data de criação e expiração).
  • follow — uma linha por par (usuário, artista) seguido, com FKs para app_user e artist e UNIQUE em (user_id, artist_id) para evitar duplicatas.

O mapeamento de cada endpoint para o efeito no banco:

  • POST /auth/register faz INSERT em app_user e INSERT em session.
  • POST /auth/login faz INSERT em session.
  • POST /auth/logout faz DELETE em session.
  • POST /me/following/{id} faz INSERT em follow (ignora se já existe).
  • DELETE /me/following/{id} faz DELETE em follow.

Os dados sobrevivem entre execuções porque o compose.yml declara um volume nomeado para o diretório de dados do Postgres:

volumes:
  - postgres_data:/var/lib/postgresql/data

Verificando que está funcionando

docker compose ps deve mostrar três contêineres no ar: o frontend com mapeamento 0.0.0.0:3000->80/tcp, e o backend e o Postgres mostrando apenas a porta interna, sem mapeamento. Isso é o esperado.

Como confirmação, uma chamada direta ao backend a partir do host falha (curl http://localhost:8080 retorna Connection refused), mas a mesma chamada feita através do proxy do frontend funciona normalmente (curl http://localhost:3000/api/home/albums devolve a lista de álbuns em JSON).

Decisões de projeto

A versão do Postgres foi fixada em 16-alpine em vez da tag latest, porque o Postgres 18 introduziu mudanças incompatíveis na estrutura do diretório de dados.

A dependência spring-boot-docker-compose do Spring Boot foi desabilitada explicitamente (SPRING_DOCKER_COMPOSE_ENABLED=false). Esse plugin tenta subir os serviços do compose automaticamente quando detecta o arquivo, o que faz sentido em desenvolvimento local mas atrapalha quando o backend já está rodando dentro de um container.

O npm install do frontend foi mantido em vez de npm ci porque o package-lock.json original estava dessincronizado de algumas dependências — como o foco do trabalho é Docker, deixei a instalação mais permissiva no Dockerfile.

Rodando no Kubernetes (Minikube)

Toda a stack também pode ser executada dentro de um cluster Kubernetes local, usando Minikube como cluster e Helm como gerenciador de pacotes. A aplicação é publicada por meio de um Ingress, acessível em http://k8s.local.

Pré-requisitos

No macOS: brew install minikube kubectl helm.

Configuração inicial (só uma vez)

Adicione a linha abaixo em /etc/hosts para que o navegador resolva k8s.local para o loopback (necessário porque o Minikube com driver Docker no macOS expõe o Ingress via minikube tunnel em 127.0.0.1):

127.0.0.1 k8s.local

Como subir tudo

Pela primeira vez (inicializa o cluster, builda as imagens e instala o chart):

./scripts/helm-up.sh -i -b

Depois, em outro terminal (precisa continuar rodando para o Ingress ficar acessível):

sudo minikube tunnel

Abra http://k8s.local no navegador — o site é servido pelo frontend do cluster, que fala com o backend via Service interno, que por sua vez fala com o Postgres.

Nas próximas vezes (cluster já inicializado, sem precisar buildar imagens de novo):

./scripts/helm-up.sh

Como derrubar

./scripts/helm-down.sh        # remove só o release do Helm (dados persistem)
./scripts/minikube-down.sh    # para o cluster (mantém o disco)

Para apagar tudo e recomeçar do zero: minikube delete.

Estrutura do Helm Chart

O chart fica em garimpo-chart/ e é organizado como um chart guarda-chuva com um subchart por componente:

  • garimpo-chart/Chart.yaml — declara as dependências backend, frontend e db.
  • garimpo-chart/values.yaml — valores globais compartilhados (nomes de serviço, portas, host do Ingress).
  • garimpo-chart/templates/ingress.yaml — o Ingress que expõe k8s.local.
  • garimpo-chart/charts/db/ — subchart do Postgres (Secret, ConfigMap, PV, PVC, Deployment, Service).
  • garimpo-chart/charts/backend/ — subchart do backend (Deployment e Service).
  • garimpo-chart/charts/frontend/ — subchart do frontend (Deployment e Service).

Cada subchart tem seu próprio values.yaml com image.repository, image.tag, resources, etc.

Artefatos Kubernetes utilizados

O chart renderiza os seguintes objetos no cluster:

  • Deployment (3): um por serviço (garimpo-db, backend, frontend), cada um com uma réplica.
  • Service (3): garimpo-db:5432, backend:8080 e frontend:80, todos do tipo ClusterIP — o cluster resolve os hostnames internamente via DNS.
  • ConfigMap (1): garimpo-db-configmap, com host, port e database, consumido pelo backend.
  • Secret (1): garimpo-db-secret, com usuário e senha do Postgres em base64, injetados como variáveis de ambiente.
  • PersistentVolume + PersistentVolumeClaim: garimpo-db-pv (hostPath, 2 Gi) e garimpo-db-pvc, montados em /var/lib/postgresql/data para que os dados sobrevivam a restarts.
  • Ingress: garimpo-ingress, com ingressClassName: nginx e host k8s.local, roteando / para o Service do frontend.

Os mesmos manifests também estão disponíveis em K8s/ como YAMLs brutos (sem Helm), servindo de referência didática do que o chart produz.

Configuração do backend (URL do banco)

O backend recebe do cluster cinco variáveis (DB_HOST, DB_PORT, DB_NAME, DB_USERNAME, DB_PASSWORD) — as três primeiras vêm do ConfigMap e as duas últimas do Secret. O Helm/K8s compõe a URL JDBC via interpolação $(VAR) e injeta o resultado em SPRING_DATASOURCE_URL, que o Spring Boot usa direto sem depender de nenhuma outra configuração.

Imagens no Docker Hub

As imagens são publicadas no Docker Hub em:

  • docker.io/rdgsmithh/garimpo-backend:1.0
  • docker.io/rdgsmithh/garimpo-frontend:1.0

O chart já aponta para elas. Para regerar e publicar as imagens (multi-arch amd64 + arm64):

docker login
./scripts/docker-hub-push.sh rdgsmithh 1.0

Localmente, para desenvolvimento, o helm-up.sh -b builda essas mesmas tags e faz minikube image load — como o imagePullPolicy é IfNotPresent, o cluster prefere a imagem local se ela existir.

Scripts

Todos ficam em scripts/ e seguem o padrão de flags -i (inicializa Minikube) e -b (builda imagens):

  • minikube-up.sh [-i] [-b] — inicia o cluster e/ou builda as imagens, sem instalar nada.
  • minikube-down.sh — para o cluster (dados no disco preservados).
  • helm-up.sh [-i] [-b] — mesmo do anterior + helm install ou helm upgrade do release garimpo.
  • helm-down.shhelm uninstall garimpo (mantém PV/PVC).
  • docker-hub-push.sh <usuário> [tag] — build multi-arch e push das duas imagens.

Verificando que está funcionando

Depois de subir, os comandos abaixo devem funcionar:

kubectl get pods                     # três pods Running: garimpo-db, backend, frontend
kubectl get svc                      # três Services ClusterIP
kubectl get ingress                  # garimpo-ingress com host k8s.local
curl http://k8s.local/api/home/albums   # JSON com os álbuns

A tela de login abre em http://k8s.local, e a conta demo (demo@garimpo.test / demo123) continua funcionando exatamente como no Docker Compose — inclusive as gravações no banco (registro, login, seguir artistas), já que o Postgres tem PV persistente.

Autor

Rodrigo Smith

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages