返回文章列表

Rust Web 服務 Docker 容器化佈署

本文介紹如何使用 Docker 容器化 Rust Actix Web 服務,包含 Dockerfile 編寫、多階段建置最佳化映像大小、Docker Compose 協調多容器應用等技巧,有效簡化 Rust Web 服務的佈署流程並提升效率。

Web 開發 DevOps

在現代軟體開發流程中,容器化技術已成為不可或缺的一環。本文將探討如何利用 Docker 容器化 Rust Actix Web 服務,從而提升開發效率、簡化佈署流程並確保應用程式在不同環境中的一致性。首先,我們會建立一個基礎的 Docker 映像,然後逐步最佳化,最後使用 Docker Compose 管理多容器應用。

Rust 的高效能和 Actix Web 框架的靈活性使其成為構建 Web 服務的絕佳選擇。然而,將 Rust Web 服務佈署到生產環境時,確保一致性和可靠性至關重要。Docker 提供了輕量級、可移植的解決方案,將應用程式及其依賴項封裝到容器中,從而解決開發環境和生產環境之間的差異問題。透過 Docker,我們可以輕鬆地構建、釋出和執行 Rust Web 服務,並確保其在不同環境中的行為一致。

12 佈署 Web 服務使用 Docker

本章涵蓋以下主題:

  • 簡介 Rust 伺服器和應用的生產佈署
  • 編寫第一個 Docker 容器
  • 建立資料函式庫容器
  • 使用 Docker 封裝 Web 服務
  • 使用 Docker Compose 協調 Docker 容器

在前面的章節中,我們學習瞭如何使用 Rust 建立 Web 服務和 Web 應用。我們還探討了非同步程式設計,甚至涉及了 P2P 架構。我們在本地開發環境中測試了我們的應用程式。但這些只是第一步,最終目標通常是佈署到生產環境。

在本章中,我們將重點關注使用一種流行的生產佈署方法——容器化來封裝軟體。這涉及將應用程式的元件及其依賴項封裝在容器中。然後,這個容器可以佈署在多個環境中,包括雲端。使用容器的優點之一是,應用程式與其他容器保持乾淨的隔離,避免了不相容函式庫的風險。

我們將詳細介紹將 Rust Web 服務容器化所需的步驟。一旦 Web 服務以 Docker 容器的形式提供,從生產佈署的角度來看,它與任何其他語言編寫的 Web 服務或應用程式沒有區別。所有佈署 Docker 容器的標準和選項都將適用。

生產佈署簡介

在這一節中,我們將介紹兩個主題,它們將概述生產佈署在軟體生命週期中的位置,以及 Docker 作為佈署的容器技術的作用。

軟體佈署週期

軟體佈署週期涉及多個級別的開發者單元和整合測試,然後是釋出版本的準備和佈署。一旦釋出版本佈署並執行,系統就會被監控,關鍵引數被測量,並進行最佳化。

雖然生產佈署生命週期中的具體步驟因團隊和 DevOps 技術而異,但圖 12.1 顯示了一組典型的步驟。

圖 12.1 生產佈署生命週期

不同的組織使用的實際開發步驟和術語差異很大,但讓我們來看看這些階段,以大致瞭解:

詳細步驟說明

軟體佈署是一個複雜的過程,涉及多個階段,從開發到最終的生產佈署。每個階段都有其特定的目標和挑戰。以下是軟體佈署生命週期的詳細步驟:

  1. 開發與單元測試:開發人員編寫程式碼並進行單元測試,以確保個別元件的功能正確。

  2. 整合測試:將不同模組或元件整合在一起,並進行測試以確保它們能正確協作。

  3. 構建與封裝:將程式碼編譯或構建成可執行檔案或佈署包,並將其封裝以便於分發。

  4. 測試環境佈署:將封裝好的應用程式佈署到測試環境中,進行進一步的測試,如功能測試、效能測試等。

  5. 自動化測試:執行自動化測試套件,以驗證應用程式的功能、效能和安全性。

  6. 預生產環境佈署:將應用程式佈署到預生產環境(或稱為 staging 環境),進行最後的驗證。

  7. 生產環境佈署:一旦所有測試透過並驗證完成,將應用程式佈署到生產環境中。

  8. 監控與維護:在生產環境中監控應用程式的效能、錯誤日誌等,並根據需要進行維護更新。

這個過程可能根據專案需求、團隊規模和所採用的 DevOps 工具有所不同,但整體流程提供了一個基本的框架,用於管理和最佳化軟體的佈署。

使用 Docker 封裝 Web 服務

Docker 提供了一個輕量級且便攜的方式來封裝應用程式及其依賴項。使用 Docker,我們可以建立一個包含應用程式執行所需的一切的容器,包括程式碼、執行時、函式庫等。這使得應用程式能夠在不同的環境中一致地執行。

建立 Docker 容器

要使用 Docker 封裝我們的 Rust Web 服務,我們首先需要建立一個 Dockerfile。Dockerfile 是一個文字檔案,包含了用於構建 Docker 映像的所有命令。

# 使用官方 Rust 映像作為基礎
FROM rust:latest

# 設定工作目錄
WORKDIR /app

# 複製 Cargo.toml 和 Cargo.lock
COPY Cargo.toml Cargo.lock ./

# 複製原始碼
COPY src ./src

# 編譯 Rust 程式
RUN cargo build --release

# 暴露埠號
EXPOSE 8080

# 執行應用程式
CMD ["cargo", "run", "--release"]

建置與執行 Docker 容器

一旦我們有了 Dockerfile,我們就可以使用以下命令來構建和執行我們的 Docker 容器:

# 建置 Docker 映像
docker build -t my-rust-app .

# 執行 Docker 容器
docker run -p 8080:8080 my-rust-app

這將啟動一個包含我們 Rust Web 服務的 Docker 容器,並將主機的 8080 連線埠對映到容器的 8080 連線埠。

使用 Docker Compose 協調多個容器

對於涉及多個服務(如資料函式庫和 Web 服務)的更複雜應用程式,我們可以使用 Docker Compose。Docker Compose 是一個用於定義和執行多容器 Docker 應用的工具。

docker-compose.yml 範例

version: '3'
services:
  web:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - db
    environment:
      - DATABASE_URL=postgres://user:password@db:5432/database

  db:
    image: postgres
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=database
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

這個 docker-compose.yml 檔案定義了兩個服務:webdbweb 服務是我們的 Rust Web 服務,而 db 服務是一個 PostgreSQL 資料函式庫。web 服務依賴於 db 服務,這意味著 db 服務必須在 web 服務啟動之前啟動。

使用 Docker Compose 管理多個容器

要使用 Docker Compose 建置和啟動所有服務,只需執行:

docker-compose up --build

這個命令會根據需要建置映像並啟動容器。它還會設定必要的網路,以便服務之間可以相互通訊。

使用Docker容器化技術佈署Rust網路服務

在軟體開發的生命週期中,從編寫程式碼到佈署至生產環境,需要經過多個階段,包括本地建置、開發測試、整合測試、封裝發行、組態與佈署、安全設定以及執行監控。在本章節中,我們將重點關注如何使用Docker容器化技術來封裝和佈署Rust網路服務。

軟體開發與佈署流程

  1. 建置(Build):開發人員在本地環境中編寫或修改程式碼,並建置二進位檔案。通常,這是一個開發建置,以便於除錯和減少建置時間。
  2. 開發測試(Dev test):開發人員在本地開發環境中執行單元測試。
  3. 預發布(Staging):程式碼與其他分支合併,並佈署到預發布環境中。在此階段,進行涉及其他開發人員編寫的程式碼和模組的整合測試。
  4. 封裝發行(Package for release):在成功完成整合測試後,建構最終的生產建置。封裝方式取決於二進位檔案的佈署方式,例如獨立二進位檔案、容器或公有雲端服務。
  5. 組態與佈署(Configure and deploy):生產二進位檔案被佈署到目標環境(例如虛擬機器),並設定必要的組態和環境引數。這也是與生產基礎設施中的其他元件建立連線的階段。
  6. 安全設定(Secure):組態額外的安全需求,例如身份驗證、授權、網路和伺服器安全等。
  7. 執行與監控(Operate and monitor):啟動伺服器以接收網路請求,並使用網路、伺服器、應用程式和雲端監控工具監控伺服器的效能。

在本章節中,我們將使用Docker Compose來簡化組態、自動化建置以及啟動和停止執行網路服務所需的Docker容器集合。

你將學到的內容

  • 建置發行二進位檔案和封裝:學習如何將Rust伺服器建置為Docker映像檔,以便佈署到任何具有容器執行時的宿主機上。你將學習如何編寫Dockerfile、建立Docker卷和網路、組態環境變數、執行多步驟Docker建置,以及減小最終Docker映像檔的大小。
  • 組態和佈署網路服務:學習如何使用Docker Compose定義網路服務和Postgres資料函式庫容器的執行時組態,定義它們之間的依賴關係,組態執行時環境變數,初始化Docker建置,以及透過簡單命令啟動和停止Docker容器。

