Advertisement
  1. Code
  2. Docker

Docker do Zero: Construindo Imagens

Scroll to top
Read Time: 10 min
This post is part of a series called Docker from the Ground Up.
Docker From the Ground Up: Understanding Images

() translation by (you can also view the original English article)

Containers Docker estão crescendo como uma melhor prática para deploy e administração de sistemas distribuídos nativos cloud. Containers são instâncias de imagens Docker. Acontece que existe muito a se saber e entender sobre imagens.

Nesse tutorial de duas partes, cobriremos imagens do Docker a fundo. Hoje, discutiremos os princípios básicos, considerações de projeto e inspecionaremos as "entranhas" de uma imagem. Cobriremos como criar nossas próprias imagens, resolução de problemas e trabalhar com repositórios de imagens.

Quando terminarmos, teremos um sólido entendimento do que são imagens Docker e como utilizá-las efetivamente em nossas aplicações e sistemas.

Criando Imagens

Há duas formas de criar imagens. Podemos modificar um container existente e então enviá-lo como uma nova imagem ou podemos criar um arquivo Dockerfile e criar uma imagem dele. Veremos ambos e explicaremos os prós e contras.

Criação Manual

Com criação manual, tratamos o container como um computador normal. Instalamos pacotes, criamos arquivos e quando tudo está feito, salvamos e acabamos com uma nova imagem para usarmos como modelo para criação de muitos outros containers ou mesmo como base de outras imagens.

Comecemos com a imagem alpine, que é bem pequena e simples, baseada no Linux Alpine. Podemos executá-la em modo interativo para acessar o terminal. Nosso objetivo é adicionar um arquivo chamado "yeah" com o texto "it works" no diretório raiz e então criar uma nova imagem chamada "yeah-alpine".

Aqui vamos nós. Certo, já estamos no diretório raiz. Vejamos o que temos lá.

1
> docker run -it alpine /bin/sh
2
/ # ls

3
bin      dev      etc      home     lib      linuxrc  media    mnt      proc     root     run      sbin     srv      sys      tmp      usr      var

Que editor está disponível? Nada de vim ou nano?

1
/ # vim

2
/bin/sh: vim: not found
3
/ # nano

4
/bin/sh: nano: not found

Bem... Apenas precisamos criar um arquivo:

1
/ # echo "it works!" > yeah

2
/ # cat yeah

3
it works!
4

Saímos do terminal e podemos ver o container chamado "vibrant_spenc" com docker ps --all. O semáforo --all é importante porque o container não está mais em execução.

1
> docker ps --all
2
CONTAINER ID IMAGE  COMMAND   CREATED       STATUS NAMES
3
c8faeb05de5f alpine "/bin/sh" 6 minutes ago Exited vibrant_spence

Aqui, criamos uma nova imagem a partir do container "vibrate_spence". Adicionamos uma mensagem de registro "mine, mine, mine" por boas práticas.

1
> docker commit -m "mine, mine, mine" vibrant_spence yeah-alpine
2
sha256:e3c98cd21f4d85a1428...e220da99995fd8bf6b49aa

Verifiquemos. Sim, há uma nova imagem e no histórico podemos ver a nova camada com o comentário "mine, mine, mine".

1
> docker images
2
REPOSITORY       TAG    IMAGE ID      SIZE
3
yeah-alpine      latest e3c98cd21f4d  4.8 MB
4
python           latest 775dae9b960e  687 MB
5
d4w/nsenter      latest 9e4f13a0901e  83.8 kB
6
ubuntu-with-ssh  latest 87391dca396d  221 MB
7
ubuntu           latest bd3d4369aebc  127 MB
8
hello-world      latest c54a2cc56cbb  1.85 kB
9
alpine           latest 4e38e38c8ce0  4.8 MB
10
nsqio/nsq        latest 2a82c70fe5e3  70.7 MB
11
12
> docker history yeah-alpine
13
IMAGE        CREATED         SIZE   COMMENT
14
e3c98cd21f4d 40 seconds ago  66 B   mine, mine, mine
15
4e38e38c8ce0 7 months ago    4.8 MB

