返回文章列表

Actix-web 生產環境準備:日誌、SSL/TLS 與無伺服器部署

本文為一份 Actix-web 應用程式的生產環境準備指南,涵蓋了日誌記錄、SSL/TLS 安全設定,並以部署到 AWS Lambda 無伺服器平台為例,展示了現代化的 Rust Web 應用部署流程。

Web 開發 DevOps

將一個 Actix-web 應用程式從開發環境推向生產環境,需要考慮可觀測性、安全性與部署策略。本文將引導您完成一個標準的生產環境準備流程:首先,我們會整合日誌系統以增強應用的可觀測性;接著,為應用啟用 SSL/TLS 來保障資料傳輸安全;最後,以部署到 AWS Lambda 為例,展示如何利用無伺服器架構來簡化維運並實現彈性擴展。

第一階段:增強可觀測性 - 日誌記錄

在生產環境中,日誌是追蹤系統行為、診斷問題的生命線。Actix-web 透過中介軟體 (Middleware) 的方式,可以輕鬆整合日誌功能。

1. 啟用 Logger 中介軟體

我們使用 actix_web::middleware::Logger 來自動記錄所有傳入的 HTTP 請求。

// 在 main.rs
use actix_web::middleware::Logger;
use std::env;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // 設定日誌級別,可由環境變數 RUST_LOG 控制
    env::set_var("RUST_LOG", "info");
    env_logger::init();

    HttpServer::new(|| {
        App::new().wrap(Logger::default())
        // ... 註冊路由
    })
    // ...
    .run()
    .await
}

程式碼解說

  • env_logger::init(): 初始化日誌記錄器。
  • .wrap(Logger::default()): 將 Logger 中介軟體應用到整個 App。它會為每個請求產生類似 Apache 的存取日誌。

2. 新增自定義日誌

除了請求日誌,我們還可以在業務邏輯的關鍵路徑中加入自定義日誌,以記錄特定事件或錯誤。

use log::{error, info};

async fn some_handler() -> impl Responder {
    info!("正在處理 some_handler 請求...");
    // ... 業務邏輯 ...
    if let Err(e) = some_critical_operation() {
        error!("關鍵操作失敗: {:?}", e);
        return HttpResponse::InternalServerError().finish();
    }
    HttpResponse::Ok().body("操作成功")
}

透過 RUST_LOG 環境變數 (例如 RUST_LOG=debug cargo run),我們可以靈活地控制日誌的詳細程度。

第二階段:強化傳輸安全 - SSL/TLS 設定

在生產環境中,使用 HTTPS 是保護資料在傳輸過程中不被竊聽或篡改的標準做法。我們將使用 OpenSSL 來產生一個自簽名證書,並在 Actix-web 中啟用它。

1. 產生證書與私鑰

使用 openssl 命令列工具產生一個有效期為一年的自簽名證書 (cert.pem) 和一個無密碼的私鑰 (key.pem)。

openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -days 365 -subj "/CN=localhost"

2. 在 Actix-web 中啟用 SSL/TLS

首先,在 Cargo.toml 中為 actix-web 啟用 openssl 功能:

[dependencies]
actix-web = { version = "4", features = ["openssl"] }
openssl = "0.10"

接著,修改 main.rs 來載入證書並綁定到 bind_openssl

use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // ... (日誌設定) ...

    // 載入 SSL 金鑰與證書
    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
    builder.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
    builder.set_certificate_chain_file("cert.pem").unwrap();

    HttpServer::new(|| {
        App::new().wrap(Logger::default())
        // ...
    })
    .bind_openssl("127.0.0.1:8443", builder)? // 使用 bind_openssl
    .run()
    .await
}

現在,您的伺服器將在 https://127.0.0.1:8443 上提供服務。

第三階段:現代化部署 - 無伺服器 (AWS Lambda)

無伺服器 (Serverless) 架構讓開發者只需專注於業務邏輯,而無需管理底層的伺服器、作業系統和執行環境。AWS Lambda 是領先的無伺服器計算平台,並且官方提供了對 Rust 的支援。

1. 為何選擇無伺服器?

  • 簡化維運: 無需管理伺服器,AWS 會處理所有底層基礎設施。
  • 彈性擴展: 根據請求量自動擴展,從零到每秒數千次請求。
  • 成本效益: 只需為實際的計算時間付費,閒置時不產生費用。

2. 使用 cargo-lambda 進行部署

cargo-lambda 是一個社群開發的工具,極大地簡化了在 AWS Lambda 上建構、測試和部署 Rust 函式的流程。

  • 安裝 cargo-lambda:
    pip3 install cargo-lambda
    
  • 建立一個 Lambda 專案:
    cargo lambda new my-lambda-function
    
  • 編寫函式邏輯: 在 src/main.rs 中,我們使用 lambda_http crate 來處理來自 API Gateway 的 HTTP 事件。
    use lambda_http::{run, service_fn, Body, Error, Request, Response};
    
    async fn function_handler(_event: Request) -> Result<Response<Body>, Error> {
        let resp = Response::builder()
            .status(200)
            .header("content-type", "text/html")
            .body("Hello from Rust on AWS Lambda!".into())
            .map_err(Box::new)?;
        Ok(resp)
    }
    
    #[tokio::main]
    async fn main() -> Result<(), Error> {
        tracing_subscriber::fmt::init();
        run(service_fn(function_handler)).await
    }
    
  • 建構與部署:
    # 1. 建構適用於 Lambda 環境的執行檔
    cargo lambda build --release --arm64 --output-format zip
    
    # 2. 部署到 AWS
    cargo lambda deploy
    

圖表解說:Rust 應用部署到 AWS Lambda 流程

此圖表展示了從本地開發到最終部署至 AWS Lambda 的完整工作流程。

@startuml
!theme _none_
skinparam dpi auto
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam minClassWidth 100
skinparam defaultFontSize 16
title Rust on AWS Lambda 部署流程

actor "開發者" as Dev

box "本地環境"
  participant "Rust 專案" as Proj
  participant "cargo-lambda" as CargoLambda
end box

box "AWS 雲端"
  participant "S3 Bucket" as S3
  participant "AWS Lambda" as Lambda
  participant "API Gateway" as APIGW
end box

Dev -> Proj : 編寫 Rust 程式碼
Dev -> CargoLambda : 執行 `cargo lambda build`
CargoLambda -> Proj : 編譯並打包成 .zip
Dev -> CargoLambda : 執行 `cargo lambda deploy`
CargoLambda -> S3 : 上傳 .zip 部署包
CargoLambda -> Lambda : 建立/更新 Lambda 函式
CargoLambda -> APIGW : 建立/更新 API Gateway 觸發器

@enduml

透過以上三個階段的準備,您的 Actix-web 應用程式不僅具備了生產環境所需的可觀測性和安全性,還能利用無伺服器架構的優勢,實現高效、彈性的現代化部署。