其他分享
首页 > 其他分享> > 云原生技术公开课--基础

云原生技术公开课--基础

作者:互联网

目录

内容大纲

云原生

相关阅读:https://www.cnblogs.com/zwtblog/tag/云原生/

云原生技术发展简史

云原生的定义

究其本质,凡是能够提高云上资源利用率和应用交付效率的行为或方式都是云原生的。

技术范畴

关键点

  1. 如何构建自包含、可定制的应用镜像。
  2. 能不能实现应用快速部署与隔离能力。
  3. 应用基础设施创建和销毁的自动化管理。
  4. 可复制的管控系统和支撑组件。

容器

说容器前 回顾 操作系统 是如何管理进程的。

通过 ps 等操作看到各式各样的进程

带来的问题

解决( 为进程提供一个独立的运行环境 ):

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

容器具有一个独立的文件系统。
因为使用的是系统的资源,所以在独立的文件系统内不需要具备内核相关的代码或者工具。
只需要提供容器所需的二进制文件、配置文件以及依赖即可。

只要容器运行时所需的文件集合都能够具备,那么这个容器就能够运行起来。

容器的生命周期

容器是一组具有隔离特性的进程集合, initial 进程启动的时候,容器也会随之启动。

因此,可以认为容器的生命周期和 initial 进程的生命周期是一致的。

initial 进程本身也可以产生其他的子进程或者通过 docker exec 产生出来的运维操作,也属于 initial 进程管理的范围内。

当 initial 进程退出的时候,所有的子进程也会随之退出,这样也是为了防止资源的泄漏。

问题

应用里面的程序往往是有状态的,可能会产生一些重要的数据,当一个容器退出被删除之后,数据也就会丢失了。

需要将容器所产生出来的重要数据持久化下来。

容器能够直接将数据持久化到指定的目录上,这个目录就称之为数据卷。

数据卷有一些特点:

其中非常明显的就是数据卷的生命周期是独立于容器的生命周期的。

它是一个特殊的目录,是用于帮助容器进行持久化的。

数据卷管理主要有两种方式:

容器项目架构

moby 是目前最流行的容器管理引擎。

moby daemon 所依赖的最重要的组件就是 containerd,containerd 是一个容器运行时管理引擎,

其独立于 moby daemon ,可以对上提供容器、镜像的相关管理。

containerd 底层有 containerd shim 模块,其类似于一个守护进程,这样设计的原因有几点:

容器 VS VM

VM 利用 Hypervisor 虚拟化技术来模拟 CPU、内存等硬件资源,这样就可以在宿主机上建立一个 Guest OS。

每一个 Guest OS 都有一个独立的内核,每个应用都是相互独立的,VM 可以提供一个更好的隔离效果。

但这样的隔离效果需要付出一定的代价,因为需要把一部分的计算资源交给虚拟化,这样就很难充分利用现有的计算资源,并且每个 Guest OS 都需要占用大量的磁盘空间。


容器是针对于进程而言的,只需要一个独立的文件系统提供其所需要文件集合即可。

所有的文件隔离都是进程级别的,因此启动时间快于 VM,并且所需的磁盘空间也小于 VM。

进程级别的隔离并没有想象中的那么好,隔离效果相比 VM 要差很多。

镜像

将这些容器运行时所需要的所有的文件集合称之为容器镜像。

通常情况下,采用 Dockerfile 来构建镜像。

每个构建步骤都会对已有的文件系统进行操作,这样就会带来文件系统内容的变化,这些变化称之为changeset。

把构建步骤所产生的变化依次作用到一个空文件夹上,就能够得到一个完整的镜像。

changeset 的好处:

Kubernetes

Kubernetes 是一个自动化的容器编排平台,它负责应用的部署、应用的弹性以及应用的管理。

核心功能:

Kubernetes 的架构

典型的二层架构和 server-client 架构。Master 作为中央的管控节点,会去与 Node 进行一个连接。

Kubernetes 的 Master 包含四个主要的组件:API Server、Controller、Scheduler 以及 etcd。

etcd 相关阅读: 可靠的分布式KV存储产品-ETCD-初见

Node

Kubernetes 的 Node 是真正运行业务负载的,每个业务负载会以 Pod 的形式运行。

一个 Pod 中运行的一个或者多个容器,真正去运行这些 Pod 的组件的是叫做 kubelet,它通过 API Server 接收到所需要 Pod 运行的状态,然后提交到 Container Runtime 组件中。

Kubernetes 的核心概念与它的 API

Pod

