為了提升k3s的使用體驗(yàn),我們將推出由k3s開發(fā)人員撰寫的“k3s黑魔法”系列文章來詳細(xì)介紹k3s功能、原理等方面的內(nèi)容。本篇文章是該系列的第一篇,文章詳細(xì)分析了k3s的單進(jìn)程模式如何運(yùn)行整個Kubernetes服務(wù)。
同時,歡迎大家添加k3s助手(微信號:k3s2019),加入官方微信群和大家一起交流。
前 言
Rancher Labs一直致力于云基礎(chǔ)設(shè)施的建設(shè),我們發(fā)布了很多產(chǎn)品Rancher1.x、Rancher2.x、RancherOS、Longhorn、Rio等來滿足基礎(chǔ)設(shè)施應(yīng)用的各種場景,但這其中沒有一款產(chǎn)品可以和k3s的發(fā)展速度相比,整個社區(qū)對它的認(rèn)可超乎我們的想象。發(fā)布了僅僅10個月的k3s項(xiàng)目,就在Github上獲得超9000顆star數(shù),我們也正星夜兼程,爭取在11月份發(fā)布1.0GA版本。我將撰寫一系列文章來介紹k3s所使用的技術(shù)及其原理,尤其是這其中使用的一些黑魔法,它讓k3s的體驗(yàn)變得無比美好。更深入更細(xì)節(jié)得了解k3s,才能將它使用好,讓工具本身產(chǎn)生事半功倍的效用,同時也能讓大家有機(jī)會一起參與k3s社區(qū)的建設(shè)。
很多人在體驗(yàn)k3s時,都對它的無比精簡感到折服。我們都了解Kubernetes(以下簡稱K8s)是個非常復(fù)雜的架構(gòu),controlplane中就包括apiserver/controller-manager/scheduler等,worker中還需要有kubelet/kube-proxy,元數(shù)據(jù)還需要存儲在etcd上。這些服務(wù)每一項(xiàng)都需要單獨(dú)部署,還需要進(jìn)行配置聯(lián)動,盡管我們可以借用很多開源工具(比如RKE),但是部署前你還是需要準(zhǔn)備大量鏡像或者二進(jìn)制文件等。k3s的部署就非常簡便,它通過一個binary就可以部署上面提到的大部分服務(wù),這也就是本文要介紹的內(nèi)容,k3s的黑魔法之一“單進(jìn)程k8s”。
單進(jìn)程k8s分析
我們先不管具體如何實(shí)現(xiàn),先來看一下單進(jìn)程k8s的表面現(xiàn)象。我們安裝一個k3s,但禁用agent,這樣更有利于觀察結(jié)果:
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable-agent" sh -
這時使用kubectl是看不到node信息,但是我們能夠獲取namespace信息,這就說明k8s的controlplane相關(guān)服務(wù)已經(jīng)啟動了:
# kubectl get no
No resources found in default namespace.
# kubectl get ns
NAME STATUS AGE
default Active 10s
kube-system Active 10s
kube-public Active 10s
kube-node-lease Active 10s
使用ps命令觀察k3s程序運(yùn)行結(jié)果,下方可以看到k3s只啟動了一個進(jìn)程:
# ps aux | grep k3s
root 1900 2.8 40.8 564468 411436 ? Ssl 12:01 0:21 /usr/local/bin/k3s server --disable-agent
那么controlplane其他服務(wù)apiserver/scheduler/controller-manager等是如何啟動的,我們查看k3s對應(yīng)的thread:
# ps -T 1900
PID SPID TTY STAT TIME COMMAND
1900 1900 ? Ssl 0:01 /usr/local/bin/k3s server --disable-agent
1900 1910 ? Ssl 0:06 /usr/local/bin/k3s server --disable-agent
1900 1911 ? Ssl 0:01 /usr/local/bin/k3s server --disable-agent
1900 1912 ? Ssl 0:00 /usr/local/bin/k3s server --disable-agent
1900 1916 ? Ssl 0:00 /usr/local/bin/k3s server --disable-agent
1900 1917 ? Ssl 0:06 /usr/local/bin/k3s server --disable-agent
1900 1918 ? Ssl 0:10 /usr/local/bin/k3s server --disable-agent
1900 1948 ? Ssl 0:06 /usr/local/bin/k3s server --disable-agent
1900 1957 ? Ssl 0:00 /usr/local/bin/k3s server --disable-agent
我們知道k3s是純粹Golang實(shí)現(xiàn)的,而Golang通常并不會直接使用thread,一般是通過goroutine來使用系統(tǒng)的thread,分析源碼中k3s server的實(shí)現(xiàn),可以看到api-server/scheduler等服務(wù)確實(shí)是goroutine來啟動的:
# https://github.com/rancher/k3s/blob/master/pkg/daemons/control/server.go
---------
go func() {
logrus.Infof("Running kube-scheduler %s", config.ArgString(args))
logrus.Fatalf("scheduler exited: %v", command.Execute())
}()
---------
---------
go func() {
logrus.Infof("Running kube-apiserver %s", config.ArgString(args))
logrus.Fatalf("apiserver exited: %v", command.Execute())
}()
startupConfig := <-app.StartupConfig
return startupConfig.Authenticator, startupConfig.Handler, nil
---------
以kube-apiserver為例,在k3s server的thread中執(zhí)行它,還需另外兩項(xiàng)工作:
在k3s中引入kube-apiserver代碼,并將其編譯到k3s binary中,這部分在源碼中的go.mod中有所體現(xiàn)
由于是在thread中執(zhí)行,所以k3s不能執(zhí)行apiserver的main函數(shù),這部分在上面提到server.go源碼中也有體現(xiàn)
k3s server的goroutine除了剛才提到的controlplane相關(guān)服務(wù)外,還包括默認(rèn)內(nèi)置運(yùn)行的flannel/ingress controller,更有一些k3s擴(kuò)展的一些高級controller,這部分我們會以單獨(dú)的文章進(jìn)行分析。
以上內(nèi)容,我們只是在單獨(dú)的k3s server層面分析,一旦我們加入一個節(jié)點(diǎn)作為worker,那么worker節(jié)點(diǎn)上會怎樣的展現(xiàn)?
curl -sfL https://get.k3s.io | K3S_URL=https://myserver:6443 K3S_TOKEN=XXX sh -
我們依然按照上面的思路,先看一下worker節(jié)點(diǎn)上k3s相關(guān)的進(jìn)程和線程情況:
# ps aux | grep k3s
root 1949 0.8 12.2 220060 123648 ? Ssl 13:12 1:03 /usr/local/bin/k3s agent
root 1967 0.3 13.0 220932 131340 ? Sl 13:13 0:23 containerd -c /var/lib/rancher/k3s/agent/etc/containerd/config.toml -a /run/k3s/containerd/containerd.sock --state /run/k3s/containerd --root /var/lib/rancher/k3s/agent/containerd
# ps -T 1949
PID SPID TTY STAT TIME COMMAND
1949 1949 ? Ssl 0:01 /usr/local/bin/k3s agent
1949 1954 ? Ssl 0:06 /usr/local/bin/k3s agent
1949 1955 ? Ssl 0:00 /usr/local/bin/k3s agent
1949 1956 ? Ssl 0:00 /usr/local/bin/k3s agent
1949 1960 ? Ssl 0:00 /usr/local/bin/k3s agent
1949 1961 ? Ssl 0:14 /usr/local/bin/k3s agent
...
worker節(jié)點(diǎn)的kubelet和kube-proxy的運(yùn)行方式與k3s server上運(yùn)行api-server/scheduler等服務(wù)的方式是一樣的,也包括agent上flannel和tunnel proxy等服務(wù),都是通過goroutine調(diào)用,并在操作系統(tǒng)上以thread方式運(yùn)行。而worker節(jié)點(diǎn)中,有一個特立獨(dú)行的存在就是containerd(如果你還是喜歡使用docker,請忽略以下內(nèi)容),containerd是作為一個k3s agent的子進(jìn)程來運(yùn)行。
containerd因?yàn)橛衅涮厥庑?,它會為每個容器創(chuàng)建單獨(dú)的containerd-shim進(jìn)程為容器提供運(yùn)行時支持,正因?yàn)檫@樣containerd本身必須是進(jìn)程級別的,它可以擁有獨(dú)立的上下文,進(jìn)而提供容器管理能力。較新版本的k3s,已經(jīng)使用了containerd-shim-runc-v2來運(yùn)行容器,這種模式對k8s的Pod更加友好,早期containerd-shim v1版本,pod的pause容器需要單獨(dú)運(yùn)行一個containerd-shim進(jìn)程,v2版本可以把Pod內(nèi)的容器都放在一個containerd-shim進(jìn)程下運(yùn)行,Pod內(nèi)每個容器會成為這個containerd-shim的子進(jìn)程。比如coredns Pod對應(yīng)的containerd-shim進(jìn)程Pid是2325,那么它的兩個子進(jìn)程分別是coredns本身和pause容器服務(wù):
# pstree -p -aT 2325
containerd-shim,2325 -namespace k8s.io -id 5aad2ea4d09f997baab6a0343dfb10abd86971601bae29200e39cffb5709b938-a
├─coredns,2598 -conf /etc/coredns/Corefile
└─pause,2392
后 記
本文向大家分析了k3s這種單進(jìn)程模式如何運(yùn)行整個k8s服務(wù),相當(dāng)于我們對k3s的原理有了一個基本了解。然而,k3s仍然有很多未解之謎,agent和server如何通信組建集群?k3s內(nèi)置的rootfs起到什么作用?k3s內(nèi)置的CLI工具如何使用?k3s如何實(shí)現(xiàn)使用sqllite/mysql來代替etcd等等,這些問題我們會在后續(xù)文章中一一解答。