Docker (2/2): Docker Compose e tópicos avançados

Docker (2/2): Docker Compose e tópicos avançados

Na primeira parte aprendemos a gerenciar contêineres, os dados e volumes, as imagens, a criar aplicações com Docker e a usar GPUs. Neste segundo capítulo damos o salto para **Docker Compose** para orquestrar vários contêineres e para uma série de **temas avançados** do Docker. 🐳

Aviso: Este post foi traduzido para o português usando um modelo de tradução automática. Por favor, me avise se encontrar algum erro.

📚 **Esta entrada faz parte da série _Guia de Docker_**, dividida em dois capítulos que são lidos em ordem:

> * Parte 1: Contenedores, imágenes y aplicaciones

* 👉 **Parte 2: Docker Compose y temas avanzados**

Docker composelink image 61

Docker compose vs docker-composelink image 62

docker-compose foi uma ferramenta que foi criada para ajudar na manutenção de imagens e contêineres e tinha que ser instalada separadamente do docker. No entanto, o docker a incorporou em suas últimas versões e já não é necessário instalá-la; porém, para usá-la, em vez de usar o comando docker-compose é preciso usar o comando docker compose. Em muitos sites você encontrará informações com docker-compose, mas ao instalar o docker já virá instalado docker compose, por isso tudo o que se podia fazer com docker-compose é compatível com docker compose

Docker composelink image 63

Docker Compose é uma ferramenta do Docker que faz tudo o que vimos até agora, mas poupando-nos tempo e esforço. Editando um arquivo .yml, podemos dizer ao Docker Compose que crie todos os contêineres que quisermos.

Para usá-lo uma vez, não haverá muita diferença entre escrever todos os comandos que vimos antes ou escrever o arquivo .yml, mas quando você quiser voltar a ter a mesma configuração de contêineres em funcionamento, basta chamar o arquivo .yml para recriar toda a configuração

Vamos criar uma pasta onde guardaremos os arquivos do Docker Compose

	
< > Input
Python
!mkdir dockerComposeFiles
Copied

Criamos o arquivo .yml dentro

	
< > Input
Python
!touch dockerComposeFiles/docker-compose.yml
Copied

Um arquivo Docker Compose tem que começar pela versão

version: "<v.v>"

No momento de escrever isto, a última versão é a 3.8, então escrevemos essa.

*docker-compose.yml*:

version: "3.8"

A seguir indicam-se os serviços, que são os contentores. Em cada serviço é necessário especificar a imagem e, além disso, podem ser adicionados outros parâmetros como portas, variáveis de ambiente, etc.

serviços:
container1:
imagem: ubuntu

container2:
imagem: ubuntu

O docker-compose.yml ficaria assim:

version: "3.8"

services:
container1:
image: ubuntu
container2:
image: ubuntu

Uma vez que criamos o arquivo, no seu caminho, podemos executar tudo mediante o comando docker compose up, mas além disso, adicionando a opção -d, faremos com que ele rode em segundo plano

	
< > Input
Python
!cd dockerComposeFiles && docker compose up -d
Copied
>_ Output
			
[+] Running 1/0
⠿ Network dockercomposefiles_default Created 0.1s
⠋ Container dockercomposefiles-container2-1 Creating 0.0s
⠋ Container dockercomposefiles-container1-1 Creating 0.0s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠙ Container dockercomposefiles-container2-1 Creating 0.1s
⠙ Container dockercomposefiles-container1-1 Creating 0.1s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.2s
⠿ Container dockercomposefiles-container1-1 Starting 0.2s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.3s
⠿ Container dockercomposefiles-container1-1 Starting 0.3s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.4s
⠿ Container dockercomposefiles-container1-1 Starting 0.4s
[+] Running 1/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Starting 0.5s
⠿ Container dockercomposefiles-container1-1 Starting 0.5s
[+] Running 2/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Started 0.5s
⠿ Container dockercomposefiles-container1-1 Starting 0.6s
[+] Running 3/3
⠿ Network dockercomposefiles_default Created 0.1s
⠿ Container dockercomposefiles-container2-1 Started 0.5s
⠿ Container dockercomposefiles-container1-1 Started 0.7s

Se observarmos, criou dois contêineres dockercomposefiles-container1-1 e dockercomposefiles-container2-1 e a rede que os une dockercomposefiles_default

Vamos apagar os dois contêineres

	
< > Input
Python
!docker rm -f dockercomposefiles-container1-1 dockercomposefiles-container2-1
Copied
>_ Output
			
dockercomposefiles-container1-1
dockercomposefiles-container2-1

E apagamos a rede que foi criada

	
< > Input
Python
!docker network rm dockercomposefiles_default
Copied
>_ Output
			
dockercomposefiles_default

Vamos tentar fazer o que fizemos antes com o que sabemos até agora. Criamos uma nova imagem que venha com ping instalada

*Dockerfile*:

FROM ubuntu:20.04
      RUN apt update
      RUN apt install iputils-ping -y

E a compilamos

	
< > Input
Python
!docker build -t ubuntu:ping ./dockerImages
Copied
>_ Output
			
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu:20.04
---&gt; a0ce5a295b63
Step 2/3 : RUN apt update
---&gt; Running in 3bd5278d39b4
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
Get:1 http://security.ubuntu.com/ubuntu focal-security InRelease [114 kB]
Get:2 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB]
Get:3 http://security.ubuntu.com/ubuntu focal-security/universe amd64 Packages [898 kB]
Get:4 http://archive.ubuntu.com/ubuntu focal-updates InRelease [114 kB]
Get:5 http://archive.ubuntu.com/ubuntu focal-backports InRelease [108 kB]
Get:6 http://archive.ubuntu.com/ubuntu focal/universe amd64 Packages [11.3 MB]
Get:7 http://security.ubuntu.com/ubuntu focal-security/main amd64 Packages [2133 kB]
Get:8 http://security.ubuntu.com/ubuntu focal-security/multiverse amd64 Packages [27.5 kB]
Get:9 http://security.ubuntu.com/ubuntu focal-security/restricted amd64 Packages [1501 kB]
Get:10 http://archive.ubuntu.com/ubuntu focal/main amd64 Packages [1275 kB]
Get:11 http://archive.ubuntu.com/ubuntu focal/restricted amd64 Packages [33.4 kB]
Get:12 http://archive.ubuntu.com/ubuntu focal/multiverse amd64 Packages [177 kB]
Get:13 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages [2594 kB]
Get:14 http://archive.ubuntu.com/ubuntu focal-updates/restricted amd64 Packages [1613 kB]
Get:15 http://archive.ubuntu.com/ubuntu focal-updates/multiverse amd64 Packages [30.2 kB]
Get:16 http://archive.ubuntu.com/ubuntu focal-updates/universe amd64 Packages [1200 kB]
Get:17 http://archive.ubuntu.com/ubuntu focal-backports/universe amd64 Packages [27.4 kB]
...
Successfully built c3d32aa9de02
Successfully tagged ubuntu:ping
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Verificamos que foi criado

	
< > Input
Python
!docker image ls
Copied
>_ Output
			
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu ping c3d32aa9de02 About a minute ago 112MB
maximofn/ubuntu test a78cf3ea16d8 25 hours ago 77.8MB
nginx latest 2d389e545974 33 hours ago 142MB
ubuntu latest 2dc39ba059dc 12 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 12 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Nós alteramos a tag

	
< > Input
Python
!docker tag ubuntu:ping maximofn/ubuntu:ping
Copied
	
< > Input
Python
!docker image ls
Copied
>_ Output
			
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu ping c3d32aa9de02 About a minute ago 112MB
maximofn/ubuntu ping c3d32aa9de02 About a minute ago 112MB
maximofn/ubuntu test c3d32aa9de02 About a minute ago 112MB
nginx latest 2d389e545974 33 hours ago 142MB
ubuntu latest 2dc39ba059dc 12 days ago 77.8MB
ubuntu 20.04 a0ce5a295b63 12 days ago 72.8MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB

Editamos o arquivo Docker Compose para que pegue as imagens que acabamos de criar

*docker-compose.yml*:

version: "3.8"

services:
container1:
image: maximofn/ubuntu:ping

container2:
image: maximofn/ubuntu:ping

E além disso, dizemos para ele executar uma operação nula

O docker-compose.yml ficaria assim:

version: "3.8"

serviços:
container1:
image: ubuntu
command: tail -f /dev/null

container2:
image: ubuntu
comando: tail -f /dev/null

Nós o levantamos

	
< > Input
Python
!cd dockerComposeFiles && docker compose up -d
Copied
>_ Output
			
