k8s学习记录(知识篇)

现在k8s有很多很好的教程,比如阿里云的云原生课堂,还有课后的练习。

资料

https://github.com/cloudnativeapp/meetupppt

https://gitchat.csdn.net/columnTopic/5d68c72ade93ed72d6eca55c?utm_source=so



容器

容器,是一个视图隔离、资源可限制、独立文件系统的进程集合。

容器:资源视图隔离namespace 控制资源使用率cgroup 独立文件系统chroot

容器的生命周期

单进程模型

1 Init 进程生命周期 = 容器生命周期

2 运行期间可运行 exec 执行运维操作

数据持久化

1 独立于容器的生命周期

2 数据卷 - docker volume vs bind


Cgroups

https://blog.csdn.net/ahilll/article/details/81940608

Kubernetes 


Kubernetes 有如下几个核心的功能

服务的发现与负载的均衡;

容器的自动装箱,我们也会把它叫做 scheduling,就是“调度”,把一个容器放到一个集群的某一个机器上,Kubernetes 会帮助我们去做存储的编排,让存储的声明周期与容器的生命周期能有一个连接;

Kubernetes 会帮助我们去做自动化的容器的恢复。在一个集群中,经常会出现宿主机的问题或者说是 OS 的问题,导致容器本身的不可用,Kubernetes 会自动地对这些不可用的容器进行恢复;

Kubernetes 会帮助我们去做应用的自动发布与应用的回滚,以及与应用相关的配置密文的管理;

对于 job 类型任务,Kubernetes 可以去做批量的执行;

为了让这个集群、这个应用更富有弹性,Kubernetes 也支持水平的伸缩。


Kubernetes 架构是一个比较典型的二层架构和 server-client 架构。Master 作为中央的管控节点,会去与 Node 进行一个连接。所有 UI 的、clients、这些 user 侧的组件,只会和 Master 进行连接,把希望的状态或者想执行的命令下发给 Master,Master 会把这些命令或者状态下发给相应的节点,进行最终的执行。

API Server: 顾名思义是用来处理 API 操作的,Kubernetes 中所有的组件都会和 API Server 进行连接,组件与组件之间一般不进行独立的连接,都依赖于 API Server 进行消息的传送;

Controller: 是控制器,它用来完成对集群状态的一些管理。比如刚刚我们提到的两个例子之中,第一个自动对容器进行修复、第二个自动进行水平扩张,都是由 Kubernetes 中的 Controller 来进行完成的;

Scheduler: 是调度器,“调度器”顾名思义就是完成调度的操作,就是我们刚才介绍的第一个例子中,把一个用户提交的 Container,依据它对 CPU、对 memory 请求大小,找一台合适的节点,进行放置;

etcd: 是一个分布式的一个存储系统,API Server 中所需要的这些原信息都被放置在 etcd 中,etcd 本身是一个高可用系统,通过 etcd 保证整个 Kubernetes 的 Master 组件的高可用性。



Kubernetes 的 Node 是真正运行业务负载的,每个业务负载会以 Pod 的形式运行。等一下我会介绍一下 Pod 的概念。一个 Pod 中运行的一个或者多个容器,真正去运行这些 Pod 的组件的是叫做 kubelet,也就是 Node 上最为关键的组件,它通过 API Server 接收到所需要 Pod 运行的状态,然后提交到我们下面画的这个 Container Runtime 组件中。

Pod


容器的本质实际上是一个进程,是一个视图被隔离,资源受限的进程。

由于容器实际上是一个“单进程”模型,所以如果你在容器里启动多个进程,只有一个可以作为 PID=1 的进程,而这时候,如果这个 PID=1 的进程挂了,或者说失败退出了,那么其他三个进程就会自然而然的成为孤儿,没有人能够管理它们,没有人能够回收它们的资源,这是一个非常不好的情况。


labels

第一个元数据,也是最重要的一个元数据是:资源标签。资源标签是一种具有标识型的 Key:Value 元数据,这里展示了几个常见的标签。

