Linux Dev ops

Docker – Gerenciamento avançado de recursos

Docker – Gerenciamento avançado de recursos

O Docker é uma ferramenta muito completa e com diversos recursos para criar e monitorar o funcionamento dos containers. Abaixo veremos algumas opções úteis que irão nos auxiliar na criação e manutenção dos nossos containers.

Opções de rede

Existem algumas configurações de rede que podemos fazer nos nossos containers, listamos abaixo alguns exemplos:

Definindo o nome do host do container

Por padrão o nome do host é o container id, podemos mudar utilizando o parâmetro −−hostname que irá definir um nome para este container:

# docker run -ti --hostname srv-web debian

root@srv-web:/# hostname
srv-web

Definindo o servidor de DNS para o container

Se quisermos definir um dns específico para nosso container, podemos utilizar o parâmetro −−dns:

# docker run -ti --dns 8.8.8.4 debian

root@9d1251fdf034:/# cat /etc/resolv.conf 
nameserver 8.8.8.4

Personalizando MAC Address da interface do container

Podemos definir um MAC Address específico para a interface de rede do container utilizando o parâmetro --mac-address:

# docker run -ti --mac-address 12:aa:11:bb:10:cc debian

root@25daa64f4d54:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 12:aa:11:bb:10:cc brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

Expondo uma porta do container

Definimos que um container terá acesso a uma porta através da opção --expose:

# docker run -ti --expose 80 --name webapp debian

# docker ps                                                 
CONTAINER ID   IMAGE     COMMAND   CREATED          STATUS         PORTS     NAMES
2ad90e528ebc   debian    "bash"    10 seconds ago   Up 9 seconds   80/tcp    webapp

Fazer redirecionamento (DNAT) de porta do host para um container

Para acessar um serviço de um container utilizando o IP do host hospedeiro, utilizamos o parâmetro −−publish, que criará uma regra pelo Iptables direcionando as portas:

# docker run -d --publish 8080:80 --name www httpd

ou

# docker run -d -p 8080:80 --name www httpd

# docker ps                                
CONTAINER ID   IMAGE     COMMAND              CREATED         STATUS         PORTS                  NAMES
dda212e8b5af   httpd     "httpd-foreground"   3 seconds ago   Up 1 second    0.0.0.0:8080->80/tcp   www

Agora iremos acessar o container para pegar o endereço dele para testarmos o redirecionamento.

# docker exec -ti dda212e8b5af /bin/bash