[+] Running 0/0
⠋ Container dockercomposefiles-container1-1 Recreate 0.1s
⠋ Container dockercomposefiles-container2-1 Recreate 0.1s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠙ Container dockercomposefiles-container2-1 Recreate 0.2s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠹ Container dockercomposefiles-container2-1 Recreate 0.3s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠸ Container dockercomposefiles-container2-1 Recreate 0.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠼ Container dockercomposefiles-container2-1 Recreate 0.5s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
⠴ Container dockercomposefiles-container2-1 Recreate 0.6s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
...
⠸ Container dockercomposefiles-container2-1 Recreate 1.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Recreated 0.1s
...
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 10.8s
⠿ Container dockercomposefiles-container2-1 Started 10.9s
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 10.8s
⠿ Container dockercomposefiles-container2-1 Started 10.9s

Vemos os contêineres que estão rodando

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
935939e5a75d maximofn/ubuntu:ping "tail -f /dev/null" 15 seconds ago Up 13 seconds dockercomposefiles-container2-1
f9138d7064dd maximofn/ubuntu:ping "tail -f /dev/null" 25 seconds ago Up 13 seconds dockercomposefiles-container1-1

Os dois contêineres estão em execução; agora entramos em um deles e tentamos fazer ping no outro

$ docker exec -it dockercomposefiles-container1-1 bash
root@f9138d7064dd:/# ping dockercomposefiles-container2-1
PING dockercomposefiles-container2-1 (172.21.0.3) 56(84) bytes of data.
64 bytes from dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=1 ttl=64 time=0.110 ms
64 bytes from dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=2 ttl=64 time=0.049 ms
64 bytes do dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=3 ttl=64 time=0.049 ms
64 bytes de dockercomposefiles-container2-1.dockercomposefiles_default (172.21.0.3): icmp_seq=4 ttl=64 time=0.075 ms^C
--- estatísticas de ping do dockercomposefiles-container2-1 ---
4 pacotes transmitidos, 4 recebidos, 0% de perda de pacotes, tempo 3068ms
rtt min/avg/max/mdev = 0.049/0.070/0.110/0.025 ms

Como vemos, podemos fazer ping, criamos corretamente a imagem com ping instalado. Além disso, no docker-compose fizemos com que seja executada uma não operação para que os contêineres estejam em execução

Apagamos os dois contêineres e a rede que criamos

	
< > Input
Python
!docker rm -f dockercomposefiles-container1-1 dockercomposefiles-container2-1
Copied
>_ Output
			
dockercomposefiles-container1-1
dockercomposefiles-container2-1
	
< > Input
Python
!docker network rm dockercomposefiles_default
Copied
>_ Output
			
dockercomposefiles_default

Como o Docker Compose nomeia os contêinereslink image 64

Se observamos, os contêineres que o Docker cria se chamam dockercomposefiles-container1-1 e dockercomposefiles-container2-1. Isso acontece porque a pasta em que está o arquivo do Docker Compose fica em uma pasta chamada dockerComposeFiles, por isso a primeira parte do nome dos contêineres é dockercomposefiles, em seguida aparece o nome do serviço que demos no arquivo Docker Compose (container1 e container2) e, por último, um número para poder criar mais se for necessário

De forma semelhante, isso ocorre com o nome da rede que foi criada dockercomposefiles_default

Logs no docker composelink image 65

Vamos agora alterar o arquivo Docker Compose, nas linhas em que tínhamos command: tail -f /dev/null, vamos colocar command: ping 0.0.0.0

E além disso, dizemos a ele para executar uma não operação

O docker-compose.yml ficaria assim:

version: "3.8"

serviços:
container1:
image: ubuntu
comando: ping 0.0.0.0

container2:
image: ubuntu
command: ping 0.0.0.0

Fazemos isso para que cada contêiner esteja disparando o ping constantemente, assim simulamos alguns logs

Se executarmos novamente o docker compose

	
< > Input
Python
!cd dockerComposeFiles && docker compose up -d
Copied
>_ Output
			
[+] Running 0/0
⠋ Container dockercomposefiles-container1-1 Recreate 0.1s
⠋ Container dockercomposefiles-container2-1 Recreate 0.1s
[+] Running 0/2
⠙ Container dockercomposefiles-container1-1 Recreate 0.2s
⠙ Container dockercomposefiles-container2-1 Recreate 0.2s
[+] Running 0/2
⠹ Container dockercomposefiles-container1-1 Recreate 0.3s
⠹ Container dockercomposefiles-container2-1 Recreate 0.3s
[+] Running 0/2
⠸ Container dockercomposefiles-container1-1 Recreate 0.4s
⠸ Container dockercomposefiles-container2-1 Recreate 0.4s
[+] Running 0/2
⠼ Container dockercomposefiles-container1-1 Recreate 0.5s
⠼ Container dockercomposefiles-container2-1 Recreate 0.5s
[+] Running 0/2
⠴ Container dockercomposefiles-container1-1 Recreate 0.6s
⠴ Container dockercomposefiles-container2-1 Recreate 0.6s
[+] Running 0/2
⠦ Container dockercomposefiles-container1-1 Recreate 0.7s
⠦ Container dockercomposefiles-container2-1 Recreate 0.7s
[+] Running 0/2
⠧ Container dockercomposefiles-container1-1 Recreate 0.8s
⠧ Container dockercomposefiles-container2-1 Recreate 0.8s
[+] Running 0/2
...
⠿ Container dockercomposefiles-container1-1 Starting 11.0s
⠿ Container dockercomposefiles-container2-1 Started 11.0s
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 11.1s
⠿ Container dockercomposefiles-container2-1 Started 11.0s

Agora podemos ver os logs dos dois contêineres por meio do comando docker compose logs

	
< > Input
Python
!cd dockerComposeFiles && docker compose logs
Copied
>_ Output
			
dockercomposefiles-container2-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.042 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.025 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.022 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.026 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=11 ttl=64 time=0.028 ms
dockercomposefiles-container1-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=12 ttl=64 time=0.027 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=13 ttl=64 time=0.039 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=14 ttl=64 time=0.035 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=15 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=16 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=17 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=18 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=19 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=20 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=21 ttl=64 time=0.033 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=22 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.037 ms
...
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=214 ttl=64 time=0.015 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=215 ttl=64 time=0.021 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=216 ttl=64 time=0.020 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=217 ttl=64 time=0.049 ms

Como vemos, podemos ver os logs dos dois contêineres, mas no caso de querer ver apenas os de um, podemos especificar o **nome do serviço**

	
< > Input
Python
!cd dockerComposeFiles && docker compose logs container1
Copied
>_ Output
			
dockercomposefiles-container1-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.037 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.025 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.023 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.031 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.033 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.022 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.032 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.029 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=11 ttl=64 time=0.031 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=12 ttl=64 time=0.024 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=13 ttl=64 time=0.029 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=14 ttl=64 time=0.032 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=15 ttl=64 time=0.033 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=16 ttl=64 time=0.034 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=17 ttl=64 time=0.028 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=18 ttl=64 time=0.034 ms
...
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=332 ttl=64 time=0.027 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=333 ttl=64 time=0.030 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=334 ttl=64 time=0.033 ms
dockercomposefiles-container1-1 | 64 bytes from 127.0.0.1: icmp_seq=335 ttl=64 time=0.036 ms
	
< > Input
Python
!cd dockerComposeFiles && docker compose logs container2
Copied
>_ Output
			
dockercomposefiles-container2-1 | PING 0.0.0.0 (127.0.0.1) 56(84) bytes of data.
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.042 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.025 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.022 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.021 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.030 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.026 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=11 ttl=64 time=0.028 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=12 ttl=64 time=0.027 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=13 ttl=64 time=0.039 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=14 ttl=64 time=0.035 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=15 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=16 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=17 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=18 ttl=64 time=0.036 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=19 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=20 ttl=64 time=0.032 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=21 ttl=64 time=0.033 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=22 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=23 ttl=64 time=0.035 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=24 ttl=64 time=0.037 ms
...
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=340 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=341 ttl=64 time=0.033 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=342 ttl=64 time=0.034 ms
dockercomposefiles-container2-1 | 64 bytes from 127.0.0.1: icmp_seq=343 ttl=64 time=0.036 ms

Se queremos ver os logs continuamente, podemos adicionar a opção -f: docker compose logs -f <service name>

Se eu tiver feito um docker compose com mais de dois serviços, quando quiser ver os logs de vários serviços, basta adicionar mais nomes ao comando: docker compose logs <name service 1> <name service 2> ...

Exec serviçoslink image 66

Como vimos, por meio do comando exec podemos entrar em um contêiner indicando o nome do contêiner, o comando que se quer executar e a opção -it. Com o Docker Compose isso é mais simples, pois basta o nome do serviço e o comando, mas não é necessária a opção -it já que o Docker Compose a assume por padrão.

$ docker compose exec container1 bash
root@a7cf282fe66c:/#

Parando docker composelink image 67

Quando terminamos de trabalhar, com um único comando (stop), o Docker Compose para tudo; não é necessário parar um a um cada contêiner.

	
< > Input
Python
!cd dockerComposeFiles && docker compose stop
Copied
>_ Output
			