Pod 是 Kubernetes 的一个最小调度以及资源单元。

用户可以通过 Kubernetes 的 Pod API 生产一个 Pod,让 Kubernetes 对这个 Pod 进行调度。

一个 Pod 简单来说是对一组容器的抽象,它里面会包含一个或多个容器。

Volume

Volume 就是卷的概念,它是用来管理 Kubernetes 存储的,是用来声明在 Pod 中的容器可以访问文件目录的,一个卷可以被挂载在 Pod 中一个或者多个容器的指定路径下面。

Deployment

Deployment 是在 Pod 这个抽象上更为上层的一个抽象。

它可以定义一组 Pod 的副本数目、以及这个 Pod 的版本。一般大家用 Deployment 这个抽象来做应用的真正的管理,而 Pod 是组成 Deployment 最小的单元。

Service

Service 提供了一个或者多个 Pod 实例的稳定访问地址。

Namespace

Namespace 是用来做一个集群内部的逻辑隔离的,它包括鉴权、资源管理等。

Kubernetes 的每个资源,比如刚才讲的 Pod、Deployment、Service 都属于一个 Namespace,同一个 Namespace 中的资源需要命名的唯一性,不同的 Namespace 中的资源可以重名。

API

Kubernetes API 是由 HTTP+JSON 组成的:用户访问的方式是 HTTP,

访问的 API 中 content 的内容是 JSON 格式的。

Kubernetes 的 kubectl 也就是 command tool,Kubernetes UI,或者有时候用 curl,直接与 Kubernetes 进行沟通,都是使用 HTTP + JSON 这种形式。

例子:

对于这个 Pod 类型的资源,它的 HTTP 访问的路径,就是 API。

然后是 apiVesion: V1,之后是相应的 Namespaces,以及 Pods 资源,最终是 Podname,也就是 Pod 的名字。

Pod和容器设计模式

Pod 实际上正是 kubernetes 项目为你抽象出来的一个可以类比为进程组的概念。

Pod 也是 Kubernetes 的原子调度单位。

例子:

假如一个 Helloworld 程序需要4个进程, Kubernetes 里它会把四个独立的进程分别用四个独立的容器启动起来,然后把它们定义在一个 Pod 里面。

所以当 Kubernetes 把 Helloworld 给拉起来的时候,实际上会看到四个容器,它们共享了某些资源。

这些资源都属于 Pod,所以Pod 在 Kubernetes 里面只有一个逻辑单位,真正起来在物理上存在的东西,就是四个容器。

为什么 Pod 必须是原子调度单位?

能不能通过调度把 Pod 这个事情给解决掉呢?为什么 Pod 必须是 Kubernetes 里面的原子调度单位?

通过一个例子来解释:

假如现在有两个容器,它们是紧密协作的,所以它们应该被部署在一个 Pod 里面。

前集群环境的可用内存是这样一个情况:Node_A:1.25G 内存,Node_B:2G 内存。


如果没有 Pod 概念,就只有两个容器,这两个容器要紧密协作、运行在一台机器上。

如果调度器先把 App 调度到了 Node_A 上面,这时会发现:LogCollector 实际上是没办法调度到 Node_A 上的,因为资源不够。

此时整个应用本身就已经出问题了,调度已经失败了,必须去重新调度。


以上就是一个非常典型的成组调度失败的例子。

英文叫做:Task co-scheduling 问题,这个问题不是说不能解,在很多项目里面,这样的问题都有解法。

在 Kubernetes 里,就直接通过 Pod 这样一个概念去解决了。

因为在 Kubernetes 里,这样的一个 App 容器和 LogCollector 容器一定是属于一个 Pod 的,它们在调度时必然是以一个 Pod 为单位进行调度。

Pod 的实现机制

Pod 是一个逻辑概念。那在机器上,它究竟是怎么实现的呢?

Pod 要解决的问题: 如何让一个 Pod 里的多个容器之间最高效的共享某些资源和数据?

容器之间原本是被 Linux Namespace 和 cgroups 隔开的,所以现在实际要解决的是怎么去打破这个隔离,然后共享某些事情和某些信息。这就是 Pod 的设计要解决的核心问题所在。


解法分为两个部分:网络和存储。

1.共享网络

例子:

现在有一个 Pod,其中包含了一个容器 A 和一个容器 B,它们两个就要共享 Network Namespace。

在 Kubernetes 里的解法是这样的:它会在每个 Pod 里,额外起一个 Infra container 小容器来共享整个 Pod 的 Network Namespace。

