Dockerfile: estender imagem, cache de build e Nginx no Docker
O FROM do Dockerfile aceita qualquer imagem, inclusive uma que você mesmo construiu, e a ordem das linhas decide quanto do docker build vem do cache. Aqui eu monto uma imagem em cima de outra, rodo um container em segundo plano com -d e publico uma página HTML com a imagem oficial do Nginx e o -p.
Estender uma imagem: do Ubuntu para a sua
Estender é pegar uma imagem pronta e incrementar. Partindo do Ubuntu 20.04 eu adiciono o Git, dou um nome ao resultado, ubuntu-custom, e passo a lançar containers dessa imagem, que já vem com coisas instaladas. Depois posso estender de novo, agora a partir do ubuntu-custom e não mais do Ubuntu.
O RUN executa os comandos na hora de construir a imagem, e o -y evita que o build fique parado esperando uma resposta:
FROM ubuntu:20.04
RUN apt-get update -y
RUN apt-get install git -y
Construa e entre no container:
docker build -t ubuntu-custom .
docker run -ti ubuntu-custom
Dentro do container, git --version responde: o Git já vem na imagem. O curl ainda não, e esse é o gancho para o cache.
Cache: por que a ordem das linhas importa
Acrescentei RUN apt-get install curl -y como quarta linha e construí de novo. As etapas 2 e 3 já tinham rodado e apareceram com Using cache; só a 4, a nova, executou do zero. Por isso o build ficou rápido.

A regra vale para a linha em que você mexe: da linha alterada em diante, o cache recomeça. Se eu tivesse mexido na linha 3, tudo dali para baixo rodaria de novo. Por isso o Dockerfile vai do mais genérico para o mais específico, ou do que muda menos para o que muda mais. O que você altera com frequência fica embaixo.
1Criar uma imagem a partir da sua
Agora não quero mais partir do Ubuntu 20.04. Crio um segundo arquivo, Dockerfile-custom, e digo que parto da imagem que acabei de construir. Para ter algo útil, clono o projeto da Bia, uma aplicação em Node e React (aqui o Git que eu coloquei na primeira imagem faz o serviço):
FROM ubuntu-custom
RUN git clone https://github.com/henrylle/bia.git
Como o arquivo não se chama Dockerfile, o -f diz qual usar. O contexto, o ponto no fim, continua obrigatório: na primeira tentativa eu esqueci e o Docker reclamou que o build exige um argumento.
docker build -t ubuntu-custom-2 -f Dockerfile-custom .
docker run -ti ubuntu-custom-2
O ls mostra a pasta bia na raiz do container, e dentro dela estão todos os arquivos do projeto. Todo container lançado de ubuntu-custom-2 já nasce com o Git instalado e o projeto baixado.

2Rodar em segundo plano com -d
Até aqui eu uso -ti, que deixa o terminal ligado ao container. Ele só vive enquanto eu estou dentro. Para rodar em segundo plano existe o -d (detached), que não prende o terminal.
Só que, rodando docker run -d ubuntu-custom-2, o container morre na sequência, e o docker ps não lista nada. Um container só continua rodando se algum processo o segura. No modo -ti quem segura é o terminal; aqui, terminou a execução, ele acaba.
Para o teste, usei um artifício. O tail -f fica imprimindo o conteúdo de um arquivo, e apontando para /dev/null ele nunca retorna nada e fica preso no processo. Coloco como ENTRYPOINT no Dockerfile-custom, o comando que o container executa sempre que é lançado:
FROM ubuntu-custom
RUN git clone https://github.com/henrylle/bia.git
ENTRYPOINT ["tail", "-f", "/dev/null"]
Construí de novo (as etapas anteriores vieram do cache, então foi rápido) e subi com -d:
docker build -t ubuntu-custom-2 -f Dockerfile-custom .
docker run -d ubuntu-custom-2
docker ps

