# Usando Dockfiles

Habitualmente, mantemos nossas máquinas atualizadas. Mas quando criamos uma imagem, "congelamos" as bibliotecas nas versões que estavam no contêiner no momento do commit. Em diversas situações, precisamos de iniciar um contêiner atualizado. Uma maneira de ter este ambiente é recriar o contêiner, tendo todo o retrabalho de instalar os pacotes e aplicações necessárias. A outra, é usar um arquivo que contenha um roteiro, uma receita, com todas as especificações necessárias para a criação da imagem.

Uma imagem do Docker consiste em camadas somente leitura, cada uma das quais representa uma instrução do Dockerfile. As camadas são empilhadas e cada uma é um resultado (delta) das alterações da camada anterior.Boas práticas para escrita de Dockerfiles (opens new window)

# Criando o Dockerfile

Uma maneira de se trabalhar com o arquivo dockerfile, é criando uma pasta para a construção da imagem e nela colocar este arquivo e outros recursos que serão adicionados à imagem. Também é possível gerar a imagem criando, na linha de comando (prompt do linux) com os passos que estariam dentro do dockerfile. Será visto aqui, a primeira forma.

Para descever o roteiro para a criação da imagem o dockerfile utiliza uma série comandos. Alguns comandos são usados para contrução da imagem e outros para definir como o contêiner será executado. O arquivo final é o resultado da combinação dos dois tipos de comandos:

# Comandos de construção (build)

São os comandos que determinam a estrutura da imagem. Definem por exemplo, que imagem base será utilizada, quais pastas do host serão compatilhadas, que portas serão expostas e que bibliotecas serão adicionadas.

Comando Descrição
ADD Adiciona um arquivo do hospedeiro para a imagem.
ARG Define uma variável de ambiente. Visível somente durante a construção.
COPY Copia arquivos para a imagem.
ENTRYPOINT Define qual é o comando padrão que será executado quando o contêiner estiver rodando.
EXPOSE Informa ao docker que uma porta da rede do contêiner está disponível.
FROM É o primeiro comando do arquivo e deve ficar na primeira linha. Define qual a imagem será utilizada.
MAINTAINER Define quem é o mantenedor da imagem. É um comando opcional.
RUN É usado para executar comandos durante os passos de construção da imagem.
VOLUME Faz a ligação entre pastas do contêiner e do hospedeiro.

COPY ou ADD?

Os dois comandos tem própósitos semelhantes, mas diferenças importantes:

  • o COPY é mais "explicito" e é usado para copiar apenas uma pasta ou um arquivo do host para uma pasta na imagem. Na sua sintaxe devem ser especificadas as pastas de origem e destino.

  • o comando ADD é mais flexível: além de copiar arquivos e pastas como faz o COPY, ele permite que seja usada uma URL como origem. Também permite a extração de um arquivo tar da origem diretamente para o destino.

# Comandos de inicialização do container

São os comandos que serão executados quando o container for inicializado.

Comando Descrição
CMD É usado para executar comandos dentro do contêiner.
ENTRYPOINT Define qual é o comando padrão que será executado quando o contêiner for iniciado.
USER Define um usuário ou UID para o contêiner criado.
ENV Define variáveis de ambiente para o contêiner.

# O arquivo .dockerignore

Ao criar a imagem, as pastas e arquivos que estão localizados na pasta do projeto serão incluídos automaticamente nela. Mas, em sua construção, com frequencia utilizamos alguns recursos auxiliares que não são necessários na imagem. A maneira de informar ao Docker que estes arquivos e pastas não devem fazer parte da imagem é listá-los em um arquivo .dockerignore, colocando um nome por linha. Este arquivo deve ficar na mesma pasta do projeto, junto com o dockerfile.

# [TODO! Detalhe os comandos dentro dos Dockerfile]

# Dockerfile Básico

Exemplo 1- Faremos um roteiro para uma imagem base com alguns utilitários de uso comum:

FROM alpine:3.9
LABEL maintainer="Marcos Honorato"
LABEL version="1.0"
LABEL build-date="2019-04-28T06:30:00.00"
LABEL description="Servidor alpine 3.9 com os utilitários rsync, mc, wget e vim."
# Instalando os utilitários básicos.
RUN apk update 
RUN apk add rsync 
RUN apk add mc 
RUN apk add wget 
RUN apk add vim
# Chamando o bash
CMD ["/bin/bash"]

Reduza o número de camadas na imagem

  • Para cada comando RUN que é colocado dentro do dockerfile é criada uma camada na imagem. Quanto mais camadas, maior a imagem se torna. Uma forma de evitar é executar vãrios comandos dentro de um RUN sempre que possível.

Reescrevendo o dockerfile para gerar uma imagem menor:

# Instalando os utilitários básicos.

FROM alpine:3.9
LABEL maintainer="Marcos Honorato"
LABEL version="1.0"
LABEL build-date="2019-04-28T06:30:00.00"
LABEL description="Servidor alpine 3.9 com os utilitários rsync, mc, wget e vim."
# Instalando os utilitários básicos.
RUN apk update && \
    apk add rsync && \
    apk add mc && \
    apk add wget && \
    apk add vim
# Chamando o bash
CMD ["/bin/bash"]

Atualizando o ambiente

  • As imagens base oficiais são regularmente atualizadas para que incluam as últimas versões dos pacotes. Logo não é necessário colocar comandos de atualização como apt-get upgrade ou yum upgrade nos roteiros criados com dockerfile.
  • Os gerenciadores de pacotes como Apt e Yum acabam deixando arquivos de listas e arquivos de cache que acabam aumentado o tamanho da imagem. Após instalar os pacotes necessário procure remover todos os arquivos criados por eles que não serão mais necessários.

Para o APT, utiliza-se a seguinte sintaxe:

RUN apt-get update && \
    apt-get install wget -y && \ 
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ apt-get clean

Para o Yum, utiliza-se a seguinte sintaxe:

RUN yum install httpd -y && \
    yum clean all

# Dockerfile ou Docker Compose?

O compose permite a configuração de recursos que costumam ser definidos durante a construção da imagem e por isso gera dúvidas. O compose é utilizado para a contrução de aplicativos Docker com vários contêineres envolvidos. Dizemos que é uma ferramenta de "orquestração".

Com o ele, você usa um arquivo YAML, o compose.yaml, para configurar os serviços do seu aplicativo. Em seguida, com um único comando, você cria e inicia todos os serviços (contêineres) definidos em sua configuração.

Um exemplo típico é construção de uma aplicação WEB onde teremos um container para o servidor da aplicação e outro para o banco de dados. No compose definiremos como os dois servidores se comunicarão e mesmo algumas configurações que eram estabelecidas na imagem, como o uso de portas e volumes são tratadas no compose. Essa flexibilidade permite a criação de imagens mais generalistas e com um nível maior de reuso.

# Conheça um pouco mais