[正在不斷更新中...]
Docker
是一個使用了Linux Namespace
和Cgroups
的虛擬化工具
Linux Namespace
是Kernel的一個功能,可以隔離一系列系統資源(PID
、UID
、Network
),幫助進程隔離出自己的單獨的空間。
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
主要來隔離nodename
和domainname
兩個系統標識。在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 IPC
和POSIX 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 ID
和Group 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