目录
什么是K8s
为什么需要K8s
什么是容器(Contianer)
K8s能做什么?
K8s的架构原理
控制平面(Control plane)
kube-apiserver
etcd
kube-scheduler
kube-controller-manager
cloud-controller-manager
小结
节点组件(Node)
container runtime
Pod
kubelet
kube-proxy
集群(Cluster)
工作负载资源
Ingress
K8s部署和调用服务
什么是kubectl
部署服务
调用服务
什么是K8s
Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态,其服务、支持和工具的使用范围相当广泛。
Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。K8s 这个缩写是因为 K 和 s 之间有 8 个字符的关系。 Google 在 2014 年开源了 Kubernetes 项目。 Kubernetes 建立在Google 大规模运行生产工作负载十几年经验的基础上, 结合了社区中最优秀的想法和实践。
K8s介于应用服务和服务器之间,能够通过策略,协调和管理多个应用服务,只需要一个 yaml 文件配置,定义应用的部署顺序等信息,就能自动部署应用到各个服务器上,还能让它们挂了自动重启,自动扩缩容。
为什么需要K8s
什么是容器(Contianer)
容器是打包应用及其运行依赖环境的技术。每个运行的容器都是可重复的; 包含依赖环境在内的标准,意味着无论你在哪里运行它都会得到相同的行为。容器将应用程序从底层的主机设施中解耦, 这使得在不同的云或 OS 环境中部署更加容易。
容器在服务器中获得独立的环境和应用程序并运行起来。这样一个独立的环境和应用程序,这个容器是不是很像我们用 vmware 或 kvm 整出来的传统虚拟机?但不同的是,传统虚拟机自带一个完整操作系统,而容器本身不带完整操作系统,容器的基础镜像实际上只包含了操作系统的核心依赖库和配置文件等必要组件。它利用一个叫 Namespace 的能力让它看起来就像是一个独立操作系统一样。再利用一个叫 Cgroup 的能力限制它能使用的计算资源。所以说,容器本质上只是个自带独立运行环境的特殊进程,底层用的其实是宿主机的操作系统内核。
Namespace 是 Linux 内核的一个特性,它允许多个进程组共享同一个系统资源,但每个进程组都认为自己独占这些资源。Namespace 通过为每个进程组提供独立的视图来实现这一点,这样每个进程组都看不到其他组的进程或资源。
Cgroup(Control Groups)是 Linux 内核的另一个特性,它允许系统管理员对一组进程的资源使用进行细粒度的控制和监控。Cgroup 可以限制、记录和隔离进程组使用的物理资源(如 CPU、内存、磁盘 I/O 等)。
K8s能做什么?
假如你是一个程序员,你用代码写了一个资源分享服务,并将它部署在了云平台上。
但资源分享服务太过受欢迎,访问量太大,经常会挂。
后来你又上线了商城应用服务和语音应用服务,随着应用服务变多,需求也千奇百怪。有的应用服务不希望被外网访问到,有的部署的时候要求内存得大于 24GB 才能正常跑。
你每次都需要登录到各个服务器上,执行手动操作更新。不仅容易出错,还贼浪费时间。
那么问题就来了,有没有一个办法,可以解决上面的问题?当然有,这次我们要加的中间层,叫 Kubernetes。
在生产环境中, 你需要管理运行着应用程序的容器,并确保服务不会下线。 例如,如果一个容器发生故障,则你需要启动另一个容器。 如果此行为交由给系统处理,是不是会更容易一些?这就是 Kubernetes 要来做的事情! Kubernetes 为你提供了一个可弹性运行分布式系统的框架。 Kubernetes 会满足你的扩展要求、故障转移你的应用、提供部署模式等
Kubernetes 不是传统的、包罗万象的 PaaS(平台即服务)系统。 由于 Kubernetes 是在容器级别运行,而非在硬件级别,它提供了 PaaS 产品共有的一些普遍适用的功能, 例如部署、扩展、负载均衡,允许用户集成他们的日志记录、监控和警报方案。 但是,Kubernetes 不是单体式(monolithic)系统,那些默认解决方案都是可选、可插拔的。 Kubernetes 为构建开发人员平台提供了基础,但是在重要的地方保留了用户选择权,能有更高的灵活性。
K8s的架构原理
为了实现上面的功能,Kubernetes 会将我们的服务器划为两部分,一部分叫控制平面(Control plane,以前叫 master),另一部分叫工作节点,也就是 Node。简单来说它们的关系就是老板和打工人, 也可以说就是训练师和帕鲁。控制平面负责控制和管理各个 Node,而 Node 则负责实际运行各个应用服务。
控制平面(Control plane)
控制平面组件会为集群做出全局决策,比如资源的调度,以及检测和响应集群事件。例如当不满足 Deployment 的replicas字段时,要启动新的 Pod。
控制平面组件可以在集群中的任何节点上运行。 然而,为了简单起见,安装脚本通常会在同一个服务器上启动所有控制平面组件, 并且不会在此服务器上运行用户容器。
kube-apiserver
apiserver负责公开了 Kubernetes API,负责处理接受请求的工作。 是 Kubernetes 控制平面的前端。Kubernetes API 设计上考虑了水平扩缩,也就是说,它可通过部署多个实例来进行扩缩,可以运行 Kubernetes API 的多个实例,并在这些实例之间平衡流量。
etcd
一致且高可用的键值存储,用作 Kubernetes 所有集群数据的后台数据库。如果你的 Kubernetes 集群使用 etcd 作为其后台数据库, 请确保你针对这些数据有一份备份计划。
kube-scheduler
scheduler负责监视新创建的、未指定运行Nodes的Pods, 并选择节点来让 Pod 在上面运行。调度决策考虑的因素包括单个 Pod 及 Pods 集合的资源需求、软硬件及策略约束、 亲和性及反亲和性规范、数据位置、工作负载间的干扰及最后时限。
kube-controller-manager
controller-manager 负责运行控制器进程。从逻辑上讲, 每个controller都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在同一个进程中运行。
控制器有许多不同类型。以下是一些例子:
- Node 控制器:负责在节点出现故障时进行通知和响应
- Job 控制器:监测代表一次性任务的 Job 对象,然后创建 Pod 来运行这些任务直至完成
- EndpointSlice 控制器:填充 EndpointSlice 对象(以提供 Service 和 Pod 之间的链接)。
- ServiceAccount 控制器:为新的命名空间创建默认的 ServiceAccount。
cloud-controller-manager
云控制器管理器(Cloud Controller Manager)允许将你的集群连接到云提供商的 API 之上, 并将与该云平台交互的组件同与你的集群交互的组件分离开来。它仅运行特定于云平台的控制器。 因此如果你在自己的环境中运行 Kubernetes,或者在本地计算机中运行环境, 所部署的集群不包含云控制器管理器。
与 kube-controller-manager
类似,cloud-controller-manager
将若干逻辑上独立的控制回路组合到同一个可执行文件中,以同一进程的方式供你运行。 你可以对其执行水平扩容(运行不止一个副本)以提升性能或者增强容错能力。
下面的控制器都包含对云平台驱动的依赖:
- Node 控制器:用于在节点终止响应后检查云平台以确定节点是否已被删除
- Route 控制器:用于在底层云基础架构中设置路由
- Service 控制器:用于创建、更新和删除云平台上的负载均衡器
小结
- 以前我们需要登录到每台服务器上,手动执行各种命令,现在我们只需要调用 k8s 的提供的 api 接口,就能操作这些服务资源,这些接口都由 API Server 组件提供。
- 以前我们需要到处看下哪台服务器 cpu 和内存资源充足,然后才能部署应用,现在这部分决策逻辑由 Scheduler(调度器)来完成。
- 找到服务器后,以前我们会手动创建,关闭服务,现在这部分功能由 Controller Manager(控制器管理器)来负责。
- 上面的功能都会产生一些数据,这些数据需要被保存起来,方便后续做逻辑,因此 k8s 还会需要一个存储层,用来存放各种数据信息,目前是用的 etcd。
节点组件(Node)
Node 是实际的工作节点,它既可以是裸机服务器,也可以是虚拟机。它会负责实际运行各个应用服务。多个应用服务共享一台 Node 上的内存和 CPU 等计算资源。
container runtime
以前我们需要上传代码到服务器,而用了 k8s 之后,我们只需要将服务代码打包成Container Image(容器镜像),就能一行命令将它部署。关于容器镜像可以简单理解为它其实就是将应用代码和依赖的系统环境打了个压缩包,在任意一台机器上解压这个压缩包,就能正常运行服务。为了下载和部署镜像,Node 中会有一个 Container runtime 组件,这个基础组件使 Kubernetes 能够有效运行容器,负责管理 Kubernetes 环境中容器的执行和生命周期。。
Pod
每个应用服务都可以认为是一个 Container(容器), 并且大多数时候,我们还会为应用服务搭配一个日志收集器 Container 或监控收集器 Container,多个 Container 共同组成一个一个 Pod,它运行在 Node 上。可以说Pod 代表的是集群上处于运行状态的一组容器的集合。
k8s 可以将 pod 从某个 Node 调度到另一个 Node,还能以 pod 为单位去做重启和动态扩缩容的操作。所以说 Pod 是 k8s 中最小的调度单位。
kubelet
另外,前面提到控制平面会用 Controller Manager (通过 API Server)控制 Node 创建和关闭服务,那 Node 也得有个组件能接收到这个命令才能去做这些动作,这个组件叫 kubelet,它主要负责管理和监控 Pod。
kubelet会在集群中每个Node上运行。 它保证Contianer都运行在Pod中。kubelet接收一组通过各类机制提供给它的 PodSpec,确保这些 PodSpec 中描述的容器处于运行状态且健康。kubelet 不会管理不是由 Kubernetes 创建的容器。
kube-proxy
最后,Node 中还有个 Kube Proxy ,它负责 Node 的网络通信功能,有了它,外部请求就能被转发到 Pod 内。实际上是集群中每个Node上所运行的网络代理, 维护节点上的一些网络规则, 这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。如果操作系统提供了可用的数据包过滤层,则 kube-proxy 会通过它来实现网络规则。 否则,kube-proxy 仅做流量转发。
集群(Cluster)
控制平面和 Node 共同构成了一个 Cluster,也就是集群。在实际生产环境中,我们一般会构建多个集群, 比如测试环境用一个集群,生产环境用另外一个集群。同时,为了将集群内部的服务暴露给外部用户使用,我们一般还会部署一个入口控制器,比如 Ingress 控制器(比如 Nginx),它可以提供一个入口让外部用户访问集群内部服务。
至此,我们可以得到K8s的整体架构:
工作负载资源
工作负载是在 Kubernetes 上运行的应用程序。在 Kubernetes 中,无论你的负载是由单个组件还是由多个一同工作的组件构成, 你都可以在一组Pod中运行它。 Kubernetes Pod 遵循预定义的生命周期。
例如,当在你的集群中运行了某个 Pod,但是 Pod 所在的Node出现致命错误时, 所有该节点上的 Pod 的状态都会变成失败。Kubernetes 将这类失败视为最终状态,即使该节点后来恢复正常运行,你也需要创建新的 Pod 以恢复应用。
不过,为了减轻用户的使用负担,通常不需要用户直接管理每个Pod。 而是使用负载资源来替用户管理一组 Pod。 这些负载资源通过配置 控制器 来确保正确类型的、处于运行状态的 Pod 个数是正确的,与用户所指定的状态相一致。
Kubernetes 提供若干种内置的工作负载资源:
- Deployment 和 ReplicaSet (替换原来的资源 ReplicationController)。 Deployment 很适合用来管理你的集群上的无状态应用,Deployment 中的所有 Pod 都是相互等价的,并且在需要的时候被替换。
- StatefulSet 让你能够运行一个或者多个以某种方式跟踪应用状态的 Pod。 例如,如果你的负载会将数据作持久存储,你可以运行一个 StatefulSet,将每个 Pod 与某个 PersistentVolume 对应起来。你在 StatefulSet 中各个 Pod 内运行的代码可以将数据复制到同一 StatefulSet 中的其它 Pod 中以提高整体的服务可靠性。
- DaemonSet 定义提供节点本地支撑设施的 Pod。这些 Pod 可能对于你的集群的运维是 非常重要的,例如作为网络链接的辅助工具或者作为网络 插件 的一部分等等。每次你向集群中添加一个新节点时,如果该节点与某
DaemonSet
的规约匹配,则控制平面会为该 DaemonSet 调度一个 Pod 到该新节点上运行。 - Job 和 CronJob。 定义一些一直运行到结束并停止的任务。 你可以使用 Job 来定义只需要执行一次并且执行后即视为完成的任务。你可以使用 CronJob 来根据某个排期表来多次运行同一个 Job。
Ingress
Ingress 是 Kubernetes 集群中的一种 API 对象,它管理外部访问到集群内服务的 HTTP 和 HTTPS 路由。Ingress 可以提供 URL 路由、负载均衡、SSL 终端、名称基的虚拟托管和路径基的虚拟托管等特性。流量路由由 Ingress 资源所定义的规则来控制。
下面是 Ingress 的一个简单示例,可将所有流量都发送到同一 Service
通过配置,Ingress 可为 Service 提供外部可访问的 URL、对其流量作负载均衡、 终止 SSL/TLS,以及基于名称的虚拟托管等能力。 Ingress 通常与 Ingress 控制器一起使用,Ingress 控制器负责完成 Ingress 的工作,具体实现上通常会使用某个负载均衡器, 不过也可以配置边缘路由器或其他前端来帮助处理流量。
Kubernetes 本身不提供 Ingress 控制器,但有许多第三方 Ingress 控制器可供选择,如 Nginx Ingress Controller、Traefik、HAProxy Ingress Controller 等。
K8s部署和调用服务
什么是kubectl
上面提到说我们可以使用 k8s 提供的 API 去创建服务,但问题就来了,这是需要我们自己写代码去调用这些 API 吗?
答案是不需要,k8s 为我们准备了一个命令行工具 kubectl,我们只需要执行命令,它内部就会调用 k8s 的 API。
部署服务
1.首先我们需要编写 YAML 文件,在里面定义 Pod 里用到了哪些镜像,占用多少内存和 CPU 等信息。
2.然后使用 kubectl 命令行工具执行 kubectl apply -f xx.yaml
,此时 kubectl 会读取和解析 YAML 文件,将解析后的对象通过 API 请求发送给 Kubernetes 控制平面内 的 API Server。
3.API Server 会根据要求,驱使 Scheduler 通过 etcd 提供的数据寻找合适的 Node。
4.Controller Manager 会通过 API Server 控制 Node 创建服务。
5.Node 内部的 kubelet 在收到命令后会开始基于 Container runtime 组件去拉取镜像创建容器,最终完成 Pod 的创建。
整个过程下来,我们只需要写一遍 yaml 文件,和执行一次 kubectl 命令,比传统方式省心太多了。部署完服务后,我们来看下服务是怎么被调用的。
调用服务
以前外部用户小明,直接在浏览器上发送 http 请求,就能送到我们服务器上的Nginx,然后转发到部署的服务内。
用了 k8s 之后,外部请求会先到达 k8s 集群的 Ingress 控制器,然后请求会被转发到 k8s 内部的某个 Node 的 Kube Proxy 上,再找到对应的 pod,然后才是转发到内部容器服务中,处理结果原路返回,到这就完成了一次服务调用。
到这里我们就大概了解了 k8s 的工作原理了,它本质上就是应用服务和服务器之间的中间层,通过暴露一系列 API 能力让我们简化服务的部署运维流程。
并且,不少中大厂基于这些 API 能力搭了自己的服务管理平台,程序员不再需要敲 kubectl 命令,直接在界面上点点几下,就能完成服务的部署和扩容等操作,非常便捷好用。