Infra container 是一个非常小的镜像,大概 100~200KB 左右,是一个汇编语言写的、永远处于“暂停”状态的容器。

由于有了这样一个 Infra container 之后,其他所有容器都会通过 Join Namespace 的方式加入到 Infra container 的 Network Namespace 中。

一个 Pod 里面的所有容器,它们看到的网络视图是完全一样的。

即:它们看到的网络设备、IP地址、Mac地址等等,跟网络相关的信息,其实全是一份。

这一份都来自于 Pod 第一次创建的这个 Infra container。

这就是 Pod 解决网络共享的一个解法。


有一个相当于说中间的容器存在,所以整个 Pod 里面,必然是 Infra container 第一个启动。

并且整个 Pod 的生命周期是等同于 Infra container 的生命周期的,与容器 A 和 B 是无关的。

这也是为什么在 Kubernetes 里面,它是允许去单独更新 Pod 里的某一个镜像的,即:做这个操作,整个 Pod 不会重建,也不会重启。

2.共享存储

把 volume 变成了 Pod level。然后所有容器,就是所有同属于一个 Pod 的容器,共享所有的 volume。

详解容器设计模式

问题:现在要发布一个应用,这个应用是 JAVA 写的,有一个 WAR 包需要把它放到 Tomcat 的 web APP 目录下面。可是像这样一个 WAR 包或 Tomcat 这样一个容器的话,怎么去做,怎么去发布?

在 Kubernetes 里解决方案是 Init Container。

通过组合不同角色的容器,并且按照类似 Init Container 这样的编排方式,统一的去打包这样一个应用,把它用 Pod 来去做。

像这样的一个概念,在 Kubernetes 里面就是一个非常经典的容器设计模式,叫做:“Sidecar”。

这个 Init Container,它就是一个 Sidecar。

它只负责把镜像里的 WAR 包拷贝到共享目录里面,以便被 Tomcat 能够用起来。

这个 Pod 就是一个自包含的,可以把这一个 Pod 在全世界任何一个 Kubernetes 上面都顺利启用起来。

不用担心没有分布式存储、Volume 不是持久化的,它一定是可以公布的。

容器设计模式:Sidecar

Sidecar模式是一种将应用功能从应用本身剥离出来作为单独进程的方式。

该模式允许我们向应用无侵入添加多种功能,避免了为满足第三方组件需求而向应用添加额外的配置代码。

场景:

前面提到的应用日志收集,业务容器将日志写在一个 Volume 里面,而由于 Volume 在 Pod 里面是被共享的。

所以日志容器 —— 即 Sidecar 容器一定可以通过共享该 Volume,直接把日志文件读出来,然后存到远程存储里面。

假如现在有个 Pod 需要访问一个外部系统,或者一些外部服务,但是这些外部系统是一个集群。

那么这个时候如何通过一个统一的、简单的方式,用一个 IP 地址,就把这些集群都访问到?

1、修改代码。因为代码里记录了这些集群的地址。

2、解耦的方法,即通过 Sidecar 代理容器。

单独写一个 Proxy,用来处理对接外部的服务集群,它对外暴露出来只有一个 IP 地址。

接下来,业务容器主要访问 Proxy,然后由 Proxy 去连接这些服务集群。

这里的关键在于 Pod 里面多个容器是通过 localhost 直接通信的,因为它们同属于一个 network Namespace,网络视图都一样,所以它们俩通信localhost,并没有性能损耗。

所以说代理容器除了做了解耦之外,并不会降低性能,并且提升了重用性。

例子:

业务容器暴露出来的监控接口是 /metrics,访问这个这个容器的 metrics 的这个 URL 就可以拿到了。

可是现在,这个监控系统升级了,它访问的 URL 是 /health,只认得暴露出 health 健康检查的 URL,才能去做监控,metrics 不认识。

可以不去改代码,而是额外写一个 Adapter,用来把所有对 health 的这个请求转发给 metrics 就可以了,所以这个 Adapter 对外暴露的是 health 这样一个监控的 URL。

这样的关键还在于 Pod 之中的容器是通过 localhost 直接通信的,所以没有性能损耗,并且这样一个 Adapter 容器可以被全公司重用起来。

核心原理

Kubernetes 的资源对象组成:

元数据部分

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

前三个标签都打在了 Pod 对象上,分别标识了对应的应用环境、发布的成熟度和应用的版本。

从应用标签的例子可以看到,标签的名字包括了一个域名的前缀,用来描述打标签的系统和工具。

