Executando 3 containers para 3 domínios em uma única instalação com SSL

Depois de mexer com o Let’s Encrypt por vários dias, aqui está para quem estiver interessado em rodar múltiplos

1 Objetivo

Suponha que você tenha um domínio assim e queira 3 fóruns:

bbs.antivte.com
cp.antivte.com
ytb.antivte.com

2. Contêiner Discourse

2.1 Preparação

Você deve ter 3 arquivos app.yml para seus fóruns diferentes, nomeados como
bbs.yml, cp.yml, ytb.yml, conforme preferir.
O conteúdo deve ser assim:
Por favor, note que usamos um socket Unix para conectar o nginx externo e o contêiner backend do Discourse, em vez de ouvir as portas 80 e 443, e também removemos a configuração SSL do contêiner backend.
Por favor, note também que aqui temos apenas um contêiner para cada fórum, não contêineres separados de dados e web para cada fórum.

## Este é o modelo de contêiner Docker Discourse tudo-em-um, autônomo
##
## Após fazer alterações neste arquivo, você DEVE reconstruir
## /var/discourse/launcher rebuild app
##
## TENHA *MUITO* CUIDADO AO EDITAR!
## ARQUIVOS YAML SÃO SUPER SUPER SENSÍVEIS A ERROS DE ESPAÇAMENTO OU ALINHAMENTO!
## visite http://www.yamllint.com/ para validar este arquivo conforme necessário

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Descomente estas duas linhas se desejar adicionar o Let's Encrypt (https)
#  - "templates/web.ssl.template.yml"
#  - "templates/web.letsencrypt.ssl.template.yml"
  - "templates/web.socketed.template.yml"  # <-- Adicionado
## Quais portas TCP/IP este contêiner deve expor?
## Se você quiser que o Discourse compartilhe uma porta com outro servidor web como Apache ou nginx,
## consulte https://meta.discourse.org/t/17247 para detalhes
**#expose:**
**#  - "8080:80"   # http**
**#  - "8443:443" # https**

params:
  db_default_text_search_config: "pg_catalog.english"

  ## Defina db_shared_buffers para no máximo 25% da memória total.
  ## será definido automaticamente pelo bootstrap com base na RAM detectada, ou você pode sobrescrever
  db_shared_buffers: "256MB"

  ## pode melhorar o desempenho de classificação, mas adiciona uso de memória por conexão
  #db_work_mem: "40MB"

  ## Qual revisão do Git este contêiner deve usar? (padrão: tests-passed)
  #version: tests-passed

env:
  LANG: en_US.UTF-8
  # DISCOURSE_DEFAULT_LOCALE: en

  ## Quantas requisições web concorrentes são suportadas? Depende de memória e núcleos de CPU.
  ## será definido automaticamente pelo bootstrap com base nas CPUs detectadas, ou você pode sobrescrever
  UNICORN_WORKERS: 4

  ## TODO: O nome de domínio ao qual esta instância do Discourse responderá
  ## Obrigatório. O Discourse não funcionará com um número IP puro.
  DISCOURSE_HOSTNAME: bbs.antivte.com

  ## Descomente se quiser que o contêiner seja iniciado com o mesmo
  ## nome de hostname (-h option) especificado acima (padrão "$hostname-$config")
  #DOCKER_USE_HOSTNAME: true

  ## TODO: Lista de e-mails separados por vírgula que serão definidos como admin e developer
  ## no cadastro inicial, exemplo 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'techempower@163.com'

  ## TODO: O servidor SMTP usado para validar novas contas e enviar notificações
  ## Endereço SMTP, nome de usuário e senha são obrigatórios
  ## AVISO: o caractere '#' na senha SMTP pode causar problemas!
  DISCOURSE_SMTP_ADDRESS: smtp.mailgun.org
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: postmaster@mail.antivte.com
  DISCOURSE_SMTP_PASSWORD: "67c9458eb7a6ff11b4db70f097b1b5c3-f7910792-0e7dbcc9"
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (opcional, padrão true)

  ## Se você adicionou o modelo Let's Encrypt, descomente abaixo para obter um certificado SSL gratuito
  LETSENCRYPT_ACCOUNT_EMAIL: techempower@163.com

  ## O endereço CDN http ou https para esta instância do Discourse (configurado para buscar)
  ## consulte https://meta.discourse.org/t/14857 para detalhes
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com