[+] Running 0/0
⠋ Container dockercomposefiles-container2-1 Stopping 0.1s
⠋ Container dockercomposefiles-container1-1 Stopping 0.1s
[+] Running 0/2
⠙ Container dockercomposefiles-container2-1 Stopping 0.2s
⠙ Container dockercomposefiles-container1-1 Stopping 0.2s
[+] Running 0/2
⠹ Container dockercomposefiles-container2-1 Stopping 0.3s
⠹ Container dockercomposefiles-container1-1 Stopping 0.3s
[+] Running 0/2
⠸ Container dockercomposefiles-container2-1 Stopping 0.4s
⠸ Container dockercomposefiles-container1-1 Stopping 0.4s
[+] Running 0/2
⠼ Container dockercomposefiles-container2-1 Stopping 0.5s
⠼ Container dockercomposefiles-container1-1 Stopping 0.5s
[+] Running 0/2
⠴ Container dockercomposefiles-container2-1 Stopping 0.6s
⠴ Container dockercomposefiles-container1-1 Stopping 0.6s
[+] Running 0/2
⠦ Container dockercomposefiles-container2-1 Stopping 0.7s
⠦ Container dockercomposefiles-container1-1 Stopping 0.7s
[+] Running 0/2
⠧ Container dockercomposefiles-container2-1 Stopping 0.8s
⠧ Container dockercomposefiles-container1-1 Stopping 0.8s
...
[+] Running 1/2
⠿ Container dockercomposefiles-container2-1 Stopped 10.4s
⠸ Container dockercomposefiles-container1-1 Stopping 10.4s
[+] Running 2/2
⠿ Container dockercomposefiles-container2-1 Stopped 10.4s
⠿ Container dockercomposefiles-container1-1 Stopped 10.4s

Como se pode ver, o docker compose parou os dois contêineres, mas não os apagou, nem apagou a rede

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1e6c1dd9adb2 maximofn/ubuntu:ping "ping 0.0.0.0" 16 minutes ago Exited (137) 25 seconds ago dockercomposefiles-container2-1
a7cf282fe66c maximofn/ubuntu:ping "ping 0.0.0.0" 16 minutes ago Exited (137) 25 seconds ago dockercomposefiles-container1-1
	
< > Input
Python
!docker network ls
Copied
>_ Output
			
NETWORK ID NAME DRIVER SCOPE
13cc632147f3 bridge bridge local
d4a2f718cd83 dockercomposefiles_default bridge local
da1f5f6fccc0 host host local
d3b0d93993c0 none null local

Docker compose como ferramenta de desenvolvimentolink image 68

Assim como vimos antes, para poder desenvolver, o ideal seria compartilhar a pasta que contém o código com o serviço. Isso, com o Docker Compose, é feito adicionando a etiqueta volumes ao arquivo docker compose. Primeiro temos que adicionar o caminho da pasta onde está o código no host e depois o caminho no contêiner.

*docker-compose.yml*:

version: "3.8"

services:
container1:
image: ubuntu
command: ping 0.0.0.0
volumes:
- ../dockerHostFolder/:/dockerContainerFolder

container2:
image: ubuntu
command: ping 0.0.0.0

Como se pode ver, o caminho da pasta do host eu o coloquei relativo

Se levantarmos o Docker Compose

	
< > Input
Python
!cd dockerComposeFiles && docker compose up -d
Copied
>_ Output
			
[+] Running 1/0
⠋ Container dockercomposefiles-container1-1 Recreate 0.1s
⠿ Container dockercomposefiles-container2-1 Created 0.0s
[+] Running 0/2
⠿ Container dockercomposefiles-container1-1 Starting 0.2s
⠿ Container dockercomposefiles-container2-1 Starting 0.2s
[+] Running 0/2
⠿ Container dockercomposefiles-container1-1 Starting 0.3s
⠿ Container dockercomposefiles-container2-1 Starting 0.3s
[+] Running 0/2
⠿ Container dockercomposefiles-container1-1 Starting 0.4s
⠿ Container dockercomposefiles-container2-1 Starting 0.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container1-1 Started 0.5s
⠿ Container dockercomposefiles-container2-1 Starting 0.5s
[+] Running 2/2
⠿ Container dockercomposefiles-container1-1 Started 0.5s
⠿ Container dockercomposefiles-container2-1 Started 0.6s

Se entrarmos no contêiner, podemos ver o que há dentro do arquivo text.txt

$ docker compose exec container1 bash
root@c8aae9d619d3:/# ls dockerContainerFolder/
bindFile.txt fileExtract.txt text.txt
root@c8aae9d619d3:/# cat dockerContainerFolder/text.txt
olá contenedor

Se agora o abrirmos no host, escrevemos hola host e voltamos a ver no contêiner

root@c8aae9d619d3:/# cat dockerContainerFolder/text.txt 
olá host

E agora ao contrário, se o modificarmos no contêiner

root@c8aae9d619d3:/# echo hola compose > dockerContainerFolder/text.txt
root@c8aae9d619d3:/# exit
exit

Se o virmos a partir do host, devemos obter hola compose

	
< > Input
Python
!cat dockerHostFolder/text.txt
Copied
>_ Output
			
hola compose

Exposição de portas no docker composelink image 69

Também podemos configurar as portas no arquivo do Docker Compose, por meio da etiqueta ports, indicando a porta do host e, em seguida, o IP do serviço

portas:
- <porta do host>:<porta do serviço>

Docker compose em equipe - docker overridelink image 70

Se somos um grupo de pessoas desenvolvendo sobre Docker com Docker Compose, é provável que muitas pessoas estejam alterando o arquivo Docker Compose, o que pode fazer com que ele não sincronize bem e haja conflitos.

Para resolver isso, o Docker oferece uma ferramenta chamada Docker Override. Dessa forma, pode haver um arquivo Docker Compose base e cada um pode modificá-lo por meio do Docker Override.

Para fazer isso, agora temos que criar um arquivo chamado docker-compose.override.yml, que será o que poderemos editar

	
< > Input
Python
!touch dockerComposeFiles/docker-compose.override.yml
Copied

Se agora tentarmos levantar o Docker Compose, vamos receber um erro

	
< > Input
Python
!cd dockerComposeFiles && docker compose up -d
Copied
>_ Output
			
Top-level object must be a mapping

E isto é porque o Docker Compose detectou que há um arquivo chamado docker-compose.override.yml e que ele está vazio, então vamos editá-lo. O arquivo docker-compose.override.yml serve para editar o arquivo docker-compose.yml, então, por exemplo, se quisermos fazer uma alteração no serviço container2 para adicionar um volume, escreveríamos assim o arquivo docker-compose.override.yml

*docker-compose.override.yml*:

version: "3.8"

serviços:
container2:
volumes:
- ../dockerHostFolder/:/dockerOverrideFolder

Note que a pasta compartilhada no serviço eu a chamei dockerOverrideFolder, então vamos subir o docker compose e ver se conseguimos ver essa pasta no contêiner container2

	
< > Input
Python
!cd dockerComposeFiles && docker compose up -d
Copied
>_ Output
			
[+] Running 1/0
⠋ Container dockercomposefiles-container2-1 Recreate 0.1s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠙ Container dockercomposefiles-container2-1 Recreate 0.2s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠹ Container dockercomposefiles-container2-1 Recreate 0.3s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠸ Container dockercomposefiles-container2-1 Recreate 0.4s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠼ Container dockercomposefiles-container2-1 Recreate 0.5s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠴ Container dockercomposefiles-container2-1 Recreate 0.6s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠦ Container dockercomposefiles-container2-1 Recreate 0.7s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 1/2
⠧ Container dockercomposefiles-container2-1 Recreate 0.8s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
...
[+] Running 1/2
⠿ Container dockercomposefiles-container2-1 Starting 10.8s
⠿ Container dockercomposefiles-container1-1 Running 0.0s
[+] Running 2/2
⠿ Container dockercomposefiles-container2-1 Started 10.8s
⠿ Container dockercomposefiles-container1-1 Running 0.0s

Vemos que demorou 10 segundos em montar o serviço container2, isso porque ele esteve aplicando as alterações

$ docker compose exec container2 bash
root@d8777a4e611a:/# ls dockerOverrideFolder/
bindFile.txt fileExtract.txt text.txt
root@d8777a4e611a:/# cat dockerOverrideFolder/text.txtolá compose
root@d8777a4e611a:/# exit

Baixamos o Compose e apagamos os contêineres e a rede criada

	
< > Input
Python
!cd dockerComposeFiles && docker compose down
Copied
>_ Output
			