讓我們從簡要介紹Docker開始。

Docker容器基礎

容器技術改變了軟體的建置、佈署和管理方式,透過彌合開發和IT維運團隊之間的差距實作了DevOps自動化。Docker既是公司的名稱,也是軟體產品的名稱(www.docker.com)。

Docker容器架構

圖表翻譯: 上圖展示了Docker容器的分層架構。硬體層之上是作業系統,作業系統之上執行Docker引擎,Docker引擎之下可以執行多個獨立的容器,每個容器內包含一個或多個應用程式。

Docker容器是完全隔離的環境,具有自己的行程、網路介面和卷掛載。Docker容器的一個重要特點是它們最終分享相同的作業系統核心。傳統的虛擬機器是物理硬體的抽象,將一台物理伺服器轉變為多台“邏輯”伺服器。虛擬機器管理程式允許多台虛擬機器在單台機器上執行,每台虛擬機器都包含作業系統的完整副本。另一方面,容器是應用層的抽象,將程式碼和依賴項封裝在一起。多個容器在同一台物理機器上執行,並與其他容器分享OS核心。

程式碼範例:編寫Dockerfile

# 使用官方Rust映像檔作為基礎映像檔
FROM rust:alpine

# 設定工作目錄
WORKDIR /app

# 複製Cargo.toml和Cargo.lock
COPY Cargo.toml Cargo.lock ./

# 建置依賴項
RUN cargo build --release

# 複製原始碼
COPY . .

# 建置應用程式
RUN cargo build --release

# 暴露埠號
EXPOSE 8080

# 執行應用程式
CMD ["cargo", "run", "--release"]

內容解密:

  1. FROM rust:alpine:使用官方Rust映像檔作為基礎映像檔,這裡選擇了根據Alpine Linux的版本,以減小映像檔大小。
  2. WORKDIR /app:設定工作目錄為/app
  3. COPY Cargo.toml Cargo.lock ./:將Cargo.tomlCargo.lock複製到工作目錄,用於建置依賴項。
  4. RUN cargo build --release:建置依賴項,這裡使用--release旗標進行最佳化建置。
  5. COPY . .:將當前目錄下的所有檔案複製到工作目錄。
  6. RUN cargo build --release:再次建置應用程式,這次包含了原始碼。
  7. EXPOSE 8080:暴露8080埠號,供外部存取。
  8. CMD ["cargo", "run", "--release"]:設定預設命令,用於執行應用程式。

透過本章節,你將能夠使用Docker容器化技術來佈署Rust網路服務,提高開發效率和佈署靈活性。

使用Docker容器化Rust Web服務

在軟體開發過程中,開發環境與生產環境之間的差異常常導致佈署問題。Docker容器技術有效地解決了這一問題。開發者可以在Dockerfile中定義基礎設施組態、環境設定、依賴下載和建置指令。Dockerfile是一種YAML語法的文字檔,用於指定Docker映像的引數,如基礎映像、環境變數、檔案系統掛載、暴露的連線埠等。

Docker容器技術的優勢

Dockerfile被建置成自訂的Docker映像,這種映像是建立多個容器執行的範本。開發者可以在本地測試Docker映像,然後將其交付給維運團隊進行生產佈署。由於Docker映像保證在任何Docker主機上執行的一致性,因此大大減少了軟體應用在生產環境中的佈署摩擦和人為錯誤。

安裝Docker開發環境

要在本機開發機器或伺服器(macOS、Windows或Linux)上安裝Docker開發環境,請參考Docker官方檔案:https://docs.docker.com/get-docker/。

編寫第一個Docker容器

本文將檢查Docker安裝、編寫Dockerfile並將其建置成Docker映像,以及使用多階段建置最佳化最終Docker映像的大小。

檢查Docker安裝

首先,從終端機檢查Docker安裝:

docker --version

預期輸出類別似於:

Docker version 20.10.16, build aa7e414

接下來,測試官方Docker映像:

docker pull hello-world
docker images
docker run hello-world

如果看到「Hello from Docker!」訊息,則表示Docker環境正常運作。

編寫簡單的Docker容器

建立一個新的Rust專案:

cargo new --bin docker-rust
cd docker-rust

Cargo.toml中新增Actix Web依賴:

[dependencies]
actix-web = "4.2.1"

編輯src/main.rs

use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn gm() -> impl Responder {
    HttpResponse::Ok().body("Hello, Good morning!")
}

async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello there!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(gm)
            .route("/hello", web::get().to(hello))
    })
    .bind(("0.0.0.0", 8080))?
    .run()
    .await
}

執行cargo run並測試:

  • localhost:8080
  • localhost:8080/hello

容器化Web服務

建立Dockerfile-basic檔案:

# 使用主要的Rust Docker映像
FROM rust

# 將應用程式複製到Docker映像中
COPY . /app

# 設定工作目錄
WORKDIR /app

# 建置應用程式
RUN cargo build --release

# 啟動應用程式
CMD ["./target/release/docker-rust"]

建置Docker映像:

docker build -f Dockerfile-basic . -t docker-rust-basic

內容解密:

  1. FROM rust:使用官方Rust映像作為基礎映像。
  2. COPY . /app:將目前目錄下的所有檔案複製到容器中的/app目錄。
  3. WORKDIR /app:將工作目錄設定為/app
  4. RUN cargo build --release:在容器中執行cargo build --release指令建置Rust應用程式。
  5. CMD ["./target/release/docker-rust"]:設定容器啟動時執行的預設指令。

本章節展示瞭如何使用Docker容器化Rust Web服務,從而簡化了開發和佈署流程。接下來,我們將進一步最佳化Docker映像的大小。

使用多階段Docker建置最佳化Rust Actix Web服務

在前面的章節中,我們已經成功地將一個基本的Rust Actix Web服務容器化,但是生成的Docker映像大小達到1.32 GB,這對於一個簡單的Web服務來說太大了。本文將介紹如何使用多階段Docker建置來最佳化Docker映像的大小。

多階段Docker建置

多階段Docker建置是一種將Docker映像建置過程分成多個階段的方法,每個階段可以使用不同的基礎映像。這種方法的優點是可以減少最終Docker映像的大小,提高安全性,並且可以自動化建立多個版本的二進位制檔案。

建立新的Dockerfile

首先,在專案根目錄下建立一個新的Dockerfile,名為Dockerfile-lite,並新增以下內容:

# 使用主要的Rust Docker映像
FROM rust as build
# 將應用程式複製到Docker映像中
COPY . /app
# 設定工作目錄
WORKDIR /app
# 建置應用程式
RUN cargo build --release
# 使用Google Distroless作為執行階段映像
FROM gcr.io/distroless/cc-debian11
# 從建置階段複製應用程式
COPY --from=build /app/target/release/docker-rust /app/docker-rust
WORKDIR /app
# 啟動應用程式
CMD ["./docker-rust"]

建置Docker映像

執行以下命令來建置Docker映像:

docker build -f Dockerfile-lite . -t docker-rust-lite

檢查Docker映像

執行以下命令來檢查建置好的Docker映像:

docker images

你應該會看到類別似以下的輸出:

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker-rust-lite    latest              40103591baaf        12 seconds ago      31.8MB

執行Docker映像

執行以下命令來執行Docker映像:

docker run -p 8080:8080 -t docker-rust-lite

測試Web服務

開啟瀏覽器,測試以下URL:

  • localhost:8080
  • localhost:8080/hello

你應該會看到相應的問候訊息。

多階段Docker建置的原理

多階段Docker建置使用多個FROM陳述式來參考特定階段的映像。每個階段可以使用AS關鍵字命名。在Dockerfile-lite範例中,我們有兩個階段。第一個階段建置了一個釋出的二進位制檔案。第二個階段使用Google Distroless作為執行階段映像,並複製了之前建立的釋出二進位制檔案,從而產生了一個較小的Docker映像。

圖表說明:多階段Docker建置流程

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Rust Web 服務 Docker 容器化佈署

package "Docker 架構" {
    actor "開發者" as dev

    package "Docker Engine" {
        component [Docker Daemon] as daemon
        component [Docker CLI] as cli
        component [REST API] as api
    }

    package "容器運行時" {
        component [containerd] as containerd
        component [runc] as runc
    }

    package "儲存" {
        database [Images] as images
        database [Volumes] as volumes
        database [Networks] as networks
    }

    cloud "Registry" as registry
}

dev --> cli : 命令操作
cli --> api : API 呼叫
api --> daemon : 處理請求
daemon --> containerd : 容器管理
containerd --> runc : 執行容器
daemon --> images : 映像檔管理
daemon --> registry : 拉取/推送
daemon --> volumes : 資料持久化
daemon --> networks : 網路配置

@enduml

圖表翻譯: 此圖示呈現了多階段Docker建置的流程。第一階段負責建置應用程式,第二階段則負責建立最終的執行環境並複製必要的二進位制檔案。