2017年4月26日 星期三

[超譯] LinuxKit 為容器而生的操作系統

超譯

https://gianarb.it/blog/linuxkit-operating-system-build-for-containers

超譯目的:
linuxkit 是什麼?從這篇來了解 linuxkit 是什麼。

內容:

Linuxkit 是 Docker 在 DockerCon 2017 呈現的的新專案。在 GitHub 的專案描述為:

一個安全,可攜、玲瓏的操作系統,為了容器而打造。

我已非常興奮,在 Justin Cormack 與其他貢獻者在其為私人庫的時候,我已經在觀察這專案。我也被邀請以 ci-wg 團隊的角色進入 CNCF,從第一天開始我就喜愛這專案。

你可想像,linuxkit 是個建立者,建立基於容器的 Linux 操作系統的所有東西。

這專案可以留在持續整合系統(CI system),讓我們可測試不同的核心版本與散佈。你可用小核心帶著你所需的所有服務,你可以利用 Docker 或 QEMU 建立不同的可執行服務在雲端上,例如谷歌雲平台。

持續交付,新模式

我對谷歌雲平台沒很大信心,我想用 AWS 為服務供應者來做些數學。假設我已經有最一般的持續整合系統,一個大盒子持續執行已設定好的工作,支援你所有的專案。或是你已經有可用的容器,擁有獨立分開的環境。

假設你的 jenkins 已經準備好在 m3.xlarge:

m3.xlarge 每月吃到吐會花掉 $194.72。

來做夢一下,你有一個非常小的伺服器,它只是個前端程式,讓你的 CI 與所有工作執行在不同的實體上,如同 t2.small 這麼小。

t2.small 使用一個小時要付 $0.72。

我算一個小時是因為付錢的最小單位為小時,我希望你的 CI job 可以跑低於一小時。簡單算一下要付多少錢,就像你以前付錢一樣。

194.72 / 0.72 ~ 270 大約是每月 270 個 build。

如果你一個月跑少於 270 個 build 的話,你可以省下一些錢。但你可以有其他的益處:

  1. 更多的 job,更多的實體。非常容易擴展。對 Jenkins master/slave 等更容易些。
  2. 有多少時間是假日,你的 Jenkins 仍然開啟但沒事可做?這些日子你仍然要為那前端付錢。

你的持續交付的不同設計,會有這些不同的益處。

LinuxKit CI 實作

這裡有個目錄叫 ./test 它包含一些 linuxkit 的使用例。我會解釋實務上 linuxkit 如何被測試。因為它自己用自己,超酷的!

首先你需要下載並編譯 linuxkit:

git clone github.com:linuxkit/linuxkit $GOPATH/src/github.com/linuxkit/linuxkit
make
./bin/moby

你可以把它搬到你的 $PATH (使用 make install)。

$ moby
Please specify a command.

USAGE: moby [options] COMMAND

Commands:
  build       Build a Moby image from a YAML file
  run         Run a Moby image on a local hypervisor or remote cloud
  version     Print version information
  help        Print this message

Run 'moby COMMAND --help' for more information on the command

Options:
  -q    Quiet execution
  -v    Verbose execution

在這時間點,CLI 還是很簡單,最重要的指令是建立與執行。linuxkit 是建基於 YAML 檔,它可以描述你的核心,需要什麼應用與服務。我們從 linuxkit/test/test.yml 開始。

kernel:
  image: "mobylinux/kernel:4.9.x"
  cmdline: "console=ttyS0"
init:
  - mobylinux/init:8375addb923b8b88b2209740309c92aa5f2a4f9d
  - mobylinux/runc:b0fb122e10dbb7e4e45115177a61a3f8d68c19a9
  - mobylinux/containerd:18eaf72f3f4f9a9f29ca1951f66df701f873060b
  - mobylinux/ca-certificates:eabc5a6e59f05aa91529d80e9a595b85b046f935
onboot:
  - name: dhcpcd
    image: "mobylinux/dhcpcd:0d4012269cb142972fed8542fbdc3ff5a7b695cd"
    binds:
     - /var:/var
     - /tmp:/etc
    capabilities:
     - CAP_NET_ADMIN
     - CAP_NET_BIND_SERVICE
     - CAP_NET_RAW
    net: host
    command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
  - name: check
    image: "mobylinux/check:c9e41ab96b3ea6a3ced97634751e20d12a5bf52f"
    pid: host
    capabilities:
     - CAP_SYS_BOOT
    readonly: true
outputs:
  - format: kernel+initrd
  - format: iso-bios
  - format: iso-efi
  - format: gcp-img

linuxkit 會建立所有的東西在一個容器裡面,這表示你不需要一堆相依,它非常容易使用。它產生不同的 output,在這例子有 kernel+initrdiso-biosiso-efigcp-img,它相依於你想要執行的核心的平台。