[+] Running 0/0
⠋ Container dockercomposefiles-container2-1 Stopping 0.1s
⠋ Container dockercomposefiles-container1-1 Stopping 0.1s
[+] Running 0/2
⠙ Container dockercomposefiles-container2-1 Stopping 0.2s
⠙ Container dockercomposefiles-container1-1 Stopping 0.2s
[+] Running 0/2
⠹ Container dockercomposefiles-container2-1 Stopping 0.3s
⠹ Container dockercomposefiles-container1-1 Stopping 0.3s
[+] Running 0/2
⠸ Container dockercomposefiles-container2-1 Stopping 0.4s
⠸ Container dockercomposefiles-container1-1 Stopping 0.4s
[+] Running 0/2
⠼ Container dockercomposefiles-container2-1 Stopping 0.5s
⠼ Container dockercomposefiles-container1-1 Stopping 0.5s
[+] Running 0/2
⠴ Container dockercomposefiles-container2-1 Stopping 0.6s
⠴ Container dockercomposefiles-container1-1 Stopping 0.6s
[+] Running 0/2
⠦ Container dockercomposefiles-container2-1 Stopping 0.7s
⠦ Container dockercomposefiles-container1-1 Stopping 0.7s
[+] Running 0/2
⠧ Container dockercomposefiles-container2-1 Stopping 0.8s
⠧ Container dockercomposefiles-container1-1 Stopping 0.8s
...
⠸ Container dockercomposefiles-container2-1 Stopping 10.4s
⠸ Container dockercomposefiles-container1-1 Stopping 10.4s
[+] Running 1/2
⠿ Container dockercomposefiles-container2-1 Removed 10.4s
⠿ Container dockercomposefiles-container1-1 Removing 10.5s
[+] Running 2/2
⠿ Container dockercomposefiles-container2-1 Removed 10.4s
⠿ Container dockercomposefiles-container1-1 Removed 10.5s
⠋ Network dockercomposefiles_default Removing 0.1s
[+] Running 3/3
⠿ Container dockercomposefiles-container2-1 Removed 10.4s
⠿ Container dockercomposefiles-container1-1 Removed 10.5s
⠿ Network dockercomposefiles_default Removed 0.2s

Neste caso, apenas com down o Docker Compose parou e apagou tudo, já que, como vemos nos contêineres e na rede, aparece Removed

Reiniciar o Docker Composelink image 71

Ao escrever um docker compose, podemos adicionar a etiqueta restart para que, se o contêiner cair, ele seja reiniciado automaticamente

reiniciar: sempre

Dessa forma, se o contêiner cair, ele será reiniciado automaticamente. Se quisermos que ele seja reiniciado apenas um número de vezes, podemos adicionar a opção on-failure

restart: on-failure:<number>

Agora o contêiner será reiniciado um número de vezes, mas se cair mais vezes, não será reiniciado. Se quisermos que ele seja reiniciado sempre, podemos adicionar a opção unless-stopped

restart: unless-stopped

Agora o contêiner será reiniciado sempre, a menos que seja parado manualmente

Docker avançadolink image 72

Administrar ambiente de trabalholink image 73

Eliminação de contêineres desligadoslink image 74

Depois de passar um tempo desenvolvendo, podemos ter vários contêineres desligados, mas guardados no computador. Isso no final ocupa memória, então com docker container prune podemos eliminar todos os que estão parados

	
< > Input
Python
!docker run ubuntu
Copied
	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
effcee24f54a ubuntu "bash" 37 seconds ago Exited (0) 36 seconds ago musing_rosalind
$ docker container prune
AVISO! Isto removerá todos os contêineres parados.
Tem certeza de que deseja continuar? [y/N] y
Containers excluídos:
effcee24f54aab22e34fdea2465b3b7af132d8c627e5432ba3e915a370876977

Espaço recuperado total: 0B

Neste caso, economizamos 0 bytes, mas, no caso de deixar contêineres desligados após muito desenvolvimento, com certeza a economia de memória será maior

Exclusão de todos os contêinereslink image 75

No caso de termos contêineres em execução, podemos eliminar todos os contêineres por meio de outro comando

O comando docker ps -q nos devolve a ID de todos os contêineres, pelo que com o comando docker rm -f $(docker ps -aq) pararemos e eliminaremos todos

	
< > Input
Python
!docker run -d ubuntu tail -f /dev/null
Copied
>_ Output
			
c22516186ef7e3561fb1ad0d508a914857dbc61274a218f297c4d80b1fc33863
	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c22516186ef7 ubuntu "tail -f /dev/null" About a minute ago Up About a minute agitated_knuth
	
< > Input
Python
!docker rm -f $(docker ps -aq)
Copied
>_ Output
			
c22516186ef7
	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Apagamento totallink image 76

Como vimos, o Docker também cria redes, imagens, volumes, etc., então com o comando docker system prune podemos apagar todos os contêineres parados, todas as redes que não estejam sendo usadas por pelo menos um contêiner, as imagens duplicadas e o que estiver duplicado no cache de compilação

$ docker system prune
AVISO! Isto removerá:
- todos os contêineres parados
- todas as redes não usadas por pelo menos um contêiner
- todas as imagens pendentes
- todo o cache de compilação pendente
Tem certeza de que deseja continuar? [y/N] y
Espaço recuperado total: 0B

Assim como antes, não foi economizado muito espaço, mas depois de muito tempo de desenvolvimento, a economia será considerável

Uso de recursos do host por parte de contêinereslink image 77

Por exemplo, ao criar um contêiner, podemos limitar a RAM que o host pode usar por meio da opção --memory

	
< > Input
Python
!docker run -d --memory 1g ubuntu tail -f /dev/null
Copied
>_ Output
			
d84888eafe531831ef8915d2270422365adec02678122bf59580e2da782e6972

Mas com docker ps não temos acesso aos recursos que o contêiner está consumindo

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d84888eafe53 ubuntu "tail -f /dev/null" 35 seconds ago Up 34 seconds musing_ritchie

Para isso temos o comando docker stats

$ docker stats
CONTAINER ID NOME CPU % USO DE MEMÓRIA / LIMITE MEM % NET I/O BLOCK I/O PIDS
d84888eafe53 musing_ritchie 0.00% 540KiB / 1GiB 0.05% 5.62kB / 0B 0B / 0B 1

Isso é muito útil se quisermos simular um ambiente com um limite de RAM

Parando contêineres corretamente: SHELL vs EXEClink image 78

Como explicamos, quando atribuimos um processo a um contêiner, quando esse processo termina, o contêiner para, mas às vezes podemos nos deparar com problemas com isso. Vamos criar uma nova pasta chamada Dockerfile_loop

	
< > Input
Python
!mkdir Dockerfile_loop
Copied

Agora vamos criar um arquivo chamado loop.sh dentro de Dockerfile_loop

	
< > Input
Python
!touch Dockerfile_loop/loop.sh
Copied

E vamos escrever o seguinte dentro de loop.sh

#!/usr/bin/env bash
      trap "exit 0" SIGTERM
      while true; do :; done

Se eu executar este script no host, ele continuará sendo executado até que eu pressione CTRL+C

./loop
^C

Agora vamos criar um arquivo Dockerfile dentro de Dockerfile_loop

	
< > Input
Python
!touch Dockerfile_loop/Dockerfile
Copied

*Dockerfile*:

FROM ubuntu:trusty
      COPY ["loop.sh", "/"]CMD /loop.sh

Vamos criar uma imagem baseada no Ubuntu que copia o script para dentro e o executa, e o script é executado até receber o sinal SIGTERM do sistema operacional. Compilamos a imagem

	
< > Input
Python
!docker build -t ubuntu:loop ./Dockerfile_loop
Copied
>_ Output
			
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM ubuntu:trusty
---&gt; 13b66b487594
Step 2/3 : COPY ["loop.sh", "/"]
---&gt; 89f2bbd25a88
Step 3/3 : CMD /loop.sh
---&gt; Running in ff52569c35fd
Removing intermediate container ff52569c35fd
---&gt; feb091e4efa3
Successfully built feb091e4efa3
Successfully tagged ubuntu:loop
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Executamos o contêiner

docker run -d --name looper ubuntu:loop bash
	
< > Input
Python
!docker run -d --name looper ubuntu:loop
Copied
>_ Output
			
8a28f8cc9892213c4e0603dfdde320edf52c091b82c60510083549a391cd6645

Verificamos e vemos que o contêiner está em execução

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8a28f8cc9892 ubuntu:loop "/bin/sh -c /loop.sh" 4 seconds ago Up 3 seconds looper

Tentamos parar o contêiner com docker stop looper. O Docker stop tenta parar o contêiner enviando o sinal SIGTERM.

	
< > Input
Python
%%time
!docker stop looper
Copied
>_ Output
			
looper
CPU times: user 89.2 ms, sys: 21.7 ms, total: 111 ms
Wall time: 10.6 s

