luiz@CyberInfra:~#

Sistemas Operacionais, Redes de Computadores, Cibersegurança...

Container

Verificando se o Docker está instalado

Para iniciar os trabalhos com o Docker, é bom primeiro verificar se o mesmo está devidamente instalado.

Isso pode ser feito de várias formas, mas vamos fazer verificando a versão do Docker instalado, com o seguinte comando:

$ docker --version
Docker version 27.0.3, build 7d4bcd863a

Caso o comando não exista, será necessário instalar o Docker em seu sistema. Nenhuma instalação será abordada aqui neste material, recomenda-se buscar informações a respeito de como proceder tal instalação no sítio oficial do Docker, já que a instalação pode variar de sistema para sistema.

Atenção

No caso do Linux veja como instalar em https://docs.docker.com/desktop/install/linux-install/.

Além do comando docker --version, é comum executar os comandos:

  • docker version, que apresenta mais informações do que o comando que foi digitado anteriormente;
  • docker info, que traz mais informações ainda, informações a respeito do cliente, servidor, etc.

Para ver se o servidor está em execução em sistemas Linux, é possível utilizar o comando ps, tal como:

$ ps ax | grep dockerd
    791 ?        Ssl    0:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
   8748 pts/0    S+     0:00 grep dockerd

Neste exemplo são retornados dois processos com o termo dockerd, sendo esses:

  • 8748, é do grep, utilizado para filtrar as saídas no comando ps (esse não interessa).
  • 791, é o processo em execução do Docker (/usr/bin/dockerd), ou seja, era o que estávamos procurando e se ele não estivesse ai, algo estaria errado com a execução do Docker neste sistema.
Atenção

Lembrando que os números dos processos provavelmente vão mudar em cada sistema, já que são aleatórios.

Bem, mas só executamos esses comandos para ter certeza que o Docker está instalado - isso não é obrigatório. Agora vamos para o próximo passo, que é realmente utilizar o Docker para fazer alguma coisa.

Executando containers (run)

Vamos executar um container Docker simples para efetivamente verificar se tudo está funcionando no Docker.

Para isso, vamos utilizar o comando docker run. Nesta primeira execução será iniciada uma imagem do Ubuntu Linux, com interação de um shell, para este sistema. Para tanto vamos executar o seguinte comando:

$ docker run -i -t ubuntu /bin/bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
9c704ecd0c69: Pull complete
Digest: sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30
Status: Downloaded newer image for ubuntu:latest
root@ebfc7d73bf3a:/#
Atenção

Pode ser necessário utilizar o comando sudo para executar o docker. Como exemplo, o comando anterior ficaria sudo docker run -i -t ubuntu /bin/bash.

O comando anterior, bem como sua saída, apresentam várias “coisas” interessantes do Docker. Assim, vamos dissecar primeiro o comando executado (docker run -i -t ubuntu /bin/bash), vendo as seguintes opções e parâmetros:

  • -i, mantém a saída padrão (STDIN) do container no console do hospedeiro, ou seja, todas as saídas geradas pelo container serão apresentadas na tela na qual o comando foi digitado;
  • -t, informa para o Docker associar um console virtual (pseudo-tty) para o container criado. Isso vai permitir interagir como container, através da execução de comandos;
  • ubuntu, é o nome da imagem utilizada para criar o container. Outras imagens poderiam ser utilizadas, essas podem estar disponível localmente ou na Internet, por exemplo no Docker Hub. Depois, em Imagens, vamos ver melhor como listar e utilizar essas imagens.
  • /bin/bash, esse é o comando a ser executado no container que está sendo criado. Mais especificamente, neste exemplo, estamos pedindo para o container executar o bash, que é normalmente o shell padrão de ambientes Linux. Neste caso, como esperamos interagir com o container, via comandos (dadas as opções -i e -t), vamos fazer isso via console. Ou seja, isso vai permitir interagir com o container através de um console texto, no qual será possível digitar comandos no container.

Interagindo com o bash do container

Bem, com o resultado do comando anterior, estamos dentro de um container, pronto para interagir com ele através do prompt de comando.

Atenção

Os comandos apresentados a seguir podem variar de container para container dependendo da distribuição e da versão da mesma, bem como das configurações já realizadas em um container pré-configurado. Tais comandos são do Linux e não do Docker.

Então vamos por exemplo executar alguns comandos Linux neste container.

Verificando o nome do container:

Podemos utilizar o comando hostname do Linux, para ver o nome do host/container.

root@ebfc7d73bf3a:/# hostname
ebfc7d73bf3a

A saída anterior, mostra que o nome do host é ebfc7d73bf3a.

Atenção

Neste exemplo o nome ebfc7d73bf3a é o ID do container, ou seja, é um valor que identifica o container de forma única dentro do Docker.

Verificando detalhes de redes do container:

