Go Discourse Forums Deployment

這兩天在試著部署 Discourse -目前主流的 forum open porject。雖然它有 cloud 版本可以用,不過因為基於一些因素,我們打算先自行部署,給團隊成員實際試用測試過後,再來看是否要使用官方提供的 Cloud 付費版本。而在部署的過程中發現蠻多問題的,所以也一併記錄下來,給想要自行部署的人參考。

Discourse Container

首先在 Discourse container 部份,我們選擇由 bitnami 所維護的 bitnami/discourse 版本,原因是它提供相對完整的 configuration 和流程,事後證明這個選擇也是對的,因為我有嘗試使用 Discourse 所提供的 container 來部署,結果根本是場災難,畢竟官方所提供的 container 本來就不適合用在自行部署,除了流程複雜之外,size 也大許多 (2.5GB)。

Version

Discourse 版本部分,目前 (2020/04) 官方是 2.5.0 beta,而 bitnami/discourse 則是 2.4.1 也就是官方所提供的最新穩定版本,因此不會有太大的版本落差。

部署環境

我們使用 GKE 作為部署環境,雖然只是供測試使用,可以簡單起個 GCE 就可以,但因為流程完整性,因此還是決定部署在 GKE 上。

最低資源限制

Discourse 安裝建議的硬體條件:

  • modern single core CPU, dual core recommended
  • 1 GB RAM minimum (with swap)
  • 64 bit Linux compatible with Docker
  • 10 GB disk space minimum

Discourse 本身分成四個部分:

  1. database (postgres)
  2. cache (redis)
  3. web server
  4. sidekiq

因為最基本就要起 4 個 pod (我習慣 1 個 container 就用 1 個 pod),因此最少要 2 個 n1-standard 等級的 node 或是更高等級的機器。

Configuration

1. Cache

2. Database

Discourse 的 cache 是採用 redis,而 database 則是 postgres,這兩個是蠻常見的部屬,所以就不多作說明。

3. Web Server

接著部屬 Discourse 的 Web server,在 bitnami/discourse 有提供完整的 configuration 參數,可以自行查閱,以下特別提醒幾個需要注意的參數項目。

Default user name and password for admin account

DISCOURSE_USERNAME=user
DISCOURSE_PASSWORD=bitnami123
DISCOURSE_EMAIL=user@example.com

這三個參數主要是設定 admin 的帳號密碼,後續可以用這組帳號透過 Web 介面登入來修改 Discourse 的配置和樣式,所以一定要記得設定。

Hostname

DISCOURSE_HOSTNAME=www.example.com

另外 hostname 也是必須要修改的參數,由於 asset URL 會根據 hostname 而有所不同 (簡單來說就是 Discourse 在 compile asset file 的時候,會連同 hostname 一起 compile,並且產出最後的 permalink) ,如果 hostname 設錯了,那麼 browser 就無法根據路徑正確地取回所需的資源,例如 js、 css 等。

另外,官方有建議最好 hostname 是 domain name,不要是 IP address,以免出現異常錯誤。

SKIP INSTALL (注意!)

DISCOURSE_SKIP_INSTALL=no

這個參數是部屬 Discourse 的其中一個最重要數值。 Discourse container 在 run 時,由於 default 參數是 no,因此會進行建置 database 流程,此時如果你的database 已經被建立好,那程式就會直接報錯並且中止執行

這意味著,當 Discourse 的 pod 因為不明原因中止,而 k8s 重新起一個的時候,此時沒有設定 DISCOURSE_SKIP_INSTALL=yes,那 pod 就永遠無法起來啦。這也變成首次部屬 Discourse 時候參數會需要調整,是我覺得稍微不友善的地方。

Database

POSTGRESQL_ROOT_USER=postgres
POSTGRESQL_ROOT_PASSWORD=""

DISCOURSE_POSTGRESQL_USERNAME=bn_discourse
DISCOURSE_POSTGRESQL_PASSWORD=bitnami1
DISCOURSE_POSTGRESQL_NAME=bitnami_application

Database 有幾個令人會搞混的設定,上面有提到 Discourse container 初始會進行 database 建置作業,因此會同時需要 postgres root account 和 discourse database account 兩個帳號資訊。

