Un'odissea migrando da Scaleway a Raspberry Pi 4

Ecco la documentazione dei miei errori e del mio successo finale nel migrare un’istanza di discourse da scaleway a un raspberry pi 4, con cloudflare davanti.

Ho creato un backup dall’istanza discourse di scaleway, e ./launcher stop app, e ho spento la macchina.

Ho installato Ubuntu Server 23.10 su un SSD SATA collegato tramite USB che alimenta il Raspberry Pi 4
Ho installato LXD, ho creato un pool di archiviazione loopback btrfs da 100GiB.
Ho aggiornato il profilo default a:

config:
  cloud-init.user-data: |
    #cloud-config
    ssh_pwauth: false
    package_update: true
    package_upgrade: true
    packages:
      - openssh-server
      - vim
      - git
      - rsync
    users:
      - name: root
        lock_passwd: true
        ssh_import_id: gh:balupton
description: Profilo LXD predefinito
devices:
  eth0:
    name: eth0
    network: lxdbr0
    type: nic
  root:
    path: /
    pool: default
    type: disk
name: default

Ho aggiunto un profilo discourse con:

config:
  limits.memory: 1GiB
  limits.memory.enforce: soft
  security.nesting: 'true'
description: Configurazione per le istanze Discourse
devices: {}
name: discourse

Ho creato un’immagine Ubuntu 23.10 Minimal Server con quei profili. Ci ho acceduto tramite quanto segue nel mio ~/.ssh/config:

Host LXD_DISCOURCE_INSTANCE
	ProxyJump LXD_HOST
	User REDACTED
	IdentityFile ~/.ssh/REDACTED.pub

Ho seguito le istruzioni di installazione cloud di discourse e ho ripristinato la mia configurazione di discourse dall’istanza scaleway:

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/cloudflare.template.yml"
  - "templates/web.ssl.template.yml"  # https
  - "templates/web.letsencrypt.ssl.template.yml"  # https
  # - "templates/web.ratelimited.template.yml" # non necessario con cloudflare

expose:
  - "80:80"
  - "443:443"  # https

params:
  db_default_text_search_config: "pg_catalog.english"

env:
  LANG: en_US.UTF-8

  ## Configurazione HTTPS per: templates/web.letsencrypt.ssl.template.yml
  LETSENCRYPT_ACCOUNT_EMAIL: "redacted"  # https

  ## Il nome del dominio a cui risponderà questa istanza Discourse
  DISCOURSE_HOSTNAME: "redacted"

  ## Elenco di email separate da virgola che diventeranno amministratori e sviluppatori
  ## alla prima registrazione, ad esempio 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: "redacted"

  ## Il server di posta che questa istanza Discourse utilizzerà
  DISCOURSE_SMTP_ADDRESS: "redacted"
  DISCOURSE_SMTP_PORT: redacted
  DISCOURSE_SMTP_USER_NAME: "redacted"
  DISCOURSE_SMTP_PASSWORD: "redacted"
  #DISCOURSE_SMTP_DOMAIN: discourse.example.com    # (richiesto da alcuni provider)
  #DISCOURSE_NOTIFICATION_EMAIL: nobody@discourse.example.com    # (indirizzo da cui inviare le notifiche)
  #DISCOURSE_MAXMIND_LICENSE_KEY: 1234567890123456

## Qualsiasi comando personalizzato da eseguire dopo la compilazione
run:
  - exec: rails r "SiteSetting.contact_email='redacted'"
  - exec: rails r "SiteSetting.notification_email='redacted'"

## Il container Docker è stateless; tutti i dati sono memorizzati in /shared
volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

## Plugin
## https://meta.discourse.org/t/19157
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/discourse-adplugin.git
          - git clone https://github.com/discourse/discourse-affiliate.git
          - git clone https://github.com/discourse/discourse-assign.git
          - git clone https://github.com/discourse/discourse-docs.git
          - git clone https://github.com/discourse/discourse-topic-voting.git
          - git clone https://github.com/discourse/discourse-github.git
          - git clone https://github.com/discourse/discourse-saved-searches.git
          - git clone https://github.com/discourse/discourse-shared-edits.git
          - git clone https://github.com/discourse/discourse-solved.git
          # - git clone https://github.com/discourse/discourse-encrypt.git
          # - git clone https://github.com/discourse/discourse-reactions.git
          # - git clone https://github.com/discourse/discourse-subscriptions.git

Prima di poter ripristinare il backup, tuttavia, ho dovuto ricostruirlo. Sfortunatamente, il pool di archiviazione btrfs si bloccava durante la fase di installazione di yarn per ore, e alla fine andava in timeout, con un carico quasi nullo sulla macchina.