最后一个标签打在 Node 对象上,还在域名前增加了版本的标识 beta 字符串。

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

例子:

假设系统中有四个 Pod,每个 Pod 都有标识系统层级和环境的标签。

一般是系统或者工具用来存储资源的非标示性信息,可以用来扩展资源的 spec/status 的描述。

例子:

第一个,存储了阿里云负载器的证书 ID,可以看到 annotations 一样可以拥有域名的前缀,标注中也可以包含版本信息。

第二个 annotation存储了 nginx 接入层的配置信息,可以看到 annotations 中包括“,”这样无法出现在 label 中的特殊字符。

第三个 annotations 一般可以在 kubectl apply 命令行操作后的资源中看到, annotation 值是一个结构化的数据,实际上是一个 json 串,标记了上一次 kubectl 操作的资源的 json 的描述。

所有者,一般就是指集合类的资源。

集合类资源的控制器会创建对应的归属资源。

Ownereference 使得用户可以方便地查找一个创建资源的对象,另外,还可以用来实现级联删除的效果。

控制型模式

控制型模式最核心的就是控制循环的概念。

在控制循环中包括了控制器,被控制的系统,以及能够观测系统的传感器,三个逻辑组件。

待补充…………

两种 API 设计方法

待补充…………

应用编排与管理(Deployment)

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

作用

Job & CronJobs & DaemonSet

Job

为什么需要job?**

Job:管理任务的控制器:

Job 语法:


CronJob,其实也可以叫定时运行 Job 。

一的不同点就是它可以设计一个时间。

特别适合晚上做一些清理任务,还有可以几分钟执行一次,几小时执行一次等等,这就叫定时任务。


架构设计

Job 管理模式

Job Controller 其实还是主要去创建相对应的 pod。

然后 Job Controller 会去跟踪 Job 的状态,及时地根据提交的一些配置重试或者继续创建。

Job 控制器

所有的 job 都是一个 controller,它会 watch 这个 API Server,每次提交一个 Job 的 yaml 都会经过 api-server 传到 ETCD 里面去。

然后 Job Controller 会注册几个 Handler,每当有添加、更新、删除等操作的时候,它会通过一个内存级的消息队列,发到 controller 里面。

通过 Job Controller 检查当前是否有运行的 pod:

如果没有的话,通过 Scale up 把这个 pod 创建出来。

如果有的话,或者如果大于这个数,对它进行 Scale down。

如果这时 pod 发生了变化,需要及时 Update 它的状态。

同时要去检查它是否是并行的 job,或者是串行的 job,根据设置的配置并行度、串行度,及时地把 pod 的数量给创建出来。

最后,它会把 job 的整个的状态更新到 API Server 里面去。

DaemonSet

为什么要有DaemonSet?

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

应用配置管理

问题:

用一个容器镜像来启动一个 container。要启动这个容器,其实有很多需要配套的问题待解决:

解决:

ConfigMap

主要管理容器运行所需的配置文件,环境变量,命令行参数等可变配置。

用于解耦容器镜像和可变配置,从而保障工作负载(Pod)的可移植性。

这是 ConfigMap 本身的一个定义,它包括两个部分:

Secret

Secret 是一个主要用来存储密码 token 等一些敏感信息的资源对象。

其中,敏感信息是采用 base-64 编码保存起来的,

类型:

第一种是 Opaque,它是普通的 Secret 文件;

第二种是 service-account-token,是用于 service-account 身份认证用的 Secret;

第三种是 dockerconfigjson,这是拉取私有仓库镜像的用的一种 Secret;

第四种是 bootstrap.token,是用于节点接入集群校验用的 Secret。

创建:

使用:

ServiceAccount

ServiceAccount 首先是用于解决 pod 在集群里面的身份认证问题,身份认证信息是存在于 Secret 里面。

…………

Resource

目前内部支持类型有三种:CPU、内存,以及临时存储。

自定义配置时,指定的数量必须为整数。

目前资源配置主要分成 request 和 limit 两种类型:

Pod 服务质量 (QoS) 配置

根据 CPU 对容器内存资源的需求,对 pod 的服务质量进行一个分类,分别是 Guaranteed、Burstable 和 BestEffort。

那么这个服务质量是什么样的呢?

资源配置好后,当这个节点上 pod 容器运行,比如说节点上 memory 配额资源不足,kubelet会把一些低优先级的,或者说服务质量要求不高的(如:BestEffort、Burstable)pod 驱逐掉。

它们是按照先去除 BestEffort,再去除 Burstable 的一个顺序来驱逐 pod 的。

