国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

docker系列--namespace解讀

cikenerd / 1986人閱讀

摘要:目前內(nèi)核總共實(shí)現(xiàn)了種隔離和消息隊(duì)列。參數(shù)表示我們要加入的的文件描述符。提供了很多種進(jìn)程間通信的機(jī)制,針對(duì)的是和消息隊(duì)列。所謂傳播事件,是指由一個(gè)掛載對(duì)象的狀態(tài)變化導(dǎo)致的其它掛載對(duì)象的掛載與解除掛載動(dòng)作的事件。

前言

理解docker,主要從namesapce,cgroups,聯(lián)合文件,運(yùn)行時(shí)(runC),網(wǎng)絡(luò)幾個(gè)方面。接下來我們會(huì)花一些時(shí)間,分別介紹。

docker系列--namespace解讀

docker系列--cgroups解讀

docker系列--unionfs解讀

docker系列--runC解讀

docker系列--網(wǎng)絡(luò)模式解讀

namesapce主要是隔離作用,cgroups主要是資源限制,聯(lián)合文件主要用于鏡像分層存儲(chǔ)和管理,runC是運(yùn)行時(shí),遵循了oci接口,一般來說基于libcontainer。網(wǎng)絡(luò)主要是docker單機(jī)網(wǎng)絡(luò)和多主機(jī)通信模式。

namespace簡介 什么是namespace

Namespace是將內(nèi)核的全局資源做封裝,使得每個(gè)Namespace都有一份獨(dú)立的資源,因此不同的進(jìn)程在各自的Namespace內(nèi)對(duì)同一種資源的使用不會(huì)互相干擾。實(shí)際上,Linux內(nèi)核實(shí)現(xiàn)namespace的主要目的就是為了實(shí)現(xiàn)輕量級(jí)虛擬化(容器)服務(wù)。在同一個(gè)namespace下的進(jìn)程可以感知彼此的變化,而對(duì)外界的進(jìn)程一無所知。這樣就可以讓容器中的進(jìn)程產(chǎn)生錯(cuò)覺,仿佛自己置身于一個(gè)獨(dú)立的系統(tǒng)環(huán)境中,以此達(dá)到獨(dú)立和隔離的目的。

這樣的解釋可能不清楚,舉個(gè)例子,執(zhí)行sethostname這個(gè)系統(tǒng)調(diào)用時(shí),可以改變系統(tǒng)的主機(jī)名,這個(gè)主機(jī)名就是一個(gè)內(nèi)核的全局資源。內(nèi)核通過實(shí)現(xiàn)UTS Namespace,可以將不同的進(jìn)程分隔在不同的UTS Namespace中,在某個(gè)Namespace修改主機(jī)名時(shí),另一個(gè)Namespace的主機(jī)名還是保持不變。

目前Linux內(nèi)核總共實(shí)現(xiàn)了6種Namespace:

IPC:隔離System V IPC和POSIX消息隊(duì)列。

Network:隔離網(wǎng)絡(luò)資源。

Mount:隔離文件系統(tǒng)掛載點(diǎn)。每個(gè)容器能看到不同的文件系統(tǒng)層次結(jié)構(gòu)。

PID:隔離進(jìn)程ID。

UTS:隔離主機(jī)名和域名。

User:隔離用戶ID和組ID。

namespae接口的使用

namespace的API包括clone()、setns()以及unshare(),還有/proc下的部分文件。為了確定隔離的到底是哪種namespace,在使用這些API時(shí),通常需要指定以下六個(gè)常數(shù)的一個(gè)或多個(gè),通過|(位或)操作來實(shí)現(xiàn)。你可能已經(jīng)在上面的表格中注意到,這六個(gè)參數(shù)分別是CLONE_NEWIPC、CLONE_NEWNS、CLONE_NEWNET、CLONE_NEWPID、CLONE_NEWUSER和CLONE_NEWUTS。

1: 通過clone()創(chuàng)建新進(jìn)程的同時(shí)創(chuàng)建namespace
使用clone()來創(chuàng)建一個(gè)獨(dú)立namespace的進(jìn)程是最常見做法,它的調(diào)用方式如下。

