Contêineres e serviços Docker: entendendo o Docker com hambúrgueres

O que realmente significa “um combo de hambúrguer, por favor”?

Na última vez, usamos um Dockerfile para criar nossa própria “imagem Docker (Image)”. Isso é como uma “receita que nunca muda”, na qual está escrito exatamente em que ordem colocar o hambúrguer no pão e depois a alface por cima.

Agora é hora de usar essa receita para começar o negócio de verdade, isto é, o serviço. Mas, quando você começa a usar Docker, aparecem termos confusos. Container, Service, Stack… todos parecem parecidos, então qual é exatamente a diferença?

Se você não entender bem esses conceitos, depois vai ficar perdido ao escrever scripts de deploy, perguntando coisas como: “Por que eu subi o contêiner e o serviço ainda não levantou?” Para organizar hoje essa árvore genealógica, trouxe a linguagem universal do mundo inteiro: a analogia da hamburgueria.

Você faz um hambúrguer, o contêiner, seguindo a receita, a imagem, gerencia o mesmo menu como serviço e o vende agrupado como combo, a stack.

1. Contêiner (Container): o hambúrguer feito depois do pedido

  • Definição: é a “entidade real” que surge quando a imagem é executada e carregada na memória.
  • Característica: não dá para entregar ao cliente o papel da receita (a imagem). É preciso servir um “hambúrguer (contêiner)” quentinho, preparado conforme a receita.
  • Comando: docker run my-burger

Se eu subir uma instância de servidor, isso significa que executei “um contêiner”.

2. Serviço (Service): gerenciamento de estoque do mesmo menu

Chega a hora do almoço e os clientes aparecem em massa. Na velocidade de preparar um hambúrguer por vez, já não dá para acompanhar. Então decidimos aumentar a equipe da cozinha e deixar três “cheeseburgers” prontos de antemão. A partir desse momento, eu não gerencio mais apenas “um cheeseburger”, mas toda a “linha de cheeseburgers”, ou seja, o Service.

  • Definição: um “conjunto de contêineres” criados a partir da mesma imagem.
  • Característica: é possível atribuir uma “política” como “mantenha sempre 3 unidades deste menu em estoque (Replicas=3)”. Se um hambúrguer cair no chão (erro), um novo é assado automaticamente.
  • Objetivo: “escalar horizontalmente (Scale-out)”. Distribuição de tráfego e estabilidade.

3. Stack: o combo, ou seja, o pacote completo

Comer só um hambúrguer não basta. Você também precisa de batatas fritas, a DB, e de refrigerante, Redis. Só quando tudo isso se junta é que você tem finalmente “uma refeição completa”, ou seja, a aplicação.

  • Definição: o “grupo de nível mais alto” que reúne serviços diferentes (web, DB, cache).
  • Característica: o hambúrguer e a cola precisam estar na mesma bandeja para ficar fácil de comer (conexão de rede). Quando são agrupados em uma stack, ganham um espaço exclusivo (Network) onde podem se comunicar entre si.

O problema: vou ficar sem fôlego só de fazer o pedido

O conceito é claro, mas, quando chega a hora de operar isso de verdade, a cabeça começa a doer. O serviço web, ou seja, a stack, que queremos construir normalmente precisa de componentes como os seguintes.

  1. Spring Boot (Burger): 2 pratos principais (redundância)
  2. PostgreSQL (Fries): 1 acompanhamento
  3. Redis (Coke): 1 bebida

Subir tudo isso com comandos crus do Docker, docker run, é como fazer um pedido no balcão deste jeito.

“Oi, eu queria um ‘cheeseburger (Spring)’, mas por favor deixe o hambúrguer medium-well, sem picles e com bastante molho. Ah, e também me dê ‘batatas fritas (DB)’, mas sem sal e com três sachês de ketchup. Por último, a ‘cola (Redis)’ pode ser cola zero e só com metade do gelo. E coloque essas três coisas em ‘uma única bandeja (Network)’, por favor.”

# Na pratica voce tem que digitar comandos assim toda vez
$ docker run -d --name my-web -e PATTY=medium -e PICKLE=no -p 8080:8080 ...
$ docker run -d --name my-db -e SALT=no ...
$ docker run -d --name my-redis -e ICE=half ...

Será que você conseguiria repetir esse pedido enorme sem nenhum erro toda vez que fizer deploy? E se errar a ordem e as batatas saírem depois do hambúrguer? O cliente, isto é, o usuário, vai ficar irritado.

O salvador: Docker Compose – “O de sempre, por favor!”

Por isso os desenvolvedores pensaram: “E se escrevêssemos esse pedido complicado com antecedência em um ‘cardápio, documento’, para simplesmente poder dizer: ‘me dá o combo número 1’?”

Foi assim que nasceu o “Docker Compose”. Se definirmos nosso próprio “combo personalizado” em um arquivo chamado docker-compose.yml, o Docker se encarrega de mandar os pedidos para a cozinha e organizar tudo direitinho na bandeja.

Até um pedido personalizado complexo se resolve com uma frase só, “quero este aqui”, se ele já estiver escrito no cardápio.

[Action] Monte o seu próprio combo

Não vamos ficar só na teoria; vamos realmente subir, de uma vez, o servidor Spring e a DB locais. Tudo o que você precisa fazer é criar um arquivo docker-compose.yml na raiz do projeto e colar o conteúdo abaixo.

version: '3.8'

services:
  # 1. Batata frita (BD)
  my-database:
    image: postgres:14
    environment:
      POSTGRES_PASSWORD: mysecretpassword
    ports:
      - "5432:5432"

  # 2. Cheeseburger (Web)
  my-web-server:
    image: my-spring-app:latest
    ports:
      - "8080:8080"
    depends_on:
      - my-database # "As batatas precisam fritar primeiro antes do hamburguer sair" (ordem garantida)
    environment:
      DB_URL: jdbc:postgresql://my-database:5432/mydb

Agora diga a palavra mágica no terminal.

# "Um combo 1 (docker-compose.yml), por favor!"
$ docker-compose up -d

Resultado: o Docker lê esse arquivo-cardápio, sobe primeiro a DB e, quando a DB está pronta, inicia o servidor Spring. Esse é exatamente o começo do conceito de IaC, Infrastructure as Code, isto é, gerenciar a infraestrutura por código. Daqui para frente, até uma configuração de servidor complexa se resolve compartilhando apenas um “arquivo-cardápio”.

Encerrando: Works on My Machine? Works on ANY Machine!

A desculpa que iniciou esta série, “mas na minha máquina funciona…”, não cola mais. Nós entendemos o Linux, o OS, isolamos ambientes com Docker, o Container, e definimos serviços complexos em um único arquivo com Docker Compose.

Agora, desde que eu tenha o arquivo docker-compose.yml que escrevi, consigo servir exatamente o mesmo combo de hambúrguer no meu computador, no computador do líder do time ou em um servidor AWS em apenas um minuto.

Não somos mais “apenas coders”. Tornamo-nos “engenheiros” capazes de projetar não só o código que escrevemos, mas também o mundo, o ambiente, em que esse código vai rodar. Com isso, a série [Works on My Machine] chega ao fim.

A partir da próxima série, vamos entrar no mundo de [The Pipeline: automação de CI/CD], no qual entregamos às máquinas o processo cansativo de deploy sobre essa infraestrutura sólida.

Deixe um comentário