Atualmente, para verificar as configurações de rede do Linux é comum utilizar o comando ip. Todavia se tentarmos utilizar o comando ip neste container, o resultado será o seguinte:

root@ebfc7d73bf3a:/# ip
bash: ip: command not found

Ou seja, o comando ip não está instalado por padrão neste container, mas é possível instalar pacotes neste container, conforme é abordado a seguir.

Atenção

Neste caso, seria possível ver o IP do container sem instalar o comando ip, isso pode ser feito com o comando hostname -I.

Atualizando o Ubuntu do container:

Para atualizar o Ubuntu do container basta fazer um atp update, tal como seria em um computador normal (sem container):

root@ebfc7d73bf3a:/# apt update
Get:1 http://archive.ubuntu.com/ubuntu noble InRelease [256 kB]
...
Reading state information... Done
2 packages can be upgraded. Run 'apt list --upgradable' to see them.

Instalando pacotes do Ubuntu do container:

Agora com o apt atualizado, é possível instalar o comando ip, no caso ele é disponibilizado através do pacotes iproute2, então vamos instalar esse:

root@ebfc7d73bf3a:/# apt install iproute2
Reading package lists... Done
...
Do you want to continue? [Y/n]
Get:1 http://archive.ubuntu.com/ubuntu noble/main amd64 libelf1t64 amd64 0.190-1.1build4 [57.6 kB]
...
Processing triggers for libc-bin (2.39-0ubuntu8.2) ...

A saída anterior mostra que o iproute2 foi instalado com sucesso.

Atenção

Se o container não for derivado do Debian, como é o caso do Ubuntu, pode ser que o comando apt não exista, ai será necessário utilizar outras formas para instalar pacotes - isso pode variar de distribuição para distribuição.

Verificando as configurações de rede com o comando ip:

Com o pacote iproute2 instalado, agora é possível utilizar o comando ip, tal como:

root@ebfc7d73bf3a:/# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

A saída anterior, mostra que no container existem as seguintes interfaces de rede:

  • lo, com o IP 127.0.0.1/8;
  • eth0, com o IP 172.17.0.2/16.

Daria para explorar mais informações, tal como a rota padrão através do comando ip route.

Verificando os processos em execução no container:

Para verificar os processos em execução no container, podemos utilizar o comando ps, tal como:

root@ebfc7d73bf3a:/# ps ax
    PID TTY      STAT   TIME COMMAND
      1 pts/0    Ss     0:00 /bin/bash
    339 pts/0    R+     0:00 ps ax

A saída do comando anterior mostra que há dois processos no container em questão. Sendo esses representados pelos seguintes PIDs:

  • 1, para o processo bash, que é o shell que pedimos para executar no comando que criou o container;
  • 339, para o processo ps, que é o último comando que foi executado.

Daria para ficar interagindo com o container, tal como em um sistema normal, mas vamos parar por aqui.

Saindo do console do container (exit)

Para sair do console do container, basta digitar exit, tal como:

root@ebfc7d73bf3a:/# exit
exit
$

Conforme a saída do comando anterior, veja que saímos do container ebfc7d73bf3a, no qual estávamos com o usuário root, e no exemplo, voltamos para o host fielDell, com o usuário luiz.

Bem, mas o que aconteceu com o container que estava em execução? A resposta é: ele parou de ser executado! Isso acontece, pois o Docker iniciou o container para executar o comando /bin/bash, e quando digitamos exit, o comando /bin/bash para de ser executado e assim o container inteiro para.

Listando containers

Uma tarefa extremamente comum é listar os containers que estão em execução, bem como os que estão parados. Veja como fazer isso a seguir.

Listar containers parados (ps -a)

Para ver os containers parados, execute o comando docker ps -a:

$ docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED             STATUS                        PORTS     NAMES
ebfc7d73bf3a   ubuntu                              "/bin/bash"              About an hour ago   Exited (0) 47 minutes ago               strange_jang
06d326091537   luizarthur/cyberinfra:hostDeb11     "/gns3/init.sh bash"     6 weeks ago         Exited (137) 6 weeks ago                admiring_burnell
Atenção

A saída infelizmente pode extrapolar o tamanho da página, e por isso as colunas ficam em posições ruins de mostrar o resultado - isso também pode acontecer em um terminal de computador.

No exemplo da saída anterior temos dois containers parados, sendo esses:

  • ebfc7d73bf3a, que tem o nome strange_jang, derivado de uma imagem chamada ubuntu, sendo esse o container deste exemplo.
  • 06d326091537, com o nome admiring_burnell, criado da imagem luizarthur/cyberinfra:hostDeb11.

Note ainda na saída anterior, que dá para ver os comandos de iniciação desses containers (COMMAND), bem como algumas informações de tempo (CREATED), estado do container (STATUS), etc.