Agora, o teste de verdade. Apaguemos o container e criemos um novo a partir da imagem. O resultado esperado é que o arquivo "yeah" esteja presente no novo container.

1
> docker rm vibrant_spence
2
vibrant_spence
3
4
> docker run -it yeah-alpine /bin/sh
5
/ # cat yeah

6
it works!
7
/ #

O que podemos dizer? Funciona!!

Usando um Dockerfile

Criar imagens a partir de containers modificados é legal, mas não é sustentável. É difícil registrar as mudanças e saber quais foram as modificações. A forma disciplinada de criar imagens é a partir do Dockerfile.

O Dockerfile é um arquio de texto similar a um script de terminal, mas suporta vários comandos. Cada comando que modifica o sistema de arquivo cria uma nova camada. Na parte um discutimos a importância de dividir a nossa imagem em camadas apropriadas. O Dockerfile é um gráde tópico por si só.

Aqui, apenas demonstraremos alguns comandos pra criar outra imagem, "oh-yeah-alpine", baseada no Dockerfile. Além de criar o infame arquivo "yeah", também instalaremos vim. A distribuição Linux Alpine usa um sistema administrador de pacotes chamado "apk". Eis o Dockerfile:

1
FROM alpine
2
3
# Copy the "yeah" file from the host

4
COPY yeah /yeah
5
6
# Update and install vim using apk

7
RUN apk update && apk add vim
8
9
CMD cat /yeah

A imagem base é alpine. Ele copia o arquivo "yeah" do diretório hospedeiro onde o Dockerfile está (o caminho do contexto de criação). Então executa apk update e instala vim. Por fim, configura o comando executado quando o container é executado. Nesse caso, imprimirá na tela o conteúdo do arquivo "yeah".

OK. Agora que sabemos onde nos metemos, construamos essa coisa. A opção "-t" configura o repositório. Não especificamos uma tag, então será o padrão "latest".

1
>  docker build -t oh-yeah-alpine .
2
Sending build context to Docker daemon 3.072 kB
3
Step 1/4 : FROM alpine
4
 ---> 4e38e38c8ce0
5
Step 2/4 : COPY yeah /yeah
6
 ---> 1b2a228cc2a5
7
Removing intermediate container a6221f725845
8
Step 3/4 : RUN apk update && apk add vim
9
 ---> Running in e2c0524bd792
10
fetch https://dl-cdn.alpinelinux.org/.../APKINDEX.tar.gz
11
fetch http://dl-cdn.alpinelinux.org.../x86_64/APKINDEX.tar.gz
12
v3.4.6-60-gc61f5bf [http://dl-cdn.alpinelinux.org/alpine/v3.4/main]
13
v3.4.6-33-g38ef2d2 [http://dl-cdn.alpinelinux.org/.../v3.4/community]
14
OK: 5977 distinct packages available
15
(1/5) Installing lua5.2-libs (5.2.4-r2)
16
(2/5) Installing ncurses-terminfo-base (6.0-r7)
17
(3/5) Installing ncurses-terminfo (6.0-r7)
18
(4/5) Installing ncurses-libs (6.0-r7)
19
(5/5) Installing vim (7.4.1831-r2)
20
Executing busybox-1.24.2-r9.trigger
21
OK: 37 MiB in 16 packages
22
 ---> 7fa4cba6d14f
23
Removing intermediate container e2c0524bd792
24
Step 4/4 : CMD cat /yeah
25
 ---> Running in 351b4f1c1eb1
26
 ---> e124405f28f4
27
Removing intermediate container 351b4f1c1eb1
28
Successfully built e124405f28f4

Parece legal. Verifiquemos se a imagem foi criada:

1
> docker images | grep oh-yeah
2
3
oh-yeah-alpine latest e124405f28f4 About a minute ago 30.5 MB

Notemos como instalar vim e suas dependências aumentaram o tamanho do container de 4.8MB da imagem alpine base para massivos 30.5MB!

É tudo muito bom. Mas funciona?

1
> docker run oh-yeah-alpine
2
it works!

Ah, sim, funciona!

Caso ainda duvidemos, acessemos o container, examinemos o arquivo "yeah" com nosso vim recém instalado.

1
> docker run -it oh-yeah-alpine /bin/sh
2
/ # vim yeah

3
4
it works!
5
~
6
~
7
.
8
.
9
.
10
~
11
"yeah" 1L, 10C

O Contexto de Criação e o Arquivo .dockerignore

Não dissemos ante, mas ao tentar criar a imagem oh-yeah-alpine, ela simplesmente ficou suspensa por vários minutos. O problema é que colocamos o Dockerfile no diretório padrão. Quando o Docker cria uma imagem, ele primeiro empacota todo o diretório onde o Dockerfile está (incluindo sub-diretório) e os disponibiliza para comandos COPY no Dockerfile.

Docker não está tentando ser esperto e analisando nossos comandos COPY. Ele apenas empacota tudo. Notemos que o conteúdo da criação não aparece na imagem, mas atrasa o comando build se o contexto de criação for grande demais.

Nesse caso, apenas copiamos o Dockerfile e o arquivo "yea" para um subdiretório e executamos o comando build no sub-diretório. Mas, alguma vezes, temos uma árvore de diretórios complicada da qual queremos copia sub-pastas e arquivos específicos e ignorar outros. O .dockerignore está aí para isso.

Ele nos permite controlar exatamente o que vai no contexto de criação. Nosso truque preferido é excluir tudo e começar a adicionar cada parte necessária. Por exemplo, nesse caso, poderíamos criar o .dockerignore abaixo e manter o Dockerfile o arquivo "yeah" no diretório raiz:

1
# Exclude EVERYTHING first

2
*
3
4
# Now selectively include stuff

5
!yeah

Não é preciso incluir o Dockerfile em si ou o .dockerignore no contexto de criação.

Copiar vs. Montar

Copiar arquivos para a imagem é algo que precisamos às vezes, mas, em outros casos, queremos que os containers sejam mais dinâmicos e trabalhem com arquivos no hospedeiro. É aí que volumes e montagens entram em ação.

Montar diretórios do hospedeiro é complicado. Os dados pertencem ao hospedeiro e não ao container, podem ser modificados quando ele está parado. E o container pode ser iniciado com diferentes diretórios hospedeiros montados.

Rotulando Imagens

Rotular imagens é importante se desenvolvemos sistemas baseados em micro-serviços e geramos várias imagens que precisam ser associadas umas com as outras. Podemos adicionar quantos rótulos quisermos a uma imagem.

Já vimos o rótulo padrão "latest". Alguma vez, faz mais sentido adicionar outros, como "tested", "release-1.4" ou hash do estado no Git que corresponde à imagem.

Podemos rotulá-la durante criação ou depois. Eis como adicionar rótulo a uma imagem existente. Notemos que, embora chamemos de rótulo, podemos atribuir um novo repositório também.

1
> docker tag oh-yeah-alpine oh-yeah-alpine:cool-tag
2
> docker tag oh-yeah-alpine oh-yeah-alpine-2
3
4
> docker images | grep oh-yeah
5
oh-yeah-alpine-2 latest    e124405f28f4 30.5 MB
6
oh-yeah-alpine   cool-tag  e124405f28f4 30.5 MB
7
oh-yeah-alpine   latest    e124405f28f4 30.5 MB

Também podemos remover rótulos de imagens usando o nome do rótulo. Isso é um pouco assustador por que se removermos o último rótulo sem querer, perdemos a imagem. Mas se criamo-na a partir de um Dockerfile, basta reconstruí-la.

1
> docker rmi oh-yeah-alpine-2
2
Untagged: oh-yeah-alpine-2:latest
3
4
> docker rmi oh-yeah-alpine:cool-tag
5
Untagged: oh-yeah-alpine:cool-tag

Se tentarmos remover remover a última imagem rotulada, obtemos um erro porque ela é usada por um container.

1
> docker rmi oh-yeah-alpine
2
3
Error response from daemon: conflict: unable to remove repository
4
reference "oh-yeah-alpine" (must force) - 
5
container a1443a7ca9d2 is using its referenced image e124405f28f4

Mas se removermos o container...

1
> docker rmi oh-yeah-alpine
2
Untagged: oh-yeah-alpine:latest
3
Deleted: sha256:e124405f28f48e...441d774d9413139e22386c4820df
4
Deleted: sha256:7fa4cba6d14fdf...d8940e6c50d30a157483de06fc59
5
Deleted: sha256:283d461dadfa6c...dbff864c6557af23bc5aff9d66de
6
Deleted: sha256:1b2a228cc2a5b4...23c80a41a41da4ff92fcac95101e
7
Deleted: sha256:fe5fe2290c63a0...8af394bb4bf15841661f71c71e9a
8
9
> docker images | grep oh-yeah

Sim, sumiu. Mas não nos preocupemos. Podemos reconstruí-la:

1
> docker build -t oh-yeah-alpine .
2
3
> docker images | grep oh-yeah
4
oh-yeah-alpine latest 1e831ce8afe1 1 minutes ago 30.5 MB

Yeah, está de volta! Viva o Dockerfile!

Trabalhando com Registros de Imagens

Imagens são similares a repositórios git. Também são criadas a partir de conjuntos ordenados de ordens. Podemos imaginar duas imagens usando a mesma imagem base como ramificação, mesmo não existindo mesclagem ou rebase no Docker). Um registro de imagem é o equivalente a uma hospedagem git centralizada, como o GitHub. Adivinhemos o nome do registro de imagens oficial do Docker: Sim, Docker Hub.

Baixando Imagens

Ao executar uma imagem, se não existir, Docker tentará baixá-la de um dos registros de imagens configurados. Por padrão, vai ao Docker Hub, mas podemos controlar pelo "~/.docker/config.json". Ao usar um registro diferente, seguimos suas instruções, o que costuma envolver autenticação com as credenciais deles.

Removamos a imagem "hello-world" e baixemo-na novamente usando o comando docker pull.

1
> dockere images | grep hello-world
2
hello-world latest c54a2cc56cbb 7 months ago 1.85 kB 
3
4
> docker rmi hello-world
5
hello-world

Se foi. Baixemos novamente.

1
> docker pull hello-world
2
Using default tag: latest
3
latest: Pulling from library/hello-world
4
78445dd45222: Pull complete

5
Digest: sha256:c5515758d4c5e1e...07e6f927b07d05f6d12a1ac8d7
6
Status: Downloaded newer image for hello-world:latest
7
8
> dockere images | grep hello-world
9
hello-world latest 48b5124b2768 2 weeks ago 1.84 kB

A hello-world antiga foi substituída por uma versão mais nova.

Enviando Imagens

Enviar imagens é um pouco mais complicado. Primeiro precisamos criar uma conta no Docker Hub (ou outro registro). Depois, autenticar. Então precisamos rotular a imagem a ser enviada, de acordo com o nome da conta ("g1g1", nesse caso).

1
> docker login -u g1g1 -p <password>
2
Login Succeeded
3
4
> docker tag hello-world g1g1/hello-world
5
6
> docker images | grep hello
7
8
g1g1/hello-world latest 48b5124b2768 2 weeks ago 1.84 kB
9
hello-world      latest 48b5124b2768 2 weeks ago 1.84 kB

Agora, podemos enviar a imagem rotulada g1g1/hello-word.

1
> docker push g1g1/hello-world
2
The push refers to a repository [docker.io/g1g1/hello-world]
3
98c944e98de8: Mounted from library/hello-world
4
latest: digest: sha256:c5515758d4c5e...f6d12a1ac8d7 size: 524

Conclusão

Imagens Docker são modelos para containers, projetadas para serem eficientes e oferecer o máximo de reuso através de um driver de armazenamento em camadas de sistema de arquivos.

Docker provê muitas ferramentas para listar, inspecionar, construir e rotular imagens. Podemos baixar ou enviar imagens para registros, como o Docker Hub, para administrar ou compartilhar imagens facilmente.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.