惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 聂微东
IT之家
IT之家
GbyAI
GbyAI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Y
Y Combinator Blog
博客园 - 【当耐特】
The Cloudflare Blog
宝玉的分享
宝玉的分享
罗磊的独立博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
Visual Studio Blog
小众软件
小众软件
博客园_首页
Last Week in AI
Last Week in AI
J
Java Code Geeks
V
V2EX
雷峰网
雷峰网
Apple Machine Learning Research
Apple Machine Learning Research
阮一峰的网络日志
阮一峰的网络日志
腾讯CDC
博客园 - 司徒正美
Engineering at Meta
Engineering at Meta
The GitHub Blog
The GitHub Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
DataBreaches.Net
博客园 - 三生石上(FineUI控件)
MyScale Blog
MyScale Blog
云风的 BLOG
云风的 BLOG
The Register - Security
The Register - Security
M
MIT News - Artificial intelligence
Microsoft Azure Blog
Microsoft Azure Blog
T
The Blog of Author Tim Ferriss
N
Netflix TechBlog - Medium
F
Full Disclosure
B
Blog
H
Help Net Security
C
Check Point Blog
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Jina AI
Jina AI
酷 壳 – CoolShell
酷 壳 – CoolShell
Blog — PlanetScale
Blog — PlanetScale
L
LangChain Blog
P
Proofpoint News Feed
D
Docker
Microsoft Security Blog
Microsoft Security Blog

Ivan on Containers, Kubernetes, and Server-Side

A grounded take on agentic coding for production environments Server-Side Playgrounds Reimagined: Build, Boot, and Network Your Own Virtual Labs [not a] Kubernetes 101 - Pods, Deployments, and Services As an Attempt To Automate Age-Old Infra Patterns JavaScript or TypeScript? How To Benefit From the Dichotomy On Software Design... and Good Writing Building a Firecracker-Powered Course Platform To Learn Docker and Kubernetes How To Publish a Port of a Running Container What Actually Happens When You Publish a Container Port A Visual Guide to SSH Tunnels: Local and Remote Port Forwarding Debugging Containers Like a Pro Docker: How To Debug Distroless And Slim Containers How To Extract Container Image Filesystem Using Docker | iximiuz Labs In Pursuit of Better Container Images: Alpine, Distroless, Apko, Chisel, DockerSlim, oh my! How To Start Programming In Go: Advice For Fellow DevOps Engineers Kubernetes Ephemeral Containers and kubectl debug Command How To Develop Kubernetes CLIs Like a Pro Docker Container Commands Explained: Understand, Don't Memorize | iximiuz Labs Learning Docker with Docker - Toying With DinD For Fun And Profit How To Extend Kubernetes API - Kubernetes vs. Django The Influence of Plumbing on Programming How To Call Kubernetes API from Go - Types and Common Machinery How To Call Kubernetes API using Simple HTTP Client Kubernetes API Basics - Resources, Kinds, and Objects OpenFaaS - Run Containerized Functions On Your Own Terms Learning Containers From The Bottom Up Docker Containers vs. Kubernetes Pods - Taking a Deeper Look | iximiuz Labs Learn-by-Doing Platforms for Dev, DevOps, and SRE Folks How HTTP Keep-Alive can cause TCP race condition How to Work with Container Images Using ctr | iximiuz Labs Multiple Containers, Same Port, no Reverse Proxy... Exploring Go net/http Package - On How Not To Set Socket Options Disposable Local Development Environments with Vagrant, Docker, and Arkade DevOps, SRE, and Platform Engineering My Choice of Programming Languages Prometheus Is Not a TSDB How to learn PromQL with Prometheus Playground Prometheus Cheat Sheet - Basics (Metrics, Labels, Time Series, Scraping) Rust - Writing Parsers With nom Parser Combinator Framework pq - parse and query log files as time series Prometheus Cheat Sheet - Moving Average, Max, Min, etc (Aggregation Over Time) Prometheus Cheat Sheet - How to Join Multiple Metrics (Vector Matching) The Need For Slimmer Containers Understanding Rust Privacy and Visibility Model Bridge vs. Switch: Takeaways from a Real Data Center Tour | iximiuz Labs From LAN to VXLAN: Networking Basics for Non-Network Engineers | iximiuz Labs KiND - How I Wasted a Day Loading Local Docker Images Go, HTTP handlers, panic, and deadlocks Exploring Kubernetes Operator Pattern Making Sense Out Of Cloud Native Buzz Service Discovery in Kubernetes: Combining the Best of Two Worlds API Developers Never REST How Container Networking Works: Building a Bridge Network From Scratch | iximiuz Labs Traefik: canary deployments with weighted load balancing Service Proxy, Pod, Sidecar, oh my! You Need Containers To Build Images Not Every Container Has an Operating System Inside Working with container images in Go Master Go While Learning Containers Implementing Container Runtime Shim: Interactive Containers How to use Flask with gevent (uWSGI and Gunicorn editions) My 10 Years of Programming Experience Implementing Container Runtime Shim: First Code Implementing Container Runtime Shim: runc Kubernetes Repository On Flame Dealing with process termination in Linux (with Rust examples) conman - [the] Container Manager: Inception Journey From Containerization To Orchestration And Beyond Linux PTY - How docker attach and docker exec Commands Work Inside Illustrated introduction to Linux iptables From Docker Container to Bootable Linux Disk Image Пишем свой веб-сервер на Python: протокол HTTP 9001 способ создать веб-сервер на Python Explaining async/await in 200 lines of code Explaining event loop in 100 lines of code Save the day with gevent Пишем свой веб-сервер на Python: процессы, потоки и асинхронный I/O Truly optional scalar types in protobuf3 (with Go examples) Node.js Writable streams distilled Node.js Readable streams distilled How to on starting processes (mostly in Linux) Дайджест интересных ссылок – Июль 2016 Пишем свой веб-сервер на Python: сокеты Наследование в JavaScript Мастерить!
You Don't Need an Image To Run a Container
Ivan Velichko · 2020-05-16 · via Ivan on Containers, Kubernetes, and Server-Side

