k8s入门

kubernetes入门

Posted by logicic on March 23, 2021

k8s从入门到精通

本文按照以下顺序进行说明

  1. k8s是什么
  2. k8s的基本组成结构
  3. 例子:创建一个pod k8s发生了哪些事?

kubernetes是什么

From k8s.io

What is Kubernetes?
Kubernetes is a portable, extensible, open-source platform for managing containerized workloads and services, that facilitates both declarative configuration and automation. It has a large, rapidly growing ecosystem. Kubernetes services, support, and tools are widely available.

总的来说,就是在大规模部署容器的场景下方便地自动化地管理这些容器。

k8s的基本组成结构

k8s的核心组件有:

  • 存储中心: etcd服务

  • kube-apiserver
    • 提供了集群管理的REST API接口(包括鉴权、数据校验及集群状态变更)
    • 负责其他模块之间的数据交互,承担通信枢纽功能
    • 是资源额控制的入口
  • Kube-controller-manager 是由一系列控制器组成,通过apiserver监控整个集群的状态,并确保集群处于预期的工作状态。由以下控制器组成:
    • Node Controller
    • Service controller
    • Volume controller
    • Endpoint controller
    • Garbage controller
    • Namespace controller
    • Job Controller
    • Resource quta Controller
    • custom resource definition
  • Kube-scheduler
    • 主要功能是接收调度pod到适合的运算节点上
  • Kubelet
    • kubelet主要功能就是定时从某个地方获取节点上pod的期望状态(运行什么容器、运行的副本数量、网络或者存储如何配置等等),并调用对应的容器平台接口达到这个状态
    • 定时汇报当前节点的状态给apiserver,以供调度的时候使用,如果不能按照规定上报节点或者pod的信息,会认为节点或者pod不健康。
    • 镜像和容器的清理工作,保证节点上镜像不会占满磁盘空间,退出的容器不会占用太多资源
  • kube-proxy
    • 是k8s在每个节点上运行网络代理,service资源的载体
    • 建立了pod网络和集群网络的关系
    • 常用三种流量调度模式
      • Userspace被废弃
      • Iptable
      • Ipvs
    • 负责建立和删除包括更新调度规则、通知apiserver自己的更新,或者从apiserver哪里获取其他kube-proxy的调度规则变化来更新自己的

cli客户端:

  • kubectl:访问apiserver

主要分为两个类型:

  • master节点:运行apiserver,controller-manager,scheduler的节点称为master节点
  • work节点:运行kubelet、kube-proxy的节点称为work节点
  • leader节点:那是master中的概念,许多master节点组成高可用的系统,正常情况下,只有一个master是在使用的,这个正在使用着的master节点称为leader,如果这个leader挂掉了,会进行选举产生新的leader,进行系统的正常运行。

这是逻辑上的概念,没有物理上的限制,即一个节点既可以是master又可以是work,只要满足上述条件。

总结:

  • kube-apiserver 、kube-controller-manager 、kube-scheduler 决策类角色,启动这三者的称为master节点;

  • etcd/dqlite:分布式存储服务,记录数据

  • kubelet:负责pod对应容器的创建、启动停止等任务, 执行类角色,启动的称为work节点。

  • kube-proxy:实现Service的通信与负载均衡机制的重要组件

  • containerd/docker engine:负责本机的容器创建和管理工作。

  • kubernetes目标:自动化资源控制====>how?====>通过跟踪对比etcd库里保存“资源期望状态”与当前环境的真实状态差异进行对比,从而实现自动化和自动纠错。

例子:创建一个pod k8s发生了哪些事?

what-happens-when-k8s

/1. ` kubectl run –image=nginx –replicas=3`,kubectl接收到命令后会执行一些客户端验证操作,以确保不合法的请求(例如,创建不支持的资源或使用格式错误的镜像名称)不会发送给api-server

/2. 验证通过后,会通过生成器generator封装HTTP请求

/3. 生成运行时对象,比如说env,name,labels等等

/4. 版本协商,为了更容易地消除字段或者重新组织资源结构,Kubernetes 支持多个 API 版本,每个版本都在不同的 API 路径下,例如 /api/v1

/5. 客户端认证,用户凭证保存在 kubeconfig 文件中,解析之后就填入HTTP请求HEAD中

