Add first draft
This commit is contained in:
commit
749780c39d
4 changed files with 104 additions and 0 deletions
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
|
||||||
|
```
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module containers-from-scratch
|
||||||
|
|
||||||
|
go 1.21.5
|
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