Isto demorou cerca de 10 segundos para parar, quando deveria ser imediato. Isso ocorre porque stop enviou a ordem SIGTERM para que o contêiner parasse, mas como ele não parava, depois de um tempo enviou um SIGKILL para forçar a interrupção. Vamos ver o que acontece, se listarmos os contêineres

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8a28f8cc9892 ubuntu:loop "/bin/sh -c /loop.sh" 23 seconds ago Exited (137) 2 seconds ago looper

Podemos ver que o sinal de Exited é 137, isso equivale a SIGKILL, ou seja, o Docker teve que forçar o encerramento.

Vamos excluir o contêiner e executá-lo novamente

	
< > Input
Python
!docker rm looper
Copied
>_ Output
			
looper
	
< > Input
Python
!docker run -d --name looper ubuntu:loop
Copied
>_ Output
			
84bc37f944d270be5f84a952968db2b8cf5372c61146d29383468198ceed18fd

Se agora tentarmos parar o contêiner com docker kill looper

	
< > Input
Python
%%time
!docker kill looper
Copied
>_ Output
			
looper
CPU times: user 9.1 ms, sys: 857 µs, total: 9.96 ms
Wall time: 545 ms

Vemos que o tempo é de uns 500 ms, ou seja, o docker o parou num momento enviando-lhe a ordem SIGKILL. Porque kill não envia SIGTERM e, se em um tempo o contêiner não tiver parado, envia SIGKILL, o que faz é enviar SIGKILL desde o início.

Se virmos os contêineres, vemos que o sinal de saída é o mesmo, 137

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
84bc37f944d2 ubuntu:loop "/bin/sh -c /loop.sh" 6 seconds ago Exited (137) 2 seconds ago looper

Esta não é a maneira correta de desligar um contêiner, porque quando quisermos desligar o contêiner, isso deve ser feito por meio do sinal SIGTERM, para que ele termine de processar o que estiver fazendo e então se desligue

Se apagarmos o contêiner e o executarmos novamente

	
< > Input
Python
!docker rm looper
Copied
>_ Output
			
looper
	
< > Input
Python
!docker run -d --name looper ubuntu:loop
Copied
>_ Output
			
b9d9f370cc0de7569eb09d0a85cd67e8ea6babc0754a517ccba5c5057f5cc50e

Se agora vemos os processos que estão sendo executados dentro do contêiner

	
< > Input
Python
!docker exec looper ps -ef
Copied
>_ Output
			
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 14:05 ? 00:00:00 /bin/sh -c /loop.sh
root 7 1 93 14:05 ? 00:00:02 bash /loop.sh
root 8 0 0 14:05 ? 00:00:00 ps -ef

Na realidade, o processo principal, o 1, não é /loop.sh, mas sim /bin/sh -c /loop.sh, ou seja, é um processo filho do shell. Portanto, quando chegava o sinal SIGTERM, ele chegava ao shell, mas este não o enviava aos seus processos filhos, por isso não chegava a loop.sh

Para que isso não aconteça, é preciso alterar o Dockerfile para o seguinte

*Dockerfile*:

FROM ubuntu:trusty
      COPY ["loop.sh", "/"]
      CMD ["/loop.sh"]    # antes era CMD /loop.sh

Esta forma é chamada exec form, enquanto a anterior é chamada shell form, de modo que, na forma anterior, o processo é executado como filho do shell, enquanto na forma exec form é executado o processo que lhe indicarmos. Assim, apagamos o contêiner, recompilamos e voltamos a executar o contêiner com a imagem

	
< > Input
Python
!docker rm -f looper
Copied
>_ Output
			
looper
	
< > Input
Python
!docker build -t ubuntu:loop ./Dockerfile_loop
Copied
>_ Output
			
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM ubuntu:trusty
---&gt; 13b66b487594
Step 2/3 : COPY ["loop.sh", "/"]
---&gt; Using cache
---&gt; 89f2bbd25a88
Step 3/3 : CMD ["/loop.sh"]
---&gt; Running in 6b8d92fcd57c
Removing intermediate container 6b8d92fcd57c
---&gt; 35a7bb2b1892
Successfully built 35a7bb2b1892
Successfully tagged ubuntu:loop
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
	
< > Input
Python
!docker run -d --name looper ubuntu:loop
Copied
>_ Output
			
850ae70c071426850b28428ac60dcbf875c6d35d9b7cc66c17cf391a23392965

Sim, agora vejo os processos dentro do contêiner

	
< > Input
Python
!docker exec looper ps -ef
Copied
>_ Output
			
UID PID PPID C STIME TTY TIME CMD
root 1 0 88 14:14 ? 00:00:02 bash /loop.sh
root 7 0 0 14:14 ? 00:00:00 ps -ef

Agora sim vejo que o processo principal, o 1, é /loop.sh

Se agora eu tentar parar o contêiner

	
< > Input
Python
%%time
!docker stop looper
Copied
>_ Output
			
looper
CPU times: user 989 µs, sys: 7.55 ms, total: 8.54 ms
Wall time: 529 ms

Vemos que demora mais. Vamos ver o código com o qual parou

	
< > Input
Python
!docker ps -a
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
850ae70c0714 ubuntu:loop "/loop.sh" About a minute ago Exited (0) 33 seconds ago looper

Contêineres executáveislink image 79

Se queremos um binário que funcione como um executável, no dockerfile é necessário especificar o comando em ENTRYPOINT e os parâmetros do comando em CMD, vamos ver isso

Vamos criar uma nova pasta onde guardaremos o Dockerfile

	
< > Input
Python
!mkdir dockerfile_ping
Copied

Agora criamos um Dockerfile dentro

	
< > Input
Python
!touch dockerfile_ping/Dockerfile
Copied

Escrevemos dentro do Dockerfile o seguinte

FROM ubuntu:trusty
      ENTRYPOINT [ "/bin/ping", "-c", "3" ]
      CMD [ "localhost" ]

Compilamos a imagem

	
< > Input
Python
!docker build -t ubuntu:ping ./dockerfile_ping
Copied
>_ Output
			
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM ubuntu:trusty
---&gt; 13b66b487594
Step 2/3 : ENTRYPOINT [ "/bin/ping", "-c", "3" ]
---&gt; Using cache
---&gt; 1cebcfb542b1
Step 3/3 : CMD [ "localhost" ]
---&gt; Using cache
---&gt; 04ddc3de52a2
Successfully built 04ddc3de52a2
Successfully tagged ubuntu:ping
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Se agora executarmos a imagem sem passar um parâmetro, o contêiner fará um ping a si mesmo

	
< > Input
Python
!docker run --name ping_localhost ubuntu:ping
Copied
>_ Output
			
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.041 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.058 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.054 ms
--- localhost ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2027ms
rtt min/avg/max/mdev = 0.041/0.051/0.058/0.007 ms

Mas se agora lhe passarmos um parâmetro, fará ping ao endereço que lhe indicarmos

	
< > Input
Python
!docker run --name ping_google ubuntu:ping google.com
Copied
>_ Output
			
PING google.com (216.58.209.78) 56(84) bytes of data.
64 bytes from waw02s06-in-f14.1e100.net (216.58.209.78): icmp_seq=1 ttl=111 time=3.93 ms
64 bytes from waw02s06-in-f14.1e100.net (216.58.209.78): icmp_seq=2 ttl=111 time=6.80 ms
64 bytes from waw02s06-in-f14.1e100.net (216.58.209.78): icmp_seq=3 ttl=111 time=6.92 ms
--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 3.930/5.886/6.920/1.383 ms

Apagamos os contêineres

	
< > Input
Python
!docker rm ping_localhost ping_google
Copied
>_ Output
			
ping_localhost
ping_google

O contexto de buildlink image 80

Vamos criar uma pasta chamada dockerfile_contexto

	
< > Input
Python
!mkdir dokerfile_contexto
Copied

Agora criamos nela dois arquivos: um test.txt e o Dockerfile

	
< > Input
Python
!touch dokerfile_contexto/Dockerfile dokerfile_contexto/text.txt
Copied

Modificamos o Dockerfile e colocamos o seguinte

FROM ubuntu:trusty
      COPY [".", "/"]

Isto que vai fazer é copiar para dentro da imagem tudo o que estiver na pasta em que se encontra o Dockerfile. Compilamos a imagem

	
< > Input
Python
!docker build -t ubuntu:contexto ./dokerfile_contexto
Copied
>_ Output
			
Sending build context to Docker daemon 2.56kB
Step 1/2 : FROM ubuntu:trusty
---&gt; 13b66b487594
Step 2/2 : COPY [".", "/"]
---&gt; 3ab79fdce389
Successfully built 3ab79fdce389
Successfully tagged ubuntu:contexto
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Vamos ver o que há dentro do contêiner

	
< > Input
Python
!docker run --name ls ubuntu:contexto ls
Copied
>_ Output
			
Dockerfile
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
text.txt
tmp
usr
var