As we already know, containers are just isolated and restricted Linux processes. We also learned that it's fairly simple to create a container with a single executable file inside starting from scratch image (i.e. without putting a full Linux distribution in there). This time we will go even further and demonstrate that containers don't require images at all. And after that, we will try to justify the actual need for images and their place in the containerverse.

How to Run Containers without Docker and... Images

You may have heard that Docker uses a tool called runc to run containers. Well, to be more accurate, Docker depends on a lower-level piece of software called containerd which in turn relies on a standardized container runtime implementation. And in the wild, most of the time runc plays the role of such a component.

Let's take a closer look at runc. Surprisingly or not, it's a command-line tool for running containers. Following the instructions from its README file, we can come up with the sequence of commands needed to create and then start a container.

Creating container using low-level container runtime - runc.

First, we need to create a container configuration file:

$ mkdir my_container && cd my_container

$ runc spec

$ file config.json
> config.json: ASCII text

The content of config.json on my machine looks as follows:

{
    "ociVersion": "1.0.1-dev",
    "process": {
        "terminal": true,
        "user": {
            "uid": 0,
            "gid": 0
        },
        "args": [
            "sh"
        ],
        "cwd": "/",
        "env": [ ... ],
        "capabilities": { ... },
        "rlimits": [ ... ]
    },
    "root": {
        "path": "rootfs",
        "readonly": true
    },
    "hostname": "runc",
    "mounts": [ ... ],
    "linux": {
        "namespaces": [
            { "type": "pid" },
            { "type": "network" },
            { "type": "ipc" },
            { "type": "uts" },
            { "type": "mount" }
        ]
    }
}

It's not so hard to notice, that this file describes the program to be launched inside of the container (process.args[0]), its environment (env, capabilities, root filesystem, etc), and the container itself (mounts, namespaces, etc).

Now, we need to prepare a root filesystem for our container. To keep it simple, we will just start with an empty folder and put a single (statically-linked) executable in there:

$ cat <<EOF > main.go
package main

import "fmt"
import "os"

func main() {
    fmt.Println(os.Hostname())
}
EOF

$ GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o printme
$ file printme
> printme: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped

$ mkdir rootfs
$ mv printme rootfs/

We also need to modify the config.json accordingly by setting process.args[0] to /printme, disabling terminal support, and changing the hostname to something more pleasing to the eye.

Finally, we can create the container using the adjusted config.json and start it:

$ sudo runc create mycont1
$ sudo runc start mycont1
> HAL-9000 <nil>

