一、前言 – 容器技術發展史
容器技術是現今計算技術的重要組成部分,其發展歷程可以追溯到很早的計算機系統提供的進程隔離工具。以下是容器技術的發展歷程,其中涵蓋了從早期的進程隔離技術到現代云計算和云原生的演變:
① Jail 時代
- 1979 年 - chroot 的發明: 在貝爾實驗室開發的第 7 版 Unix 中引入了 chroot 系統調用。它通過將進程的根目錄更改為文件系統中的新位置,實現文件系統的隔離,這一技術被稱為 Chroot Jail。可以說,這奠定了容器技術的基礎。
- 2000 年 - FreeBSD Jail: 為了實現安全性和易于管理,FreeBSD Jail 在原有的 chroot 功能上增加了獨立的進程和網絡空間,從而為托管環境中的客戶服務提供了更好的資源隔離。
- 2001 年 - Linux VServer: 類似于 FreeBSD Jail,Linux VServer 將文件系統、網絡地址、內存等資源進行分區,實現了更為復雜的資源隔離。
- 2004 年 - Solaris Containers: 這一技術結合了系統資源控制和區域隔離,并增加了快照和克隆功能,為后來的云計算技術發展鋪平了道路。
② 云時代
- 2006 年 - 云計算概念的提出: Google 提出了當數據量達到現在的1000倍甚至10000倍時,該如何處理的問題。云計算的概念引領了新的資源管理和分配需求。
- 2006 年 - Process Containers 和 cgroups: 由 Google 推出,用于限制、統計和隔離一組進程的資源使用。一年后,它被更名為"Control Groups (cgroups)",并最終并入 Linux 內核。
- 2008 年 - LXC 和 GAE 的推出: LXC 是第一個完整的 Linux 容器管理器實現。同時,Google 推出了 Google App Engine,將開發平臺作為一種服務提供。
③ 云原生時代
- 2011 年 - Warden 和 Kubernetes 的出現: CloudFoundry 推出 Warden,解決多主機環境下的容器管理問題。
- 2013 年 - Docker 的問世與風靡: Docker 作為一個工具和生態系統,將應用程序及其依賴打包到可以跨平臺運行的容器中。Docker 迅速成為業界標準,帶動了容器生態的繁榮。
- 2014 年 - Kubernetes 的崛起: Google 發布了 Kubernetes,支持對容器的編排和管理。隨著各大公司加入,Kubernetes 成為了云原生架構的重要部分。
- 2015-2016 年 - OCI 和 CNCF 的成立: 為了解決行業標準問題,Docker 和其他公司成立 OCI,以制定容器和鏡像的標準。與此同時,CNCF 的成立,推動了云原生計算的發展。
k8s 成為云原生技術事實標志
- 2016 年 - CRI 標準的發布: 為了將 Kubernetes 和特定的容器運行時解耦,Google 和紅帽主導發布 CRI 接口標準。
- 2017 年 - containerd 的發展: 隨著 containerd 成為標準的 CRI,實現了容器運行時的標準化,各大廠商紛紛采用 containerd 和 Kubernetes 作為云原生解決方案的基礎。
時間 | 事件 |
---|---|
2001 | Linux Vserver 分區 |
2008 | LVS – 第一個 完整 的 Linux 容器管理器 |
2013 | Docker – 打包容器 |
2014 | Google K8s – 編排容器 – 云原生架構 |
2016 | Docker – OCI 標準 |
2017 | K8s API標準 – CRI |
容器技術自其誕生以來,通過文件系統和進程隔離逐步發展到今天的云原生生態系統。在這一過程中,技術創新帶來了資源的高效利用和管理的便捷性。尤其是隨著 Kubernetes 的普及和標準化,容器已成為現代應用開發和部署的核心技術之一,并對云計算產業的發展產生了深遠影響。
二、虛擬化 & 容器化
1. 什么是虛擬化、容器化
物理機:實際的服務器或者計算機。
- 相對于虛擬機而言的對實體計算機的稱呼。
- 物理機提供給虛擬機以硬件環境,有時也稱為“寄主”或“宿主”。
虛擬化:是指通過虛擬化技術將一臺計算機虛擬為多臺邏輯計算機。
- 在一臺計算機上同時運行多個邏輯計算機,每個邏輯計算機可運行不同的操作系統,并且應用程序都可以在相互獨立的空間內運行而互不影響,從而顯著提高計算機的工作效率。
容器化:容器化是一種虛擬化技術,又稱操作系統層虛擬化(Operating system levelvirtualization),這種技術將操作系統內核虛擬化,可以允許用戶空間軟件實例(instances)被分割成幾個獨立的單元,在內核中運行,而不是只有一個單一實例運行。
- 這個軟件實例,也被稱為是一個容器(containers)。對每個實例的擁有者與用戶來說,他們使用的服務器程序,看起來就像是自己專用的。
- 容器技術是虛擬化的一種,而 docker是現今容器技術的事實標準
類比理解
- 物理機就像一個莊園,獨立占用了一塊土地,花園都是自己的,其他人無法共享使用。
- 虛擬機相當于開發商的一個樓盤,一棟樓一套房子一戶人家,共享一塊宅基地,共享小區的花園,共享小區的游樂設施
- 容器相當于幾個人一起合租一套房,共享這套房子的衛生間、共享廚房、共享 WiFì,只有衣服、電腦等私人物品是你自己的。
2. 為什么要虛擬化、容器化?
我們從上面的歷史發展來看,虛擬化和容器化的最主要目的就是資源隔離,隨著資源隔離的實現逐漸也帶來了更大的收益。
- 資源利用率高:將利用率較低的服務器資源進行整合,用更少硬件資源運行更多業務,降低 IT 支出和運維管理成本。
- 比如上圖中我們的土地直接復用,使用這塊土地的人多了,但是成本還是莊園那塊地
- 環境標準化:一次構建,隨處執行。實現執行環境的標準化發布,部署和運維。
- 開發過程中一個常見的問題是 環境一致性問題。由于開發環境、測試環境、生產環境不一致,導致有些bug 并未在開發過程中被發現。
- 而 Docker 的鏡像提供了除內核外完整的運行時環境確保了應用運行環境一致性,從而不會再出現 「這段代碼在我機器上沒問題啊」 這類問題。
- 資源彈性收縮:根據業務情況,動態調整計算、存儲、網絡等硬件及軟件資源。
- 比如遇到雙 11 了,把服務擴容 100 個,雙 11 過去了, 把擴容的 100 個收回去。
- 差異化環境提供:同時提供多套差異化的執行環境,限制環境使用資源。
- 比如我的服務一個以來 Ubuntu 操作系統,一個服務依賴 CentOS 操作系統,但是沒有預算購買兩個物理機,這個時候容器化就能很好的提供多種不同的環境。
- 沙箱安全:為避免不安全或不穩定軟件對系統安全性、穩定性造成影響,可使用虛擬化技術構建虛擬執行環境。
- 比如我在容器里面執行
rm -f/*
不會把整個服務器搞死,也不影響其他人部署的程序使用。
- 比如我在容器里面執行
- 容器對比虛擬機更輕量,啟動更快:傳統的虛擬機技術啟動應用服務往往需要數分鐘,而 Docker 容器應用,由于直接運行于宿主內核,無需啟動完整的操作系統,因此可以做到秒級、甚至毫秒級的啟動時間。
- 大大的節約了開發、測試、部署的時間。docker不需要虛擬內核,所以啟動可以更快,相當于 windows 的開機時間省去了。
- 維護和擴展容易:Docker 使用的分層存儲以及鏡像的技術,使得應用重復部分的復用更為容易,也使得應用的維護更新更加簡單,基于基礎鏡像進一步擴展鏡像也變得非常簡單。
- 此外,Docker 團隊同各個開源項目團隊一起維護了一大批高質量的 官方鏡像,既可以直接在生產環境使用,又可以作為基礎進一步定制,大大的降低了應用服務的鏡像制作成本。
- 比如 docker hub 提供了很多鏡像,各個系統的一個命令就可以拿到了,研發也可以自己定制鏡像分享給各個產品。
3. 虛擬化常見類別
虛擬機:存在于硬件層和操作系統層間的虛擬化技術。
- 虛擬機通過“偽造”一個硬件抽象接口將一個操作系統以及操作系統層以上的層嫁接到硬件上,實現和真實物理機幾乎一樣的功能。
- 比如我們在一臺 Windows 系統的電腦上使用 Android 虛擬機,就能夠用這臺電腦打開 Android 系統上的應用。
容器:存在于操作系統層和函數庫層之間的虛擬化技術。
- 容器通過“偽造”操作系統的接口將函數庫層以上的功能置于操作系統上。以 Docker 為例,其就是一個基于 Linux 操作系統的 Namespace 和 Cgroup 功能實現的隔離容器,可以模擬操作系統的功能。
- 簡單來說,如果虛擬機是把整個操作系統封裝隔離,從而實現跨平臺應用的話,那么容器則是把一個個應用單獨封裝隔離,從而實現跨平臺應用。
- 所以 容器體積比虛擬機小很多,理論上占用資源更少M。
- 容器化就是應用程序級別的虛擬化技術。
- 容器提供了將應用程序的代碼、運行時、系統工具、系統庫和配置打包到一個實例中的標準方法。容器共享一個內核(操作系統),它安裝在硬件上。
JVM 之類的虛擬機:存在于函數庫層和應用程序之間的虛擬化技術。
- Java 虛擬機同樣具有跨平臺特性,所謂跨平臺特性實際上也就是虛擬化的功勞。
- 因為 Java 語言是 調用操作系統函數庫 的,JVM 就是在應用層與函數庫層之間建立一個 抽象層,對下通過不同的版本適應不同的操作系統函數庫,對上提供統一的運行環境交給程序和開發者,使開發者能夠調用不同操作系統的函數庫。
補充:應用程序執行環境分層
- 硬件層:提供硬件抽象,包括指令集架構、硬件設備及硬件訪問接口
- 操作系統層:提供系統調用接口,管理硬件資源
- 程序庫層:提供數據結構定義及函數調用接口
三、常見虛擬化實現方式
1. 主機虛擬化(虛擬機)實現
主機虛擬化的原理是通過在物理服務器上安裝一個 虛擬化層 來實現。這個虛擬化層可以在物理服務器和客戶操作系統之間建立虛擬機,使得它們可以獨立運行。
從軟件框架的角度上,根據虛擬化層是直接位于硬件之上還是在一個宿主操作系統之上,將虛擬化劃分為
Type1
和Type2
- Type1 類的
Hypervisor
:是一種系統軟件,它充當計算機硬件和虛擬機之間的中介,負責有效地分配和利用由各個虛擬機使用的硬件資源,這些虛擬機在物理主機上單獨工作,因此,Hypervisor 也稱為 虛擬機管理器- 特點:直接運行在硬件之上,沒有宿主機操作系統,Hypervisor 直接控制硬件資源和客戶機
- 典型框架為Xen、VmwareESX
- Type2 類的
Hypervisor
:運行在一個宿主機操作系統之上(Vmware Workstation) 或者 系統里面,Hypervisor 作為宿主機操作系統中的一個應用程序,客戶機就是在宿主機操作系統上的一個進程。
2. 容器虛擬化實現
容器虛擬化,有別于主機虛擬化,是操作系統層的虛擬化。
基本原理:通過 namespace
進行各程序的隔離,加上 cgroups
進行資源的控制,以此來進行虛擬化。
2.1 Namespace(命名空間)
-
定義:namespace 是 Linux 內核用來隔離內核資源的方式。
-
作用:實現進程、文件系統、用戶等資源的隔離。
-
具體實現:通過將一個或多個進程指定在同一個namespace中,使得這些進程只能看到與自己相關的資源。
注意:Linux namespaces 是對全局系統資源的一種封裝隔離,使得處于不同 namespace 的進程擁有獨立的全局系統資源,改變一個 namespace 中的系統資源只會影響當前namespace 里的進程,對其他 namespace 中的進程沒有影響。
Namespace | 系統調用參數 | 隔離的全局系統資源 | 內核版本 |
---|---|---|---|
UTS | CLONE_NEWUTS | 主機名和域名 | 2.6.19 |
IPC | CLONE_NEWIPC | 信號量、消息隊列和共享內存 – 進程間通信 | 2.6.19 |
PID | CLONE_NEWPID | 進程編號 | 2.6.24 |
Network | CLONE_NEWNET | 網絡設備、網絡棧、端口等 | 2.6.29 |
Mount | CLONE_NEWNS | 文件系統掛載點 | 2.4.19 |
User | CLONE_NEWUSER | 用戶和用戶組 | 3.8 |
以上命名空間在容器環境下的隔離效果如下:
UTS
:每個容器能看到自己的 hostname,擁有獨立的主機名和域名。IPC
:同一個 IPC namespace 的進程之間能互相通訊,不同的IPC namespace 之間不能通信。PID
:每個 PID namespace 中的進程可以有其獨立的 PID,每個容器可以有其 PID 為進程。1 的 rootNetwork
:每個容器用有其獨立的網絡設備,IP地址,IP路由表,/proc/net目錄,端口號。Mount
:每個容器能看到不同的文件系統層次結構。User
:每個 container 可以有不同的 user 和 group id.
想想以下如果我們要隔離兩個進程需要怎么辦?
- 首先容器進程與進程之間需要隔離,所以需要 PID 隔離
- 首先 容器 A 進程 不能讀取 容器 B進程 通訊內容需要、隔離信號量等,所以需要 IPC 隔離
- 首先 **容器 A 進程 **不能讀取 容器 B進程 的文件,所以需要 Mount 隔離
- 首先 **容器 A 進程 **不能讀取 容器 B進程 的 socket,所以需要 網絡隔離、主機隔離
- Docker 允許用戶在主機和容器間共享文件夾,同時不需要限制容器的訪問權限這就容易讓容器突破資源限制。需要借助用戶空間來完成用戶之間的隔離。
命令詳解
命令 | 功能 | 用途 |
---|---|---|
dd | 復制文件并轉換數據 | 創建鏡像、測試性能、備份磁盤 |
mkfs | 創建文件系統 | 格式化新分區 |
df | 查看磁盤空間 | 查看掛載點使用情況 |
mount | 掛載文件系統 | 掛載 ISO、U盤、設備等 |
unshare | 創建隔離命名空間 | 容器底層調試、實驗性隔離環境 |
① dd 命令:用于讀取、轉換并輸出數據,可以從標準輸入或文件中讀取數據,根據指定的格式轉換數據,再輸出到文件、設備或標準輸出
語法:dd OPTION
,參數如下:
參數 | 描述 |
---|---|
if=文件名 | 輸入文件名,默認為標準輸入。即指定源文件。 |
of=文件名 | 輸出文件名,默認為標準輸出。即指定目的文件。 |
bs=bytes | 同時設置讀入/輸出的塊大小為 bytes 個字節,如果是ibs 設置單次讀入塊大小, obs 設置單次輸出塊大小, cbs 設置轉換塊大小 |
skip/seek=blocks | 從 輸入/輸出 文件開頭跳過 blocks 個塊后再開始復制 |
count=blocks | 僅拷貝 blocks 個塊,塊大小等于 ibs 指定的字節數。 |
conv=<關鍵字>
,關鍵字有如下類型:
conversion
:用指定的參數轉換文件ascii
:轉換ebcdic
為ascii
ebcdic
:轉換asccii
為ebcdic
ibm
:轉換ascii
為alternate ebcdic
block/unblock
:把每一行轉換為長度為cbs
,不足部分用空格lcase
/ucase
:交換輸入的每對字節noerror
:出錯時不停止notrunc
:不截斷輸出文件sync
:將每個輸入塊填充到 ibs 個字節,不足部分用空(NULL)字符補齊
案例
# 生成鏡像文件
dd if=/dev/zero of=test.txt bs=8k count=1014
# 大寫轉小寫
dd if=in.txt of=out.txt conv=ucase
# 測試磁盤寫入速度
dd if=/dev/zero of=testfile bs=1G count=1 oflag=direct
② mkfs 命令:在設備上創建指定類型的文件系統,即 “格式化”
語法:mkfs [-V] [-t fstype] [fs-options] filesys [blocks]
,參數如下:
參數 | 描述 |
---|---|
-t fstype | 指定要建立何種文件系統;如 ext3,ext4。 |
filesys | 指定要創建的文件系統對應的設備文件名。 |
blocks | 指定文件系統的磁盤塊數。 |
-V | 詳細顯示模式。 |
fs-options | 傳遞給具體文件系統的參數。 |
案例
# 格式化分區為 ext4 文件系統
mkfs -t ext4 test.txtmke2fs 1.46.5 (30-Dec-2021)
Discarding device blocks: done
Creating filesystem with 20480 4k blocks and 20480 inodesAllocating group tables: done
Writing inode tables: done
Creating journal (1024 blocks): done
Writing superblocks and filesystem accounting information: done
③ df 命令:用于顯示目前在 Linux 系統上的文件系統磁盤使用情況統計。
語法:dd OPTION
,參數如下:
參數 | 描述 |
---|---|
-a, --all | 包含所有的具有 0 Blocks 的文件系統。 |
-h, --human-readable | 使用人類可讀的格式。 |
-H, --si | 很像 -h ,但是用 1000 為單位而不是用 1024。 |
-t, --type=TYPE | 限制列出文件系統的類型。 |
-T, --print-type | 顯示文件系統的形式。 |
案例:
lighthouse@VM-8-10-ubuntu:~$ df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 340M 1020K 339M 1% /run
/dev/vda2 69G 22G 45G 33% /
tmpfs 1.7G 24K 1.7G 1% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 340M 4.0K 340M 1% /run/user/1002
tmpfs 340M 4.0K 340M 1% /run/user/1001
④ mount 命令:用于加載文件系統到指定的加載點
語法:
mount [-l]
mount [-t vfstype] [-o options] device dir
常見參數如下:
參數 | 描述 |
---|---|
-l | 顯示已加載的文件系統列表。 |
-t vfstype | 指定加載文件系統類型,支持常見的 ext3, ext4, iso9660, tmpfs, xfs 等。 |
-o options | 主要用來描述設備或檔案的掛接方式。 |
loop | 用來把一個文件當成硬盤分區掛接上系統。 |
ro | 采用只讀方式掛接設備。 |
rw | 采用讀寫方式掛接設備。 |
device | 要掛接(mount)的設備。 |
dir | 掛載點的目錄。 |
案例
lighthouse@VM-8-10-ubuntu:test$ sudo mkdir /mymount
lighthouse@VM-8-10-ubuntu:test$ sudo mount test.txt /mymount
lighthouse@VM-8-10-ubuntu:test$ ll /mymount/
total 24
drwxr-xr-x 3 root root 4096 Jul 9 20:51 ./
drwxr-xr-x 21 root root 4096 Jul 9 20:51 ../
drwx------ 2 root root 16384 Jul 9 20:47 lost+found/
lighthouse@VM-8-10-ubuntu:test$ df -t ext4
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/vda2 72127952 22751700 46319384 33% /
/dev/loop4 72652 24 66896 1% /mymount# 取消掛載
lighthouse@VM-8-10-ubuntu:test$ sudo umount /mymount
lighthouse@VM-8-10-ubuntu:test$ df -t ext4
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/vda2 72127952 22751804 46319280 33% /
⑤ unshare 命令:
語法:dd OPTION
,參數如下:
參數 | 描述 |
---|---|
-i, --ipc | 不共享 IPC 空間。 |
-m, --mount | 不共享 Mount 空間。 |
-n, --net | 不共享 Net 空間。 |
-p, --pid | 不共享 PID 空間。 |
-u, --uts | 不共享 UTS 空間。 |
-U, --user | 不共享用戶。 |
-V, --version | 版本查看。 |
--fork | 執行 unshare 的進程 fork 一個新的子進程,在子進程中執行 unshare 傳入的參數。 |
--mount-proc | 執行子進程前,將 proc 優先掛載過去。 |
案例:
lighthouse@VM-8-10-ubuntu:test$ sudo unshare -u /bin/bash
root@VM-8-10-ubuntu:/home/lighthouse/code/test# hostname test1
root@VM-8-10-ubuntu:/home/lighthouse/code/test# hostname
test1
# 查看進程
root@VM-8-10-ubuntu:/home/lighthouse/code/test# ps -ajx | grep bash
1877172 1877180 1877180 1877180 pts/2 1886346 Ss 1001 0:00 -bash
1877180 1886346 1886346 1877180 pts/2 1886346 S+ 0 0:00 sudo unshare -u /bin/bash
1886346 1886347 1886347 1886347 pts/1 1886575 Ss 0 0:00 sudo unshare -u /bin/bash
1886347 1886348 1886348 1886347 pts/1 1886575 S 0 0:00 /bin/bash
1886348 1886576 1886575 1886347 pts/1 1886575 S+ 0 0:00 grep --color=auto bash# exit 退出進程, 發現之前的沒有持久設置
root@VM-8-10-ubuntu:/home/lighthouse/code/test# exit
exit
lighthouse@VM-8-10-ubuntu:test$ hostname
VM-8-10-ubuntu
?? 注意:unshare
是一個運行時命令,執行后會啟動一個新的 shell 進程,該進程處于獨立的命名空間中,但不會持久化 ,退出即失效。
空間隔離實戰
① 進程隔離:unshare
命令專門提供了一個參數--mount-proc
,在新的namespace
掛載一個獨立的/proc
目錄,方便進行進程的監控。
lighthouse@VM-8-10-ubuntu:test$ sudo unshare --fork --pid --mount-proc /bin/bash
root@VM-8-10-ubuntu:/home/lighthouse/code/test# ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 7636 4272 pts/1 S 21:00 0:00 /bin/bash
root 7 0.0 0.1 10336 3752 pts/1 R+ 21:00 0:00 ps -aux
- 可以看到,創建了新的
namespace
后,ps -aux
只能查到兩個進程,一個是bash
,一個是grep
。這就將namespace
內部的進程與宿主機的進程隔離開了。
② 文件隔離
2.2 CGroup
- 定義:
cgroups(Control Groups)
是 linux 內核提供的一種機制,這種機制可以根據需求把系列系統任務及其子任務整合(或分隔)到按資源劃分等級的不同組內,從而為系統資源管理提供一個統一的框架。 - 作用:限制CPU、內存、網絡等所使用的物理資源。
- 具體實現:本質上來說,cgroups 是內核附加在程序上的一系列鉤子(hook),通過程序運行時對資源的調度觸發相應的鉤子以達到資源追蹤和限制的目的。
用途如下:
Resource limitation
:限制資源使用,例:內存使用上限/cpu 的使用限制Prioritization
:優先級控制,例:CPU 利用/磁盤 IO 吞吐Accounting
:一些審計或一些統計Control
:掛起進程/恢復執行進程
相關命令
① pidstat:用于檢測一個進程的 CPU、內存、IO、線程等資源的占用情況。
sudo apt install sysstat # 安裝
pidstat [option] [時間間隔] [次數] # 語法
選項:
-u
:檢測 CPU 使用情況(默認)-r
:檢測內存使用情況-d
:檢測 IO 使用情況-p
:指定進程 PID,如果指定ALL
則監視所有進程-C
:檢測通過指定命令啟動的進程
示例:
- 默認輸出:
pidstat
會輸出所有進程的 CPU 占用情況。 - 指定進程:
pidstat -p 1234
檢測 PID 為 1234 的進程。 - 指定命令:
pidstat -C bash
檢測通過bash
命令啟動的進程。 - 檢測內存:
pidstat -r
檢測內存占用情況。 - 指定檢測次數與頻率:
pidstat 1 3
每隔一秒檢測一次,共檢測三次。
簡單測試:
lighthouse@VM-8-10-ubuntu:test$ pidstat -C bash
Linux 5.15.0-126-generic (VM-8-10-ubuntu) 07/09/2025 _x86_64_ (2 CPU)09:19:13 PM UID PID %usr %system %guest %wait %CPU CPU Command
09:19:13 PM 1001 1877180 0.00 0.00 0.00 0.00 0.00 0 bash
lighthouse@VM-8-10-ubuntu:test$ pidstat -r
Linux 5.15.0-126-generic (VM-8-10-ubuntu) 07/09/2025 _x86_64_ (2 CPU)09:19:20 PM UID PID minflt/s majflt/s VSZ RSS %MEM Command
09:19:20 PM 0 1 0.09 0.00 184012 10948 0.31 systemd
09:19:20 PM 0 366 0.00 0.83 179024 81516 2.34 systemd-journal
09:19:20 PM 0 410 0.00 0.00 289312 27096 0.78 multipathd
09:19:20 PM 0 412 0.00 0.00 12120 5012 0.14 systemd-udevd
09:19:20 PM 101 828 0.00 0.00 16128 1608 0.05 systemd-network
09:19:20 PM 0 843 0.00 0.00 2816 1104 0.03 acpid
09:19:20 PM 103 848 0.00 0.00 8876 4008 0.12 dbus-daemon
09:19:20 PM 0 864 0.00 0.00 234504 4472 0.13 polkitd
② stress:一個壓力測試工具,可以對 CPU、內存、IO 等進行壓力測試。
sudo apt install stress # 安裝
stress [option] # 語法
參數:
-c --cpu N
:產生 N 個進程,每個進程都循環調用sqrt
函數產生 CPU 壓力。-m --vm N
:產生 N 個進程,每個進程都循環調用malloc
和free
函數,產生內存壓力。
示例:stress -c 1
創建一個進程進行 CPU 壓力測試。
- 左側使用
stress
創建了一個進程進行CPU壓力輸出 - 右側檢測
stress
產生的壓力 - 結果一個進程占滿了
100%
的CPU資源
資源控制實戰
① 信息查看
-
cgroups 版本查看
lighthouse@VM-8-10-ubuntu:~$ cat /etc/*release* # 查看當前系統版本 DISTRIB_ID=Ubuntu DISTRIB_RELEASE=22.04 DISTRIB_CODENAME=jammy DISTRIB_DESCRIPTION="Ubuntu 22.04 LTS" PRETTY_NAME="Ubuntu 22.04 LTS" NAME="Ubuntu" VERSION_ID="22.04" VERSION="22.04 (Jammy Jellyfish)" VERSION_CODENAME=jammy ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=jammylighthouse@VM-8-10-ubuntu:~$ cat /proc/filesystems |grep cg nodev cgroup nodev cgroup2 # 可以支持兩個版本 cgroup
-
cgroups 子系統查看
lighthouse@VM-8-10-ubuntu:test$ cat /proc/cgroups #subsys_name hierarchy num_cgroups enabled cpuset 0 109 1 cpu 0 109 1 cpuacct 0 109 1 blkio 0 109 1 memory 0 109 1 devices 0 109 1 freezer 0 109 1 net_cls 0 109 1 perf_event 0 109 1 net_prio 0 109 1 hugetlb 0 109 1 pids 0 109 1 rdma 0 109 1 misc 0 109 1
-
查看掛載信息:這里是每一個資源的控制目錄,比如在
/sys/fs/cgroup/cpu
目錄下,就是控制CPU資源的配置文件lighthouse@VM-8-10-ubuntu:~$ mount | grep cgroup cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
-
查看一個進程上的 cgroup 限制
lighthouse@VM-8-10-ubuntu:~$ cat /proc/$$/cgroup 0::/user.slice/user-1001.slice/session-83590.scope # 層級編號:掛載的子系統:路徑
② 內存控制
-
創建內存控制組
cd /sys/fs/cgroup/memory mkdir test_memory
-
設置最大內存
echo "20971520" > test_memory/memory.limit_in_bytes
-
將進程加入控制組
stress -m 1 --vm-bytes 50m # 每個進程占用 50M pidstat -r echo "15070" > test_memory/tasks
③ CPU 控制
-
創建 CPU 控制組
cd /sys/fs/cgroup/cpu mkdir test_cpu
-
設置 CPU 占用率
echo "5000" > test_cpu/cpu.cfs_period_us echo "2000" > test_cpu/cpu.cfs_quota_us
-
將進程加入控制組
stress -c 1 pidstat -u echo "60769" > test_cpu/tasks