快速入门 K8S
date
Dec 6, 2023
slug
quick-start-with-k8s
status
Published
tags
K8S
summary
简单介绍下 K8S 的每个组件
type
Post
K8s 是什么K8s 的作用控制平面上的组件数据平面上的组件K8s 是如何运作的K8s 的应用打包声明式模型(declarative model)和期望状态(desired state)DeploymentService总结Ref
K8s 是什么
K8s 是应用编排器(orchestrator),用于对容器化的微服务应用进行编排。
编排器是一套部署和管理服务的系统,它除了能够部署应用之外,还负责管理集群的状态,大体功能如下:
- 部署应用程序
- 动态扩、缩容
- 出现故障时自动修复
- 不停机进行升级和回滚。
术语
初学者很容易搞混这几个名词的含义,下面我介绍一下
容器化应用
运行在容器中的应用。
应用的运行平台按照时间线发展:
- 20 世纪 80 到 90 年代,运行在物理机上
- 2000 年后,运行在虚拟机上
- 云原生时代,运行在容器中
云原生应用
运行在 K8s 上的应用,能够自动扩缩容,故障自愈,滚动升级。
微服务应用
一个微服务应用,是指由许多小而专的服务组件,通过互相通信而组成的一套完整的业务系统。
以一个电子商务系统为例,它可能由以下功能独立的微服务组成:
- 购物车
- 授权服务
- 日志服务
- 订单服务
- …
每个单独的服务可以被称为一个微服务,通常每个微服务都可以由不同的团队负责研发和运维,可以拥有自己的发布节奏,并且独自进行扩缩容。
例如,可以在不影响其他组件的情况下,为日志服务打补丁或扩容。这种构建方式正是云原生应用的一个很重要的特点。
K8s 的诞生
2014 年 Google 开源自家的容器编排技术,将其命名为 Kubernetes(K8s) 然后捐献给了云原生计算基金会(Cloud Native Computing Foundation,CNCF),以对抗当时为了商业化而越来越封闭的 Docker 公司和其产品,在当时 Docker 自己的容器编排技术叫做 swarm。
请记住,K8s 是容器编排技术,docker 是容器技术。
后续 Docker 抽取出来了容器运行时接口(Container Runtime Interface,CRI),作为实现容器技术的规范和标准,也因此k8s不仅支持docker的CRI实现,也支持多个其它CRI实现,例如下图:
K8s 的作用
K8s 担任了两个角色:
- 作为集群
- 作为编排器
作为集群的 K8s
集群由节点(node)组成,节点指的是运行服务的物理机或者虚拟机,按照工作职责可以分类成主节点(Master Node)和工作节点(Worker Node)。
我们可以把一组节点看成一个平面(Plane),由工作节点组成的平面叫做数据平面(Data Plane),由主节点组成的平面叫做控制平面(Control Plane),如下图所示:
上图的集群,可以划分为控制平面和数据平面,主节点(Master Node)作为控制平面的一部分,对外提供API,对内负责各节点的任务分配与调度,并在持久化存储中记录各节点的状态,类似于人体的大脑,控制肌肉的行为。
而工作节点就是负责服务运行的载体,它们类似于人的肌肉,在大脑的控制下做出某些行为。
作为编排器的 K8s
把应用看作球员,把 K8s 看作教练,那么:
K8s 在其中扮演的角色就跟教练在足球队中的角色有点相似了:负责将这些应用组织起来并保持顺畅的运行,当出现一些特定事件或者变化时也要做出响应。
在足球领域中,这种行为称之为指导(coaching),而在服务应用的领域中,这种行为称之为编排(orchestration)。
下面我们来介绍下每个平面上的组件以及它们的作用。
控制平面上的组件
控制平面是由多个组件组成的,这些组件合力完成了控制集群状态的工作。
在默认的 Kubernetes 配置中,所有的控制平面组件都会在单一的 master 节点上运行。然而,在一个高可用性(High-Availability, HA)的配置中,每个组件可能会在多个 master 节点上以复制的方式运行。如果其中一个 master 节点发生故障,那么其它 master 节点可以接替它完成工作。
控制平面上的组件有:
- kube-apiserver:Kubernetes API 服务器是集群中的前端,是所有控制和管理操作的入口点。
- etcd:这是一个轻量级、分布式的键值存储系统,用于保存整个 Kubernetes 集群的所有数据。
- kube-scheduler:这个组件负责根据资源可用性和其他约束条件,对新创建的 Pod 进行调度(即决定运行它们的节点)。
- kube-controller-manager:该组件运行了 Kubernetes 中所有的控制器。例如,Replication controller, Endpoints controller, Namespace controller 等等。
- cloud-controller-manager:这个组件连接到你使用的云提供商,并管理与底层云平台交互的逻辑,例如网络路由、负载均衡器、卷一类的基础设施元素。
还有一个非常重要的组件是 kubelet,但通常认为它不直接属于控制平面,因为它在每个工作节点上运行,而不仅仅运行在 master 节点上。kubelet 是一种命令行工具,使用它来和运行中的集群、容器进行通信。
数据平面上的组件
- Kubelet:正如前面所述,kubelet 是每个节点上的代理,它会定期检查和维护描述的 Pods 确保其状态与 Kubernetes API server 的指示相匹配。
- Kube-proxy:kube-proxy 是实现 Kubernetes 服务概念的网络代理,它处理节点级别的网络路由并进行连接转发。
- Container Runtime:这是运行容器的软件。Kubernetes 支持多种容器运行时,包括 Docker、containerd、CRI-O 和其他符合 Kubernetes CRI (Container Runtime Interface) 的运行时。
- Pods:Pod 是 Kubernetes 的最小部署单元,它包含一个或多个紧密相关的容器。在工作节点上运行的实际应用程序都包装在 Pod 内。
上图是一个运行在数据平面上的工作节点,它的容器运行时是 Docker,用户可以通过 kubelet 和该节点进行通信。在这个容器上启动了 4 个 Pod,每个 Pod 在隔离的环境中完成自己的工作。Proxy的概念在这里没有体现,我们可以从之前的插图中看出,proxy 是负责处理网络相关任务的组件。
K8s 是如何运作的
在了解 K8s 如何运作之前,我们需要了解 K8s 中有关应用运行的概念。
K8s 的应用打包
K8s 中的最小部署单位是 Pod,而不是容器,容器需要装在 Pod 中。一个 Pod 可以进行运行多个容器,这是通过配置文件来决定的。
而 Pod 需要一个组织者来控制多个 Pod 的状态和之间的协作。在 K8s 中,组织者叫做 Deployment。
用户需要编写配置文件,定义 Deployment,告诉 K8s 应该如何管理和组织 Pod,K8s 收到后,会通过一种期望状态管理的方式来实现管控。Deployment 是对 Pod 的更高一层的封装,提供了如扩缩容管理、不停机更新以及版本控制等其他特性。
声明式模型(declarative model)和期望状态(desired state)
前面我们提到了 K8s 具有故障自愈和自动扩缩容的功能,其实现就需要用到这两个功能。
在声明式系统中,用户只需要描述他们想要的系统状态,而不需要明确说明如何达到这个状态。对于 Kubernetes 而言,这就意味着你只需要提供一个描述你期望的集群状态的文件(通常是 YAML 或 JSON 格式),然后 Kubernetes 就会工作以满足你的预期。
期望状态是用户希望系统达到的状态。在 Kubernetes 中,用户通过编写和应用配置文件来描述期望状态,例如描述 Deployment、Service 等。这些配置文件定义了应该运行哪些容器,以及其他的管理信息。
这么说很抽象,其实它们流程很简单:
- 在 manifest 文件中声明一个应用(微服务)期望达到的状态。
- 将 manifest 文件发送到 API Server,可以通过 kubectl、curl 等工具。
- Kubernetes 将 manifest 存储到集群存储,并作为应用的期望状态。
- Kubernetes 在集群中实现上述期望状态
- Kubernetes启动监控循环,保证应用的当前状态(current state)不会与期望状态出现差异。
Deployment
一个可能的 manifest 文件内容如下,用于描述一个 Deployment 对象:
这个 YAML 文件描述了一个 Kubernetes Deployment 对象,其期望状态为运行 3 个副本的 my-app 应用。我们来更详细地看一下各个部分:
apiVersion
和kind
指定了资源类型,这里是 apps/v1 API 版本中的 Deployment。
metadata
字段包含了资源的元数据,例如资源的名称。
spec
字段包含了资源的期望状态:replicas
:期望有 3 个 Pod 在运行。selector
:指定了哪些 Pod 属于这个 Deployment。这个选项匹配所有标签为app: my-app
的 Pod。template
:定义了每个 Pod 的规格(Spec)。在这个例子中,每个 Pod 中都有一个名为 "my-app" 的容器,该容器使用的镜像为 "my-image:1.0。
Service
通常我们的应用还需要通信,在 K8s 中,通信的组织者就是 Service。
Pod 的 IP 地址是不可靠的。在某个 Pod 失效之后,它会被一个拥有新的 IP 的 Pod 代替。Deployment 扩容也会引入拥有新 IP 的 Pod;而缩容则会删除 Pod。这会导致大量的 IP 流失,因而Pod 的 IP 地址是不可靠的,我们不能通过 IP 来进行 Pod 通信,我们需要另一种间接的方式,解耦对 IP 的依赖。
在 Kubernetes 中,Service 是一个抽象概念,用于描述一组提供相同功能的 Pods 的访问策略。它定义了如何访问这些 Pods,无论后端的 Pods 如何变化。
下面就是一个典型的 Service 定义:
在这个 Service 文件中:
selector
字段定义了一个匹配标签为zone=prod
和version=v1
的所有 Pods 的规则。所以,此 Service 会将传入的网络流量转发到带有标签app=my-app
的任何 Pod 上。
在 pod 中,对应的标签声明方式是:
ports
字段指定了服务监听的端口(port)和 Pod 上的目标端口(targetPort)。在这个例子中,Service 会监听端口 80,并将所有传入的流量转发到后端 Pod 的 8080 端口。
当我们的集群开始运行后,K8s 会创建一个 Service 对象用于通信:
当有请求到达 Service 时,Service 会将请求转发给符合筛选条件的 Pod,如下所示,请求将会转发给符合筛选条件的 3 个 Pod。
那么 Service 是如何维护匹配列表的呢?
Endpoint对象
每一个 Service 在创建的时候,都会得到一个关联的 Endpoint 对象,整个 Endpoint 对象其实就是一个动态的列表,其中包含集群中所有的匹配 Service Label 筛选器的健康 Pod。
Kubernetes 会不断地检查 Service 的 Label 筛选器和当前集群中健康 Pod 的列表。如果有新的能够匹配 Label 筛选器的Pod出现,它就会被加入 Endpoint 对象,而消失的 Pod 则会被剔除。也就是说,Endpoint 对象始终是保持更新的。这时,当 Service 需要将流量转发到 Pod 的时候,就会到Endpoint 对象中最新的 Pod 的列表中进行查找。
Service 的使用例子
Service 可以很好地支持金丝雀发布:
假设我们当前版本是 4.1,我们需要发布 4.2,那么我们可以不停机的发布,只需要更改 Service 的 label 为 4.2 就好,如果发现有任何问题,切换回 4.1 就行了。
Service 的种类
Kubernetes 提供了四种类型的 Service,以满足不同的网络需求和场景,这里简单介绍一下:
- ClusterIP:这是默认的 Service 类型。它在整个集群内部分配一个 IP 地址(即 ClusterIP),其他应用可以通过这个 IP 地址访问服务。由于这个地址仅在集群内部有效,所以这种类型的 Service 只支持集群内部的通信。
- NodePort:这种类型的 Service 在每个节点上都开放一个静态端口(称为 NodePort)。外部用户可以通过
<node-ip>:<node-port>
访问该服务。对于集群内部的 Pod 来说,还是通过 ClusterIP 和端口访问服务。
- LoadBalancer:这种类型的 Service 除了具有 NodePort 的功能外,还会向云提供商请求配置负载均衡器,通过外部的负载均衡器来进一步公开服务。这样,外部用户就能直接使用配置好的域名或者 IP 地址来访问服务。
- ExternalName:这种类型的 Service 允许你将 Service 映射到外部服务。它通过返回 CNAME 和它的值,而不是通过维护单一的 IP 地址,实现对外部服务的访问。
总结
给一个简单的流程,描述如何从开发开始,到在 K8s 中部署运维。
- 开发应用:首先,开发者需要编写应用代码。这个应用可以是任何类型的程序,只要它能在 Docker 容器中运行。
- 创建 Dockerfile:接下来,开发者需要创建一个 Dockerfile 文件。这个文件包含了构建 Docker 镜像所需的指令,比如基础镜像、添加的文件、执行的命令等。
- 构建 Docker 镜像:在本地或 CI/CD 系统中运行
docker build
命令,按照 Dockerfile 的指示构建应用的 Docker 镜像。
- 推送 Docker 镜像:将构建好的 Docker 镜像推送到 Docker Registry。这个 Registry 可以是 Docker Hub、Google Container Registry (GCR)、Harbor 或公司内部的私有 Registry。
- 编写 Kubernetes 配置文件:创建一个或多个 YAML 或 JSON 文件,描述 Kubernetes 应该如何运行你的应用。这通常至少包括一个 Deployment 对象(用来保证 Pod 数量和更新)和一个 Service 对象(用来暴露网络服务)。你也可以选择使用其他 Kubernetes 资源对象,如 ConfigMap、Secret、Ingress 等。
- 部署到 Kubernetes:使用
kubectl apply
命令将你的配置文件部署到 Kubernetes 集群。Kubernetes 会读取这些配置文件,并根据其中的描述创建相应的资源。
- 验证部署:通过运行
kubectl get pods
、kubectl get services
等命令,检查你的应用是否正确部署并运行。
- 更新和维护:随着时间的推移,你可能需要更新应用或修复 bug。在这种情况下,你只需修改你的 Dockerfile 和 Kubernetes 配置文件,然后重复上述步骤即可。Kubernetes 的 Deployment 会处理滚动更新,无需停机即可升级你的应用。
关于 K8s 还有很多知识点,如服务发现、ConfigMap、statefulSet、Secret 等,篇幅有限,这里就不一一展开了,感兴趣大家可以去学习下。
Ref
《Kubernetes 修炼手册》