{"id":13461509,"url":"https://github.com/shuveb/containers-the-hard-way","last_synced_at":"2025-05-16T02:09:20.641Z","repository":{"id":38400478,"uuid":"266515261","full_name":"shuveb/containers-the-hard-way","owner":"shuveb","description":"Learning about containers and how they work by creating them the hard way","archived":false,"fork":false,"pushed_at":"2022-01-22T04:25:22.000Z","size":132,"stargazers_count":1634,"open_issues_count":3,"forks_count":194,"subscribers_count":31,"default_branch":"master","last_synced_at":"2025-04-08T13:05:47.604Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shuveb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-24T10:11:30.000Z","updated_at":"2025-04-07T08:53:18.000Z","dependencies_parsed_at":"2022-08-09T03:16:37.538Z","dependency_job_id":null,"html_url":"https://github.com/shuveb/containers-the-hard-way","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuveb%2Fcontainers-the-hard-way","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuveb%2Fcontainers-the-hard-way/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuveb%2Fcontainers-the-hard-way/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuveb%2Fcontainers-the-hard-way/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shuveb","download_url":"https://codeload.github.com/shuveb/containers-the-hard-way/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254453667,"owners_count":22073618,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T11:00:41.283Z","updated_at":"2025-05-16T02:09:20.563Z","avatar_url":"https://github.com/shuveb.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Containers the hard way: Gocker: A mini Docker written in Go\nIt is a set of Linux's operating system primitives that provide the illusion of a container. A process or a set of processes can shed their environment or namespaces and live in new namespaces of their own, separate from the host's `default` namespace. Container management systems like Docker make it incredibly easy to manage containers on your machine. But how are these containers constructed? It is just a sequence of Linux system calls (involving namespaces and cgroups, mainly), at the very basic level while also leveraging other existing Linux technologies for container file system, networking, etc.\n\n## What is Gocker?\nGocker is an implementation from scratch of the core functionalities of Docker in the Go programming language. The main aim here is to provide an understanding of how exactly containers work at the Linux system call level. Gocker allows you to create containers, manage container images, execute processes in existing containers, etc.\n\n![Gocker session](https://unixism.net/wp-content/uploads/2020/06/Gocker.png)\n\n## Gocker explanation\nGocker and how it works is explained at the Linux system call level [on the Unixism blog](https://unixism.net/2020/06/containers-the-hard-way-gocker-a-mini-docker-written-in-go/). If you are interested in that level of detail, please read it.\n\n## Why Gocker?\nWhen I came across [bocker](https://github.com/p8952/bocker), which is Docker-like container management written system in Bash shell script, I found 2 problems with it:\n* Bocker uses various Linux utilities. While you get the point, command line utilities are opaque, and you don't get to understand what they are doing at the Linux system call level. Also, a single command can sometime issue a more than one pertinent system calls.\n* Bocker's last commit is more than 5 years ago, and it does not work anymore. Docker Hub API changes seem to have broken it.\n\nGocker on the other hand is pure Go source code which allows you to see what exactly goes on at the Linux system call level. This should give you a way better understanding of how containers actually work.\n\nDon't get me wrong here. Bocker is still a fantastic and very creatively written tool. If you want to understand how containers work, you should still take a look at it and I'm confident you'll learn a thing or two from it, just like I did.\n \n## Gocker capabilities\nGocker can emulate the core of Docker, letting you manage Docker images (which it gets from Docker Hub), run containers, list running containers or execute a process in an already running container:\n* Run a process in a container\n   * `gocker run \u003c--cpus=cpus-max\u003e \u003c--mem=mem-max\u003e \u003c--pids=pids-max\u003e \u003cimage[:tag]\u003e \u003c/path/to/command\u003e`\n* List running containers\n   * `gocker ps`\n* Execute a process in a running container\n   * `gocker exec \u003ccontainer-id\u003e \u003c/path/to/command\u003e`\n* List locally available images\n   * `gocker images`\n* Remove a locally available image\n   * `gocker rmi \u003cimage-id\u003e`\n\n### Other capabilities     \n* Gocker uses the Overlay file system to create containers quickly without the need to copy whole file systems while also sharing the same container image between multiple container instances.\n* Gocker containers get their own networking namespace and are able to access the internet. See limitations below.\n* You can control system resources like CPU percentage, the amount of RAM and the number of processes. Gocker achieves this by leveraging cgroups.\n    \n## Gocker container isolation\nContainers created with Gocker get the following namespaces of their own (see `run.go`):\n* File system (via `chroot`)\n* PID \n* IPC\n* UTS (hostname)\n* Mount\n* Network\n\nWhile cgroups to limit the following are created, containers are left to use unlimited resources unless you specify the `--mem`, `--cpus` or `--pids` options to the `gocker run` command. These flags limit the maximum RAM, CPU cores and PIDs the container can consume respectively.\n* Number of CPU cores\n* RAM\n* Number of PIDs (to limit processes)\n\n ## An example Gocker session\n ```\n ➜  sudo ./gocker images          \n 2020/06/12 08:32:23 Cmd args: [./gocker images]\n IMAGE\t             TAG\t   ID\n centos\n \t          latest 470671670cac\n redis\n \t          latest c349430fd524\n ubuntu\n \t           18.04 c3c304cb4f22\n \t          latest 1d622ef86b13\n➜  sudo ./gocker run alpine /bin/sh\n2020/06/12 08:33:33 Cmd args: [./gocker run alpine /bin/sh]\n2020/06/12 08:33:33 New container ID: 7bfe9b0f1c2e\n2020/06/12 08:33:33 Downloading metadata for alpine:latest, please wait...\n2020/06/12 08:33:36 imageHash: a24bb4013296\n2020/06/12 08:33:36 Checking if image exists under another name...\n2020/06/12 08:33:36 Image doesn't exist. Downloading...\n2020/06/12 08:33:38 Successfully downloaded alpine\n2020/06/12 08:33:38 Uncompressing layer to: /var/lib/gocker/images/a24bb4013296/fe8bebfdf212/fs \n2020/06/12 08:33:38 Image to overlay mount: a24bb4013296\n2020/06/12 08:33:38 Cmd args: [/proc/self/exe setup-netns 7bfe9b0f1c2e]\n2020/06/12 08:33:38 Cmd args: [/proc/self/exe setup-veth 7bfe9b0f1c2e]\n2020/06/12 08:33:38 Cmd args: [/proc/self/exe child-mode --img=a24bb4013296 7bfe9b0f1c2e /bin/sh]\n/ # ifconfig \nlo        Link encap:Local Loopback  \n          inet addr:127.0.0.1  Mask:255.0.0.0\n          inet6 addr: ::1/128 Scope:Host\n          UP LOOPBACK RUNNING  MTU:65536  Metric:1\n          RX packets:0 errors:0 dropped:0 overruns:0 frame:0\n          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0\n          collisions:0 txqueuelen:1000 \n          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)\n\nveth1_7bfe9b Link encap:Ethernet  HWaddr 02:42:6E:E8:FC:06  \n          inet addr:172.29.41.13  Bcast:172.29.255.255  Mask:255.255.0.0\n          inet6 addr: fe80::42:6eff:fee8:fc06/64 Scope:Link\n          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1\n          RX packets:22 errors:0 dropped:0 overruns:0 frame:0\n          TX packets:7 errors:0 dropped:0 overruns:0 carrier:0\n          collisions:0 txqueuelen:1000 \n          RX bytes:2328 (2.2 KiB)  TX bytes:586 (586.0 B)\n\n/ # ps aux\nPID   USER     TIME  COMMAND\n    1 root      0:00 /proc/self/exe child-mode --img=a24bb4013296 7bfe9b0f1c2e /bin/sh\n    7 root      0:00 /bin/sh\n    9 root      0:00 ps aux\n/ # apk add python3\nfetch http://dl-cdn.alpinelinux.org/alpine/v3.12/main/x86_64/APKINDEX.tar.gz\nfetch http://dl-cdn.alpinelinux.org/alpine/v3.12/community/x86_64/APKINDEX.tar.gz\n(1/10) Installing libbz2 (1.0.8-r1)\n(2/10) Installing expat (2.2.9-r1)\n(3/10) Installing libffi (3.3-r2)\n(4/10) Installing gdbm (1.13-r1)\n(5/10) Installing xz-libs (5.2.5-r0)\n(6/10) Installing ncurses-terminfo-base (6.2_p20200523-r0)\n(7/10) Installing ncurses-libs (6.2_p20200523-r0)\n(8/10) Installing readline (8.0.4-r0)\n(9/10) Installing sqlite-libs (3.32.1-r0)\n(10/10) Installing python3 (3.8.3-r0)\nExecuting busybox-1.31.1-r16.trigger\nOK: 53 MiB in 24 packages\n/ # python3\nPython 3.8.3 (default, May 15 2020, 01:53:50) \n[GCC 9.3.0] on linux\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n\u003e\u003e\u003e exit()\n/ # exit\n2020/06/12 08:34:34 Container done.\n➜  sudo ./gocker run ubuntu /bin/bash\n2020/06/12 08:35:13 Cmd args: [./gocker run ubuntu /bin/bash]\n2020/06/12 08:35:13 New container ID: c7eb7bab7e4c\n2020/06/12 08:35:13 Image already exists. Not downloading.\n2020/06/12 08:35:13 Image to overlay mount: 1d622ef86b13\n2020/06/12 08:35:13 Cmd args: [/proc/self/exe setup-netns c7eb7bab7e4c]\n2020/06/12 08:35:13 Cmd args: [/proc/self/exe setup-veth c7eb7bab7e4c]\n2020/06/12 08:35:13 Cmd args: [/proc/self/exe child-mode --img=1d622ef86b13 c7eb7bab7e4c /bin/bash]\nroot@c7eb7bab7e4c:/# \n```\n[On another terminal]\n```\n➜  sudo ./gocker ps\n[sudo] password for shuveb: \n2020/06/12 08:36:19 Cmd args: [./gocker ps]\nCONTAINER ID\tIMAGE\t\tCOMMAND\nc7eb7bab7e4c\tubuntu:latest\t/usr/bin/bash\n➜  sudo ./gocker exec c7eb7bab7e4c /bin/bash\n2020/06/12 08:37:15 Cmd args: [./gocker exec c7eb7bab7e4c /bin/bash]\nroot@c7eb7bab7e4c:/# ps aux\nUSER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND\nroot           1  0.0  0.0 1153100 6132 ?        Sl   03:05   0:00 /proc/self/exe child-mode --img=1d622ef86b13 \nroot           8  0.0  0.0   4116  3236 ?        S+   03:05   0:00 /bin/bash\nroot          11  0.0  0.0   4116  3376 ?        S    03:07   0:00 /bin/bash\nroot          14  0.0  0.0   5888  2956 ?        R+   03:07   0:00 ps aux\nroot@c7eb7bab7e4c:/# \n```\n## Gocker limitations\nHere are some limitations I'd love to fix in a future release:\n\n* Gocker does not currently support exposing container ports on the host. Whenever Docker containers need to expose ports on the host, Docker uses the program `docker-proxy` as a proxy to get that done. Gocker needs a similar proxy developed. While Gocker containers can access the internet today, the ability to expose ports on the host will be a great feature to have (mainly to learn how that's done).\n* Gocker does not do error handling well. Should something go wrong especially when attempting to run a container, Gocker might not cleanly unmount some file systems.\n\n## Containers accessing internet\nWhen you run Gocker for the first time, a new bridge, `gocker0` is created. Since all container network interfaces are connected to this bridge, they can talk to each other without you having to do anything. For containers to be able to reach the internet though, you need to enable packet forwarding on the host. For this, a convenience script `enable_internet.sh` has been provided. You might need to change it to reflect the name of your internet connected interface before you run it. There are instructions in the script. After you run this, Gocker containers should be able to reach the internet and install packages, etc.\n\n## External Go libraries used\n* [GoContainerRegistry](https://github.com/google/go-containerregistry) for downloading container images from a container registry, the default being Docker Hub.\n* [PFlag](https://github.com/spf13/pflag) for handling command line flags.\n* [Netlink](https://github.com/vishvananda/netlink) to configure Linux network interfaces without having to get bogged down by Netlink socket programming.\n* [Unix](https://golang.org/x/sys/unix) Because Unix :)\n\n## Disclaimer\nGocker runs as root. Use at your own risk. This is my first Go program beyond a reasonable number of lines, and I'm sure there are better ways to write Go programs and there might still be a lot of bugs lingering in here. Here are some things Gocker does to your system so you know:\n\n* It creates the `gocker0` bridge if it does not exist.\n* It blindly assumes that the IP address range `172.29.*.*` is available and uses it.\n* It creates various namespaces and cgroups.\n* It mounts overlay file systems.\n\nTo this end, the safest way to run Gocker might be in a virtual machine.\n\n### Distributions\nI developed Gocker on my day-to-day Arch Linux based computer. I also tested Gocker on an Ubuntu 20.04 virtual machine. It works great.\n\n## Building and running\nOnce you clone the repo, assuming you have Go installed on your machine, change into the Gocker directory and use the following command to retrieve dependencies:\n\n```go mod download```\n\nThen, to build `gocker`, run the following command:\n\n```go build -o gocker .```\n\n## About me\nMy name is Shuveb Hussain and I'm the author of the Linux-focused blog [Unixism.net](https://unixism.net). You can [follow me on Twitter](https://twitter.com/shuveb) where I post tech-related content mostly focusing on Linux, performance, scalability and cloud technologies.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshuveb%2Fcontainers-the-hard-way","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshuveb%2Fcontainers-the-hard-way","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshuveb%2Fcontainers-the-hard-way/lists"}