int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);

clone()實(shí)際上是傳統(tǒng)UNIX系統(tǒng)調(diào)用fork()的一種更通用的實(shí)現(xiàn)方式,它可以通過flags來控制使用多少功能。一共有二十多種CLONE_*的flag(標(biāo)志位)參數(shù)用來控制clone進(jìn)程的方方面面(如是否與父進(jìn)程共享虛擬內(nèi)存等等),下面外面逐一講解clone函數(shù)傳入的參數(shù)。

參數(shù)child_func傳入子進(jìn)程運(yùn)行的程序主函數(shù)。

參數(shù)child_stack傳入子進(jìn)程使用的棧空間

參數(shù)flags表示使用哪些CLONE_*標(biāo)志位

參數(shù)args則可用于傳入用戶參數(shù)

2: 通過setns()加入一個(gè)已經(jīng)存在的namespace
在進(jìn)程都結(jié)束的情況下,也可以通過掛載的形式把namespace保留下來,保留namespace的目的自然是為以后有進(jìn)程加入做準(zhǔn)備。通過setns()系統(tǒng)調(diào)用,你的進(jìn)程從原先的namespace加入我們準(zhǔn)備好的新namespace,使用方法如下。

int setns(int fd, int nstype);

參數(shù)fd表示我們要加入的namespace的文件描述符。上文已經(jīng)提到,它是一個(gè)指向/proc/[pid]/ns目錄的文件描述符,可以通過直接打開該目錄下的鏈接或者打開一個(gè)掛載了該目錄下鏈接的文件得到。

參數(shù)nstype讓調(diào)用者可以去檢查fd指向的namespace類型是否符合我們實(shí)際的要求。如果填0表示不檢查。

3: 通過unshare()在原先進(jìn)程上進(jìn)行namespace隔離
后要提的系統(tǒng)調(diào)用是unshare(),它跟clone()很像,不同的是,unshare()運(yùn)行在原先的進(jìn)程上,不需要啟動(dòng)一個(gè)新進(jìn)程,使用方法如下。

int unshare(int flags);

調(diào)用unshare()的主要作用就是不啟動(dòng)一個(gè)新進(jìn)程就可以起到隔離的效果,相當(dāng)于跳出原先的namespace進(jìn)行操作。這樣,你就可以在原進(jìn)程進(jìn)行一些需要隔離的操作。Linux中自帶的unshare命令,就是通過unshare()系統(tǒng)調(diào)用實(shí)現(xiàn)的。

各個(gè)namespace介紹

UTS Namespace

UTS Namespace用于對(duì)主機(jī)名和域名進(jìn)行隔離,也就是uname系統(tǒng)調(diào)用使用的結(jié)構(gòu)體struct utsname里的nodename和domainname這兩個(gè)字段,UTS這個(gè)名字也是由此而來的。
那么,為什么要使用UTS Namespace做隔離?這是因?yàn)橹鳈C(jī)名可以用來代替IP地址,因此,也就可以使用主機(jī)名在網(wǎng)絡(luò)上訪問某臺(tái)機(jī)器了,如果不做隔離,這個(gè)機(jī)制在容器里就會(huì)出問題。

IPC Namespace

IPC是Inter-Process Communication的簡寫,也就是進(jìn)程間通信。Linux提供了很多種進(jìn)程間通信的機(jī)制,IPC Namespace針對(duì)的是SystemV IPC和Posix消息隊(duì)列。這些IPC機(jī)制都會(huì)用到標(biāo)識(shí)符,例如用標(biāo)識(shí)符來區(qū)別不同的消息隊(duì)列,然后兩個(gè)進(jìn)程通過標(biāo)識(shí)符找到對(duì)應(yīng)的消息隊(duì)列進(jìn)行通信等。
IPC Namespace能做到的事情是,使相同的標(biāo)識(shí)符在兩個(gè)Namespace中代表不同的消息隊(duì)列,這樣也就使得兩個(gè)Namespace中的進(jìn)程不能通過IPC進(jìn)程通信了。

