dockerfile.jpg

Neste artigo, vamos mergulhar mais fundo em Imagens Docker e criar nossa primeira imagem usando o Dockerfile

Observação: Este é o quarto artigo chamada “Docker”. Fica o convite de conhecer os demais artigos!

Entendendo melhor uma imagem Docker.

Uma imagem do Docker é um binário que inclui todos os requisitos para a criação e execução de um único contêiner do Docker, bem como metadados que descrevem suas necessidades e capacidades, icnluindo o código do aplicativo dentro do contêiner e suas configurações.

Toda imagem é composta de uma ou mais camadas somente leitura, em um sistema chamado de Layered File System.

Todas as camadas formam a imagem e uma vez criada, a imagem nunca é modificada.

Mas porque usar camadas? - As camadas podem ser reaproveitadas em outras imagens assim sendo compartilhadas.

Para exemplificar as camadas somente leitura que existem em uma imagem docker, vamos baixar a imagem do redis para ver seu comportamento. O resultado pode ser visto na imagem abaixo:

layred-redis.png

Ao executar o comando docker pull redis, não informamos a versão, desta forma, será realizado o download da última versão, chamada latest.

Podemos perceber em várias linhas a Informação Pull complete, após download e extração das camadas em seu dispositivo. Sendo assim, a “junção” de todas as camadas baixadas e extraídas irão compor a imagem do redis. Ao verificarmos quantas imagens temos em nosso dispositivo, podemos ter um resultado semelhante ao abaixo, caso apenas a imagem do redis esteja disponível.

Para verificarmos as imagens disponíveis, executar o comando docker images ou docker image ls

docker-images.png

Quando criamos um contêiner, o Docker cria uma nova camada acima da última camada da imagem, e nessa camada podemos ler e escrever. Read/Write Layer.

Usando uma imagem base, podemos reaproveitá-la para criar diversos containers diferentes compartilhando os componentes básicos Isso ajuda a otimizar o espaço de armazenamento e a minimizar o tempo de início de um contêiner.

Para escrever na camada de Read/Write, o Docker utiliza a estratégia de copy-on-write. No caso da criação de um contêiner onde alteramos um arquivo de uma determinada camada, o copy-on-write realiza uma cópia deste arquivo para a camada do contêiner e o arquivo que passa a ser considerado é este que está na camada do contêiner e não mais o que está na camada da imagem. Lembre-se que a camada da imagem é inalterada.

Como criar uma imagem?

  1. Processo de build usando o Dockerfile: O Dockerfile é um arquivo texto com instruções, comandos e passos que é executado através do comando “build” para gerar uma imagem. Exemplo: $ docker build -t <imagem>.

  2. Usando o comando commit: Não é uma boa prática porque ele não permite que você tenha um registro de como você chegou naquela imagem.

Criando Imagens usando o Dockerfile

Os contêiners do Docker são baseados em imagens do Docker.

Um contêiner sempre começa com uma imagem e é considerado uma instanciação dessa imagem.

Imagens prontas podem ser encontradas em repositórios e podem ser baixadas e usadas conforme nossa necessidade.

Nem sempre a imagem que desejamos usar está pronta do jeitinho que queremos, e, muitas vezes, teremos que ter uma imagem customizada.

Neste caso, vamos criar imagens automaticamente lendo as instruções de um arquivo chamado Dockerfile, que contém todos os comandos em ordem necessários para criar uma imagem customizada. E é neste arquivo que definimos todas as regras, informações e instruções que estarão contidas nas imagens.

No Dockerfile são definidas instruções que o docker vai seguir para conseguir realizar a criação de uma imagem.

Essas instruções são interpretadas linha a linha pela engine do docker.

Pense no Dockerfile como um arquivo de lote que contém instruções com uma sintaxe definida (INSTRUÇÃO argumento) que devemos seguir para que uma imagem seja criada. Cada camada de imagem representa uma instruçao do Dockerfile.

Criando uma imagem de exemplo: Criar uma imagem a partir do debian que instale e inicie o servidor nginx.

Definindo as etapas que vamos usar para criar a imagem:

  1. Definir uma “imagem base”
  2. Definir as informações para a imagem
  3. Executar comandos para “instalar” e “iniciar o nginx”
  4. Expor qual “porta o servidor” vai atender (no contêiner)
  5. Definir qual “o ponto de entrada” para a aplicação
  6. Definir a execução de um “comando” para inicializar o servidor “nginx”
FROM debian:latest
LABEL version="1.0" description="Debian/Nginx"
RUN apt-get update && apt-get install -y nginx && apt-get clean
EXPOSE 80
ENTRYPOINT ["usr/sbin/nginx"]
CMD ["-g", "daemon off;"]

Entendendo melhor a estrutura do Dockerfile.

  • FROM: Informar a sua imagem base a partir deste comando. O nome da images base no nosso exemplo é debian:latest.
  • LABEL: Podemos adicionar informações para a imagem, como por exemplo versão e descrição. No nosso exemplo utilizamos os parâmetros version="1.0" e description="Debian/Nginx"
  • RUN: Executa comandos e podem ser concatenados. No nosso exemplo estamos executando 3 (três) comandos: apt-get update, apt-get install -y nginx e apt-get clean. Para executar mais de uma ação, como nosso exemplo, separamos as intruções com os caracteres &&.
  • EXPOSE: Informa qual porta será exposta no contêiner
  • ENTRYPOINT: Define o ponto de entrada da aplicação. Um caminho de onde o comando para inicializar a aplicação é executado.
  • CMD: Define os comandos que seão executados