## O contêiner Docker é sem estado; todos os dados são armazenados em /shared
volumes:
  - volume:
      **host: /var/discourse/shared/bbs**
      guest: /shared
  - volume:
      **host: /var/discourse/shared/bbs/log/var-log**
      guest: /var/log

## Plugins vão aqui
## consulte https://meta.discourse.org/t/19157 para detalhes
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/procourse/procourse-installer

## Quaisquer comandos personalizados para executar após a construção
run:
  - exec: echo "Início dos comandos personalizados"
  ## Se quiser definir o endereço de e-mail 'De' para seu primeiro cadastro, descomente e altere:
  ## Após receber o primeiro e-mail de cadastro, comente novamente a linha. Só precisa rodar uma vez.
  #- exec: rails r "SiteSetting.notification_email='info@unconfigured.discourse.org'"
  - exec: echo "Fim dos comandos personalizados"

2.2 Configuração

Tenha um script de setup assim:

#!/usr/bin/env bash
./launcher bootstrap bbs
./launcher bootstrap test
./launcher bootstrap cp
./launcher bootstrap ytb
./launcher start bbs
./launcher  start test
./launcher  start  cp
./launcher  start  ytb

E se você já usou este script e executou cada contêiner uma vez, se editar qualquer conteúdo do app.yml, deve usar o seguinte para que o contêiner entre em vigor:

./launcher rebuild bbs

2.3 Verificação

Você verá 3 contêineres rodando com sucesso e poderá verificar se o socket Unix está OK:

root@docker-s-1vcpu-2gb-sgp1-01:~# docker ps
CONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS              PORTS               NAMES
9702f94ea9b4        local_discourse/bbs   "/sbin/boot"        9 horas atrás         Up 9 horas                              bbs
dc13c303c38e        local_discourse/cp    "/sbin/boot"        9 horas atrás         Up 9 horas                              cp
dafa592ee16f        local_discourse/ytb   "/sbin/boot"        9 horas atrás         Up 9 horas                              ytb
root@docker-s-1vcpu-2gb-sgp1-01:~#  curl --unix-socket /var/discourse/shared/bbs/nginx.http.sock http:/images/json
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <title>血栓之家</title>

3. Nginx externo

Instale o nginx/openresty no seu servidor e crie um nginx.conf em qualquer diretório que preferir. O diretório afeta apenas o comando de execução do nginx. O conteúdo do nginx.conf é assim:
Aqui eu tenho um certificado e chave do Cloudflare e os coloquei no diretório do host assim:

3.1 nginx.conf

    ssl_certificate      /var/discourse/shared/ssl/antivte.com.cert.pem;
    ssl_certificate_key  /var/discourse/shared/ssl/antivte.com.key.pem;

No futuro, vou ensinar como usar certificados e chaves automáticos do Let’s Encrypt no nginx externo.

events {
  worker_connections 1024;
}