Como vemos, está o arquivo text.txt. Mas é possível que dentro da pasta que está no mesmo diretório que o Dockerfile haja arquivos ou pastas que não queremos que sejam copiados para a imagem, por qualquer motivo. Então, assim como no git temos o .gitignore, no docker temos o .dockerignore, onde colocamos os arquivos ou pastas que não queremos que sejam considerados no momento da compilação

Então criamos um arquivo .dockerignore

	
< > Input
Python
!touch dokerfile_contexto/.dockerignore
Copied

E, dentro, adicionamos o text.txt e, de passagem, o Dockerfile, que não precisamos dentro da imagem

*.dockerignore*:

```Dockerfiletext.txt Dockerfiletext.txt ```

Apagamos o contêiner que havíamos criado, voltamos a compilar e vemos o que há dentro do contêiner

	
< > Input
Python
!docker rm ls
Copied
>_ Output
			
ls
	
< > Input
Python
!docker build -t ubuntu:contexto ./dokerfile_contexto
Copied
>_ Output
			
Sending build context to Docker daemon 3.072kB
Step 1/2 : FROM ubuntu:trusty
---&gt; 13b66b487594
Step 2/2 : COPY [".", "/"]
---&gt; 7a6689546da4
Successfully built 7a6689546da4
Successfully tagged ubuntu:contexto
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
	
< > Input
Python
!docker run --name ls ubuntu:contexto ls
Copied
>_ Output
			
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

Vemos que agora não estão nem Dockerfile nem text.txt. Apagamos o contêiner

	
< > Input
Python
!docker rm ls
Copied
>_ Output
			
ls

Build multi-stagelink image 81

No final de um desenvolvimento, não queremos que todo o código esteja na imagem que será enviada para produção.

Podemos dividir o dockerfile em dois, por exemplo, o developer.Dockerfile e o production.Dockerfile, onde no desenvolvimento haverá mais coisas do que no de produção. Na hora de compilá-los, por meio da opção -f escolhemos o dockerfile que queremos usar

docker build -t <tag> -f developer.Dockerfiledocker build -t <tag> -f production.Dockerfile

Mas, para não ter que criar dois arquivos Dockerfile, o Docker criou os multi-stage builds. Com um único Dockerfile, vamos resolver o problema

Criamos a pasta onde vamos guardar o Dockerfile

	
< > Input
Python
!mkdir docker_multi_stage
Copied

E dentro criamos o arquivo Dockerfile

	
< > Input
Python
!cd docker_multi_stage && touch Dockerfile
Copied

Editamos o arquivo, inserindo o seguinte

# Etapa 1: Gerar o executável com Python baseado em Alpine
FROM python:3.9-alpine as build-stage
WORKDIR /app
# Instalar dependências para PyInstaller
RUN apk add --no-cache gcc musl-dev libc-dev
# Gerar hello.py
RUN echo 'print("Hello from Alpine!")' > hello.py
# Instalar PyInstaller
RUN pip install pyinstaller
# Usar PyInstaller para criar um executável independente
RUN pyinstaller --onefile hello.py

# Etapa 2: Executar o executável em uma imagem Alpine
FROM alpine:latest
WORKDIR /app
# Copiar o executável da etapa de build
COPY --from=build-stage /app/dist/hello .
# Comando padrão para executar o executável
CMD ["./hello"]

Como pode ser visto, o Dockerfile está dividido em dois. Por um lado, trabalha-se sobre a imagem python:3.9-alpine, que se chama build-stage. E, por outro lado, trabalhamos sobre a imagem alpine:latest, que é uma imagem de Linux muito leve e é muito utilizada em produção

Nós o compilamos

	
< > Input
Python
!docker build -t maximofn/multistagebuild:latest ./docker_multi_stage
Copied
>_ Output
			
[+] Building 0.0s (0/2) docker:default
>_ Output
			
[+] Building 0.2s (4/6) docker:default
=&gt; [internal] load build definition from Dockerfile 0.0s
=&gt; =&gt; transferring dockerfile: 722B 0.0s
=&gt; [internal] load .dockerignore 0.0s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/alpine:latest 0.1s
=&gt; [internal] load metadata for docker.io/library/python:3.9-alpine 0.1s
...
=&gt; CACHED [stage-1 3/3] COPY --from=build-stage /app/dist/hello . 0.0s
=&gt; exporting to image 0.0s
=&gt; =&gt; exporting layers 0.0s
=&gt; =&gt; writing image sha256:7fb090d1495d00e892118b6bc3c03400b63a435fd4703 0.0s
=&gt; =&gt; naming to docker.io/maximofn/multistagebuild:latest 0.0s

Se agora vemos as imagens que temos

	
< > Input
Python
!docker image ls
Copied
>_ Output
			
REPOSITORY TAG IMAGE ID CREATED SIZE
maximofn/multistagebuild latest 7fb090d1495d 8 minutes ago 13.6MB

Vamos baixar a imagem do Python para ver quanto ela pesa

	
< > Input
Python
!docker pull python:3.9-alpine
Copied
>_ Output
			
3.9-alpine: Pulling from library/python
a8db6415: Already exists
d5e70e42: Already exists
3fe96417: Already exists
aa4dddbb: Already exists
518be9f7: Already exists Digest: sha256:6e508b43604ff9a81907ec17405c9ad5c13664e45a5affa2206af128818c7486
Status: Downloaded newer image for python:3.9-alpine
docker.io/library/python:3.9-alpine
	
< > Input
Python
!docker image ls
Copied
>_ Output
			
REPOSITORY TAG IMAGE ID CREATED SIZE
maximofn/multistagebuild latest 7fb090d1495d 9 minutes ago 13.6MB
python 3.9-alpine 6946662f018b 9 days ago 47.8MB

Podemos ver que, mientras nossa imagem pesa apenas 13,6 MB, a do Python com a qual a aplicação foi construída pesa 47,8 MB. Portanto, podemos tirar duas conclusões: com a primeira imagem, a do Python, ele construiu a aplicação, gerou o executável e esse executável é o que usamos na segunda imagem, a do Alpine. Além disso, podemos ver que, embora a primeira imagem que usa seja a do Python, ela não é baixada no nosso sistema, já que fomos nós que tivemos que baixá-la.

Bem, agora só falta testá-lo

	
< > Input
Python
!docker run --rm --name multi_stage_build maximofn/multistagebuild
Copied
>_ Output
			
Hello from Alpine!

Funciona!

Builds multi-archlink image 82

Suponhamos que queremos fazer uma imagem que possa ser executada em um computador e em uma Raspberry. O computador provavelmente terá um processador com arquitetura AMD64, enquanto a Raspberry tem um processador com arquitetura ARM. Portanto, não podemos criar a mesma imagem para ambos. Ou seja, quando criamos uma imagem, a criamos com um Dockerfile que costuma começar assim

DE ...

Portanto, o Dockerfile da imagem do computador poderia começar assim

FROM ubuntu:latest

Enquanto o da Raspberry poderia começar assim

FROM arm64v8/ubuntu:latest

Tendríamos que crear dos archivos Dockerfile, compilarlos y en el ordenador usar una imagen y en la Raspberry usar otra

Para evitar ter que ver a arquitetura do computador e ver qual imagem temos que usar, o Docker cria os manifest, que, como o nome indica, é um manifesto que indica em função de qual arquitetura de micro tenhamos, usa uma imagem ou outra

Então, vamos ver como fazer isso

Em primeiro lugar, criamos uma pasta onde vamos criar nossos arquivos Dockerfile

	
< > Input
Python
!mkdir docker_multi_arch
Copied

Agora criamos os dois Dockerfiles

	
< > Input
Python
!cd docker_multi_arch && touch Dockerfile_arm64 Dockerfile_amd64
Copied

Escrevemos o Dockerfile para AMD64

	
< > Input
Python
!cd docker_multi_arch && echo "FROM ubuntu:20.04" &gt;&gt; Dockerfile_amd64 && echo "CMD echo 'Hello from amd64'" &gt;&gt; Dockerfile_amd64
Copied
	
< > Input
Python
!cd docker_multi_arch && echo "FROM arm64v8/ubuntu:latest" &gt;&gt; Dockerfile_arm && echo "CMD echo 'Hello from ARM'" &gt;&gt; Dockerfile_arm
Copied

Agora combinamos as duas imagens

	
< > Input
Python
!cd docker_multi_arch && docker build -t maximofn/multiarch:arm -f Dockerfile_arm .
Copied
>_ Output
			
