Dicas para criar uma boa imagem Docker com Python

Distribuir soluções utilizando containers tem sido uma prática cada vez mais comum, muito por conta da sua praticidade e também da quantidade surpreendente de imagens já publicadas que salvam nossa pele de ter que gastar um tempo considerável lutando com as dependências para instalar um programa ou serviço.

Mas quando pensamos em distribuir nossos próprios softwares algumas boas práticas devem ser levadas em conta.

Atenção ao cache

Cada instrução do Dockerfile é transformada numa camada da imagem docker e armazenada individualmente num cache, dessa forma as linhas não alteradas entre as versões da imagem são reaproveitadas diretamente do cache e quando uma linha nova/alterada é identificada, todas as camadas (comandos) subsequentes tem seu cache invalidado e são recompilados.

Quando começamos a criar nossas imagens nos preocupamos com isso porque tem um impacto direto no tempo de compilação das nossas imagens, seja local ou num CI. Mas é importante extrapolar essa preocupação para o momento do deploy, pois a gestão correta das camadas e ao uso do cache vão economizar o tempo e o custo de rede para atualizar a imagem nos ambientes.

Reduza as camadas da imagem

Uma prática importante é reduzir as camadas da imagem agrupando execuções de comando numa única instrução RUN no Dockerfile.

Em vez de:

FROM python:3.6
RUN apt-get update
RUN apt-get install curl vim
RUN mkdir /app

Prefira sempre:

FROM python:3.6
RUN apt-get update && apt-get install curl vim && mkdir /app

Instale as dependências no antes

Os comandos ADD/COPYconsideram os arquivos adicionados para invalidar o cache da camada, por conta disso é importante copiar o arquivo requirements.txt ou Pipfile separadamente e instalar as dependências antes de copiar os arquivos do projeto.

Em vez de:

FROM python:3.6
RUN apt-get update && apt-get install curl vim && mkdir /app
COPY . /app
RUN pip install -r /app/requirements.txt

Prefira sempre:

FROM python:3.6
RUN apt-get update && apt-get install curl vim && mkdir /app
COPY requirements.txt /app/
RUN pip install -r /app/requirements.txt
COPY . /app

Não rode a aplicação como root

Às vezes ficamos tão empolgados com o poder e facilidade dos containers que nos esquecemos de algumas questões básicas de segurança, então, mesmo dentro de um container, não rode a aplicação como root.

Para trocar o usuário utilize o comando USER:

FROM python:3.6
RUN apt-get update && apt-get install curl vim && mkdir /app && useradd -ms /bin/bash app_user
COPY requirements.txt /app/
RUN pip install -r /app/requirements.txt
COPY . /app
USER app_user

Sempre configure o .dockerignore

Adicione um arquivo .dockerignore à raiz do seu projeto para evitar que sua imagem contenha arquivos indesejados tais como:

  • Sistemas de gerenciamento de versão
  • Arquivos temporários
  • Dados sensíveis
  • Logs locais
  • Configurações de projeto
  • Cache
  • Testes

Exemplo:

.git
.gitignore
*.pyc
*.swp
.idea
temp/
logs/
tests/

Resumindo

Sempre que planejar distribuir aplicações Python com Docker preste atenção ao gerenciamento de cache das camadas da imagem e busque instalar as dependências do sistema o quanto antes.

Veja mais dicas de escrita de Dockerfile no blog oficial do Docker.