SMTP

SMTP 設定關係到 notification email 寄送和帳號驗證確認信,因此是必要設定項目,如果設定後發現沒有收到 email,可以參閱官方所提供的 Troubleshooting email on a new Discourse install

4. Sidekiq

除了 Web server 外,還需要部屬一個 sidekiq 來負責處理一些 backend tasks,例如寄送驗證 email 等。要注意的地方是,它與 Web server 是使用同一個 container,只是下的指令不同。

Sidekiq 的參數數值就直接 copy Web server configuration 就好,如果參數數值與 Web server 設定不一致,有可能導致系統出錯。(e.g. 系統無法寄信等)

另外,sidekiq 需要與 web server 進行溝通,所以要多兩個參數設定:

DISCOURSE_HOST=web-server
DISCOURSE_PORT=80

DISCOURSE_PORT 設定要依據 web-server Service 是使用哪一個 port 而作調整。sidekiq 本身不需要與其他 pod 溝通,因此不需要建立對應的 Service。但因為會連結外面網路寄信的關係,因此要注意 VPC 設定。

Deployment

Discourse 的 kubernates configuration file 可以參閱 Github discourse-k8s-demo

可以先用

kubectl kustomize ./ > discourse_secret.yaml

來產出 secret yaml (記得不要把 secret yaml 一起 commit 上去了),並且部屬到 GKE 上。

要注意,Web server 一定要掛 volume,因為 Discourse 允許用戶上傳 avatar,以及網站所用的 logo 圖檔等,都會放在 /bitnami 底下。(注意:如果是官方版本,那就會在 /share 底下),所以記得要使用 persistant disk 去保存。更重要的是,連同加裝的 plugins 都會放在其中,如果沒有設定 volume 的話,pod 重啟時,除了圖檔消失造成一堆 error,連額外安裝的 plugin 都不會存在。

另外, Web server 的 livenessProbe 記得要設成 /srv/status,不然直接打 root path / 會有效能上的問題。而 initialDelaySeconds 要設長一點,因為 discourse Web server 需要蠻長的初始化時間。

livenessProbe:
  httpGet:
    path: /srv/status
    port: server-port
  initialDelaySeconds: 300
  periodSeconds: 60

Install Plugins

Discourse 另一個重要功能就是能安插各式 plugin,其中像是 openID、oauth2 這些主流第三方驗證流程都要靠 plugin 才能使用。不過很可惜的是, bitnami/discourse container 無法透過 configuration 設定 plugins 來讓 container 剛起來時就去抓,而是要自己手動進入 pod 中去安裝,當 plugins 被安裝到 volume 設定好的空間後,接下來 pod 重啟時就不需要再安裝一次。

總結一下安裝 plugins 流程:

  1. 首次部屬 web server,並等 pod 狀態變成正常運作
  2. 使用 kubectl exec --it <pod> -- /bin/bash 進入 pod
  3. 進到指定 folder cd /opt/bitnami/discourse
  4. 安裝 plugins RAILS_ENV=production bundle exec rake plugin:install repo=PLUGIN_REPO_URL
  5. 在 pod 中直接 compile RAILS_ENV=production bundle exec rake assets:precompile

然後檢查 /bitnami/discourse/plugins 就會看到剛剛安裝的 plugin 在裡面了。

總結

總結一下 Discourse 部屬的幾個要點:

  1. 有 4 個 container 需要部屬: redis, postgres, web server, sidekiq
  2. web server 和 sidekiq 使用同一個 container,其中參數設定也要一致,並且 sidekiq 需要多加 web server 的連線資訊。
  3. 須注意 DISCOURSE_SKIP_INS TALL 參數,避免 web server container 無法順利啟動
  4. web server 要掛 volume,並且 plugins 需要手動安裝進去。(當然也可以選擇 clone bitnami-docker-discourse),然後把自己想要的 plugins 先預裝進去,不過這樣 container 也會比較胖。
  5. Web server 剛啟動時需要將近 1 core,後續 memory 使用也可能需要到 2.5 GB 這麼多,請先設定好 resource 限制,避免在執行時會有嚴重效能影響。