Containers from First Principles

栏目: IT技术 · 发布时间: 4年前

内容简介:You’ve likely heard everyone at the office or online proclaim that “K8s has eaten everyone’s lunch!” or that “everything should be in a docker container!”.While there are advantages to the above methodologies; it’s very easy to have cargo-culted their adop

You’ve likely heard everyone at the office or online proclaim that “K8s has eaten everyone’s lunch!” or that “everything should be in a docker container!”.

While there are advantages to the above methodologies; it’s very easy to have cargo-culted their adoption; especially for Kubernetes (K8s). I find the biggest problem however that there is a fundamental lacking of what is a *container .

There s a 1000 other posts online explaining containers and I’m adding my own to the pool. Perhaps this 1001th explanation will do so in such a way that it groks.

Any commands written were done in a Linux environment; if you try to follow along in OSX; it may be more challenging since Docker is running within a hypervisor or virtual machine.

What’s a Docker Image ?

Let’s start by first grabbing one of the simplest Docker images around, Alpine Linux .

Alpine Linux is a Linux distribution based on musl and BusyBox, designed for security, simplicity, and resource efficiency.

Let’s grab the image & save it

# download the image locally
docker pull alpine:3.12.0
3.12.0: Pulling from library/alpine
Digest: sha256:185518070891758909c9f839cf4ca393ee977ac378609f700f60a771a2dfe321
Status: Downloaded newer image for alpine:3.12.0

# save the image
docker save alpine:3.12.0 > alpine-3.12.0.tar

If we inspect the image; we can see a few interesting files

tar --list --file=alpine-3.12.0.tar
a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e.json
fa27ccac46e9a5b7ff1d188e99763db1a5fb45adb34a917c1c482dfde99ad432/
fa27ccac46e9a5b7ff1d188e99763db1a5fb45adb34a917c1c482dfde99ad432/VERSION
fa27ccac46e9a5b7ff1d188e99763db1a5fb45adb34a917c1c482dfde99ad432/json
fa27ccac46e9a5b7ff1d188e99763db1a5fb45adb34a917c1c482dfde99ad432/layer.tar
manifest.json
repositories

manifest.json : Describes Container properties

…faec1b8045e42.json : The SHA256 of the image.

…4395f806b63d83e/ : One of the “layers” that comprise the image

In fact, when we see what’s inside that layer.tar ; it’s a full Linux filesystem layout.

