여기에는 Scaleway에서 Raspberry Pi 4로 Discourse 인스턴스를 마이그레이션하는 과정에서 겪은 실패와 최종적인 성공, 그리고 Cloudflare를 프록시로 사용하는 방법에 대한 문서가 담겨 있습니다.
Scaleway의 Discourse 인스턴스에서 백업을 생성하고 ./launcher stop app을 실행한 뒤, 머신을 종료했습니다.
Raspberry Pi 4에 전원을 공급하는 USB로 연결된 SATA SSD에 Ubuntu Server 23.10을 설치했습니다.
LXD를 설치하고 100GiB btrfs 루프백 스토리지 풀을 생성했습니다.
default 프로필을 다음과 같이 업데이트했습니다:
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: Default LXD profile
devices:
eth0:
name: eth0
network: lxdbr0
type: nic
root:
path: /
pool: default
type: disk
name: default
다음과 같은 discourse 프로필을 추가했습니다:
config:
limits.memory: 1GiB
limits.memory.enforce: soft
security.nesting: 'true'
description: Configuration for Discourse instances
devices: {}
name: discourse
이러한 프로필을 사용하여 Ubuntu 23.10 Minimal Server 이미지를 생성했습니다. ~/.ssh/config에 다음 내용을 추가하여 해당 인스턴스에 접근했습니다:
Host LXD_DISCOURCE_INSTANCE
ProxyJump LXD_HOST
User REDACTED
IdentityFile ~/.ssh/REDACTED.pub
Discourse 클라우드 설치 지침을 따랐고, Scaleway 인스턴스에서 Discourse 구성을 복원했습니다:
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" # not needed with cloudflare
expose:
- "80:80"
- "443:443" # https
params:
db_default_text_search_config: "pg_catalog.english"
env:
LANG: en_US.UTF-8
## HTTPS configuration for: templates/web.letsencrypt.ssl.template.yml
LETSENCRYPT_ACCOUNT_EMAIL: "redacted" # https
## The domain name this Discourse instance will respond to
DISCOURSE_HOSTNAME: "redacted"
## List of comma delimited emails that will be made admin and developer
## on initial signup example 'user1@example.com,user2@example.com'
DISCOURSE_DEVELOPER_EMAILS: "redacted"
## The mailserver this Discourse instance will use
DISCOURSE_SMTP_ADDRESS: "redacted"
DISCOURSE_SMTP_PORT: redacted
DISCOURSE_SMTP_USER_NAME: "redacted"
DISCOURSE_SMTP_PASSWORD: "redacted"
#DISCOURSE_SMTP_DOMAIN: discourse.example.com # (required by some providers)
#DISCOURSE_NOTIFICATION_EMAIL: nobody@discourse.example.com # (address to send notifications from)
#DISCOURSE_MAXMIND_LICENSE_KEY: 1234567890123456
## Any custom commands to run after building
run:
- exec: rails r "SiteSetting.contact_email='redacted'"
- exec: rails r "SiteSetting.notification_email='redacted'"
## The Docker container is stateless; all data is stored in /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
그러나 백업을 복원하기 전에 먼저 이를 재빌드해야 했습니다. 안타깝게도 btrfs 스토리지 풀은 yarn install 단계에서 몇 시간 동안 멈춰 있었고, 결국 타임아웃이 발생했으며 머신의 부하가 거의 제로 상태였습니다.
조사를 해본 후, 대신 zfs 스토리지 풀을 사용해 보기로 했습니다. 이렇게 하면 더 진행되었지만, Background saving terminated with sucess 이후에도 여전히 무한정 멈추었으며, 머신의 부하는 여전히 거의 제로였습니다.
(스크린샷은 가지고 있지만, 여기에 업로드하는 데 실패했습니다.)
그 후 저는 LXD를 포기하고 Raspberry Pi 4의 Ubuntu Server 인스턴스에서 직접 실행해 보기로 했습니다.
처음으로 재빌드에 성공했지만, 접근을 시도하는 모든 시도는 자체로 리다이렉트되어 리다이렉트 루프에 빠졌습니다.
리다이렉트 루프에는 두 가지 원인이 있었습니다…
Discourse 구성에 다음 내용이 있었다면:
expose:
- "8080:80"
- "8081:443" # https
끝없이 리다이렉트되어 항상 https://hostname으로 리다이렉트하려고 했습니다.
이를 해결하려면 다음으로 돌아가야 했습니다:
expose:
- "80:80"
- "443:443" # https
둘째로, Cloudflare 터널을 통해 접근하는 모든 것도 자체로 끝없이 리다이렉트되었습니다. 원인은 HTTP 터널과 HTTPS 터널을 모두 가지고 있었던 것이었습니다. 터널을 HTTPS로만 변경하면 해결되었습니다.
이 시점에서 중요했는지 불확실하지만 제가 수행한 다른 작업들:
- Letsencrypt를 제거하고 대신 Cloudflare Origin Certificate을 사용했습니다.
- HTTPS 터널의
Origin Sever Name을 의도된 호스트명으로 구성했습니다.
향후 개선할 수 있는 사항:
- 머신을 Cloudflare에서만 연결을 허용하도록 제한하고 SSH 터널을 설정하면, Origin에서 Cloudflare로의 HTTPS를 피할 수 있습니다. 그러나 Discourse가 자체적으로 HTTPS(예: http2 등)를 가질 때 더 잘 작동하는지 확신이 서지 않습니다.
- Letsencrypt가 Cloudflare 터널과 함께 작동하는지 여부 (Letsencrypt를 사용했을 때 리다이렉트 루프가 발생했기 때문에 테스트할 수 없었습니다).
리다이렉트 루프를 디버깅한 방법:
- Discourse 리다이렉트 루프 디버깅:
/etc/hosts를 설정하여 Discourse 호스트명을 IP 주소로 직접 가리키도록 하고,curl -k --head 'https://hostname:8081등을 사용하여 테스트했습니다. - Cloudflare 터널 리다이렉트 루프 디버깅:
/etc/hosts에서 해당 항목을 제거하여 호스트명이 DNS를 통해 해석되도록 한 후,curl -k --head 'https://hostname등을 사용하여 테스트했습니다.
그 외에도 여정 중에 발견한 유용한 것들과 학습 사항들이 많이 있지만, 이는 나중에 다룰 수 있습니다.
Discourse에 대한 피드백:
- 재빌드 과정이 무엇을 하고 있는지 더 명확하게 설명해야 합니다. 너무 자주 눈에 띄는 작업 없이 긴 지연이 발생합니다.
- 다른 포트를 노출하는 것이 리다이렉트 루프를 일으키는 이유를 찾아야 합니다.
- Letsencrypt가 등장한 이후, 자신의 SSL 인증서를 지정하는 방법에 대한 문서를 찾는 것이 번거롭습니다. 또한
/var/discourse/shared/standalone/ssl/ssl.key에 고정되어 있어 하나의 인증서만 사용할 수 있는 것 같습니다. 예를 들어/var/discourse/shared/standalone/ssl/CONTAINER_ID.key(예:/var/discourse/shared/standalone/ssl/app.key)와 같이 변경할 수 있다면, Cloudflare는 Origin 인증서를 제공하므로 Cloudflare 사용자에게 좋은 옵션이 될 것입니다. - Cloudflare + Raspberry Pi 4를 위한 포괄적이고 단계별이며 모든 것을 포함하는 가이드를 게시했으면 도움이 되었을 것입니다. 현재 이러한 가이드는 서로 인식이 없는 제3자에게 너무 많은 정보를 위임하고 있으며, 모든 복잡성과 디버깅은 각 부분이 단독으로 작동하는 방식이 아니라 서로 어떻게 함께 작동하는지에 있습니다.
나중에 해야 할 일 목록:
- LXD에서 멈추는 이유를 찾아보기
- Raspberry Pi 5의 LXD, macOS의 Multipass, 또는 스토리지 풀이 루프백 파일이 아닌 파티션/드라이브인 LXD에서 작동하는지 확인: 이렇게 하면 전체 머신을 이 용도로 낭비하지 않아도 됩니다.
- Docker와 launcher가 sudo를 필요로 하지 않도록 할 수 있는지 확인