> For the complete documentation index, see [llms.txt](https://overleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://overleaf-pro.ayaka.space/on-premises/zh-tw/wei-hu/horizontal-scaling.md).

# 水平擴充

從 3.5.6 版開始，Server Pro 支援水平擴展。

本文列出技術需求，並提供在多於一個節點上執行 Server Pro 的指引。

{% hint style="danger" %}
從 Server CE/Server Pro 開始 `5.0.3` 環境變數已重新命名，自 `SHARELATEX_*` 到 `OVERLEAF_*`.

如果您使用的是 `4.x` 版本（或更早版本），請確保變數前綴相應設定（例如 `SHARELATEX_SITE_URL` 而不是 `OVERLEAF_SITE_URL`)
{% endhint %}

設定水平擴展需要投入相當多的工作。我們建議在 **只** 達到某個規模時再考慮水平擴展。舉例來說，一個總共 1,000 位使用者的 Server Pro 安裝已成功使用一台配備兩顆 4 核心處理器和 32GB 系統記憶體的伺服器完成部署。請參閱 [硬體需求](/on-premises/zh-tw/ru-men-zhi-nan/requirements/hardware-requirements.md) 文件以取得建議。

使用水平擴展部署的 Server Pro 會涉及一組外部元件，例如負載平衡器和相容 S3 的儲存後端。

我們可以協助排查 Server Pro 容器中的錯誤，這些錯誤可能是由設定不當所導致，並根據本文提供一般建議。不過，很抱歉，我們無法協助設定第三方應用程式／系統。

為提供外部元件而排解與您的硬體／軟體相關的技術問題，不在我們的支援條款範圍內。

### 需求

#### 外部集中式資料儲存

Server Pro 中的資料儲存可分為四個資料存放區：

* **MongoDB**

  * 大部分資料都會持久化到 MongoDB。
  * 我們支援本機實例或外部實例，例如 [MongoDB](https://www.mongodb.com/atlas) Atlas（在 AWS 基礎架構中執行的全代管 MongoDB 服務）。<br>

  **注意：** 很遺憾，目前尚未正式支援與 MongoDB 相容的資料庫，例如 CosmoDB/DocumentDB，因為我們尚未使用它們測試過 Server Pro。雖然以相容資料庫部署 Server Pro **可能** 可行，但我們僅正式支援使用 MongoDB 的部署。<br>
* **Redis**

  * Redis 會儲存暫時性資料，例如在寫入 MongoDB 之前待處理的文件更新。
  * Redis 用於在不同服務之間傳遞文件更新，並通知編輯器某個專案的狀態變更。
  * Redis 用於儲存使用者工作階段。
  * 我們支援本機實例或外部實例。<br>

  **注意：** 很遺憾，目前尚未正式支援與 Redis 相容的鍵值儲存，例如 KeyDB/Valkey，因為我們尚未使用它們測試過 Server Pro。雖然以相容儲存部署 Server Pro **可能** 可行，但我們僅正式支援使用 Redis 的部署。<br>
* **專案檔案與歷史檔案**

  * 不可編輯的專案檔案會儲存在 MongoDB 之外。

    新的專案歷史系統（Server Pro 3.5 之後）也會將歷史資料儲存在 MongoDB 之外。
  * 對於小型單一實例，我們支援本機檔案系統（可由本機 SSD、NFS 或 EBS 支援）或 [相容 S3 的資料儲存系統](/on-premises/zh-tw/she-ding/overleaf-toolkit/s3.md).
  * 對於水平擴展，我們 **只** 支援相容 S3 的資料儲存系統。<br>

  **重要：** NFS/Amazon EFS/Amazon EBS **不要** 支援用於水平擴展。請參閱 [硬體儲存](/on-premises/zh-tw/ru-men-zhi-nan/requirements/hardware-requirements.md#storage) 需求章節，以了解更多關於在 Server Pro 中擴展儲存的資訊。
* **暫存檔案**
  * LaTeX 編譯需要在快速的本機磁碟上執行，才能達到最佳效能。編譯輸出不需要持久化或備份。
  * 新檔案上傳的緩衝以及建立專案 zip 檔，也會因使用本機磁碟而受益。

{% hint style="danger" %}
我們強烈建議使用本機磁碟。使用任何形式的網路磁碟（例如 NFS 或 EBS）都可能導致非預期的編譯錯誤及其他效能問題。
{% endhint %}

#### **Git-bridge**

{% hint style="info" %}
從 4.0.1 版開始，Server Pro 提供 Git-bridge。
{% endhint %}

git-repositories 會儲存在本機磁碟上。沒有可用的複寫選項。Git-bridge 應以 **單例**方式執行。為了達到最佳效能，我們建議為 git-bridge 資料使用本機磁碟。git-bridge 資料磁碟應定期備份。

若要使用水平擴展進行資料儲存，您需要：

* 一個可從所有 Server Pro 實例存取的中央 MongoDB 實例
* 一個可從所有 Server Pro 實例存取的中央 Redis 實例
* 一個用於專案與歷史檔案的中央相容 S3 儲存後端
* 每個實例上一個用於暫存檔案的本機磁碟
* 承載 git-bridge 容器的實例上一個用於 git-bridge 資料的本機磁碟

#### 負載平衡器需求

* **持久化路由**，例如使用 cookie

  此需求源自下列元件：

  * Server Pro 中的即時編輯功能使用 WebSocket，並以 XHR 輪詢作為後備。每個編輯工作階段在伺服器端都有本地狀態，且某個編輯工作階段的請求始終需要路由到同一個 Server Pro 實例。協作功能使用 Redis [Pub/Sub](https://redis.io/docs/latest/develop/interact/pubsub/) 在多個 Server Pro 實例之間共享更新。
  * LaTeX 編譯會將輸出與編譯快取保留在本機，以獲得最佳效能。當向某個 Server Pro 實例發出編譯請求時，後續的 PDF／log 下載請求需要路由到同一個 Server Pro 實例。
* **較長的請求逾時** 以支援大型 LaTeX 文件的編譯
* **支援 WebSocket** 以達到最佳效能
* **50MB 的 POST 載荷大小**
* **keep-alive 逾時** 必須低於 Server Pro 的 keep-alive 逾時

  Server Pro 中的 keep-alive 逾時可使用環境變數進行設定 `NGINX_KEEPALIVE_TIMEOUT`。預設值為 65 秒。

  使用預設值時，負載平衡器設定 60 秒的 keep-alive 逾時即可運作。

  使用 `NGINX_KEEPALIVE_TIMEOUT=120`時，負載平衡器可以設定為 115 秒。
* **用戶端 IP**

  將請求標頭設定為 `X-Forwarded-For` 以帶入用戶端 IP。
* 當 **終止 SSL**

  負載平衡器需要新增請求標頭 `X-Forwarded-Proto: https`.

<details>

<summary>範例 HAProxy 設定</summary>

```
global
  group haproxy
  user haproxy

  # 詳細記錄
  log stdout format raw local0 debug

defaults
  mode                    http
  option                  httpchk HEAD /status
  http-check              expect status 200
  default-server          check

  # 詳細記錄
  log                     global
  option                  httplog

  # 如果固定的 backend 已失效，重新路由到其他 backend
  option                  redispatch 1
  # 這些重試是針對 TCP 連線錯誤，不是針對 HTTP 狀態 500 回應
  retries                 3

  # 24 小時未活動的 sticky session -- 編譯輸出會在 24 小時後刪除
  cookie                  server-pro-ha insert maxidle 24h

  # 嘗試連線到任一 backend 持續 1 分鐘，然後回傳 503
  timeout queue           1m
  # 給 Server Pro 實例 15 秒啟動時間
  timeout connect         15s

  # 中止來自非常慢用戶端的請求（讀取請求時允許 1 分鐘的不活動時間）
  timeout client          1m

  # 允許較慢的編譯 -- clsi 中的硬編碼限制為 10 分鐘
  timeout server          10m

  # 在 23 小時後中斷與編輯器的連線 -- 比昨天最後使用時間早 1 小時
  timeout tunnel          23h

  # 注意：haproxy 中的 keepalive 行為與 Server Pro 的預設 keepalive 設定搭配得非常好。
  #       Haproxy 會在背景清理連線，並在需要時重新分派請求。

listen server-pro-ha-http
  bind :80
  http-request redirect scheme https unless { ssl_fc }

listen server-pro-ha-https
  bind :443 ssl crt /etc/ssl/certs/ssl-key-and-certificate-bundle.pem

  # 告訴應用程式我們位於 https 之後
  http-request set-header X-Forwarded-Proto https

  # 告訴應用程式實際的用戶端 IP
  option forwardfor

  # 請參閱 https://hstspreload.org/#deployment-recommendations
  http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload;"

  # 將 git 流量路由到 git-bridge 的同層容器
  use-server server-pro-ha-1 if { path_beg /git/ }

  # 除錯
  http-response add-header X-Served-By %s
  stats enable
  stats uri /haproxy

  server server-pro-ha-1 198.18.1.1:80 cookie server-pro-ha-1
  server server-pro-ha-2 198.18.1.2:80 cookie server-pro-ha-2
  server server-pro-ha-3 198.18.1.3:80 cookie server-pro-ha-3
```

</details>

#### Server Pro 設定

**祕密**

各 Server Pro 實例需要就共享祕密達成一致：

* `WEB_API_PASSWORD` （web api 驗證）
* `STAGING_PASSWORD` 以及 `V1_HISTORY_PASSWORD` 相同值（歷史驗證）
* `CRYPTO_RANDOM` （用於工作階段 cookie）
* `OT_JWT_AUTH_KEY` （歷史驗證）

所有這些祕密都需要設定為各自唯一的值，並在各個實例之間共享。

如果未設定，而使用者請求被路由到不同的 Server Pro 實例，他們的請求將無法通過驗證檢查，因而不是經常被重新導向至登入頁面，就是在 UI 中的操作會以意想不到的方式失敗。

若未設定，Server Pro 會為每個祕密使用一個新的隨機值，該值基於來自 `/dev/urandom` 的 32 個隨機位元組（256 個隨機位元）。

{% code overflow="wrap" %}

```bash
# https://github.com/overleaf/overleaf/blob/45ca0f796c679103efd305ddbef28073c4a5de32/server-ce/init_scripts/00_regen_sharelatex_secrets.sh#L14
dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 -w 0 | rev | cut -b 2- | rev | tr -d '\n+/'
```

{% endcode %}

**MongoDB**

將 `OVERLEAF_MONGO_URL` (`SHARELATEX_MONGO_URL` 針對版本 `4.x` 及更早版本）指向中央 MongoDB 實例。

**Redis**

將 `OVERLEAF_REDIS_HOST` (`SHARELATEX_REDIS_HOST` 針對版本 `4.x` 及更早版本）和 `REDIS_HOST` 指向中央 Redis 實例。

**用於專案與歷史檔案的相容 S3 儲存**

請參閱 [相容 S3 的儲存空間](/on-premises/zh-tw/she-ding/overleaf-toolkit/s3.md) 的文件以了解詳細資訊。

**暫存檔案**

將本機 SSD 預設綁定掛載到 `/var/lib/overleaf` (`/var/lib/sharelatex` 針對版本 `4.x` 及更早版本）就足夠了。請務必將 `SANDBOXED_COMPILES_HOST_DIR` 指向主機上的掛載點。

{% hint style="danger" %}
我們強烈建議使用本機磁碟。使用任何形式的網路磁碟（例如 NFS 或 EBS）都可能導致非預期的編譯錯誤及其他效能問題。
{% endhint %}

**代理伺服器設定**

* 設定 `OVERLEAF_BEHIND_PROXY=true` (`SHARELATEX_BEHIND_PROXY` 針對版本 `4.x` 及更早版本）以取得正確的用戶端 IP。
* 設定 `TRUSTED_PROXY_IPS` 為負載平衡器的 IP。（可指定多個 CIDR，以逗號分隔。）

**Git-bridge 整合**

{% hint style="info" %}
從 4.0.1 版開始，Server Pro 提供 Git-bridge。
{% endhint %}

git-bridge 容器需要一個同層的 Server Pro 容器來處理傳入的 git 請求。這個同層容器也可以服務一般使用者流量。在範例設定中，第一個實例作為 git-bridge 的同層容器，但實際上任何實例都可以擔任此角色。

為什麼我們需要指定一個 Server Pro 容器作為 git-bridge 的同層容器？Server Pro 會將歷史服務的下載 URL 提供給 git-bridge。我們需要將這些歷史 URL 設定為可由 git-bridge 容器存取。

Server Pro 容器設定：

* 設定 `GIT_BRIDGE_ENABLED` 到 `'true'`
* 設定 `GIT_BRIDGE_HOST` 到 `<git-bridge 容器名稱>` 例如 `git-bridge`
* 設定 `GIT_BRIDGE_PORT` 到 `8000`
* 設定 `V1_HISTORY_URL` 到 `http://<server-pro 同層容器名稱>:3100/api`.

  注意：這只在 git-bridge 容器的同層容器上才是必要的。其他實例可以使用 localhost URL，這也是預設值。

git-bridge 容器設定：

* 設定 `GIT_BRIDGE_API_BASE_URL` 到 `http://<server-pro 同層容器名稱>/api/v0`，例如 `http://server-pro-ha-1/api/v0`
* 設定 `GIT_BRIDGE_OAUTH2_SERVER` 到 `http://<server-pro 同層容器名稱>`，例如 `http://server-pro-ha-1`
* 設定 `GIT_BRIDGE_POSTBACK_BASE_URL` 到 `http://<git-bridge 容器名稱>:8000`，例如 `http://git-bridge:8000`
* 設定 `GIT_BRIDGE_ROOT_DIR` 指向綁定掛載的 git-bridge 資料磁碟，例如 `/data/git-bridge`

<details>

<summary>範例 docker-compose.yml 設定</summary>

以下設定展示的是一個自成一體的部署。若要讓示範正常運作，您需要提供有效的 SSL 金鑰／憑證，並調整 `OVERLEAF_SITE_URL` (`SHARELATEX_SITE_URL` 針對版本 `4.x` 及更早版本）。若是實際部署，您必須如內文所註，將範例祕密替換為真正的祕密。若是實際部署，您還需要把各個容器移到專用節點上，並依照您的本機網路設定調整 IP 位址。

```yaml
version: '2.2'

# 實際的水平擴展設定：選擇您自己的網路並替換設定中的 IP。
networks:
    default:
        ipam:
            config:
                # 這個子網路是供基準測試使用的保留子網路的一部分
                # https://tools.ietf.org/html/rfc2544
                # 完整子網路為 198.18.0.0/15
                # 為 lb 和 dbs 使用 198.18.0.0/24
                # 為 server-pro 使用 198.18.1.0/24
                # 為暫存容器使用 198.18.0.128/25
                - gateway: 198.18.0.1
                  ip_range: 198.18.0.128/25
                  subnet: 198.18.0.0/23

services:
    # 實際的水平擴展設定：在另一台主機上、docker 外部執行 haproxy。
    lb:
        image: haproxy:2.6
        container_name: lb
        user: root
        logging:
            driver: local
            options:
                max-size: 10g
                max-file: '100'
        volumes:
            - ./haproxy.conf:/usr/local/etc/haproxy/haproxy.cfg
            # $ cat certificate.pem key.pem > ssl-key-and-certificate-bundle.pem
            - /path/to/ssl-key-and-certificate-bundle.pem:/etc/ssl/certs/ssl-key-and-certificate-bundle.pem
        # 「ports」的替代方案：使用主機網路以避免 docker-proxy 的額外負擔
        network_mode: host

        # 「network_mode: host」的替代方案：使用 docker-proxy 進行網路隔離
        # ports:
        #     - "80:80"
        #     - "443:443"
        # networks:
        #     default:
        #         ipv4_address: 198.18.0.2

        # 實際的水平擴展設定：將這些移除，因為它們是在其他主機上執行。
        depends_on:
            server-pro-ha-1:
                condition: service_started
            server-pro-ha-2:
                condition: service_started
            server-pro-ha-3:
                condition: service_started

    # 實際的水平擴展設定：將此容器與 server-pro-ha-1 一起執行。
    # 供 Server Pro 4.0 之後使用。
    git-bridge:
        restart: always
        # 此標籤應與 `server-pro-ha-1` 容器標籤相符。
        image: quay.io/sharelatex/git-bridge:4.0.1
        volumes:
            # 實際的水平擴展設定：將 /data/git-bridge 指向專用的本機 SSD。
            - ~/git_bridge_data:/data/git-bridge
        container_name: git-bridge
        environment:
            GIT_BRIDGE_API_BASE_URL: "http://server-pro-ha-1/api/v0"
            GIT_BRIDGE_OAUTH2_SERVER: "http://server-pro-ha-1"
            GIT_BRIDGE_POSTBACK_BASE_URL: "http://198.18.0.6:8000"
            GIT_BRIDGE_ROOT_DIR: "/data/git-bridge"
        user: root
        command: ["/server-pro-start.sh"]

        # 實際的水平擴展設定：在主機 198.18.0.6 上執行並公開埠
        # ports:
        #     - "8000:8000"
        networks:
            default:
                ipv4_address: 198.18.0.6

    # 實際的水平擴展設定：將此容器部署到另一台主機上。
    server-pro-ha-1: &server-pro-ha-config
        restart: always
        image: quay.io/sharelatex/sharelatex-pro:4.0.1
        container_name: server-pro-ha-1
        hostname: server-pro-ha-1
        depends_on:
            # 實際的水平擴展設定：保留這一項。
            git-bridge:
                condition: service_started

            # 實際的水平擴展設定：移除下面的項目，因為它們是在其他主機上執行。
            mongo:
                condition: service_healthy
            redis:
                condition: service_started
            minio:
                condition: service_started
            mongo_replica_set_setup:
                condition: service_completed_successfully
            minio_setup:
                condition: service_completed_successfully
        stop_grace_period: 60s
        volumes:
            - /tmp/scratch-disk1:/var/lib/sharelatex
            - /var/run/docker.sock:/var/run/docker.sock
        environment: &server-pro-ha-environment
            # 實際的水平擴展設定：提供您自己的網域／應用程式名稱。
            OVERLEAF_SITE_URL: 'https://overleaf.example.com'
            OVERLEAF_APP_NAME: Server Pro Horizontal Scaling Demo

            OVERLEAF_MONGO_URL: mongodb://198.18.0.3/sharelatex
            OVERLEAF_REDIS_HOST: 198.18.0.4
            REDIS_HOST: 198.18.0.4

            ENABLED_LINKED_FILE_TYPES: 'project_file,project_output_file'
            EMAIL_CONFIRMATION_DISABLED: 'true'

            SANDBOXED_COMPILES: 'true'
            SANDBOXED_COMPILES_SIBLING_CONTAINERS: 'true'
            SANDBOXED_COMPILES_HOST_DIR: '/tmp/scratch-disk1/data/compiles'

            # S3
            # 實際的水平擴展設定：請選擇安全的憑證。
            OVERLEAF_FILESTORE_BACKEND: s3
            OVERLEAF_FILESTORE_USER_FILES_BUCKET_NAME: overleaf-user-files
            OVERLEAF_FILESTORE_TEMPLATE_FILES_BUCKET_NAME: overleaf-template-files
            OVERLEAF_FILESTORE_S3_ACCESS_KEY_ID: OVERLEAF_FILESTORE_S3_ACCESS_KEY_ID
            OVERLEAF_FILESTORE_S3_SECRET_ACCESS_KEY: OVERLEAF_FILESTORE_S3_SECRET_ACCESS_KEY
            OVERLEAF_FILESTORE_S3_ENDPOINT: http://198.18.0.5:9000
            OVERLEAF_FILESTORE_S3_PATH_STYLE: 'true'
            OVERLEAF_FILESTORE_S3_REGION: ''

            OVERLEAF_HISTORY_BACKEND: "s3"
            OVERLEAF_HISTORY_PROJECT_BLOBS_BUCKET: "overleaf-project-blobs"
            OVERLEAF_HISTORY_CHUNKS_BUCKET: "overleaf-chunks"
            OVERLEAF_HISTORY_S3_ACCESS_KEY_ID: "OVERLEAF_HISTORY_S3_ACCESS_KEY_ID"
            OVERLEAF_HISTORY_S3_SECRET_ACCESS_KEY: "OVERLEAF_HISTORY_S3_SECRET_ACCESS_KEY"
            OVERLEAF_HISTORY_S3_ENDPOINT: http://198.18.0.5:9000
            OVERLEAF_HISTORY_S3_PATH_STYLE: 'true'
            OVERLEAF_HISTORY_S3_REGION: ''
            # /S3

            # git-bridge
            GIT_BRIDGE_ENABLED: 'true'
            GIT_BRIDGE_HOST: 198.18.0.6
            GIT_BRIDGE_PORT: 8000
            只需要在 git-bridge 的同級執行個體上
            V1_HISTORY_URL: "http://server-pro-ha-1:3100/api"
            # /git-bridge

            # 水平擴充
            # 實際的水平擴展設定：請選擇安全的憑證。
            WEB_API_PASSWORD: WEB_API_PASSWORD
            STAGING_PASSWORD: V1_HISTORY_PASSWORD
            V1_HISTORY_PASSWORD: V1_HISTORY_PASSWORD
            CRYPTO_RANDOM: CRYPTO_RANDOM
            OT_JWT_AUTH_KEY: OT_JWT_AUTH_KEY
            OVERLEAF_BEHIND_PROXY: 'true'
            # 實際水平擴充設定：負載平衡器的 IP
            TRUSTED_PROXY_IPS: 198.18.0.1,198.18.0.2
            # /水平擴充

        # 實際水平擴充設定：在主機 198.18.1.1 上執行並公開連接埠
        # ports:
        #     - "80:80"
        networks:
            default:
                ipv4_address: 198.18.1.1

    # 實際的水平擴展設定：將此容器部署到另一台主機上。
    server-pro-ha-2:
        <<: *server-pro-ha-config
        hostname: server-pro-ha-2
        container_name: server-pro-ha-2
        volumes:
            - /tmp/scratch-disk2:/var/lib/sharelatex
            - /var/run/docker.sock:/var/run/docker.sock
        environment:
            <<: *server-pro-ha-environment
            SANDBOXED_COMPILES_HOST_DIR: '/tmp/scratch-disk2/data/compiles'
            V1_HISTORY_URL: "http://localhost:3100/api"

        # 實際水平擴充設定：在主機 198.18.1.2 上執行並公開連接埠
        # ports:
        #     - "80:80"
        networks:
            default:
                ipv4_address: 198.18.1.2

    # 實際的水平擴展設定：將此容器部署到另一台主機上。
    server-pro-ha-3:
        <<: *server-pro-ha-config
        hostname: server-pro-ha-3
        container_name: server-pro-ha-3
        volumes:
            - /tmp/scratch-disk3:/var/lib/sharelatex
            - /var/run/docker.sock:/var/run/docker.sock
        environment:
            <<: *server-pro-ha-environment
            SANDBOXED_COMPILES_HOST_DIR: '/tmp/scratch-disk3/data/compiles'
            V1_HISTORY_URL: "http://localhost:3100/api"

        # 實際水平擴充設定：在主機 198.18.1.3 上執行並公開連接埠
        # ports:
        #     - "80:80"
        networks:
            default:
                ipv4_address: 198.18.1.3

    # 實際的水平擴展設定：將此容器部署到另一台主機上。
    minio:
        image: minio/minio:RELEASE.2023-05-18T00-05-36Z
        container_name: minio
        command: server /data
        volumes:
            # 實際水平擴充設定：使用多個磁碟執行 minio，請參閱 minio 文件。
            - ~/minio_data:/data
        environment:
            # 實際的水平擴展設定：請選擇安全的憑證。
            MINIO_ROOT_USER: MINIO_ROOT_USER
            MINIO_ROOT_PASSWORD: MINIO_ROOT_PASSWORD

        # 實際水平擴充設定：在主機 198.18.0.5 上執行並公開連接埠
        # ports:
        #     - "9000:9000"
        networks:
            default:
                ipv4_address: 198.18.0.5

    # 實際水平擴充設定：請在另一台主機上只執行一次此設定。
    minio_setup:
        depends_on:
            - minio
        image: minio/mc:RELEASE.2023-05-18T16-59-00Z
        entrypoint: sh
        命令：
            - '-c'
            # 實際的水平擴展設定：請選擇安全的憑證。
            - |
                mc alias set s3 http://198.18.0.5:9000 MINIO_ROOT_USER MINIO_ROOT_PASSWORD \
                || sleep 10 && \
                mc alias set s3 http://198.18.0.5:9000 MINIO_ROOT_USER MINIO_ROOT_PASSWORD \
                || sleep 10 && \
                mc alias set s3 http://198.18.0.5:9000 MINIO_ROOT_USER MINIO_ROOT_PASSWORD \
                || sleep 10 && \
                mc alias set s3 http://198.18.0.5:9000 MINIO_ROOT_USER MINIO_ROOT_PASSWORD

                mc mb --ignore-existing s3/overleaf-user-files
                mc mb --ignore-existing s3/overleaf-template-files
                mc admin user add s3 \
                  OVERLEAF_FILESTORE_S3_ACCESS_KEY_ID \
                  OVERLEAF_FILESTORE_S3_SECRET_ACCESS_KEY

                mc mb --ignore-existing s3/overleaf-project-blobs
                mc mb --ignore-existing s3/overleaf-chunks
                mc admin user add s3 \
                  OVERLEAF_HISTORY_S3_ACCESS_KEY_ID \
                  OVERLEAF_HISTORY_S3_SECRET_ACCESS_KEY

                echo '
                  {
                    "Version": "2012-10-17",
                    "Statement": [
                      {
                        "Effect": "Allow",
                        "Action": [
                          "s3:ListBucket"
                        ],
                        "Resource": "arn:aws:s3:::overleaf-user-files"
                      },
                      {
                        "Effect": "Allow",
                        "Action": [
                          "s3:PutObject",
                          "s3:GetObject",
                          "s3:DeleteObject"
                        ],
                        "Resource": "arn:aws:s3:::overleaf-user-files/*"
                      },
                      {
                        "Effect": "Allow",
                        "Action": [
                          "s3:ListBucket"
                        ],
                        "Resource": "arn:aws:s3:::overleaf-template-files"
                      },
                      {
                        "Effect": "Allow",
                        "Action": [
                          "s3:PutObject",
                          "s3:GetObject",
                          "s3:DeleteObject"
                        ],
                        "Resource": "arn:aws:s3:::overleaf-template-files/*"
                      }
                    ]
                  }' > policy-filestore.json

                echo '
                  {
                    "Version": "2012-10-17",
                    "Statement": [
                      {
                        "Effect": "Allow",
                        "Action": [
                          "s3:ListBucket"
                        ],
                        "Resource": "arn:aws:s3:::overleaf-project-blobs"
                      },
                      {
                        "Effect": "Allow",
                        "Action": [
                          "s3:PutObject",
                          "s3:GetObject",
                          "s3:DeleteObject"
                        ],
                        "Resource": "arn:aws:s3:::overleaf-project-blobs/*"
                      },
                      {
                        "Effect": "Allow",
                        "Action": [
                          "s3:ListBucket"
                        ],
                        "Resource": "arn:aws:s3:::overleaf-chunks"
                      },
                      {
                        "Effect": "Allow",
                        "Action": [
                          "s3:PutObject",
                          "s3:GetObject",
                          "s3:DeleteObject"
                        ],
                        "Resource": "arn:aws:s3:::overleaf-chunks/*"
                      }
                    ]
                  }' > policy-history.json

                # 將上一節政策的內容放入 policy-filestore.json
                # 提醒：請相應替換儲存桶名稱。
                mc admin policy create s3 overleaf-filestore policy-filestore.json
                mc admin policy attach s3 overleaf-filestore \
                  --user=OVERLEAF_FILESTORE_S3_ACCESS_KEY_ID || true

                mc admin policy create s3 overleaf-history policy-history.json
                mc admin policy attach s3 overleaf-history \
                  --user=OVERLEAF_HISTORY_S3_ACCESS_KEY_ID || true

    # 實際的水平擴展設定：將此容器部署到另一台主機上。
    mongo:
        restart: always
        image: mongo:4.4
        container_name: mongo
        command: "--replSet overleaf"
        expose:
            - 27017
        volumes:
            - ~/mongo_data:/data/db
        healthcheck:
            test: echo 'db.stats().ok' | mongo localhost:27017/test --quiet
            interval: 10s
            timeout: 10s
            retries: 5

        # 實際水平擴充設定：在主機 198.18.0.3 上執行並公開連接埠
        # ports:
        #     - "27017:27017"
        networks:
            default:
                ipv4_address: 198.18.0.3

    mongo_replica_set_setup:
        image: mongo:4.4
        entrypoint: sh
        depends_on:
            mongo:
                condition: service_healthy
        命令：
            - '-c'
            - |
                mongo 198.18.0.3 --eval "rs.initiate({ _id: \"overleaf\", members: [ { _id: 0, host: \"198.18.0.3:27017\" } ] })"

    # 實際的水平擴展設定：將此容器部署到另一台主機上。
    redis:
        restart: always
        image: redis:6.2
        container_name: redis
        expose:
            - 6379
        volumes:
            - ~/redis_data:/data

        # 實際水平擴充設定：在主機 198.18.0.4 上執行並公開連接埠
        # ports:
        #     - "6379:6379"
        networks:
            default:
                ipv4_address: 198.18.0.4
```

</details>

#### 硬體

我們建議所有參與水平擴充的 Server Pro 執行個體都使用相同的硬體規格。

關於……的一般建議 [硬體規格](/on-premises/zh-tw/ru-men-zhi-nan/requirements/hardware-requirements.md) 同樣適用於 Server Pro 執行個體。

#### 升級 Server Pro

作為升級流程的一部分，Server Pro 會自動執行資料庫遷移。這些遷移是 **不要** 設計為可由多個執行個體並行執行。

遷移必須在實際 Web 應用程式啟動前完成。你可以查看日誌中是否有一條 `遷移完成` 或等到應用程式開始接受流量。

升級流程如下：

1. 安排維護時段
2. 停止所有 Server Pro 執行個體
3. 依照下列說明建立一致性備份： [文件](/on-premises/zh-tw/wei-hu/data-and-backups.md#performing-a-consistent-backup)
4. 以新版本啟動單一 Server Pro 執行個體
5. 驗證新執行個體是否如預期運作
6. 以新版本啟動其他執行個體


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://overleaf-pro.ayaka.space/on-premises/zh-tw/wei-hu/horizontal-scaling.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