[+] Building 0.0s (0/1) docker:default
[+] Building 0.2s (2/3) docker:default
=&gt; [internal] load build definition from Dockerfile_amd64 0.1s
=&gt; =&gt; transferring dockerfile: 89B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/ubuntu:20.04 0.1s
[+] Building 0.3s (2/3) docker:default
=&gt; [internal] load build definition from Dockerfile_amd64 0.1s
=&gt; =&gt; transferring dockerfile: 89B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/ubuntu:20.04 0.2s
[+] Building 0.5s (2/3) docker:default
=&gt; [internal] load build definition from Dockerfile_amd64 0.1s
=&gt; =&gt; transferring dockerfile: 89B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/ubuntu:20.04 0.4s
[+] Building 0.6s (2/3) docker:default
=&gt; [internal] load build definition from Dockerfile_amd64 0.1s
=&gt; =&gt; transferring dockerfile: 89B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/ubuntu:20.04 0.5s
...
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load build definition from Dockerfile_arm 0.0s
=&gt; =&gt; transferring dockerfile: 94B 0.0s
=&gt; [internal] load metadata for docker.io/arm64v8/ubuntu:latest 1.8s
=&gt; [auth] arm64v8/ubuntu:pull token for registry-1.docker.io 0.0s
=&gt; CACHED [1/1] FROM docker.io/arm64v8/ubuntu:latest@sha256:94d12db896d0 0.0s
=&gt; exporting to image 0.0s
=&gt; =&gt; exporting layers 0.0s
=&gt; =&gt; writing image sha256:a9732c1988756dc8e836fd96e5c9512e349c97ea5af46 0.0s
=&gt; =&gt; naming to docker.io/maximofn/multiarch:arm 0.0s

Vamos ver o que temos nas duas imagens compiladas

	
< > Input
Python
!docker image ls
Copied
>_ Output
			
REPOSITORY TAG IMAGE ID CREATED SIZE
maximofn/multiarch arm a9732c198875 4 weeks ago 69.2MB
maximofn/multiarch amd64 5b612c83025f 6 weeks ago 72.8MB

Vemos que compilamos as duas imagens. Para poder criar um manifest, primeiro temos que enviar as imagens para o Docker Hub, então vamos enviá-las

	
< > Input
Python
!docker push maximofn/multiarch:amd64
Copied
>_ Output
			
The push refers to repository [docker.io/maximofn/multiarch]
82bdeb5f: Mounted from library/ubuntu amd64: digest: sha256:30e820f2a11a24ad4d8fb624ae485f7c1bcc299e8cfc72c88adce1acd0447e1d size: 529
	
< > Input
Python
!docker push maximofn/multiarch:arm
Copied
>_ Output
			
The push refers to repository [docker.io/maximofn/multiarch]
>_ Output
			
eda53374: Layer already exists arm: digest: sha256:6ec5a0752d49d3805061314147761bf25b5ff7430ce143adf34b70d4eda15fb8 size: 529

Se eu for ao meu Docker Hub, posso ver que minha imagem maximofn/multiarch tem as tags amd64 e arm

docker_multi_arch_tags

Agora vamos criar o manifest com base nessas duas imagens

	
< > Input
Python
!docker manifest create maximofn/multiarch:latest maximofn/multiarch:amd64 maximofn/multiarch:arm
Copied
>_ Output
			
Created manifest list docker.io/maximofn/multiarch:latest

Uma vez criado, temos que indicar as arquiteturas das CPUs às quais cada uma corresponde

	
< > Input
Python
!docker manifest annotate maximofn/multiarch:latest maximofn/multiarch:amd64 --os linux --arch amd64
Copied
	
< > Input
Python
!docker manifest annotate maximofn/multiarch:latest maximofn/multiarch:arm64 --os linux --arch arm64
Copied
>_ Output
			
manifest for image maximofn/multiarch:arm64 does not exist in maximofn/multiarch:latest

Uma vez criado e anotado, podemos enviar o manifest para o Docker Hub

	
< > Input
Python
!docker manifest push maximofn/multiarch:latest
Copied
>_ Output
			
sha256:1ea28e9a04867fe0e0d8b0efa455ce8e4e29e7d9fd4531412b75dbd0325e9304

Se agora eu voltar a olhar para as tags que a minha imagem maximofn/multiarch tem, também vejo a de latest

docker_multi_arch_tags_manifest

Agora, tanto se eu quiser usar minha imagem desde uma máquina com CPU AMD64 ou CPU ARM ao fazer FROM maximofn/multiarch:latest, o docker verificará a arquitetura da CPU e baixará a tag amd64 ou a tag arm. Vamos ver isso, se a partir do meu computador eu executo a imagem obtenho

	
< > Input
Python
!docker run maximofn/multiarch:latest
Copied
>_ Output
			
Unable to find image 'maximofn/multiarch:latest' locally
>_ Output
			
latest: Pulling from maximofn/multiarch
Digest: sha256:7cef0de10f7fa2b3b0dca0fbf398d1f48af17a0bbc5b9beca701d7c427c9fd84
Status: Downloaded newer image for maximofn/multiarch:latest
Hello from amd64

Como não a tem, faz o download dela

Se agora me conecto por SSH a um Raspberry Pi e testo o mesmo, obtenho

raspberry@raspberrypi:~ $ docker run maximofn/multiarch:latest
Não foi possível encontrar a imagem 'maximofn/multiarch:latest' localmente
latest: Baixando de maximofn/multiarch
Digest: sha256:1ea28e9a04867fe0e0d8b0efa455ce8e4e29e7d9fd4531412b75dbd0325e9304
Status: Imagem mais recente baixada para maximofn/multiarch:latest
Olá do ARM

Aparece Hello from ARM porque a Raspberry tem um micro com arquitetura ARM

Como se pode ver, cada máquina baixou a imagem de que precisava

Escrita correta de Dockerfiles avançadolink image 83

Já vimos a maneira de escrever corretamente Dockerfiles, mas há mais uma coisa que podemos fazer agora que conhecemos o multi-stage build: criar um contêiner para gerar o executável e outro menor para executá-lo

Chegamos à conclusão de que um bom Dockerfile poderia ser este

FROM python:3.9.18-alpine
      WORKDIR /sourceCode/sourceApp
      COPY ./sourceCode/sourceApp .
      CMD ["python3", "app.py"]

Vamos criar agora um executável em um contêiner builder e, em outro menor, vamos executá-lo

FROM python:3.9.18-alpine as builder
WORKDIR /sourceCode/sourceApp
RUN apk add --no-cache gcc musl-dev libc-dev && pip install pyinstaller
COPY ./sourceCode/sourceApp .
RUN pyinstaller --onefile app.py

FROM alpine:3.18.3
WORKDIR /sourceCode/sourceApp
COPY --from=builder /sourceCode/sourceApp/dist/app .
CMD ["./app"]

Criamos o código Python no caminho necessário

	
< > Input
Python
!mkdir multistagebuild/sourceCode
!mkdir multistagebuild/sourceCode/sourceApp
!touch multistagebuild/sourceCode/sourceApp/app.py
!echo 'print("Hello from Alpine!")' &gt; multistagebuild/sourceCode/sourceApp/app.py
Copied

Agora compilando a imagem

	
< > Input
Python
!docker build -t maximofn/multistagebuild:alpine-3.18.3 ./multistagebuild
Copied
>_ Output
			
[+] Building 0.0s (0/0) docker:default
[+] Building 0.0s (0/1) docker:default
[+] Building 0.2s (3/5) docker:default
=&gt; [internal] load build definition from Dockerfile 0.1s
=&gt; =&gt; transferring dockerfile: 357B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/alpine:3.18.3 0.1s
=&gt; [internal] load metadata for docker.io/library/python:3.9.18-alpine 0.1s
=&gt; [auth] library/alpine:pull token for registry-1.docker.io 0.0s
[+] Building 0.3s (3/5) docker:default
=&gt; [internal] load build definition from Dockerfile 0.1s
=&gt; =&gt; transferring dockerfile: 357B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/alpine:3.18.3 0.2s
=&gt; [internal] load metadata for docker.io/library/python:3.9.18-alpine 0.2s
=&gt; [auth] library/alpine:pull token for registry-1.docker.io 0.0s
[+] Building 0.5s (4/6) docker:default
=&gt; [internal] load build definition from Dockerfile 0.1s
=&gt; =&gt; transferring dockerfile: 357B 0.0s
=&gt; [internal] load .dockerignore 0.1s
=&gt; =&gt; transferring context: 2B 0.0s
=&gt; [internal] load metadata for docker.io/library/alpine:3.18.3 0.4s
...
=&gt; exporting to image 0.1s
=&gt; =&gt; exporting layers 0.1s
=&gt; =&gt; writing image sha256:8a22819145c6fee17e138e818610ccf46d7e13c786825 0.0s
=&gt; =&gt; naming to docker.io/maximofn/multistagebuild:alpine-3.18.3 0.0s

Nós a executamos

	
< > Input
Python
!docker run --rm --name multi_stage_build maximofn/multistagebuild:alpine-3.18.3
Copied
>_ Output
			
Hello from Alpine!

A imagem maximofn/multistagebuild:alpine-3.18.3 pesa apenas 13,6 MB

Diferença entre RUN, CMD e ENTRYPOINTlink image 84