A princípio o nome do container é gerado automaticamente, mas é possível informar um nome para o container utilizando a opção --name. Os nomes devem ser únicos. Assim, na hora de criar o container, informe o nome deste container, tal como:

$ docker run --name meuContainer -i -t ubuntu /bin/bash

No exemplo anterior seria criado um container chamado meuContainer.

Listar containers em execução (ps)

Para listar containers em execução, basta executar o comando docker ps, ou seja, é apenas tirar o -a do comando anterior. Veja o exemplo:

$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

No exemplo da saída anterior, não há nenhum container em execução.

Atenção

Note que o comando ps dentro de sistemas Unix-Like, apresentam os processos em execução. Já o ps passado como parâmetro para o comando docker, apresenta containers em execução ou parados. Ou seja, eles não são equivalentes.

Para listagens, há duas opções úteis:

  • -n seguida de um número inteiro, mostrará os últimos x containers (não importa o estado do container - em execução ou parado), sendo que x é o número digitado na frente de -n;
  • -l, mostrará apenas o último container (não importa o estado).

As opções -n e -l são importantes, pois podem haver vários containers na lista retornada pelo comando ps. Assim tais opções vão filtrar e mostrar os mais recentes, que normalmente são os que estamos trabalhando no momento.

Iniciando containers parados (start)

É possível iniciar um container que está parado, isso é feito com o comando docker start seguido do ID do container ou nome.

Então vamos iniciar o container que criamos anteriormente, através de seu ID (visto na listagem dos containers parados):

$ docker start ebfc7d73bf3a
ebfc7d73bf3a

Feito isso, agora se listarmos os containers em execução, teremos o seguinte resultado:

$ docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED       STATUS         PORTS     NAMES
ebfc7d73bf3a   ubuntu    "/bin/bash"   2 hours ago   Up 3 seconds             strange_jang

Ou seja, o container ebfc7d73bf3a, saiu de parado para em execução (Up).

Acessando containers em execução (attach)

Bem, mas e agora? Como interagir com o console deste container que acabamos de ligar novamente, no exemplo anterior? Para isso é possível utilizar a opção attach, tal como:

$ docker attach ebfc7d73bf3a
root@ebfc7d73bf3a:/#

Assim estamos novamente no console do container que voltou à execução pela opção start.

Atenção

Atenção, para sair de um console de um container, sem parar o processo, é possível pressionando as teclas Ctrl+p seguido de Ctrl+q. Então, pressione a tecla ctrl e a tecla p. Depois, sem soltar o ctrl, solte o p e pressione q.

Parando containers em execução (stop)

Para parar um container que está em execução podemos utilizar a opção stop. Então, normalmente você vai listar os containers em execução com o comando docker ps, vai pegar o ID ou nome deste e executar o comando docker, com a opção stop, tal como:

  • Verificando os containers em execução:
$ docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED       STATUS         PORTS     NAMES
ebfc7d73bf3a   ubuntu    "/bin/bash"   2 hours ago   Up 3 seconds             strange_jang

Neste caso vamos utilizar ID ebfc7d73bf3a.

  • Parando o container:
$ docker stop ebfc7d73bf3a
ebfc7d73bf3a
  • Verificando se realmente o container parou:
$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Como observa-se na última saída, o container ebfc7d73bf3a não está mais em execução. É claro que os passos de verificação apresentados aqui, não são obrigatórios, mas é comum executá-los no dia a dia, para ter certeza do que estamos fazendo.

A opção stop do Docker, envia um sinal SIGTERM para o container. Todavia é possível enviar o sinal SIGKILL, com a opção kill.

Desta forma, com o stop, o container é fechado de forma elegante, encerrando o processo corretamente. Já com o kill, o processo do container é fechado abruptamente, por exemplo, sem salvar conteúdos em disco - se for o caso. Então, o kill só deve ser utilizado em casos extremos, nos quais o container pode comprometer a integridade do sistema como um todo, ou por estar travado (não responde de forma alguma).

Atenção

As opções stop e kill do Docker fazem mais sentido para containers que rodam processos em plano de fundo, no estilo daemon (veremos esse a seguir). Se o container for executado de forma interativa, utilizando por exemplo o /bin/bash, é possível pará-lo com o exit, via console.

Criando containers no estilo daemon (-d)

Da forma que utilizamos container até o momento, eles ficam bastante parecidos com VMs completas, utilizados no VirtualBox ou VMWare. Entretanto, na prática, não se espera que os containers funcionem assim, de forma interativa - mesmo que eles possam ser utilizados desta forma.

Atualmente, esperá-se que os containers executem pequenas partes de um serviço maior, no estilo microsserviço. Por exemplo, um container pode executar um servidor de banco de dados, outro executa um serviço Web, tal como JavaScript, outro PHP e assim por diante. Lembre-se que o Docker, ao contrário das VMs completas, compartilham recursos do computador hospedeiro, então os containers não consomem recursos em demasia se comparados às VMs completas e por isso, são uma boa opção para segmentar serviços, isolando ambientes diferentes e assim fornecendo mais segurança, escalabilidade, etc.

