Projeto App com Docker
Neste laboratório você coloca no ar duas aplicações com Docker Compose: o Uptime Kuma, um painel que vigia se sites estão no ar, e um pequeno site servido pelo nginx. Tudo descrito num único arquivo, que liga, desliga, atualiza e recria as aplicações com um comando.
- O Docker instalado e o seu usuário autorizado a usá-lo.
- Um arquivo
compose.yamlcom dois serviços, portas e volumes. - Os dados do Uptime Kuma num volume, que sobrevive à recriação dos containers.
- Um procedimento testado de atualização e de backup do volume.
| Item | Detalhe |
|---|---|
| Tempo estimado | 1h |
| Pré-requisitos | Como Usar os Projetos, Docker, Docker Compose, tar |
| Onde roda | VM lab (servidor) + navegador do notebook |
As versões das imagens e do Docker mudam com frequência. Os números nas saídas abaixo são exemplos; o formato é o que importa.
As etapas
-
Preparar a VM
$ multipass launch 26.04 --name lab --disk 15G --memory 2G Launched: lab $ multipass list Name State IPv4 Image lab Running 10.85.140.27 Ubuntu 26.04 LTS $ multipass shell lab
Usamos 15 GB de disco porque imagens de containers ocupam espaço.
-
Instalar o Docker
O que vamos fazer e por quê: o Ubuntu traz o Docker nos repositórios oficiais:
docker.ioé o motor edocker-compose-v2adiciona o comandodocker compose. Depois, colocamos o usuário no grupodocker, para não precisar desudoem todo comando.$ sudo apt update && sudo apt install -y docker.io docker-compose-v2 … $ sudo usermod -aG docker ubuntu $ exit
Saia e entre de novo para o grupo novo valer:
$ multipass shell lab
$ docker run --rm hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world … Hello from Docker! « funcionou This message shows that your installation appears to be working correctly. …
O grupo docker equivale a rootQuem está no grupo
dockerconsegue montar qualquer pasta do sistema dentro de um container e alterá-la. Só coloque nele quem já poderia tersudo.Se deu errado: "permission denied while trying to connect to the Docker daemon socket"$ docker ps permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: …
O que aconteceu: você não saiu e entrou de novo depois do
usermod, então a sessão ainda não tem o grupodocker.Como confirmar:
groupsnão mostradocker.Como resolver:
exitemultipass shell lab. -
Escrever o compose.yaml
O que vamos fazer e por quê: em vez de lembrar comandos
docker runlongos, descrevemos as aplicações num arquivo. Ele vira a "receita" do servidor: dá para versionar no Git e recriar tudo em outra máquina.$ mkdir -p ~/monitor/site && cd ~/monitor $ echo '<h1>Site no container</h1>' > site/index.html $ nano compose.yaml
services: kuma: # nome do serviço image: louislam/uptime-kuma:1 # imagem e versão (tag) container_name: kuma restart: unless-stopped # volta sozinho após reboot ou falha ports: - "3001:3001" # porta da VM : porta do container volumes: - kuma-dados:/app/data # volume nomeado: dados persistentes site: image: nginx:alpine container_name: site restart: unless-stopped ports: - "8080:80" volumes: - ./site:/usr/share/nginx/html:ro # pasta da VM, só leitura (ro) volumes: kuma-dados: # declara o volume usado acimaConceito No arquivo Significado Imagem image: nginx:alpineO "molde" do container. O que vem depois do :é a versão (tag).Porta "8080:80"Quem acessar a VM na porta 8080 chega na porta 80 do container. Sempre fora:dentro. Volume nomeado kuma-dados:/app/dataUma área de dados gerenciada pelo Docker. Sobrevive quando o container é apagado. Bind mount ./site:/usr/…/html:roUma pasta da VM aparece dentro do container. Você edita os arquivos direto na VM. restart unless-stoppedReinicia sempre, a não ser que você pare o container de propósito. Confira se deu certo: o próprio Compose valida o arquivo. Se houver erro de indentação, ele diz a linha:
$ docker compose config --quiet && echo "arquivo válido" arquivo válido
-
Subir as aplicações
- docker compose
- A ferramenta que lê o
compose.yamlda pasta atual. - up
- Cria e liga tudo o que está descrito.
- -d
- Detached: roda em segundo plano e devolve o terminal.
$ docker compose up -d [+] Running 4/4 ✔ Network monitor_default Created ✔ Volume "monitor_kuma-dados" Created « o nome ganha o prefixo da pasta ✔ Container site Started ✔ Container kuma Started $ docker compose ps NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS kuma louislam/uptime-kuma:1 "/usr/bin/dumb-init …" kuma 40 seconds ago Up 39 seconds (healthy) 0.0.0.0:3001->3001/tcp, [::]:3001->3001/tcp site nginx:alpine "/docker-entrypoint.…" site 40 seconds ago Up 39 seconds 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp
Na primeira vez, antes dessas linhas, aparece o download (pull) das imagens. A coluna
STATUScomUpsignifica rodando;(healthy)significa que o teste de saúde da própria imagem passou.Confira se deu certo: do notebook:
$ curl http://10.85.140.27:8080 <h1>Site no container</h1>
E abra
http://10.85.140.27:3001no navegador: o Uptime Kuma pede para criar um usuário administrador.Se deu errado: "port is already allocated"O que aconteceu: outra coisa já usa a porta 8080 ou 3001 na VM.
Como confirmar:
sudo ss -tlnp | grep -E ':8080|:3001'.Como resolver: mude o número da esquerda, por exemplo
"8081:80", e rodedocker compose up -dde novo. -
Containers conversam pelo nome
O que vamos fazer e por quê: o Compose cria uma rede própria (
monitor_default), onde cada serviço é encontrado pelo nome. No Uptime Kuma, crie um monitor do tipo HTTP(s) com a URLhttp://site. Ele vai acessar o container do nginx pela rede interna, sem passar pela porta 8080.Confira pelo terminal:
$ docker run --rm --network monitor_default alpine wget -qO- http://site <h1>Site no container</h1> « "site" virou um endereço dentro da rede
Esse comando liga um container descartável (
alpine) na mesma rede do projeto e pede a página ao endereçosite. -
Ler os logs
$ docker compose logs --tail 3 site site | 10.85.140.1 - - [15/Oct/2026:15:02:11 +0000] "GET / HTTP/1.1" 200 30 "-" "curl/8.14.1" "-" site | 172.18.0.3 - - [15/Oct/2026:15:04:40 +0000] "GET / HTTP/1.1" 200 30 "-" "Uptime-Kuma/1.23.16" "-" site | 172.18.0.3 - - [15/Oct/2026:15:05:40 +0000] "GET / HTTP/1.1" 200 30 "-" "Uptime-Kuma/1.23.16" "-"
A primeira linha veio do notebook; as outras, do Kuma vigiando o site a cada 60 segundos, pelo IP interno
172.18.0.3. Usedocker compose logs -fpara acompanhar ao vivo (Ctrl+C para sair). -
Provar que os dados persistem
O que vamos fazer e por quê: containers são descartáveis; dados não podem ser. Vamos apagar os containers e recriá-los. O usuário e o monitor que você criou no Kuma precisam continuar lá.
$ docker compose down [+] Running 3/3 ✔ Container kuma Removed ✔ Container site Removed ✔ Network monitor_default Removed $ docker volume ls DRIVER VOLUME NAME local monitor_kuma-dados « o volume NÃO foi apagado $ docker compose up -d …
Confira se deu certo: abra o Kuma no navegador: o login e o monitor continuam lá.
down -vapaga os volumesdocker compose down -vremove também os volumes, ou seja, os dados. Só use quando quiser zerar tudo de propósito. -
Atualizar a aplicação
O que vamos fazer e por quê: atualizar um container é trocar a imagem por uma mais nova e recriá-lo. Como os dados estão no volume, nada se perde.
$ docker compose pull [+] Pulling 2/2 ✔ kuma Pulled ✔ site Pulled $ docker compose up -d [+] Running 2/2 ✔ Container site Running « imagem igual: nada muda ✔ Container kuma Started « imagem nova: container recriado $ docker image prune -f Total reclaimed space: 0B
O
image pruneapaga imagens antigas que nenhum container usa mais, liberando disco. A tag:1nocompose.yamlgarante que você recebe atualizações da versão 1, sem pular sozinho para uma versão nova que poderia ser incompatível. -
Backup e restauração do volume
O que vamos fazer e por quê: um volume é uma pasta gerenciada pelo Docker. Para fazer backup, ligamos um container descartável (
alpine) com o volume e uma pasta da VM, e usamos otarpara empacotar.$ docker compose stop kuma « parar evita copiar o banco no meio de uma escrita $ docker run --rm -v monitor_kuma-dados:/dados:ro -v "$PWD":/backup alpine \ tar czf /backup/kuma-$(date +%F).tar.gz -C /dados . $ docker compose start kuma $ ls -lh kuma-*.tar.gz -rw-r--r-- 1 root root 1.2M Oct 15 15:20 kuma-2026-10-15.tar.gz- docker run --rm
- Roda um container e apaga-o ao terminar.
- -v monitor_kuma-dados:/dados:ro
- O volume aparece em
/dados, só leitura. - -v "$PWD":/backup
- A pasta atual da VM aparece em
/backup. - alpine
- Uma imagem Linux minúscula, só para ter o
tar. - tar czf … -C /dados .
- Compacta o conteúdo de
/dadosnum.tar.gz.
Teste a restauração num volume novo, sem tocar no original:
$ docker volume create kuma-teste kuma-teste $ docker run --rm -v kuma-teste:/dados -v "$PWD":/backup alpine \ tar xzf /backup/kuma-2026-10-15.tar.gz -C /dados $ docker run --rm -v kuma-teste:/dados alpine ls /dados docker kuma.db « o banco de dados do Kuma está lá screenshots upload $ docker volume rm kuma-testePara restaurar de verdade:
docker compose down, apague e recrie o volumemonitor_kuma-dados, extraia o backup nele e rodedocker compose up -d. A lista exata de arquivos dentro do volume pode variar com a versão do Kuma. -
Cuidado: o Docker passa por cima do ufw
O que vamos fazer e por quê: o Docker cria regras de firewall próprias para as portas publicadas, que valem antes das regras do ufw. Vamos ver isso acontecer.
$ sudo ufw allow OpenSSH && sudo ufw --force enable … Firewall is active and enabled on system startup $ sudo ufw status Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6)
$ curl http://10.85.140.27:8080 <h1>Site no container</h1> « continua acessível, mesmo sem regra no ufw!
Como proteger: publique a porta só no próprio servidor, trocando
"8080:80"por"127.0.0.1:8080:80". Assim, só o próprio servidor acessa, e você expõe a aplicação por um nginx na frente (proxy reverso, com HTTPS). Veja Servidor Web (nginx).
Quando não funciona
$ docker compose ps NAME IMAGE … STATUS kuma louislam/uptime-kuma:1 … Restarting (1) 5 seconds ago
O que aconteceu: o programa dentro do container está terminando com erro.
Como confirmar: docker compose logs --tail 30 kuma mostra o motivo, por exemplo uma permissão ou um volume errado.
O que aconteceu: você rodou docker compose fora da pasta do projeto.
Como resolver: cd ~/monitor, ou use docker compose -f ~/monitor/compose.yaml ….
Como confirmar: docker system df mostra quanto ocupam imagens, containers e volumes.
Como resolver: docker image prune -a apaga as imagens que nenhum container usa. Volumes nunca são apagados por esse comando.
O que você aprendeu
- Instalar o Docker pelos repositórios do Ubuntu e os riscos do grupo
docker. - Descrever aplicações em
compose.yaml: imagem, portas, volumes erestart. up -d,ps,logs,exec,down,pull.- A diferença entre volume nomeado e bind mount, e que
down -vapaga dados. - Fazer e testar o backup de um volume com um container descartável.
- Que portas publicadas pelo Docker ignoram o ufw.
Desafios extras
- Coloque um nginx na frente do Kuma como proxy reverso em
http://kuma.lab, e publique a porta 3001 só em127.0.0.1. - Automatize o backup do volume com o script e o timer do Backup Automático.
- Guarde a pasta
~/monitornum repositório Git e recrie tudo numa VM nova só comgit cloneedocker compose up -d.
Desfazer e limpar
$ docker compose down -v « aqui sim: apaga containers E volumes $ exit
$ multipass delete --purge lab
Exercícios
1. Subir em segundo plano
Complete o comando que cria e liga tudo, devolvendo o terminal:
2. Porta
Complete para que a porta 9000 da VM leve à porta 80 do container:
3. Você rodou docker compose down. O que aconteceu com o volume monitor_kuma-dados?
4. O ufw só libera SSH, mas a porta 8080 de um container continua acessível. Por quê?