>> Apresentação e considerações

Antes de começar a pensar em ambientes complexos, escaláveis, em Cluster, com LoadBalancer tudo o que envolve a arquitetura de um projeto em que vai ser utilizado Docker precisamos ter bases bem sólidas pois a sua base pode determinar muitas vezes o sucesso ou o fracasso do seu projeto.

O seu ambiente e o seu projeto só vai existir se você tiver imagens, sem imagem não existe containers, temos também um outro paralelo, onde, uma imagem sem uma configuração adequada ou poluída afeta diretamente o resultado do seu projeto deixando muitos lixos, arquivos e pacotes desnecessários.

Construir uma imagem no Docker chega a ser uma tarefa na maioria das vezes muito simples, porém é necessário se atentar há algumas regras que fazem toda a diferença.

Nesse artigo vamos conhecer o que é Dockerfile e indicar algumas boas práticas decisivas para a construção da sua imagem, espero que gostem…

Let’s go!!!

>> O que é Dockefile?

Dockerfile é o arquivo de build das imagens Docker, ele é escrito com uma syntax simples no formato “YML” ou “YAML”.

O nome precisa essencialmente ser "Dockerfile" pois ao executar o "docker build" ele entende que é o arquivo e executa as instruções contidas nele

Como assim arquivo de build?

Bem, imagina ele como um script, onde você passa as instruções e as executa para ter o resultado esperado, no caso do Dockerfile ele possui uma série de opções bem definidas que tornam muito simples a construção de uma imagem, nele você declara a sua imagem base, quais pacotes serão instalados ou removidos, envio de arquivos e/ou diretórios, adição de variáveis de ambiente e muito mais, ou seja, tudo o que você precisa para ter para que a sua aplicação funcione, ao executá-lo o resultado será uma imagem personalizada que depois você pode guardar no Dockerhub por exemplo ou no seu Registry privado para utilizar quando precisar para subir seus containers.

>> Exemplificando e construindo a primeira imagem

Crie o diretório que conterá seu Dockerfile e em seguida criei o arquivo

~$ mkdir Docker-nginx; cd Docker-nginx
~$ vim Dockerfile

Copie e cole o conteúdo abaixo dentro do seu Dockerfile

# Chama a imagem "ubuntu:16.04" como imagem base para o build
# O FROM é obrigatório no início
FROM ubuntu:16.04