Dito isso, é comum executar containers sem interagir diretamente com eles. Desta forma, o administrador basicamente vai configurar qual serviço ou serviços o container deve executar quando for iniciado, e depois, só vai interagir com os serviços providos pelo container - não vai ficar interagindo com o container via shell. Então, normalmente será necessário “daemonizar” os serviços que o container vai executar, ou seja, é necessário deixar os processos executados pelo container rodando em plano de fundo (backgroud), já que simplesmente não haverá uma tela por padrão esperando a interação do usuário. Em outras palavras, a ideia é colocar serviço/processo, bem como o container como daemon.

Atenção

Normalmente serviços de rede são executados em servidores como daemons, tais como: HTTP, SSH, SMB, NFS, etc.

Para iniciar processos em backgroud no Docker, utiliza-se a opção -d. Uma curiosidade é que o -d significa detach e não daemon, mas ela serve para deixar o processo como se fosse um daemon. Desta forma, com a opção detach, espera-se que não exista um console acoplado para interação, tal como:

$ docker run --name cont1 -d ubuntu /bin/sh -c "while true; do echo Olá mundo; date; sleep 10; done"
7b4b0a3be0b58fdfa7f8e0e4674bc38a400c564ec969a853ae6ee0add9796010

No comando anterior, estamos basicamente informando o seguinte para o Docker:

  • run: inicie o container;
  • --name cont1: atribuir o nome cont1 ao container;
  • -d: ele vai ser executado em plano de fundo (no estilo daemon);
  • ubuntu: utiliza a imagem do Ubuntu;
  • /bin/sh: vai executar um script.

Em resumo, iniciamos um container para executar um shell script que fica apresentando na saída do container o seguinte: texto “Olá mundo”, seguido da data/hora do container, repedindo isso a cada 10 segundos. Tudo isso foi feito através dos comandos que estão na frente de /bin/sh -c.

Atenção