我多解釋一下這 YAML 如何工作的。你可以看到有不同的主要區段:kernelinitonbootservice與其他。

幾乎每個都有個關鍵字 image,就如我之前所說,因為每個東西都用在容器上,在這例子都存在 hub.docker.com/u/mobylinux/ 裡。

基礎的核心是 mobylinux/kernel:4.9.xREADME.md說:

  • kernel 指定 Docker image 的核心,包含核心與檔案系統的 tarball,eg 包含 modules。這例子的核心是從 kernel/ 所建立。
  • init 是 Docker image 基礎的 init 行程,這是由基礎系統所取出來的,包含 initcontainerdrunc與一些其他工具。其由 pkg/init/ 所建立。
  • onboot 是系統容器,按順序執行,他們應該做完事就馬上結束。
  • services 是系統服務,它們會在系統開啟之後一直執行。
  • files 是額外加入 image 的檔案。
  • outputs 是描述哪些要建立的東西,像是 ISO 之類的。

到此,我們可以試,如果你在 MacOS 上,你不需要安裝任何東西,只要一個被 linuxkit 支援的 hyperkit 就可以了。

./test 包含不同的測試,但現在我們先專注在 ./test/check 的目錄。這包含一組檢定,確認這核心怎麼被 LinuxKit 建立。舉例來說,它們是 smoke test,在每次新的 pull request 被建在庫時被執行。

就如我之前說,每個東西都在容器內執行,如果你看 check 目錄,那裡有個 makefilie,它會建立一個 mobylinux/check image,在 test.yml 裡面是這樣:

onboot:
  - name: check
    image: "mobylinux/check:c9e41ab96b3ea6a3ced97634751e20d12a5bf52f"
    pid: host
    capabilities:
     - CAP_SYS_BOOT
    readonly: true

你可以用這 check 目錄的 Makefile 來建立一個新版的 check,只要使用這指令 make

當你有正確版本的測試,我們可以用 moby 來建立 image:

cd $GOPATH/src/github.com/linuxkit/linuxkit
moby build test/test.yml

它的部份輸出:

Create outputs:
  test-bzImage test-initrd.img test-cmdline
  test.iso
  test-efi.iso
  test.img.tar.gz

這目錄裡面,你會看到有許多根目錄下所有的檔案,這些檔案可用 qemu 執行,可用谷歌雲平台執行,可用 hyperkit 執行…等。

moby run test

在 MacOS 用 LinuxKit 這指令是使用 hyperkit 去啟動一個 VM,我無法把所有的輸出都貼出來,但你可以看 hypervisor 的記錄:

virtio-net-vpnkit: initialising, opts="path=/Users/gianlucaarbezzano/Library/Containers/com.docker.docker/Data/s50"
virtio-net-vpnkit: magic=VMN3T version=1 commit=0123456789012345678901234567890123456789
Connection established with MAC=02:50:00:00:00:04 and MTU 1500
early console in extract_kernel
input_data: 0x0000000001f2c3b4
input_len: 0x000000000067b1e5
output: 0x0000000001000000
output_len: 0x0000000001595280
kernel_total_size: 0x000000000118a000
booted via startup_32()
Physical KASLR using RDRAND RDTSC...
Virtual KASLR using RDRAND RDTSC...

