FastAPI 作為新興 Python 網路框架,以其高效能和易用性著稱。搭配 Ray 分散式計算框架,更能顯著提升應用程式的可擴充性和處理能力。本文將以實際案例示範如何結合 FastAPI 與 Ray 處理 Twilio 的簡訊訊息,包含程式碼實作細節、單元測試策略、Kubernetes 佈署方法,以及在 AWS 和 IBM Cloud 上安裝 Ray 的詳細步驟。

首先,我們會建立一個 FastAPI 應用程式,並將其封裝在 Ray 佈署中,利用 Ray 的 actor 模型來管理和分配任務。這種架構能夠充分利用分散式計算的優勢,有效處理大量並行請求。接著,我們會示範如何撰寫單元測試,確保程式碼的正確性和穩定性。佈署方面,我們將使用 Kubernetes 來管理和協調服務,並提供對外存取的入口。

此外,本文還會詳細說明如何在 AWS 和 IBM Cloud 上安裝和配置 Ray 叢集,包含使用 YAML 檔案佈署、監控自動擴充、連線叢集等操作。同時,也提供如何在虛擬機器上安裝 Ray 的步驟和建議,方便讀者根據實際需求選擇佈署方式。最後,我們將討論 FastAPI 與 Ray 結合的優勢,以及在微服務架構和分散式計算趨勢下的應用前景。

FastAPI 與 Ray 的佈署與測試實務

FastAPI 是一個現代化的 Python 網路框架,設計用來建立快速且高效的 API。當結合 Ray 分散式計算框架時,可以大幅提升應用程式的可擴充性和效能。Ray 提供了強大的分散式任務排程能力,能夠將運算負載分散到多個節點上,實現真正的水平擴展。這種組合特別適合處理需要大量並行運算的場景,例如訊息處理、資料分析或機器學習推論。

以下是一個實際案例,展示如何使用 FastAPI 與 Ray 來處理來自 Twilio 的簡訊訊息,並進行單元測試和佈署。這個案例涵蓋了從接收 HTTP 請求、驗證訊息真實性到非同步處理的完整流程,充分展現了兩個框架協同工作的優勢。

FastAPI 應用與 Ray 佈署的整合實作

首先,我們需要定義一個 FastAPI 應用程式,並將其包裝在 Ray 的佈署中。這種架構允許我們利用 FastAPI 處理 HTTP 請求,同時使用 Ray 進行分散式任務處理。Ray 的 actor 模型提供了狀態管理和任務隔離的能力,讓每個處理單元都能獨立運作而不互相干擾。

from fastapi import FastAPI, Request, HTTPException
from pydantic import BaseModel, Field
from typing import Optional
from twilio.request_validator import RequestValidator
import ray

app = FastAPI()

class InboundMessage(BaseModel):
    x_twilio_signature: str
    message_from: Optional[str] = Field(None, alias='from')
    to: str
    body: str
    msg_type: Optional[str] = Field(None, alias="type")

@ray.remote
class PhoneWeb:
    def __init__(self, settings, poolsize):
        self.settings = settings
        self.poolsize = poolsize
        self.user_pool = utils.LazyNamedPool("user", poolsize)
        self.validator = RequestValidator(settings.TW_AUTH_TOKEN)

    async def handle_message(self, request: Request, message: InboundMessage) -> str:
        request_valid = self.validator.validate(
            request.url,
            request.form,
            request.headers.get('X-TWILIO-SIGNATURE', '')
        )
        if request_valid:
            internal_message = CombinedMessage(
                text=message.body, to=message.to, protocol=SMS_PROTOCOL,
                msg_from=message.message_from, from_device=False
            )
            self.user_pool.get_pool().submit(
                lambda actor, msg: actor.handle_message.remote(msg),
                internal_message
            )
            return ""
        else:
            raise HTTPException(status_code=403, detail="Validation failed.")

@app.get("/sms")
async def inbound_message(request: Request, message: InboundMessage):
    phone_web = PhoneWeb.remote(settings, poolsize)
    return await phone_web.handle_message.remote(request, message)

此程式碼範例展示了如何使用 FastAPI 和 Ray 來處理來自 Twilio 的簡訊訊息。首先,我們定義了一個 InboundMessage 模型來解析來自 Twilio 的 HTTP 請求資料。這個模型使用 Pydantic 進行資料驗證,確保接收到的資料符合預期格式。接著,我們定義了一個 PhoneWeb 類別,該類別使用 Ray 的 remote 裝飾器標記為分散式 actor,能夠在 Ray 叢集中的任何節點上執行。

