Introdução
Os contêineres agora são cidadãos de primeira classe em qualquer ciclo de desenvolvimento. É essencial para nós entendermos como funciona a rede de contêineres. Isso não é importante apenas do ponto de vista da comunicação de serviço, mas também constitui um aspecto importante da segurança da infraestrutura.
Nesta postagem, aprenderemos brevemente sobre vários modos de rede disponíveis para contêineres do Docker e nos aprofundaremos na rede do modo de host.
Visão geral dos modos de rede
Nenhum
Nenhuma é direta, pois o contêiner recebe uma pilha de rede, mas não possui uma interface de rede externa. No entanto, ele recebe uma interface de loopback. Os projetos de contêiner rkt e Docker fornecem um comportamento semelhante quando a rede None ou Null é usada. Esse modo de rede de contêineres tem vários usos, incluindo testar contêineres, preparar um contêiner para uma conexão de rede posterior e ser atribuído a contêineres sem necessidade de comunicação externa.
Modo Ponte
Uma ponte Linux fornece uma rede interna de host na qual os contêineres no mesmo host podem se comunicar, mas os endereços IP atribuídos a cada contêiner não são acessíveis de fora do host. A rede de ponte aproveita o iptables para NAT e mapeamento de porta, que fornece rede de host único. A rede de ponte é o tipo de rede padrão do Docker (ou seja, docker0), onde uma extremidade de um par de interfaces de rede virtual é conectada entre a ponte e o contêiner.
Veja um exemplo do fluxo de criação:
- Uma ponte é provisionada no host.
- Um namespace para cada contêiner é provisionado dentro dessa ponte.
- O ethX dos contêineres é mapeado para interfaces de ponte privada.
- Iptables com NAT é usado para mapear entre cada container privado e a interface pública do host.
O NAT é usado para fornecer comunicação além do host. Embora as redes em ponte resolvam problemas de conflito de portas e forneçam isolamento de rede para contêineres executados em um host, há um custo de desempenho relacionado ao uso de NAT.
Modo de host
Nessa abordagem, um contêiner recém-criado compartilha seu namespace de rede com o host, proporcionando maior desempenho — velocidade próxima ao metal — e eliminando a necessidade de NAT; no entanto, ele sofre conflitos de porta. Embora o contêiner tenha acesso a todas as interfaces de rede do host, a menos que implantado no modo de privilégio, o contêiner não pode reconfigurar a pilha de rede do host.
A rede de host é o tipo padrão usado no Mesos. Em outras palavras, se a estrutura não especificar um tipo de rede, um novo namespace de rede não será associado ao contêiner, mas à rede do host. Às vezes chamada de rede nativa, a rede de host é conceitualmente simples, tornando-a mais fácil de entender, solucionar problemas e usar.
Sobreposição
As sobreposições usam túneis de rede para fornecer comunicação entre hosts. Isso permite que os contêineres se comportem como se estivessem na mesma máquina, encapsulando sub-redes de rede de um host para o próximo; em essência, abrangendo uma rede em vários hosts. Existem muitas tecnologias de encapsulamento, como redes locais extensíveis virtuais (VXLAN).
VXLAN tem sido a tecnologia de encapsulamento escolhida para o Docker libnetwork, cuja rede multi-host entrou como um recurso nativo na versão 1.9. Com a introdução desse recurso, o Docker optou por alavancar o Serf da HashiCorp como o protocolo de fofocas, selecionado por sua eficiência na troca de tabelas vizinhas e nos tempos de convergência.
Para aqueles que precisam de suporte para outras tecnologias de tunelamento, o Flannel pode ser o caminho a percorrer. Ele suporta udp, vxlan, host-gw, AWS-vpc ou gce. Cada um dos tipos de túnel do provedor de nuvem cria rotas nas tabelas de roteamento do provedor, apenas para sua conta ou nuvem privada virtual (VPC). O suporte para nuvens públicas é particularmente importante para drivers de sobreposição, uma vez que, entre outros, as sobreposições abordam melhor os casos de uso de nuvem híbrida e fornecem dimensionamento e redundância sem a necessidade de abrir portas públicas.
A rede de vários hosts requer parâmetros adicionais ao iniciar o daemon do Docker, bem como um armazenamento de chave-valor. Algumas sobreposições dependem de um armazenamento de chave-valor distribuído. Se você estiver fazendo a orquestração de contêineres, já terá um armazenamento de chave-valor distribuído por aí.
As sobreposições se concentram no desafio de comunicação entre hosts. Os contêineres no mesmo host que estão conectados a duas redes de sobreposição diferentes não podem se comunicar entre si por meio da ponte local — eles são segmentados um do outro.
Base
Drivers de rede subjacentes expõem interfaces de host (ou seja, a interface de rede física em eth0) diretamente para contêineres ou VMs em execução no host. Dois desses drivers subjacentes são a rede de área local virtual de controle de acesso à mídia (MACvlan) e o protocolo de Internet VLAN (IPvlan). A operação e o comportamento dos drivers MACvlan e IPvlan são muito familiares aos engenheiros de rede. Ambos os drivers de rede são conceitualmente mais simples do que a rede de ponte, eliminam a necessidade de mapeamento de portas e são mais eficientes. Além disso, o IPvlan possui um modo L3 que ressoa bem com muitos engenheiros de rede. Dadas as restrições – ou falta de recursos – na maioria das nuvens públicas, os underlays são particularmente úteis quando você tem cargas de trabalho no local, preocupações de segurança, prioridades de tráfego ou conformidade para lidar, tornando-os ideais para uso brownfield.
Agora vamos nos concentrar na rede do modo host.
Entendendo a rede do modo host em detalhes
O modo de host parece bastante simples, mas há alguns itens que você precisa ter em mente ao implantá-lo. Vejamos um exemplo para que você possa ver do que estamos falando. Primeiro, vamos iniciar um contêiner web básico em um host.
docker run -d --name=web1 --net=host vaibhavthakur/docker:webinstance1
Observe que estou passando o sinalizador '–net=host' no comando docker run. Além disso, observe que não estou especificando nenhum mapeamento de porta. Depois que a imagem for baixada, o docker executará a imagem como um contêiner chamado 'web'. Como dissemos ao docker para executar este contêiner como um daemon, vamos nos conectar a um shell bash no contêiner.
docker exec -it web1 /bin/bash
Isso deve colocá-lo em um shell dentro do contêiner e agora você deve verificar todos os adaptadores de rede disponíveis para o contêiner. Você pode fazê-lo como mostrado abaixo.
root@docker-1:~# docker exec -it web1 /bin/bash
[root@docker-1 /]# ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:9F:DE:3C:3C
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
inet6 addr: fe80::42:9fff:fede:3c3c/64 Scope:Link
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:180 (180.0 b)
ens4 Link encap:Ethernet HWaddr 42:01:0A:80:0F:E8
inet addr:10.128.15.232 Bcast:0.0.0.0 Mask:255.255.255.255
inet6 addr: fe80::4001:aff:fe80:fe8/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1460 Metric:1
RX packets:99119 errors:0 dropped:0 overruns:0 frame:0
TX packets:10016 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:236101229 (225.1 MiB) TX bytes:881725 (861.0 KiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:336 errors:0 dropped:0 overruns:0 frame:0
TX packets:336 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:35505 (34.6 KiB) TX bytes:35505 (34.6 KiB)
Agora, você deve sair do contêiner e executar o mesmo comando no host.
root@docker-1:~# ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:9fff:fede:3c3c prefixlen 64 scopeid 0x20<link>
ether 02:42:9f:de:3c:3c txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2 bytes 180 (180.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1460
inet 10.128.15.232 netmask 255.255.255.255 broadcast 0.0.0.0
inet6 fe80::4001:aff:fe80:fe8 prefixlen 64 scopeid 0x20<link>
ether 42:01:0a:80:0f:e8 txqueuelen 1000 (Ethernet)
RX packets 99198 bytes 236116665 (236.1 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 10074 bytes 888616 (888.6 KB)
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
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 336 bytes 35505 (35.5 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 336 bytes 35505 (35.5 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
root@docker-1:~#
Pode-se ver que a saída do comando é exatamente a mesma. Isso significa que você deve poder acessar o contêiner usando o IP do host. Lembre-se de que uma regra de firewall para aceitar na porta 80 está habilitada na máquina se você estiver usando um provedor de nuvem.
Vamos tentar acessar o serviço no IP público da instância.
Podemos ver a resposta do serviço rodando dentro do container web1.
Você deve observar que não fornecemos nenhum mapeamento de porta ao executar o contêiner e a rede do nó do host explicitamente especificada e, portanto, podemos acessar o servidor apache em execução dentro do contêiner na porta 80.
Agora vamos tentar outra coisa. Tentaremos executar outro contêiner do mesmo serviço usando a rede de modo de host neste host e ver o que acontece.
docker run -it --name web2 --net=host vaibhavthakur/docker:webinstance2
Se você observar com atenção, o contêiner nem sequer começou. Vamos ver os logs.
root@docker-1:~# docker run -it --name web2 --net=host vaibhavthakur/docker:webinstance2
Unable to find image vaibhavthakur/docker:webinstance2' locally
webinstance2: Pulling from vaibhavthakur/docker
Image docker.io/jonlangemak/docker:webinstance2 uses outdated schema1 manifest format. Please upgrade to a schema2 image for better future compatibility. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/
a3ed95caeb02: Already exists
3a5831f32025: Already exists
1070eeab2b98: Already exists
86ba7b11cc42: Already exists
8ea0f21db18b: Already exists
772c76531fbf: Already exists
04120008298a: Already exists
6a221adc032a: Already exists
4a835ccd3c00: Already exists
78e705e491cf: Already exists
c343940bb393: Already exists
663a5158a2b7: Already exists
09cf647f3fa5: Pull complete
Digest: sha256:62883de5c6d7786ec4267b8a0ccb9b104dea7523c9db934ecb911b78a5d983ee
Status: Downloaded newer image for vaibhavthakur/docker:webinstance2
2019-12-26 04:57:29,606 CRIT Supervisor running as root (no user in config file)
2019-12-26 04:57:29,619 INFO supervisord started with pid 1
2019-12-26 04:57:29,621 INFO spawned: 'httpd' with pid 6
2019-12-26 04:57:29,664 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:29,665 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:30,668 INFO spawned: 'httpd' with pid 7
2019-12-26 04:57:30,707 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:30,707 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:32,711 INFO spawned: 'httpd' with pid 8
2019-12-26 04:57:32,749 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:32,750 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:35,755 INFO spawned: 'httpd' with pid 9
2019-12-26 04:57:35,796 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:35,796 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:36,798 INFO gave up: httpd entered FATAL state, too many start retries too quickly
Portanto, isso mostra claramente que o contêiner não pôde ser ativado, pois o processo do ponto de entrada morreu rapidamente. Vamos cavar um pouco mais fundo e entrar no contêiner.
root@docker-1:~
[root@docker-1 /]
httpd dead but subsys locked
[root@docker-1 /]
Starting httpd: (98)Address already in use: make_sock: could not bind to address [::]:80
(98)Address already in use: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
Unable to open logs
[FAILED]
Primeiro verificamos o status do serviço httpd e ele apareceu morto. Em seguida, tentamos iniciá-lo, mas falhou novamente porque não conseguiu se conectar à porta 80. Vamos fazer mais um teste e verificar o status da porta 80.
[root@docker-1 /]# netstat -plnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 :::80 :::* LISTEN -
tcp 0 0 :::22 :::* LISTEN -
[root@docker-1 /]#
Pode-se ver claramente que a porta 80 está ocupada por algum processo, e este processo é nosso container chamado web1. Portanto, é seguro concluir que o contêiner web2 tentou se conectar à porta 80 no mesmo host em que o web1 está sendo executado e, portanto, falhou. Agora, vamos matar o contêiner web1 e tentar iniciar o serviço httpd no contêiner web2.
Depois de matar o web1, tentamos iniciar o httpd no web2 e foi bem-sucedido.
[root@docker-1 /]
Starting httpd: [ OK ]
[root@docker-1 /]
Um teste final, vamos acessar a instância na porta 80.
Podemos ver a resposta do serviço rodando dentro do container web2.
Conclusão
É importante entender que a rede no modo host fornece melhor desempenho de rede para contêineres, pois o tráfego de rede não precisa atravessar a ponte docker0 e os mapeamentos de porta iptables. Espero que este artigo explique claramente o que é a rede no modo host e como ela é diferente de outras opções de rede.
#CONTAINERS #NETWORKING #DOCKERS #HOST-MODE-NETWORKING #DOCKER-NETWORKING
Fonte: https://www.metricfire.com/blog/understanding-dockers-net-host-option/