PID Namespace

PID Namespace用于隔離進(jìn)程PID號(hào),這樣一來,不同的Namespace里的進(jìn)程PID號(hào)就可以是一樣的了。

Network Namespace

這個(gè)Namespace會(huì)對(duì)網(wǎng)絡(luò)相關(guān)的系統(tǒng)資源進(jìn)行隔離,每個(gè)Network Namespace都有自己的網(wǎng)絡(luò)設(shè)備、IP地址、路由表、/proc/net目錄、端口號(hào)等。網(wǎng)絡(luò)隔離的必要性是很明顯的,舉一個(gè)例子,在沒有隔離的情況下,如果兩個(gè)不同的容器都想運(yùn)行同一個(gè)Web應(yīng)用,而這個(gè)應(yīng)用又需要使用80端口,那就會(huì)有沖突了。

Mount namespace

Mount namespace通過隔離文件系統(tǒng)掛載點(diǎn)對(duì)隔離文件系統(tǒng)提供支持,它是歷史上第一個(gè)Linux namespace,所以它的標(biāo)識(shí)位比較特殊,就是CLONE_NEWNS。隔離后,不同mount namespace中的文件結(jié)構(gòu)發(fā)生變化也互不影響。你可以通過/proc/[pid]/mounts查看到所有掛載在當(dāng)前namespace中的文件系統(tǒng),還可以通過/proc/[pid]/mountstats看到mount namespace中文件設(shè)備的統(tǒng)計(jì)信息,包括掛載文件的名字、文件系統(tǒng)類型、掛載位置等等。

進(jìn)程在創(chuàng)建mount namespace時(shí),會(huì)把當(dāng)前的文件結(jié)構(gòu)復(fù)制給新的namespace。新namespace中的所有mount操作都只影響自身的文件系統(tǒng),而對(duì)外界不會(huì)產(chǎn)生任何影響。這樣做非常嚴(yán)格地實(shí)現(xiàn)了隔離,但是某些情況可能并不適用。比如父節(jié)點(diǎn)namespace中的進(jìn)程掛載了一張CD-ROM,這時(shí)子節(jié)點(diǎn)namespace拷貝的目錄結(jié)構(gòu)就無法自動(dòng)掛載上這張CD-ROM,因?yàn)檫@種操作會(huì)影響到父節(jié)點(diǎn)的文件系統(tǒng)。

ps
在mount這塊,需要特別注意,掛載的傳播性。在實(shí)際應(yīng)用中,很重要。2006 年引入的掛載傳播(mount propagation)解決了這個(gè)問題,掛載傳播定義了掛載對(duì)象(mount object)之間的關(guān)系,系統(tǒng)用這些關(guān)系決定任何掛載對(duì)象中的掛載事件如何傳播到其他掛載對(duì)象。所謂傳播事件,是指由一個(gè)掛載對(duì)象的狀態(tài)變化導(dǎo)致的其它掛載對(duì)象的掛載與解除掛載動(dòng)作的事件。

User Namespace

User Namespace用來隔離用戶和組ID,也就是說一個(gè)進(jìn)程在Namespace里的用戶和組ID與它在host里的ID可以不一樣,這樣說可能讀者還不理解有什么實(shí)際的用處。User Namespace最有用的地方在于,host的普通用戶進(jìn)程在容器里可以是0號(hào)用戶,也就是root用戶。這樣,進(jìn)程在容器內(nèi)可以做各種特權(quán)操作,但是它的特權(quán)被限定在容器內(nèi),離開了這個(gè)容器它就只有普通用戶的權(quán)限了。

代碼解讀

首先runc中有一個(gè)nsenter文件夾,主要是go通過cgo,實(shí)現(xiàn)了nsexec等方法。

在Go運(yùn)行時(shí)啟動(dòng)之前,nsenter包注冊了一個(gè)特殊init構(gòu)造函數(shù)。這讓我們有可能在現(xiàn)有名稱空間“setns”,并避免了Go運(yùn)行時(shí)在多線程場景下可能出現(xiàn)的問題。

具體是在runc的main.go中引入:

package main

