资源隔离之 Linux namespace
生活随笔
收集整理的這篇文章主要介紹了
资源隔离之 Linux namespace
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
Linux namespace 簡稱 ns,在 2002 年 2.4.19 內(nèi)核中被引入,發(fā)展到今天已經(jīng)有 15 個年頭了。
2010 年后國內(nèi)云計算爆發(fā),緊接著 2013 年 Docker 崛起,ns 才作為不可或缺的一部分被重視起來。
ns 本身其實比較簡單,它是 Linux 內(nèi)核的一種機制,給進程隔離和虛擬化內(nèi)核資源用的。
不同的進程是共享內(nèi)核資源的。好比說大家住在同一個小區(qū),雖然到家后關起門來誰都不影響誰。但公共場所就沒辦法了,如果有人破壞環(huán)境,那么勢必會影響到其他人。
內(nèi)核資源在這里就像是公共場所。ns 就是把公共場所隔離開來,你扔煙頭到地上只影響你自己,其他人都看不到。這里的隔離不是說把公共場所分成幾塊兒,每個人分一小塊兒,而是每個人都有一個和原來一樣大的公共場所,就像是每個人都有一個四維空間一樣,是不是有點玄乎啊。不要緊,下面會結合幾個小例子來說明一下 :D。
目前有七種 ns 類型:
Linux 初始化的時候為 init 進程(進程號為1)為每個 ns 類型創(chuàng)建一個實例。后面其他所有進程都可以創(chuàng)建新的 ns 或者加入已有的 ns。
這些 ns 實例在 /proc/[pid]/ns 下面,比如說 1 號進程的 ns:
Mount ns 隔離的是掛載點掛載的是文件系統(tǒng)。子進程創(chuàng)建時(clone 時使用 CLONE_NEWNS),父進程 ns 下的所有掛載點都拷貝到子進程中,Mount ns 隔離之后,Mount Point 的創(chuàng)建或刪除都不會在 ns 之間傳播(除非 mount 時使用了shared subtree) ,媽媽在也不用擔心我的掛載了呢。
使用 Docker 啟動一個 Container,可以查看它的掛載,有很多和 Container 所在的 Host 的不一樣,因為它內(nèi)部做了新的掛載,比如說 aufs 掛載到了根目錄 / 下面:
還有 PID ns,分屬不同 ns 的進程下可以有相同的 PID,比如說 Host 中 PID 為 1 的進程是 init,而 Container 內(nèi) PID 為 1 的進程是 bash(Docker 啟動指定的命令)。
其他幾個 ns 類型,可以參考 namespaces(7) - Linux manual page 。
我之前很好奇怎么查看在 Linux 一共有多少 ns 實例,所以就寫了個簡單 Python 腳本 https://gist.github.com/wanzixyz/53333b15d9290dd971527d61c4ee9f0c :
#!/usr/bin/env python #coding=utf-8import os import re#format: pid, [namespaces], cmdline def _get_namespace(pid):path = '/proc/{0}/ns/'.format(pid)namespaces = []for ns in os.listdir(path):namespaces.append(os.readlink(path + ns))cmdline = open('/proc/{0}/cmdline'.format(pid)).read()if not cmdline:cmdline = open('/proc/{0}/comm'.format(pid)).read()return (pid, namespaces, cmdline)SBIN_INIT = _get_namespace(1) OUTPUT = [SBIN_INIT]for pid in [elt for elt in os.listdir('/proc/') if re.match('\d+', elt)]:output = _get_namespace(pid)if output[1] != SBIN_INIT[1]:OUTPUT.append(output)for val in OUTPUT:print '{0:>10} {1} {2}'.format(val[0],' '.join(val[1]),' '.join(val[2].split('\x00'))[:-1])
運行后結果如下:
1 是 init 進程,21 是 kdevtmosfs,15320 和 29739 都是 Docker 啟動的 Container。
之后我又很好奇,如何才能進入到 Container(其實不算是進入,只是加入 Container 的 ns,看到和 Container 一樣的視圖),于是就又寫了一個腳本 https://gist.github.com/wanzixyz/8dd24aa8882bb983873274a221934cbc :
#!/usr/bin/env python #coding=utf-8import argparse import ctypes import osCLONE_NEWNS = 0x00020000 # /* New mount namespace group */ CLONE_NEWCGROUP = 0x02000000 # /* New cgroup namespace */ CLONE_NEWUTS = 0x04000000 # /* New utsname namespace */ CLONE_NEWIPC = 0x08000000 # /* New ipc namespace */ CLONE_NEWUSER = 0x10000000 # /* New user namespace */ CLONE_NEWPID = 0x20000000 # /* New pid namespace */ CLONE_NEWNET = 0x40000000 # /* New network namespace */parser = argparse.ArgumentParser() parser.add_argument('--pid', type = str, help = 'process id') args = parser.parse_args() if not args.pid:print 'plz input pid..'exit(1)#setns libc = ctypes.CDLL('libc.so.6') namespace = [('ipc', CLONE_NEWIPC),('uts', CLONE_NEWUTS),('net', CLONE_NEWNET),('pid', CLONE_NEWPID),('mnt', CLONE_NEWNS), ] for ns_type, ns_flag in namespace:fd = os.open('/proc/{0}/ns/{1}'.format(args.pid, ns_type), os.O_RDONLY)ret = libc.setns(fd, ns_flag)os.close(fd)if ret == -1:print 'libc.setns failed'exit(1)#child exec shell pid = os.fork() if pid != 0: #fatheros.waitpid(pid, 0) else: #childshell = os.getenv('SHELL')os.execl(shell, os.path.basename(shell))
2010 年后國內(nèi)云計算爆發(fā),緊接著 2013 年 Docker 崛起,ns 才作為不可或缺的一部分被重視起來。
ns 本身其實比較簡單,它是 Linux 內(nèi)核的一種機制,給進程隔離和虛擬化內(nèi)核資源用的。
不同的進程是共享內(nèi)核資源的。好比說大家住在同一個小區(qū),雖然到家后關起門來誰都不影響誰。但公共場所就沒辦法了,如果有人破壞環(huán)境,那么勢必會影響到其他人。
內(nèi)核資源在這里就像是公共場所。ns 就是把公共場所隔離開來,你扔煙頭到地上只影響你自己,其他人都看不到。這里的隔離不是說把公共場所分成幾塊兒,每個人分一小塊兒,而是每個人都有一個和原來一樣大的公共場所,就像是每個人都有一個四維空間一樣,是不是有點玄乎啊。不要緊,下面會結合幾個小例子來說明一下 :D。
目前有七種 ns 類型:
Linux 初始化的時候為 init 進程(進程號為1)為每個 ns 類型創(chuàng)建一個實例。后面其他所有進程都可以創(chuàng)建新的 ns 或者加入已有的 ns。
這些 ns 實例在 /proc/[pid]/ns 下面,比如說 1 號進程的 ns:
Mount ns 隔離的是掛載點掛載的是文件系統(tǒng)。子進程創(chuàng)建時(clone 時使用 CLONE_NEWNS),父進程 ns 下的所有掛載點都拷貝到子進程中,Mount ns 隔離之后,Mount Point 的創(chuàng)建或刪除都不會在 ns 之間傳播(除非 mount 時使用了shared subtree) ,媽媽在也不用擔心我的掛載了呢。
使用 Docker 啟動一個 Container,可以查看它的掛載,有很多和 Container 所在的 Host 的不一樣,因為它內(nèi)部做了新的掛載,比如說 aufs 掛載到了根目錄 / 下面:
還有 PID ns,分屬不同 ns 的進程下可以有相同的 PID,比如說 Host 中 PID 為 1 的進程是 init,而 Container 內(nèi) PID 為 1 的進程是 bash(Docker 啟動指定的命令)。
其他幾個 ns 類型,可以參考 namespaces(7) - Linux manual page 。
我之前很好奇怎么查看在 Linux 一共有多少 ns 實例,所以就寫了個簡單 Python 腳本 https://gist.github.com/wanzixyz/53333b15d9290dd971527d61c4ee9f0c :
#!/usr/bin/env python #coding=utf-8import os import re#format: pid, [namespaces], cmdline def _get_namespace(pid):path = '/proc/{0}/ns/'.format(pid)namespaces = []for ns in os.listdir(path):namespaces.append(os.readlink(path + ns))cmdline = open('/proc/{0}/cmdline'.format(pid)).read()if not cmdline:cmdline = open('/proc/{0}/comm'.format(pid)).read()return (pid, namespaces, cmdline)SBIN_INIT = _get_namespace(1) OUTPUT = [SBIN_INIT]for pid in [elt for elt in os.listdir('/proc/') if re.match('\d+', elt)]:output = _get_namespace(pid)if output[1] != SBIN_INIT[1]:OUTPUT.append(output)for val in OUTPUT:print '{0:>10} {1} {2}'.format(val[0],' '.join(val[1]),' '.join(val[2].split('\x00'))[:-1])
運行后結果如下:
1 是 init 進程,21 是 kdevtmosfs,15320 和 29739 都是 Docker 啟動的 Container。
之后我又很好奇,如何才能進入到 Container(其實不算是進入,只是加入 Container 的 ns,看到和 Container 一樣的視圖),于是就又寫了一個腳本 https://gist.github.com/wanzixyz/8dd24aa8882bb983873274a221934cbc :
#!/usr/bin/env python #coding=utf-8import argparse import ctypes import osCLONE_NEWNS = 0x00020000 # /* New mount namespace group */ CLONE_NEWCGROUP = 0x02000000 # /* New cgroup namespace */ CLONE_NEWUTS = 0x04000000 # /* New utsname namespace */ CLONE_NEWIPC = 0x08000000 # /* New ipc namespace */ CLONE_NEWUSER = 0x10000000 # /* New user namespace */ CLONE_NEWPID = 0x20000000 # /* New pid namespace */ CLONE_NEWNET = 0x40000000 # /* New network namespace */parser = argparse.ArgumentParser() parser.add_argument('--pid', type = str, help = 'process id') args = parser.parse_args() if not args.pid:print 'plz input pid..'exit(1)#setns libc = ctypes.CDLL('libc.so.6') namespace = [('ipc', CLONE_NEWIPC),('uts', CLONE_NEWUTS),('net', CLONE_NEWNET),('pid', CLONE_NEWPID),('mnt', CLONE_NEWNS), ] for ns_type, ns_flag in namespace:fd = os.open('/proc/{0}/ns/{1}'.format(args.pid, ns_type), os.O_RDONLY)ret = libc.setns(fd, ns_flag)os.close(fd)if ret == -1:print 'libc.setns failed'exit(1)#child exec shell pid = os.fork() if pid != 0: #fatheros.waitpid(pid, 0) else: #childshell = os.getenv('SHELL')os.execl(shell, os.path.basename(shell))
這里進入進程 15320 看一下:
腫么樣親,是不是很簡單了呢? :D
https://zhuanlan.zhihu.com/p/25576438
總結
以上是生活随笔為你收集整理的资源隔离之 Linux namespace的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一篇不一样的docker原理解析 提高篇
- 下一篇: LBE平行空间的技术实现