Add first draft
This commit is contained in:
commit
749780c39d
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
container-root
|
||||
.idea
|
21
README.md
Normal file
21
README.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
# containers-from-scratch
|
||||
|
||||
This repository contains my re-implementation of
|
||||
[Containers From Scratch • Liz Rice • GOTO 2018](https://www.youtube.com/watch?v=8fi7uSYlOdc).
|
||||
|
||||
## Setup
|
||||
|
||||
One time setup:
|
||||
|
||||
```shell
|
||||
docker run --rm -it ubuntu bash
|
||||
|
||||
# Copy contents of the container:
|
||||
docker cp 9405abfa4a78:/ container-root
|
||||
```
|
||||
|
||||
Then, just run the Go program as *root*:
|
||||
|
||||
```shell
|
||||
sudo go run main.go run bash
|
||||
```
|
78
main.go
Normal file
78
main.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// docker run <container-name> <command> <args>
|
||||
// go main.go run <command> <args>
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 3 {
|
||||
panic("found too few arguments for the program.")
|
||||
}
|
||||
switch os.Args[1] {
|
||||
case "run":
|
||||
executeRun()
|
||||
case "child":
|
||||
executeChild()
|
||||
default:
|
||||
panic(fmt.Sprintf("found an unsupported command: %s", os.Args[0]))
|
||||
}
|
||||
}
|
||||
|
||||
func executeRun() {
|
||||
fmt.Printf("starting: 'run' in PID: %d\n", os.Getpid())
|
||||
|
||||
cmd := exec.Command("/proc/self/exe", append([]string{"child"}, os.Args[2:]...)...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
|
||||
Unshareflags: syscall.CLONE_NEWNS,
|
||||
}
|
||||
|
||||
must(cmd.Run())
|
||||
}
|
||||
|
||||
func executeChild() {
|
||||
fmt.Printf("starting: %s in PID: %d\n", os.Args[2], os.Getpid())
|
||||
|
||||
setupCgroup()
|
||||
|
||||
syscall.Sethostname([]byte("container"))
|
||||
syscall.Chroot("./container-root")
|
||||
os.Chdir("/")
|
||||
|
||||
syscall.Mount("proc", "proc", "proc", 0, "")
|
||||
defer syscall.Unmount("/proc", 0)
|
||||
|
||||
cmd := exec.Command(os.Args[2])
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
must(cmd.Run())
|
||||
}
|
||||
|
||||
func setupCgroup() {
|
||||
cgroupDir := fmt.Sprintf("/sys/fs/cgroup/pids/%d", time.Now().Unix())
|
||||
must(os.Mkdir(cgroupDir, 0755))
|
||||
|
||||
os.WriteFile(fmt.Sprintf("%s/pids.max", cgroupDir), []byte("20"), 0644)
|
||||
os.WriteFile(fmt.Sprintf("%s/notify_on_release", cgroupDir), []byte("1"), 0644)
|
||||
os.WriteFile(fmt.Sprintf("%s/cgroup.procs", cgroupDir), []byte(strconv.Itoa(os.Getpid())), 0644)
|
||||
}
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue