Por que usar um proxy reverso com Docker?

Containers recebem IPs aleatórios, acessíveis apenas no hospedeiro. Você pode acessar o serviço do container mapeando a porta interna daquele serviço com uma porta no hospedeiro. Por exemplo, um container nginx cuja porta interna é a 80, mapeada para a porta 80 do hospedeiro:

docker run --name container-nginx-php7.4 -d -p 80:80 webdevops/php-nginx-dev:7.4

E quando outros containers também usam a porta 80? Aí entra o proxy reverso. Considere 2 containers rodando nginx, mas sem mapear a porta 80 com o hospedeiro:

docker run --name container-nginx-php7.4 -d -p 80:80 webdevops/php-nginx-dev:7.4
docker run --name container-nginx-php8.0 -d -p 80:80 webdevops/php-nginx-dev:8.0

As requisições externas chegarão ao proxy reverso instalado no hospedeiro usando o seguinte esquema:

nginx-php74.server.com --> redireciona o tráfego para o container container-nginx-php7.4
nginx-php80.server.com --> redireciona o tráfego para o container container-nginx-php8.0

Ao invés de acessar o serviço mapeando portas diferentes no hospedeiro, você acessará através do mapeamento de URLs. Porém, como os IPs dos containers não são fixos, como fazer este mapeamento automaticamente?

nginx-proxy

A solução é automatizar a configuração do proxy reverso.

nginx-proxy configura um container rodando nginx e docker-gen. docker-gen gera um proxy reverso para nginx e recarrega nginx quando containers são iniciados ou encerrados no hospedeiro.

Requisitos

Como usar

Para o nosso exemplo, criaremos 3 serviços em arquivos docker-compose.yml independentes. Nos links a seguir você poderá baixá-los:

  1. nginx-proxy
  2. container-nginx-php7.4
  3. container-nginx-php8.0

Crie um diretório para cada docker-compose.yml.

Rede Docker

Para que o container nginx-proxy encontre os demais containers inicializados no hospedeiro, é preciso que compartilhem uma rede em comum. Isto é obtido através da seguinte diretiva do docker-compose.yml:

networks:
  default:
    external:
      name: docker-production   <=== rede em modo bridge

Para que os containers fiquem visíveis ao nginx-proxy, também precisam ter acesso a rede docker-production.

Para criar a rede, execute no hospedeiro:

$ docker network create docker-production
fdbacfcb063758665c0101c9eaf94cf908569eaa248b00438a429a2a341d088c    <=== identificador da rede criada

Inicialização

Inicialize o docker-compose de cada projeto:

docker-compose up -d

Conectando projetos

Para que um projeto (e.g. container-nginx-php7.4, container-nginx-php8.0) utilize o proxy reverso nginx-proxy, é necessário conectá-lo a rede docker-production, incluindo ao final do seu docker-compose.yml:

networks:
  default:
    external:
      name: docker-production   <=== rede em modo bridge

Ainda no docker-compose.yml, defina uma variável de ambiente com o nome do host que os usuários utilizarão para acessar o serviço e a sua porta dentro do container (a porta não precisa ficar exposta no host):

services:
    cms-web:
        image: webdevops/php-nginx-dev:7.4
        environment:
            - VIRTUAL_HOST=nginx-php74.server.com
            - VIRTUAL_PORT=80

Certificados SSL

acme-companion é um container que trabalha para nginx-proxy, criando e renovando certificados SSL Let’s Encrypt dos serviços guarnecidos pelo proxy reverso, através do protocolo ACME (Ambiente de Gerenciamento de Certificados Automatizados).

É inicializado com o container nginx-proxy.

Para que os containers guarnecidos pelo nginx-proxy utilizem a criação e renovação automatizada de certificados SSL Let’s Encrypt, declare o nome do host na variável de ambiente LETSENCRYPT_HOST, no docker-compose.yml do projeto:

services:
    cms-web:
        image: webdevops/php-nginx-dev:7.4
        environment:
            - VIRTUAL_HOST=nginx-php74.server.com
            - LETSENCRYPT_HOST=nginx-php74.server.com  <== host para criação ou renovação do certificado SSL
            - VIRTUAL_PORT=80

CI/CD

Crie projetos no Github, Gitlab ou a sua ferramenta de CI/CD e automatize a criação dos containers. ;)