本文講的是Linux命名空間學習教程(一) UTS,【編者的話】Docker核心解決的問題是利用LXC來實作類似VM的功能,進而利用更加節省的硬體資源提供給使用者更多的計算資源。而 LXC所實作的隔離性主要是來自核心的命名空間, 其中pid、net、ipc、mnt、uts 等命名空間将容器的程序、網絡、消息、檔案系統和hostname 隔離開。本文是Linux命名空間系列教程的第一篇,通過一個簡單的例子介紹了Linux容器以及UTS命名空間。DockerOne在撸代碼的基礎上進行了校對和整理。
我在OVH工作的時候,曾經為一款“即将釋出”的産品添加安全機制,而這項工作需要用到Linux的命名空間(Linux namespace)。在學習過程中我發現,Linux的命名空間很強大,但是這方面的文檔卻非常少。
大多數人都應該聽說過LXC——LinuX Containers,它是一個加強版的Chroot。簡單的說,LXC就是将不同的應用隔離開來,這有點類似于chroot,chroot是将應用隔離到一個虛拟的私有root下,而LXC在這之上更進了一步。LXC内部依賴Linux核心的3種隔離機制(isolation infrastructure):
Chroot
Cgroups
Namespaces
我本可以将這個系列命名為《如何建構你自己的LXC》,進而赢得更高的Google排名,但這樣做可能會顯得我太狂妄。事實上,LXC所做的事情遠不止隔離,它還包含模闆管理、當機以及其它更多的功能。這個系列将會為你解開LXC的神秘面紗。
在這個系列中,我們将使用最簡單的C程式啟動/bin/bash ,并逐漸為它加入各種隔離機制。
好了,現在我們開始吧。
有意思的是,Linux的容器工具并沒有提供一個類似黑盒般神秘的容器解決方案,而是提供單獨隔離構件(isolation building block),統稱為Namespaces。每一個新版本都會有新的構件釋出出來。這樣你就可以跟據你的特定應用的情況,選擇所需要的構件。
Linux的3.12核心支援6種Namespace:
UTS: hostname(本文介紹)
IPC: 程序間通信 (之後的文章會講到)
PID: "chroot"程序樹(之後的文章會講到)
NS: 挂載點,首次登陸Linux(之後的文章會講到)
NET: 網絡通路,包括接口(之後的文章會講到)
USER: 将本地的虛拟user-id映射到真實的user-id(之後的文章會講到)
如下是一個程式的完整骨架,用于從子程序啟動/bin/bash程式(為了保持例子簡單,是以去掉了錯誤檢查):
main-0-template.c
define _GNU_SOURCE
include <sys/types.h>
include <sys/wait.h>
include <stdio.h>
include <sched.h>
include <signal.h>
include <unistd.h>
define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];
char* const child_args[] = {
"/bin/bash",
NULL
};
int child_main(void* arg) {
printf(" - World !\n");
execv(child_args[0], child_args);
printf("Ooops\n");
return 1;
}
int main() {
printf(" - Hello ?\n");
int child_pid = clone(child_main, child_stack + STACK_SIZE, SIGCHLD, NULL);
waitpid(child_pid, NULL, 0);
return 0;
請注意,這裡我們使用的是clone而不是fork系統調用。這正是魔法(即将)發生的地方。
jean-tiare@jeantiare-Ubuntu:~/blog$ gcc -Wall main.c -o ns && ./ns
- Hello ?
- World !
jean-tiare@jeantiare-Ubuntu:~/blog$ # inside the container
jean-tiare@jeantiare-Ubuntu:~/blog$ exit
jean-tiare@jeantiare-Ubuntu:~/blog$ # outside the container
OK,帥呆了。不過,假如沒有注釋,你很難注意到我們在子程序的/bin/bash中。事實上,當我在寫這篇文章的時候,好幾次不小心退出父程序的shell。
現在做一些修改,如直接修改主機名(the hostname with 0% env vars tricks),是不是會很牛逼?就單單用Namespace?很簡單,我們隻要:
在clone系統調用中使用CLONE_NEWUTS的标志
在子程序中調用sethostname
main-1-uts.c
// (needs root privileges (or appropriate capabilities))
//[...]
sethostname("In Namespace", 12);
int child_pid = clone(child_main, child_stack+STACK_SIZE,
CLONE_NEWUTS | SIGCHLD, NULL);
運作一下:
jean-tiare@jeantiare-Ubuntu:~/blog$ gcc -Wall main.c -o ns && sudo ./ns
root@In Namespace:~/blog$ # inside the container
root@In Namespace:~/blog$ exit
事情就是這個樣子(至少這篇文章中是這樣)!namespace真是太TM容易上手了:clone,設定特定的CLONE_NEW*标志,設定新的env,搞定!
你希望更加深入?有興趣可以閱讀一下excellent LWN article series on namespaces。
原文連結:Introduction to Linux namespaces – Part 1: UTS(翻譯:孫科 審校:李穎傑)
原文釋出時間為: 2014-12-24
本文作者:孫科
本文來自雲栖社群合作夥伴DockerOne,了解相關資訊可以關注DockerOne。
原文标題:Linux命名空間學習教程(一) UTS