自己動手寫docker筆記(4)構(gòu)造簡單實(shí)現(xiàn)run命令版本的容器

原書代碼

https://github.com/xianlubird/mydocker.git
#code-3.1

Linux Proc

Linux 下的/proc 文件系統(tǒng)是由內(nèi)核提供,它其實(shí)不是一個真正的文件系統(tǒng)浑槽,只包含了系統(tǒng)運(yùn)行時信息(比如系統(tǒng)內(nèi)存居扒,mount 設(shè)備信息正什,一些硬件配置等等,它只存在于內(nèi)存中裹唆,而不占用外存空間誓斥。它是以文件系統(tǒng)的形式為訪問內(nèi)核數(shù)據(jù)的操作提供接口。
比如說lsmod就和cat /proc/modules是等效的

root@taroballs-PC:~# ls /proc/
1     1284  1524  1802  2103  31    430   514        cpuinfo      modules
10    1294  1525  1815  2144  312   4332  515        crypto       mounts
1005  13    1529  1818  2148  318   435   516        devices      mtrr
1030  1312  153   1826  22    32    436   517        diskstats    net
1031  1320  1530  1832  221   3237  437   518        dma          pagetypeinfo
1032  1346  154   1837  225   3241  438   525        driver       partitions

當(dāng)你去遍歷這個目錄的時候會發(fā)現(xiàn)很多數(shù)字许帐,這些都是為每個進(jìn)程創(chuàng)建的空間劳坑,數(shù)字就是他們的 PID。

重要術(shù)語 相關(guān)說明
/proc/N pid為N的進(jìn)程信息
/proc/N/cmdline 進(jìn)程啟動命令
/proc/N/cwd 鏈接到進(jìn)程當(dāng)前工作目錄
/proc/N/environ 進(jìn)程環(huán)境變量列表
/proc/N/exe 鏈接到進(jìn)程的執(zhí)行命令文件
/proc/N/fd 包含進(jìn)程相關(guān)的所有的文件描述符
/proc/N/maps 與進(jìn)程相關(guān)的內(nèi)存映射信息
/proc/N/mem 指代進(jìn)程持有的內(nèi)存成畦,不可讀
/proc/N/root 鏈接到進(jìn)程的根目錄
/proc/N/stat 進(jìn)程的狀態(tài)
/proc/N/statm 進(jìn)程使用的內(nèi)存的狀態(tài)
/proc/N/status 進(jìn)程狀態(tài)信息泡垃,比stat/statm更具可讀性
/proc/self 鏈接到當(dāng)前正在運(yùn)行的進(jìn)程

實(shí)現(xiàn) run 命令

實(shí)現(xiàn)一個簡單版本的run命令,類似docker run -ti [command]

代碼目錄結(jié)構(gòu)如下:

root@taroballs-PC:~# tree  mydocker/ -L 2
mydocker/
├── container
│   ├── container_process.go
│   └── init.go
├── Godeps
│   ├── Godeps.json
│   └── Readme
├── main_command.go
├── main.go
├── run.go
└── vendor
    ├── github.com
    └── golang.org

首先分析下main.go函數(shù)寫了些什么:

package main

import (
    log "github.com/Sirupsen/logrus"
    "github.com/urfave/cli"http://這個包提供了命令行工具
    "os"
)

const usage = `mydocker is a simple container runtime implementation.
                   The purpose of this project is to learn how docker works and how to write a docker by ourselves
                   Enjoy it, just for fun.`

func main() {
    app := cli.NewApp()
    app.Name = "mydocker"
    app.Usage = usage
    //暫時定義兩個命令init、run
    app.Commands = []cli.Command{
        initCommand,
        runCommand,
    }
    //`app.Before` 內(nèi)初始化了一下`logrus`的日志配置羡鸥。
    app.Before = func(context *cli.Context) error {
        // Log as JSON instead of the default ASCII formatter.
        log.SetFormatter(&log.JSONFormatter{})
        log.SetOutput(os.Stdout)
        return nil
    }
    //運(yùn)行出錯時 記錄日志
    if err := app.Run(os.Args); err != nil {
        log.Fatal(err)
    }
}

分別看下兩條命令的具體定義蔑穴,查看main_command.go文件

runcommand命令實(shí)現(xiàn)

//main_command.go
var runCommand = cli.Command{
    Name:  "run",
    Usage: `Create a container with namespace and cgroups limit
                mydocker run -ti [command]`,
    Flags: []cli.Flag{
        cli.BoolFlag{
            Name:        "ti",
            Usage:       "enable tty",
        },
    },
    //這里是run命令執(zhí)行的真正函數(shù)
    Action: func(context *cli.Context) error {
        if len(context.Args()) < 1 {//判斷是否包含參數(shù)
            return fmt.Errorf("Missing container command")
        }
        cmd := context.Args().Get(0)//獲取參數(shù)
        tty := context.Bool("ti")
        Run(tty, cmd)//調(diào)用Run方法去準(zhǔn)備啟動容器
        return nil
    },
}

先來看看Run函數(shù)做了些什么:

//run函數(shù)在run.go中

func Run(tty bool, command string) {
    parent := container.NewParentProcess(tty, command)
    if err := parent.Start(); err != nil {
        log.Error(err)
    }
    parent.Wait()
    os.Exit(-1)
}

解釋一下:

讓我們看一下NewParentProcess函數(shù)都寫了些什么

//container/container_process.go 
func NewParentProcess(tty bool, command string) *exec.Cmd {
    args := []string{"init", command}
    cmd := exec.Command("/proc/self/exe", args...)
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
        syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
    }
    if tty {
        cmd.Stdin = os.Stdin
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
    }
    return cmd
}

