Docker原理筆記

[正在不斷更新中...]
Docker是一個使用了Linux NamespaceCgroups的虛擬化工具
Linux Namespace是Kernel的一個功能,可以隔離一系列系統資源(PIDUIDNetwork),幫助進程隔離出自己的單獨的空間。
Cgroups限制一組進程及將來子進程的資源的大小,保證不會相互爭搶,這些資源包括CPU、內存、存儲、網絡等,並進行監控和統計信息。

Namespace

  • Namespace類型及系統調用參數:

 Namespace類型             系統調用參數
Mount Namespace         CLONE_NEWNS
 UTS Namespace          CLONE_NEWUTS
 IPC Namespace            CLONE_NEWIPC
 PID Namespace            CLONE_NEWPID
Network Namespace      CLONE_NEWNET
 User Namespace          CLONE_NEWUSER

Namespace的API主要有三個系統調用:
clone()創建新進程
unshare()將進程移除某個Namespace
setns()將進程加入到某個Namespace

  • UTS Namespace
    UTS Namespace主要來隔離nodenamedomainname兩個系統標識。在UTS Namespace裏面,每個Namespace有自己的hostname
    Go實現代碼:
package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main(){
    cmd := exec.Command("bash")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags:syscall.CLONE_NEWUTS,
    }
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run();err!=nil{
        log.Fatal(err)
    }
}

exec.Command("bash")用來制定被fork出來的新進程內的初始命令,默認用bash來執行。

驗證UTS隔離:

[jin1ming@ML ~]$ echo $$
5576
[ML myDockerLite]# echo $$
5780
$ readlink /proc/5780/ns/uts 
uts:[4026532587]
$ readlink /proc/5576/ns/uts 
uts:[4026531838]
[ML myDockerLite]# hostname dockelite
[ML myDockerLite]# hostname
dockelite
[jin1ming@ML ~]$ hostname
ML
  • IPC Namespace
    IPC Namespace用來隔離System V IPCPOSIX message queue
    略微修改一下代碼:
package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main(){
    cmd := exec.Command("bash")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags:syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,
    }
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run();err!=nil{
        log.Fatal(err)
    }
}

驗證 IPC 隔離:

[jin1ming@ML ~]$ ipcs

--------- 消息隊列 -----------
鍵        msqid      擁有者  權限     已用字節數 消息      

------------ 共享內存段 --------------
鍵        shmid      擁有者  權限     字節     連接數  狀態      
0x512c001f 131072     jin1ming   600        33024      1                       
0x00000000 1441793    jin1ming   600        16777216   2          目標       
0x00000000 327682     jin1ming   600        524288     2          目標       
0x00000000 1769475    jin1ming   600        22528      2          目標       
0x00000000 1474564    jin1ming   700        16472      2          目標       
0x00000000 1638405    jin1ming   700        130544     2          目標       

--------- 信號量數組 -----------
鍵        semid      擁有者  權限     nsems     
0x512c001e 32768      jin1ming   600        1         
[ML myDockerLite]# ipcs

--------- 消息隊列 -----------
鍵        msqid      擁有者  權限     已用字節數 消息      

------------ 共享內存段 --------------
鍵        shmid      擁有者  權限     字節     連接數  狀態      

--------- 信號量數組 -----------
鍵        semid      擁有者  權限     nsems     
  • PID Namespace
    PID Namespace用來隔離進程ID,同一個進程在不同的PID Namespace有不同的PID
    略微修改代碼:
package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main(){
    cmd := exec.Command("bash")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags:syscall.CLONE_NEWUTS | 
            syscall.CLONE_NEWIPC | 
            syscall.CLONE_NEWPID,
    }
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run();err!=nil{
        log.Fatal(err)
    }
}

驗證PID隔離:

[ML linux_shell]# cat testgo.sh 
#!/bin/bash

echo PID:$$
read input
echo $input

1.未進行隔離時:

[ML linux_shell]# ./testgo.sh 
PID:14141
[jin1ming@ML linux_shell]$ pgrep -f testgo
14141

2.進行隔離時:

[ML linux_shell]# ./testgo.sh 
PID:7
$ pgrep -f testgo
14586
  • Mount Namespace
    Mount Namespace 用來隔離各個進程看到的掛載點視圖。在不同的Namespace中看到的文件系統層次是不一樣的。在Mount Namespace中調用mount()umount()僅僅只會影響到當前Namespace內的文件系統,而對全局的文件系統是沒有影響的。
    略微修改代碼:
package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main(){
    cmd := exec.Command("bash")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags:syscall.CLONE_NEWUTS |
            syscall.CLONE_NEWIPC |
            syscall.CLONE_NEWPID |
            syscall.CLONE_NEWNS,
    }
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run();err!=nil{
        log.Fatal(err)
    }
}

