Skip to content

Instantly share code, notes, and snippets.

@ifasanelli
Last active April 23, 2024 11:48
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ifasanelli/4bf6d03a589539f1daba0b203072b5ea to your computer and use it in GitHub Desktop.
Save ifasanelli/4bf6d03a589539f1daba0b203072b5ea to your computer and use it in GitHub Desktop.

0. Índice

1. Instalação

1.1 Instalando o Docker no Ubuntu

Comandos utilizados no terminal

Atualiza o package index:

sudo apt-get update

Instala as dependências:

sudo apt-get install \
     apt-transport-https \
     ca-certificates \
     curl \
     gnupg \
     lsb-release

Adiciona a chave GPG oficial do Docker:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Configura o repositório estável:

echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Instala a engine do Docker:

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

Verifica se a instalação teve sucesso:

sudo docker run hello-world

Output:

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

1.2 Desinstalando o Docker no Ubuntu

Comandos utilizados no terminal:

sudo apt-get remove docker docker-engine docker.io containerd runc

2. Testando o Docker

Nome da imagem: whalaesay Comando utilizado no terminal:

sudo docker run docker/whalesay cowsay Olá, mundo!

Output:

 _____________
< Olá, mundo! >
 -------------
    \
     \
      \
                    ##        .
              ## ## ##       ==
           ## ## ## ##      ===
       /""""""""""""""""___/ ===
  ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~
       \______ o          __/
        \    \        __/
          \____\______/

Containers x Imagens

  • Imagem(Dockerfile) é o "projeto" que será executado pelo container, todas as instruções estarão nela;
  • Container é o Docker rodando alguma imagem, consequentemente, executando algum códigoo proposto por ela;
  • O fluxo é: programamos uma imagem e a executamos por meio de um container

3. Containers

3.1 O que são containers?

  • Container é um pacote de código que pode executar uma ação, por exemplo: rodar uma aplicação de Node.js, Ruby, Python e etc. Ou seja, nossos projetos são executados dentro dos containers que criamos/utilizamos;
  • Containers utilizam imagens para poderem ser executados;
  • Múltiplos containers podem rodar juntos, exemplo: um para Ruby e o outro para Postgres;

3.2 Verificando containers em execução e quais já foram executados

  • O comando docker ps ou docker container ls exibe quais containers estão sendo executados no momento;
  • Utilizando a flag -a, temos também todos os containers já executados na máquina, exemplo: docker ps -a;
  • Este comando é útil para entender o que acontece e o que está sendo executado no nosso ambiente

3.3 Executando container no modo Iterativo

  • Podemos rodar um container e deixá-lo executando no terminal;
  • Para isso utilizamos a flag -it;
  • Desta maneira podemos executar comandos disponíveis no container que estamos utilizando no comando run;
  • Podemos utilizar a imagem do Ubuntu para isso

Exemplo1: docker run -it ubuntu o container continuará sendo executado e teremos acesso ao terminal do Ubuntu com todos os comandos existentes nele, como ls, cd e etc. Exemplo2: docker run -it node o container continuará sendo executado e teremos acesso ao terminal e teremos acesso aos comandos disponíveis pelo Node, como var x = 10, console.log(x), 2 + 2 e etc

3.4 Executando containers em background (detached)

  • Quando iniciamos um container que persiste, ele fica ocuando o terminal;
  • Poodemos executar um container em background, para não precisar ficar com diversas abas de terminal abertas, utilizamos a flag -d
  • Verificamos containers em background com docker ps
  • Podemos utilizar nginx para este exemplo: docker run -d nginx

3.5 Expondo portas de containers

  • Os containers de docker não tem conexão com nada fora deles
  • Por isso precisamos expor portas utilizando a flag -p
  • Ao configurarmos a porta desta maneira: -p <numero-da-porta-do-computador>:<numero-da-portado-docker>

Exemplo: docker run -p 3000:80 nginx, o container estará acessível na porta 3000 no navegador

3.6 Parando containers

  • Podemos parar um container com o comando docker stop <id-ou-nome-do-container>
  • Desta maneira estaremos liberando recursos que estão sendo gastos pelo mesmo
  • Podemos verificar os containers rodando com o comando docker ps

3.7 Reiniciando containers

  • Para voltar a rodar um container parado utilizamos o comando docker start <id-ou-nome-do-container>
  • Lembrando que o comando docker run sempre criará um novo container
  • Então caso seja necessário aproveitar um antigo, opte pelo start

3.8 Definindo nome de container

  • Podemos definir um nome do container com a flag --name
  • Se não definido, um nome aleatório será atribuído, o que pode ser um problema para uma aplicação profissional
  • A flag run é inserida junto do comando run

Exemplo: docker run -d -p 80:80 --name nginx_app nginx

3.9 Verificando os logs do container

  • Podemos verificar o quie aconteceu em um container com o comando logs
  • Utilizando da seguinte maneira: docker logs <id-ou-nome-do-container>
  • As últimas ações realizadas no container, serão exibidas no terminal
  • Adicionando a flag -f, de follow, o terminal permenecerá mostrando os logs e os atualizando em tempo real

Exemplo: docker logs -f nginx_app

3.10 Removendo containers

  • Podemos remover um container da máquina que estamos executando o Docker
  • O comando é o docker rm <id-ou-nome-do-container>
  • Se o container ainda estiver rodando, podemos utilizar a flag -f(force)
  • O container removido não é mais listado em docker ps -a

Exemplo: docker rm nginx_app -f

4. Imagens

4.1 O que são imagens?

  • Imagens são originadas de arquivos que programamos Dockerfile para que o Docker crie uma estrutura que execute determinadas ações em containers
  • Elas contém informações como: imagem base, diretório base, comandos a serem executados, portaa da aplicação e etc
  • Ao rodar um container baseado na imagem, as instruções serão executadas em camadas

4.2 Onde encontrar e como escolher boas imagens?

  • No repositório oficial do Docker;
  • Neste site podemos verificar quais imagens existem da tecnologia que estamos procurando, por exemplo: Node.js;
  • E também aprender com utilizá-la;
  • Executamos uma imagem em um container com o comando docker run <nome-da-imagem>
  • Porém qualquer pessoa pode fazer upload de uma imagem, isso é um problema
  • Devemos então nos atentar as imagens oficiais
  • Outro parâmetro interessante é a quantidade de downloads e a quantidade de stars que a imagem possui

4.3 Criando uma imagem

  • Para criar uma imagem vamos precisar de um arquivo Dockerfile em uma pasta que ficará o projeto
  • Este arquivo vai precisar de algumas instruções para poder ser executado
  • FROM: imagem base
  • WORKDIR: diretório da aplicação
  • EXPOSE: porta da aplicação
  • COPY: quais arquivos precisam ser copiados

Exemplo: Primeiramente criaremos uma pequena aplicação em Node para ilustrarmos melhor o exemplo

# Cria um diretório para o projeto
mkdir ~/Projetos/primeira-imagem && cd ~/Projetos/primeira-imagem

# Inicializa um projeto em Node
npm init -y

# Instala o framework express
npm install express

# Cria o arquivo principal da aplicação
touch app.js

Já no arquivo app.js:

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
   res.send('Olá, minha imagem')
});

app.listen(port, () => {
   console.log(`Executand da porta ${port}`)
});

Então, no terminal, rodamos o comando node app.js, que executará a aplicação. E ao abrirmos o navegador em localhost:3000 podemos verificar a mensagem Olá, minha imagem enviada da aplicação e que a mesma funciona corretamente. Agora criaremos uma imagem que executará esta aplicação. No terminal executamos o comando: touch Dockerfile para criar o arquivo de configuração. No arquivo Dockerfile:

# Define a imagem base da aplicação
FROM node

# Define o diretório que será utilizado pela aplicação
WORKDIR /src

# Copia todos os arquivo package.json para o diretório de trabalho
COPY package*.json /src/

# Instala o projeto no container do Docker
RUN npm install

# Copia os demais arquivos para o diretório de trabalho
COPY . .

# Define a porta exposta
EXPOSE 3000

# Inicializa a aplicação
CMD ["node", "app.js"]

4.4 Executando uma imagem

  • Para executar uma imagem primeiramente vamos precisar fazer o build dela
  • O comando para isso é o docker build <diretorio-da-imagem>
  • Depois utilizamos o docker run <nome-da-imagem> para executá-la

No terminal:

# Vai para o diretório do projeto
cd ~/Projetos/primeira-imagem

# Se estivermos no diretório com o Dockerfile
docker build .

# Ou se estivermos em outro diretório
docker build ~/Projetos/primeira-imagem

Ao buildarmos a imagem, obteremos algo como:

Sending build context to Docker daemon  2.015MB
Step 1/7 : FROM node
 ---> 1016313cda78
Step 2/7 : WORKDIR /src
 ---> Using cache
 ---> 63c180dedba9
Step 3/7 : COPY package*.json /src/
 ---> e5ae9d50761f
Step 4/7 : RUN npm install
 ---> Running in 678f23e2fbee
Removing intermediate container 678f23e2fbee
 ---> 61fb92c04453
Step 5/7 : COPY . .
 ---> 15f436a21259
Step 6/7 : EXPOSE 3000
 ---> Running in b1482c93bc1d
Removing intermediate container b1482c93bc1d
 ---> 7ecb5fa2364e
Step 7/7 : CMD ["node", "app.js"]
 ---> Running in 6f93bd827cfa
Removing intermediate container 6f93bd827cfa
 ---> 8a1cebce1ec0
Successfully built 8a1cebce1ec0

4.5 Listando e Executando imagens

  • Para listar as imagens utilizamos o comando docker image ls

Exemplo:

docker image ls

Output:

REPOSITORY        TAG       IMAGE ID       CREATED         SIZE
<none>            <none>    8a1cebce1ec0   7 minutes ago   914MB

Nossa imagem recém buildada é listada e podemos perceber que ela ainda não possui nome ou tag, mais tarde os definiremos, mas a executaremos pelo id.

No console:

# Executa o container utilizando as flags:
# Detached(-d) que deixa o terminal livre para uso
# Port(-p) define a porta de execução <porta-no-computador>:<porta-definida-na-aplicação>
# Name(--name) define o nome do container
docker run -d -p 3000:3000 --name node_app 8a1cebce1ec0

# Verifica os containers em execução
docker ps

# Output
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                       NAMES
09e2afb2d5f2   8a1cebce1ec0   "docker-entrypoint.s…"   18 seconds ago   Up 18 seconds   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   node_app

4.6 Alterando uma imagem

  • Sempre que alteramos o código de uma imagem precisamos fazer o build novamente
  • Para o Docker é como se fosse uma imagem completamente nova
  • Após fazer o build vamos executá-la por outro id único criado com o docker run

Exemplo: Alteraremos a mensagem enviada pela aplicação no arquivo app.js

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
   res.send('Olá, minha imagem!!!!!!!')
});

app.listen(port, () => {
   console.log(`Executand da porta ${port}`)
});

Se o container estiver sendo executado devemos pará-lo com o comando docker stop <id-ou-nome-do-container>. Com o container já parado rodaremos o comando docker build ~/Projetos/primeira-imagem novamente. Rodando o comando docker images percebemos essa nova imagem presente. Então poderemos executar essa nova imagem a partir do seu id.

# Builda a imagem novamente
docker build ~/Projetos/primeira-imagem

# Output
Sending build context to Docker daemon  2.015MB
Step 1/7 : FROM node
 ---> 1016313cda78
Step 2/7 : WORKDIR /src
 ---> Using cache
 ---> 63c180dedba9
Step 3/7 : COPY package*.json /src/
 ---> Using cache
 ---> e5ae9d50761f
Step 4/7 : RUN npm install
 ---> Using cache
 ---> 61fb92c04453
Step 5/7 : COPY . .
 ---> 173faafe74e6
Step 6/7 : EXPOSE 3000
 ---> Running in 22d0425ad914
Removing intermediate container 22d0425ad914
 ---> f6c677d0ba19
Step 7/7 : CMD ["node", "app.js"]
 ---> Running in 9c2658b1d94c
Removing intermediate container 9c2658b1d94c
 ---> 90e62cc44004
Successfully built 90e62cc44004


# Verifica imagens
sudo docker images

# Output com a nova imagem buildada
REPOSITORY        TAG       IMAGE ID       CREATED          SIZE
<none>            <none>    90e62cc44004   30 seconds ago   914MB
<none>            <none>    8a1cebce1ec0   56 minutes ago   914MB

# Para evitar conflito entre containers, devemos também modificar o nome dele ao executá-lo
sudo docker run -d -p 3000:3000 --name node_app2 90e62cc44004

4.7 Camadas das imagens

4.7.1 Cache de camadas

  • As imagens do Docker são divididas em camadas(layers)
  • Cada instrução no Dockerfile representa um layer
  • Quando algo é atualizado apenas os layers depois da linha atualizada são refeitos
  • O resto permanece em cache, tornando o build mais rápido

Exemplo: Se mudar a camada de definição da porta para EXPOSE 3001, então o Docker executará o Dockerfile a partir dela até o final.

4.8 Download de imagens

  • Podemos fazer o download de alguma imagem do hub e deixá-la disponível em nosso ambiente
  • Pra isso utilizamos o comando docker pull <noma-da-imagem>
  • Desta maneira, caso eu use em outro container, a imagem já estará pronta para ser utilizada

Exemplo:

docker pull python

# Output
Using default tag: latest
latest: Pulling from library/python
4c25b3090c26: Pull complete
1acf565088aa: Pull complete
b95c0dd0dc0d: Pull complete
5cf06daf6561: Pull complete
942374d5c114: Pull complete
64c0f10e4cfa: Pull complete
419e258e9e29: Pull complete
3fff52a3f4a6: Pull complete
9476e460b958: Pull complete
Digest: sha256:46a1f3fd3d713fc9151753a6a75da327d1c872152ace16e8b3225458f8754d07
Status: Downloaded newer image for python:latest
docker.io/library/python:latest


docker run -it python

# Output
Python 3.9.7 (default, Aug 31 2021, 18:27:13)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

4.9 Multiplas aplicações, mesmo container

  • Podemos inicializar vários containers com a mesma imagem
  • As aplicações funcionarão em paralelo
  • Para testar isso, podemos determinar uma porta diferente para cada aplicação, e rodá-las no modo detached

Exemplo:

docker images

# Output
REPOSITORY        TAG       IMAGE ID       CREATED          SIZE
node            <none>    8a1cebce1ec0   2 hours ago      914MB
# Criando três containers a partir da mesma imagem de id 8a1cebce1ec0
docker run -d -p 3000:3000 --name node_app1 8a1cebce1ec0
docker run -d -p 4000:3000 --name node_app2 8a1cebce1ec0
docker run -d -p 5000:3000 --name node_app3 8a1cebce1ec0

# Listando containers em execução
docker ps

# Output
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                       NAMES
8d4454dc534f   8a1cebce1ec0   "docker-entrypoint.s…"   6 seconds ago    Up 5 seconds    0.0.0.0:5000->3000/tcp, :::5000->3000/tcp   node_app3
cfd48322c7b9   8a1cebce1ec0   "docker-entrypoint.s…"   14 seconds ago   Up 13 seconds   0.0.0.0:4000->3000/tcp, :::4000->3000/tcp   node_app2
c6a7270f0e8f   8a1cebce1ec0   "docker-entrypoint.s…"   30 seconds ago   Up 29 seconds   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   node_app1

4.10 Alterando o nome e tag de uma imagem

  • Podemos utilizar o comando docker tag <nome-da-imagem> para isso
  • Também podemos modificar a tag, que seria como uma versão da imagem, semelhante ao git
  • Para inserir a tag utilizamos: docker tag <nome-da-imagem>:<tag>

Exemplo:

# Lista as imagens
docker images

# Output
REPOSITORY        TAG       IMAGE ID       CREATED        SIZE
<none>            <none>    90e62cc44004   2 hours ago    914MB

# Atualiza o nome da imagem
docker tag 90e62cc44004 imagem_node

# Lista as imagens
docker images

# Output
REPOSITORY        TAG       IMAGE ID       CREATED        SIZE
imagem_node       latest    90e62cc44004   2 hours ago    914MB

# Atualiza o tag da imagem
docker tag 90e62cc44004 imagem_node:imagem_node_tag

# Lista as imagens
docker images

# Output
REPOSITORY        TAG               IMAGE ID       CREATED        SIZE
imagem_node       imagem_node_tag   90e62cc44004   2 hours ago    914MB

4.11 Iniciando imagem com um nome

  • Podemos nomear a imagem já na sua criação
  • Para isso utilizamos a flag -t
  • Sendo possível inserir o nome e a tag, na sintaxe nome:tag
  • Isso torna o processo de nomeação mais simples

Exemplo:

docker build -t outro_node:outra_tag ~/Projetos/primeira-imagem

4.12 Reiniciando container com iteratividade

  • A flag -i pode ser utilizada com o comando start também
  • Ou seja, não precisamos criar um novo container para utilizá-lo no terminal
  • O comando é o docker start -i <id-ou-nome-do-container>

4.13 Removendo imagens

  • Assim como nos containers, podemos remover imagens com um comando
  • Ele é o docker rmi <id-ou-nome-da-imagem>
  • Imagens que estão sendo utilizadas por um container, apresentarão um erro no terminal
  • Podemos utilizar a flag -f para forçar a remoção Exemplos: docker rmi app_nome docker rmi -f ga97adsgha

4.14 Removendo imagens e containers

  • Com o comando docker system prune podemos remover imagens, containers e networks não utilizados
  • O sistema irá exigir uma confimação para realizar a remoção
  • Todos os containers parados
  • Todas as networks não usadas por pelo menos um container
  • Todas as imagens pendentes (imagens pendentes são camadas que não têm relação com nenhuma imagem tageada)
  • Todo o cache de build pendente

4.15 Removendo container após utilização

  • Um container pode ser automaticamente deletado após sua utilização
  • Para isso, utiliza-se a flag -rm
  • Desta maneira economizamos espaço no computador e deixamos o ambiente mais organizado

Exemplo: docker run -d -p 3000:3000 --name node_app --rm imagem_node

4.16 Copiando arquivos entre containers

  • Para cópia de arquivos entre contaaainers utilizamos o comando docker cp
  • Pode ser utilizado para copiar um arquivo de um diretório para um container ou de um container para um diretório

Exemplo:

# Primeiramente rodamos o container
docker run -d -p 3000:3000 --name node_app imagem_node

# E então copiaremos um arquivo do container para o computador
docker cp node_app:/src/app.js ~/Projetos/dir_para_copia/

4.17 Verificando processamento do container

  • Para verificar dados de execução de um container utilizamos o comando docker top <id-ou-nome-do-container>
  • Desta maneira temos acesso a quando ele foi iniciado, id do processo, descrição do comando CMD e etc

Exemplo:

# Primeiramente rodamos o container
docker run -d -p 3000:3000 --name node_app imagem_node

# Então rodamos o comando top
docker top node_app

# Output
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                25883               25860               0                   12:57               ?                   00:00:00            node app.js

4.18 Inspecionando um container

  • Para verificar diversas informações de um container como: id, data de criação, imagem e muito mais, utilizamos o comando docker inspect <id-ou-nome-do-container>
  • Desta maneira conseguimos entender como o container está configurado

Exemplo:

# Primeiramente rodamos o container
docker run -d -p 3000:3000 --name node_app imagem_node

# Então rodamos o comando inspect
docker inspect node_app

# Output
[
    {
        "Id": "c6a7270f0e8fdbf7679f90ba4d6a1d3796fd382639ec04145485f1ff3469a419",
        "Created": "2021-09-01T15:57:24.208236121Z",
        "Path": "docker-entrypoint.sh",
        "Args": [
            "node",
            "app.js"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 25883,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2021-09-01T15:57:24.532630263Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:8a1cebce1ec01ec1274f217b1e6fe72f998cd639c5f46d109dd5ab773d63e097",
        "ResolvConfPath": "/var/lib/docker/containers/c6a7270f0e8fdbf7679f90ba4d6a1d3796fd382639ec04145485f1ff3469a419/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/c6a7270f0e8fdbf7679f90ba4d6a1d3796fd382639ec04145485f1ff3469a419/hostname",
        "HostsPath": "/var/lib/docker/containers/c6a7270f0e8fdbf7679f90ba4d6a1d3796fd382639ec04145485f1ff3469a419/hosts",
        "LogPath": "/var/lib/docker/containers/c6a7270f0e8fdbf7679f90ba4d6a1d3796fd382639ec04145485f1ff3469a419/c6a7270f0e8fdbf7679f90ba4d6a1d3796fd382639ec04145485f1ff3469a419-json.log",
        "Name": "/node_app1",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "docker-default",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {
                "3000/tcp": [
                    {
                        "HostIp": "",
                        "HostPort": "3000"
                    }
                ]
            },
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "CgroupnsMode": "host",
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/360079c7f44498ba6238f1fe92c744a66d8313804152daf9fcbece255ef0f7c5-init/diff:/var/lib/docker/overlay2/eb784ba0954cf7c6e04ef6c3c85c03704d8320c67c728add50ecd1c582318682/diff:/var/lib/docker/overlay2/292a2485ddc87904e1d4b952ae978d90ebd22ad385f027557c8c3b81e2567cf3/diff:/var/lib/docker/overlay2/134ee1bcc9f1f00dafc09bd019f57fe94be1fdafcbc16ba1355b549d97197f49/diff:/var/lib/docker/overlay2/5f1588bfdd97b2f601b0a5b72f2008897258f1f5021653e695de10d395b181aa/diff:/var/lib/docker/overlay2/e8ea37de439e8ba79e99f009ce816f15692c547706de0fc2965ff7ff4e52acab/diff:/var/lib/docker/overlay2/cea47f1faa4b5523bb0e791e725dcfdf9bd9db93e02b4d1e69269f8deb30233f/diff:/var/lib/docker/overlay2/690e607bd38b1ae749130e0624e7c6bd0714eb8b551061b3f0539c65fc851007/diff:/var/lib/docker/overlay2/edca917eec0ce555aa09c649ded6c4d695b0470f9a6fae35f5706b0b41b83d19/diff:/var/lib/docker/overlay2/308b7abfe7773d584472bf6408ded36bffc1e79758ce2de5b4b82e5d247d13c1/diff:/var/lib/docker/overlay2/1c119afec87fb85da8509113e02dc792a491a8ce33c99dc618d715aa1c3e2d8e/diff:/var/lib/docker/overlay2/bbafad5017fa53277f25a9218e4b8ee0b94c8f37c94f7d04b201c2de7e4ca03b/diff:/var/lib/docker/overlay2/858df878124c5d12755b29a4154e8da001f1ecf6b76d3634b28a62ff9039e642/diff:/var/lib/docker/overlay2/5bf28f17d91364ede1d6b13514f3a9aa65904a823ab47ac86105a16888cbd116/diff",
                "MergedDir": "/var/lib/docker/overlay2/360079c7f44498ba6238f1fe92c744a66d8313804152daf9fcbece255ef0f7c5/merged",
                "UpperDir": "/var/lib/docker/overlay2/360079c7f44498ba6238f1fe92c744a66d8313804152daf9fcbece255ef0f7c5/diff",
                "WorkDir": "/var/lib/docker/overlay2/360079c7f44498ba6238f1fe92c744a66d8313804152daf9fcbece255ef0f7c5/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [],
        "Config": {
            "Hostname": "c6a7270f0e8f",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "3000/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "NODE_VERSION=16.8.0",
                "YARN_VERSION=1.22.5"
            ],
            "Cmd": [
                "node",
                "app.js"
            ],
            "Image": "8a1cebce1ec0",
            "Volumes": null,
            "WorkingDir": "/src",
            "Entrypoint": [
                "docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "7505c8fda380bad76e4a87ae71c0c61b62fdb29b990b12564c7c752ea826b475",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {
                "3000/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "3000"
                    },
                    {
                        "HostIp": "::",
                        "HostPort": "3000"
                    }
                ]
            },
            "SandboxKey": "/var/run/docker/netns/7505c8fda380",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "d5083a9bc641e1ea1df54e829fdd856ee65dcfe01d06b4f5ea1ebad8a7c5b587",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "22a324e2d2fb9d4b374483792cde962eab0cd840150224ceeb11aad1dcbf9c44",
                    "EndpointID": "d5083a9bc641e1ea1df54e829fdd856ee65dcfe01d06b4f5ea1ebad8a7c5b587",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]

4.19 Verificando processamento do Docker

  • Para verificar os processos que estão sendo executados em um container, utilizamos o comando docker stats
  • Desta maneira temos acesso ao andamento do processamento e memória gasta pelo mesmo

Exemplo:

docker stats

# Output
CONTAINER ID   NAME        CPU %     MEM USAGE / LIMIT     MEM %     NET I/O      BLOCK I/O     PIDS
c6a7270f0e8f   node_app   0.00%     14.38MiB / 7.543GiB   0.19%     275kB / 0B   4.16MB / 0B   7

4.20 Informações sobre comandos

  • Todo comando no Dopcker tem acesso a uma flag --help
  • Utilizando desta maneira, podemos ver todas as opções disponíveis nos comandos para relembrar algo ou executar uma tarefa diferente com o mesmo Exemplo:
# Comando start
docker start --help

# Output
Usage:  docker start [OPTIONS] CONTAINER [CONTAINER...]

Start one or more stopped containers

Options:
  -a, --attach               Attach STDOUT/STDERR and forward signals
      --detach-keys string   Override the key sequence for detaching a container
  -i, --interactive          Attach container\'s STDIN
# Comando images
docker images --help

# Output
Usage:  docker images [OPTIONS] [REPOSITORY[:TAG]]

List images

Options:
   -a, --all             Show all images (default hides intermediate images)
      --digests         Show digests
   -f, --filter filter   Filter output based on conditions provided
      --format string   Pretty-print images using a Go template
      --no-trunc        Don't truncate output
   -q, --quiet           Only show image IDs

5. Docker Hub

5.1 Autenticação no Docker Hub pelo terminal

  • Primeiramente precisamos de uma conta no Docker Hub
  • Para se autenticar pelo terminal vamos utilizar o comando docker login, inserindo usuário e senha
  • Então poderemos enviar nossas próprias imagens para o Hub

Exemplo:

docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: ifasanelli
Password: meu-password
WARNING! Your password will be stored unencrypted in /home/italo/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

5.2 Encerrando autenticação no Docker Hub

  • Para remover a conexão entre nossa máquina e o Docker Hub, vamos utilizar o comando docker logout
  • Agora não poderemos mais enviar imagens, pois não estamos mais autenticados

Exemplo:

docker logout

# output
Removing login credentials for https://index.docker.io/v1

5.3 Enviando imagem para o Docker Hub

  • Para enviar uma imagem nossa ao Docker Hub utilizamos o comando docker push <id-ou-nome-da-imagem>
  • Mas antes devemos criar um repositório para a mesma no site do Docker Hub aqui
  • Também é necessário estar autenticado

Exemplo: No site do Docker Hub criamos um repositório com nome de nodeteste

# Devemos buildar uma imagem com o mesmo nome do repositório
docker build -t ifasanelli/nodeteste ~/Projetos/imagem-node

# Então rodamos o comando push
docker push ifasanelli/nodeteste

Podemos verificar aqui o repositório com a imagem recém enviada.

5.4 Enviando atualização de imagem para o Docker Hub

  • Para enviar uma atualização vamos primeiramente fazer o build
  • Trocando a tag da imagem para a verão atualizada
  • Depois vamos fazer um push novamente para o repositório
  • Assim todas as versões estarão disponíveis para serem utilizadas

Exemplo:

# Buildamos a imagem novamente com o código já atualizado
docker build -t ifasanelli/nodeteste:novaversao ~/Projetos/imagem-node

# Então rodamos o comando push
docker push ifasanelli/nodeteste:novaversao

5.5 Baixanado e utilizando a imagem enviada ao Docker Hub

  • Para baixar a imagem podemos utilizar o comando docker pull <nome-da-imagem>
  • Depois criar um novo container com o comando docker run <nome-da-imagem>

6. Volumes

6.1 O que são volumes?

  • Uma forma prática de persistir dados em aplicações e não depender de containers para isso
  • Todo dado criado por um container é salvo nele, quando o container é removido perdemos os dados
  • Então precisamos dos volumes para gerenciar os dados e também conseguir fazer backups de forma mais simples

6.2 Tipos de volumes

  • Anônimos (anonymous volume): Diretórios criados pela flag -v, porém com um nome aleatório
  • Nomeados (named volume): São volumes com nomes, podemos nos referir a estes facilmente e saber para que são utilizaados no nosso ambiente
  • Bind mounts: Uma forma de salvar dados na nossa máquina, sem o gerenciamento do Docker, informamos um diretório para este fim

6.3 O problema da persistência de dados

  • Se criarmos um container com alguma imagem, todos os arquivos que geramos dentro dele serão do container
  • Quando o container for removido, perderemos estes arquivos
  • Por isso precisamos dos volumes

6.4 Criando uma aplicação que persiste dados no container

Em um diretório ~/Projetos/app_persiste_dados, criaremos um pequeno app em PHP com um input onde escreveremos mensagens e estas serão salvas em arquivos .txt em um diretório messages no container. Os arquivos são: Dockerfile

FROM php:8-apache

WORKDIR /var/www/html/

COPY . .

EXPOSE 80

RUN chown -R www-data:www-data /var/www

index.php

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Mensagens</title>
    </head>

    <body>
        <h1>Escreva sua Mensagem</h1>
        <form action="process.php" method="POST">
            <input type="text" name="message" id="message">
            <input type="submit" value="Enviar Mensagem">
        </form>
    </body>
</html>

process.php

<?php

  $message = $_POST["message"];

  $files = scandir("./messages");
  $num_files = count($files) - 2; // . e ..

  $fileName = "msg-{$num_files}.txt";

  $file = fopen("./messages/{$fileName}", "x");

  fwrite($file, $message);

  fclose($file);

  header("Location: index.php");

Devemos buildar e rodar a imagem:

# Comando para fazer o build da imagem
docker build -t phpmessages ~/Projetos/app_persiste_dados

# Comando para rodar o container
docker run -d -p 80:80 --name phpmessages_container phpmessages

Então acessamos localhost e para cada mensgem submetida, poderemos acessá-la em http://localhost:80/messages/msg-numero_da_msg.txt. Exemplo: http://localhost:80/messages/msg-0.txt para a primeira mensagem salva.

Pra verificarmos os problema da persistência de dados na prática, primeiramente vamos parar e reiniciar o container para verificar se os dados ainda persistem nele.

# Comando para parar o container
docker stop phpmessages_container

# Comando para reiniciar o container
docker start phpmessages_container

Então podemos acessar a primeira mensagem salva pelo link http://localhost:80/messages/msg-0.txt e perceber que ainda está lá.

Mas caso o container seja removido e iniciado novamente, seguindo os comandos:

# Comando para parar o container
docker stop phpmessages_container

# Comando para remover o container
docker rm phpmessages_container

# Comando para rodar o container
docker run -d -p 80:80 --name phpmessages_container phpmessages

Uma mensagem de Not Found será mostrada na página http://localhost:80/messages/msg-0.txt

6.5 Volumes anônimos

  • Podemos criar um volume anônimo(anonymous) com o seguinte comando docker run -v /data, onde /data será o diretório que contém o volume anônimo sendo compatível com o WORKDIR
  • E este container estará atrelado ao volume anônimo
  • Com o comando docker volume ls, podemos ver todos os volumes do nosso ambiente

Exemplo: docker run -d -p 80:80 --name phpmessages_container -v /data phpmessages

Podemos verificar se o volume foi criado com o seguinte comando:

docker volume ls

# Output
DRIVER      VOLUME NAME
local       acae7ecb6eabdefb9eaaab9ea7eab53

6.6 Volumes nomeados

  • Podemos criar um volume nomeado(named) utilizando o seguinte comando docker run -v nomedovolume:/data
  • Agora o volume tem nome e pode ser facilmente referenciado
  • Lembrando que o diretório definido para o volume precisa estar compatível com o diretório do WORKDIR

Exemplo: Dockerfile

FROM php:8-apache

WORKDIR /var/www/html/

COPY . .

EXPOSE 80

RUN chown -R www-data:www-data /var/www

Terminal:

# Comando para subir o container com o named volume
docker run -d -p 80:80 --name phpmessages_container -v nomedovolume:/var/www/html/messages phpmessages

Agora acessaremos a aplicação no http://localhost:80/index.php, salvar uma mensagem e acessá-la pelo http://localhost:80/messages/msg-0.txt.

E mesmo que o container seja removido, criaremos outro com acesso ao mesmo volume e poderemos acessar esta mensagem.

# Comando para parar o container
docker stop phpmessages_container

# Comando para remover o container
docker rm phpmessages_container

# Comando para subir o container acessando o volume do container anterior
docker run -d -p 80:80 --name phpmessages_container -v nomedovolume:/var/www/html/messages phpmessages

E já podemos acessar o http://localhost:80/messages/msg-0.txt e verificar a presença da mensagem salva pelo container já removido.

Além disso, também podemos criar um segundo container rodando simultaneamente com o primeiro, com acesso ao mesmo volume. E cada registro de mensagem feito em qualquer um dos containers, será de acesso de ambos.

# Comando para subir outro container acessando o volume do primeiro container
docker run -d -p 81:80 --name outro_phpmessages_container -v nomedovolume:/var/www/html/messages phpmessages

E acessando pelo segundo container http://localhost:81/messages/msg-0.txt verificamos também a presença da mensagem registrada pelo container já removido

Também podemos acessar o http://localhost:81/index.php da segunda aplicação, registrar uma mensagem e verificar a presença dela na primeira aplicação http://localhost:80/messages/msg-1.txt

6.7 Bind mounts

  • Bind mount também é um volume, porém ele fica em um diretório que nós especificamos
  • Então não criamos um volume em si, apontamos um diretório
  • O comando para criar um bind mount é docker run /dir/data:/data
  • Desta maneira o diretório /dir/data no nosso computador, será o volume deste container

Exemplo:

docker run -d -p 80:80 --name phpmessages_container -v ~/Projetos/app_persiste_dados/messages:/var/www/html/messages --rm phpmessages

Lembrando que já temos criado o diretório ~/Projetos/app_persiste_dados/messages no computador.

Agora testaremos a persistência das mensagens no diretório acessando http://localhost:80/index.php e registrando uma mensagem qualquer. Além de verificar sua presença em http://localhost:80/messages/msg-0.txt, também estará presente o arquivo msg-0.txt no diretório especificado no computador com a mensagem registrada.

6.8 O poder extra do bind mount

6.8.1 Atualização do projeto com bind mount

  • Bind mount não serve apenas para volumes
  • Podemos utilizar esta técnica para atualização em tempo real do projeto, sem ter que refazer o build a cada atualização do mesmo

Exemplo:

# Comando para rodar o container com bind mount definido agora pelo diretório raíz da aplicação e WORKDIR e não mais pelos diretórios "message" dentro deles.
# Antes: ~/Projetos/app_persiste_dados/messages:/var/www/html/messages
# Agora: ~/Projetos/app_persiste_dados:/var/www/html/
docker run -d -p 80:80 --name phpmessages_container -v ~/Projetos/app_persiste_dados:/var/www/html/ --rm phpmessages

E para testarmos a atualização do projeto, primeiramente acessaremos http://localhost:80/index.php, registraremos uma mensagem, verificaremos sua presença no arquivo ~/Projetos/app_persiste_dados/messages/msg-0.txt. Agora, no arquivo index.php alteraremos o contéudo da tag <h1>:

index.php

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Mensagens</title>
    </head>

    <body>
        <h1>Escreva sua Mensagem ATUALIZADO PELO BIND MOUNT</h1>
        <form action="process.php" method="POST">
            <input type="text" name="message" id="message">
            <input type="submit" value="Enviar Mensagem">
        </form>
    </body>
</html>

E sem parar o container, atualizamos a página index da aplicação com F5 e verificamos o título da página atualizado sem a necessidade de buildar a imagem novamente.

6.9 Criando volumes manualmente

  • Podemos criar volumes manualmente também utilizando o comando docker volume create <nome-do-volume>
  • Desta maneira temos um named volume criado, podendo ser atrelado a algum container na execução do mesmo

Exemplo:

# Comando para criar um volume
docker volume create volumeteste

# Comando para listar volumes
docker volume ls

# Output
DRIVER      VOLUME NAME
local       volumeteste

# Comando para rodar um container atrelado ao volume recém criado
docker run -d -p 80:80 --name phpmessages_container -v volumeteste:/var/www/html/ --rm phpmessages

6.10 Listando todos os volumes

  • Com o comando docker volume ls listamos todos os volumes, desta maneira temos acesso aos anonymous e aos named volumes
  • Não serão listados bind mounts
  • É útil para saber quais volumes estão criados no nosso ambiente

Exemplo:

docker volume ls

# Output
DRIVER      VOLUME NAME
local       volumeteste

6.11 Inspecionando volumes

  • Podemos verificar os detalhes de um volume em específico com o comando docker volume inspect <nome-do-volume>
  • Desta forma temos acesso ao local em que o volume guarda dados, nome, escopo e etc
  • O Docker salva os dados dos volumes em algum diretório do nosso computador, desta forma podemos saber qual é

Exemplo:

# Comando para listar volumes
docker volume ls

# Output
DRIVER      VOLUME NAME
local       volumeteste

# Comando para inspecionar o volume
docker volume inspect volumeteste

# Output
[
    {
        "CreatedAt": "2021-09-02T15:23:34-03:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/volumeteste/_data",
        "Name": "volumeteste",
        "Options": {},
        "Scope": "local"
    }
]

6.12 Removendo volumes

  • Podemos também remover um volume existente de forma fácil, para isso utilizamos o comando docker volume rm <nome-do-volume>
  • Observe que os dados serão removidos também, tome cuidado com este comando

Exemplo:

# Comando para listar volumes
docker volume ls

# Output
DRIVER      VOLUME NAME
local       volumeteste

# Comando para remover o volume
docker volume rm volumeteste

# Comando para listar volumes
docker volume ls

# Output
DRIVER      VOLUME NAME

6.13 Removendo volumes em massa

  • Podemos remover todos os volumes que não estão sendo utilizados com apenas um comando docker volume prune
  • Ele é semlhante ao prune que remove as imagens

Exemplo:

# Comando para listar volumes
docker volume ls

# Output
DRIVER      VOLUME NAME
local       volumeteste
local       volumeteste1
local       volumeteste2

# Comando para remover o volume
docker volume prune

# Comando para listar volumes
docker volume ls

# Output
DRIVER      VOLUME NAME

6.14 Volume apenas de leitura

  • Podemos criar um volume que tem apenas permissão de leitura, isso é útil em algumas aplicações
  • Para realizar esta configuração devemos utilizar o comando docker run -v volume:/data:ro, onde :ro significa read only

Exemplo:

docker run -d -p 80:80 --name phpmessages_container -v volumeleitura:/var/www/html:ro --rm phpmessages

Se tentarmos persistir dados via http://localhost:80/index.php, uma mensagem de erro aparecerá notificando a falta de permissão de escrita.

7. Networks

7.1 O que são Networks no Docker?

  • Uma forma de gerenciar a conexão do Docker com outras plataformas ou até mesmo entre containers
  • As redes ou networks são criadas separadas do container, como os volumes
  • Além disso existem alguns drivers de rede, que veremos em seguida
  • Uma rede deixa muito simples a comunicação entre containers

7.2 Tipo de conexão

  • Os containers costumam ter três principais tipos de comunicação
  • Externa: conexão com uma API de um servidor remoto
  • Com o host: comunicação com a máquina que está executando o Docker
  • Entre containers: comunicação que utiliza o driver bridge e permite a comunicação entre dois ou mais containers

7.3 Tipo de rede (drivers)

  • bridge: o mais comum e default do Docker, utilizado quando containers precisam se conctar (na maioria das vezes optamos por este dirver)
  • host: permite a conexão entre um container a uma máquina que está hosteando o Docker
  • macvlan: permite a conexão a um container por um MAC address
  • none: remove todas as conexões de rede de um container
  • plugins: permite extensões de terceiros para criar outras redes

7.4 Listando redes

  • Podemos verificar todas as redes do nosso ambiente com o comando docker network ls
  • Algumas redes já estão criadas, estas fazem parte da configuração inicial do Docker

Exemplo:

# Comando para listar as redes
docker network ls

# Output
NETWORK ID     NAME      DRIVER    SCOPE
33f340c389be   bridge    bridge    local
c22aff5ea72f   host      host      local
7d3dd836f2cc   none      null      local

7.5 Criando rede

  • Para criar uma rede vamos utilizar o comando docker network create <nome-da-rede>
  • Esta rede será do tipo bridge, que é o mais utilizado

Exemplo:

# Comando para criar uma rede default
docker network create minharedeteste

# Output
99e3d08e78f3ea6960783afb593e726255d96cdeb79b43991544f4b483023a71

# Comando para listar redes
docker network ls

# Output
NETWORK ID     NAME             DRIVER    SCOPE
33f340c389be   bridge           bridge    local
c22aff5ea72f   host             host      local
99e3d08e78f3   minharedeteste   bridge    local
7d3dd836f2cc   none             null      local

# Comando para criar uma rede com driver especificado
docker network create -d macvlan minharedemacvlan

# Output
NETWORK ID     NAME               DRIVER    SCOPE
33f340c389be   bridge             bridge    local
c22aff5ea72f   host               host      local
0dcfb559e6ea   minharedemacvlan   macvlan   local
99e3d08e78f3   minharedeteste     bridge    local
7d3dd836f2cc   none               null      local

7.6 Removendo redes

  • Podemos remover redes de forma simples com o comando docker network rm <nome-da-rede>, assim a rede não estará mais disponível para utilizarmos
  • Devemos tomar cuidado com containers já conectados

Exemplo:

# Comando para listar redes
docker network ls

# Output
NETWORK ID     NAME               DRIVER    SCOPE
33f340c389be   bridge             bridge    local
c22aff5ea72f   host               host      local
0dcfb559e6ea   minharedemacvlan   macvlan   local
99e3d08e78f3   minharedeteste     bridge    local
7d3dd836f2cc   none               null      local

# Comando para criar uma rede com driver especificado
docker network rm minharedemacvlan

# Comando para listar redes
docker network ls

# Output
NETWORK ID     NAME             DRIVER    SCOPE
33f340c389be   bridge           bridge    local
c22aff5ea72f   host             host      local
99e3d08e78f3   minharedeteste   bridge    local
7d3dd836f2cc   none             null      local

7.7 Removendo redes não utilizadas

  • Podemos remover redes de forma simple também com o comando docker network prune, assim todas as redes não utilizadas no momento serão removidas
  • As redes criadas por padrão do Docker não serão removidas
  • Receberemos uma mensagem de confirmação do Docker antes da ação ser executada

Exemplo:

# Comando para listar redes
docker network ls

# Output
NETWORK ID     NAME               DRIVER    SCOPE
33f340c389be   bridge             bridge    local
c22aff5ea72f   host               host      local
0dcfb559e6ea   minharedemacvlan   macvlan   local
99e3d08e78f3   minharedeteste     bridge    local
7d3dd836f2cc   none               null      local

# Comando para remover redes não utilizadas
docker network prune

# Output
WARNING! This will remove all custom networks not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Networks:
minharedemacvlan
minharedeteste

# Comando para listar redes
docker network ls

# Output
NETWORK ID     NAME             DRIVER    SCOPE
33f340c389be   bridge           bridge    local
c22aff5ea72f   host             host      local
7d3dd836f2cc   none             null      local

7.8 Conexão externa

  • Os containers podem se conectar livremente ao mundo externo
  • Um caso seria uma API de código aberto
  • Podemos acessá-la livremente e utilizar seus dados

Para exemplificar uma conexão externa criaremos um diretório para o projeto:

# Cria e muda para o diretório redeexterna
mkdir ~/Projetos/redeexterna && cd ~/Projetos/redeexterna

Neste diretório criamos dois arquivos: Dockerfile

FROM python:3

RUN apt-get update -y && \
    apt-get install -y python-pip python-dev

WORKDIR /app

RUN pip install Flask
RUN pip install requests

COPY . .

EXPOSE 5000

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

app.py

import flask
from flask import request, json, jsonify
import requests

app = flask.Flask(__name__)
app.config["DEBUG"] = True

@app.route("/", methods=["GET"])
def index():
  data = requests.get('https://randomuser.me/api')
  return data.json()

if __name__ == "__main__":
  app.run(host="0.0.0.0", debug=True, port="5000")

No terminal:

# Comando para buildar a imagem
docker build -t flaskexterno ~/Projetos/redeexterna

# Comando para rodar o container
docker run -d -p 5000:5000 --name flaskexternacontainer --rm flaskexterno

No Postman faremos uma requisição GET para http://localhost:5000/ e obteremos uma resposta parecida com:

{
    "results": [
        {
            "gender": "male",
            "name": {
                "title": "Mr",
                "first": "Alexander",
                "last": "Wright"
            },
            "location": {
                "street": {
                    "number": 4077,
                    "name": "White Oak Dr"
                },
                "city": "Denver",
                "state": "Virginia",
                "country": "United States",
                "postcode": 88784,
                "coordinates": {
                    "latitude": "84.9187",
                    "longitude": "-98.5076"
                },
                "timezone": {
                    "offset": "0:00",
                    "description": "Western Europe Time, London, Lisbon, Casablanca"
                }
            },
            "email": "alexander.wright@example.com",
            "login": {
                "uuid": "0f76f3ee-a8f5-43ed-8356-ae4dddf4324d",
                "username": "greengorilla706",
                "password": "chelsea1",
                "salt": "QI0xp5ZV",
                "md5": "388781e5fa6c95476d614d2a6703be99",
                "sha1": "0504d3f9b1cea9514d3a20cae78beaf3c70641f2",
                "sha256": "d8a28e737bf6fc701a49f0dfbbd4158603bc5c6ef0daad8e41d56e7ab54dd4ae"
            },
            "dob": {
                "date": "1993-05-21T01:12:20.330Z",
                "age": 28
            },
            "registered": {
                "date": "2007-08-18T13:36:38.335Z",
                "age": 14
            },
            "phone": "(889)-221-3883",
            "cell": "(262)-126-5556",
            "id": {
                "name": "SSN",
                "value": "777-44-2926"
            },
            "picture": {
                "large": "https://randomuser.me/api/portraits/men/86.jpg",
                "medium": "https://randomuser.me/api/portraits/med/men/86.jpg",
                "thumbnail": "https://randomuser.me/api/portraits/thumb/men/86.jpg"
            },
            "nat": "US"
        }
    ],
    "info": {
        "seed": "5b539454c72a2240",
        "results": 1,
        "page": 1,
        "version": "1.3"
    }
}

7.9 Conexão com o host

  • Podemos também conectar um container com o host do Docker
  • Host é a máquina que está executando o Docker
  • Como ip de host utilizamod host.docker.internal

Para esse exemplo é necessário ter instalado o MySQL, criar um banco de dados com nome flaskhost, com uma tabela users, com as colunas id(INT) e name(VARCHAR)

Para exemplificar uma conexão com o host criaremos um diretório para o projeto:

# Cria e muda para o diretório redehost
mkdir ~/Projetos/redehost && cd ~/Projetos/redehost

Neste diretório criamos dois arquivos: Dockerfile

FROM python:3

RUN apt-get update -y && \
  apt-get install -y python-pip python-dev

WORKDIR /app

RUN pip install Flask requests flask_mysqldb

COPY . .

EXPOSE 5000

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

app.py

import flask
from flask import request, json, jsonify
import requests
import flask_mysqldb
from flask_mysqldb import MySQL

app = flask.Flask(__name__)
app.config["DEBUG"] = True

app.config['MYSQL_HOST'] = 'host.docker.internal'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = ''
app.config['MYSQL_DB'] = 'flaskhost'

mysql = MySQL(app)

@app.route("/", methods=["GET"])
def index():
  data = requests.get('https://randomuser.me/api')
  return data.json()

@app.route("/inserthost", methods=['POST'])
def inserthost():
  data = requests.get('https://randomuser.me/api').json()
  username = data['results'][0]['name']['first']

  cur = mysql.connection.cursor()
  cur.execute("""INSERT INTO users(name) VALUES(%s)""", (username,))
  mysql.connection.commit()
  cur.close()

  return username

if __name__ == "__main__":
  app.run(host="0.0.0.0", debug=True, port="5000")

No terminal:

# Comando para buildar a imagem
docker build -t flaskhost ~/Projetos/redehost

# Comando para rodar o container
docker run -d -p 5000:5000 --name flaskhostcontainer --rm flaskhost

Agora podemos fazer uma requisição POST no Postman para http://localhost:5000/inserhost e obteremos uma resposta parecida com:

Julia

No MySQL Workbench podemos verificar a presença dos dados com a query SELECT * FROM flaskhost.users;

7.10 Conexão entre containers

  • Podemos também estabelecer uma conexão entre containers
  • Duas imagens distintas rodando em containers separados que precisam se conectar para inserir um dado no banco, por exemplo
  • Vamos precisar de uma rede bridge, para fazer esta conexão
  • Para isso utilizamos o comando --network <nome-da-rede>
  • Agora nosso container de flask vai inserir dados em um MySQL que roda pelo Docker também

Para exemplificar uma conexão entre dois containers, criaremos um diretório ~/Projetos/conn_containers e, dentro dele, mais dois diretórios: flask e mysql.

Dentro de ~/Projetos/conn_containers/flask:

app.py

import flask
from flask import request, json, jsonify
import requests
import flask_mysqldb
from flask_mysqldb import MySQL

app = flask.Flask(__name__)
app.config["DEBUG"] = True

app.config['MYSQL_HOST'] = 'mysql_api_container'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = ''
app.config['MYSQL_DB'] = 'flaskdocker'

mysql = MySQL(app)

@app.route("/", methods=["GET"])
def index():
  data = requests.get('https://randomuser.me/api')
  return data.json()

@app.route("/inserthost", methods=['POST'])
def inserthost():
  data = requests.get('https://randomuser.me/api').json()
  username = data['results'][0]['name']['first']

  cur = mysql.connection.cursor()
  cur.execute("""INSERT INTO users(name) VALUES(%s)""", (username,))
  mysql.connection.commit()
  cur.close()

  return username

if __name__ == "__main__":
  app.run(host="0.0.0.0", debug=True, port="5000")

Dockerfile

FROM python:3

RUN apt-get update -y && \
  apt-get install -y python-pip python-dev

WORKDIR /app

RUN pip install Flask requests flask_mysqldb

COPY . .

EXPOSE 5000

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

E dentro de ~/Projetos/conn_containers/mysql:

Dockerfile

FROM mysql:5.7

COPY schema.sql /docker-entrypoint-initdb.d/

EXPOSE 3306

VOLUME ["/backup/"]

schema.sql

CREATE DATABASE flaskdocker;
USE flaskdocker;

CREATE TABLE `flaskdocker`.`users` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(255),
  PRIMARY KEY (ID));

No terminal:

# Comando para buildar a imagem do mysql
docker build -t mysqlapinetwork ~/Projetos/conn_containers/mysql

# Comando para criar uma conexão bridge
docker network create flasknetwork

# Comando para rodar o container do mysql conectando a rede "flasknetwork"
docker run -d -p 3306:3306 --name mysql_api_container --rm --network flasknetwork -e MYSQL_ALLOW_EMPTY_PASSWORD=True mysqlapinetwork
# Obs.: O comando "-e MYSQL_ALLOW_EMPTY_PASSWORD=True" cria uma variável de ambiente que retira a necessidade de senha para o banco de dados.

# Comando para buildar a imagem do flask
docker build -t flaskapinetwork ~/Projetos/conn_containers/flask

# Comando para rodar o container do flask conectando a rede "flasknetwork"
docker run -d -p 5000:5000 --name flaskapicontainer --rm --network flasknetwork flaskapinetwork

Testaremos primeiramente o container do flask com uma chamada GET em http://localhost:5000 recebendo como resposta um JSON com dados de um usuário utilizando o Postman.

Agora testaremos a inserção de dados no container do mysql através do container do flask. Faremos uma chamada POST em http://localhost:5000/inserthost e para verificarmos se esses dados foram persistidos corretamente, podemos acessar o banco de dados flaskdocker pelo MySQL Workbench na tabela users, uma vez que externalizamos a porta 3306

7.11 Conectando um container a uma rede

  • Podemos conectar um container a uma rede, para isso utilizamos o comando: docker network connect <nome-da-rede> <id-ou-nome-do-container>

No terminal:

# Comando para rodar o container do flask'
docker run -d -p 5000:5000 --name flaskapicontainer --rm flaskapinetwork

# Comando para conectar o container do flask na rede flasknetwork
docker network connect flasknetwork flaskapicontainer

Para verificarmos se a conexão com a rede foi estabelecida, podemos rodar o comando docker inspect flaskapicontainer e confirmar a presença do atributo "flasknetwork".

7.12 Desconectando um container de uma rede

  • Podemos desconectar um container a uma rede utilizando o comando docker network disconnect <nome-da-rede> <id-ou-nome-do-container>

Usaremos o container do exemplo anterior para exemplificar a desconexão do container Exemplo:

docker network disconnect flasknetwork flaskapicontainer

E rodando o comando docker inspect flaskapicontainer, não encontraremos mais o atributo "flasknetwork".

7.13 Inspecionando redes

  • Podemos analisar os detalhes de uma rede com o comando docker network inspect <nome-da-rede>
  • Receberemos informações como: data de criação, driver, nome e etc.

Exemplo:

docker network inspect flasknetwork

# Output
[
    {
        "Name": "flasknetwork",
        "Id": "22f5fca63d7fbf8b721e363ad57bc927b3fa28629a1538170e4dcd7b81f7b477",
        "Created": "2021-09-09T00:34:56.222025796-03:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

8. YAML

8.1 O que é YAML?

  • Uma linguagem de serialização, seu nome é YAML ain't Markup Language (YAML não é uma linguagem de marcação)
  • Usada geralmente para arquivos de configuração, inclusive no Docker, para configuraar o Docker Compose
  • É de fácil leitura para nós humanos
  • A extensão dos arquivos é .yml ou .yaml

8.2 Criando um arquivo YAML

  • O arquivo .yaml geralmente possui chaves e valores, que é de onde vamos retirar as configurações do nosso sistema
  • Para definir uma chave apenas inserimos o nome dela, em seguida colocamos dois pontos e depois o valor

Para exemplificar o funcionamento do YAML, criaremos um script em python que imprimirá as chaves e valores do arquivo. E para isso precisamos ter o python instalado na máquina e o pacote pyyaml com o comando pip3 install pyyaml.

app.py

import yaml

if __name__ == '__main__':
  stream = open("test.yaml", "r")
  dictionary = yaml.safe_load(stream)

  for key, value in dictionary.items():
    print(key + " : " + str(value))

test.yaml

nome: "Mariana"
idade: 24

No terminal:

# Comando para rodar o script
python app.py

# Output
idade : 24
nome : Mariana

8.3 Espaçamento e identação

  • O O fim de uma linha indica o fim de uma instrução, não há ponto e vírgula
  • A identação deve conter um ou mais espaços, e não devemos utilizar tab
  • Cada identação define um bloco
  • O espaço é obrigatório após a declaração da chave

test.yaml

objeto:
  versao: 2
  arquivo: "teste.txt"

8.4 Comentários no YAML

  • Podemos escrever comentários em YAML também, utilizando o símbolo #
  • O processador do YAML ignora comentários
  • Eles são úteis para escrever como o arquivo funciona e/ou foi configurado

test.yaml

# Dados pessoais
idade: 24
nome: Mariana

# Objeto de configuração
objeto:
  versao: 2
  arquivo: "teste.txt"

No terminal:

# Comando para rodar o script
python app.py

# Output
idade : 24
nome : Mariana
objeto : {'versao': 2, 'arquivo': 'teste.txt'}

8.5 Dados numéricos

  • Em YAML podemos escrever dados numéricos com:
    • Inteiros: 12
      • Ex.: versao: 12
    • Floats: 15.8
      • Ex.: versao: 15;8

8.6 Strings no YAML

  • Em YAML podemos escrever textos de duas formas:
    • Sem aspas: este é um texto válido
      • Ex.: nome: Mariana Flor
    • Com aspas: "este também"
      • Ex.: nome: "Mariana Flor"

8.7 Valores nulos

  • Em YAML podemos definir um dado como nulo de duas formas: ~ ou null
    • Ex.: id: ~
    • Ex.: id: null
  • Os dois vão resultar em None, após a interpretação

8.8 Booleanos

  • Podemos inserir booleanos em YAML das seguintes formas:
    • Para True: True ou On
      • Ex.: has_id: True
      • Ex.: has_id: On
    • Para False: False e Off
      • Ex.: has_id: False
      • Ex.: has_id: Off

8.9 Arrays

  • Os arrays, tipos de dados para listas, posssuem duas sintaxes:
    • items: [1, "teste", 3.14, outro teste, True]
    • items:
        - 1
        - "teste"
        - 3.14
        - outro teste
        - True
      

8.10 Dicionários

  • Dicionários, tipo de dados para objetos ou listas com chaves e valores, podem ser escritos da seguintes formas:
    • obj: {a: 1, b: "teste", c: 3.14, d: outro teste, e: {x: True, y: False}}
    • obj:
        a: 1
        b: "teste"
        c: 3.14
        d: outro teste
        e: True
        f:
          x: ~
          y: null
        g: Off
      

9. Gerenciando múltiplos containers com Docker Compose

9.1 O que é o Docker Compose

  • O Docker Compose é uma ferramenta para rodar múltiplos containers
  • Teremos apenas um arquivo de configuração, que orquestra totalmente esta situação
  • É uma forma de rodar múltiplos builds e runs com um comando
  • Em projetos maiores é essencial o uso do Compose

9.2 Instalando Docker Compose no Linux

9.3 Criando nosso primeiro arquivo do Compose

  • Primeiramente vamos criar um arquivo chamado docker-compose.yml na raiz do projeto
  • Este arquivo vai coordenar os containers e imagens, e possui algumas chaves muito utilizadas:
    • version: versão do Compose
    • services: Containers/serviços que vão rodar nessa aplicação
    • volumes: Possível adição de volumes

Primeiramente, criaremos um diretório para o projeto:

mkdir ~/Projetos/compose/criacao_compose && cd ~/Projetos/compose/criacao_compose

Então, criamos o arquivo do compose:

touch docker-compose.yaml

docker-compose.yaml

version: '3.3'

services:
  db: # Container de MySQL
    image: mysql:5.7 # FROM mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: wordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: italo
      MYSQL_PASSWORD: secret

  wordpress:
    depends_on:
      -db
    image: wordpress:latest
    ports:
      "8000:80"
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: italo
      WORDPRESS_DB_PASSWORD: secret
      WORDPRESS_DB_NAME: wordpress

volumes:
  db_data: {}

9.4 Rodando o Compose

  • Para rodar nossa estrutura em Compose, vamos utilizar o comando docker-compose up
  • Isso fará com que as instruções no arquivo sejam executadas
  • Da mesma forma que realizamos os builds e também os runs
  • Podemos parar o Compose com o comando ctrl+c no terminal

No terminal:

docker-compose up

Receberemos diversas informações como:

  • Criação da rede
  • Criação do volume
  • Criação do container do mysql
  • Criação do container do wordpress
  • Logs das operações

E ao acessar o localhost na porta 8000, uma instalação do wordpress será inicializada.

Então verificaremos os containers rodando:

# Verifica os containers em execução
docker ps

# Output
CONTAINER ID   IMAGE              COMMAND                  CREATED          STATUS          PORTS                                       NAMES
09e2afb2d5f2   wordpress:latest   "docker-entrypoint.s…"   18 seconds ago   Up 18 seconds   0.0.0.0:8000->80/tcp, :::8000->80/tcp       1_criacao_compose_wordpress_1
2ac1e2b06660   mysql:5.7          "docker-entrypoint.s…"   18 seconds ago   Up 18 seconds   0.0.0.0:3306/tcp, :::3000->3306/tcp         1_criacao_compose_db_1

9.5 Rodando o Compose em background

  • O Compose também pode ser executado em modo detached para isso utilizaremos a flag -d no comando
  • E então os containers estarão rodando em background e poderemos ver suas execuções com docker ps

No terminal:

docker-compose up -d

9.6 Parando o Compose

  • Podemos parar o Compose que roda em background com o comando docker-compose down
  • Desta maneira o serviço para e teremos os containers adicionados listados no docker ps -a

No terminal:

docker-compose down

9.7 Variáveis de ambiente no Compose

  • Podemos definir variáveis de ambiente para o Docker Compose
  • Para isso vamos definir um arquivo base em env_file
  • As variáveis podem ser chamadas pela sintaxe: ${VARIAVEL}
  • Esta técnica é útil quando o dado a ser inserido é sensível e/ou não pode ser compartilhado, como uma senha

Para esse item, criaremos um diretório variaveis:

mkdir ~/Projetos/compose/variaveis && cd ~/Projetos/compose/variaveis

Dentro do diretório variaveis, criaremos outro chamado config com os arquivos db.env e wp.env:

mkdir config && cd config

touch db.env
touch wp.env

db.env

MYSQL_ROOT_PASSWORD=wordpress
MYSQL_DATABASE=wordpress
MYSQL_USER=italo
MYSQL_PASSWORD=secret

wp.env

WORDPRESS_DB_HOST=db:3306
WORDPRESS_DB_NAME=italo
WORDPRESS_DB_PASSWORD=secret
WORDPRESS_DB_NAME=wordpress

Na raiz do projeto(~/Projetos/compose/variaveis) teremos um arquivo de Compose parecido com o das seções anteriores, mas com as variáveis de ambiente removidas e no lugar delas o caminho para o db.env:

docker-compose.yaml

version: '3.3'

services:
  db: # Container de MySQL
    image: mysql:5.7 # FROM mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    env_file:
      - ./config/db.env

  wordpress:
    depends_on:
      -db
    image: wordpress:latest
    ports:
      "8000:80"
    restart: always
    env_file:
      - ./config/wp.env

volumes:
  db_data: {}

9.8 Redes no Compose

  • O Compose cria uma rede básica Bridge entre os containers da aplicação, porém podemos isolar as redes com a chave networks
  • Desta maneira, podemos conectar apenas os containers que optarmos
  • Podemos também definir drivers diferentes

Para esse item, criaremos um diretório networks:

mkdir ~/Projetos/compose/networks && cd ~/Projetos/compose/networks

Na raiz do projeto(~/Projetos/compose/networks): docker-compose.yaml

version: '3.3'

services:
  db: # Container de MySQL
    image: mysql:5.7 # FROM mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    env_file:
      - ./config/db.env
    networks:
      - backend

  wordpress:
    depends_on:
      -db
    image: wordpress:latest
    ports:
      "8000:80"
    restart: always
    env_file:
      - ./config/wp.env
    networks:
      - backend

volumes:
  db_data: {}

networks:
  backend:
    driver: bridge

9.9 Criando o Compose de um projeto

  • Agora vamos inserir o nosso projeto da última seção no Compose para verificar na prática como fazer a transferência de Dockerfiles para Docker Compose

Para esse item, criaremos um diretório projeto:

mkdir ~/Projetos/compose/projeto && cd ~/Projetos/compose/projeto

No diretório raíz teremos outros três diretórios flask mysql e config. No terminal:

mkdir flask
touch ./flask/app.py
touch ./flask/Dockerfile

mkdir mysql
touch ./mysql/schema.sql
touch ./mysql/Dockerfile

mkdir config
touch ./config/db.env

/flask/app.py

import flask
from flask import request, json, jsonify
import requests
import flask_mysqldb
from flask_mysqldb import MySQL

app = flask.Flask(__name__)
app.config["DEBUG"] = True

app.config['MYSQL_HOST'] = 'db'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = ''
app.config['MYSQL_DB'] = 'flaskdocker'

mysql = MySQL(app)

@app.route("/", methods=["GET"])
def index():
  data = requests.get('https://randomuser.me/api')
  return data.json()

@app.route("/inserthost", methods=['POST'])
def inserthost():
  data = requests.get('https://randomuser.me/api').json()
  username = data['results'][0]['name']['first']

  cur = mysql.connection.cursor()
  cur.execute("""INSERT INTO users(name) VALUES(%s)""", (username,))
  mysql.connection.commit()
  cur.close()

  return username

if __name__ == "__main__":
  app.run(host="0.0.0.0", debug=True, port="5000")

/flask/Dockerfile

FROM python:3

RUN apt-get update -y && \
  apt-get install -y python-pip python-dev

WORKDIR /app

RUN pip install Flask requests flask_mysqldb

COPY . .

EXPOSE 5000

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

/mysql/schema.sql

CREATE DATABASE flaskdocker;
USE flaskdocker;

CREATE TABLE `flaskdocker`.`users` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(255),
  PRIMARY KEY (ID));

/mysql/Dockerfile

FROM mysql:5.7

COPY schema.sql /docker-entrypoint-initdb.d/

EXPOSE 3306

/config/db.env

MYSQL_ALLAW_EMPTY_PASSWORD=True

Ainda no diretório raíz do projeto criaremos o arquivo do Compose. No terminal:

touch docker-compose.yaml

docker-compose.yaml

version: '3.3'

services:
  db:
    image: mysqlcompose
    restart: always
    env_file:
      - ./config/db.env
    ports:
      - "3306:3306"
    networks:
      - dockercompose

  backend:
    depends_on:
      - db
    image: flaskcompose
    ports:
      - "5000:5000"
    restart: always
    networks:
      - dockercompose

networks:
  dockercompose:

Agora precisamos buildar a imagem do flask. No terminal:

cd ~/Projetos/compose/projeto/flask

docker build -t flaskcompose .

E também buildar a imagem do mysql. No terminal:

cd ~/Projetos/compose/projeto/mysql

docker build -t mysqlcompose .

E finalmente podemos rodar a aplicação. No terminal:

docker-compose up -d

9.10 Build de imagens no Compose

  • Podemos gerar o build durante o Compose também, isso vai eliminar o processo de gerar build da imagem a cada atualização

Para esse item, criaremos um diretório build_compose:

mkdir ~/Projetos/compose/build_compose && cd ~/Projetos/compose/build_compose

No diretório raíz teremos outros três diretórios flask mysql e config. No terminal:

mkdir flask
touch ./flask/app.py
touch ./flask/Dockerfile

mkdir mysql
touch ./mysql/schema.sql
touch ./mysql/Dockerfile

mkdir config
touch ./config/db.env

/flask/app.py

import flask
from flask import request, json, jsonify
import requests
import flask_mysqldb
from flask_mysqldb import MySQL

app = flask.Flask(__name__)
app.config["DEBUG"] = True

app.config['MYSQL_HOST'] = 'db'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = ''
app.config['MYSQL_DB'] = 'flaskdocker'

mysql = MySQL(app)

@app.route("/", methods=["GET"])
def index():
  data = requests.get('https://randomuser.me/api')
  return data.json()

@app.route("/inserthost", methods=['POST'])
def inserthost():
  data = requests.get('https://randomuser.me/api').json()
  username = data['results'][0]['name']['first']

  cur = mysql.connection.cursor()
  cur.execute("""INSERT INTO users(name) VALUES(%s)""", (username,))
  mysql.connection.commit()
  cur.close()

  return "Usuário inserido: " + username

if __name__ == "__main__":
  app.run(host="0.0.0.0", debug=True, port="5000")

/flask/Dockerfile

FROM python:3

RUN apt-get update -y && \
  apt-get install -y python-pip python-dev

WORKDIR /app

RUN pip install Flask requests flask_mysqldb

COPY . .

EXPOSE 5000

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

/mysql/schema.sql

CREATE DATABASE flaskdocker;
USE flaskdocker;

CREATE TABLE `flaskdocker`.`users` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(255),
  PRIMARY KEY (ID));

/mysql/Dockerfile

FROM mysql:5.7

COPY schema.sql /docker-entrypoint-initdb.d/

EXPOSE 3306

/config/db.env

MYSQL_ALLAW_EMPTY_PASSWORD=True

Ainda no diretório raíz do projeto criaremos o arquivo do Compose. No terminal:

touch docker-compose.yaml

docker-compose.yaml

version: '3.3'

services:
  db:
    build: ./mysql/
    restart: always
    env_file:
      - ./config/db.env
    ports:
      - "3306:3306"
    networks:
      - dockercompose

  backend:
    depends_on:
      - db
    build: ./flask/
    ports:
      - "5000:5000"
    restart: always
    networks:
      - dockercompose

networks:
  dockercompose:

Agora sem a necessidade de buildarmos as imagens, subiremos a aplicação pelo Compose. No terminal:

docker-compose up -d

9.11 Bind Mount no Compose

  • O volume de Bind Mount garante atualização em tempo real dos artquivos do container
  • Podemos configurar nosso projeto de Compose para utilizar esta funcionalidade também

Para esse item, criaremos um diretório bind_mount_compose:

mkdir ~/Projetos/compose/bind_mount_compose && cd ~/Projetos/compose/bind_mount_compose

No diretório raíz teremos outros três diretórios flask mysql e config. No terminal:

mkdir flask
touch ./flask/app.py
touch ./flask/Dockerfile

mkdir mysql
touch ./mysql/schema.sql
touch ./mysql/Dockerfile

mkdir config
touch ./config/db.env

/flask/app.py

import flask
from flask import request, json, jsonify
import requests
import flask_mysqldb
from flask_mysqldb import MySQL

app = flask.Flask(__name__)
app.config["DEBUG"] = True

app.config['MYSQL_HOST'] = 'db'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = ''
app.config['MYSQL_DB'] = 'flaskdocker'

mysql = MySQL(app)

@app.route("/", methods=["GET"])
def index():
  data = requests.get('https://randomuser.me/api')
  return data.json()

@app.route("/inserthost", methods=['POST'])
def inserthost():
  data = requests.get('https://randomuser.me/api').json()
  username = data['results'][0]['name']['first']

  cur = mysql.connection.cursor()
  cur.execute("""INSERT INTO users(name) VALUES(%s)""", (username,))
  mysql.connection.commit()
  cur.close()

  return "Usuário inserido: " + username

if __name__ == "__main__":
  app.run(host="0.0.0.0", debug=True, port="5000")

/flask/Dockerfile

FROM python:3

RUN apt-get update -y && \
  apt-get install -y python-pip python-dev

WORKDIR /app

RUN pip install Flask requests flask_mysqldb

COPY . .

EXPOSE 5000

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

/mysql/schema.sql

CREATE DATABASE flaskdocker;
USE flaskdocker;

CREATE TABLE `flaskdocker`.`users` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(255),
  PRIMARY KEY (ID));

/mysql/Dockerfile

FROM mysql:5.7

COPY schema.sql /docker-entrypoint-initdb.d/

EXPOSE 3306

/config/db.env

MYSQL_ALLAW_EMPTY_PASSWORD=True

Ainda no diretório raíz do projeto criaremos o arquivo do Compose. No terminal:

touch docker-compose.yaml

docker-compose.yaml

version: '3.3'

services:
  db:
    build: ./mysql/
    restart: always
    env_file:
      - ./config/db.env
    ports:
      - "3306:3306"
    networks:
      - dockercompose

  backend:
    depends_on:
      - db
    build: ./flask/
    ports:
      - "5000:5000"
    restart: always
    volumes:
      - ~/Projetos/compose/bind_mount_compose/flask/app
    networks:
      - dockercompose

networks:
  dockercompose:

Subiremos a aplicação pelo Compose. No terminal:

cd ~/Projetos/compose/bind_mount_compose/

docker-compose up -d

9.12 Verificando serviços no Compose

  • Podemos fazer a verificação do compose com o comando docker-compose ps
  • Receberemos um resumo dos serviços que subiram ao rodar o compose
  • Desta maneira, podemos avaliar rapidamente o projeto

No terminal:

cd ~/Projetos/compose/bind_mount_compose/

docker-compose up -d

docker-compose ps

# Output
          Name                         Command              State           Ports
---------------------------------------------------------------------------------------------------
6_bind_mount_compose_db_1       docker-entrypoint.sh mysql  Up      0.0.0:3306->3306/tcp, 33060/tcp
6_bind_mount_compose_flask_1    python ./app.py             Up      0.0.0.0:5000->5000/tcp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment