HOME TAGS ARCHIVE ABOUT

Let's GO a little bit with Docker !

You may have noticed that GO has taken the top spot in the devops world, DON’T BELIEVE ME. I don’t really know why, besides what google tells about how good concurrency is in Go, or what they calls go-routines. But most of the exciting tools we use are built with this language :

- Docker
- Kubernets 
- Terraform
- .... 

So I decided to take a look at this mysterious language, get familiar a little bit the syntax, and why not replace my favorite language Python (Naaah, NO WAY)`.

Before starting, I highly recommend that you watch this video, in case you’re not familiar with linux namespaces, cgroups …

So let’s build our first container from scratch with GO !!

package main

import (
	"fmt"
	"os"
	"os/exec"
	"syscall"
)

func main() {
    // entrypoint, possible args are : run or child 
	switch os.Args[1] {
	case "run":
		parent()
	case "child":
		child()
	default:
		fmt.Println("run <COMMAND> | run a command inside a container")
	}
}

func parent() {
    // The parent calls forks himself and calls the 'child' with the specified arguments
	cmd := exec.Command("/proc/self/exe", append([]string{"child"}, os.Args[2:]...)...)
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    // Here is the good part 
    // Cloneflags: Where we define the used namespaces :
    // NEWUTS : for hostname 
    // NEWPID: for pids
    // NEWNS : for mounts 
    // NEWUSER: for users 
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
		Credential: &syscall.Credential{Uid: 0, Gid: 0},
		UidMappings: []syscall.SysProcIDMap{
			{ContainerID: 0, HostID: os.Getuid(), Size: 1},
		},
		GidMappings: []syscall.SysProcIDMap{
			{ContainerID: 0, HostID: os.Getegid(), Size: 1},
		},
	}
	catch(cmd.Run())
}

func child() {
	cmd := exec.Command(os.Args[2], os.Args[3:]...)
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout

    catch(syscall.Sethostname([]byte("container")))
    // mount a filesystem to test with 
	catch(syscall.Chroot("/home/neo/fakefs"))
	catch(os.Chdir("/"))
	catch(syscall.Mount("proc", "proc", "proc", 0, ""))
	catch(cmd.Run())
	// catch(syscall.Unmount("proc", 0))
}

// catch errors if they happened
func catch(err error) {
	if err != nil {
		panic(err)
	}
}

Time to get real :

    $ go run main.go run /bin/bash # And now we are inside the container :)