Building a Koker - Kien's mini Docker.
What I cannot create, I do not understand โ Richard Feynman
- What is Koker?
- Koker is a tiny educational-purpose Docker-like tool, written in Golang.
- Unlike Docker, Koker just uses a set of Linux's operating system primitives that provide the illusion of a container. It uses neither containerd nor runc.
- Why Koker?
- Have you ever wondered how Docker containers are constructed?
- Koker provides an understanding of how extactly containers work at the Linux system call level by using logging (every steps!).
- Control Groups for resource restriction (CPU, Memory, Swap, PIDs). All CGroups modes (Legacy - v1, Hybrid - v1 & v2, Unified - v2) are handled.
- Namespace for global system resources isolation (Mount, UTS, Network, IPS, PID).
- Union File System for branches to be overlaid in a single coherent file system. (OverlayFS)
- Koker is highly inspired by:
- Should I use Koker in production?
- Nope, Koker isn't a production ready tool!
- Can Koker perform every Docker tasks?
- Nope, ofc, Koker doesn't aim to recreate every Docker's tasks. There are just some simple tasks for educational-purpose.
- If you are looking for a tool similar to Docker in terms of functionality but do not require a daemon to operate, Podman is a suitable choice for you.
- Install:
$ go get -u github.com/ntk148v/koker
# Or you can build yourself
$ git clone https://github.com/ntk148v/koker.git koker
$ cd koker
$ make build
$ sudo /tmp/koker --help
-
You can also download binary file in releases.
-
Usage:
- Note that you must have root permission to execute koker.
$ sudo koker --help
NAME:
koker - Kien's mini Docker
USAGE:
koker [global options] command [command options] [arguments...]
VERSION:
v0.0.1
AUTHOR:
Kien Nguyen-Tuan <[email protected]>
COMMANDS:
container, c Manage container
image, i Manage images
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--debug, -D Set log level to debug. You will see step-by-step what were executed (default: false)
--help, -h show help (default: false)
--quiet, -q Disable logging altogether (quiet mode) (default: false)
--version, -v print the version (default: false)
$ sudo koker container --help
NAME:
koker container - Manage container
USAGE:
koker container command [command options] [arguments...]
COMMANDS:
run Run a command in a new container
child
rm Remove a container (WIP)
ls List running containers
exec Run a command inside a running container
help, h Shows a list of commands or help for one command
OPTIONS:
--help, -h show help (default: false)
$ sudo koker image --help
NAME:
koker image - Manage images
USAGE:
koker image command [command options] [arguments...]
COMMANDS:
ls List all available images
pull Pull an image or a repository from a registry (using image's name)
rm Remove a image (using image's name)
help, h Shows a list of commands or help for one command
OPTIONS:
--help, -h show help (default: false)
- Start container and execute command.
$ sudo koker -D container run --hostname test --mem 1024 alpine sh # Enable debugging
11:08AM INF Load image repository from file repository=/var/lib/koker/images/repositories.json
11:08AM DBG Load image repository
11:08AM DBG Check default bridge is up or not bridge=koker0
11:08AM INF Setup network for container container=ccjuq1p3l1hn8clpgib0
11:08AM DBG Setup virtual ethernet peer=veth1_ccjumf9 virt=veth0_ccjumf9
11:08AM DBG Set the master of the link device link=veth0_ccjumf9 master=koker0
11:08AM DBG Mount new network namespace netns=/var/lib/koker/netns/ccjuq1p3l1hn8clpgib0
11:08AM DBG Call syscall unshare CLONE_NEWNET netns=/var/lib/koker/netns/ccjuq1p3l1hn8clpgib0
11:08AM DBG Mount new network namespace netns=/var/lib/koker/netns/ccjuq1p3l1hn8clpgib0
11:08AM DBG Mount target source=/proc/self/ns/net target=/var/lib/koker/netns/ccjuq1p3l1hn8clpgib0
11:08AM DBG Set network namespace netns=/var/lib/koker/netns/ccjuq1p3l1hn8clpgib0
11:08AM DBG Put link device into a new network namespace link=veth1_ccjumf9 netns=/var/lib/koker/netns/ccjuq1p3l1hn8clpgib0
11:08AM DBG Set network namespace by file netns=/var/lib/koker/netns/ccjuq1p3l1hn8clpgib0
11:08AM DBG Change the name of the link device newname=eth0 oldname=veth1_ccjumf9
11:08AM DBG Add IP address to the ip device ip=172.69.216.38/16 link=eth0
11:08AM DBG Enable the link device link=eth0
11:08AM DBG Set gateway for the link device gateway=172.69.0.1 link=eth0
11:08AM DBG Enable the link device link=lo
11:08AM INF Construct new Image instance image=alpine
11:08AM INF Image exists, reuse image=index.docker.io/library/alpine:latest
11:08AM INF Mount filesystem for container from an image container=ccjuq1p3l1hn8clpgib0 image=index.docker.io/library/alpine:latest
11:08AM DBG Mount target source=none target=/var/lib/koker/containers/ccjuq1p3l1hn8clpgib0/mnt
11:08AM DBG Copy container config from image config container=ccjuq1p3l1hn8clpgib0 image=library/alpine
11:08AM INF Load image repository from file repository=/var/lib/koker/images/repositories.json
11:08AM DBG Load image repository
11:08AM DBG Load container config from file container=ccjuq1p3l1hn8clpgib0
11:08AM INF Set hostname container=ccjuq1p3l1hn8clpgib0
11:08AM INF Set container's limit using cgroup container=ccjuq1p3l1hn8clpgib0
11:08AM DBG Set container's memory limit container=ccjuq1p3l1hn8clpgib0
11:08AM DBG Set container's pids limit container=ccjuq1p3l1hn8clpgib0
11:08AM DBG Set container's cpus limit container=ccjuq1p3l1hn8clpgib0
11:08AM INF Copy nameserver config container=ccjuq1p3l1hn8clpgib0
11:08AM INF Execute command container=ccjuq1p3l1hn8clpgib0
11:08AM DBG Set network namespace container=ccjuq1p3l1hn8clpgib0
11:08AM DBG Set network namespace by file netns=/var/lib/koker/netns/ccjuq1p3l1hn8clpgib0
11:08AM DBG Mount target source=tmpfs target=dev
11:08AM DBG Mount target source=proc target=proc
11:08AM DBG Mount target source=sysfs target=sys
11:08AM DBG Mount target source=tmpfs target=tmp
11:08AM DBG Execute command command=sh container=ccjuq1p3l1hn8clpgib0
/ #
# Hit <Ctrl+c>
11:09AM DBG Unmount target source=tmpfs target=dev
11:09AM DBG Unmount target source=proc target=proc
11:09AM DBG Unmount target source=sysfs target=sys
11:09AM DBG Unmount target source=tmpfs target=tmp
11:09AM INF Save image repository to file repository=/var/lib/koker/images/repositories.json
11:09AM DBG Unmount target source=none target=/var/lib/koker/containers/ccjuq1p3l1hn8clpgib0/mnt
11:09AM DBG Unmount target source=/proc/self/ns/net target=/var/lib/koker/netns/ccjuq1p3l1hn8clpgib0
11:09AM INF Delete container container=ccjuq1p3l1hn8clpgib0
11:09AM DBG Remove container's directory container=ccjuq1p3l1hn8clpgib0
11:09AM DBG Remove container's network namespace container=ccjuq1p3l1hn8clpgib0
11:09AM DBG Remove container cgroups container=ccjuq1p3l1hn8clpgib0
11:09AM INF Save image repository to file repository=/var/lib/koker/images/repositories.json
- Pull and list image(s).
$ sudo koker -D image pull alpine
$ sudo koker -D image ls
11:13AM INF Load image repository from file repository=/var/lib/koker/images/repositories.json
11:13AM DBG Load image repository
REPOSITORY TAG IMAGE ID
jwilder/whoami latest 4a4c1589a078
library/alpine 3.16.2 0261ca8a4a79
library/alpine edge 9a2e669787f4
library/alpine latest 0261ca8a4a79
11:13AM INF Save image repository to file repository=/var/lib/koker/images/repositories.json
- List all available containers (run a container then run the above command in the another session).
$ koker -D container ls
11:11AM INF Load image repository from file repository=/var/lib/koker/images/repositories.json
11:11AM DBG Load image repository
11:11AM DBG Load container config from file container=ccjuo013l1hkmh7sk540
CONTAINER ID IMAGE COMMAND
ccjuq1p3l1hn8clpgib0 0261ca8a4a79 sh
11:11AM INF Save image repository to file repository=/var/lib/koker/images/repositories.json
- Run a command inside a running container.
$ sudo koker -D container exec ccjuq1p3l1hn8clpgib0 sh
11:17AM INF Load image repository from file repository=/var/lib/koker/images/repositories.json
11:17AM DBG Load image repository
11:17AM DBG Load container config from file container=ccjuq1p3l1hn8clpgib0
11:17AM INF Execute command container=ccjuq1p3l1hn8clpgib0
11:17AM DBG Execute command command=sh container=ccjuq1p3l1hn8clpgib0
/ #
- Run container with limited resource.
- Run golang-memtest container to allocate 20MiB in 10MiB container.
$ sudo koker container run --mem 10 fabianlee/golang-memtest:1.0.0 20
9:20AM INF Load image repository from file repository=/var/lib/koker/images/repositories.json
9:20AM INF Setup network for container container=cdmr2vmfvq0sn7gs7r80
9:20AM INF Construct new Image instance image=fabianlee/golang-memtest:1.0.0
9:20AM INF Image exists, reuse image=index.docker.io/fabianlee/golang-memtest:1.0.0
9:20AM INF Mount filesystem for container from an image container=cdmr2vmfvq0sn7gs7r80 image=index.docker.io/fabianlee/golang-memtest:1.0.0
9:20AM INF Load image repository from file repository=/var/lib/koker/images/repositories.json
9:20AM INF Set hostname container=cdmr2vmfvq0sn7gs7r80
9:20AM INF Set container's limit using cgroup container=cdmr2vmfvq0sn7gs7r80
9:20AM INF Copy nameserver config container=cdmr2vmfvq0sn7gs7r80
9:20AM INF Execute command container=cdmr2vmfvq0sn7gs7r80
Alloc = 0 MiB TotalAlloc = 0 MiB Sys = 68 MiB NumGC = 0
Asked to allocate 20Mb
Alloc = 1 MiB TotalAlloc = 1 MiB Sys = 68 MiB NumGC = 0
Alloc = 2 MiB TotalAlloc = 2 MiB Sys = 68 MiB NumGC = 0
Alloc = 3 MiB TotalAlloc = 3 MiB Sys = 68 MiB NumGC = 0
Alloc = 4 MiB TotalAlloc = 4 MiB Sys = 68 MiB NumGC = 1
Alloc = 5 MiB TotalAlloc = 5 MiB Sys = 68 MiB NumGC = 1
Alloc = 6 MiB TotalAlloc = 6 MiB Sys = 68 MiB NumGC = 1
Alloc = 7 MiB TotalAlloc = 7 MiB Sys = 68 MiB NumGC = 1
Alloc = 8 MiB TotalAlloc = 8 MiB Sys = 68 MiB NumGC = 2
9:20AM ERR Something went wrong error="error running child command: signal: killed"
9:20AM INF Save image repository to file repository=/var/lib/koker/images/repositories.json
9:20AM INF Delete container container=cdmr2vmfvq0sn7gs7r80
9:20AM INF Save image repository to file repository=/var/lib/koker/images/repositories.json
- If you find logging is annoying, ignore them with "--quiet" option.
$ sudo koker -q container ls
CONTAINER ID IMAGE COMMAND
ccjuq1p3l1hn8clpgib0 0261ca8a4a79 sh
$ sudo koker -q container run --hostname test --mem 1024 alpine sh
/ #
- Note if you hit this kind of error, you should check the current max open files.
5:09PM ERR Something went wrong error="unable to pull image: unable to extract tarball's layer: open /var/lib/koker/images/2d389e545974d4a93ebdef09b650753a55f72d1ab4518d17a30c0e1b3e297444/31b3f1ad4ce1f369084d0f959813c51df0ca17d9877d5ee88c2db6ff88341430/usr/lib/x86_64-linux-gnu/perl-base/unicore/lib/Age/V80.pl: too many open files"
# Check current max open files
ulimit -n
# Change value to appropriate value, for example 4096
ulimit -n 4096
Pull requests and issues are alway welcome!