示例代码node
相似这么启动./myDocker run -ti /bin/bash
git
大体流程 github
![]()
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.Start(), 调用本身传入了init,自身处理Init传参以下
复制代码
/bin/bash
)func RunContainerInitProcess(command string, args []string) error {
logrus.Infof("command %s", command)
defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
syscall.Mount("", "/", "", syscall.MS_PRIVATE | syscall.MS_REC, "")
// 须要加上上面一行,源码有误,或者见后面的坑的修改方法,同时这个方法会返回error最好捕捉下,MS_REC为目录子树递归的建立绑定挂载
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
}
复制代码
- 关于mount namespace和共享子树
- mount namespaces提供了过分的隔离,若是彻底隔离,一个挂在可能须要在全部的namespace都挂载一遍
- mount 时关于共享提供了4个参数MS_SHARED, MS_PRIVATE,MS_SLAVE,MS_UNBINDABLE
- 有个坑,例如ubuntu18中,默认挂载时shared,能够经过/proc/$pid/mountinfo
![]()
- 须要从新设置下
sudo mount --make-rprivate /
否则namespace里面修改/proc会影响其余namespace里面的/proc(应该只对此行命令以后的子进程等适用)- 修改以后
![]()
- 其余 使用
unshare
建立新的namespace shell:$unshare --user --mount --ipc --pid --net --uts -r --fork --propagation private bash
大体流程 docker
![]()
package subsystems
import(
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
)
type MemorySubSystem struct {
}
func (s *MemorySubSystem) Set(cgroupPath string, res *ResourceConfig) error {
if subsysCgroupPath, err := GetCgroupPath(s.Name(), cgroupPath, true); err == nil {
if res.MemoryLimit != "" {
if err := ioutil.WriteFile(path.Join(subsysCgroupPath, "memory.limit_in_bytes"), []byte(res.MemoryLimit), 0644); err != nil {
return fmt.Errorf("set cgroup memory fail %v", err)
}
}
return nil
} else {
return err
}
}
func (s *MemorySubSystem) Remove(cgroupPath string) error {
if subsysCgroupPath, err := GetCgroupPath(s.Name(), cgroupPath, false); err == nil {
return os.RemoveAll(subsysCgroupPath)
} else {
return err
}
}
func (s *MemorySubSystem) Apply(cgroupPath string, pid int) error {
if subsysCgroupPath, err := GetCgroupPath(s.Name(), cgroupPath, false); err == nil {
if err := ioutil.WriteFile(path.Join(subsysCgroupPath, "tasks"), []byte(strconv.Itoa(pid)), 0644); err != nil {
return fmt.Errorf("set cgroup proc fail %v", err)
}
return nil
} else {
return fmt.Errorf("get cgroup %s error: %v", cgroupPath, err)
}
}
func (s *MemorySubSystem) Name() string {
return "memory"
}
复制代码
对于
GetCgroupPath
方法其实作了如下事情shell
- 根据
/proc/self/mountinfo
找出对应的hierarchy的虚拟文件系统- 此文件每行相似(此行为memory 挂载的hierarchy文件系统) :
46 32 0:41 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:24 - cgroup cgroup rw,memory
, 根据最后的memory 找到/sys/fs/cgroup/memory
,- 最终此文件夹下建立cgroup文件夹名就是传入的参数
cgroupPath
,而后在cgroup下修改限制(Set)或在tasks文件中增长pid(Apply)
- 主要是修改为经过管道传输cmd,和加入了环境变量,略
- 修改以后的流程
![]()