Dopo aver letto un po’, ho deciso di provare a utilizzare un pool di archiviazione zfs invece, questo sarebbe andato più avanti, ma si sarebbe comunque bloccato indefinitamente dopo Background saving terminated with sucess, con un carico quasi nullo sulla macchina.

(Ho screenshot, tuttavia caricarli qui fallisce.)

Ho quindi deciso di abbandonare LXD e provarlo direttamente sull’istanza Ubuntu Server sul Raspberry Pi 4.
Per la prima volta ho avuto una ricostruzione riuscita, tuttavia tutti i tentativi di accedervi reindirizzavano a se stessa, in un loop di reindirizzamento.

Il loop di reindirizzamento aveva due cause…

Se avessi quanto segue nella mia configurazione di discourse:

expose:
  - "8080:80"
  - "8081:443"  # https

Si reindirizzava all’infinito, volendo sempre reindirizzare a https://hostname.
Risolvere questo è stato tornare a:

expose:
  - "80:80"
  - "443:443"  # https

In secondo luogo, qualsiasi cosa accessibile tramite il tunnel cloudflare reindirizzava all’infinito a se stessa. La causa si è rivelata essere avere un tunnel sia per HTTP che per HTTPS. Cambiare il tunnel solo a HTTPS lo ha risolto.

Altre cose che ho fatto ma a questo punto non sono sicuro se abbiano avuto importanza:

  1. Ho rimosso letsencrypt poiché ho utilizzato invece un certificato di origine Cloudflare.
  2. Ho configurato il Origin Server Name nel tunnel HTTPS sull’hostname desiderato.

Cose che potrebbero essere migliorate:

  1. L’HTTPS dall’origine a Cloudflare potrebbe essere evitato se bloccassi la macchina per consentire solo connessioni da Cloudflare e impostassi un tunnel SSH. Tuttavia, non sono sicuro se Discourse funzioni meglio avendo HTTPS su se stesso (ad esempio, http2, ecc.).
  2. Se letsencrypt funzioni con il tunnel cloudflare (non sono riuscito a testarlo poiché quando usavo letsencrypt ottenevo loop di reindirizzamento).

Come ho eseguito il debug dei loop di reindirizzamento:

  • Per eseguire il debug del loop di reindirizzamento di discourse: ho impostato /etc/hosts per puntare il mio hostname discourse direttamente all’indirizzo IP, quindi ho usato curl -k --head 'https://hostname:8081 ecc. per testarlo.
  • Per eseguire il debug del loop di reindirizzamento del tunnel cloudflare: ho rimosso quello da /etc/hosts in modo che l’hostname venga risolto tramite DNS, quindi ho usato curl -k --head 'https://hostname ecc. per testarlo.

Ci sono un sacco di altre cose utili e apprendimenti lungo il percorso, tuttavia questo può aspettare.

Feedback per discourse:

  • La ricostruzione deve essere più chiara su ciò che sta facendo. Troppo spesso ci sono lunghi ritardi senza azioni ovvie in corso.
  • Scoprire perché l’esposizione di porte diverse causerebbe un loop di reindirizzamento.
  • Poiché letsencrypt è diventato una cosa, la documentazione su come specificare il proprio certificato SSL è noiosa da scoprire. Inoltre, sembra che possa essere utilizzato solo un certificato poiché è fissato a /var/discourse/shared/standalone/ssl/ssl.key invece di, ad esempio, /var/discourse/shared/standalone/ssl/CONTAINER_ID.key, ad esempio /var/discourse/shared/standalone/ssl/app.key - cloudflare fornisce certificati di origine, quindi questa è una buona opzione per gli utenti cloudflare.
  • La pubblicazione di una guida completa passo dopo passo per cloudflare + raspberry pi 4 avrebbe aiutato enormemente, attualmente tali guide delegano troppe informazioni a terze parti che non hanno consapevolezza reciproca, e tutta la complessità e il debug risiedono nel modo in cui le diverse parti funzionano insieme, non in come funzionano da sole.

Altri todo per un giorno:

  • Scoprire perché si bloccava in LXD.
  • Vedere se funziona in LXD su un Raspberry Pi 5, o su Multipass su macOS, o LXD con il pool di archiviazione che è una partizione/disco invece di un file di loopback: poiché allora non devo sprecare un’intera macchina per questo.
  • Vedere se posso far sì che docker e launcher non richiedano sudo.
3 Mi Piace