SecurityContext

主要是用于限制容器的一个行为,它能保证系统和其他容器的安全。

Kubernetes 和 runtime 通过用户的配置,最后下传到内核里,再通过内核的机制让 SecurityContext 来生效。

SecurityContext 主要分为三个级别:

权限和访问控制设置项,现在一共列有七项(这个数量后续可能会变化):

  1. 第一个就是通过用户 ID 和组 ID 来控制文件访问权限;
  2. 第二个是 SELinux,它是通过策略配置来控制用户或者进程对文件的访问控制;
  3. 第三个是特权容器;
  4. 第四个是 Capabilities,它也是给特定进程来配置一个 privileged 能力;
  5. 第五个是 AppArmor,它也是通过一些配置文件来控制可执行文件的一个访问控制权限,比如说一些端口的读写;
  6. 第六个是一个对系统调用的控制;
  7. 第七个是对子进程能否获取比父亲更多的权限的一个限制。

InitContainer

主要为普通 container 服务 。

和普通 container 的区别,有以下三点内容:

  1. InitContainer 首先会比普通 container 先启动,并且直到所有的 InitContainer 执行成功后,普通 container 才会被启动;
  2. InitContainer 之间是按定义的次序去启动执行的,执行成功一个之后再执行第二个,而普通的 container 是并发启动的;
  3. InitContainer 执行成功后就结束退出,而普通容器可能会一直在执行。

应用存储与持久化数据卷

Volumes

Pod Volumes

因此 K8s 中又引入了 Persistent Volumes 概念。

它可以将存储和计算分离,通过不同的组件来管理存储资源和计算资源,然后解耦 pod 和 Volume 之间生命周期的关联。

这样,当把 pod 删除之后,它使用的PV仍然存在,还可以被新建的 pod 复用。

Persistent Volumes Claim

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

PV 对象有俩种 产生的方式:

架构设计

PV 和 PVC 的处理流程

csi 的全称是 container storage interface,它是K8s社区后面对存储插件实现(out of tree)的官方推荐方式。csi 的实现大体可以分为两部分:

PV、PVC 以及通过 csi 使用存储流程

存储快照和拓扑调度

在使用存储时,为了提高数据操作的容错性,我们通常有需要对线上数据进行snapshot,以及能快速restore的能力。

Snapshot

存储快照的设计其实是仿照 pvc & pv 体系的设计思想。

当用户需要存储快照的功能时,可以通过 VolumeSnapshot 对象来声明,并指定相应的 VolumeSnapshotClass 对象。

之后由集群中的相关组件动态生成存储快照以及存储快照对应的对象 VolumeSnapshotContent。

Topolopy

PV 在给 PVC 绑定或者动态生成 PV 的时候,并不知道后面将使用它的 pod 将调度在哪些 node 上。

但 PV 本身的使用,是对 pod 所在的 node 有拓扑位置的限制的。

在 K8s 中将 PV 和 PVC 的 binding 操作和动态创建 PV 的操作做了 delay,delay 到 pod 调度结果出来之后,再去做这两个操作。这样的话有什么好处?

为了实现上面所说的延迟绑定和延迟创建 PV,需要在 K8s 中的改动涉及到的相关组件有三个:

观测

kubernetes网络概念以及策略控制

问题:

Pod 与 Netns 的关系

每个 pod 都有着独立的网络空间,pod net container 会共享这个网络空间。

一般 K8s 会推荐选用 Loopback 接口,在 pod net container 之间进行通信,而所有的 container 通过 pod 的 IP 对外提供服务。

另外对于宿主机上的 Root Netns,可以把它看做一个特殊的网络空间,只不过它的 Pid 是1。

主流网络方案简介

Kubernetes Service

为什么需要服务发现?

在 K8s 集群里面应用是通过 pod 去部署的, 而 pod 生命周期是短暂的。

在 pod 的生命周期过程中,比如它创建或销毁,它的 IP 地址都会发生变化,这样就不能使用传统的部署方式,不能指定 IP 去访问指定的应用。

K8s 服务发现以及 K8s Service 是这样的整体的一个架构。

K8s 分为 master 节点和 worker 节点:

在 K8s master 节点里面有 APIServer,就是统一管理 K8s 所有对象的地方,所有的组件都会注册到 APIServer 上面去监听这个对象的变化。

这里面最关键的有三个组件:

参考

标签:原生,容器,PV,Kubernetes,--,公开课,一个,Pod,pod
来源: https://www.cnblogs.com/zwtblog/p/16356682.html