PhoneWeb 類別在初始化時建立了一個使用者池來管理並行處理,並配置了 Twilio 的請求驗證器來確保訊息來源的真實性。handle_message 方法負責驗證訊息並將其轉發給使用者池進行處理。最後,我們定義了一個 FastAPI 路由處理器,該處理器會建立 Ray actor 實例並呼叫其方法來處理訊息。這種設計將 HTTP 請求處理與業務邏輯分離,提高了系統的可維護性。

測試策略與實作方法

為了方便測試,我們將 actor 的程式碼拆分成基礎類別和擴充類別,這樣可以獨立地測試核心功能而不需要依賴 Ray 的佈署環境。這種分層設計是測試驅動開發的最佳實踐,能夠讓測試更加簡潔和可靠。基礎類別包含了核心業務邏輯,而擴充類別則處理與 Ray 相關的分散式功能。

import unittest
import random

class StandaloneMailServerActorTests(unittest.TestCase):
    port = 7779 + 100 * random.randint(0, 9)

    def setUp(self):
        self.port = self.port + 1
        self.actor = mailserver_actor.MailServerActorBase(
            idx=1, poolsize=1, port=self.port, hostname="0.0.0.0",
            label=None)
        self.actor.user_pool = test_utils.FakeLazyNamedPool("u", 1)
        self.pool = self.actor.user_pool.get_pool()

    def tearDown(self):
        self.actor.server.stop()
        self.server = None

    def test_constructor_makes_server(self):
        self.assertEquals(self.actor.server.hostname, "0.0.0.0")

    def test_extract_body_and_connect(self):
        client = Client("localhost", self.port)
        msg_text = "Hi Boop, this is timbit."
        client.sendmail("c@gull.com", "boop@spacebeaver.com", msg_text)
        self.assertEquals(self.pool.submitted[0][1].text, msg_text)
        self.assertEquals(self.pool.submitted[0][1].protocol, EMAIL_PROTOCOL)
        self.assertEquals(self.pool.submitted[0][1].from_device, False)

@ray.remote
class MailServerActorForTesting(mailserver_actor.MailServerActorBase):
    def __init__(self, idx, poolsize, port, hostname):
        mailserver_actor.MailServerActorBase.__init__(self, idx, poolsize,
                                                    port, hostname)
        self.user_pool = test_utils.FakeLazyNamedPool("user", 1)

