16 庖丁解牛:kubelet
整体概览
+——————————————————–+ | +———————+ +———————+ | kubelet | kube-proxy | +———————+ +———————+ | +—————————————————-+ | Container Runtime (Docker) | +———————+ +———————+ | Pod | Pod | +—–+ +—–+ | +—–++—–++—–+ | C1 | C2 | C1 | C2 | C3 | +—–+ +—–+ | +—–++—–++—–+ | +———————+ +———————+ | +—————————————————-+ | +——————————————————–+ |
在第 3 节《宏观认识:整体架构》 中,我们知道了 K8S 中 Node 由一些必要的组件构成,而其中最为核心的当属
kubelet 了,如果没有
kubelet 的存在,那我们预期的各类资源就只能存在于
Master 的相关组件中了,而 K8S 也很能只是一个 CRUD 的普通程序了。本节,我们来介绍下
kubelet 及它是如何完成这一系列任务的。
## kubelet 是什么
按照一般架构设计上的习惯,
kubelet 所承担的角色一般会被叫做
agent ,这里叫做
kubelet 很大程度上受
Borg 的命名影响,
Borg 里面也有一个
Borglet 的组件存在。
kubelet 便是 K8S 中的
agent ,负责
Node 和
Pod 相关的管理任务。
同样的,在我们下载 K8S 二进制文件解压后,便可以得到
kubelet 的可执行文件。在第 5 节中,我们也完成了
kubelet 以
systemd 进行启动和管理的相关配置。
## kubelet 有什么作用
通常来讲
agent 这样的角色起到的作用首先便是要能够注册,让
server 端知道它的存在,所以这便是它的第一个作用:节点管理。
节点管理
当我们执行
kubelet –help 的时候,会看到它所支持的可配置参数,其中有一个
–register-node 参数便是用于控制是否向
kube-apiserver 注册节点的,默认是开启的。
我们在第 5 节中还介绍了如何新增一个
Node ,当
kubeadm join 执行成功后,你便可以通过
kubectl get node 查看到新加入集群中的
Node ,与此同时,你也可以在该节点上通过以下命令查看
kubelet 的状态。 master $ systemctl status kubelet ● kubelet.service - kubelet: The Kubernetes Agent Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: disabled) Drop-In: /etc/systemd/system/kubelet.service.d └─kubeadm.conf Active: active (running) since Thu 2018-12-13 07:49:51 UTC; 32min ago Docs: http://kubernetes.io/docs/ Main PID: 3876259 (kubelet) Memory: 66.3M CGroup: /system.slice/kubelet.service └─3876259 /usr/bin/kubelet –bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf –kubeconfig=/etc/kubernete…
当我们查看
Node 信息时,也能得到如下输出:
master $ kubectl get nodes node01 -o yaml apiVersion: v1 kind: Node metadata: annotations: kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock node.alpha.kubernetes.io/ttl: “0” volumes.kubernetes.io/controller-managed-attach-detach: “true” creationTimestamp: 2018-12-13T07:50:47Z labels: beta.kubernetes.io/arch: amd64 beta.kubernetes.io/os: linux kubernetes.io/hostname: node01 name: node01 resourceVersion: “4242” selfLink: /api/v1/nodes/node01 uid: cd612df6-feab-11e8-9a0b-0242ac110096 spec: {} status: addresses: - address: 172.17.0.152 type: InternalIP - address: node01 type: Hostname allocatable: cpu: “4” ephemeral-storage: “89032026784” hugepages-1Gi: “0” hugepages-2Mi: “0” memory: 3894788Ki pods: “110” capacity: cpu: “4” ephemeral-storage: 96605932Ki hugepages-1Gi: “0” hugepages-2Mi: “0” memory: 3997188Ki pods: “110” conditions: - lastHeartbeatTime: 2018-12-13T08:39:41Z lastTransitionTime: 2018-12-13T07:50:47Z message: kubelet has sufficient disk space available reason: KubeletHasSufficientDisk status: “False” type: OutOfDisk - lastHeartbeatTime: 2018-12-13T08:39:41Z lastTransitionTime: 2018-12-13T07:50:47Z message: kubelet has sufficient memory available reason: KubeletHasSufficientMemory status: “False” type: MemoryPressure - lastHeartbeatTime: 2018-12-13T08:39:41Z lastTransitionTime: 2018-12-13T07:50:47Z message: kubelet has no disk pressure reason: KubeletHasNoDiskPressure status: “False” type: DiskPressure - lastHeartbeatTime: 2018-12-13T08:39:41Z lastTransitionTime: 2018-12-13T07:50:47Z message: kubelet has sufficient PID available reason: KubeletHasSufficientPID status: “False” type: PIDPressure - lastHeartbeatTime: 2018-12-13T08:39:41Z lastTransitionTime: 2018-12-13T07:51:37Z message: kubelet is posting ready status. AppArmor enabled reason: KubeletReady status: “True” type: Ready daemonEndpoints: kubeletEndpoint: Port: 10250 images: - names: - k8s.gcr.io/kube-apiserver-amd64@sha256:956bea8c139620c9fc823fb81ff9b5647582b53bd33904302987d56ab24fc187 - k8s.gcr.io/kube-apiserver-amd64:v1.11.3 sizeBytes: 186676561 nodeInfo: architecture: amd64 bootID: 89ced22c-f7f8-4c4d-ad0d-d10887ab900e containerRuntimeVersion: docker://18.6.0 kernelVersion: 4.4.0-62-generic kubeProxyVersion: v1.11.3 kubeletVersion: v1.11.3 machineID: 26ba042302eea8095d6576975c120eeb operatingSystem: linux osImage: Ubuntu 16.04.2 LTS systemUUID: 26ba042302eea8095d6576975c120eeb
可以看到
kubelet 不仅将自己注册给了
kube-apiserver ,同时它所在机器的信息也都进行了上报,包括 CPU,内存,IP 信息等。
这其中有我们在第 2 节中提到的关于
Node 状态相关的一些信息,可以对照着看看。
当然这里除了这些信息外,还有些值得注意的,比如
daemonEndpoints 之类的,可以看到目前
kubelet 监听在了
10250 端口,这个端口可通过
–port 配置,但是之后会被废弃掉,我们是写入了
/var/lib/kubelet/config.yaml 的配置文件中。 master $ cat /var/lib/kubelet/config.yaml address: 0.0.0.0 apiVersion: kubelet.config.k8s.io/v1beta1 authentication: anonymous: enabled: false webhook: cacheTTL: 2m0s enabled: true x509: clientCAFile: /etc/kubernetes/pki/ca.crt authorization: mode: Webhook webhook: cacheAuthorizedTTL: 5m0s cacheUnauthorizedTTL: 30s cgroupDriver: cgroupfs cgroupsPerQOS: true clusterDNS: - 10.96.0.10 clusterDomain: cluster.local containerLogMaxFiles: 5 containerLogMaxSize: 10Mi contentType: application/vnd.kubernetes.protobuf cpuCFSQuota: true cpuManagerPolicy: none cpuManagerReconcilePeriod: 10s enableControllerAttachDetach: true enableDebuggingHandlers: true enforceNodeAllocatable: - pods eventBurst: 10 eventRecordQPS: 5 evictionHard: imagefs.available: 15% memory.available: 100Mi nodefs.available: 10% nodefs.inodesFree: 5% evictionPressureTransitionPeriod: 5m0s failSwapOn: true fileCheckFrequency: 20s hairpinMode: promiscuous-bridge healthzBindAddress: 127.0.0.1 healthzPort: 10248 httpCheckFrequency: 20s imageGCHighThresholdPercent: 85 imageGCLowThresholdPercent: 80 imageMinimumGCAge: 2m0s iptablesDropBit: 15 iptablesMasqueradeBit: 14 kind: KubeletConfiguration kubeAPIBurst: 10 kubeAPIQPS: 5 makeIPTablesUtilChains: true maxOpenFiles: 1000000 maxPods: 110 nodeStatusUpdateFrequency: 10s oomScoreAdj: -999 podPidsLimit: -1 port: 10250 registryBurst: 10 registryPullQPS: 5 resolvConf: /etc/resolv.conf rotateCertificates: true runtimeRequestTimeout: 2m0s serializeImagePulls: true staticPodPath: /etc/kubernetes/manifests streamingConnectionIdleTimeout: 4h0m0s syncFrequency: 1m0s volumeStatsAggPeriod: 1m0s master $
这其中有一些需要关注的配置:
- maxPods :最大的
Pod 数
- healthzBindAddress 和
healthzPort :配置了健康检查所监听的地址和端口
我们可用以下方式进行验证: master $ curl 127.0.0.1:10248/healthz ok
- authentication 和
authorization :认证授权相关
- evictionHard :涉及到
kubelet 的驱逐策略,对
Pod 调度分配之类的影响很大
其余部分,可参考手册内容
Pod 管理
从上面的配置以及我们之前的介绍中,
kube-scheduler 处理了
Pod 应该调度至哪个
Node ,而
kubelet 则是保障该
Pod 能按照预期,在对应
Node 上启动并保持工作。
同时,
kubelet 在保障
Pod 能按预期工作,主要是做了两方面的事情:
- 健康检查:通过
LivenessProbe 和
ReadinessProbe 探针进行检查,判断是否健康及是否已经准备好接受请求。
- 资源监控:通过
cAdvisor 进行资源监。
## kubelet 是如何工作的
大致的功能已经介绍了,我们来看下它大体的实现。
首先是在
cmd/kubelet/app/server.go 文件中的
Run 方法: func Run(s /options.KubeletServer, kubeDeps /kubelet.Dependencies, stopCh <-chan struct{}) error { glog.Infof(“Version: %+v”, version.Get()) if err := initForOS(s.KubeletFlags.WindowsService); err != nil { return fmt.Errorf(“failed OS init: %v”, err) } if err := run(s, kubeDeps, stopCh); err != nil { return fmt.Errorf(“failed to run Kubelet: %v”, err) } return nil }
这个方法看起来很简单那,它是在读取完一系列的配置和校验之后开始被调用的,在调用过程中,会在日志中输出当前的版本号,如果你的
kubelet 已经正常运行,当你执行
journalctl -u kubelet 的时候,便会看到一条相关的日志输出。
之后,便是一个
run 方法,其中包含着各种环境检查,容器管理,
cAdvisor 初始化之类的操作,直到
kubelet 基本正确运行后,则会调用
pkg/kubelet/kubelet.go 中的一个
BirthCry 方法,该方法从命名就可以看出来,它其实就是宣告
kubelet 已经启动: func (kl /*Kubelet) BirthCry() { kl.recorder.Eventf(kl.nodeRef, v1.EventTypeNormal, events.StartingKubelet, “Starting kubelet.”) }
后续则是关于注册,
Pod 管理以及资源相关的处理逻辑,内容较多,这里就不再展开了。
总结
本节中我们介绍了
kubelet 的主要功能和基本实现,了解到了它不仅可将自身注册到集群,同时还承担着保障
Pod 可在该
Node 上按预期工作。另外
kubelet 其实还承担着清理
Node 上一些由 K8S 调度
Pod 所造成的磁盘占用之类的工作。
从上面的配置中基本能看出来一些,这部分的功能大多数情况下不需要大家人为干预,所以也就不再展开了。
当
Pod 在
Node 上正常运行之后,若是需要对外提供服务,则需要将其暴露出来。下节,我们来介绍下
kube-proxy 是如何来处理这些工作的。
参考资料
https://learn.lianglianglee.com/%e4%b8%93%e6%a0%8f/Kubernetes%20%e4%bb%8e%e4%b8%8a%e6%89%8b%e5%88%b0%e5%ae%9e%e8%b7%b5/16%20%e5%ba%96%e4%b8%81%e8%a7%a3%e7%89%9b%ef%bc%9akubelet.md
更多学习
更多实时资讯,前沿技术,生活趣事。尽在【老马啸西风】
交流社群:[交流群信息](https://mp.weixin.qq.com/s/rkSvXxiiLGjl3S-ZOZCr0Q)