• 【协作式原创】自己动手写docker之run代码解析


    预备知识: Linux命令

    预备知识:namespace和cgroup(CentOS7.7)

    一,exec替换进程映像
    函数功能: 用exec函数可以把当前进程替换为一个新进程,且新进程与原进程有相同的PID
    例如:在shell命令行执行ps命令,实际上是shell进程调用fork复制一个新的子进程,在利用exec系统调用将新产生的子进程完全替换成ps进程。

    • MNT namespace的go代码实现
      启动一个bash进程,并且单独给予和父进程不同的mount namespace.
    func main()  {
        cmd := exec.Command("/bin/sh") // 加载可执行文件/bin/sh到内存中并运行。
        cmd.SysProcAttr = &syscall.SysProcAttr{
            Cloneflags: syscall.CLONE_NEWNS,
        }
        cmd.Stdin  = os.Stdin
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        if err := cmd.Run(); err != nil {
            log.Printf("Run error:%v
    ", err)
            log.Fatal(err)
        }
    }
    
    • 对应如下代码
      构造一个命令,用于启动init进程。
    func NewParentProcess(command string, tty bool) *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
    }
    

    urfave cli预备知识

    准备工作

    1. 阿里云抢占式实例:centos7.4
    2. 每次实例释放后都要重新安装go
    wget https://dl.google.com/go/go1.13.4.linux-amd64.tar.gz
    sudo tar -C /usr/local -xf go1.13.4.linux-amd64.tar.gz
    export PATH=$PATH:/usr/local/go/bin
    source ~/.bash_profile
    
    yum -y install nano
    
    yum install git
    git clone https://github.com/yudidi/go-docker.git
    git branch -all
    git checkout remotes/origin/ns
    

    demo

    理解为什么增加init命令和为什么挂载proc

    1_no_proc
    2_add_proc
    3_add_proc_and_not_affect_host

    demo-init源码

    syscall.Exec启动进程和os/exec.Command启动进程的区别

    • Q: run和init到底启动了几个进程
      A: 2个进程,一个运行go-docker run,一个运行go-docker init。

    • 这些进程的ns
      13864和父进程3911,1号进程均一致
      13868和父进程13864不同

    • kill掉13864,不会影响13864的运行(这就是容器内的第一个进程)

    [root@192 go-docker]# kill 13864
    //
    [root@192 go-docker]# ps -ef|grep 138
    root      13868      1  0 10:27 pts/0    00:00:00 /proc/self/exe init /bin/sh
    root      13962   1323  0 10:50 pts/1    00:00:00 grep --color=auto 138
    // 13868进程仍在继续打印时间
    
    • 所以13864进程不表示容器(杀死了他,子进程仍在打印时间,说明这两个进程没有依赖关系),容器本身就不是一个进程,容器只是描述一种边界,而ns就是实现这个边界的方法。
      ns这个空间中,13864是第一个在该空间运行的进程。

    • 踩坑

    [root@192 go-docker]# ./go-docker run --ti /bin/sh
    {"level":"fatal","msg":"fork/exec /proc/self/exe: no such file or directory","time":"2020-03-06T08:24:23-05:00"}
    

    原因: // TODO
    运行之后关闭之后,centos的/proc就没有了
    问题的原理

    解决: mount -t proc proc /proc

    demo-ns源码

    main.go

    package main
    
    import (
    	"github.com/sirupsen/logrus"
    	"github.com/urfave/cli"
    	"os"
    )
    
    const usage = `go-docker`
    
    func main() {
    	app := cli.NewApp()
    	app.Name = "go-docker"
    	app.Usage = usage
    
    	app.Commands = []cli.Command{
    		runCommand,
    		initCommand,
    	}
    	app.Before = func(context *cli.Context) error {
    		logrus.SetFormatter(&logrus.JSONFormatter{})
    		logrus.SetOutput(os.Stdout)
    		return nil
    	}
    	if err := app.Run(os.Args); err != nil {
    		logrus.Fatal(err)
    	}
    }
    
    

    参考

    1. nano在CentOS上的安装和使用
    2. 如何在 CentOS 8 上安装 Go
    3. 用go写一个docker#
  • 相关阅读:
    电脑能ping127.0.0.1但是ping不通本机ip
    用iis调试源代码
    pl登录提示服务不存在
    sqlserver保留一位小数(不是四舍五入)
    web应用程序与web网站发布时区别
    java的覆盖重写隐藏和C#中的不同
    导出word
    点击登录提交两次的问题
    oracle通过plsql代码倒库
    apply方法自解
  • 原文地址:https://www.cnblogs.com/yudidi/p/12317032.html
Copyright © 2020-2023  润新知