tar -xOf alpine-3.12.0.tar fa27ccac46e9a5b7ff1d188e99763db1a5fb45adb34a917c1c482dfde99ad432/layer.tar | tar -tf - | shuf | head
var/lib/apk/
usr/bin/getent
bin/lzop
usr/bin/openvt
dev/
bin/tar
sbin/rmmod
usr/lib/libtls-standalone.so.1.0.0
usr/bin/[[
sbin/ifconfig

Feel free to dig into the concept of an overlay filesystem ; however at a minimum these layers represent individual steps made during the Dockerfile .

If an image that more than 1 image; they contents are superimposed to create the final filesystem view.

What’s a container?

Many people might think the word “container” has a specific meaning within the Linux kernel; however the kernel has no notion of a “container”. The word has been synonymous with a variety of Linux tooling which when applied give the resemblance of what we expect a container to be.

To be put it simply; a container should resemble somewhat as a separate machine (or virtual machine) although many of them run with a single kernel.

Each container should have at least the following isolated:

  1. network stack
  2. filesystem
  3. processes

Linux achieves these isolations via, Namespaces . Let’s rebuild together the isolation using simple bash commands.

Virtual Machine Setup

The following commands were executed on a Debian machine running in GCP

gcloud compute instances create containers-demystified --image-family debian-10 --image-project debian-cloud --zone us-west1-a
gcloud compute ssh containers-demystified

# kernel version
uname -r
4.19.0-9-cloud-amd64

# install docker
sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian buster stable"
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
sudo groupadd docker
sudo usermod -aG docker $USER

Containers from scratch

First lets grab the Alpine Linux filesystem and place it in a container/ directory.

mkdir container
tar -xOf alpine-3.12.0.tar fa27ccac46e9a5b7ff1d188e99763db1a5fb45adb34a917c1c482dfde99ad432/layer.tar | tar -C container/ -xf -

ls container/ | head
bin
dev
etc
home
root
tmp
usr
var

Great! Now let’s try creating a new mount namespace using unshare

unshare - run program with some namespaces unshared from parent
sudo unshare --fork --mount bash

# we need to bind mount itself --
# this is just a pre-requisite for pivot_root which is the next step
mount --bind container/ container/
# create a location to store the pivot of the filesystem
pivot_root container/ container/_old
# check that we have pivoted
ls -l /_old/ | head
total 128
drwxr-xr-x    1 root     root          4096 May 28 01:21 bin
drwxr-xr-x    2 root     root          4096 Feb  1 17:09 boot
drwxr-xr-x   10 root     root          1400 Jun  1 00:31 dev
drwxr-xr-x    1 root     root          4096 Jun  1 00:31 etc
drwxr-xr-x    1 root     root          4096 May 28 00:11 google
drwxr-xr-x    4 root     root          4096 Jun  1 00:31 home

# great now un mount it; for security reasons so the process can't escape the jail
cd /
umount -l /_old
pivot_root moves the root file system of the calling process to the directory of the second argument and makes first argument the new root file system of the calling process.

Great! It looks like we are in a separate filesystem environment. Let’s re-add the proc filesystem

mount -t proc /proc /proc

ps aux  | head
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/bash /google/scripts/onrun.sh sleep infinity
    8 root      0:00 /usr/sbin/rsyslogd
  573 root      0:00 /usr/sbin/sshd -p 22 -o AuthorizedKeysFile=/etc/ssh/keys/authorized_keys
  647 root      0:20 /usr/bin/dockerd -p /var/run/docker.pid --mtu=1460 --registry-mirror=https://us-mirror.gcr.io
  675 root      0:16 containerd --config /var/run/docker/containerd/containerd.toml --log-level info

Hmmmm.. that’s not isolated from the outside. Let’s fix it!

# add a `--pid` now
sudo unshare --fork --pid --mount bash
# do all the same commands as above

mount --bind container/ container/
pivot_root container/ container/_old
umount -l /_old

mount -t proc /proc /proc

ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 bash
  104 root      0:00 ps aux

Much better! Let’s checkout the network configuration

ip link list

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
    link/ether 02:42:c2:8a:54:22 brd ff:ff:ff:ff:ff:ff
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff

Looks like we still have access to the physical ethernet device; that’s not isolated… let’s fix that!

# we run the following command in the background
sudo unshare --fork --pid --mount --uts --net bash

# in a separate window (host namespace)
# grab the PID of unshare
CPID=$(pidof unshare)
# This will use the PID of the process to pick the correct netns
sudo ip link add h$CPID type veth peer name c$CPID netns $CPID
# turn on the device & attach it to the docker0 bridge
sudo ip link set h$CPID up master docker0


# in the original window (new namespace)
# filesystem
mount --bind container/ container/
pivot_root container/ container/_old
umount -l /_old
mount -t proc /proc /proc

# network
# let's change hostname
hostname container
# bring up the loopback address
ip link set lo up
# bring the veth device up
ip link set c$CPID up
# lets give the veth a IP address
ip addr add 172.17.42.3/16 dev c$CPID
# set the veth as the default gateway
ip route add default via 172.17.0.1

# This should work!
# note: DNS don't work due to /etc/resolv.conf being missing
ping 8.8.8.8

The above kind of cheats by using the Docker bridge setup. This was done because setting up a bridge on a cloud instance is non-trivial; moving the physical ethernet device temporarily makes the instance unroutable. Docker uses a myriad of iptables rules to setup a NAT.


以上所述就是小编给大家介绍的《Containers from First Principles》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

你也能看得懂的Python算法书

你也能看得懂的Python算法书

王硕,董文馨,张舒行,张洁 著 / 电子工业出版社 / 2018-11-1 / 59.00

编程的核心是算法,学习算法不仅能教会你解决问题的方法,而且还能为你今后的发展提供一种可能。 《你也能看得懂的Python算法书》面向算法初学者,首先介绍当下流程的编程语言Python,详细讲解Python语言中的变量和循序、分支、循环三大结构,以及列表和函数的使用,为之后学习算法打好基础。然后以通俗易懂的语言讲解双指针、哈希、深度优先、广度优先、回溯、贪心、动态规划和至短路径等经典算法。 ......一起来看看 《你也能看得懂的Python算法书》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具