import (
    "os"
    "runtime"

    "github.com/opencontainers/runc/libcontainer"
    _ "github.com/opencontainers/runc/libcontainer/nsenter"
    "github.com/urfave/cli"
)

func init() {
    if len(os.Args) > 1 && os.Args[1] == "init" {
        runtime.GOMAXPROCS(1)
        runtime.LockOSThread()
    }
}

var initCommand = cli.Command{
    Name:  "init",
    Usage: `initialize the namespaces and launch the process (do not call it outside of runc)`,
    Action: func(context *cli.Context) error {
        factory, _ := libcontainer.New("")
        if err := factory.StartInitialization(); err != nil {
            // as the error is sent back to the parent there is no need to log
            // or write it to stderr because the parent process will handle this
            os.Exit(1)
        }
        panic("libcontainer: container init failed to exec")
    },
}

下面重點(diǎn)講一下在linux container中namespace的實(shí)現(xiàn)。

runc/libcontainer/configs/config.go中定義了container對(duì)應(yīng)的Namespaces。另外對(duì)于User Namespaces,還定義了UidMappings和GidMappings for user map。

// Config defines configuration options for executing a process inside a contained environment.
type Config struct {
    ...
 
    // Namespaces specifies the container"s namespaces that it should setup when cloning the init process
    // If a namespace is not provided that namespace is shared from the container"s parent process
    Namespaces Namespaces `json:"namespaces"`
 
    // UidMappings is an array of User ID mappings for User Namespaces
    UidMappings []IDMap `json:"uid_mappings"`
 
    // GidMappings is an array of Group ID mappings for User Namespaces
    GidMappings []IDMap `json:"gid_mappings"`
 
    ...
}

而Namespaces定義如下:

package configs

import (
    "fmt"
    "os"
    "sync"
)

const (
    NEWNET  NamespaceType = "NEWNET"
    NEWPID  NamespaceType = "NEWPID"
    NEWNS   NamespaceType = "NEWNS"
    NEWUTS  NamespaceType = "NEWUTS"
    NEWIPC  NamespaceType = "NEWIPC"
    NEWUSER NamespaceType = "NEWUSER"
)

var (
    nsLock              sync.Mutex
    supportedNamespaces = make(map[NamespaceType]bool)
)

// NsName converts the namespace type to its filename
func NsName(ns NamespaceType) string {
    switch ns {
    case NEWNET:
        return "net"
    case NEWNS:
        return "mnt"
    case NEWPID:
        return "pid"
    case NEWIPC:
        return "ipc"
    case NEWUSER:
        return "user"
    case NEWUTS:
        return "uts"
    }
    return ""
}

// IsNamespaceSupported returns whether a namespace is available or
// not
func IsNamespaceSupported(ns NamespaceType) bool {
    nsLock.Lock()
    defer nsLock.Unlock()
    supported, ok := supportedNamespaces[ns]
    if ok {
        return supported
    }
    nsFile := NsName(ns)
    // if the namespace type is unknown, just return false
    if nsFile == "" {
        return false
    }
    _, err := os.Stat(fmt.Sprintf("/proc/self/ns/%s", nsFile))
    // a namespace is supported if it exists and we have permissions to read it
    supported = err == nil
    supportedNamespaces[ns] = supported
    return supported
}

func NamespaceTypes() []NamespaceType {
    return []NamespaceType{
        NEWUSER, // Keep user NS always first, don"t move it.
        NEWIPC,
        NEWUTS,
        NEWNET,
        NEWPID,
        NEWNS,
    }
}

// Namespace defines configuration for each namespace.  It specifies an
// alternate path that is able to be joined via setns.
type Namespace struct {
    Type NamespaceType `json:"type"`
    Path string        `json:"path"`
}

func (n *Namespace) GetPath(pid int) string {
    return fmt.Sprintf("/proc/%d/ns/%s", pid, NsName(n.Type))
}

func (n *Namespaces) Remove(t NamespaceType) bool {
    i := n.index(t)
    if i == -1 {
        return false
    }
    *n = append((*n)[:i], (*n)[i+1:]...)
    return true
}

