在linux環(huán)境下常規(guī)頁面大小是4K芬沉,常規(guī)巨頁大小有兩種一種是2MB望蜡,一種是1GB棍厂。巨頁的好處是:減少硬件tlb miss末誓,如此在連續(xù)內(nèi)存訪問場景下可以得到較大的性能提升扯俱。一般在數(shù)據(jù)庫如:postgreSQL,mySql等數(shù)據(jù)庫都有使用巨頁時的優(yōu)化措施喇澡。本文將詳細(xì)介紹如何在容器環(huán)境下使用巨頁蘸吓,以及如何對容器使用的巨頁進行限制。
當(dāng)前centos 7.5為止撩幽,透明巨頁不支持1GB库继,只支持4k—>2MB,4MB
第一章 巨頁在linux上的配置
在內(nèi)核中配置CONFIG_HUGETLB_PAGE和CONFIG_HUGETLBFS可以啟動巨頁窜醉。內(nèi)核啟動后通過如下命令可以掛在hugepagefs:
mount -t hugetlbfs nodev /dev/hugepages
我們在centos7.4上宪萄,內(nèi)核配置和上述mount hugetlbfs的過程在系統(tǒng)已經(jīng)默認(rèn)啟動了。同時系統(tǒng)還啟動了透明巨頁thp榨惰,它簡化了我們使用巨頁的過程拜英。同時還在系統(tǒng)中配置了掃描和整理巨頁的內(nèi)核進程khugepaged,此進程周期性的將頁面進行掃描和整理琅催。
cat /sys/kernel/mm/transparent_hugepage/enabled
如果顯示時always或madvise就表明透明巨頁啟動了居凶。
其他透明巨頁的配置請參考網(wǎng)絡(luò)https://blog.csdn.net/wodatoucai/article/details/78493202
設(shè)置主機巨頁
通過如下命令可以看到主機巨頁數(shù)
cat /proc/meminfo |grep -i huge
AnonHugePages: 421888 KB
Hugepages_Total: 0
Hugepages_Free: 0
Hugepages_Rsvd:0
HugePagesize: 2048KB
先喂系統(tǒng)中開16個2MB巨頁,可以通過如下方法
方法1:sysctl
sysctl vm.nr_hugepages=16
方法2:直接修改/sys/目錄
echo 16 >/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
或
echo 16 >/sys/device/system/node/$node/hugepages/hugepages-2048kb/nr_hugepages
上述方法喂系統(tǒng)巨頁以后藤抡,通過cat /proc/meminfo |grep -i huge 就可以看到系統(tǒng)中空閑巨頁侠碧、巨頁總數(shù)為16.
第二章 在docker容器里使用巨頁
oci runc里對巨頁的處理
在oci標(biāo)準(zhǔn)里定義了Spec.Linux.Resource.HugepageLimites,其格式如下:
type HugepageLimit struct{
Pagesize string
Limit uint64
}
而且runc確實也能通過傳遞過來的config.json文件解析出來的spec設(shè)置容器hugetlb.$(hugetlb.Pagesize).limit_in_bytes
$(hugetlb.Pagesize) 為runc中的變量這里用shell方式寫出來缠黍。
Docker-engine(Moby)對巨頁的處理
但是在Docker(moby)中弄兜,并沒有命令參數(shù)設(shè)置容器的hugepage,在docker-engine里也沒有相應(yīng)的流程處理hugepage。Docker-engine在容器start時的*Daemon containerStart()中首先通過Daemon createSpec()創(chuàng)建的符合Oci標(biāo)準(zhǔn)定義的Spec替饿,然后調(diào)用*client create()創(chuàng)建/var/run/docker/libcontainerd/$containerid/config.json语泽。就是在Daemon createSpec()里調(diào)用docker-engine\daemon\oci_linux.go:setResouces()填充Spec.Linux.Resources時刻沒有對hugetlblimit成員進行任何處理。
在Moby社區(qū)里视卢,截止2018年9月27日已經(jīng)有人提交了hugetlbLimit的pr踱卵,但是社區(qū)尚未merge過:
https://github.com/moby/moby/pull/29911
操作步驟
既然Docker尚未真正實現(xiàn)巨頁,我們也可以手動通過下面的步驟實現(xiàn)對docker容器里限制巨頁的使用据过。
限制所有Docker容器的巨頁使用
我們在第一章中介紹了如何向主機系統(tǒng)喂巨頁的操作方法惋砂。容器作為主機上的進程組,如果不做任何多余配置蝶俱,那么所有的容器都可以受到第一章中配置的巨頁的約束班利。也就是說假使我在主機上配置16個巨頁饥漫,我有兩個容器那么榨呆,這兩個容器和主機上的其他進程一共可以16個巨頁。
還有一種方式可以將設(shè)置所有的Docker容器使用的總巨頁數(shù)
在docker.service啟動以后通過修改:
echo 4194304>/sys/fs/cgroup/docker/hugetlb.2MB.limit_in_bytes
如此修改后庸队,所有的docker容器一共只能用2個2MB的巨頁积蜻。當(dāng)然這里配置巨頁數(shù)要小于或等于第一章中配置的巨頁內(nèi)存大小
限制某個容器的巨頁使用
前面的方法可以對所有docker使用的巨頁進行限制,這里我們再介紹一種對某個容器使用的巨頁進行限制彻消。注意:此方法只能在容器業(yè)已啟動后進行限制
echo 4194304>/sys/fs/cgroup/hugetlb/docker/$dockerid/hugetlb.2MB.limit_in_bytes
當(dāng)容器里遭遇到cgroup設(shè)置的hugetlblimit導(dǎo)致的巨頁分配失敗時候竿拆,應(yīng)用會受到SigBus 信號
驗證
這里使用如下程序hugetlb.c驗證巨頁:
#include <sys/mman.h>
#include <stdio.h>
#include <memory.h>
int main(int argc, char *argv[]) {
char *m;
size_t s = (2UL * 1024 * 1024);
m = mmap(NULL, s, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | 0x40000 /*MAP_HUGETLB*/, -1, 0);
if (m == MAP_FAILED) {
perror("map mem");
m = NULL;
return 1;
}
memset(m, 0, s);
printf("map_hugetlb ok, press ENTER to quit!\n");
getchar();//等待
munmap(m, s);
return 0;
}
在主機上完成編譯。
gcc -c hugetlb -o hugetlb.o
gcc hugetlb.o -static -o hugetlb
創(chuàng)建一個目錄將測試程序拷貝進去宾尚,并通過容器-v參數(shù)講此目錄bind mount到容器里首先你需要一個ubuntu鏡像
mkdir -p /home/zxy/work
mv hugetlb /home/zxy/work
docker run -it -v /home/zxy/work:/home/work ubuntu:latest bash
進入容器后丙笋,運行hugetlb。同時在另外一個主機控制臺上煌贴,通過docker exec命令再在剛創(chuàng)建的容器上御板,再創(chuàng)一個bash
docker exec -it dockerid bash
此處運行cat /proc/meminfo|grep -i huge可以看到有一個巨頁被使用了。
在hugetlb程序運行完成后牛郑,如果按照上一小節(jié)的方法將測試容器的/sys/fs/cgroup/hugetlb/docker/$dockerid/hugetlb.2MB.limit_in_bytes修改為0怠肋,然后再在上述容器里運行hugetlb程序會提示收到sigbus信號中止了。
如何知道程序使用了巨頁
通過如下命令可以看到程序是否使用巨頁
echo /proc/$pidof programfilename/maps|grep -i hugepage
Xxxxxxx---xxxxxxx xxxxx. /anon_hugepage(deleted)
上述maps中anon_hugepage里的delete表明這個頁面被hugetlbfs使用了淹朋,并不是表明刪除了笙各。
cgroup實現(xiàn)的hugetlb 控制
cgoup中hugetlb控制hugepage的使用是從內(nèi)核3.10才開始支持的。有如下控制項:
hugetlb.<hugepagesize>.limit_in_bytes //可讀寫础芍,控制hugepage的使用量
Hugetlb.<hugepagesize>.max_usage_in_bytes//只讀杈抢,顯示歷史上最大hugepage使用量
Hugetlb.<hugepagesize>.usage_in_bytes//只讀,顯示當(dāng)前的hugepage使用量
Hugetlb.<hugepagesize>.failcnt//只讀仑性,顯示因為當(dāng)前的cgroup hugetlb限制而導(dǎo)致的hugepage分配失敗次數(shù)春感。*
總結(jié)
在docker上要使用hugepage,需要首先在主機上喂hugepage。如果需要對容器使用的hugepage進行控制鲫懒,那么需要手動在主機上設(shè)置cgroup hugetlb limit嫩实。