11 ms·
Linux containers in a few lines of code
- lidHanteyk 6y agoNice work. I'm reminded of bocker [0], which also implements this sort of functionality in only a few dozen lines of code. The function which corresponds to this post [1] is relatively short and readable. [0] https://github.com/p8952/bocker https://github.com/p8952/bocker [1] https://github.com/p8952/bocker/blob/master/bocker#L61-L90 https://github.com/p8952/bocker/blob/master/bocker#L61-L90
- benjaminjosephw 6y agoI love minimal code like this as a way of really understanding how something works. The author is pretty consistent too - he's got some great projects like an ultra-minimal electron alternative: https://github.com/zserge/webview https://github.com/zserge/webview
- faizshah 6y agoSame, I really like the book 500 Lines or Less for this as well: https://github.com/aosabook/500lines https://github.com/aosabook/500lines I’ve personally been getting in to writing minimal code because I’ve grown frustrated with how simple tasks can result in complex code that’s difficult to maintain. Minimal code is easier to come back to. A 100 line script can be easier to understand than a 500 line script.
- dmayle 6y agoA little bit of education about container systems in linux[1]. A container system is typically made up a number of components: isolation layer: the piece that limits privileges and resource usage. (On linux, this is usually handled by cgroups and the kernel, but could also be handled by something like kvm for vm-based containers) raw container configuration: Given an image and some metadata (like cpu limits), launch an isolated process. (On linux, this is usually handled by runc when working with cgroups) container api daemon: Manage the list of container processes and available images. Provide a unix socket based API for manipulating isolated processes, launching, deleting, connecting, etc. (In the case of docker, they provide a daemon which abstracts the containerd daemon, or you can use containerd alone without docker) container command line tool: Provide a user/developer interface to the three things above. This is the docker command. When you install containerd without docker this is the ctr command. Docker, which is probably the most famous container distribution, pairs the docker command with the docker daemon to abstract away the containerd daemon, runc, and cgroups. If you use containerd alone, you get ctr/containerd/runc/cgroups. There's a standalone command line tool (crictl) which replaces both ctr and docker and can be used on top of either the docker daemon or containerd. [1] Container systems seem to have a relatively complex abstraction over what is a relatively simple architecture.
- Spivak 6y agoI feel like podman is proving that you don't really need the api daemon and a porcelain over runc with a one-off process supervisor is sufficient for a good number of workloads. Being able to run containers like any other process and leave the lifecycle management to systemd is actually really nice.
- freedomben 6y agoCould not agree more. As a Fedora user I was mildly intrigued when Podman showed up, I played with it briefly but stopped because most of my projects used docker-compose, which doesn't work with Podman. When I went to work at Red Hat I decided to really try Podman, and I love it now. Once I discovered that Podman supports Kubernetes Pods (same YAML and all) I realized how clunky docker-compose actually is. Since most of my projects now run in K8s anyway, it's awesome to have the app run as a Pod locally so it can easily be tested with any sidecars in the same configuration. There are still uses for docker-compose, and I don't expect it to disappear from my life completely any time soon, but Podman does have a great place in my toolkit.
- throwaway8941 6y agopodman-compose works fine for me, despite being advertised as "still under development". https://github.com/containers/podman-compose https://github.com/containers/podman-compose
- freedomben 6y agoThanks, it might be time for me to give it another try.
- terrywang 6y agopodman-compose works better than expected, however, it is NoT a "drop-in" replacement for docker-compose, at least now.
- jacobr1 6y ago
- ShorsHammer 6y agoDIY Containers on Linux is probably a better term here given that Linux Containers is already heavily in use around the world and included in ubuntu by Canonical? For me this is enough to get a container running: lxd init lxc launch ubuntu mycontainer lxc exec mycontainer bash https://linuxcontainers.org/ https://linuxcontainers.org/
- throwaway55554 6y agoI don't know, I find "Linux Containers" to be sufficiently generic as Linux has native support for them. LXD/LXC, Docker, etc, are simply tools built upon that.
- deleted 6y ago[deleted]
- sp332 6y agoI agree. Most people who read "Linux Containers" are going to think of Docker, not LXD.
- cyphar 6y agoWhich is ironic given that LXC long predates Docker (having been involved in the original discussions of the kernel APIs being used by containers today), and Docker used LXC for a long portion of its history.
- josteink 6y agoPeople are different. When I think about containers, I think about isolated system images, lightweight VMs, which I can use and adapt to solve a problem at hand. When I hear Docker, I think about static, locked-down application-images made by others, to deploy in the cloud, which I'm not given to adapt to my needs, and I also think about things which is not natively integrated into my Linux-distro and for which I will have to provide glue manually. I also think about a startling amount of complexity in a new stack which I would have to learn, just to manage what is actually really just basic Linux-systems. While all that may or may not be true, that's what I think about when I hear "Docker". Hearing "containers" on the other hand, makes me happy. And yeah, I'll got with LXC/LXD any day.
- notRobot 6y agoSee also: Linux containers in 500 lines of code (2016) https://news.ycombinator.com/item?id=22232705 https://news.ycombinator.com/item?id=22232705
- p4bl0 6y agoI love this kind of posts, it's never something you will effectively use instead of the actual product (here, Docker), but it's really a great way to learn new little things.
- devxpy 6y agoWhile this is interesting, it doesn't really show how containers actually work, only lists the specific syscall flags to tell Linux create one. A similar snippet[1] exists for go, and it doesn't do anything particularly special either. I don't know, maybe David beazley has altered my sense of what "from scratch" means. [1] https://gist.github.com/lizrice/a5ef4d175fd0cd3491c7e8d716826d27 https://gist.github.com/lizrice/a5ef4d175fd0cd3491c7e8d71682...
- sp332 6y agoThis is how containers work though. Or did you want more detail about the internals of pivot_root or something?
- devxpy 6y agoYes, something like that. Like here's David's "Build Your Own Async" [1], which I prefer over Philip's (still extremely good) "What the heck is the event loop anyway?" [2]. It's one thing to tell how something works, but to successfully show what the hell its actually doing under the covers, just conveys much more information. [1] https://www.youtube.com/watch?v=Y4Gt3Xjd7G8 https://www.youtube.com/watch?v=Y4Gt3Xjd7G8 [2] https://www.youtube.com/watch?v=8aGhZQkoFbQ https://www.youtube.com/watch?v=8aGhZQkoFbQ
- ape4 6y agoAgree, you can't make your own code to isolate from the OS. You need the OS to do that.
- jart 6y agoSoftware history should be written that Docker gave Phil Katz the Oliver Cromwell treatment.
- streb-lo 6y agoAnyone want to chime in on why pivot_root is preferable to a chroot jail? It's kind of hand-waved in the article.
- rantwasp 6y agopivot_root is supposed to switch the whole system to a new root. chroot applies to a process, but the underlying system keeps going with what it had.
- cyphar 6y agoThis is true (though it's scoped per mount namespace), but it isn't the primary security reason container runtimes use pivot_root().
- cyphar 6y agoThis is mostly to do with the implementation of chroot(). Because it only applies to a single process (and mount tables are per mount namespace), it was implemented in such a way that directories above the root of the chroot are still technically accessible (the mounts above the root directory are still present in the mount hierarchy). This results in all sorts of fun bugs where if you chroot() inside a chroot() you can get out of the chroot() entirely. Container runtimes generally block CAP_SYS_CHROOT by default for this reason, but there are all sorts of other subtle security bugs which pop up because of this fundamental design choice. pivot_root() doesn't suffer from this problem because it applies to the entire mount namespace, and thus its implementation could be made much safer. Instead of just changing the current process's filesystem root, the actual / mount of the mount namespace is swapped with another mountpoint on the system (and the old root is mounted elsewhere). Thus once the old root is unmounted there isn't a way to get back to the old mountpoints. This isn't perfect protection (magic-links and other mounts could expose the host filesystem) but it is a damn sight better than chroot(). Oh, and nesting pivot_root()s doesn't cause a breakout. Note that this different behaviour in relation to mounts has resulted in completely unrelated security bugs with containers (such as being able to bypass procfs masks because chroot() doesn't hide the unmasked procfs in the host mount namespace). This is why us container runtime authors always tell people they should never use chroot() and always use pivot_root() -- though sadly sometimes chroot() is needed because pivot_root() doesn't work on initramfs. (I'm one of the maintainers of runc, the runtime which underlies Docker/podman/containerd/cri-o/...)
- mehrdadn 6y agoCould someone comment on how secure such a container is, at least nominally? Should I be able to theoretically run untrusted code on such a container if the system is bug-free and I add proper error-checking to the code? Or are there things that you'd need to worry about the code being able to access? Any considerations regarding sudo permissions?
- lmuench 6y agoDocker has a concept of layered images where only the top-layer is writable. The layer above the "scratch"[1] image usually contain all the files of the base images OS and that's what you set your root directory to. The writable layer disappears when the container is stopped. If you mount your DIY container into /tmp for example, the process running inside your container won't be able to access any OS functionality. You couldn't run a web server in such a container for instance. On the other hand, whatever your containerized process writes into the mounted part of your hard disk won't disappear when the container stops. Because of that, I wouldn't run untrusted code in it. [1] https://hub.docker.com/_/scratch https://hub.docker.com/_/scratch
- rantwasp 6y agothis is the file system not docker itself. you can get the overlay behavior without any docker
- mmastrac 6y agoBig caution here. Do NOT use this style of code to invoke ip tools. This was the cause of a huge number of security vulnerabilities on Android in the first few years. Even if you're hardcoding interfaces to start, it's likely someone else will drive by later on and replace one of the args with %s. > system("ip link add veth0 type veth peer name veth1"); Always, always, always use exec*() APIs.
- alexchamberlain 6y agoAre there not better C APIs to call that `ip` et al are wrapping?
- jeffbee 6y agoLinux containers in one shell statement $ echo $$ > tasks
- mmastrac 6y agoI'm not sure if this is serious or a joke, but can you explain it further?
- jeffbee 6y agoGiven certain initial conditions, this statement moves the current process (and any process it subsequently creates) into a control group, which meets minimal definitions of containerization.
- mmastrac 6y agoAhh. [1] $ mkdir /dev/cpuset $ mount -t cpuset cpuset /dev/cpuset $ cd /dev/cpuset $ mkdir Charlie $ cd Charlie $ /bin/echo 2-3 > cpuset.cpus $ /bin/echo 1 > cpuset.mems $ /bin/echo $$ > tasks # The current shell is now running in cpuset Charlie # The next line should display '/Charlie' $ cat /proc/self/cpuset [1] http://man7.org/linux/man-pages/man7/cpuset.7.html http://man7.org/linux/man-pages/man7/cpuset.7.html
- cyphar 6y agoIt really doesn't. I would say that "unshare -mpf ; pivot_root" matches the most minimal definition of a container more accurately than joining a cgroup (it's an isolated system which can't directly interact with the host). Otherwise you'd have to argue that configuring rlimits actually makes your shell a container, which is too much of a stretch (for me at least).
- jeffbee 6y agoI think that just underscores my point, which is that containment means different things to different people. To me, it means only the resource limiting features from cgroups. I have no use for namespaces, bind mounts, virtual network interfaces, or any of that stuff. In my application all that stuff is either pointless or harmful. But to you, container means at least PID and mount namespaces. rlimit is sort of a thing but it's not actually effective so to me it's not part of the picture. If unix limits worked, Google would not have needed to contribute cgroups before deploying Borg. Indeed, in this LWN article which is actually about control groups, they call control groups "containers". Just shows there is not a universal meaning of the term. The earliest control groups patch I can find says "We use the term container to indicate a structure against which we track and charge utilization of system resources like memory, tasks etc for a workload." It doesn't say anything about isolation, namespaces, or security, but it uses the term container to describe resource control. https://lwn.net/Articles/236038/ https://lwn.net/Articles/236038/
- fwip 6y agoJulia Evans has an excellent zine on how containers work, including a 15-line bash implementation: https://jvns.ca/blog/2020/04/27/new-zine-how-containers-work/ https://jvns.ca/blog/2020/04/27/new-zine-how-containers-work... Definitely worth the $12.
- silentguy 6y agoI loved it. It helped me understand the nitty gritty of containers. You can download it for free too: https://gumroad.com/l/containers-zine/buyonegiveone https://gumroad.com/l/containers-zine/buyonegiveone There is an official option to either pay or get it for free. Do support her if you can.
- brauzi 6y agothanks for sharing!
- bswamina 6y agoA few weeks back had published on how to build a container in go programming ... In case interested here are the links: https://www.polarsparc.com/xhtml/Containers-1.html https://www.polarsparc.com/xhtml/Containers-1.html https://www.polarsparc.com/xhtml/Containers-2.html https://www.polarsparc.com/xhtml/Containers-2.html
- davexunit 6y agoSmall container implementation in Scheme: http://git.savannah.gnu.org/cgit/guix.git/tree/gnu/build/linux-container.scm http://git.savannah.gnu.org/cgit/guix.git/tree/gnu/build/lin...
- accelbred 6y agoOne thing to note, is that using a PID namespace in that way is incorrect. PID1 in a PID namespace has to perform the duties normally performed by a PID1, so you will normally want PID1 in the namespace to be a minimal init. If not, there may be issues, like unreaped zombie processes.
- josteink 6y agoThe title is about Docker containers in a few lines of code. Could the mods consider renaming the submission?