30

k3s的单进程模式如何运行整个K8S服务

 4 years ago
source link: http://dockone.io/article/9392
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

为了提升k3s的使用体验,我们将推出由k3s开发人员撰写的“k3s黑魔法”系列文章来详细介绍k3s功能、原理等方面的内容。本篇文章是该系列的第一篇,文章详细分析了k3s的单进程模式如何运行整个Kubernetes服务。

前 言

Rancher Labs一直致力于云基础设施的建设,我们发布了很多产品Rancher1.x、Rancher2.x、RancherOS、Longhorn、Rio等来满足基础设施应用的各种场景,但这其中没有一款产品可以和k3s的发展速度相比,整个社区对它的认可超乎我们的想象。发布了仅仅10个月的k3s项目,就在Github上获得超9000颗star数,我们也正星夜兼程,争取在11月份发布1.0GA版本。我将撰写一系列文章来介绍k3s所使用的技术及其原理,尤其是这其中使用的一些黑魔法,它让k3s的体验变得无比美好。更深入更细节得了解k3s,才能将它使用好,让工具本身产生事半功倍的效用,同时也能让大家有机会一起参与k3s社区的建设。

iuY7Rnm.png!web

很多人在体验k3s时,都对它的无比精简感到折服。我们都了解Kubernetes(以下简称K8s)是个非常复杂的架构,controlplane中就包括apiserver/controller-manager/scheduler等,worker中还需要有kubelet/kube-proxy,元数据还需要存储在etcd上。这些服务每一项都需要单独部署,还需要进行配置联动,尽管我们可以借用很多开源工具(比如RKE),但是部署前你还是需要准备大量镜像或者二进制文件等。k3s的部署就非常简便,它通过一个binary就可以部署上面提到的大部分服务,这也就是本文要介绍的内容,k3s的黑魔法之一“单进程k8s”。

单进程k8s分析

我们先不管具体如何实现,先来看一下单进程k8s的表面现象。我们安装一个k3s,但禁用agent,这样更有利于观察结果:

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable-agent" sh -

这时使用kubectl是看不到node信息,但是我们能够获取namespace信息,这就说明k8s的controlplane相关服务已经启动了:

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程序运行结果,下方可以看到k3s只启动了一个进程:

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其他服务apiserver/scheduler/controller-manager等是如何启动的,我们查看k3s对应的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实现的,而Golang通常并不会直接使用thread,一般是通过goroutine来使用系统的thread,分析源码中k3s server的实现,可以看到api-server/scheduler等服务确实是goroutine来启动的:

https://github.com/rancher/k3s ... er.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中执行它,还需另外两项工作:

  1. 在k3s中引入kube-apiserver代码,并将其编译到k3s binary中,这部分在源码中的go.mod中有所体现
  2. 由于是在thread中执行,所以k3s不能执行apiserver的main函数,这部分在上面提到server.go源码中也有体现

k3s server的goroutine除了刚才提到的controlplane相关服务外,还包括默认内置运行的flannel/ingress controller,更有一些k3s扩展的一些高级controller,这部分我们会以单独的文章进行分析。

以上内容,我们只是在单独的k3s server层面分析,一旦我们加入一个节点作为worker,那么worker节点上会怎样的展现?

curl -sfL https://get.k3s.io | K3S_URL=https://myserver:6443 K3S_TOKEN=XXX sh -

我们依然按照上面的思路,先看一下worker节点上k3s相关的进程和线程情况:

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节点的kubelet和kube-proxy的运行方式与k3s server上运行api-server/scheduler等服务的方式是一样的,也包括agent上flannel和tunnel proxy等服务,都是通过goroutine调用,并在操作系统上以thread方式运行。而worker节点中,有一个特立独行的存在就是containerd(如果你还是喜欢使用docker,请忽略以下内容),containerd是作为一个k3s agent的子进程来运行。

containerd因为有其特殊性,它会为每个容器创建单独的containerd-shim进程为容器提供运行时支持,正因为这样containerd本身必须是进程级别的,它可以拥有独立的上下文,进而提供容器管理能力。较新版本的k3s,已经使用了containerd-shim-runc-v2来运行容器,这种模式对k8s的Pod更加友好,早期containerd-shim v1版本,pod的pause容器需要单独运行一个containerd-shim进程,v2版本可以把Pod内的容器都放在一个containerd-shim进程下运行,Pod内每个容器会成为这个containerd-shim的子进程。比如coredns Pod对应的containerd-shim进程Pid是2325,那么它的两个子进程分别是coredns本身和pause容器服务:

pstree -p -aT 2325

containerd-shim,2325 -namespace k8s.io -id 5aad2ea4d09f997baab6a0343dfb10abd86971601bae29200e39cffb5709b938-a

├─coredns,2598 -conf /etc/coredns/Corefile

└─pause,2392

后 记

本文向大家分析了k3s这种单进程模式如何运行整个k8s服务,相当于我们对k3s的原理有了一个基本了解。然而,k3s仍然有很多未解之谜,agent和server如何通信组建集群?k3s内置的rootfs起到什么作用?k3s内置的CLI工具如何使用?k3s如何实现使用sqllite/mysql来代替etcd等等,这些问题我们会在后续文章中一一解答。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK