Linux 多租戶環境的隔離策略 文章首圖

Linux 多租戶環境的隔離策略

Linux 多租戶環境的隔離策略

在現代雲端運算與虛擬化時代,「多租戶」(Multi-tenancy)已成為基礎設施的核心概念。所謂多租戶,指的是多個使用者(租戶)共用同一套物理硬體資源,但在邏輯上必須保持完全隔離,確保彼此的數據、進程與網路流量互不干擾。對於系統管理員而言,如何在 Linux 環境中實現這種隔離,不僅是安全性的要求,更是穩定性的關鍵。

許多初學者誤以為只要建立不同的使用者帳號(User)就能實現隔離,這是一大迷思。Unix 系統中的權限管理確實能防止使用者 A 讀取使用者 B 的家目錄,但無法阻止他們共享相同的 CPU 核心、記憶體頁或網路埠號。當一個租戶的應用程式發生記憶體洩漏時,可能會拖垮整個系統的效能,甚至導致其他租戶的服務中斷。因此,我们需要更底層、更嚴格的隔離技術。

容器化:最實用的輕量隔離方案

對於大多數應用場景,Docker 或 Podman 等容器技術是目前實現多租戶隔離的主流選擇。容器利用 Linux 核心的 NamespacesCgroups 技術,在單一作業系統核心上運行多個獨立的用戶空間。

Namespaces 負責隔離資源視圖(如網路介面、PID 列表、檔案系統),而 Cgroups 則負責資源限制(如 CPU 使用率、記憶體上限)。以下我們以 Docker 為例,展示如何為兩個不同的租戶設定資源隔離。

首先,安裝 Docker 並啟動服務:

sudo apt update
sudo apt install -y docker.io
sudo systemctl enable --now docker

假設我們有租戶 Alpha 和租戶 Beta,他們分別運行不同的 Web 服務。為了防止租戶 Alpha 佔用過多記憶體導致系統崩潰,我們可以為其容器設定資源限制。

建立租戶 Alpha 的容器,限制其記憶體使用量為 512MB,CPU 使用率上限為 50%:

docker run -d \
  --name tenant_alpha \
  --memory=512m \
  --cpus=0.5 \
  --memory-swap=1g \
  nginx:alpine

接著,建立租戶 Beta 的容器,給予較高的資源配額:

docker run -d \
  --name tenant_beta \
  --memory=1g \
  --cpus=1.0 \
  --memory-swap=2g \
  nginx:alpine

透過 docker stats 指令,你可以即時監控這兩個租戶的資源使用狀況,確保他們沒有越界。這種方式不僅隔離了進程空間,更透過硬體層級的資源配額,實現了真正的公平性。

進階隔離:利用 Cgroups v2 進行更細緻的控制

雖然容器提供了便利的抽象層,但在某些高安全性需求或特定應用場景下,你可能需要直接操作 Linux 核心的 Cgroups v2 機制。Cgroups v2 是現代 Linux 核心(Kernel 5.4+)的標準,它整合了原本分散的 memory、cpu、io 等控制器,配置更加簡潔且強大。

在 Ubuntu 22.04 或 Debian 12 上,Cgroups v2 通常預設已啟用。你可以透過以下指令檢查狀態:

cat /sys/fs/cgroup/cgroup.controllers

若你需要為某個特定服務手動建立隔離組,可以透過 systemd 來實現,因為 systemd 是 Cgroups 的主要管理者。編輯或建立一個 systemd 服務單元檔,例如 /etc/systemd/system/tenant_gamma.service

[Unit]
Description=Tenant Gamma Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/my_application
# 限制記憶體上限為 256MB
MemoryMax=256M
# 限制 CPU 佔用比例為 20%
CPUWeight=20
# 限制 IO 頻寬
IOWeight=100

[Install]
WantedBy=multi-user.target

重新載入 systemd 設定並啟動服務:

sudo systemctl daemon-reload
sudo systemctl start tenant_gamma.service

這樣做的好處是,即使沒有使用 Docker,任何透過 systemd 管理的服務都能獲得嚴格的資源隔離。如果租戶 Gamma 的應用程式試圖消耗過多記憶體,核心會直接殺死該進程,保護其他租戶不受影響。

常見問題與解決方案

Q1:容器內的進程可以看到宿主機的 PID 1 嗎? 這取決於 Namespaces 的設定。預設情況下,Docker 會為每個容器建立獨立的 PID Namespace,因此容器內的 PID 1 是該容器內的 init 進程,與宿主機無關。然而,如果未正確配置安全上下文(Security Context),某些特權容器可能擁有過高的權限。務必避免在生產環境中使用 --privileged 參數,並嚴格限制容器的 Capabilities。

Q2:如何防止租戶透過網路掃描其他租戶的埠號? 單純的容器隔離並不完全等同於網路隔離。預設情況下,Docker 使用橋接網路(Bridge Network),雖然進程隔離了,但網路封包仍可能透過 NAT 或 iptables 規則互相影響。建議為每個租戶建立專屬的 Docker Network,並設定嚴格的防火牆規則(iptables 或 nftables)來阻斷租戶間的直接通訊。例如,使用 docker network create --driver bridge tenant_network 並僅允許特定的 IP 範圍互通。

小結

Linux 多租戶環境的隔離並非單一技術可以解決,而是需要層層防護的策略。對於大多數開發與部署場景,Docker 或 Podman 結合 Cgroups 資源限制,已經能提供足夠的隔離效果與管理便利性。然而,對於高安全性要求的環境,理解底層的 Namespaces 與 Cgroups v2 機制,並善用 systemd 進行服務級別的資源管控,是確保系統穩定性的必要技能。記住,隔離不僅是為了安全,更是為了在資源競爭中維持服務的品質與公平性。