func (n *Namespaces) Add(t NamespaceType, path string) {
    i := n.index(t)
    if i == -1 {
        *n = append(*n, Namespace{Type: t, Path: path})
        return
    }
    (*n)[i].Path = path
}

func (n *Namespaces) index(t NamespaceType) int {
    for i, ns := range *n {
        if ns.Type == t {
            return i
        }
    }
    return -1
}

func (n *Namespaces) Contains(t NamespaceType) bool {
    return n.index(t) != -1
}

func (n *Namespaces) PathOf(t NamespaceType) string {
    i := n.index(t)
    if i == -1 {
        return ""
    }
    return (*n)[i].Path
}

runC支持的namespce type包括($nsName) "net"、"mnt"、"pid"、"ipc"、"user"、"uts":

const (
       NEWNET  NamespaceType = "NEWNET"
       NEWPID  NamespaceType = "NEWPID"
       NEWNS   NamespaceType = "NEWNS"
       NEWUTS  NamespaceType = "NEWUTS"
       NEWIPC  NamespaceType = "NEWIPC"
       NEWUSER NamespaceType = "NEWUSER"
)

除了驗(yàn)證 Namespce Type是否在以上常量中,還要去驗(yàn)證 /proc/self/ns/$nsName是否存在并且可以read,都通過時(shí),才認(rèn)為該Namespace是在當(dāng)前系統(tǒng)中是被支持的。

// IsNamespaceSupported returns whether a namespace is available or
// not
func IsNamespaceSupported(ns NamespaceType) bool {
       ...
       supported, ok := supportedNamespaces[ns]
       if ok {
              return supported
       }
       ...
       // 除了驗(yàn)證 Namespce Type是都在指定列表中,還要去驗(yàn)證 /proc/self/ns/$nsName是否存在并且可以read
       _, err := os.Stat(fmt.Sprintf("/proc/self/ns/%s", nsFile))
       supported = err == nil
       ...
       return supported
}

在runc/libcontainer/configs/namespaces_syscall.go中,定義了linux clone時(shí)這些namespace對(duì)應(yīng)的clone flags。

var namespaceInfo = map[NamespaceType]int{
       NEWNET:  syscall.CLONE_NEWNET,
       NEWNS:   syscall.CLONE_NEWNS,
       NEWUSER: syscall.CLONE_NEWUSER,
       NEWIPC:  syscall.CLONE_NEWIPC,
       NEWUTS:  syscall.CLONE_NEWUTS,
       NEWPID:  syscall.CLONE_NEWPID,
}
 
// CloneFlags parses the container"s Namespaces options to set the correct
// flags on clone, unshare. This function returns flags only for new namespaces.
func (n *Namespaces) CloneFlags() uintptr {
       var flag int
       for _, v := range *n {
              if v.Path != "" {
                     continue
              }
              flag |= namespaceInfo[v.Type]
       }
       return uintptr(flag)
}

在容器創(chuàng)建初始化的過程中,主要執(zhí)行以下方法:

func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*initProcess, error) {
    cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard))
    nsMaps := make(map[configs.NamespaceType]string)
    for _, ns := range c.config.Namespaces {
        if ns.Path != "" {
            nsMaps[ns.Type] = ns.Path
        }
    }
    _, sharePidns := nsMaps[configs.NEWPID]
    data, err := c.bootstrapData(c.config.Namespaces.CloneFlags(), nsMaps)
    if err != nil {
        return nil, err
    }
    return &initProcess{
        cmd:             cmd,
        childPipe:       childPipe,
        parentPipe:      parentPipe,
        manager:         c.cgroupManager,
        intelRdtManager: c.intelRdtManager,
        config:          c.newInitConfig(p),
        container:       c,
        process:         p,
        bootstrapData:   data,
        sharePidns:      sharePidns,
    }, nil
}

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/27449.html