Salvando o arquivo Dockerfile em uma pasta de sua preferência, abra o terminal nesta pasta e vamos executar o comando para a construção da imagem. O processo de contrução irá executar todos os passos e instruções do dockerfile. Ao final do processo, sendo ele executado com sucesso, teremos como resultado a imagem que desejamos construída. Vamos construir a imagem a partir do comando abaixo

$ docker build -t exemplo/img:1.0 .

Entendendo melhor o comando:

  • docker build: Comando que constrói a imagem
  • -t: Usado para informar que a imagem pertence ao meu usuário
  • exemplo/img:1.0: O nome da imagem e a “tag” atribuída à imagem
  • .: Significa o diretório atual (pois o build foi dentro da pasta do Dockerfile)

Como resultado, teremos um resultado semelhante ao abaixo após a execução dos passos do Dockerfile e com a execução do comando docker images para listar as imagens em nosso dispositivo:

dockerfile-exemplo.png

Com a imagem criada, rodar um contâiner com a mesma, para validar o funcionamento

$ docker container run -d -p 8080:80 --name=ws1 exemplo/img:1.0

Entendendo melhor o comando:

  • docker container run: Cria, inicia e executa o conteiner.
  • -d: Executa em background, liberando o terminal utilizado.
  • -p 8080:80: Mapeamento da porta 8080 (host) para 80 (conteiner)
  • --name: Define o nome do conteiner.
  • exemplo/img:1.0: Nome da imagem usada

Como utilizamos a extensão de comando -d, teremos um resultado semelhante ao abaixo, com a disponibilização do hash do conteiner e a liberação do terminal.

exemplo-img-nginx.png

Para validarmos a execução do conteiner, basta abrir seu navegador e inserir a url http://localhost:8080/. Teremos a tela de início do nginx, como demonstrado na imagem abaixo:

nginx-debian.png

Parâmetros / Instruções do Dockerfile

Os argumentos utilizados no Dockerfile foram apenas os necessários para a construção de nossa imagem, e em um segundo ponto, são os argumentos mais utilizados em nosso dia a dia. Temos outros argumentos como ENV, ADD ou COPY, que utilizaremos no momento de criar nossas imagens a partir do código fonte que construímos.

Segue uma tabela com parâmetros e instruções mais comuns para criar um Dockerfile, com uma breve explicação de como cada um pode ser usado.

Parâmetro Exemplo Definição
FROM FROM ubuntu:20.04 Define a imagem base para o contêiner. É obrigatório e normalmente é a primeira instrução.
LABEL LABEL maintainer="seu_nome@example.com" ou LABEL description="Imagem para aplicação X" Adiciona metadados à imagem, como informações de autor ou descrição. Esses dados podem ser consultados posteriormente.
RUN RUN apt-get update && apt-get install -y python3 Executa comandos na criação da imagem. Cada RUN cria uma camada na imagem final.
CMD CMD ["python3", "app.py"] Define o comando padrão para rodar quando o contêiner é iniciado. Só pode haver um CMD, e ele pode ser substituído pela linha de comando do docker run.
ENTRYPOINT ENTRYPOINT ["python3", "app.py"] Semelhante ao CMD, mas é difícil de sobrescrever. Comumente usado para definir comandos principais, como o executável da aplicação.
WORKDIR WORKDIR /app Define o diretório de trabalho para as instruções RUN, CMD, ENTRYPOINT, COPY, e ADD.
COPY COPY . /app Copia arquivos ou diretórios do sistema host para o sistema de arquivos do contêiner.
ADD ADD https://example.com/file.tar.gz /app/ Parecido com COPY, mas com funcionalidades adicionais. Suporta URLs e pode descompactar arquivos automaticamente.
EXPOSE EXPOSE 8080 Informa a porta em que a aplicação dentro do contêiner estará escutando. Não abre a porta, apenas documenta.
ENV ENV APP_ENV=production Define variáveis de ambiente no contêiner.
ARG ARG VERSION=1.0 RUN echo "Building version $VERSION" Define variáveis para a construção da imagem (tempo de build), que podem ser passadas via linha de comando com --build-arg.
VOLUME VOLUME /data Cria um ponto de montagem de volume que pode ser usado para persistir dados.
USER USER appuser Define o usuário que será usado nas próximas instruções RUN, CMD e ENTRYPOINT.
ONBUILD ONBUILD RUN echo "Imagem base em uso!" Define instruções a serem executadas quando a imagem é usada como base em outro Dockerfile.
STOPSIGNAL STOPSIGNAL SIGTERM Define o sinal do sistema para parar o contêiner.
HEALTHCHECK HEALTHCHECK --interval=30s --timeout=10s --retries=3 CMD curl -f http://localhost/ || exit 1 Configura verificações de saúde (status) para o contêiner.
SHELL SHELL ["/bin/bash", "-c"] Define o shell padrão para comandos RUN. Útil para escolher entre sh e bash em imagens Linux.
COPY –chown COPY --chown=user:group localpath /containerpath Permite definir o proprietário e grupo dos arquivos ao copiá-los com COPY.

Conclusão

Neste artigo você conseguiu mergulhar um pouco mais no mundo das imagens do docker. Apenas um gostinho do poder que temos com o docker e o Dockerfile. Futuramente iremos ver a construção de imagens a partir de nosso código .Net e seremos capazes de customizar nossas imagens e sua respectiva contrução.

Se você gostou do conteúdo, deixe um comentário ou uma reação para apoiar o projeto! Compartilhe com alguém e ajude a divulgar!

Até a próxima!

[ ]´s Degas.