Esecuzione di 3 container per 3 domini in un'unica installazione con SSL

dopo aver sperimentato con Let’s Encrypt per diversi giorni, ecco una guida per chiunque sia interessato a eseguire più istanze.

1 Scopo

Supponiamo di avere un dominio come questo e di voler gestire 3 forum:

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

2. Contenitore Discourse

2.1 Preparazione

Dovresti avere 3 file app.yml per i tuoi diversi forum, chiamati come desideri, ad esempio:
bbs.yml, cp.yml, ytb.yml.
Il contenuto dovrebbe essere simile a questo:
Si prega di notare che utilizziamo un socket Unix per connettere nginx esterno e il contenitore backend Discourse invece di ascoltare le porte 80 e 443, rimuovendo anche la configurazione SSL per il contenitore backend.
Si prega di notare che qui abbiamo un solo contenitore per ogni forum, non contenitori separati per dati e web per ciascun forum.

## Questo è il modello di container Docker Discourse standalone "all-in-one"
##
## Dopo aver apportato modifiche a questo file, DEVI ricostruire
## /var/discourse/launcher rebuild app
##
## FAI *MOLTA* ATTENZIONE DURANTE LA MODIFICA!
## I FILE YAML SONO MOLTO, MOLTO SENSIBILI A ERRORI NELLO SPAZIATURA O NELL'ALLINEAMENTO!
## Visita http://www.yamllint.com/ per validare questo file se necessario

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Scommenta queste due righe se desideri aggiungere Let's Encrypt (https)
#  - "templates/web.ssl.template.yml"
#  - "templates/web.letsencrypt.ssl.template.yml"
  - "templates/web.socketed.template.yml"  # <-- Aggiunto
## Quali porte TCP/IP dovrebbe esporre questo contenitore?
## Se desideri che Discourse condivida una porta con un altro server web come Apache o nginx,
## consulta https://meta.discourse.org/t/17247 per i dettagli
**#expose:**
**#  - "8080:80"   # http**
**#  - "8443:443" # https**

params:
  db_default_text_search_config: "pg_catalog.english"

  ## Imposta db_shared_buffers a un massimo del 25% della memoria totale.
  ## verrà impostato automaticamente dal bootstrap in base alla RAM rilevata, oppure puoi sovrascriverlo
  db_shared_buffers: "256MB"

  ## può migliorare le prestazioni di ordinamento, ma aumenta l'utilizzo della memoria per connessione
  #db_work_mem: "40MB"

  ## Quale revisione Git dovrebbe utilizzare questo contenitore? (predefinito: tests-passed)
  #version: tests-passed

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

  ## Quante richieste web contemporanee sono supportate? Dipende dalla memoria e dai core CPU.
  ## verrà impostato automaticamente dal bootstrap in base alle CPU rilevate, oppure puoi sovrascriverlo
  UNICORN_WORKERS: 4

  ## TODO: Il nome di dominio a cui risponderà questa istanza Discourse
  ## Obbligatorio. Discourse non funzionerà con un semplice numero IP.
  DISCOURSE_HOSTNAME: bbs.antivte.com

  ## Scommenta se desideri che il contenitore venga avviato con lo stesso
  ## nome host (opzione -h) specificato sopra (predefinito "$hostname-$config")
  #DOCKER_USE_HOSTNAME: true

  ## TODO: Elenco di email separate da virgola che diventeranno amministratori e sviluppatori
  ## al primo esempio di registrazione 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'techempower@163.com'

  ## TODO: Il server SMTP utilizzato per validare nuovi account e inviare notifiche
  ## INDIRIZZO SMTP, nome utente e password sono obbligatori
  ## ATTENZIONE: il carattere '#' nella password SMTP può causare problemi!
  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           # (opzionale, predefinito true)

  ## Se hai aggiunto il modello Let's Encrypt, scommenta qui sotto per ottenere un certificato SSL gratuito
  LETSENCRYPT_ACCOUNT_EMAIL: techempower@163.com

  ## L'indirizzo CDN http o https per questa istanza Discourse (configurato per il recupero)
  ## consulta https://meta.discourse.org/t/14857 per i dettagli
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com

## Il contenitore Docker è senza stato; tutti i dati sono archiviati in /shared
volumes:
  - volume:
      **host: /var/discourse/shared/bbs**
      guest: /shared
  - volume:
      **host: /var/discourse/shared/bbs/log/var-log**
      guest: /var/log

## I plugin vanno qui
## consulta https://meta.discourse.org/t/19157 per i dettagli
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

