Dockerfile: o que é, para que serve e como criar um arquivo Dockerfile no Docker 

Daniel Artine
Daniel Artine

Compartilhe

Avalie este artigo

12 minutos de leitura

Você sabe o que é Dockerfile e para que serve esse arquivo fundamental no Docker?

Neste artigo, explicamos de forma prática o que é Dockerfile, para que serve, quando usar, como criar um arquivo Dockerfile e detalhamos os principais comandos (incluindo o RUN Dockerfile) para você dominar a construção de containers no Docker Linux ou Windows. 

Para uma rápida revisão, começo trazendo essa entrevista que fizemos com o Giovani Bassi sobre Docker e Containers. Se preferir, ouça o episódio do podcast Hipsters Ponto Tech com o mesmo tema. Após essa breve revisão, passaremos para a explicação detalhada sobre Dockerfile: 

Containers, Docker e Kubernetes com Giovanni Bassi | #HipstersPontoTube 

Tudo começa com um arquivo Dockerfile: por que ele é importante? 

Vamos iniciar com um exemplo prático: rodar um container simples contendo Ubuntu com Java instalado. Como essa tarefa pode ser executada? 

A primeira é escrever o Dockerfile e criar o container a partir dele. A segunda é utilizar o comando docker run, que busca a imagem no Docker Hub e executa o container automaticamente. 

Mas, quando se usa o comando docker run, ele vai até o Docker Hub, busca essa imagem e baixa para nós. E como essa imagem foi gerada? Isso mesmo, com um Dockerfile feito por outra pessoa! 

Banner promocional da Alura destacando até 35% de desconto em cursos de tecnologia. A mensagem reforça que a diferença entre potencial e resultado está no preparo, incentivando profissionais a se anteciparem às mudanças do mercado e investirem no desenvolvimento de novas habilidades. A imagem mostra uma pessoa usando fones de ouvido e há um botão com a chamada "Aproveitar agora" para começar a evoluir na carreira tech.

O que é Dockerfile e para que serve esse arquivo no Docker? 

Dockerfile é um arquivo de texto utilizado para criar imagens personalizadas no Docker. Ele funciona como uma receita, listando os comandos necessários para construir e configurar um container exatamente do jeito que você deseja.  

Arquivo Dockerfile permite definir a instalação de programas, configurações de ambiente, permissões e muito mais. Seja para projeto pessoal ou empresarial. 

Há ainda um outro ponto muito interessante que deve ser explorado (muitas pessoas confundem quando ainda estão aprendendo) para entendermos melhor o conceito e começarmos a compreender o Dockerfile mais a fundo: qual é exatamente a diferença entre um container e uma imagem

Qual a diferença entre imagem Docker e container Docker? 

Uma imagem é uma representação imutável das instruções para criar um container. Não executamos imagens diretamente; elas são usadas como base para criar containers. 

O fluxo geralmente segue estas etapas: escreve-se um Dockerfile, usa-se o comando docker build para criar uma imagem e, em seguida, utiliza-se docker run para criar e executar o container. A imagem é o meio e o container é o resultado. 

O ponto que temos que entender agora é o seguinte: escrevemos um Dockerfile, construímos uma imagem a partir dele executando o comando docker build, e, por fim, criamos e rodamos o container com o comando docker run. O container é o fim, enquanto a imagem é o meio. 

Fluxograma do processo de escrever um dockerfile e construir uma imagem a partir dele executando o comando docker build. 

Como criar um Dockerfile: passo a passo prático para construir seu arquivo no Docker 

Escolhi uma pasta no meu computador e nela criei um arquivo chamado Dockerfile (com letra maiúscula e sem extensão mesmo). Nele, adicione a seguinte linha: 

`FROM ubuntu:18.04` 

Então, iniciei o processo de criação da imagem. Para isso, abri o terminal e acessei a pasta que contém o Dockerfile. Depois, executei o comando docker build. (com o ponto), e o Docker começou a construir a imagem a partir do arquivo. 

Ao terminar o processo, executei o comando docker image ls e obtive a seguinte saída: 

Código sobre as etapas de criação de um container: docker image ls. 

O resultado acima mostra uma imagem a partir do Dockerfile e ela está pronta para rodar um container com o comando docker run. 

Agora que já sei o que é e para que serve o Dockerfile, decidi aprender qual o propósito de cada uma das instruções desta ferramenta

Virtualização com Docker - Alura Live #25 

Como rodar um Dockerfile e entender seus principais comandos 

FROM 

A instrução FROM é a mais utilizada para a criação de Dockerfiles. Sabe o motivo? Simplesmente porque ela é obrigatória 🙂. 

Com essa instrução, pode-se definir qual será o ponto de partida da imagem que criaremos com o nosso Dockerfile, ou seja, se eu quiser utilizar a imagem do Java para produzir meu container, basta que eu especifique para utilizar a imagem do openjdk como base. Ficaria algo como: 

FROM openjdk 