EXECUTARlink image 85

O comando RUN é o mais simples, simplesmente executa um comando no momento da compilação da imagem. Por exemplo, se quisermos instalar um pacote na imagem, fazemos isso por meio de RUN.

Portanto, é importante: RUN é executado no momento da compilação da imagem, não quando o contêiner é executado

CMDlink image 86

O comando CMD é o comando que é executado quando o contêiner é executado. Por exemplo, se quisermos que o contêiner execute um comando quando for executado, fazemos isso por meio de CMD. Por exemplo, se tivermos uma aplicação em Python em um contêiner, com CMD podemos indicar que, quando o contêiner for executado, execute a aplicação em Python.

Dessa maneira, quando o contêiner for iniciado, a aplicação de Python será executada. Ou seja, se fizermos docker run <image> a aplicação de Python será executada. Mas CMD nos permite sobrescrever o comando que é executado quando o contêiner é iniciado; por exemplo, se fizermos docker run <image> bash bash será executado em vez da aplicação de Python.

PONTO DE ENTRADAlink image 87

O comando ENTRYPOINT é semelhante ao comando CMD, mas com uma diferença: e é que ENTRYPOINT não foi pensado para ser sobrescrito. Ou seja, se tivermos uma aplicação Python num contentor, com ENTRYPOINT podemos indicar que, quando o contentor for executado, execute a aplicação Python. Mas se fizermos docker run <image> bash, a aplicação Python será executada, e não bash.

Um uso muito comum de ENTRYPOINT é quando queremos que o contêiner seja um executável; por exemplo, se quisermos que o contêiner seja um executável de uma versão do Python que não temos no nosso host, porque, por exemplo, queremos testar a nova versão do Python que foi lançada, podemos fazer

FROM python:3.9.18-alpine
      ENTRYPOINT ["python3"]

Dessa forma, quando o contêiner for iniciado, o Python será executado. Ou seja, se fizermos docker run <image>, o Python será executado. Mas ENTRYPOINT nos permite sobrescrever o comando que é executado quando o contêiner é iniciado; por exemplo, se fizermos docker run <image> myapp.py, será executado python3 myapp.py dentro do contêiner. Assim, podemos testar nossa aplicação Python na nova versão do Python

Alterações em um contêinerlink image 88

Com docker diff podemos ver as diferenças que existem entre o contêiner e a imagem, o que é o mesmo que a diferença no contêiner desde que foi criado até agora

Vamos executar um contêiner e, dentro dele, criar um arquivo

	
< > Input
Python
!docker run --rm -it --name ubuntu-20.04 ubuntu:20.04 bash
Copied
>_ Output
			
root@895a19aef124:/# touch file.txt

Agora podemos ver a diferença

	
< > Input
Python
!docker diff ubuntu-20.04
Copied
>_ Output
			
C /root
A /root/.bash_history
A /file.txt

A significa que foi adicionado, C significa que foi alterado e D significa que foi apagado

Docker no Dockerlink image 89

Suponhamos que temos contêineres que precisam iniciar ou desligar outros contêineres. Isso é alcançado da seguinte forma

Como no Linux tudo é um arquivo e o host se comunica com o Docker por meio de um socket. Portanto, para o Linux, esse socket é um arquivo. Então, se montarmos esse socket como um arquivo no contêiner, ele poderá se comunicar com o Docker

Primeiro, vamos montar um contêiner com Ubuntu

	
< > Input
Python
!docker run -d --name ubuntu ubuntu:latest tail -f /dev/null
Copied
>_ Output
			
144091e4a3325c9068064ff438f8865b40f944af5ce649c7156ca55a3453e423

Vamos montar o contêiner que vai poder falar com o Docker montando a pasta /var/run/docker.sock

$ docker run -it --rm --name main -v /var/run/docker.sock:/var/run/docker.sock docker:19.03.12
/ #

Entramos dentro de um contêiner e, se dentro dele executarmos docker ps

# docker ps
CONTAINER ID IMAGEM COMANDO CRIADO STATUS PORTAS NOMES9afb778d6c20 docker:19.03.12 "docker-entrypoint.s…" 3 segundos atrás Em execução há 2 segundos main
144091e4a332 ubuntu:latest "tail -f /dev/null" 19 segundos atrás Ativo há 18 segundos ubuntu

Como podemos ver, dentro do Docker podemos ver os contêineres do host

Podemos executar um novo contêiner

# docker run -d --name ubuntu_from_main ubuntu:latest tail -f /dev/null
362654a72bb0fb047c13968707a6f16b87fed7ce051eb5c1a146b15828589a1a
/ #

E se voltarmos a ver os contêineres

# docker ps
CONTAINER ID IMAGE COMANDO CRIADO STATUS PORTAS NOMES362654a72bb0 ubuntu:latest "tail -f /dev/null" 3 segundos atrás Em execução há 3 segundos ubuntu_from_main
9afb778d6c20 docker:19.03.12 "docker-entrypoint.s…" Há cerca de um minuto Ativo há cerca de um minuto main
144091e4a332 ubuntu:latest "tail -f /dev/null" 2 minutos atrás Em execução há cerca de um minuto ubuntu

Mas se agora executarmos um novo terminal do host, veremos o contêiner criado de dentro do contêiner

	
< > Input
Python
!docker ps
Copied
>_ Output
			
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
362654a72bb0 ubuntu:latest "tail -f /dev/null" About a minute ago Up About a minute ubuntu_from_main
9afb778d6c20 docker:19.03.12 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes main
144091e4a332 ubuntu:latest "tail -f /dev/null" 3 minutes ago Up 3 minutes ubuntu

Tudo o que fizermos a partir do contêiner main será refletido no host

Isso tem a vantagem de que podemos instalar programas em um contêiner que tem acesso ao host para não termos que instalá-los no host. Por exemplo dive é uma ferramenta para explorar contêineres, mas se você não quiser instalá-la no host, pode instalá-la em um contêiner com acesso ao host, assim, a partir desse contêiner main, você pode explorar o restante dos contêineres sem ter que instalá-la no host

Continuar lendo

Últimos posts -->

Você viu esses projetos?

Gymnasia

Gymnasia Gymnasia
React Native
Expo
TypeScript
FastAPI
Next.js
OpenAI
Anthropic

Aplicativo móvel de treino pessoal com assistente de IA, biblioteca de exercícios, acompanhamento de rotinas, dieta e medidas corporais

Horeca chatbot

Horeca chatbot Horeca chatbot
Python
LangChain
PostgreSQL
PGVector
React
Kubernetes
Docker
GitHub Actions

Chatbot conversacional para cozinheiros de hotéis e restaurantes. Um cozinheiro, gerente de cozinha ou serviço de quarto de um hotel ou restaurante pode falar com o chatbot para obter informações sobre receitas e menus. Mas também implementa agentes, com os quais pode editar ou criar novas receitas ou menus

Naviground

Naviground Naviground
Ver todos os projetos -->
>_ Disponível para projetos

Tem um projeto com IA?

Vamos conversar.

maximofn@gmail.com

Especialista em Machine Learning e Inteligência Artificial. Desenvolvo soluções com IA generativa, agentes inteligentes e modelos personalizados.

Quer assistir alguma palestra?

Últimas palestras -->

Quer melhorar com essas dicas?

Últimos tips -->

Use isso localmente

Os espaços do Hugging Face nos permitem executar modelos com demos muito simples, mas e se a demo quebrar? Ou se o usuário a deletar? Por isso, criei contêineres docker com alguns espaços interessantes, para poder usá-los localmente, aconteça o que acontecer. Na verdade, se você clicar em qualquer botão de visualização de projeto, ele pode levá-lo a um espaço que não funciona.

Flow edit

Flow edit Flow edit

Edite imagens com este modelo de Flow. Baseado em SD3 ou FLUX, você pode editar qualquer imagem e gerar novas

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
Ver todos os contêineres -->
>_ Disponível para projetos

Tem um projeto com IA?

Vamos conversar.

maximofn@gmail.com

Especialista em Machine Learning e Inteligência Artificial. Desenvolvo soluções com IA generativa, agentes inteligentes e modelos personalizados.

Você quer treinar seu modelo com esses datasets?

short-jokes-dataset

HuggingFace

Dataset com piadas em inglês

Uso: Fine-tuning de modelos de geração de texto humorístico

231K linhas 2 colunas 45 MB
Ver no HuggingFace →

opus100

HuggingFace

Dataset com traduções de inglês para espanhol

Uso: Treinamento de modelos de tradução inglês-espanhol

1M linhas 2 colunas 210 MB
Ver no HuggingFace →

netflix_titles

HuggingFace

Dataset com filmes e séries da Netflix

Uso: Análise de catálogo Netflix e sistemas de recomendação

8.8K linhas 12 colunas 3.5 MB
Ver no HuggingFace →
Ver mais datasets -->