前三个标签都打在了 Pod 对象上,分别标识了对应的应用环境、发布的成熟度和应用的版本。从应用标签的例子可以看到,标签的名字包括了一个域名的前缀,用来描述打标签的系统和工具, 最后一个标签打在 Node 对象上,还在域名前增加了版本的标识 beta 字符串。

标签主要用来筛选资源和组合资源,可以使用类似于 SQL 查询 select,来根据 Label 查询相关的资源。

控制器模式


控制型模式最核心的就是控制循环的概念。在控制循环中包括了控制器,被控制的系统,以及能够观测系统的传感器,三个逻辑组件。

当然这些组件都是逻辑的,外界通过修改资源 spec 来控制资源,控制器比较资源 spec 和 status,从而计算一个 diff,diff 最后会用来决定执行对系统进行什么样的控制操作,控制操作会使得系统产生新的输出,并被传感器以资源 status 形式上报,控制器的各个组件将都会是独立自主地运行,不断使系统向 spec 表示终态趋近。

Sensor

控制循环中逻辑的传感器主要由 Reflector、Informer、Indexer 三个组件构成。

Reflector 通过 List 和 Watch K8s server 来获取资源的数据。List 用来在 Controller 重启以及 Watch 中断的情况下,进行系统资源的全量更新;而 Watch 则在多次 List 之间进行增量的资源更新;Reflector 在获取新的资源数据后,会在 Delta 队列中塞入一个包括资源对象信息本身以及资源对象事件类型的 Delta 记录,Delta 队列中可以保证同一个对象在队列中仅有一条记录,从而避免 Reflector 重新 List 和 Watch 的时候产生重复的记录。

Informer 组件不断地从 Delta 队列中弹出 delta 记录,然后把资源对象交给 indexer,让 indexer 把资源记录在一个缓存中,缓存在默认设置下是用资源的命名空间来做索引的,并且可以被 Controller Manager 或多个 Controller 所共享。之后,再把这个事件交给事件的回调函数

Deployment:管理部署发布的控制器

现在有以下的问题:

首先,如何保证集群内可用 Pod 的数量?也就是说我们应用 A 四个 Pod 如果出现了一些宿主机故障,或者一些网络问题,如何能保证它可用的数量?

如何为所有 Pod 更新镜像版本?我们是否要某一个 Pod 去重建新版本的 Pod?

然后在更新过程中,如何保证服务的可用性?

以及更新过程中,如果发现了问题,如何快速回滚到上一个版本?

首先,Deployment 定义了一种 Pod 期望数量,比如说应用 A,我们期望 Pod 数量是四个,那么这样的话,controller

就会持续维持 Pod 数量为期望的数量。当我们与 Pod 出现了网络问题或者宿主机问题的话,controller

能帮我们恢复,也就是新扩出来对应的 Pod,来保证可用的 Pod 数量与期望数量一致;

配置 Pod 发布方式,也就是说 controller 会按照用户给定的策略来更新 Pod,而且更新过程中,也可以设定不可用 Pod 数量在多少范围内;

如果更新过程中发生问题的话,即所谓“一键”回滚,也就是说你通过一条命令或者一行修改能够将 Deployment 下面所有 Pod 更新为某一个旧版本 。

Job:管理任务的控制器

我们来看一下 Kubernetes 的 Job 为我们提供了什么功能:

首先 kubernetes 的 Job 是一个管理任务的控制器,它可以创建一个或多个 Pod 来指定 Pod 的数量,并可以监控它是否成功地运行或终止;

我们可以根据 Pod 的状态来给 Job 设置重置的方式及重试的次数;

我们还可以根据依赖关系,保证上一个任务运行完成之后再运行下一个任务;

同时还可以控制任务的并行度,根据并行度来确保 Pod 运行过程中的并行次数和总体完成大小。

 - Cronjob

DaemonSet

DaemonSet 也是 Kubernetes 提供的一个 default controller,它实际是做一个守护进程的控制器,它能帮我们做到以下几件事情:

首先能保证集群内的每一个节点都运行一组相同的 pod;

同时还能根据节点的状态保证新加入的节点自动创建对应的 pod;

在移除节点的时候,能删除对应的 pod;

而且它会跟踪每个 pod 的状态,当这个 pod 出现异常、Crash 掉了,会及时地去 recovery 这个状态。

Pod 的配置管理

可变配置就用 ConfigMap

敏感信息是用 Secret

身份认证是用 ServiceAccount 这几个独立的资源来实现的;

资源配置是用 Resources

安全管控是用 SecurityContext

前置校验是用 InitContainer 这几个在 spec 里面加的字段,来实现的这些配置管理。

InitContainer

InitContainer 首先会比普通 container 先启动,并且直到所有的 InitContainer 执行成功后,普通 container 才会被启动;

InitContainer 之间是按定义的次序去启动执行的,执行成功一个之后再执行第二个,而普通的 container 是并发启动的;

InitContainer 执行成功后就结束退出,而普通容器可能会一直在执行。它可能是一个 longtime 的,或者说失败了会重启,这个也是 InitContainer 和普通 container 不同的地方。

Pod Volumes

本地存储,常用的有 emptydir/hostpath;

网络存储:网络存储当前的实现方式有两种,一种是 in-tree,它的实现的代码是放在 K8s

代码仓库中的,随着k8s对存储类型支持的增多,这种方式会给k8s本身的维护和发展带来很大的负担;而第二种实现方式是

out-of-tree,它的实现其实是给 K8s

本身解耦的,通过抽象接口将不同存储的driver实现从k8s代码仓库中剥离,因此out-of-tree

是后面社区主推的一种实现网络存储插件的方式;

Projected Volumes:它其实是将一些配置信息,如 secret/configmap 用卷的形式挂载在容器中,让容器中的程序可以通过POSIX接口来访问配置数据;

PV 与 PVC 就是今天要重点介绍的内容。

PV(Persistent Volumes)

场景一:pod 重建销毁,如用 Deployment 管理的 pod,在做镜像升级的过程中,会产生新的 pod并且删除旧的 pod ,那新旧 pod 之间如何复用数据?

场景二:宿主机宕机的时候,要把上面的 pod 迁移,这个时候 StatefulSet 管理的 pod,其实已经实现了带卷迁移的语义。这时通过 Pod Volumes 显然是做不到的;

场景三:多个 pod 之间,如果想要共享数据,应该如何去声明呢?我们知道,同一个 pod 中多个容器想共享数据,可以借助 Pod Volumes 来解决;当多个 pod 想共享数据时,Pod Volumes 就很难去表达这种语义;

场景四:如果要想对数据卷做一些功能扩展性,如:snapshot、resize 这些功能,又应该如何去做呢?

以上场景中,通过 Pod Volumes 很难准确地表达它的复用/共享语义,对它的扩展也比较困难。因此 K8s 中又引入了

**Persistent Volumes **概念,它可以将存储和计算分离,通过不同的组件来管理存储资源和计算资源,然后解耦 pod 和

Volume 之间生命周期的关联。这样,当把 pod 删除之后,它使用的PV仍然存在,还可以被新建的 pod 复用。

PVC(Persistent Volume Claim)

访问模式是什么?其实就是:我要使用的存储是可以被多个node共享还是只能单node独占访问(注意是node level而不是pod level)?只读还是读写访问?用户只用关心这些东西,与存储相关的实现细节是不需要关心的。

通过 PVC 和 PV 的概念,将用户需求和实现细节解耦开,用户只用通过 PVC

声明自己的存储需求。PV是有集群管理员和存储相关团队来统一运维和管控,这样的话,就简化了用户使用存储的方式。可以看到,PV 和 PVC

的设计其实有点像面向对象的接口与实现的关系。用户在使用功能时,只需关心用户接口,不需关心它内部复杂的实现细节。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容