Mas e caso eu queira criar uma imagem do zero absoluto? Sem me basear em imagem alguma? Para isso, posso utilizar a imagem scratch. 

FROM scratch 

Essa imagem nada mais é do que simplesmente… nada! Com ela, consigo criar uma imagem completamente do zero, sem utilizar nada de ninguém. 

RUN 

A instrução RUN é bem interessante. Ela pode ser executada uma ou mais vezes e, com ela, posso definir quais serão os comandos executados na etapa de criação de camadas da imagem. Mas como assim? 

Temos o seguinte Dockerfile: 

FROM ubuntu:18.04 
RUN apt-get update 
RUN apt-get install openjdk-8-jdk -y

Quando executo o comando docker build ., além de baixar a imagem do Ubuntu 26.04 para colocar na minha imagem, o processo de criação também executará os comandos para atualizar os repositórios do Ubuntu por meio do apt-get update, e para instalar o Java 8 utilizará o apt-get install openjdk-8-jdk-y.

Ele executará uma série de downloads, produzindo o seguinte resultado: 

Dockerfile: instrução RUN. 

Como resultado em minha máquina, foi gerada uma imagem com o ID 41afdc6b059f (em sua máquina será algo completamente diferente, não se assuste). 

Se criarmos um container a partir dessa imagem, os dois comandos já terão sido executados, já que esses foram executados no momento de criação da imagem, não da criação do container. Então qualquer container criado a partir dessa imagem terá o repositório do Ubuntu na mesma versão e o Java 8 instalado! 

Para criar um container utilizando a imagem gerada e podendo manter a execução do container, executei o comando passando o ID da imagem gerada na minha máquina através do comando docker run-it 41afdc6b059f. 

O -it serve para rodar o container em modo interativo, ou seja, eu quero interagir com ele efetivamente. Caso não utilizasse esse parâmetro, o container subiria e cairia logo em seguida. 

O -it serve para rodar o container em modo interativo, ou seja, eu quero interagir com ele efetivamente. 

Lembra lá no início da explicação sobre o RUN quando falei que ele podia ser usado uma ou mais vezes? Então, isso faz uma baita diferença na hora da criação de imagens, pois, como foi dito, cada `RUN` criará uma etapa na criação da imagem. Mas, o que isso muda? 

O grande diferencial da instrução RUN é que cada camada gerada por ele poderá ser reutilizada na criação de outras imagens. 

Então, para testar, alterei meu Dockerfile para criar uma imagem nova com elementos em comum da outra imagem: 

FROM ubuntu:18.04 
RUN apt-get update 
RUN apt-get install openjdk-8-jdk -y 
RUN touch arquivo-de-boas-vindas

O Docker consegue reutilizar várias camadas já existentes, acelerando significativamente a criação de novas imagens. 

Caso eu queira adicionar um comando novo qualquer, ao executar o comando docker build . para gerar essa nova imagem, vejam o resultado: 

Caso eu queira adicionar um comando novo qualquer, ao executar o comando docker build . 

Essas práticas com RUN Dockerfile ajudam não só a economizar tempo, mas também a criar arquivos Dockerfile mais eficientes – ponto essencial para projetos grandes ou ao usar Docker no Linux. 

O RUN aceita parâmetros de dois jeitos: 

RUN apt-get install openjdk-8-jdk –y ou
RUN ["apt-get", "install" "openjdk-8-jdk" ,"-y"] 

Isso permite passar os comandos como uma lista de argumentos, sendo executados da mesma forma. 

CMD e ENTRYPOINT 

A instrução CMD é semelhante à RUN, mas cada uma possui um propósito distinto. CMD define o comando padrão a ser executado quando o container inicia, enquanto RUN executa comandos durante a construção da imagem. 

A sintaxe é a mesma, podemos passar os parâmetros do mesmo modo que a instrução RUN, alterando o último RUN por um CMD

FROM ubuntu:18.04 
RUN apt-get update 
RUN apt-get install openjdk-8-jdk -y 
CMD touch arquivo-de-boas-vindas

Ao construir a imagem com o Dockerfile acima, o comando especificado por CMD não será executado na etapa de build. 

Isso aconteceu porque, na verdade, a instrução CMD executa o comando apenas quando criamos o container e não passamos nenhum parâmetro para ele, ou seja, quando executarmos o comando docker run –it nessa imagem. 

Caso passássemos algo como docker run -it <id da imagem> /bin/bash, ele sobrescreveria o comando CMD touch arquivo-de-boas-vindas e executaria apenas o /bin/bash. 

Um outro teste também poderia ser: 

FROM ubuntu:18.04 
RUN apt-get update 
RUN apt-get install openjdk-8-jdk -y 
CMD touch arquivo-de-boas-vindas 
CMD touch outro-arquivo

Ao inicializar o container, vi que apenas o outro-arquivo foi criado, mas por quê? O que acontece é que podemos ter quantos CMD quisermos, mas, no fim das contas, apenas será executado o último CMD, sem nenhum erro aparente! 