解釋一下:

接著看看返回cmd之后的調(diào)用:


查看InitCommand命令的具體實(shí)現(xiàn)

//main_command.go
//此方法為內(nèi)部操作,禁止外部調(diào)用
var initCommand = cli.Command{
    Name:   "init",
    Usage:  "Init container process run user's process in container. Do not call it outside",
    Action: func(context *cli.Context) error {
        log.Infof("init come on")
        cmd := context.Args().Get(0)//獲取傳遞過來的參數(shù)
        log.Infof("command %s", cmd)//寫入日志
        err := container.RunContainerInitProcess(cmd, nil)//執(zhí)行容器初始化操作
        return err
    },
}

那么這里看看RunContainerInitProcess函數(shù)做了些什么

//container/init.go 
func RunContainerInitProcess(command string, args []string) error {
    logrus.Infof("command %s", command)

    defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
    syscall.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), "")
    argv := []string{command}
    if err := syscall.Exec(command, argv, os.Environ()); err != nil {
        logrus.Errorf(err.Error())
    }
    return nil
}

解釋一下先:

這里的MountFlag的意思如下

  • MS_NOEXEC 在本文件系統(tǒng)中不允許運(yùn)行其他程序
  • MS_NOSUID 在本系統(tǒng)中運(yùn)行程序的時候不允許set-user-ID或者set-group-ID
  • MS_NODEV 這個參數(shù)是自從Linux 2.4以來所有 mount 的系統(tǒng)都會默認(rèn)設(shè)定的參數(shù)

解釋一下

——————

運(yùn)行一下:

#記得先在GOPATH準(zhǔn)備兩個包
git clone https://github.com/Sirupsen/logrus.git
git clone https://github.com/urfave/cli.git
#記得移動到GOPATH跑程序

Result

root@taroballs-PC:/home/taroballs/go/src/github.com/xianlubird/mydocker# ./mydocker run -ti /bin/sh
{"level":"info","msg":"init come on","time":"2018-02-01T00:47:22+08:00"}
{"level":"info","msg":"command /bin/sh","time":"2018-02-01T00:47:22+08:00"}
{"level":"info","msg":"command /bin/sh","time":"2018-02-01T00:47:22+08:00"}
# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 00:47 pts/0    00:00:00 /bin/sh
root         6     1  0 00:47 pts/0    00:00:00 ps -ef
# 

對比一下運(yùn)行docker鏡像容器

root@taroballs-PC:~# docker run -ti ubuntu /bin/sh
# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 16:51 pts/0    00:00:00 /bin/sh
root         5     1  0 16:51 pts/0    00:00:00 ps -ef
# 

是不是相類似呢惧浴?在運(yùn)行個/bin/ls試試看

root@taroballs-PC:/home/taroballs/go/src/github.com/xianlubird/mydocker# ./mydocker run -ti /bin/ls
{"level":"info","msg":"init come on","time":"2018-02-01T00:54:45+08:00"}
{"level":"info","msg":"command /bin/ls","time":"2018-02-01T00:54:45+08:00"}
{"level":"info","msg":"command /bin/ls","time":"2018-02-01T00:54:45+08:00"}
container  main_command.go  mydocker  README.md  vendor
Godeps     main.go      network   run.go
root@taroballs-PC:/home/taroballs/go/src/github.com/xianlubird/mydocker# 
#由于我們沒有`chroot`存和,所以目前我們的系統(tǒng)文件系統(tǒng)是繼承自我們的父進(jìn)程的,這里我們運(yùn)行了一下`ls`命令衷旅,發(fā)現(xiàn)容器啟動起來以后捐腿,打印出來了當(dāng)前目錄的內(nèi)容,然后退出了.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末柿顶,一起剝皮案震驚了整個濱河市茄袖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嘁锯,老刑警劉巖宪祥,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異家乘,居然都是意外死亡蝗羊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門仁锯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耀找,“玉大人,你說我怎么就攤上這事业崖∫懊ⅲ” “怎么了蓄愁?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長狞悲。 經(jīng)常有香客問我涝登,道長,這世上最難降的妖魔是什么效诅? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任胀滚,我火速辦了婚禮,結(jié)果婚禮上乱投,老公的妹妹穿的比我還像新娘咽笼。我一直安慰自己,他們只是感情好戚炫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布剑刑。 她就那樣靜靜地躺著,像睡著了一般双肤。 火紅的嫁衣襯著肌膚如雪施掏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天茅糜,我揣著相機(jī)與錄音七芭,去河邊找鬼。 笑死蔑赘,一個胖子當(dāng)著我的面吹牛狸驳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缩赛,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼耙箍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酥馍?” 一聲冷哼從身側(cè)響起辩昆,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎旨袒,沒想到半個月后汁针,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡峦失,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年扇丛,在試婚紗的時候發(fā)現(xiàn)自己被綠了术吗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尉辑。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖较屿,靈堂內(nèi)的尸體忽然破棺而出隧魄,到底是詐尸還是另有隱情卓练,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布购啄,位于F島的核電站襟企,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏狮含。R本人自食惡果不足惜顽悼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望几迄。 院中可真熱鬧蔚龙,春花似錦、人聲如沸映胁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽艇潭。三九已至双戳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間弛姜,已是汗流浹背脐瑰。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留廷臼,地道東北人蚪黑。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像中剩,于是被迫代替她去往敵國和親忌穿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容