Docker: Instalando e usando

Saudações. Instruções de como instalar o Docker (CE – Community Edition) no Linux.

Artigo vivo, sofrerá adições constantes.

Procedimentos prévios recomendados:

  • Instalação de programas básicos;
  • Data/hora via NTP;
  • Ajuste fino no kernel Linux;
  • DNS Cache local de alta velocidade;

1 – Instalando Docker (CE – Community Edition)

Bash
# Baixar script instalador oficial:
curl -fsSL get.docker.com -o /tmp/get-docker.sh

# Executar script instalador:
sh /tmp/get-docker.sh

# Ferramentas recomendadas:
apt-get -y install iproute2
apt-get -y install bridge-utils
apt-get -y install tcpdump
apt-get -y install strace

2 – Criar comandinhos rápidos

Bash
# Comando para parar e destruir um container:
    (
        echo '#!/bin/sh';
        echo;
        echo 'for x in $@; do'
        echo '    docker stop $x;'
        echo 'docker stop $x;'
        echo 'docker rm $x;'
        echo 'done';
        echo;
    ) > /usr/bin/undocker
    chmod +x /usr/bin/undocker

# Comando para entrar no shell (/bin/sh) de um container:
    (
        echo '#!/bin/sh';
        echo;
        echo 'docker exec -it $1 /bin/bash';
        echo;
    ) > /usr/bin/dsh
    chmod +x /usr/bin/dsh

# Comando para listar containers em execucao (inclusive parados):
    (
        echo '#!/bin/sh';
        echo;
        echo 'docker ps -a';
        echo;
    ) > /usr/bin/dps
    chmod +x /usr/bin/dps

3 – Conferindo processos principais

Bash
# Conferindo docker em execução:
# 1 - verificar se o containerd está rodando:
ps aux | grep '[c]ontainerd'
   # root  1895  0.4  0.2 3575228 74044 ? Ssl Nov17  14:49 /usr/bin/containerd

# 2 - verificar se o dockerd está rodando:
ps aux | grep '[d]ockerd'
    # root 1986  0.8  0.4 5011344 151252 ? Ssl Nov17 25:01 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

4 – Comandos mais utilizados

Bash
# Listar containers:
docker  ps  -a
docker  container ls
docker  container ls -a

# Listar imagens presentes (inicialmente não há nada mesmo!)
docker image ls
docker images

# Listar redes:
docker network ls

# Procurar por imagens publicas:
docker search alpine
docker search debian
docker search redis
docker search n8n

# Procurar por imagens públicas e OFICIAIS (seguras):
docker search --filter is-official=true php
docker search --filter is-official=true debian

# Baixando imagens:
docker pull ubuntu
docker pull alpine
docker pull debian

# Baixar versoes (tags) específicas de uma imagem:
docker pull debian:bookworm
docker pull debian:bullseye
docker pull debian:buster
docker pull alpine:3.9

# Removendo imagem do repositório local:
docker rmi alpine:3.9
docker rmi debian:buster

# Listar volumes:
docker volume ls

# Removendo todas as imagens sem uso:
docker image prune -f

# Removendo todas as redes sem uso:
docker network prune -f

# Removendo todos os volumes abandonados (perigoso, faz backup primeiro):
docker volume prune -f

# Removendo todos os objetos (redes, imagens, ...) sem uso:
docker system prune -f

5 – Exemplos de manipulação de redes em docker

Bash
# Listar redes:
docker network ls

# REDES BRIDGE (tipo padrão/normal):
#------------------------------------------------------------------------

# - Criar uma rede bridge adicional simples (nome: sites1):
docker network create sites1

# - Criar uma rede bridge adicional simples (nome: sites2):
docker network create -d bridge sites2
     
# - Cuidado:
#   ** o docker cria a rede inicial na faixa 172.17.0.0/16
#   ** ao criar redes novas sem informar a faixa, ele usa um /16 seguinte
#      ou seja, a proxima rede 172.18.0.0/16, e 172.19.0.0/16, ...
#      mas a faixa privada termina em 172.31.0.0/16,
#   ** a rede 172.32.0.0/16 NÃO É PRIVADA, SE VOCÊ INVADIR ESSA FAIXAR
#      ou faixas acima dela sites vão deixar de abrir no seu servidor.
#