class MailServerActorTestCases(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        ray.init()

    @classmethod
    def tearDownClass(cls):
        ray.shutdown()

    def test_mail_server_actor_construct(self):
        mailserver_actor.MailServerActor.remote(0, 1, 7587, "localhost")

此測試範例展示了如何進行單元測試和完整 actor 測試。單元測試部分使用 unittest 框架來測試基礎類別的核心功能,不依賴任何外部服務或分散式環境。這些測試能夠快速執行並提供即時回饋,適合在開發過程中頻繁執行。測試案例涵蓋了伺服器建立、訊息解析和連線處理等關鍵功能。

完整 actor 測試部分則使用 Ray 的分散式特性來測試完整的 actor 行為,確保在分散式環境中的正確性。這些測試會初始化 Ray 執行環境,建立實際的 Ray actor,並驗證其在分散式場景下的行為。透過這種分層測試策略,我們可以在不同層級確保程式碼的品質,從單元級別的邏輯正確性到系統級別的整合可靠性。

Kubernetes 佈署的配置策略

雖然 Ray 處理了大部分的佈署工作,但我們仍需要建立 Kubernetes 服務來使 SMTP 和 SMS 服務可達。Kubernetes 提供了服務發現、負載平衡和自動擴充等關鍵功能,是生產環境佈署的理想選擇。透過適當的 Kubernetes 配置,我們可以確保服務的高可用性和可擴充性。

apiVersion: v1
kind: Service
metadata:
  name: message-backend-svc
  namespace: spacebeaver
spec:
  selector:
    mail_ingress: present
  ports:
    - name: smtp
      protocol: TCP
      port: 25
      targetPort: 7420
  type: LoadBalancer
  loadBalancerIP: 23.177.16.210

---
apiVersion: v1
kind: Service
metadata:
  name: phone-api-svc
  namespace: spacebeaver
spec:
  selector:
    ray-cluster-name: spacebeaver
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 8000

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: spacebeaver-phone-api-ingress
  namespace: spacebeaver
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - phone-api.spacebeaver.com
      secretName: phone-api-tls-secret
  rules:
    - host: "phone-api.spacebeaver.com"
      http:
        paths:
          - pathType: Prefix
            path: "/"
            backend:
              service:
                name: phone-api-svc
                port:
                  number: 80

此配置展示了 Kubernetes 中的 SMTP 和 SMS 應用服務組態。第一個服務定義了 SMTP 後端服務,使用 LoadBalancer 類型來提供外部存取能力,並指定了固定的外部 IP 位址。選擇器使用標籤來路由請求到正確的 Pod 中,確保流量能夠正確分發。

第二個服務定義了電話 API 服務,它選擇具有特定 Ray 叢集名稱的 Pod,確保請求被路由到正確的 Ray 叢集節點。最後的 Ingress 資源配置了 HTTPS 終端和基於主機名稱的路由規則,使用 nginx ingress controller 來處理外部流量。TLS 配置確保了通訊的安全性,而路徑規則則將所有請求導向到相應的後端服務。

安裝與佈署 Ray 的基本方法

Ray 支援多種佈署模型,從本地單節點到包含數千台機器的叢集。這種靈活性讓開發者可以從小規模開始,隨著需求增長逐步擴展到大規模分散式環境。Ray 的設計理念是讓擴展過程盡可能無縫,最小化程式碼和配置的變更。

pip install -U ray

docker run --rm --shm-size=<shm-size> -t -i <image name>

本地安裝 Ray 透過 pip 套件管理器非常簡單,只需要一個命令即可完成。這種方式適合開發和測試階段,讓開發者能夠快速驗證程式碼邏輯。Docker 方式則提供了更好的環境隔離和可重現性,特別適合需要特定系統依賴或多版本管理的場景。共享記憶體大小的設定對於某些需要大量資料共享的應用很重要,應該根據實際需求適當調整。

啟發與未來趨勢展望

FastAPI 與 Ray 的結合提供了一種高效且可擴充的解決方案,特別是在處理大量並行請求時表現出色。這種架構能夠充分利用現代硬體的多核心能力和雲端基礎設施的彈性擴展特性。未來隨著更多企業採用微服務架構和分散式計算,這種模式將會變得更加普遍。

這種模式不僅提升了系統的可靠性和效能,還降低了開發和維護的複雜度。開發者可以專注於業務邏輯的實作,而將分散式系統的複雜性交給 Ray 框架處理。未來可能會看到更多跨平台、跨語言的支援,使得開發者能夠更靈活地選擇最適合他們需求的工具和技術堆疊。隨著機器學習和人工智慧應用的普及,這種能夠無縫整合運算密集型任務的架構將發揮更大的價值。

使用 Ray 框架在雲端環境中進行分散式計算

Ray 是一個功能強大且靈活的分散式計算框架,特別適合用於機器學習、資料處理及其他高效能計算任務。它不僅能在單機上執行,更強大的是其能夠在雲端環境中擴展,利用多台機器進行分散式計算。Ray 的設計理念是讓分散式程式設計變得像編寫本地程式一樣簡單,同時提供生產級別的效能和可靠性。

安裝 Ray 的基本概念與實踐

在探討雲端佈署之前,先了解如何在本地安裝及使用 Ray。Ray 提供了多種安裝方式,包括直接在本地機器上安裝或使用 Docker 容器。本地安裝適合快速開發和原型驗證,而 Docker 方式則提供了更好的環境一致性和可移植性。

本地安裝可以直接使用 pip 套件管理器完成。使用 Docker 則需要先安裝 Docker 環境,然後拉取官方映像。啟動 Ray 非常簡單,只需要匯入套件並呼叫初始化函數即可。這個初始化過程會建立 Ray 叢集的核心組件,包括排程器、物件儲存和其他必要的服務。

pip install ray

docker run -it --rm rayproject/ray:latest
import ray
ray.init()

使用 Ray Clusters 進行分散式計算的架構

雖然本地安裝對於初步測試和除錯非常有用,但 Ray 的真正威力在於其能夠在多台機器上執行和擴展。Ray 會將這些機器視為邏輯節點,並透過 Docker 映像來管理這些節點。這種設計讓擴展變得非常直觀,只需要增加節點數量而不需要修改應用程式碼。

Ray 叢集由一個頭節點和多個工作節點組成。頭節點負責排程和協調工作,而工作節點則執行實際的運算任務。這種架構提供了良好的可擴展性和容錯能力,即使某些工作節點失敗,系統仍然可以繼續運作。Ray 會自動處理任務的重新排程和資料的重新分配。

Docker 映像與自定義映像的最佳實踐

Ray 提供的 Docker 映像包含了所有必要的程式碼來執行邏輯節點,但並不包含使用者應用程式所需的特定 Python 函式庫。為了解決這個問題,Ray 支援在叢集安裝過程中安裝特定的函式庫,但這會影響節點的建立效能。因此,建議在生產環境中使用自定義映像。

自定義映像可以預先安裝所有必要的依賴套件,大幅減少節點啟動時間。這對於需要快速擴展的應用特別重要,因為每次新增節點時不需要等待套件安裝。自定義映像還可以包含特定的配置檔案和工具,讓環境更加符合生產需求。

FROM rayproject/ray:latest

RUN pip install numpy pandas scikit-learn

在 AWS 上安裝 Ray 的完整流程

AWS 是最受歡迎的雲端服務提供者之一,Ray 支援透過 Boto3 AWS SDK 來進行配置和佈署。這種整合讓在 AWS 上佈署 Ray 叢集變得非常簡單,只需要準備好配置檔案和認證資訊即可。AWS 提供了豐富的運算資源選項,從經濟型實例到高效能 GPU 實例,可以根據工作負載特性選擇最合適的配置。

首先需要在本地配置 AWS 認證資訊,這通常透過 AWS CLI 工具完成。認證檔案包含了存取金鑰和秘密金鑰,Ray 會使用這些資訊來建立和管理 EC2 實例。接著可以使用 YAML 檔案來定義叢集的配置,包括實例類型、數量、區域等參數。

ray up <your location>/ray-aws.yaml

監控和連線叢集是維護和除錯的重要環節。Ray 提供了多種方式來監控叢集狀態,包括查看自動擴展日誌、連線到頭節點終端機,或是直接透過 SSH 存取特定節點。這些工具讓管理者能夠深入了解叢集的運作狀態,及時發現和解決問題。

ray exec ~/Projects/Platform-Infrastructure/middleware/ray/install/ray-aws.yaml 'tail -n 100 -f /tmp/ray/session_latest/logs/monitor*'

ray attach ~/Projects/Platform-Infrastructure/middleware/ray/install/ray-aws.yaml

ssh -o IdentitiesOnly=yes -i /Users/boris/Downloads/id.rsa.ray-boris root@52.118.80.225

在 AWS EC2 控制台中,需要修改安全群組以開放埠 8265 和埠 10001。埠 8265 用於存取 Ray 儀表板,提供了視覺化的叢集監控介面。埠 10001 則用於 gRPC 通訊,是 Ray 內部元件之間通訊的主要管道。適當的防火牆配置既保證了系統的安全性,又確保了必要的通訊能夠順利進行。

在 IBM Cloud 上安裝 Ray 的特殊機制

IBM Cloud 提供了一個獨特的 Gen2 機制來佈署 Ray 叢集。這個機制針對 IBM Cloud 的基礎設施特性進行了最佳化,提供了更好的整合和效能。Lithopscloud 工具簡化了在 IBM Cloud 上佈署 Ray 的過程,自動處理了許多底層的配置細節。

首先需要安裝 Lithopscloud 工具,這是一個專門為 IBM Cloud 設計的 Ray 佈署工具。安裝完成後,可以使用它來生成叢集配置檔案。這個自動生成的檔案包含了基本的叢集配置,但可能需要根據實際需求進行調整。

pip3 install lithopscloud

lithopscloud -o cluster.yaml

生成的 YAML 檔案需要根據實際需求進行修改,特別是為頭節點和工作節點指定不同的映像類型和資源配置。頭節點通常需要較少的運算資源但需要更多的記憶體來管理叢集狀態,而工作節點則需要更強的運算能力來執行實際任務。

available_node_types:
  ray_head_default:
    max_workers: 0
    min_workers: 0
    node_config:
      boot_volume_capacity: 100
      image_id: r006-dd164da8-c4d9-46ba-87c4-03c614f0532c
      instance_profile_name: bx2-4x16
      key_id: r006-d6d823da-5c41-4e92-a6b6-6e98dcc90c8e
      resource_group_id: 5f6b028dc4ef41b9b8189bbfb90f2a79
      security_group_id: r006-c8e44f9c-7159-4041-a7ab-cf63cdb0dca7

在虛擬機器上安裝 Ray 的替代方案

除了使用 Docker 映像之外,您也可以直接在虛擬機器上安裝 Ray。這種方法的優點是可以輕鬆新增額外的軟體,例如 Python 函式庫或其他應用程式。直接安裝還可以獲得更好的效能,因為避免了容器化帶來的額外開銷。這種方式特別適合需要特殊系統配置或驅動程式的場景。

在虛擬機器上安裝 Ray 需要先安裝必要的系統依賴和 Python 環境。接著安裝 Ray 套件和應用程式需要的其他函式庫。啟動 Ray 節點時需要指定一些關鍵參數,包括頭節點的位址、連接埠和認證資訊。驗證連線功能可以確保節點正確加入了叢集。

pip install numpy pandas scikit-learn

ray start --head --port=6379 --redis-password=your_password_here --block

python localPython.py connect_to_cluster()

由於在虛擬機器上設定 Ray 需要較多時間和步驟,建議您先啟動一次叢集,驗證所有配置都正確無誤後,建立一個新映像來移除額外的設定命令。這種方式可以顯著加快後續的叢集擴展速度,因為新節點可以直接使用預配置的映像,而不需要重複執行所有安裝步驟。映像快照還提供了環境一致性的保證,確保所有節點運行在相同的軟體配置下。