Docker: Roteamento HTTPS

Como gerir o roteamento HTTP e a encriptação TLS (HTTPS) nos seus contentores Docker

👋 Bem-vindo à documentação da Stackhero!

A Stackhero oferece uma solução Docker cloud CaaS (Containers as a Service) pronta a usar que proporciona uma série de benefícios, incluindo:

  • Implemente facilmente os seus containers em produção com apenas um docker-compose up.
  • Nome de domínio personalizável seguro com HTTPS (por exemplo, https://api.sua-empresa.com, https://www.sua-empresa.com, https://backoffice.sua-empresa.com).
  • Desempenho ótimo e segurança robusta alimentados por uma VM privada e dedicada.
  • Atualizações sem esforço com apenas um clique.

Poupe tempo e simplifique a sua vida: são necessários apenas 5 minutos para experimentar a solução de hospedagem cloud Docker CaaS da Stackhero e implementar os seus containers em produção!

Cada instância do Stackhero para Docker inclui um servidor Traefik dedicado e pré-configurado que gere o tráfego HTTP e o encripta automaticamente usando certificados TLS. Esta configuração simplificada torna o roteamento simples e eficiente. Por exemplo:

  • Enviar tráfego de https://www.my-company.com (com ou sem o prefixo www) para o seu contentor frontend.
  • Roteie https://www.my-company.com/documentations para o seu contentor documentations.
  • Direcione https://api.my-project.io para o seu contentor api.

Pode gerir um número ilimitado de domínios com criação e renovação automática de certificados TLS. O melhor de tudo é que pode configurar tudo isto com apenas três linhas!

Em todos os exemplos de configuração seguintes, terá de substituir <XXXXXX>.stackhero-network.com pelo nome de domínio da sua instância Stackhero para Docker.

Abaixo está um exemplo básico de um ficheiro docker-compose.yml:

services:
  test:
    image: nginx
    labels:
      - "traefik.enable=true" # Ativar Traefik para rotear tráfego para este contentor
      - "traefik.http.routers.test.rule=Host(`<XXXXXX>.stackhero-network.com`)" # Definir o host
      - "traefik.http.routers.test.tls.certresolver=letsencrypt" # Usar 'letsencrypt' como o resolvedor de certificado TLS

Neste exemplo, um contentor chamado test funciona com a imagem Nginx. A configuração chave é fornecida na secção de labels. Apenas precisa de copiar estas linhas para o seu ficheiro docker-compose.yml e substituir <XXXXXX>.stackhero-network.com pelo seu domínio de serviço.

Pode implantar o contentor usando:

docker context use <XXXXXX>.stackhero-network.com
docker-compose up

Após o arranque do contentor, visite https://<XXXXXX>.stackhero-network.com/ para ver a página "Welcome to nginx!".

Página de boas-vindas do NginxPágina de boas-vindas do Nginx

Se não vir a página de boas-vindas do Nginx, verifique o painel de controlo do Traefik para possíveis erros!

Com esta configuração, os pedidos HTTP enviados para <XXXXXX>.stackhero-network.com são roteados para o seu contentor test e o Traefik cria e gere automaticamente os certificados TLS para HTTPS.

No exemplo anterior, utilizámos o domínio padrão <XXXXXX>.stackhero-network.com. Na prática, provavelmente usará os seus próprios domínios de empresa ou projeto, como www.my-company.com ou api.my-project.io. A secção abaixo explica como configurar domínios personalizados.

Neste exemplo, irá configurar o domínio api.my-project.io. Substitua my-project.io por um domínio que possua e api pelo subdomínio desejado.

Primeiro, atualize as definições DNS do seu domínio para que api.my-project.io aponte para o seu domínio <XXXXXX>.stackhero-network.com.

  1. Inicie sessão no seu fornecedor de domínio e aceda à sua configuração DNS.
  2. Crie uma nova entrada chamada api (ou outro subdomínio à sua escolha), defina o seu tipo como CNAME e defina o seu destino como <XXXXXX>.stackhero-network.com.

Exemplo de configuração DNS na interface DNS do CloudflareExemplo de configuração DNS na interface DNS do Cloudflare

Uma vez configurado o DNS, pode validá-lo executando:

host api.my-project.io

Deverá ver uma resposta semelhante a:

api.my-project.io is an alias for <XXXXXX>.stackhero-network.com

A propagação DNS pode demorar até 24 horas, dependendo do seu fornecedor. Se o comando host não devolver a resposta esperada, aguarde e tente novamente mais tarde.

Em seguida, atualize o seu ficheiro docker-compose.yml com a seguinte configuração:

services:
  api:
    image: traefik/whoami
    hostname: api
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`api.my-project.io`)" # Adicione o seu domínio aqui
      - "traefik.http.services.api.loadbalancer.server.port=<PORT>" # Substitua "<PORT>" pelo porto em que a sua API está a ouvir
      - "traefik.http.routers.api.tls.certresolver=letsencrypt"

Não se esqueça de substituir api.my-project.io pelo seu domínio real no label Traefik Host.

Implante o seu contentor com:

docker-compose up -d

Depois visite https://api.my-project.io. Deverá ver uma página de texto exibindo o nome do host do seu contentor api.