http {
  # O dicionário compartilhado "auto_ssl" deve ser definido com espaço de armazenamento suficiente para
  # armazenar os dados do seu certificado. 1 MB de armazenamento comporta certificados para
  # aproximadamente 100 domínios separados.
  lua_shared_dict auto_ssl 1m;
  # O dicionário compartilhado "auto_ssl_settings" é usado para armazenar temporariamente várias configurações
  # como o segredo usado pelo servidor de hook na porta 8999. Não altere nem
  # omita isso.
  lua_shared_dict auto_ssl_settings 64k;

  # Um resolvedor de DNS deve ser definido para que o OCSP stapling funcione.
  #
  # Este exemplo usa o servidor DNS do Google. Você pode querer usar os servidores DNS
  # padrão do seu sistema, que podem ser encontrados em /etc/resolv.conf. Se sua rede
  # não for compatível com IPv6, talvez queira desativar resultados IPv6 usando a
  # flag "ipv6=off" (como "resolver 8.8.8.8 ipv6=off").
  resolver 8.8.8.8;
server {
    listen 80; listen [::]:80;
    server_name bbs.antivte.com;  # <-- altere isso

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;  listen [::]:443 ssl http2;
    server_name bbs.antivte.com;  # <-- altere isso

    ssl_certificate      /var/discourse/shared/ssl/antivte.com.cert.pem;
    ssl_certificate_key  /var/discourse/shared/ssl/antivte.com.key.pem;
#    ssl_dhparam          /var/discourse/shared/standalone/ssl/dhparams.pem;
    ssl_session_tickets off;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;

    http2_idle_timeout 5m; # aumentado do padrão de 3m

    location / {
        proxy_pass http://unix:/var/discourse/shared/bbs/nginx.http.sock;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

server {
    listen 80; listen [::]:80;
    server_name cp.antivte.com;  # <-- altere isso

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;  listen [::]:443 ssl http2;
    server_name cp.antivte.com;  # <-- altere isso

    ssl_certificate      /var/discourse/shared/ssl/antivte.com.cert.pem;
    ssl_certificate_key  /var/discourse/shared/ssl/antivte.com.key.pem;
#    ssl_dhparam          /var/discourse/shared/standalone/ssl/dhparams.pem;
    ssl_session_tickets off;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;

    http2_idle_timeout 5m; # aumentado do padrão de 3m

    location / {
        proxy_pass http://unix:/var/discourse/shared/cp/nginx.http.sock;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP $remote_addr;
    }
}



server {
    listen 80; listen [::]:80;
    server_name ytb.antivte.com;  # <-- altere isso

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;  listen [::]:443 ssl http2;
    server_name ytb.antivte.com;  # <-- altere isso

    ssl_certificate      /var/discourse/shared/ssl/antivte.com.cert.pem;
    ssl_certificate_key  /var/discourse/shared/ssl/antivte.com.key.pem;
#    ssl_dhparam          /var/discourse/shared/standalone/ssl/dhparams.pem;
    ssl_session_tickets off;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;

    http2_idle_timeout 5m; # aumentado do padrão de 3m

    location / {
        proxy_pass http://unix:/var/discourse/shared/ytb/nginx.http.sock;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP $remote_addr;
    }
}



}

3.2 Iniciar nginx

Vá para o diretório onde você colocou seu nginx.conf e execute:

 nginx -p `pwd`/ -c nginx.conf

4. Você terá o que deseja então

Uhuu!

Isso parece ser uma versão mais complicada do já existente Discourse Multisite. Gostaria de saber qual vantagem há em executar três containers separados todos competindo pelos mesmos recursos, em vez de executar apenas um (ou dois, no caso de web e dados separados) ??

Isso é tudo uma questão de orçamento. E você pode considerar isso como uma etapa preliminar ao executar o protótipo, testando seus clientes e marketing.

Mesmo na fase de orçamento, eu acho que o multisite será uma implementação muito mais simples e prática. Assim, tenho menos coisas para me preocupar em termos de configuração e mais tempo para dedicar aos requisitos do cliente.

Tudo isso é devido ao fato de eu querer realizar testes e produção no meu servidor solo. Se as portas 80 e 443 do host já estão ocupadas, não tenho outra maneira de realizar tudo isso.

Muito obrigado por compartilhar, mas sinto que isso precisa de muito mais detalhe antes de se tornar um howto, recategorizando.