Aqui estão as anotações sobre meus percalços e meu sucesso eventual em migrar uma instância do Discourse de Scaleway para um Raspberry Pi 4, com Cloudflare na frente.
Criei um backup da instância do Discourse da Scaleway, e ./launcher stop app, e desliguei a máquina.
Instalei o Ubuntu Server 23.10 em um SSD SATA conectado via USB alimentando o Raspberry Pi 4.
Instalei o LXD, criei um pool de armazenamento loopback btrfs de 100GiB.
Atualizei o perfil default para:
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: Perfil LXD padrão
devices:
eth0:
name: eth0
network: lxdbr0
type: nic
root:
path: /
pool: default
type: disk
name: default
Adicionei um perfil discourse com:
config:
limits.memory: 1GiB
limits.memory.enforce: soft
security.nesting: 'true'
description: Configuração para instâncias Discourse
devices: {}
name: discourse
Criei uma imagem mínima do Ubuntu 23.10 Server com esses perfis. Acessei-a através do seguinte em meu ~/.ssh/config:
Host LXD_DISCOURCE_INSTANCE
ProxyJump LXD_HOST
User REDACTED
IdentityFile ~/.ssh/REDACTED.pub
Segui as instruções de instalação na nuvem do Discourse e restaurei minha configuração do Discourse da instância 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" # não necessário com cloudflare
expose:
- "80:80"
- "443:443" # https
params:
db_default_text_search_config: "pg_catalog.english"
env:
LANG: en_US.UTF-8
## Configuração HTTPS para: templates/web.letsencrypt.ssl.template.yml
LETSENCRYPT_ACCOUNT_EMAIL: "redacted" # https
## O nome de domínio que esta instância Discourse responderá
DISCOURSE_HOSTNAME: "redacted"
## Lista de e-mails separados por vírgula que se tornarão administradores e desenvolvedores
## na primeira inscrição, exemplo 'user1@example.com,user2@example.com'
DISCOURSE_DEVELOPER_EMAILS: "redacted"
## O servidor de e-mail que esta instância Discourse usará
DISCOURSE_SMTP_ADDRESS: "redacted"
DISCOURSE_SMTP_PORT: redacted
DISCOURSE_SMTP_USER_NAME: "redacted"
DISCOURSE_SMTP_PASSWORD: "redacted"
#DISCOURSE_SMTP_DOMAIN: discourse.example.com # (requerido por alguns provedores)
#DISCOURSE_NOTIFICATION_EMAIL: nobody@discourse.example.com # (endereço para enviar notificações)
#DISCOURSE_MAXMIND_LICENSE_KEY: 1234567890123456
## Quaisquer comandos personalizados para executar após a compilação
run:
- exec: rails r "SiteSetting.contact_email='redacted'"
- exec: rails r "SiteSetting.notification_email='redacted'"
## O contêiner Docker é sem estado; todos os dados são armazenados em /shared
volumes:
- volume:
host: /var/discourse/shared/standalone
guest: /shared
- volume:
host: /var/discourse/shared/standalone/log/var-log
guest: /var/log
## Plugins
## 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
Antes de poder restaurar o backup, no entanto, precisei reconstruí-lo. Infelizmente, o pool de armazenamento btrfs travava na etapa do yarn install por horas e, eventualmente, expirava, com carga quase zero na máquina.
Pesquisando um pouco, decidi tentar usar um pool de armazenamento zfs em vez disso, o que avançou mais, mas ainda travava indefinidamente após Background saving terminated with sucess, com carga quase zero na máquina.
(Tenho capturas de tela, mas o upload delas aqui falha.)
Então decidi abandonar o LXD e tentar diretamente na instância do Ubuntu Server no Raspberry Pi 4.
Pela primeira vez, tive uma reconstrução bem-sucedida, no entanto, todas as tentativas de acessá-la redirecionavam para si mesma, em um loop de redirecionamento.
O loop de redirecionamento tinha duas causas…
Se eu tivesse o seguinte em minha configuração do Discourse:
expose:
- "8080:80"
- "8081:443" # https
Ele redirecionaria infinitamente, sempre querendo redirecionar para https://hostname.
Resolver isso foi voltar para:
expose:
- "80:80"
- "443:443" # https
Em segundo lugar, qualquer coisa acessada através do túnel Cloudflare também redirecionava infinitamente para si mesma. A causa, ao que parece, foi ter um túnel para HTTP e um túnel para HTTPS. Mudar o túnel para apenas HTTPS resolveu.
Outras coisas que fiz, mas neste ponto não tenho certeza se importaram:
- Removi o letsencrypt, pois usei um Certificado de Origem Cloudflare em vez disso.
- Configurei o
Origin Server Nameno túnel HTTPS para ser o hostname pretendido.
Coisas que podem ser melhoradas:
- O HTTPS da Origem para o Cloudflare poderia ser evitado se eu bloqueasse a máquina para permitir apenas conexões do Cloudflare e configurasse um túnel SSH. No entanto, não tenho certeza se o Discourse roda melhor tendo HTTPS em si mesmo (por exemplo, http2, etc.).
- Se o letsencrypt funciona com o túnel Cloudflare (não consegui testar, pois quando estava usando letsencrypt, estava recebendo os loops de redirecionamento).
Como depurei os loops de redirecionamento:
- Para depurar o loop de redirecionamento do Discourse: configurei
/etc/hostspara apontar meu hostname do Discourse diretamente para o endereço IP, então useicurl -k --head 'https://hostname:8081etc. para testar. - Para depurar o loop de redirecionamento do túnel Cloudflare: removi isso de
/etc/hostspara que o hostname fosse resolvido via DNS, então useicurl -k --head 'https://hostnameetc. para testar.
Há um monte de outras coisas úteis e aprendizados ao longo do caminho, no entanto, isso pode esperar.
Feedback para o Discourse:
- A reconstrução precisa ser mais clara sobre o que está fazendo. Muitas vezes há longos atrasos sem ação óbvia sendo realizada.
- Descobrir por que expor portas diferentes causaria um loop de redirecionamento.
- Desde que o letsencrypt se tornou uma coisa, a documentação sobre como especificar seu próprio certificado SSL é tediosa de descobrir. Além disso, parece que apenas um certificado pode ser usado, pois está fixo em
/var/discourse/shared/standalone/ssl/ssl.keyem vez de, digamos,/var/discourse/shared/standalone/ssl/CONTAINER_ID.key, por exemplo,/var/discourse/shared/standalone/ssl/app.key— o Cloudflare fornece certificados de origem, então esta é uma boa opção para usuários do Cloudflare. - Publicar um guia completo passo a passo para Cloudflare + Raspberry Pi 4 teria ajudado enormemente, atualmente tais guias delegam muita informação a terceiros que não têm conhecimento um do outro, e toda a complexidade e depuração está em como as diferentes partes funcionam juntas, não em como funcionam sozinhas.
Outros “algum dia” para fazer:
- Descobrir por que travava no LXD.
- Ver se funciona no LXD em um Raspberry Pi 5, ou no Multipass no macOS, ou LXD com o pool de armazenamento sendo uma partição/disco em vez de um arquivo loopback: pois assim não preciso desperdiçar uma máquina inteira com isso.
- Ver se consigo fazer com que o docker e o launcher não precisem de sudo.