相關(guān)文章

  • docker系列--namespace解讀

    摘要:目前內(nèi)核總共實(shí)現(xiàn)了種隔離和消息隊(duì)列。參數(shù)表示我們要加入的的文件描述符。提供了很多種進(jìn)程間通信的機(jī)制,針對(duì)的是和消息隊(duì)列。所謂傳播事件,是指由一個(gè)掛載對(duì)象的狀態(tài)變化導(dǎo)致的其它掛載對(duì)象的掛載與解除掛載動(dòng)作的事件。 前言 理解docker,主要從namesapce,cgroups,聯(lián)合文件,運(yùn)行時(shí)(runC),網(wǎng)絡(luò)幾個(gè)方面。接下來我們會(huì)花一些時(shí)間,分別介紹。 docker系列--names...

    wupengyu 評(píng)論0 收藏0
  • docker系列--namespace解讀

    摘要:目前內(nèi)核總共實(shí)現(xiàn)了種隔離和消息隊(duì)列。參數(shù)表示我們要加入的的文件描述符。提供了很多種進(jìn)程間通信的機(jī)制,針對(duì)的是和消息隊(duì)列。所謂傳播事件,是指由一個(gè)掛載對(duì)象的狀態(tài)變化導(dǎo)致的其它掛載對(duì)象的掛載與解除掛載動(dòng)作的事件。 前言 理解docker,主要從namesapce,cgroups,聯(lián)合文件,運(yùn)行時(shí)(runC),網(wǎng)絡(luò)幾個(gè)方面。接下來我們會(huì)花一些時(shí)間,分別介紹。 docker系列--names...

    Acceml 評(píng)論0 收藏0
  • docker系列--網(wǎng)絡(luò)模式解讀

    摘要:網(wǎng)絡(luò)主要是單機(jī)網(wǎng)絡(luò)和多主機(jī)通信模式。下面分別介紹一下的各個(gè)網(wǎng)絡(luò)模式。設(shè)計(jì)的網(wǎng)絡(luò)模型。是以對(duì)定義的元數(shù)據(jù)。用戶可以通過定義這樣的元數(shù)據(jù)來自定義和驅(qū)動(dòng)的行為。 前言 理解docker,主要從namesapce,cgroups,聯(lián)合文件,運(yùn)行時(shí)(runC),網(wǎng)絡(luò)幾個(gè)方面。接下來我們會(huì)花一些時(shí)間,分別介紹。 docker系列--namespace解讀 docker系列--cgroups解讀 ...

    haitiancoder 評(píng)論0 收藏0
  • docker系列--網(wǎng)絡(luò)模式解讀

    摘要:網(wǎng)絡(luò)主要是單機(jī)網(wǎng)絡(luò)和多主機(jī)通信模式。下面分別介紹一下的各個(gè)網(wǎng)絡(luò)模式。設(shè)計(jì)的網(wǎng)絡(luò)模型。是以對(duì)定義的元數(shù)據(jù)。用戶可以通過定義這樣的元數(shù)據(jù)來自定義和驅(qū)動(dòng)的行為。 前言 理解docker,主要從namesapce,cgroups,聯(lián)合文件,運(yùn)行時(shí)(runC),網(wǎng)絡(luò)幾個(gè)方面。接下來我們會(huì)花一些時(shí)間,分別介紹。 docker系列--namespace解讀 docker系列--cgroups解讀 ...

    zollero 評(píng)論0 收藏0
  • docker系列--網(wǎng)絡(luò)模式解讀

    摘要:網(wǎng)絡(luò)主要是單機(jī)網(wǎng)絡(luò)和多主機(jī)通信模式。下面分別介紹一下的各個(gè)網(wǎng)絡(luò)模式。設(shè)計(jì)的網(wǎng)絡(luò)模型。是以對(duì)定義的元數(shù)據(jù)。用戶可以通過定義這樣的元數(shù)據(jù)來自定義和驅(qū)動(dòng)的行為。 前言 理解docker,主要從namesapce,cgroups,聯(lián)合文件,運(yùn)行時(shí)(runC),網(wǎng)絡(luò)幾個(gè)方面。接下來我們會(huì)花一些時(shí)間,分別介紹。 docker系列--namespace解讀 docker系列--cgroups解讀 ...

    xiaotianyi 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<