That was it! We ran a process inside of a container and from its point of view, the hostname was HAL-9000 while running /usr/bin/hostname on this machine outside of a container gives localhost.localdomain. Pretty simple, right?

Click here to see how to run a container with a full Linux distro inside.

The procedure is exactly the same as above. The only difference is that we need to have a root filesystem of the desired Linux distribution beforehand.

$ mkdir my_container2 && cd my_container2
$ wget https://github.com/alpinelinux/docker-alpine/raw/c5510d5b1d2546d133f7b0938690c3c1e2cd9549/x86_64/alpine-minirootfs-3.11.6-x86_64.tar.gz

$ mkdir rootfs
$ tar -C rootfs -xvf alpine-minirootfs-3.11.6-x86_64.tar.gz

$ runc spec
$ sudo runc run mycont2  # `run` is a combination of `create` and `start`
/# hostname
> runc

Well, I hope you noticed, that we haven't used any image-related facilities so far. That's because we don't really need images to create and/or run containers with runc or any other OCI-compliant runtime. And just to make it clear, when I say images here I mean these layered beasts we build using Dockerfiles, push and pull from the registries like Docker Hub, and base other images on.

Reflecting on the exercise from above, we can say that runc needs just a regular filesystem directory with at least one executable file inside and a config.json. This combination is called a bundle.

The runtime doesn't know anything about the storage driver used to mount this folder, it's not even aware of its potential layered structure, thanks to union mount abstracting it away. Of course, it has nothing to do with the image building procedure and all those Dockerfile directives. The content of a bundle is also out of scope for the runtime, i.e. you can put a full Linux distro in there if you will, or keep it as lightweight as a single statically linked executable.

Any compliant container runtime should adhere to the OCI Runtime Specification and surprisingly or not the specification never mentions images! Even though the OCI Image Format specification is its sibling project. It's all about the strict separation of concerns.

Docker (or any other container engine like containerd, or podman) takes an image and converts it to an OCI bundle before invoking the lower-level container runtime like runc. I.e. it's an engine who is supposed to unpack an image, extract environment variables, exposed ports, etc. that were set on the building stage; prepare the config.json file using these details; mount filesystem layers from the image, and finally pass it over to the OCI runtime in a form of a bundle.

So, why do we need all this hassle? Wouldn't it be much simpler if we just used bundles in the first place? Apparently, this would simplify the containers' creation and starting logic. This could also positively affect the performance of the container runtime by offloading the overhead of the union mounts from the filesystem I/O.

Layered image structure combined with an overlay mount actually makes your container runtime slower.

— Ivan Velichko (@iximiuz) May 10, 2020

Why Do You Need Container Images

The actual reasons why we have images so widespread come from the image building and storage sides. The layered image structure and immutability of the layers are the key contributors to the extremely powerful building and storage techniques that made Docker so popular back in the days.

Imagine for a second, that we use raw bundles to store and distribute containers. In that case, if you needed to create two new custom bundles both based on the base ubuntu bundle, you'd have to copy over all the files from the ubuntu bundle into two new folders. And if you needed to create a third- or fourth-level bundles, you'd need to apply the same procedure over and over again multiplying the number of duplicate files on your disk. Moving all these files around would also add a significant build time overhead.

Of course, we need to somehow avoid this inefficiency and the layered storage seems like a great solution. If we pack the ubuntu bundle in a tar archive and compute its checksum (sha256), we will always be able to refer to it later on using content-addressable techniques. I.e. every new image derived from the base ubuntu image will be referring only the hash of the uniquely presented base image and will not include its actual content.

Images layers form a directed acyclic graph.

Images form a directed acyclic graph.

The layered image structure solves the problem of the efficient building (by eradicating the need for copying files, hence decreasing the build time) and storage (by eradicating file duplication, hence decreasing the space requirements). But what if we want to run hundreds of instances of exactly the same container on one Linux host? We may need to have hundreds of copies of the same bundle and this may occupy the full disk. Union mount to the rescue! Thanks to the immutability of the layers, we need to unpack the image only once and then mount the underlying layers as many times as we need. The only mutable layer will be the topmost (empty) layer corresponding to the bundle folder of every container.

Obviously, the pros of having images outweighed the negative impact of the layered mounts and the need for a slightly more complex container management procedures.

Wrapping up

So, the moral may have sounded something like that: while the images aren't really needed to run containers, they have made the usage of the containers simple and handy and it contributed a lot to the containers' popularity.