Decompressing Linux... Parsing ELF... Performing relocations... done.
Booting the kernel.
[    0.000000] Linux version 4.9.21-moby (root@84baa8e89c00) (gcc version 6.2.1 20160822 (Alpine 6.2.1) ) #1 SMP Sun Apr 9 22:21:32 UTC 2017
[    0.000000] Command line: earlyprintk=serial console=ttyS0
[    0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
[    0.000000] x86/fpu: xstate_offset[2]:  576, xstate_sizes[2]:  256
[    0.000000] x86/fpu: Enabled xstate features 0x7, context size is 832 bytes, using 'standard' format.
[    0.000000] x86/fpu: Using 'eager' FPU context switches.
[    0.000000] e820: BIOS-provided physical RAM map:
[    0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
[    0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000003fffffff] usable

當 VM 準備好,LinuxKit 會啟動所有的 initonboot,這些記錄非常好懂,像 test.yml 啟動 containerdrunc

init:
  - mobylinux/init:8375addb923b8b88b2209740309c92aa5f2a4f9d
  - mobylinux/runc:b0fb122e10dbb7e4e45115177a61a3f8d68c19a9
  - mobylinux/containerd:18eaf72f3f4f9a9f29ca1951f66df701f873060b
  - mobylinux/ca-certificates:eabc5a6e59f05aa91529d80e9a595b85b046f935
onboot:
  - name: dhcpcd
    image: "mobylinux/dhcpcd:0d4012269cb142972fed8542fbdc3ff5a7b695cd"
    binds:
     - /var:/var
     - /tmp:/etc
    capabilities:
     - CAP_NET_ADMIN
     - CAP_NET_BIND_SERVICE
     - CAP_NET_RAW
    net: host
    command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
  - name: check
    image: "mobylinux/check:c9e41ab96b3ea6a3ced97634751e20d12a5bf52f"
    pid: host
    capabilities:
     - CAP_SYS_BOOT
    readonly: true
Welcome to LinuxKit

            ##         .
          ## ## ##        ==
           ## ## ## ## ##    ===
       /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
       \______ o           __/
         \    \         __/
          \____\_______/


/ # INFO[0000] starting containerd boot...                   module=containerd
INFO[0000] starting debug API...                         debug="/run/containerd/debug.sock" module=containerd
INFO[0000] loading monitor plugin "cgroups"...           module=containerd
INFO[0000] loading runtime plugin "linux"...             module=containerd
INFO[0000] loading snapshot plugin "snapshot-overlay"...  module=containerd
INFO[0000] loading grpc service plugin "healthcheck-grpc"...  module=containerd
INFO[0000] loading grpc service plugin "images-grpc"...  module=containerd
INFO[0000] loading grpc service plugin "metrics-grpc"...  module=containerd

最後一步是 check,它執行真正的測試套裝:

kernel config test succeeded!
info: reading kernel config from /proc/config.gz ...

Generally Necessary:
- cgroup hierarchy: properly mounted [/sys/fs/cgroup]
- CONFIG_NAMESPACES: enabled
- CONFIG_NET_NS: enabled
- CONFIG_PID_NS: enabled
- CONFIG_IPC_NS: enabled
- CONFIG_UTS_NS: enabled
- CONFIG_CGROUPS: enabled
- CONFIG_CGROUP_CPUACCT: enabled
- CONFIG_CGROUP_DEVICE: enabled
- CONFIG_CGROUP_FREEZER: enabled
- CONFIG_CGROUP_SCHED: enabled

........
.......

Moby test suite PASSED

            ##         .
          ## ## ##        ==
           ## ## ## ## ##    ===
       /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
       \______ o           __/
         \    \         __/
          \____\_______/

[    3.578681] ACPI: Preparing to enter system sleep state S5
[    3.579063] reboot: Power down

最後的記錄是 check-kernel-config.sh 的產出。

如果你使用 linux,你可以用相同的指令,但預設你是使用 qemu,它是一個開源機器模擬器。

sudo apt-get install qemu

我的 Asus Zenbook 是裝 Ubuntu,我在它上面測試,當我執行 moby run,它會用 qemu 來執行其指令:

/usr/bin/qemu-system-x86_64 -device virtio-rng-pci -smp 1 -m 1024 -enable-kvm
    -machine q35,accel=kvm:tcg -kernel test-bzImage -initrd test-initrd.img -append
    console=ttyS0 -nographic

預設是在 x86_64 上測試,但 qemu 支援其他的架構與裝置。與例來說,你可以模擬一個 arm 與樹莓派。在這時點,LinuxKit 還沒準備好支援其他架構。但這是此專案的主要範圍。這只是時間問題,應該很快!

偵測一個 build 是否成功可能不如你所想的簡單。這 VM 內的狀態不是你從筆電內取得的那一個。在這時候要了解你的 PR 內的程式碼好或不好,我們是這樣解析輸出的:

define check_test_log
    @cat $1 |grep -q 'Moby test suite PASSED'
endef

./linuxkit/Makefile

現在解釋 linuxkit 自我測試是如何工作的,正是解釋它本身如何工作的最好方法。這正好是拼圖的一片,如果你看過 every pr 它有個 GitHub 狀態,裡面指到一個網站,它有一些特殊 build 的記錄。那些不在 linuxkit 的管理之內,因為它只是一個 builder 用來建立環境的。其餘是被 datakit 所管。我會在另一個貼文談到。

結論

runc、docker、containerd、rkt,還有 Prometheus、InfluxDB、Telegraf 很多專案支援不同的架構,它們需要用不同的設定與能力在不同的核心上執行。它們需要在你的筆電上跑,在你的 IBM 伺服器跑,在樹莓派上跑。

這專案還在早期階段,但我了解為何 Docker 需要類似的東西,如我所言,其他專案需要這種東西以獲得一些益處。讓它開源,是非常好的,而且我很榮幸成為此驚艷專案的其中一員。我做了一些最終測試並嘗試了解它如何設計與工作。這是我測試的結果。我希望這篇文章能對給予正確觀念上幫上一些忙。

我計畫是建立一組設定來測試 InfluxDB 且用 qemu 讓它在不同架構與裝置測試。敬請期待新的貼文。

一些連結:

審閱者: Justin Cormack

沒有留言:

張貼留言