## Qualsiasi comando personalizzato da eseguire dopo la costruzione
run:
  - exec: echo "Inizio dei comandi personalizzati"
  ## Se desideri impostare l'indirizzo email 'Da' per la tua prima registrazione, scommenta e modifica:
  ## Dopo aver ricevuto la prima email di registrazione, riscommenta la riga. Deve essere eseguita solo una volta.
  #- exec: rails r "SiteSetting.notification_email='info@unconfigured.discourse.org'"
  - exec: echo "Fine dei comandi personalizzati"

2.2 Configurazione

Crea uno script di configurazione come questo:

#!/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

Se hai già utilizzato questo script e hai già avviato ogni contenitore una volta, dopo aver modificato qualsiasi contenuto di app.yml, dovrai eseguire il rebuild del contenitore per applicare le modifiche:

./launcher rebuild bbs

2.3 Verifica

Vedrai 3 contenitori in esecuzione con successo e potrai verificare se il socket Unix funziona correttamente:

root@docker-s-1vcpu-2gb-sgp1-01:~# docker ps
CONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS              PORTS               NAMES
9702f94ea9b4        local_discourse/bbs   "/sbin/boot"        9 hours ago         Up 9 hours                              bbs
dc13c303c38e        local_discourse/cp    "/sbin/boot"        9 hours ago         Up 9 hours                              cp
dafa592ee16f        local_discourse/ytb   "/sbin/boot"        9 hours ago         Up 9 hours                              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 esterno

Installa nginx/OpenResty sul tuo server e crea un file nginx.conf in qualsiasi directory desideri (la directory influisce solo sul comando di avvio di nginx). Il contenuto di nginx.conf è simile a questo:
Qui ho un certificato e una chiave di Cloudflare che ho inserito nella directory host come segue:

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;

In futuro ti spiegherò come utilizzare certificati e chiavi automatici di Let’s Encrypt in nginx esterno.

events {
  worker_connections 1024;
}

http {
  # Il dizionario condiviso "auto_ssl" deve essere definito con spazio di archiviazione sufficiente
  # per contenere i dati del certificato. 1 MB di archiviazione contiene certificati per
  # circa 100 domini separati.
  lua_shared_dict auto_ssl 1m;
  # Il dizionario condiviso "auto_ssl_settings" viene utilizzato per memorizzare temporaneamente varie impostazioni
  # come il segreto utilizzato dal server hook sulla porta 8999. Non modificarlo o ometterlo.
  lua_shared_dict auto_ssl_settings 64k;

  # Deve essere definito un risolutore DNS per il funzionamento dell'OCSP stapling.
  #
  # Questo esempio utilizza il server DNS di Google. Potresti voler utilizzare i server DNS
  # predefiniti del tuo sistema, che si trovano in /etc/resolv.conf. Se la tua rete
  # non è compatibile con IPv6, potresti voler disabilitare i risultati IPv6 utilizzando
  # il flag "ipv6=off" (ad esempio "resolver 8.8.8.8 ipv6=off").
  resolver 8.8.8.8;
server {
    listen 80; listen [::]:80;
    server_name bbs.antivte.com;  # <-- modifica questo

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

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

    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; # aumentato da 3m predefinito

    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;  # <-- modifica questo

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

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

    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; # aumentato da 3m predefinito

    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;  # <-- modifica questo

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

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

    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; # aumentato da 3m predefinito

    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 Avvio di nginx

Vai alla directory in cui hai posizionato il tuo nginx.conf ed esegui questo comando:

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

4. Otterrai ciò che desideri

Woola

Sembra una versione più complessa del già esistente multisito di Discourse. Mi chiedo quale vantaggio offra l’esecuzione di tre container separati che competono per le stesse risorse invece di eseguirne uno solo (o due, nel caso in cui web e dati siano separati).

È tutto legato al budget. Puoi considerarlo una fase preliminare quando esegui test di prototipazione con i tuoi clienti e il marketing.

Anche nella fase di budgeting, penso che una soluzione multisito sia un’implementazione molto più semplice e pratica. In questo modo, avrò meno cose di cui preoccuparmi dal punto di vista della configurazione e più tempo da dedicare ai requisiti del cliente.

Tutto questo è dovuto al fatto che voglio eseguire test e produzione sul mio server singolo; se le porte 80 e 443 dell’host sono occupate, non ho altre possibilità per realizzare tutto questo.

Grazie mille per la condivisione, ma ritengo che servano molti più dettagli prima che diventi una #guida, quindi la ricategorizzo.