> For the complete documentation index, see [llms.txt](https://ayakaleaf-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://ayakaleaf-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/kai-shi-shi-yong/requirements/hardware-requirements.md) 文件中的建議。

採用水平擴充的 Server Pro 部署會包含一組外部元件，例如負載平衡器與相容 S3 的儲存後端。

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

針對你的硬體／軟體以提供外部元件所產生的特定技術問題，不在我們的支援條款範圍內。

### 需求

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

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

* **MongoDB**

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

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

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

  **注意：** 很抱歉，目前尚未正式支援 KeyDB/Valkey 等相容 Redis 的鍵值儲存，因為我們尚未使用它們測試過 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/kai-shi-shi-yong/requirements/hardware-requirements.md#storage) 中關於在 Server Pro 中擴充儲存的需求章節以取得更多詳情。
* **暫時性檔案**
  * LaTeX 編譯需要在快速的本機磁碟上執行，才能獲得最佳效能。編譯輸出不需要持久保存或備份。
  * 緩衝新檔案上傳以及建立專案 zip 檔案，也會受益於使用本機磁碟。

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

#### **Git-bridge**

{% hint style="info" %}
Git-bridge 自 4.0.1 版起可在 Server Pro 中使用。
{% endhint %}

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

在水平擴充下的資料儲存，你需要：

* 一個可由所有 Server Pro 執行個體存取的集中式 MongoDB 執行個體
* 一個可由所有 Server Pro 執行個體存取的集中式 Redis 執行個體
* 一個用於專案與歷史檔案的集中式相容 S3 儲存後端
* 每個執行個體上用於暫時性檔案的本機磁碟
* 承載 git-bridge 容器的執行個體上用於 git-bridge 資料的本機磁碟

#### 負載平衡器需求

* **持續性路由**，例如使用 cookie

  此需求源自以下元件：

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

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

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

  使用 `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

  # 如果黏著的後端故障，則重新導向至其他後端
  option                  redispatch 1
  # 這些重試是針對 TCP 連線錯誤，不適用於 HTTP 狀態 500 回應
  retries                 3

  # 24 小時未活動的黏著工作階段——編譯輸出會在 24 小時後刪除
  cookie                  server-pro-ha insert maxidle 24h

  # 嘗試連線至任一後端 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 預設 bind-mount 到 `/var/lib/overleaf` (`/var/lib/sharelatex` 適用於版本 `4.x` 及更早版本）即可。請務必將 `SANDBOXED_COMPILES_HOST_DIR` 指向主機上的掛載點。

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

**Proxy 設定**

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

**Git-bridge 整合**

{% hint style="info" %}
Git-bridge 自 4.0.1 版起可在 Server Pro 中使用。
{% 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 container name>` 例如 `git-bridge`
* 設定 `GIT_BRIDGE_PORT` 到 `8000`
* 設定 `V1_HISTORY_URL` 到 `http://<server-pro sibling container name>:3100/api`.

  注意：這只在 git-bridge 容器的同層容器上需要。其他執行個體可以使用 localhost URL，這是預設值。

git-bridge 容器設定：

* 設定 `GIT_BRIDGE_API_BASE_URL` 到 `http://<server-pro sibling container name>/api/v0`，例如 `http://server-pro-ha-1/api/v0`
* 設定 `GIT_BRIDGE_OAUTH2_SERVER` 到 `http://<server-pro sibling container name>`，例如 `http://server-pro-ha-1`
* 設定 `GIT_BRIDGE_POSTBACK_BASE_URL` 到 `http://<git-bridge container name>:8000`，例如 `http://git-bridge:8000`
* 設定 `GIT_BRIDGE_ROOT_DIR` 指向 bind-mounted 的 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
                # 將 198.18.0.0/24 用於 lb 與 dbs
                # 將 198.18.1.0/24 用於 server-pro
                # 將 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/kai-shi-shi-yong/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://ayakaleaf-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.