# Atualizado o repo, instala o NGINX e remove o cache do APT
RUN apt-get update -y && apt-get install nginx -y \
    && rm -rf /var/lib/apt/lists/*

# Inicia o NGINX quando o container for executado
CMD ["nginx", "-g", "daemon off;"]

Agora pra validar se está vamos criar a nossa imagem criando uma tag com o parâmetro -t

$ docker build -t churrops/nginx:1.0.0 .

Se ao final do build aparecer essa mensagem, você está no caminho certo!

Successfully built 7c33dad1a649
Successfully tagged churrops/nginx:1.0.0

Agora vamos listar a nossa imagem, e em seguida iniciar o nosso container

$ docker image ls |grep churrops/nginx
churrops/nginx    1.0.0    7c33dad1a649    About a minute ago   177MB
$ docker container run -d -p 80:80 --name churrops-nginx churrops/nginx:1.0.0
f7acfa15accc354df7600a860b34977765484854215ce061863b8f64cef09108
$ docker container ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f7acfa15accc churrops/nginx:1.0.0 "nginx -g 'daemon ..." 47 seconds ago Up 46 seconds 0.0.0.0:80->80/tcp churrops-nginx

Agora, ao abrir no browser vemos que a nossa primeira imagem está ok!

Captura de Tela 2017-09-07 às 10.31.37

Vamos remover o nosso container para prosseguir no artigo.

~$ docker container rm -f churrops-nginx

>> Algumas observações que temos que ter em mente

  • Os containers devem ser efêmeros, ou seja, nascem pra morrer, então sua imagem precisa estar preparada pra isso.
  • Use um arquivo .dockerignore. O .dockerignore possui a mesma funcionalidade do .gitignore, fazendo com que você possa gerar a sua imagem excluindo alguns arquivos que estejam no diretório do seu Dockerfile.
  • Evite instalar pacotes desnecessários. Lembre-se que o container precisa ter somente o básico e necessário para o funcionamento da sua APP
  • Cada container deve rodar apenas um processo principal. Não faz sentido você ter um container rodando diversas aplicações
  • Minimize o número de camadas ou layers. Cada RUN, COPY, ADD por exemplo cria uma camada
  • Quebrar argumentos grandes em várias linhas utilizando barra invertida \
  • Sempre usar TAGS quando for fazer o build das imagens
  • Build cache – build-cache

>> Conhecendo os parâmetros do Dockerfile

Cientes do que é um Dockerfile vamos conhecer os seus parâmetros, vou adicionar algumas informações a respeito das boas práticas, mais claro sempre procurem ler a documentação oficial para entender mais a fundo alguns parâmetros.

FROM

Define qual imagem base a ser utilizada para iniciar o estágio de compilação, ao executar o "docker build" essa imagem precisa estar acessível, pois o Docker irá realizar o pull e somente com a imagem no local ele irá seguir para os demais estágios, lembrando que recentemente agora podemos utilizar mais de um FROM no mesmo Dockerfile para a realização de build Multi-Stage

Recomendações: Sempre procure utilizar imagens OFICIAIS ou imagens da sua confiança

RUN

A imagem base é a primeira camada da construção, o RUN inicia uma nova camada, e nele você pode executar N comandos, basicamente o que você faria logado na instância ele faz, seja um apt-get, yum, rm, etc, com o RUN você passa as instruções do que será construído em tempo de execução do Dockerfile

Recomendações: Para tornar seu Dockerfile mais legível, compreensível e sustentável, divida instruções RUN longas ou complexas em várias linhas separadas com barras invertidas \ e use o mínimo de RUN possível, pois a cada RUN é gerada uma camada na imagem.

Vamos ver um exemplo de uso ineficaz do RUN, dessa forma ao executar o primeiro RUN a compilação cria uma camada ReadOnly onde o próximo RUN não limpa de fato o cache do apt da imagem, quando subimos um container com essa imagem “aparentemente”os arquivos não estão lá, mas na verdade só não estamos vendo, mais o lixo está fixo na camada anterior deixando a imagem “suja”.

RUN apt-get update -y
RUN apt-get install -y package-bar package-baz package-foo -y
RUN rm -rf /var/lib/apt/lists/*

Agora vamos melhorar o nosso RUN, dessa forma melhoramos a aparência do RUN quebrando as linhas com barra invertida e ainda garantimos que estamos removendo o lixo de fato da imagem

RUN apt-get update && apt-get install -y \
    package-bar \ 
    package-baz \
    package-foo \
    && rm -rf /var/lib/apt/lists/*

LABEL

Adiciona metadados a uma imagem no formato chave-valor, é extremamente importante para identificação da imagem e inclusive para automação é muito utilizado.

Exemplos:

# Setando 1 ou mais LABELs individuais
LABEL com.example.version="0.0.1-beta"
LABEL vendor="ACME Incorporated"

# Setando multiplos LABLEs em uma mesma linha
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"

# Set multiple labels at once, using line-continuation characters to break long lines
LABEL vendor=ACME\ Incorporated \
      com.example.is-beta= \
      com.example.is-production=""

MAINTAINER (deprecated)

Opção para definir o mantenedor da imagem, porém ela foi substituída pelo LABEL.

EXPOSE

Expõe as portas de escuta do container especificadas na execução, porém o EXPOSE no Dockerfile não faz o BIND com a porta do host fazendo a exposição, isso precisa ser feito ou em um Compose File ou no start do Container com o parâmetro "-p 80:80", ou seja, jamais coloque EXPOSE 80:80 assim no seu Dockerfile

ENV

Variáveis de ambiente a serem enviadas para a imagem

COPY

Copia os arquivos do diretório especificado do host para o diretório especificado dentro do container

ADD

O ADD além de copiar também arquivos do local ele serve para enviar arquivos empacotados com “tar” na origem e automaticamente descompactar no destino e também tem a capacidade de baixar arquivos de uma URL, desde que não tenha autenticação.

ENTRYPOINT

É o ponto de entrada quando você iniciar o container, ou seja, geralmente definimos no ENTRYPOINT o comando ou script que chama o processo responsável pela execução do container e que manterá o container vivo.

CMD

Geralmente é utilizado para prover argumentos para o ENTRYPOINT, quando você usa o CMD sem ENTRYPOINT o CMD é o que vale no start do container, quando você tem CMD e ENTRYPOINT no mesmo DockerFile, o que é colocado no CMD geralmente são parâmetros complementares para o ENTRYPOINT

VOLUME

Instrução que fala qual volume será mapeado ou criado para o container

USER

Nome de usuário (ou UID) e, opcionalmente, o grupo de usuários (ou GID) a serem usados ao executar a imagem e as instruções RUN, CMD e ENTRYPOINT que o seguem no Dockerfile.

WORKDIR

Diretório à partir de onde os comandos ou as ações serão realizadas durante a contrução da imagem, para maior clareza e confiabilidade, você sempre deve usar caminhos absolutos para o seu WORKDIR.

ARG

Define uma variável que os usuários podem passar no tempo de compilação para o construtor com o comando de compilação docker usando o sinalizador --build-arg =

ONBUILD

Define algumas instruções que podem ser realizadas quando alguma determinada ação for executada, é basicamente como uma trigger.

STOPSIGNAL

Define o sinal de chamada do sistema que será enviado para o contêiner para sair. Este sinal pode ser um número não assinado válido que coincide com uma posição na tabela syscall do kernel, por exemplo, 9 ou um nome de sinal no formato SIGNAME, por exemplo SIGKILL.

HEALTHCHECK

A instrução HEALTHCHECK diz ao Docker como testar um container para verificar se ele ainda está funcionando. Isso pode detectar casos como um servidor web que está preso em um loop infinito e incapaz de lidar com novas conexões, mesmo que o processo do servidor ainda esteja em execução.

SHELL

Serve para definir ou mudar no caso o SHELL padrão para a execução dos comandos, no linux o padrão é o ["/bin/sh", "-c"] e no Windows ["cmd", "/S", "/C"]

>> Exemplos de Dockerfile

Segue exemplos de Dockerfiles de fontes oficiais para que vocês possam ter uma base e acredito que com isso já podemos ter uma boa idéia de como funciona!

Dockerfile | WordPress Oficial

Dockerfile | Ruby Oficial

Dockerfile | Golang Oficial

>> Build

O próximo passo da criação de um Dockerfile é justamente o buid, que pode ser usado em sua forma mais básica, como fizemos lá no início do tutorial, lembre-se que a TAG no build é extremamente importante.

Acredito que o assunto build pode até virar um artigo futuramente, de qualquer forma, segue a documentação a respeito para consulta:

https://docs.docker.com/engine/reference/commandline/build/

>> Conclusão

É isso ChurrOpeiros, procurei abordar algumas das boas práticas para a contrução de Dockerfile de acordo com a documentação, e é claro, sempre leiam a documentação pois vocês sempre estarão atualizados e sincronizados com as mudanças, acho espero ter conseguido passar a mensagem!

FeedBacks são bem vindos, gostou desse artigo? Ajude-nos COMPARTILHANDO nas suas redes sociais!

Abraços!

>> Referências

https://docs.docker.com/engine/reference/builder/

https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/