Olá!
Dando prosseguimento à primeira parte, vou trazer hoje como o Docker cria o tunelamento entre os containers para criar o Link. É possível criar um acesso seguro entre os containers sem a necessidade de expor portas tcp/udp tanto do host quanto do próprio container; para isso, basta utilizar o parâmetro “link” e você vinculará dois containers. Mas como o Docker faz com que esse acesso seja seguro e confiável? Basicamente de duas formas, são elas:
1 – Variáveis de ambiente:
O Docker cria diversas variáveis de ambiente quando você cria um link entre os containers. O Docker automaticamente cria as variáveis de ambiente no container de destino com base nos parâmetros –link, ele também irá expor todas as variáveis de ambiente provenientes do container de origem. Essas variáveis podem vir:
- Do comando ENV, utilizado na criação da imagem de origem via Dockerfile;
- Das opções -e, –env e –env-file no comando docker run quando o container de origem é criado.
Essas variáveis de ambiente possibilitam acessar programaticamente as informações do container de origem, ou seja, podemos automatizar tarefas de deploy utilizando as variáveis carregadas de um container para o outro..
Aviso: É importante compreender que todas as variáveis de ambiente provenientes de um container são disponibilizados para qualquer outro container que esteja ligado a ele. Isso poderia ter sérias implicações de segurança se dados sensíveis são armazenados nas variáveis de ambiente (como usuário e senha de banco de dados).
O Docker define um <alias>_NAME padrão como variável de ambiente para cada container de destino listado no parâmetro –link. Por exemplo, se um novo container chamado web está ligado a um container de banco de dados chamado db via –link db: webdb, então o Docker cria uma variável WEBDB_NAME=/web/ webdb no container web.
Docker também define um conjunto de variáveis de ambiente para cada porta exposta pelo container de origem. Cada variável tem um prefixo único no formulário:
<name>_PORT_<port>_<PROTOCOL>
Os componentes desses prefixos são:
- O alias <name> especificado no parâmetro –link (por exemplo, webdb).
- A <port> exposta no container de origem.
- O <protocol>, se é TCP ou UDP.
Docker usa esse formato de prefixo para definir três variáveis de ambiente distintas:
- A variável prefix_ADDR contém o endereço IP a partir do URL, por exemplo WEBDB_PORT_5432_TCP_ADDR = 172.17.0.82.
- A variável prefix_PORT contém apenas o número da porta, por exemplo WEBDB_PORT_5432_TCP_PORT = 5432.
- A variável prefix_PROTO contém apenas o protocolo, por exemplo WEBDB_PORT_5432_TCP_PROTO = tcp.
Se o container expõe várias portas, uma variável de ambiente é definida para cada porta exposta. Isso significa, por exemplo, que se um container expõe 4 portas, o Docker cria 12 variáveis de ambiente, 3 para cada porta.
Além disso, o Docker cria uma variável de ambiente chamada <alias>_PORT. Essa variável contém o container de origem e a primeira porta exposta. A ‘primeira’ porta exposta é definida como a de menor número. Agora, considere esta variável: WEBDB_PORT=tcp: //172.17.0.82:5432; se essa porta é usada para TCP e UDP, você deve especificar o protocolo tcp primeiro.
Agora, na prática, vamos criar um container chamado site ligado a um container chamado db; veja o retorno do comando env dentro do container web:
$ docker run --rm --name site --link db:db mundodocker/app env . . . DB_NAME=/site/db DB_PORT=tcp://172.17.0.5:5432 DB_PORT_5432_TCP=tcp://172.17.0.5:5432 DB_PORT_5432_TCP_PROTO=tcp DB_PORT_5432_TCP_PORT=5432 DB_PORT_5432_TCP_ADDR=172.17.0.5 . . .
Você pode ver que o Docker criou uma série de variáveis de ambiente com informações úteis sobre o container de origem ‘db‘. Cada variável é prefixada com DB_, que é preenchida a partir do alias especificado acima. Se o alias fosse db1, as variáveis seriam prefixadas com DB1_. Você pode usar essas variáveis de ambiente para configurar suas aplicações para se conectar ao banco de dados no container db. A conexão será segura e privada; apenas o container site será capaz de falar com o container db.
Importante sobre variáveis de ambiente Docker
Ao contrário de entradas de host no arquivo /etc/hosts, os endereços IP armazenados nas variáveis de ambiente não são atualizados automaticamente se o container de origem é reiniciado. É recomendada a utilização das entradas de host em /etc/hosts para atualizar automaticamente o endereço IP de containers ligados.
As variáveis de ambiente só são definidas para o primeiro processo no container. Alguns daemons, como sshd, limparão essas variáveis em uma nova conexão.
2 – Atualizando o arquivo / etc / hosts
Além das variáveis de ambiente, o Docker adiciona uma entrada de host para o container de origem no arquivo /etc/hosts do container de destino. Veja como fica o /etc/hosts do container de destion:
$ docker run -t -i --rm --link db:sitedb mundodocker/app /bin/bash root@aed84ee21bde:/opt/app# cat /etc/hosts 172.17.0.7 aed84ee21bde . . . 172.17.0.5 sitedb 6e5cdeb2d300 db
Você pode ver duas entradas de host. A primeira é uma entrada para o container site que usa o Container ID como um nome de host. A segunda entrada utiliza o alias para referenciar o endereço IP do container db. Quer testar? Ótimo, do container que você acabou de criar (sitedb), você pode pingar os nomes definidos no link, por exemplo:
root@aed84ee21bde:/opt/app# ping db PING webdb (172.17.0.5): 48 data bytes 56 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.267 ms 56 bytes from 172.17.0.5: icmp_seq=1 ttl=64 time=0.250 ms 56 bytes from 172.17.0.5: icmp_seq=2 ttl=64 time=0.256 ms
Você pode ver que o nome db responde ao ip do container onde está o seu banco de dados, que resolve para 172.17.0.5. Você pode usar essa entrada do host para configurar um aplicativo para fazer uso de seu container db.
Nota: É possível ligar vários containers de destino em um único de origem. Por exemplo, você pode ter vários (com nomes diferentes) containers web conectados ao seu container db.
Se você reiniciar o container db, os containers ligados via /etc/hosts serão atualizados automaticamente com o novo endereço IP do container de origem, permitindo que a comunicação continue.
Legal, né? A partir disso, é possível criar sistemas distribuídos de forma mais fácil. Uma utilização bem importante é relacionada a criar link entre containers de aplicação e de volume, o que será tratado em artigos futuros.
Abraço!