2 · Worked example — read every step
A minimal container runtime in ~100 lines:
func parent() {
cmd := exec.Command("/proc/self/exe", append([]string{"child"}, os.Args[2:]...)...)
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWPID |
syscall.CLONE_NEWUTS |
syscall.CLONE_NEWNS,
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
}
func child() {
// Set hostname
syscall.Sethostname([]byte("container"))
// Set up cgroups
cgroupPath := "/sys/fs/cgroup/minicontainer"
setCgroupMemory(cgroupPath, 256*1024*1024) // 256MB
setCgroupPids(cgroupPath, 64)
// Set up filesystem
setupRootfs("/tmp/rootfs")
mountProc()
// Run the command
cmd := exec.Command(os.Args[2], os.Args[3:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Println("error:", err)
}
// Cleanup
syscall.Unmount("/proc", 0)
cleanupCgroup(cgroupPath)
}
Run it: sudo go run main.go run /bin/sh
You now have a shell running in an isolated PID namespace, with its own hostname, limited to 256MB of memory, with a separate root filesystem. That's a container.
3 · Fill the gaps
The child sequence, from memory — limits before user code, /proc after the pivot, teardown last.
func child() {
syscall.Sethostname([]byte("container"))
cgroupPath := "/sys/fs/cgroup/minicontainer"
<span class="gap-slot"><input class="gap-input" data-answer="setCgroupMemory" size="17" spellcheck="false" autocomplete="off" autocapitalize="off"></span>(cgroupPath, 256*1024*1024)
setCgroupPids(cgroupPath, 64)
setupRootfs("/tmp/rootfs")
<span class="gap-slot"><input class="gap-input" data-answer="mountProc" size="11" spellcheck="false" autocomplete="off" autocapitalize="off"></span>()
cmd := exec.Command(os.Args[2], os.Args[3:]...)
cmd.Run()
syscall.Unmount(<span class="gap-slot"><input class="gap-input" data-answer=""/proc"" size="9" spellcheck="false" autocomplete="off" autocapitalize="off"></span>, 0)
<span class="gap-slot"><input class="gap-input" data-answer="cleanupCgroup" size="15" spellcheck="false" autocomplete="off" autocapitalize="off"></span>(cgroupPath)
}
Ship your project: Mini Container Runtime
Take what this module built up and make it a real tool: Project 4: Mini Container Runtime, four milestones from re-exec skeleton to cgroup limits with cleanup. Work in a VM, ship it with the safety notes in its README — four of five.