1.5 Dockerfile, Imagem e Contêiner Docker

Esta seção apresenta conceitos básicos relacionados com a terminologia Docker. A figura a seguir, extraída de Anil et al. (2022), ilustra de forma simplificada a relação entre os termos (taxonomia básica do Docker.

Uma maneira simplificada de compreender a figura acima é associar os conceitos de imagem e contêiner com Orientação a Objetos. Nessa comparação, as imagens Docker seria as classes, ou seja, o molde a partir do qual objetos (contêineres) são criados. Ou seja, uma imagem Docker nada mais é do que uma representação estática que viabiliza a construção de contêineres. Um contêiner é uma instância de uma imagem em execução. Uma imagem pode dar origem a vários contêineres e um contêiner é uma instância de uma imagem.

Para facilitar o armazenamento e busca por imagens Docker existem os registradores de imagens (Registry). Esses registradores são serviços disponíveis em núvens públicas ou privadas. O próprio Docker oferece um serviço público, denominado Docker Hub, que será descrito em mais detalhes na Seção 1.6, e também serviços de registry privado. Outras empresas, como Azure, Google e AWS também oferecem serviços de registry públicos.

Crindo Imagem Docker: Dockerfile

O processo de criação de uma imagem Docker passa pela edição de um arquivo denominado de Dockerfile. O exemplo a seguir, extraído de https://hub.docker.com/_/python, ilustra um Dockerfile para configuração de uma aplicação Python. Como pode ser observado, um Dockerfile nada mais é do que uma receita de como uma imagem deve ser construída.

Conteúdo do arquivo Dockerfile

FROM python:3

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [ "python", "./script.py" ]

No exemplo acima, a "receita" diz o seguinte:

A partir da imagem do "python:3" (FROM python:3), defina como diretório de trabalho na imagem a pasta /usr/src/app (WORKDIR /usr/src/app). Em seguida, copie o arquivo requirements.txt, localizado na pasta local, junto com este arquivo Dockerfile, para a pasta /usr/src/app da imagem (COPY requirements.txt ./).

Na sequência, execute o comando pip para instalar a lista de bibliotecas Python presentes em requirements.txt (RUN pip install --no-cache-dir -r requirements.txt).

Finalmente, copie todo o restante dos arquivos da pasta local para a pasta /usr/src/app da imagem (COPY . .) e execute o comando python passando como parâmetro o arquivo ./your-daemon-or-script.py (CMD [ "python", "./script.py" ]).

Conteúdo do arquivo requirements.txt

parse
realpython-reader

Conteúdo do arquivo script.py

# script.py

import parse
from reader import feed

tutorial = feed.get_article(1)
headlines = [
    r.named["header"]
    for r in parse.findall("\n## {header}\n", tutorial)
]
print("\n".join(headlines))

Considerando que esses três arquivos estão juntos em um diretório, o processo de construção de uma imagem é realizado por meio do comando docker build. No comando a seguir, o parâmetro -t (--tag) permite atribuir um nome para a imagem sendo criada e o "." indica que o Dockerfile para a construção da imagem deve ser procurado no diretório corrente.

O resultado da primeira execução do comando de build faz com que as várias camadas que irão compor a imagem solicitada sejam baixadas e combinadas. O resultado abaixo ilustra esse processo:

$ docker build -t python:sample .
Sending build context to Docker daemon  4.096kB
Step 1/6 : FROM python:3
3: Pulling from library/python
5492f66d2700: Pull complete 
540ff8c0841d: Pull complete 
a0bf850a0df0: Pull complete 
d751dc38ae51: Pull complete 
9720a112e886: Pull complete 
f97b81fbdbd9: Pull complete 
a70c58953c25: Pull complete 
6f7b858c1584: Pull complete 
74b4b07d81e4: Pull complete 
Digest: sha256:6441e2f0bd2e566de0df6445cb8e7e395ea1a376dd702de908d70401d3700961
Status: Downloaded newer image for python:3
 ---> ee2a4300ffa2
Step 2/6 : WORKDIR /usr/src/app
 ---> Running in e7464a2483a6
Removing intermediate container e7464a2483a6
 ---> c311c48f19db
Step 3/6 : COPY requirements.txt ./
 ---> ea7e1c3c6991
Step 4/6 : RUN pip install --no-cache-dir -r requirements.txt
 ---> Running in bff3765cf691
Collecting parse
  Downloading parse-1.19.0.tar.gz (30 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
Collecting realpython-reader
  Downloading realpython_reader-1.0.0-py3-none-any.whl (5.7 kB)
Collecting html2text
  Downloading html2text-2020.1.16-py3-none-any.whl (32 kB)
Collecting feedparser
  Downloading feedparser-6.0.8-py3-none-any.whl (81 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 81.0/81.0 KB 7.1 MB/s eta 0:00:00
Collecting typing
  Downloading typing-3.7.4.3.tar.gz (78 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 78.6/78.6 KB 22.4 MB/s eta 0:00:00
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
Collecting importlib-resources
  Downloading importlib_resources-5.6.0-py3-none-any.whl (28 kB)
Collecting sgmllib3k
  Downloading sgmllib3k-1.0.0.tar.gz (5.8 kB)
  Preparing metadata (setup.py): started
  Preparing metadata (setup.py): finished with status 'done'
Building wheels for collected packages: parse, typing, sgmllib3k
  Building wheel for parse (setup.py): started
  Building wheel for parse (setup.py): finished with status 'done'
  Created wheel for parse: filename=parse-1.19.0-py3-none-any.whl size=24591 sha256=c5199f956a159e678dfaf936a03214269adf9265cb0aa964b95cd6b95e5ce632
  Stored in directory: /tmp/pip-ephem-wheel-cache-banbtltw/wheels/70/4b/f0/eaf5a8de646d8676dc25caa01949b9f9d883b8fa2efb435bc3
  Building wheel for typing (setup.py): started
  Building wheel for typing (setup.py): finished with status 'done'
  Created wheel for typing: filename=typing-3.7.4.3-py3-none-any.whl size=26325 sha256=f994d46721e8611affa5a7e7f862be68e13f4f1f583d4176a97a60b3cf22aa6d
  Stored in directory: /tmp/pip-ephem-wheel-cache-banbtltw/wheels/7c/d0/9e/1f26ebb66d9e1732e4098bc5a6c2d91f6c9a529838f0284890
  Building wheel for sgmllib3k (setup.py): started
  Building wheel for sgmllib3k (setup.py): finished with status 'done'
  Created wheel for sgmllib3k: filename=sgmllib3k-1.0.0-py3-none-any.whl size=6066 sha256=54cdac922cd615a2d5476da50fc3e81202548d9ff8d42e294c52ee52a680387d
  Stored in directory: /tmp/pip-ephem-wheel-cache-banbtltw/wheels/f0/69/93/a47e9d621be168e9e33c7ce60524393c0b92ae83cf6c6e89c5
Successfully built parse typing sgmllib3k
Installing collected packages: sgmllib3k, parse, typing, importlib-resources, html2text, feedparser, realpython-reader
Successfully installed feedparser-6.0.8 html2text-2020.1.16 importlib-resources-5.6.0 parse-1.19.0 realpython-reader-1.0.0 sgmllib3k-1.0.0 typing-3.7.4.3
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
Removing intermediate container bff3765cf691
 ---> ca94e718b78b
Step 5/6 : COPY . .
 ---> 4fec572aaf81
Step 6/6 : CMD [ "python", "./script.py" ]
 ---> Running in ba2d27c38ae9
Removing intermediate container ba2d27c38ae9
 ---> 9862ce240bbe
Successfully built 9862ce240bbe
Successfully tagged python:sample

Numa segunda execução do mesmo comando em sequência, como as partes que compõem a imagem já estão disponíveis localmente, não há a necessidade de buscá-las no registry e, desse modo, o processo de construção é acelerado. Com esse recurso de construção de imagens por camadas ganha-se em desempenho e se permite um melhor reaproveitamento das camadas já disponíveis.

$ docker build -t python:sample .
Sending build context to Docker daemon  4.096kB
Step 1/6 : FROM python:3
 ---> ee2a4300ffa2
Step 2/6 : WORKDIR /usr/src/app
 ---> Using cache
 ---> 1f48c37c77f6
Step 3/6 : COPY requirements.txt ./
 ---> Using cache
 ---> 3dca482a4287
Step 4/6 : RUN pip install --no-cache-dir -r requirements.txt
 ---> Using cache
 ---> ad9b2c5f81ee
Step 5/6 : COPY . .
 ---> 7c01f2a4c087
Step 6/6 : CMD [ "python", "./script.py" ]
 ---> Running in ef40ba306c05
Removing intermediate container ef40ba306c05
 ---> 09b1b0e92fcb
Successfully built 09b1b0e92fcb
Successfully tagged python:sample

O comando docker images -a permite consultar as camadas disponíveis localmente, inclusive as camadas intermediárias que compõem a imagem:

$ docker images -a
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
<none>       <none>    4fec572aaf81   5 minutes ago   929MB
python       sample    9862ce240bbe   5 minutes ago   929MB
<none>       <none>    ca94e718b78b   5 minutes ago   929MB
<none>       <none>    c311c48f19db   6 minutes ago   919MB
<none>       <none>    ea7e1c3c6991   6 minutes ago   919MB
python       3         ee2a4300ffa2   28 hours ago    919MB

De forma genérica pode se dizer que cada linha do Dockerfile contendo alguma instrução que irá agregar recursos na imagem fará com que uma nova camada seja sobreposta a anterior compondo a imagem desejada.

Dado que exista a imagem de um contêiner para realizar a tarefa que se deseja, basta solicitar a execução de um contêiner a partir da imagem desejada. Para se criar um contêiner a partir da imagem criada basta utilizar o comando docker run. No exemplo a seguir, o parâmetro --rm indica que o contêiner deve ser removido ao final de sua execução.

$ docker run --rm python:sample
Basic Image Operations With the Python Pillow Library
Read the full article at https://realpython.com/image-processing-with-the-

Opcionalmente, executado o comando acima sem o --rm fará com que o contêiner, após finalizar sua execução, continue disponível para execução futura e pode ser consultado por meio do comando docker ps -a.

$ docker ps -a
CONTAINER ID   IMAGE           COMMAND                CREATED         STATUS                     PORTS     NAMES
529d2796888a   python:sample   "python ./script.py"   2 minutes ago   Exited (0) 2 minutes ago             interesting_wescoff

Observa-se que, caso não seja nomeado no comando docker run, o Docker atribui um nome genérico para cada contêiner executado que pode ser utilizado para referenciá-lo no futuro (coluna NAMES). Cada contêiner também recebe um ID único (coluna CONTAINER ID).

Por exemplo, os comandos a seguir, são equivalentes: os dois primeiros reexecutam o contêiner com base no seu ID e os dois últimos utilizando o nome.

$ docker start 529d2796888a; docker attach 529d2796888a
529d2796888a
Basic Image Operations With the Python Pillow Library
Read the full article at https://realpython.com/image-processing-with-the-
$ docker start interesting_wescoff; docker attach interesting_wescoff
interesting_wescoff
Basic Image Operations With the Python Pillow Library
Read the full article at https://realpython.com/image-processing-with-the-

O comando docker start reinicia o contêiner em segundo plano e o comando docker attach anexa o terminal à saída padrão stdin, permitindo visualizar o resultado da execução.

Last updated