O docker run -d devolve o ID do container, e agora o docker ps o lista rodando. Para parar, passe os primeiros caracteres do ID:
docker stop <primeiros-caracteres-do-id>
Esse truque é só para o exemplo. Numa imagem de verdade o ENTRYPOINT aponta para o processo que o container deve executar, como uma aplicação web ou um trabalho que roda e termina. Quando o processo acaba, o container acaba, e é isso que se quer: o container tem um conceito de finitude, por isso é tão usado em microsserviços.
3Usar uma imagem pronta do Docker Hub, o Nginx
Em vez de partir de um sistema operacional, dá para partir de uma imagem que já faz o que você quer. Quero servir uma página HTML estática, então busco nginx no Docker Hub. O selo de imagem oficial e o número de downloads, mais de um bilhão, indicam que é a oficial. Veja a página em hub.docker.com/_/nginx: ela traz o exemplo de Dockerfile e o comando de build.
Crio a pasta meu_html com um hello.htm ("Oi Mundo... Saí da máquina local e fui para o container") e um Dockerfile-nginx, que copia a pasta para o diretório que o Nginx publica:
FROM nginx
COPY meu_html /usr/share/nginx/html
docker build -t meu-nginx -f Dockerfile-nginx .
Na primeira tentativa esqueci o -f e o Docker pegou o Dockerfile padrão. Com o -f, ele baixa o Nginx, copia o conteúdo da pasta e a imagem fica pronta.
4Publicar a porta com -p
O container roda na minha máquina, mas para abrir a página como localhost preciso compartilhar uma porta dele. O Nginx responde na porta 80 dentro do container. Do lado da minha máquina eu escolho qualquer porta livre: a documentação sugere a 8080 porque a 80 pode estar ocupada, e eu usei a 8081. O -p mapeia as duas, <porta-da-máquina>:<porta-do-container>:
docker run --name meu-container-nginx -d -p 8081:80 meu-nginx
O --name dá um nome ao container; sem ele o Docker gera um nome aleatório. O docker ps confirma que está rodando e mostra o mapeamento. Abri localhost:8081/hello.htm e a página apareceu.
5Atualizar a página exige uma imagem nova
Mudei o texto do HTML, acrescentando "(Sou a v2)". Rodar o container de novo não muda nada, porque o HTML foi copiado para dentro da imagem. O caminho é parar o container, construir a imagem outra vez e subir de novo, agora na porta 8082:
docker stop <id>
docker build -t meu-nginx -f Dockerfile-nginx .
docker run -d -p 8082:80 meu-nginx
A 8081 saiu do ar e a 8082 mostrou a página com a v2. Um container roda de uma imagem: para o container mudar, a imagem muda primeiro. O mesmo raciocínio aparece em Docker na EC2, com o Apache, e os comandos que você usa no dia a dia estão em Comandos Docker essenciais.
Checklist
- Parta de uma imagem sua com
FROM <nome-da-imagem>e construa a próxima em cima dela. - Use
-f <arquivo>quando o Dockerfile não se chamaDockerfile, e não esqueça o contexto (o ponto) no fim dodocker build. - Ponha no Dockerfile o que muda menos nas primeiras linhas: da linha alterada em diante o cache recomeça.
- Com
docker run -d, garanta que um processo segura o container, senão ele termina na hora. - Pare um container com
docker stope os primeiros caracteres do ID. - Para servir uma página, parta da imagem oficial do Nginx e copie o HTML para
/usr/share/nginx/html. - Publique a porta com
-p <porta-da-máquina>:80e confira nodocker ps. - Mudou o conteúdo? Pare o container, construa a imagem de novo e suba outro.
Estender imagens, ordenar bem o Dockerfile e publicar uma porta são a base para o passo seguinte: pegar uma aplicação local, containerizar e levar para a nuvem da AWS.
O que os alunos dizem

“Eu queria voltar para a área técnica, mas aprender AWS sozinho só me deixava frustrado.”
William Santos, Tech Lead
Ver depoimento
“Eu sempre fugi da AWS. Achava complicado, distante da minha rotina de dev. Mas só a primeira aula do Bootcamp já mudou tudo: usei o conteúdo para resolver um problema real de um cliente e fui contratado por causa disso.”
André Scherrer, Engenheiro de Software
Ver depoimento
“Só quando coloquei a mão na massa com a Formação AWS é que avancei de verdade. Essa prática me levou ao próximo nível”
Caio, Desenvolvedor (Dev)
Ver depoimento
Formação AWS
Aprenda AWS e IA na prática, com projeto real e acompanhamento. Converse com o time sobre a Formação AWS.
Agende uma conversaPrefere assistir?
A aula completa (29 min) está no canal Domine AWS. O vídeo toca aqui mesmo.