O ENTRYPOINT é semelhante ao CMD, mas seus parâmetros não são sobrescritos por argumentos passados via linha de comando ao executar docker run. 

ADD e COPY 

Os nomes dessas instruções são bem intuitivos. 

O papel do ADD é fazer a cópia de um arquivo, diretório ou até mesmo fazer o download de uma URL de nossa máquina host e colocá-la dentro da imagem

Eu utilizei o ADD para copiar o arquivo chamado “arquivo-host” da minha máquina para dentro da imagem, com o nome arquivo-host-transferido: 

FROM ubuntu:18.04 
RUN apt-get update 
RUN ["apt-get", "install", "openjdk-8-jdk" ,"-y"]

ADD arquivo-host arquivo-host-transferido 

Se criarmos uma imagem e rodarmos um container, teremos o seguinte resultado: 

O arquivo está dentro do container. 

A instrução ADD também tem alguns efeitos interessantes, como: caso o arquivo que esteja sendo passado seja um arquivo de extensão “.tar”, ele fará a descompressão automaticamente, além do fato já mencionado de poder fazer download de arquivos por URLS. 

É sempre importante saber escolher entre COPY e ADD em seu arquivo Dockerfile, de acordo com a necessidade do seu projeto. 

FROM ubuntu:18.04 
RUN apt-get update 
RUN ["apt-get", "install", "openjdk-8-jdk" ,"-y"] 
COPY arquivo-host arquivo-host-transferido

A próxima instrução mostrará como podemos documentar de maneira sucinta o nosso Dockerfile para quem for utilizá-lo. 

EXPOSE: para que serve esse comando Dockerfile? 

Há uma certa dúvida quanto ao uso dessa instrução. Muitas pessoas pensam que o EXPOSE serve para definir em qual porta nossa aplicação rodará dentro do container, mas na verdade o propósito é servir apenas para documentação. 

Essa instrução não publica a porta efetivamente, já que o propósito dela é fazer uma comunicação entre quem escreveu o Dockerfile e quem rodará o container. 

FROM ubuntu:18.04 
RUN apt-get update 
RUN apt-get install openjdk-8-jdk -y 
EXPOSE 8080

Logo, o Dockerfile acima não faz a publicação da porta, apenas serve como documentação. 

Agora vamos entender como podemos compartilhar informações entre o nosso container e nossa máquina host. 

VOLUME 

Essa instrução cria uma pasta em nosso container que será compartilhada entre o container e o host, funcionando do seguinte modo: 

FROM ubuntu:18.04 
RUN apt-get update 
RUN apt-get install openjdk-8-jdk -y 
VOLUME /foo

Quando criarmos um container dessa imagem, ele criará uma pasta chamada foo: 

Quando criarmos um container dessa imagem, ele criará uma pasta chamada foo. 

Todo arquivo criado dentro dessa pasta será acessível a partir da máquina host no caminho /var/lib/docker/volumes

Por fim, como pode ser feita a organização de trabalho do container? Será que devemos trabalhar em qualquer pasta indefinidamente? 

WORKDIR 

Essa instrução tem o propósito de definir o nosso ambiente de trabalho. Com ela, definimos onde as instruções CMD, RUN, ENTRYPOINT, ADD e COPY executarão suas tarefas, além de definir o diretório padrão que será aberto ao executarmos o container. 

FROM ubuntu:18.04 
RUN apt-get update 
RUN ["apt-get", "install", "openjdk-8-jdk" ,"-y"] 
WORKDIR /pasta-qualquer 
COPY arquivo-host arquivo-host-transferido

Ao acessarmos o container gerado por essa imagem, teremos o seguinte resultado: 

WORKDIR: essa instrução tem o propósito de definir o nosso ambiente de trabalho. Visualização do container gerado por essa imagem 

Ou seja, além de inicializarmos o container nessa pasta, o COPY colocou o arquivo dentro da pasta definida! 

Resumo: como usar o Dockerfile para criar, rodar e personalizar containers no Docker 

O processo de criação de imagens é fundamental para o aprendizado de Docker.

Neste artigo, foram apresentadas as principais instruções do Dockerfile, como FROM para definir uma base de imagem, RUN para configurar camadas, e CMD ou ENTRYPOINT para especificar comandos de inicialização do container. 

Além disso, as instruções ADD e COPY foram apresentadas para transferir arquivos para o container; EXPOSE para documentar portas utilizadas; VOLUME para compartilhamento entre host e container; e WORKDIR para definir o diretório de trabalho padrão. 

Aqui na Alura, temos um curso sobre Docker! Nele você aprenderá tudo sobre o que é um container, como fazer eles se comunicarem, além de aprender como criar suas próprias imagens para personalizar seus containers. 

Avalie este artigo

Daniel Artine
Daniel Artine

Daniel é instrutor na Alura e Tech Lead na Stone Age.Possui certificação Docker e formação em Ciência da Computação pela Universidade Federal do Rio de Janeiro.Atualmente trabalha com .NET 6, AWS, Terraform, Docker e Kubernetes.

Veja outros artigos sobre DevOps