/6. 发送请求到apiserver


/7. Authenticate认证请求,kube-apiserver 是客户端和系统组件用来保存和检索集群状态的主要接口。为了执行相应的功能,kube-apiserver 需要能够验证请求者是合法的

/8. authorize授权,认证成功后,需要知道是否有权限执行此操作,在micro8s上,使用--authorization-mode=AlwaysAllow

Using Flags for Your Authorization Module
You must include a flag in your policy to indicate which authorization module your policies include:

The following flags can be used:

--authorization-mode=ABAC Attribute-Based Access Control (ABAC) mode allows you to configure policies using local files.
--authorization-mode=RBAC Role-based access control (RBAC) mode allows you to create and store policies using the Kubernetes API.
--authorization-mode=Webhook WebHook is an HTTP callback mode that allows you to manage authorization using a remote REST endpoint.
--authorization-mode=Node Node authorization is a special-purpose authorization mode that specifically authorizes API requests made by kubelets.
--authorization-mode=AlwaysDeny This flag blocks all requests. Use this flag only for testing.
--authorization-mode=AlwaysAllow This flag allows all requests. Use this flag only if you do not require authorization for your API requests.
You can choose more than one authorization module. Modules are checked in order so an earlier module has higher priority to allow or deny a request.

/9. admission controller准入控制,不同于授权和认证只关心请求的用户和操作,准入控制还处理请求的内容,并且仅对创建、更新、删除等等。通过--enable-admission-plugins打开,如果没有设置,则会有默认设置,如下:

NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota
  • ResourceQuota 不仅能限制某个 Namespace 中创建资源的数量,而且能限制某个 Namespace 中被 Pod 所请求的资源总量。该准入控制器和资源对象 ResourceQuota 一起实现了资源配额管理。
  • LimitRanger 作用类似于上面的 ResourceQuota 控制器,针对 Namespace 资源的每个个体(Pod 与 Container 等)的资源配额。该插件和资源对象 LimitRange 一起实现资源配额管理。

/10. 资源将会通过 storage provider保存到 etcd 中,资源创建过程中出现的任何错误都会被捕获,最后 storage provider 会执行 get 调用来确认该资源是否被成功创建。

/11. 此时就可以返回http response给kubectl了。

/12. 在一个资源对象被持久化到数据存储之后,apiserver 还无法完全看到或调度它,在此之前还要执行一系列Initializers (这个还不怎么明白)

/13. 到了这个阶段,我们的 Deployment 记录已经保存在 etcd 中,并且所有的初始化逻辑都执行完成,接下来的阶段将会涉及到该资源所依赖的拓扑结构

/14. 15. 在 Kubernetes 中,Deployment 实际上只是一系列 Replicaset 的集合,该 Deployment 的 status 就会被更新,然后重新进入与之前相同的循环,等待 Deployment 与期望的状态相匹配

/16. 如果匹配则进入下层

/17. 18. 19. Replicaset 是一系列 Pod 的集合,ReplicaSet Controller 来继续协调直到status与期望相符

/18. Infomer 是一种模式,它允许 Controller 查找缓存在本地内存中的数据(这份数据由 Informer 自己维护)并列出它们感兴趣的资源。(这个还不怎么明白)

/19 . 当所有的 Controller 正常运行后,etcd 中就会保存一个 Deployment、一个 ReplicaSet 和 三个 Pod 资源记录,并且可以通过 kube-apiserver 查看,等待scheduler的调度。

/21. 一旦找到了合适的节点,Scheduler 就会创建一个 Binding 对象,该对象的 NameUid 与 Pod 相匹配,并且其 ObjectReference 字段包含所选节点的名称,然后通过 POST 请求发送给 apiserver

/22. 23. 当 kube-apiserver 接收到此 Binding 对象时,将该对象反序列化并更新 Pod 资源中的字段,把绑定信息写入 etcd 中

/24. Scheduler 将 Pod 调度到某个节点上,该节点的 Kubelet 就会接管该 Pod 并开始部署


总结:

到目前为止,所有的状态变化仅仅只是针对保存在 etcd 中的资源记录,接下来,在每个 work 节点上启动的 Kubelet 服务进程,该进程用于处理 Scheduler 下发到本节点的任务,管理 Pod 的生命周期,包括挂载卷volume、容器日志记录logs、垃圾回收gc以及其他与 Pod 相关的事件event。

Kubelet每隔 20 秒(好像我查到的default是每5s上报一次,如果40s都没有上报,apiserver就认为这个node是notready的)向 kube-apiserver 通过 NodeName 获取自身 Node 上所要运行的 Pod 清单。一旦获取到了这个清单,它就会通过与自己的内部缓存进行比较来检测新增加的 Pod,如果有差异,就开始同步 Pod 列表。

/25. 接收 Scheduler 下发到本节点的任务,生成一个 PodStatus 对象,它表示 Pod 当前阶段的状态。Pod 的状态(Phase) 是 Pod 在其生命周期中的最精简的概要,包括 PendingRunningSucceededFailedUnkown 这几个值。生成 PodStatus 之后(Pod 中的 status 字段),Kubelet 就会将它发送到 Pod 的状态管理器,该管理器的任务是通过 apiserver 异步更新 etcd 中的记录。

/26. 接下来运行一系列准入处理器来确保该 Pod 是否具有相应的权限(包括强制执行 AppArmor 配置文件和 NO_NEW_PRIVS),被准入控制器拒绝的 Pod 将一直保持 Pending 状态。

/27. 如果 Kubelet 启动时指定了 cgroups-per-qos 参数,Kubelet 就会为该 Pod 创建 cgroup 并进行相应的资源限制。这是为了更方便地对 Pod 进行服务质量(QoS)管理。

/28. 然后为 Pod 创建相应的目录,包括 Pod 的目录(/var/run/kubelet/pods/<podID>),该 Pod 的卷目录(<podDir>/volumes)和该 Pod 的插件目录(<podDir>/plugins)。

/29. 卷管理器会挂载 Spec.Volumes 中定义的相关数据卷,然后等待是否挂载成功。根据挂载卷类型的不同,某些 Pod 可能需要等待更长的时间(比如 NFS 卷)。

/30. 从 apiserver 中检索Spec.ImagePullSecrets 中定义的所有 Secret,然后将其注入到容器中。

/31. 通过容器运行时接口(Container Runtime Interface(CRI))开始启动容器。容器运行时(例如 DockerRktcontainerd

/32. 第一次启动 Pod 时,Kubelet 会通过 Remote Procedure Command(RPC) 协议调用 RunPodSandbox,创建 sandbox 时首先创建的是 pause 容器。pause 容器作为同一个 Pod 中所有其他容器的基础容器,它为 Pod 中的每个业务容器提供了大量的 Pod 级别资源,这些资源都是 Linux 命名空间(包括网络命名空间,IPC 命名空间和 PID 命名空间)。

/33. 现在我们的 Pod 已经有了基本的骨架:一个共享所有命名空间以允许业务容器在同一个 Pod 里进行通信的 pause 容器。但现在还有一个问题,那就是容器的网络是如何建立的?创建网络的任务交给 CNI 插件。CNI 表示容器网络接口(Container Network Interface)它可以给 pause 容器配置相关的网络,然后 Pod 中其他的容器都使用 pause 容器的网络。这里的配置可能包括bridge plugin生成veth,ipam plugin生成ip并且设置相应的路由等等

/34. CNI 插件就会将操作的结果以 json 的格式返回给 Kubelet。

/35. 所有网络都配置完成后,接下来就开始真正启动业务容器,先拉取容器的镜像

/36. 37. kubelet向 PodSpec 中填充了一个 ContainerConfig 数据结构(在其中定义了命令,镜像,标签,挂载卷,设备,环境变量等待)

/38. 39. 对于 Docker 来说,它会将这些信息反序列化并填充到自己的配置信息中,然后再发送给 Dockerd 守护进程。在这个过程中,它会将一些元数据标签(例如容器类型,日志路径,dandbox ID 等待)添加到容器中。

/40. 41. 创建以及启动业务容器

/42. 如果 Pod 中配置了容器生命周期钩子(Hook),容器启动之后就会运行这些 Hook。Hook 的类型包括两种:Exec(执行一段命令) 和 HTTP(发送HTTP请求)。如果 PostStart Hook 启动的时间过长、挂起或者失败,容器将永远不会变成 running 状态。

Reference:

  1. https://fuckcloudnative.io/posts/what-happens-when-k8s/