Prova de que o Traefik está a gerir o nosso tráfego HTTP com encriptação TLS para o nosso novo domínioProva de que o Traefik está a gerir o nosso tráfego HTTP com encriptação TLS para o nosso novo domínio

Na primeira criação do contentor, os certificados TLS podem demorar alguns segundos a serem gerados. Se encontrar um erro TLS, aguarde alguns segundos e atualize a página para permitir tempo para os certificados serem gerados.

Parabéns, configurou agora o seu primeiro domínio personalizado!

Ao definir um URL de site como my-company.com, é uma boa ideia configurar também um subdomínio "www". Isto garante que os utilizadores que se conectam via www.my-company.com são redirecionados para o seu site principal e ajuda a evitar problemas de conteúdo duplicado.

No exemplo abaixo, tanto my-company.com como www.my-company.com são geridos. Os utilizadores que acedem a www.my-company.com são redirecionados para my-company.com:

services:
  frontend:
    image: traefik/whoami
    hostname: frontend
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=Host(`my-company.com`) || Host(`www.my-company.com`)" # Adicione ambos os domínios aqui
      - "traefik.http.services.frontend.loadbalancer.server.port=<PORT>" # Substitua "<PORT>" pelo porto em que o seu frontend está a ouvir
      - "traefik.http.routers.frontend.tls.certresolver=letsencrypt"

      # Redirecionar 'www.my-company.com' para 'my-company.com':
      - 'traefik.http.routers.frontend.middlewares=redirect-www'
      - "traefik.http.middlewares.redirect-www.redirectregex.regex=^https://www.my-company.com/(.*)"
      - "traefik.http.middlewares.redirect-www.redirectregex.replacement=https://my-company.com/$${1}"
      - "traefik.http.middlewares.redirect-www.redirectregex.permanent=true"

Suponha que tem um contentor dedicado ao seu site de documentação. Pode querer rotear https://my-company.com/docs para este contentor enquanto envia outros pedidos, como https://my-company.com/, para o seu contentor frontend. O exemplo abaixo mostra como isto é feito:

services:
  documentations:
    image: traefik/whoami
    hostname: documentations
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.documentations.rule=Host(`my-company.com`) && PathPrefix(`/docs`)" # Defina o prefixo do caminho aqui
      - "traefik.http.services.documentations.loadbalancer.server.port=<PORT>" # Substitua "<PORT>" pelo porto em que o seu contentor de documentação está a ouvir
      - "traefik.http.routers.documentations.tls.certresolver=letsencrypt"

  frontend:
    image: traefik/whoami
    hostname: frontend
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=Host(`my-company.com`) || Host(`www.my-company.com`)"
      - "traefik.http.services.frontend.loadbalancer.server.port=<PORT>" # Substitua "<PORT>" pelo porto em que o seu frontend está a ouvir
      - "traefik.http.routers.frontend.tls.certresolver=letsencrypt"

      # Redirecionar 'www.my-company.com' para 'my-company.com':
      - 'traefik.http.routers.frontend.middlewares=redirect-www'
      - "traefik.http.middlewares.redirect-www.redirectregex.regex=^https://www.my-company.com/(.*)"
      - "traefik.http.middlewares.redirect-www.redirectregex.replacement=https://my-company.com/$${1}"
      - "traefik.http.middlewares.redirect-www.redirectregex.permanent=true"

Agora, visitar https://my-company.com/docs (ou qualquer subcaminho como https://my-company.com/docs/something) exibirá conteúdo do contentor documentations. Outros caminhos, por exemplo https://my-company.com/help, serão servidos pelo contentor frontend.

Por padrão, o Traefik conecta-se à primeira porta exposta do contentor. Em alguns casos, pode precisar de especificar uma porta particular. O exemplo abaixo demonstra como definir uma porta personalizada:

services:
  frontend:
    image: traefik/whoami
    hostname: frontend
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=Host(`my-company.com`)"
      - "traefik.http.routers.frontend.tls.certresolver=letsencrypt"

      # Roteie o tráfego de 'https://my-company.com' para o contentor 'frontend' na porta 80
      - "traefik.http.services.frontend.loadbalancer.server.port=80"

Ao definir um contentor no seu ficheiro docker-compose.yml, alguns campos são importantes para melhor consistência e facilidade de gestão. Considere usar a seguinte configuração recomendada:

services:
  <CONTAINER_NAME>:
    image: traefik/whoami
    hostname: <CONTAINER_NAME>
    container_name: <CONTAINER_NAME>
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.<CONTAINER_NAME>.rule=Host(`my-company.com`)"
      - "traefik.http.routers.<CONTAINER_NAME>.tls.certresolver=letsencrypt"

Basta substituir <CONTAINER_NAME> pelo nome desejado para o seu contentor, por exemplo frontend.

Ao criar um subdomínio, o Let's Encrypt segue as RFC 952 e 1123, que permitem apenas caracteres no conjunto [a-zA-Z0-9-].

Embora os underscores ('_') sejam permitidos nos nomes de registos DNS, não são aceitáveis em nomes de host. Como resultado, o Let's Encrypt rejeita subdomínios como "my_subdomain.example.com" e exibe o erro "Domain name contains an invalid character".

Para resolver este problema, basta remover quaisquer underscores dos seus subdomínios.