# - Criar uma rede bridge adicional simples com faixa ipv4 específica:
docker network create -d bridge --subnet 192.168.3.0/24 sites3
docker network create -d bridge --subnet 192.168.4.0/24 sites4

# - Nota: o primeiro IP é usado como gateway, mas vc pode especificar:
docker network create -d bridge \
    --subnet 192.168.5.0/24 --gateway 192.168.5.254 \
    sites5

# - Nota: por padrão todas as redes são criadas ipv4-only

# - Criar uma rede bridge adicional com IPv6:
docker network create -d bridge \
    --subnet 10.6.0.0/16 --gateway 10.6.255.254 \
    --ipv6 \
    --subnet=2001:db8:10:6::/64 \
    --gateway=2001:db8:10:6::1 \
    sites6

# - Criar uma rede bridge adicional com nome especifico
#   de interface de rede (ip link show; brctl show):
docker network create -d bridge \
    -o "com.docker.network.bridge.name"="brsites7" \
    --subnet 10.7.0.0/16 --gateway 10.7.255.254 \
    --ipv6 \
    --subnet=2001:db8:10:7::/64 \
    --gateway=2001:db8:10:7::1 \
    sites7

# - Criar uma rede bridge adicional com nome especifico
#   de interface de rede (ip link show; brctl show) e
#   permitir comunicação livre entre containers:
docker network create -d bridge \
    -o "com.docker.network.bridge.name"="brsites8" \
    -o "com.docker.network.bridge.enable_icc"="true" \
    --subnet 10.8.0.0/16 --gateway 10.8.255.254 \
    --ipv6 \
    --subnet=2001:db8:10:8::/64 \
    --gateway=2001:db8:10:8::1 \
    brsites8

# - Criar uma rede bridge adicional com nome especifico
#   de interface de rede (ip link show; brctl show),
#   configurar MTU em 9000 (jumbo-frame), desativar NAT,
#   permitir comunicação livre entre containers e
#   nao fazer NAT (rotear ou usar ip publico mesmo!):
docker network create -d bridge \
    -o "com.docker.network.bridge.name"="brsites9" \
    -o "com.docker.network.bridge.enable_icc"="true" \
    -o "com.docker.network.driver.mtu"="9000" \
    -o "com.docker.network.bridge.enable_ip_masquerade"="false" \
    --subnet 45.255.128.128/25 --gateway 45.255.128.254 \
    --ipv6 \
    --subnet=2804:cafe:ffff:a009::/64 \
    --gateway=2804:cafe:ffff:a009::1 \
    brsites9


# REDES MACVLAN (tipo transparente, joga o container na rede externa):
#------------------------------------------------------------------------

# - Nota: o container receberá um MAC novo, a bridge do Linux vai
#         deixar passar, mas o Port-Group e vSwitch do VMware
#         por padrão não permitirá, vc precisa ativar o recurso
#         allow-mac-change na Port-Group no no vSwitch
# - Nota: a rede MACVLAN precisa ser anexada uma interface
#         ethernet (eth0, eno0, ens192, ...) que esteja UP
#
# - Nota: você só pode usar 1 rede MACVLAN por interface ethernet
#
# - Alerta: a gerencia de IP é selvagem, cuidado ao
#         informar a faixa pois criar containers e alocar
#         IPs em uso na LAN externa pode resultar em conflito
#         de ARP (o famoso conflito de IP)
#
# - No VMware ESXi (terminal):
#    esxcli network vswitch standard policy security set \
#       --vswitch-name=vSwitch0 --allow-mac-change yes
#

# Criar rede MACVLAN simples para contato externo na eth0:
docker network create -d macvlan -o parent=eth0 \
    --subnet=10.160.0.0/16 --gateway 10.160.255.254 \
    mth0

# Criar rede MACVLAN com IPv6 para contato externo na eth1:
docker network create -d macvlan -o parent=eth1 \
    --subnet=45.255.130.0/27 --gateway 45.255.130.1 \
    --ipv6 \
    --subnet=2804:cafe:ffff:b000::/64 --gateway=2804:cafe:ffff:b000::1
    mth1

# Removendo todas as redes sem uso (todas acima se vc não as usou):
docker network prune -f

6 – Exemplos: criação e manipulação de containers