root@dda212e8b5af:/usr/local/apache2# ifconfig 
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.3  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:03  txqueuelen 0  (Ethernet)
        RX packets 3001  bytes 8866111 (8.4 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2348  bytes 169737 (165.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Agora vamos executar o comando curl diretamente do nosso host.

# curl 172.17.0.3 8080
<html><body><h1>It works!</h1></body></html>

Pronto funcionou o redirecionamento! 😄

Podemos verificar através do iptables que o docker adicionou uma regra de direcionamento:

# sudo iptables -nL DOCKER -t nat
Chain DOCKER (2 references)
target     prot opt source               destination         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8080 to:172.17.0.3:80

Fazendo a ligação entre dois container

Para que um container reconheça outro, utilizamos o parâmetro --link, basicamente ele acrescenta o container desejado no arquivo /etc/hosts:

# docker run -ti --link www debian

root@f9dc1977bd65:/# ping www
PING www (172.17.0.3) 56(84) bytes of data.
64 bytes from www (172.17.0.3): icmp_seq=1 ttl=64 time=0.171 ms
64 bytes from www (172.17.0.3): icmp_seq=2 ttl=64 time=0.089 ms
64 bytes from www (172.17.0.3): icmp_seq=3 ttl=64 time=0.098 ms
^C
--- www ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 8ms
rtt min/avg/max/mdev = 0.089/0.119/0.171/0.037 ms

root@f9dc1977bd65:/# cat /etc/hosts 
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.3	www dda212e8b5af
172.17.0.4	f9dc1977bd65

Definindo IP do hospedeiro para o container

Desta forma o ip do container será o mesmo do host hospedeiro com o parâmetro --net=host. Basicamente seria como se o container estivesse simulando o host hospedeiro. Não é o ideal usar essa opção:

# docker run -ti --net=host debian

Monitorando recursos

Existem diversos comandos do docker que nos auxiliam a gerenciar os recursos e analisar nossos containers, abaixo seguem alguns exemplos:

Listando informações do Docker

O comando docker info, traz diversas informações sobre o Docker, versão, imagens, quantos containers foram criados, quantos estão em execução, pausados, etc:

# docker info

Client:
 Context:    default
 Debug Mode: false
 Plugins:
  app: Docker App (Docker Inc., v0.9.1-beta3)
  buildx: Build with BuildKit (Docker Inc., v0.5.0-docker)

Server:
 Containers: 5
  Running: 3
  Paused: 0
  Stopped: 2
 Images: 7
 Server Version: 20.10.1
 Storage Driver: overlay2
(...)

Verificando os logs dos containers

O comando docker logs exibe todas as ações realizadas dentro do container:

# docker logs webapp

root@f9dc1977bd65:/# ping www
PING www (172.17.0.3) 56(84) bytes of data.
64 bytes from www (172.17.0.3): icmp_seq=1 ttl=64 time=0.171 ms
64 bytes from www (172.17.0.3): icmp_seq=2 ttl=64 time=0.089 ms
64 bytes from www (172.17.0.3): icmp_seq=3 ttl=64 time=0.098 ms
^C
--- www ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 8ms
rtt min/avg/max/mdev = 0.089/0.119/0.171/0.037 ms
root@f9dc1977bd65:/# cat /etc/hosts 
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.3	www dda212e8b5af
172.17.0.4	f9dc1977bd65
root@f9dc1977bd65:/# df -h
Filesystem      Size  Used Avail Use% Mounted on
overlay         115G   69G   41G  63% /
tmpfs            64M     0   64M   0% /dev
tmpfs           7.7G     0  7.7G   0% /sys/fs/cgroup
shm              64M     0   64M   0% /dev/shm
/dev/sdb5       115G   69G   41G  63% /etc/hosts
tmpfs           7.7G     0  7.7G   0% /proc/asound
tmpfs           7.7G     0  7.7G   0% /proc/acpi
tmpfs           7.7G     0  7.7G   0% /proc/scsi
tmpfs           7.7G     0  7.7G   0% /sys/firmware

Se você estiver trabalhando com um container que direciona os logs para saída padrão e quer analisar a saída desses logs, você pode utilizar o parâmetro -f, assim ele será atualizado de acordo com os novos registros:

# docker logs -f www

AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
[Sun Jan 03 20:05:30.537687 2021] [mpm_event:notice] [pid 1:tid 140355815597184] AH00489: Apache/2.4.46 (Unix) configured -- resuming normal operations
[Sun Jan 03 20:05:30.540778 2021] [core:notice] [pid 1:tid 140355815597184] AH00094: Command line: 'httpd -D FOREGROUND'
172.17.0.1 - - [03/Jan/2021:20:08:25 +0000] "GET / HTTP/1.1" 200 45

Verificando informações de processos em execução no container

O comando docker top exibe no console, informações dos processos executando no container. Precisamos passar como parâmetro o <nome> ou :

# docker top www

UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                15793               15773               0                   17:05               ?                   00:00:00            httpd -DFOREGROUND
daemon              15842               15793               0                   17:05               ?                   00:00:00            httpd -DFOREGROUND
daemon              15843               15793               0                   17:05               ?                   00:00:00            httpd -DFOREGROUND
daemon              15844               15793               0                   17:05               ?                   00:00:00            httpd -DFOREGROUND

Visualizando todas as informações de um container

O comando docker inspect, lista diversas informações de um container, desde aspectos da sua criação (data, imagem base, autor…),  como características (rede, volumes, etc.). É um comando bem completo, que pode ser utilizado em conjunto com o grep para facilitar a pesquisa:

# docker inspect www

[
    {
        "Id": "dda212e8b5afc20d8b3576906b0d88a9f2e5151c4ecfba23f46124a66a41e855",
        "Created": "2021-01-03T20:05:29.923508826Z",
        "Path": "httpd-foreground",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 15793,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2021-01-03T20:05:30.450089691Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:dd85cdbb99877b73f0de2053f225af590ab188d79469eebdb23ec2d26d0d10e8",
(...)
# docker inspect www | grep -i IPAddress

            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.3",
                    "IPAddress": "172.17.0.3",

Também podemos formatar a saída do comando docker, usando o parâmetro -f, passando algumas strings:

Exibe o IP do container:

# docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' centos

172.17.0.3

Exibe o MAC Address do container:

# docker inspect -f '{{range .NetworkSettings.Networks}}{{.MacAddress}}{{end}}' centos

02:42:ac:11:00:03

Exibe os volumes do container:

# docker inspect -f {{.Mounts}} centos

[{bind /volumes/data /data  true rprivate}]

Limitando uso de memória no container

Por padrão, quando a memória não é limitada, o container utilizará até o limite de memória do host hospedeiro. O ideal é limitar de acordo com a utilização, evitando que um container interfira no funcionamento do outro. Utilizamos o parâmetro -m seguindo do tamanho que desejamos:

# docker run -ti -m 512M −−name webs debian

# docker inspect webs | grep -i mem
            "Memory": 536870912,
            "CpusetMems": "",
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 1073741824,
            "MemorySwappiness": null,

Para alterar o valor de um container que está em execução, utilizamos o comando docker update, com o parâmetro -m:

# docker update -m 256M webs

webs

# docker inspect webs | grep -i mem
            "Memory": 268435456,
            "CpusetMems": "",
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 1073741824,
            "MemorySwappiness": null,

Limitando uso de cpu no container

Por padrão, quando a cpu não é limitada, o container utilizará até o limite de cpu do host hospedeiro. Usamos proporção de uso de cpu para especificar quanto cada container poderá usar.

Caso não seja limitado o uso, o desempenho de um container pode afetar os demais. Utilizamos o parâmetro --cpu-shares seguindo de um valor proporcional, vamos criar 3 containers como exemplo:

# docker run -ti --cpu-shares 2048 --name container1 ubuntu
# docker run -ti --cpu-shares 1024 --name container2 ubuntu
# docker run -ti --cpu-shares 1024 --name container3 ubuntu

# docker inspect container1 container2 container3 | grep CpuShares
            "CpuShares": 2048,
            "CpuShares": 1024,
            "CpuShares": 1024,

No exemplo acima, container1 poderá usar 50% de recursos de cpu do host hospedeiro, enquanto que o container2 e container3 poderão usar 25% cada.

Para alterar recursos de cpu de containers em execução, utilizamos o comando docker update. Para igualar uso de cpu (33%) dos 3 containers, vamos alterar o valor do conatiner1, deixando igual aos demais:

# docker update --cpu-shares 1024 container1

# docker inspect container1 container2 container3 | grep CpuShares
            "CpuShares": 1024,
            "CpuShares": 1024,
            "CpuShares": 1024,

Verificando o consumo de recursos pelos containers

O comando docker stats faz o acompanhamento em tempo real de todos os containers. Ele exibe as seguintes informações:

CONTAINER ID: Identificação do container no docker.
CPU %: Percentual de uso de CPU.
MEM USAGE / LIMIT: Quantidade de memória usada/limite.
MEM %: Percentual de memória usada.
NET I/O: Quantidade de uso de rede.
BLOCK I/O: Quantidade de escrita/leitura de blocos no disco.
PIDS: Quantidade de processos executando.

# docker stats

CONTAINER ID   NAME            CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O         PIDS
0ce1b1923a38   container3      0.00%     2.223MiB / 15.33GiB   0.01%     7.26kB / 0B      0B / 0B           1
75ee571056c9   container2      0.00%     1.777MiB / 15.33GiB   0.01%     8.61kB / 0B      0B / 0B           1
4dcfe1743e08   container1      0.00%     4.934MiB / 15.33GiB   0.03%     9.95kB / 0B      4.24MB / 0B       1
ad6351d55ffc   webs            0.00%     1.477MiB / 256MiB     0.58%     13.8kB / 0B      0B / 0B           1
dda212e8b5af   www             0.01%     45.04MiB / 15.33GiB   0.29%     8.88MB / 171kB   28.4MB / 9.92MB   82
2ad90e528ebc   webapp         0.00%     1.449MiB / 15.33GiB   0.01%     20.5kB / 0B      0B / 0B           1

Podemos verificar um container específico, passando como parâmetro o <nome> ou <container id>:

# docker stats www

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O         PIDS
dda212e8b5af   www       0.01%     45.04MiB / 15.33GiB   0.29%     8.88MB / 171kB   28.4MB / 9.92MB   82

Trabalhando com Dockerfiles

Um arquivo Dockerfile é um script que contém algumas instruções para facilitar a criação de imagens personalizadas. A utilização desses arquivos automatizam o processo de criação e atualização de suas imagens. Abaixo estão descritos os mais importantes comandos usados nos Dockerfiles:

FROM: Define qual imagem base estamos usando para criarmos nossa imagem personalizada;
MAINTAINER: Definimos quem é o mantenedor da imagem, ela é opcional;
RUN: Permite executar comandos no momento da criação da imagem. O ideal é executar diversos comandos na mesma instrução RUN;
LABEL: Define metadados na nossa imagem, como descrição, versão;
ADD: Adiciona arquivos para dentro da imagem que estamos criando. Ele também envia arquivos tar descompactando-os;
COPY: Copia arquivos e diretórios para dentro da imagem;
CMD: Define o comando que será executado na inicialização de um container, pode ser sobrescrito na criação do mesmo. Também serve como parâmetro para o ENTRYPOINT;
ENTRYPOINT: Permite especificar qual será o principal processo dentro de um container. Por padrão é o bash, ele não pode ser sobrescrito;
ENV: Define variáveis de ambiente para o container. Úteis na configuração de uma ambiente;
EXPOSE: Permite determinar quais portas do container estarão disponível para conexão;
USER: Usuário padrão da imagem, o usuário padrão é o root;
WORKDIR: Define o diretório de trabalho do container;
VOLUME: Faz o mapeamento de um diretório para o container;

Exemplo simples de Dockerfile com Nginx e Php7

Para este exemplo, vamos criar uma pasta com alguns arquivos para adicionar na imagem, no momento do build. Também será instalado o supervisor, para controlar a inicialização de dois serviços diferentes dentro da imagem. Segue os passos:

# mkdir nginx
# cd nginx

Criando arquivo de configuração do nginx com suporte ao php7:

# vim default

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/html;
    index index.php index.html index.htm index.nginx-debian.html;

    server_name localhost;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }
}

Arquivos de configuração do supervisor, configurando o start do nginx e php7:

# vim supervisord.conf

[supervisord]
nodaemon=true

[program:php-fpm]
command=/etc/init.d/php7.0-fpm start
autostart=true
autorestart=true

[program:httpd]
command=/etc/init.d/nginx start
autostart=true
autorestart=true

Agora iremos criar nosso arquivo Dockerfile, lembrando que ele deve sempre ser escrito desta forma, com a primeira letra maiúscula:

# vim Dockerfile

FROM ubuntu
MAINTAINER Cesar A. Gaspar - rasec.rapsag@gmail.com
LABEL description="Webserver" version="1.0"

RUN apt update && apt dist-upgrade -y && \
   apt install -y nginx php-fpm supervisor && \
   apt clean && \
   sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/g' /etc/php/7.0/fpm/php.ini

ADD default /etc/nginx/sites-available/
ADD supervisord.conf /etc/supervisord.conf

VOLUME [ "/var/www/html" ]

EXPOSE 80

CMD ["/usr/bin/supervisord"]

Para criarmos a imagem iremos utilizar o comando docker build, com o parâmetro -t, para definirmos um nome e versão. O ponto no final do comando indica que o arquivo Dockerfile está na pasta atual, caso o arquivo Dockerfile esteja em outra pasta, basta passar o caminho até a pasta:

# docker build -t webserver:1.0 .

Após finalizar todo o processo de construção da imagem, ela já estará disponível para criar seus containers:

docker images webserver REPOSITORY TAG IMAGE ID CREATED SIZE webserver 1.0 dd85cdbb9987 26 seconds jan 138MB

Agora para finalizar vamos criar um container usando nossa imagem. Para testar se está funcionando corretamente vamos mapear um volume e adicionar um arquivo php de teste:

# mkdir -pv /volumes/nginx
# docker run -d -p 8000:80 −−name nginx-server -v /volumes/nginx:/var/www/html webserver:1.0
# echo "<?php phpinfo() ?>" > /volumes/nginx/index.php

Podemos acessar pelo browser do host hospedeiro, já que definimos um redirecionamento pela porta 8000: image

Referências:

https://docs.docker.com
https://linuxcontainers.org
https://www.howtoforge.com/tutorial/how-to-use-docker-introduction
https://www.server-world.info/en/note?os=Debian_9&p=docker&f=1
https://www.youtube.com/channel/UCJnKVGmXRXrH49Tvrx5X0Sw

comments powered by Disqus

Assine nossa Newsletter! 🐧

Se una com os assinantes de nossa Newsletter, sempre que tiver postagem nova você será notificado.