驗證Mount Namespace 隔離:

[ML myDockerLite]# mount -t proc proc /proc
[ML myDockerLite]# ls /proc
1          crypto       ioports        loadavg       schedstat      timer_list
7          devices      irq            locks         scsi           tty
acpi       diskstats    kallsyms       meminfo       self           uptime
asound     dma          kcore          misc          slabinfo       version
buddyinfo  driver       keys           modules       softirqs       vmallocinfo
bus        execdomains  key-users      mounts        stat           vmstat
cgroups    fb           kmsg           mtrr          swaps          zoneinfo
cmdline    filesystems  kpagecgroup    net           sys
config.gz  fs           kpagecount     pagetypeinfo  sysrq-trigger
consoles   interrupts   kpageflags     partitions    sysvipc
cpuinfo    iomem        latency_stats  sched_debug   thread-self
[ML myDockerLite]# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 10:56 pts/2    00:00:00 bash
root         8     1  0 10:58 pts/2    00:00:00 ps -ef
[jin1ming@ML ~]$ ls /proc
1      1176   1382   16675  247   479   613   80    962          kmsg
10     1177   1386   16765  25    48    615   81    963          kpagecgroup
1000   1184   139    16770  2552  484   62    810   9659         kpagecount
10075  1185   1395   16776  26    488   63    815   982          kpageflags
1021   1188   14     1724   269   489   637   8175  987          latency_stats
1027   1189   140    1781   27    49    639   82    989          loadavg
1029   1195   14001  18     275   493   64    824   993          locks
10459  1197   14004  1800   28    496   65    826   994          meminfo
#........此處省略一部分
[jin1ming@ML ~]$ ps -ef
Error, do this: mount -t proc proc /proc
  • User Namespace
    User Namespace 主要是隔離用戶的用戶組ID。一個進程的User IDGroup ID在User Namespace內外可以是不同的。
    對代碼略作修改:
package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main(){
    cmd := exec.Command("bash")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags:syscall.CLONE_NEWUTS |
            syscall.CLONE_NEWIPC |
            syscall.CLONE_NEWPID |
            syscall.CLONE_NEWNS |
            syscall.CLONE_NEWUSER,
    }

    cmd.SysProcAttr.Credential = &syscall.Credential{
        Uid:0,
        Gid:0,
    }
//此處代表root

    cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{ContainerID: 0, HostID: 1001, Size: 1}}
    cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{{ContainerID: 0, HostID: 1001, Size: 1}}
//此處指用戶的真實權限

    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr


    if err := cmd.Run();err!=nil{
        log.Fatal(err)
    }
    os.Exit(-1)
}

驗證User Namespace隔離:

[ML home]# id
uid=0(root) gid=0(root) 組=0(root),65534(nobody)
[ML home]# cd jin1ming/
bash: cd: jin1ming/: 權限不夠

如果具有真正的root權限將可以訪問

  • Network Namespace
    Network Namespace 是用來隔離網絡設備IP地址端口等網絡棧的Namespace。Network Namespace可以讓每個容器擁有自己獨立的網絡設備(虛擬的),而且容器內的應用可以綁定到自己的端口,每個Namespace內的端口都不會衝突。
    略微修改代碼:
package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main(){
    cmd := exec.Command("bash")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags:syscall.CLONE_NEWUTS |
            syscall.CLONE_NEWIPC |
            syscall.CLONE_NEWPID |
            syscall.CLONE_NEWNS |
            syscall.CLONE_NEWUSER |
            syscall.CLONE_NEWNET,
    }

    cmd.SysProcAttr.Credential = &syscall.Credential{
        Uid:0,
        Gid:0,
    }

    cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{ContainerID: 0, HostID: 1001, Size: 1}}
    cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{{ContainerID: 0, HostID: 1001, Size: 1}}

    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr


    if err := cmd.Run();err!=nil{
        log.Fatal(err)
    }
    os.Exit(-1)
}

驗證Network隔離:

[jin1ming@ML ~]$ ifstat 
#kernel
Interface        RX Pkts/Rate    TX Pkts/Rate    RX Data/Rate    TX Data/Rate  
                 RX Errs/Drop    TX Errs/Drop    RX Over/Rate    TX Coll/Rate  
lo                    48 0            48 0          4072 0          4072 0      
                       0 0             0 0             0 0             0 0      
enp1s0                 0 0             0 0             0 0             0 0      
                       0 0             0 0             0 0             0 0      
wlp2s0             11052 0          9055 0        11277K 0         2388K 0      
                       0 0             0 0             0 0             0 0     
[ML myDockerLite]# ifstat 
#kernel
Interface        RX Pkts/Rate    TX Pkts/Rate    RX Data/Rate    TX Data/Rate  
                 RX Errs/Drop    TX Errs/Drop    RX Over/Rate    TX Coll/Rate  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章