Bash
# Nota: todo container precisa rodar alguma coisa pra justificar
#       sua existencia na lista de processos do Linux, assim,
#       imagens puras como Debian, Alpine, não possuem programas
#       de ENTRYPOINT ou CMD. Você precisará imperativamente
#       informar um comando após o nome da imagem.
#       As imagens de aplicativos (MariaDB, Redis, ...)
#       já possuem ENTRYPOINT/CMD e por isso não exigem
#       um comando após o nome da imagem.
#       Nos exemplos abaixo, vou executar o comando 'sleep'
#       como uma forma de manter um processo rodando dentro
#       do container.
#
# Exemplo 1
#------------------------------------------------------------------

# Criar e rodar em 2 etapas (pouco usual, apenas exemplo):
# - Criar container (não roda)
docker create --name debian01 debian:bookworm sleep 999999999

# - Executar/iniciar o container:
docker start  debian01

# Listar (comando puramente informativo):
docker ps -a

# Extrair todas as informações do container:
docker inspect debian01

# Extrair todas as informações do container (puramente informativo):
docker exec -it debian01  /bin/bash
  exit   #< para sair do container

# Parar o container:
docker stop debian01

# Destruir o container:
docker rm -f debian01


# Exemplo 2 (versão rápida dos comandos acima)
#------------------------------------------------------------------

# Criar e rodar:
docker run -d --name debian02 debian:bookworm sleep 999999999

# Listar (comando puramente informativo):
docker ps -a

# Extrair todas as informações do container (puramente informativo):
docker inspect debian02

# Entrar no container (executar um shell interativo no ambiente):
docker exec -it debian02  /bin/bash
  exit   #< para sair do container

# Parar e destruir o container:
docker rm -f debian02


# Exemplo 3 (adicionando elegancia e mais opções)
#------------------------------------------------------------------

# Criar pasta de dados:
mkdir -p /storage/debian03

# Criar container cheio de opções elegantes:
docker run -d \
    --name debian03 \
		-h debian03.intranet.br \
    --restart=unless-stopped \
    \
		-e TZ=America/Sao_Paulo \
		-e MAINTAINER="Patrick Brandao" \
    \
    --mount \
       type=bind,source=/storage/debian03,destination=/data,readonly=false \
    \
    debian:bookworm \
        sleep 999999999

# Argumentos:
#    -h especifica o hostname interno
#    --restart pode ser always ou unless-stopped
#    -e define variável de ambiente (comando: env)
#    --mount mapeia uma pasta do HOST (Linux) dentro do container
#        no exemplo acima, tudo que dentro do container
#        que gravar em /data/ estará de fato gravando
#        no HOST principal em /storage/debian03/
#

# Parar e destruir o container:
docker rm -f debian03



# Exemplo 4 (argumentos de rede e multi-rede)
#------------------------------------------------------------------

# Criando duas redes:
# - Rede para acesso à Internet
docker network create -d bridge \
    -o "com.docker.network.bridge.name"="brsites" \
    --subnet 10.99.0.0/24 --gateway 10.99.0.254 \
    --ipv6 \
    --subnet=2001:db8:ffff:1099::/64 --gateway=2001:db8:ffff:1099::1 \
    brsites

# - Rede para acesso entre containers e container banco de dados
docker network create -d bridge \
    -o "com.docker.network.bridge.name"="brdatabase" \
    -o "com.docker.network.driver.mtu"="9000" \
    -o "com.docker.network.bridge.enable_icc"="true" \
    -o "com.docker.network.bridge.enable_ip_masquerade"="false" \
    \
    --subnet 10.255.0.0/24 --gateway 10.255.0.254 \
    brdatabase

# - Criar container de banco de dados MariaDB:
	mkdir -p /storage/mariadb-main
	docker run \
		-d --restart=unless-stopped \
		--name mariadb-main \
		-h mariadb-main.intranet.br \
		\
		--network brdatabase \
		--ip=10.255.0.100 \
		\
		-e MYSQL_ROOT_PASSWORD=tulipasql \
		-e MYSQL_DATABASE=admin \
		-e MYSQL_USER=suporte \
		-e MYSQL_PASSWORD=tulipasql \
		\
		--mount \
 type=bind,source=/storage/mariadb-main,destination=/var/lib/mysql,readonly=false \
		\
			mariadb:latest

# - Criar container Debian que se conectará na Internet
#   e possuirá uma segunda conexão de rede conectada à rede
#   de banco de dados
docker run -d \
    --name debian04 \
		-h debian04.intranet.br \
    --restart=always \
		\
		--network brsites --mac-address "00:ca:fe:f1:f2:f0" \
		--ip=10.99.0.14 \
		--ip6=2001:db8:ffff:1099::14 \
		\
    debian:bookworm \
        sleep 999999999