Note que o comando passado para esse container é o /bin/sh, tudo que vêm após tal comando (-c "while true; do echo Olá...), são opções e parâmetros do sh e não do comando docker.

Se tudo correr bem, após iniciar o container utilizando a opção -d, não haverá nenhuma saída do container na tela do computador hospedeiro. Desta forma, para ver o status do container é possível utilizar o ps, tal como:

$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES
7b4b0a3be0b5   ubuntu    "/bin/sh -c 'while t…"   9 seconds ago   Up 9 seconds             cont1

O comando anterior, mostra pelo status, que o container está em execução, à 9 segundos (Up 9 seconds). Todavia, o status não mostra o que está acontecendo dentro do container, para ter mais detalhes vamos ver o comando logs na seção a seguir.

Verificando as saídas dos containers (logs)

Com o container sendo executado tal como um daemon, ou seja, em plano de fundo, o resultado da execução do container não aparecerá por padrão na tela do hospedeiro.

Todavia, é possível utilizar o comando docker logs para ver a saída de containers Docker que estão sendo executados como daemons.

Atenção

O docker logs apenas apresenta a saída do container, ele não permite a interação com o mesmo (ex. digitar comandos no container).

Desta forma, para ver o que está acontecendo no terminal do container do exemplo anterior, que está em background, podemos executar o seguinte comando:

$ docker logs cont1
Olá mundo
Tue Jul 16 18:56:51 UTC 2024
Olá mundo
Tue Jul 16 18:57:01 UTC 2024
Olá mundo
Tue Jul 16 18:57:11 UTC 2024
Olá mundo
Tue Jul 16 18:57:21 UTC 2024
Olá mundo
Tue Jul 16 18:57:31 UTC 2024
Olá mundo
Tue Jul 16 18:57:41 UTC 2024

A saída anterior mostra que o script executado no container do exemplo anterior, está funcionando corretamente, pois está apresentando na tela o texto “Olá mundo”, seguido da data/hora do container, a cada 10 segundos, tal como programado no script.

O comando docker logs, sem nenhuma opção, vai apresentar uma prévia da saída do container e parar. Caso seja necessário monitorar as saídas do container de forma continua, dá para utilizar a opção -f. Desta forma, as saídas do container ficam aparecendo na tela do hospedeiro, até o administrador pressionar Ctrl+c, para sair. Então para ter esse resultado o comando anterior ficaria da seguinte forma:

$ docker logs -f cont1
Olá mundo
Tue Jul 16 18:56:51 UTC 2024
Olá mundo
Tue Jul 16 18:57:01 UTC 2024
Olá mundo
Tue Jul 16 18:57:11 UTC 2024
...

O comando docker logs -f é muito utilizado no dia a dia, pois permite o monitoramento continuo do container que está sendo executado no estilo daemon.

Atenção

A opção docker logs -f é similar ao comando tail -f, utilizada para monitorar arquivos de log de sistemas Unix-Like. Lembre que é utilizado o Ctrl+c para sair desses comandos.

Reiniciando automaticamente containers (--restart)

Os containers podem parar de funcionar por causa de algum erro inesperado e se ele tiver sendo executado em plano de fundo, você provavelmente não verá tal problema.

Então é possível iniciar o container informando, por exemplo, que se algo der errado, ele deve se auto reiniciar e isso é feito com a opção --restart. Desta forma, caso o programa executado pelo container termine normalmente ou termine devido à algum erro, o container vai reiniciar sozinho.

Atenção

É importante perceber que o container sai do status de “em execução” para “parado”, devido à basicamente duas situações:

  • O programa que ele executa simplesmente terminou normalmente;
  • Houve algum erro com o programa e por isso ele foi abortado.

Exitem algumas opções para o reinicio automático para containers Docker, sendo as mais comuns:

  • always: com esta opção, o container sempre será reinicializado, não importa o motivo (terminou normalmente ou por erro). Mesmo se o computador hospedeiro for reinicializado, o Docker vai iniciar novamente o container.
  • unless-stopped: similar ao always, mas neste caso o container não é reinicializado se estiver no estado de “parado” (ex. alguém parou o container com docker stop). Todavia, se o processo do Docker for reinicializado (ex. reinicializaram o computador hospedeiro), o container será religado.
  • on-failure: com esta opção, o container é reinicializado apenas se tiver um erro (exit diferente de zero). Nesta opção ainda dá para determinar o número de vezes que o container vai tentar religar, caso essa quantidade seja atingida ele desiste de religar.

Para exemplificar o uso dessas funções do restart, vamos alterar um pouco o script utilizado no cont1 (exemplo anterior). Vamos criar um novo container chamado cont2, que basicamente tem o mesmo script do cont1, só que adicionado um exit 1, após o “Olá mundo” e o resto continua como era antes. O objetivo aqui é simular um erro no script, já que quando o código chegar a linha do exit 1, ele determina que o script deve ser interrompido, e é retornado um 1 para o sistema (que representa um erro). Desta forma, o script nunca vai mostrar a data/hora, tal como fazia antes, já que o exit é executado antes.

A seguir são apresentados exemplos de opções para o uso do restart com esse novo script alterado:

Opção always

No exemplo a seguir é utilizada a opção always do restart, de forma que o container seja reinicializada toda vez que ele for encerrado (por erro ou porque o programa terminou normalmente):

$ docker run --restart=always --name cont2 -d ubuntu /bin/sh -c "while true; do echo Olá mundo; exit 1; date; sleep 10; done"
6a17852229690344c0931d3fdcdd44775a17a79902d53bb70805b2ba43b7bd69

O comando anterior, mostra como utilizar a opção --restart=always. No comando também determinamos que o nome do container é cont2, que este deve ser executado em plano de fundo (-d), é criado a partir da imagem ubuntu, e principalmente está com o script alterado, que é abortado ao chegar no exit.

Assim, vamos utilizar a opção log, para ver a saída deste exemplo:

$ docker logs -f cont2
Olá mundo
Olá mundo
Olá mundo
Olá mundo
Olá mundo
Olá mundo
Olá mundo

Dada a saída anterior, observa-se que o container cont2 apresenta o texto “Olá mundo”, várias vezes, mas nunca a data/hora do sistema. Isso significa que o script é executado até o echo Olá mundo e é abortado. Desta forma, sem a opção restart o container seria encerrado, mas como estamos utilizando a opção --restart=always, o container é reiniciado toda vez que o script é finalizado pelo exit, esse comportamento vai se repetir indefinidamente.

Opção on-failure

O exemplo a seguir mostra como é utilizada a opção on-failure do restart. Tal opção só reinicializa o container se o processo for finalizado com um exit maior que zero, ou seja, se o container for finalizado por causa de erros do programa sendo executado. Desta forma, se o programa terminar normalmente, o container não será reinicializado.

Para esse exemplo, foi criado um container chamado cont3, que fora o nome do container, a única diferença do exemplo anterior (cont2) é que foi utilizado a opção --restart=on-failure:3. Sendo que esse :3, na frente da opção, significa que ele só será reinicializado três vezes. Se for utilizado apenas o --restart=on-failure, sem nada na frente (ex. :3), ele será reinicializado de forma indefinida.

$ docker run --restart=on-failure:3 --name cont3 -d ubuntu /bin/sh -c "while true; do echo Olá mundo; exit 1; date; sleep 10; done"
857022977d13473202ce0ac1988a124ff76375dfc1326ff7c5eb4f50b24949c4

Depois de executar o comando para criar o container chamado cont3, vamos ver sua saída com o logs:

$ docker logs -f cont3
Olá mundo
Olá mundo
Olá mundo
Olá mundo

Dada a saída anterior, note que há quatro vezes o texto “Olá mundo”, então o container foi executado pela primeira vez, ai saiu com um exit 1, depois isso aconteceu mais três vezes e o container foi abortado (ficou no estado de parado).

Vamos executar os comandos para verificar o status dos containers criados até aqui, primeiro vamos ver os containers ativos:

$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED              STATUS                          PORTS     NAMES
6a1785222969   ubuntu    "/bin/sh -c 'while t…"   About a minute ago   Restarting (1) 48 seconds ago             cont2
7b4b0a3be0b5   ubuntu    "/bin/sh -c 'while t…"   40 hours ago         Up 27 hours                               cont1

Note que estão ativos os containers cont1 e cont2, ou seja, o cont2 está em execução, mesmo que o seu processo esteja retornando erro em toda execução. Perceba que não há o container cont3 na saída anterior, então vamos ver se esse se encontra parado com o comando docker ps -l, já que este foi o último container que trabalhamos:

$ docker ps -l
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS                      PORTS     NAMES
857022977d13   ubuntu    "/bin/sh -c 'while t…"   58 seconds ago   Exited (1) 56 seconds ago             cont3

A saída anterior, demonstra mais uma vez que a opção --restart=on-failure:3 foi executada com sucesso, já que o cont3 foi abortado/parado, depois de algumas execuções.

Atenção

No comando anterior utilizamos docker ps -l para ver apenas o último container que foi abortado pelo Docker, pois como não há outros containers sabíamos que provavelmente o cont3 era o último container que foi abortado.

Deletando containers (rm)

Como descrito até aqui, uma vez que você execute um container, ele vai estar na lista de containers ativos ou parados, tal como pode ser visto a seguir, com os container que trabalhamos neste material:

$ docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED        STATUS                          PORTS     NAMES
857022977d13   ubuntu                              "/bin/sh -c 'while t…"   6 hours ago    Exited (1) 6 hours ago                    cont3
6a1785222969   ubuntu                              "/bin/sh -c 'while t…"   6 hours ago    Restarting (1) 55 seconds ago             cont2
7b4b0a3be0b5   ubuntu                              "/bin/sh -c 'while t…"   46 hours ago   Up 33 hours                               cont1
ebfc7d73bf3a   ubuntu                              "/bin/bash"              2 days ago     Exited (137) 47 hours ago                 strange_jang
06d326091537   luizarthur/cyberinfra:hostDeb11     "/gns3/init.sh bash"     7 weeks ago    Exited (137) 6 weeks ago                  admiring_burnell

Todavia, em alguns momentos vão haver containers que não vamos mais utilizar, como por exemplo containers de testes ou defasados. Assim, é possível remover esses containers utilizando o comando rm do Docker.

Por exemplo, vamos remover o container chamado cont3, para isso podemos utilizar o seguinte comando:

$ docker rm cont3
cont3

O resultado do comando será apenas uma saída com o nome do container, neste caso foi cont3. Neste exemplo anterior, utilizamos o nome do container para removê-lo, mas é possível utilizar o ID, vamos remover o primeiro container que criamos utilizando o ID dele, tal como:

$ docker rm ebfc7d73bf3a
ebfc7d73bf3a

Depois de remover o cont3, vamos tentar remover o cont2, entretanto o resultado para essa deleção será o seguinte:

$ docker rm cont2
Error response from daemon: cannot remove container "/cont2": container is restarting: stop the container before removing or force remove

A saída anterior informa que o cont2 está configurado para reiniciar, assim é necessário primeiro pará-lo e depois removê-lo, tal como:

$ docker stop cont2
cont2
$ docker rm cont2
cont2

Agora que os containers foram removidos, é possível constatar tal tarefa utilizando o comando docker ps -a:

$ docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED        STATUS                     PORTS     NAMES
7b4b0a3be0b5   ubuntu                              "/bin/sh -c 'while t…"   47 hours ago   Up 33 hours                          cont1
06d326091537   luizarthur/cyberinfra:hostDeb11     "/gns3/init.sh bash"     7 weeks ago    Exited (137) 6 weeks ago             admiring_burnell

A saída anterior mostra que foram removidos: o primeiro container que criamos; cont2 e cont3, ou seja, chegamos no resultado que queríamos para esses exemplos de remoção de containers.

Em alguns casos especiais é necessário remover todos os containers, mas não há um comando específico do Docker para tal tarefa. Assim, é possível combinar comandos, no estilo shell script para conseguir tal resultado, neste caso um possível comando no Linux seria:

$ docker rm -f $(docker ps -aq)

No comando anterior, foram combinados os comandos docker ps -aq, que gera uma lista de ID (-q) de containers do Docker, depois cada item desta lista é executada pelo comando docker rm, é claro que esse comando deve ser utilizado com cautela, pois apaga todos os containers do sistema.

Também dá para iniciar um container com a opção --rm, desta forma o container é executado uma única vez, e quando o programa que ele está executando terminar, o container é imediatamente removido do sistema. Um exemplo de comando utilizando o --rm é:

$ docker run --rm --name cont5 -d ubuntu /bin/sh -c "echo Olá mundo"
060e1626cd5e460ff6a9270da00ab350b9fbaea5d73841d5c50bfc6a5067fc09

Utilize o comando docker ps -l e você notará que o container cont5, criando anteriormente, não aparecerá na listagem de containers do sistema.

A opção --rm é muito útil durante a criação de containers de teste, pois assim que o container for encerrado o mesmo será removido e não vai ficar ocupando recursos dentro do sistema hospedeiro.

Rótulos nos containers (-l)

Como uma das ideias principais de containers é utilizar vários desses, cada um provendo determinados serviços, é natural que exista algum tipo de recurso mínimo para ajudar à organizar os containers.

Bem, um desses recursos são os rótulos (labels), que servem como metadados que podem ajudar a identificar os containers, por exemplo, quais containers são de uma dada organização, ou quais containers estão relacionados a um dado serviço. Então para utilizar os labels no Docker basta utilizar a opção -l. Veja o exemplo a seguir:

$ docker run --rm -l teste -d ubuntu /bin/sh -c "while true; do echo Olá mundo; date; sleep 10; done"
937619dfdf891ef84a98ac94c3d60d1edae2cd0cc596ba8459b2afeaaa815d8b

$ docker run --rm -l teste -d ubuntu /bin/sh -c "while true; do echo Olá mundo; date; sleep 10; done"

Anteriormente são criados dois containers, esses basicamente executam o primeiro script que utilizamos de exemplo, não foram passados nomes para esses, utilizam a opção --rm para serem removidos quando forem desligados e principalmente para estes exemplo, utilizam uma label chamada “teste” (-l teste).

Desta forma, agora é possível utilizar tal label para localizar os containers, tal como:

$ docker ps -a -f label=teste
CONTAINER ID   IMAGE     COMMAND                  CREATED              STATUS              PORTS     NAMES
496d3dc5d3dd   ubuntu    "/bin/sh -c 'while t…"   57 seconds ago       Up 56 seconds                 cranky_hodgkin
937619dfdf89   ubuntu    "/bin/sh -c 'while t…"   About a minute ago   Up About a minute             goofy_agnesi

Graças a esse rótulo, também é possível criar comandos mais complexos, tal como desligar todas os containers que tenham a label “teste”. O comando a seguir realiza essa tarefa:

$ docker stop $(docker ps -q --filter "label=teste")
496d3dc5d3dd
937619dfdf89

Como foi utilizada a opção --rm, esses container também foram removidos da lista de containers do sistema.

Atenção

É comum utilizar labels com mais de uma palavra separada por =, tal como desenvolvimento=maria.

Criando Containers (create)

Para criar um container é possível utilizar o comando run, tal como já fizemos no início deste texto, ou utilizar o comando create.

A grande diferença é que o create só cria o container e não coloca ele em execução. Então, após criar tal container é bem provável que o administrador execute o comando start. Desta forma, ao executar o comando run, ele internamente está executando um create seguido do start.

O comando a seguir é um exemplo do uso do comando create para criar um container chamado cont6:

$ docker create --name cont6 ubuntu /bin/sh -c "while true; do echo Olá mundo; date; sleep 10; done"

Após executar o comando anterior, o status do container será de criado (Created), veja:

$ docker ps -l
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS    PORTS     NAMES
931b22bca56f   ubuntu    "/bin/sh -c 'while t…"   10 seconds ago   Created             cont6

Todavia, o container do exemplo anterior, foi criado mas não está em execução, ou seja, está parado. Assim, para mudar o seu estado para em execução, é necessário executar um start, tal como:

$ docker start cont6
cont6

Desta forma o container irá para o estado de “executando” (Up), tal como apresentado na saída a seguir do comando docker ps -l, deste exemplo:

$ docker ps -l
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES
931b22bca56f   ubuntu    "/bin/sh -c 'while t…"   2 minutes ago   Up 5 seconds             cont6

É possível utilizar o create e start do Docker para criar containers, entretanto se for para criar o container e imediatamente colocá-lo em execução, é mais fácil/prático utilizar o comando run.

Atenção

Após criar um container com o comando create do Docker, não é possível utilizar o comando run, para executar um container com o mesmo nome do container criado. Neste caso, será reportado um erro dizendo que o container já existe.

Informações dos containers (inspect)

Comando inspect do Docker, permite verificar a configuração completa do container. O inspect, mostrará as mais diversas informações do container, tal como: nome, configurações de rede, armazenamento, estado, imagem, comando, etc. Isso pode ser muito útil, por exemplo, para a depuração de erros, identificação ou criação de containers similares.

Para inspecionar um container utilizando o inspect é necessário apenas executar docker inspect seguido do nome ou ID do container, tal como:

$ docker inspect cont6
[
    {
        "Id": "931b22bca56f04add7b383e12a862cd3cd9ef1c582cb8136e5b5afb0a8f4b935",
        "Created": "2024-07-19T14:56:22.519662813Z",
        "Path": "/bin/sh",
        "Args": [
            "-c",
            "while true; do echo Olá mundo; date; sleep 10; done"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            ...
        },
        "Image": "sha256:35a88802559dd2077e584394471ddaa1a2c5bfd16893b829ea57619301eb3908",
        "ResolvConfPath": "/var/lib/docker/containers/931b22bca56f04add7b383e12a862cd3cd9ef1c582cb8136e5b5afb0a8f4b935/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/931b22bca56f04add7b383e12a862cd3cd9ef1c582cb8136e5b5afb0a8f4b935/hostname",
        ...
        "Name": "/cont6",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        ...
            "NetworkMode": "bridge",
            "PortBindings": {},
           ...
        "Config": {
            "Hostname": "931b22bca56f",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "while true; do echo Olá mundo; date; sleep 10; done"
            ],
            "Image": "ubuntu",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "org.opencontainers.image.ref.name": "ubuntu",
                "org.opencontainers.image.version": "24.04"
            }
        },
        "NetworkSettings": {
            "Bridge": "",
            ...
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.4",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:04",
            ...
            }
        }
    }
]

