Skip to content

Commit fe5bd0b

Browse files
committed
bash: linux container in a few lines of code
1 parent 9ebbb77 commit fe5bd0b

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed
+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
---
2+
title: "Code"
3+
description: "Build a container runtime in a few lines of bash code."
4+
lead: "Build a container runtime in a few lines of bash code."
5+
date: 2021-02-20T20:03:33+08:00
6+
lastmod: 2021-02-23T23:30:45+08:00
7+
draft: false
8+
images: []
9+
menu:
10+
docs:
11+
parent: "containers"
12+
weight: 330
13+
toc: false
14+
---
15+
16+
I was inspired by this blog post ["Linux containers in a few lines of code"](https://zserge.com/posts/containers/).
17+
18+
So, I wrote a basic container runtime in a few lines of bash to learn deeper
19+
about containers. Containers aren't blackbox.
20+
21+
This will start a container running busybox:
22+
23+
```sh
24+
#!/bin/bash
25+
26+
set -eux # let's be safe
27+
28+
# Here's a tarball with a busybox container in it.
29+
# This is just the official busybox Docker container flattened into a single tarball.
30+
# Download the container (it's in a GitHub Gist published by my GitHub account).
31+
# If you cannot download it from my GitHub, you can try to download it from my
32+
# Google Drive: https://drive.google.com/file/d/10v-I8u_DX1B5hnrGk5AQBQWluDUzqYzY/view?usp=sharing
33+
# You can also easily make your own tarball to run instead of this one with `docker export`.
34+
curl -sL https://gist.githubusercontent.com/cedrickchee/dcca56be2ea72d57e1da63a5f8544635/raw/28a19e2d847159c42e9a5ee23b0e18f45b544bf6/gdrive_get_large_file.sh | bash -s 10v-I8u_DX1B5hnrGk5AQBQWluDUzqYzY busybox.tar
35+
36+
# Extract busybox.tar into a directory.
37+
mkdir container-root
38+
cd container-root
39+
tar -xf ../busybox.tar
40+
41+
# Generate a random cgroup id.
42+
uuid="cgroup_$(shuf -i 1000-2000 -n 1)"
43+
44+
# Create the cgroup.
45+
cgcreate -g "cpu,cpuacct,memory:$uuid"
46+
47+
# Assign CPU/memory limits to the cgroup.
48+
cgset -r cpu.shares=512 "$uuid"
49+
cgset -r memory.limit_in_bytes=1000000000 "$uuid"
50+
51+
# The following line does a lot of work:
52+
# 1. cgexec: use our new cgroup
53+
# 2. unshare: make and use a new PID, network, hostname, and mount namespace
54+
# 3. chroot: change root directory to current directory
55+
# 4. mount: use the right /proc in our new mount namespace
56+
# 5. hostname: change the hostname in the new hostname namespace to something fun
57+
# 6. busybox: finally, start busybox shell
58+
cgexec -g "cpu,cpuacct,memory:$uuid" \
59+
unshare -fmuipn --mount-proc \
60+
chroot "$PWD" \
61+
/bin/sh -c "
62+
/bin/mount -t proc proc /proc &&
63+
hostname container-fun &&
64+
sh"
65+
66+
# Here are some fun things to try once you're running your container!
67+
# Run them both in the container and in a normal shell and see the difference.
68+
# - ps aux
69+
# - hostname
70+
```
71+
72+
You have to run this as root.
73+
It only runs on Linux (namespaces and cgroups only exist on Linux).
74+
If you don't have it, `cgcreate` is in the libcgroup package.
75+
76+
If this didn't work on your linux distro, you might need to install some tools.
77+
If you already have the libcgroup package but don't have the cgroup tools,
78+
`cgcreate` comes in the libcgroup-tools package (RHEL based distros).
79+
80+
```sh
81+
# Ubuntu/Debian - This package contains the command-line tools.
82+
$ apt-get install cgroup-tools
83+
84+
# RHEL/CentOS - Control groups infrastructure
85+
yum install libcgroup
86+
87+
# Fedora - Tools and daemons for libcgroup
88+
$ dnf install libcgroup-tools
89+
```
90+
91+
I'm using an Ubuntu distro and I run these commands:
92+
93+
```sh
94+
$ apt-get install cgroup-tools
95+
Reading package lists... Done
96+
Building dependency tree
97+
Reading state information... Done
98+
The following additional packages will be installed:
99+
libcgroup1
100+
The following NEW packages will be installed:
101+
cgroup-tools libcgroup1
102+
0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
103+
Need to get 109 kB of archives.
104+
After this operation, 472 kB of additional disk space will be used.
105+
106+
$ sudo ./container-in-few-lines-of-code.sh
107+
... ...
108+
/ # ps aux
109+
PID USER TIME COMMAND
110+
1 root 0:00 sh
111+
6 root 0:00 ps aux
112+
```
113+
114+
References:
115+
- [Linux containers in 500 lines of code](https://blog.lizzie.io/linux-containers-in-500-loc.html)
116+
- [Writing a container in a few lines of Go code, as seen at DockerCon 2017](https://github.com/lizrice/containers-from-scratch)
117+
- [Docker implemented in around 100 lines of bash](https://github.com/p8952/bocker)
118+
- [How to build a minimalistic hello-world Docker image](https://codeburst.io/docker-from-scratch-2a84552470c8)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/bin/bash
2+
3+
set -eux # let's be safe
4+
5+
# Here's a tarball with a busybox container in it.
6+
# This is just the official busybox Docker container flattened into a single tarball.
7+
# Download the container (it's in a GitHub Gist published by my GitHub account).
8+
# If you cannot download it from my GitHub, you can try to download it from my
9+
# Google Drive: https://drive.google.com/file/d/10v-I8u_DX1B5hnrGk5AQBQWluDUzqYzY/view?usp=sharing
10+
# You can also easily make your own tarball to run instead of this one with `docker export`.
11+
curl -sL https://gist.githubusercontent.com/cedrickchee/dcca56be2ea72d57e1da63a5f8544635/raw/28a19e2d847159c42e9a5ee23b0e18f45b544bf6/gdrive_get_large_file.sh | bash -s 10v-I8u_DX1B5hnrGk5AQBQWluDUzqYzY busybox.tar
12+
13+
# Extract busybox.tar into a directory.
14+
mkdir container-root
15+
cd container-root
16+
tar -xf ../busybox.tar
17+
18+
# Generate a random cgroup id.
19+
uuid="cgroup_$(shuf -i 1000-2000 -n 1)"
20+
21+
# Create the cgroup.
22+
cgcreate -g "cpu,cpuacct,memory:$uuid"
23+
24+
# Assign CPU/memory limits to the cgroup.
25+
cgset -r cpu.shares=512 "$uuid"
26+
cgset -r memory.limit_in_bytes=1000000000 "$uuid"
27+
28+
# The following line does a lot of work:
29+
# 1. cgexec: use our new cgroup
30+
# 2. unshare: make and use a new PID, network, hostname, and mount namespace
31+
# 3. chroot: change root directory to current directory
32+
# 4. mount: use the right /proc in our new mount namespace
33+
# 5. hostname: change the hostname in the new hostname namespace to something fun
34+
# 6. busybox: finally, start busybox shell
35+
cgexec -g "cpu,cpuacct,memory:$uuid" \
36+
unshare -fmuipn --mount-proc \
37+
chroot "$PWD" \
38+
/bin/sh -c "
39+
/bin/mount -t proc proc /proc &&
40+
hostname container-fun &&
41+
sh"
42+
43+
# Here are some fun things to try once you're running your container!
44+
# Run them both in the container and in a normal shell and see the difference.
45+
# - ps aux
46+
# - hostname

0 commit comments

Comments
 (0)