# - Conectar container debian04 na rede de banco de dados:
docker network connect brdatabase debian04 --ip=10.255.0.14

# - Destruir tudo!
undocker debian04
undocker mariadb-main
docker   system prune -f

# - Observe os arquivos em /storage/mariadb-main/
#   se você recriar o container mariadb-main novamente
#   ele continuará de onde parou pois os dados objetivos
#   foram salvos, somente software (parte descartavel) foi
#   destruida.

7 – Configuração principal do Docker

O Docker faz a leitura do arquivo (JSON) no caminho /etc/docker/daemon.json (precisa criar o arquivo), segue algumas configurações comuns, junte as que precisa num único objeto JSON.

Acelerar downloads

JSON
# Paralelismo de downloads e uploads de imagens
{
    "max-concurrent-uploads": 16,
    "max-concurrent-downloads": 16
}

Suporte a GPU

JSON
# Suporte a GPU NVIDIA (requer instalacao previa dos drivers):
{
    "runtimes": {
        "nvidia": {
            "args": [],
            "path": "nvidia-container-runtime"
        }
    }
}

Desativar firewall e redirecionamento de portas

Referência: https://docs.docker.com/reference/cli/dockerd/

JSON
# Desativar suporte a regras de firewall e redirecionamento
{
  "iptables": false,
  "ip6tables": false,
  "ip-masq": false
}

Rede IPv4 da rede padrão (docker default network)

JSON
# Rede IP padrao: 172.17.0.0/16
# Escolha uma das opcoes abaixo:

# Alterar rede padrão:
{
  "bip": "10.251.0.0/16"
}

# Versao mais nova do docker requer a config assim:
{
    "default-address-pools": [ { "base":"10.251.0.0/16", "size": 16 } ]
}

Reinicie para aplicar

Para aplicar as alterações realizadas no /etc/docker/daemon.json, reinicie o docker:

Bash
systemctl restart docker

Em caso de problemas, retire o daemon.json e reinicie o servidor.

8 – Ferramentas de gestão de containers

Algumas ferramentas podem te ajudar a monitorar e manipular containers usando interface WEB e templates prontos, vou deixar 2 exemplos:

  • WeaveScope: permite visualização de ambiente e manipulação básica;
  • Portainer CE: permite gestão completa, desde o básico ao avançado de todos os recursos do Docker, fornece templates de ambientes prontos e pre-configurados, considerado um dos melhores sistemas para gestão de containers;

WeaveScope

Referência: https://github.com/weaveworks/scope

Bash
# Nota: se ele bugar, apenas destrua-o e execute novamente!

# Baixar script de provisionamento:
curl -L git.io/scope -o /usr/sbin/scope

# Tornar executavel:
chmod a+x /usr/sbin/scope

# Subir container do WeaveScope com usuário e senha (senão ele fica aberto):
arg1="ENABLE_BASIC_AUTH=true"
arg2="BASIC_AUTH_USERNAME=admin"
arg3="BASIC_AUTH_PASSWORD=tulipa"
export WEAVESCOPE_DOCKER_ARGS="-e $arg1 -e $arg2 -e $arg3"
scope launch

# Acesso: http://  +  IP  +  :4040/

# Para destruir o WeaveScope:
undocker  weavescope

Portainer CE (Community Edition)

Referência: https://docs.portainer.io/start/install-ce/server/docker/linux

Bash
# Nota: se ele bugar, apenas destrua-o e execute novamente, os dados
#       que importa estarão no diretorio de armazenamento persistente

# Diretorio de dados persistentes:
mkdir -p /storage/portainer-data

# Criar e rodar:
docker run \
		-d --restart=always \
		--name portainer \
		-h portainer.intranet.br \
		\
		-p 8000:8000 \
		-p 9443:9443 \
		\
		-v /var/run/docker.sock:/var/run/docker.sock \
		--mount \
		type=bind,source=/storage/portainer-data,destination=/data,readonly=false \
		\
		portainer/portainer-ce:2.21.4

# Acesso: https:// +  IP  +  :9443/
# Defina a primeira senha e salve-a

Terminamos por hoje, isso deve ter te economizado alguns livros de Docker!

Patrick Brandão, patrickbrandao@gmail.com