Como é possível ver na saída anterior, que inclusive teve partes omitidas, o inspect traz muitas informações a respeito do container. Neste exemplo, verificamos as informações do cont6, que foi criando na seção anterior. Por exemplo, no final da saída, é possível ver muitas configurações de rede do container, tais como: IP, gateway padrão, endereço MAC, etc.

Todas as informações do inspect são apresentadas no formato JSON, o que facilita a automação de extração dessas informações, inclusive utilizando-se scripts. Na verdade o próprio Docker fornece meios para filtrar as informações que podem ser obtidas do inspect (a quantidade de informações pode atrapalhar a visão e análise em determinadas tarefas). Por exemplo, para obter o endereço IP do container, é possível executar o seguinte comando:

$ docker inspect --format '' cont6
172.17.0.4

Isso mostra que o cont6 tem o IP 172.17.0.4. Com o comando anterior, note que foi utilizado docker inspect, com a opção --format, seguido da estrutura JSON que armazena a informação que estamos querendo, e no final o nome ou ID do container. Para encontrar essa estrutura você deve dar realizar o inspect e analisar a hierarquia do JSON, no exemplo anterior o IPAddress está sob NetworkSettings, assim ficou .NetworkSettings.IPAddress.

Vamos obter do container cont6 qual é o comando que ele está executando, tal informação está em Path (ver primeira saída que geramos do inspect). Assim a busca por tal informação seria o seguinte comando:

$ docker inspect --format '' cont6
/bin/sh

Repare na saída do inspect apresentada no início da seção, que basicamente a mesma informação a respeito do comando executado, também está em Cmd, que está dentro de Config, ai a busca seria com o seguinte comando:

$ docker inspect --format '' cont6
[/bin/sh -c while true; do echo Olá mundo; date; sleep 10; done]

Também é possível pedir para o inspect retornar mais de uma informação na consulta, bem como fazer isso para mais de um container, tal como:

$ docker inspect --format '  ' cont1 cont4
/cont1 exited ubuntu
/cont4 exited ubuntu

Na saída anterior, o inspect foi instruído para buscar nos containers cont1 e cont4, as seguintes informações:

  • Nome do container (``);
  • Estado (``);
  • Imagem que está sendo utilizada (``).

Novamente, este tipo de busca por informações específicas a respeito de containers, pode ser extremamente útil no dia a dia do administrador de sistemas, em processos como a criação de telas que mostram o status dos sistemas para identificação de falhas, etc. Então, entender o inspect e como utilizar seus filtros é muito importante, principalmente em ambientes com diversos containers.

Conclusão

Neste texto foram apresentados os comandos básicos para a criação e gerenciamento de containers Docker. Então, este texto abordou como realizar tarefas simples, como: criar containers, listar, parar/executar, remover, inspecionar, etc. Lembrando que apesar dessas tarefas serem rotineiras, conhecer bem como realizá-las é fundamental para o bom funcionamento do sistema.

Todavia ainda não aprendemos como trabalhar com as imagens Dockers, que são a base para a criação dos containers, isso será feito em no texto de Imagens.