"The only way to do great work is to love what you do. If you haven't found it yet, keep looking. Don't settle. As with all matters of the heart, you'll know when you find it."

“成就一番伟业的唯一途径就是热爱自己的事业。如果你还没能找到让自己热爱的事业,继续寻找,不要放弃。跟随自己的心,总有一天你会找到的。”

—— Steven Paul Jobs,Stanford University,2005

本文章是为kubernetes初学者准备的入门文章,因为k8s核心概念繁多冗杂,所以主要讲解最核心的相关概念而屏蔽诸多的细节。主要参考k8s官方中文文档,与csdn中的优秀文章,部分内容由大模型分析生成。

本文章是为kubernetes初学者准备的入门文章,因为k8s核心概念繁多冗杂,所以主要讲解最核心的相关概念而屏蔽诸多的细节。主要参考k8s官方中文文档,与csdn中的优秀文章,部分内容由大模型分析生成。

1.介绍:

Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。K8s 这个缩写是因为 K 和 s 之间有 8 个字符的关系

它是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。

Google 在 2014 年开源了 Kubernetes 项目。 Kubernetes 建立在Google 大规模运行生产工作负载十几年经验的基础上, 结合了社区中最优秀的想法和实践。

容器是打包和运行应用程序的好方式。在生产环境中, 你需要管理运行着应用程序的容器,并确保服务不会下线。 例如,如果一个容器发生故障,则你需要启动另一个容器。 如果此行为交由给系统处理,是不是会更容易一些? Kubernetes 为你提供了一个可弹性运行分布式系统的框架。 Kubernetes 会满足你的扩展要求、故障转移你的应用、提供部署模式等。

k8s 本质上就是用来简化微服务的开发和部署的,关注点包括自愈和自动伸缩、调度和发布、调用链监控、配置管理、Metrics 监控、日志监控、弹性和容错、API 管理、服务安全等,k8s 将这些微服务的公共关注点以组件形式封装打包到 k8s 这个大平台中,让开发人员在开发微服务时专注于业务逻辑的实现,而不需要去特别关系微服务底层的这些公共关注点,大大简化了微服务应用的开发和部署,提高了开发效率。

2.架构:

kubernetes的架构图如下:

image-20240614155854159

2.1 控制平面:Control Plane:

控制平面由一系列组件构成,会为集群做出全局决策,比如资源的调度。 以及检测和响应集群事件,例如当不满足部署的 replicas 字段时,要启动新的Pod。

2.2 节点:node

节点是Kubernetes集群中的工作机器,可以是物理机或虚拟机。每个节点都运行着容器运行时(如Docker)、kubelet和kube-proxy。

kubernetes中并没有针对节点做分类,每个节点是什么类型取决于在这个节点上部署的组件,但是我们一般将控

制平面相关的组件单独的部署在一台或者多台机器上以便于拓展和管理。

这样就变成了经典的 master slave 架构模式,我们可以把kubernetes中的组件分为两类:主节点(master node),工作节点(worker node)。下面分别

介绍这两种节点以及其相关组件。

2.2.1 主节点:Master node:

主节点是Kubernetes集群的控制中心,负责管理集群的状态和运行。它负责调度决策、集群的全局状态管理以及与集群中所有节点的通信。

主节点上运行着以下5个关键的Kubernetes组件:

  1. kube-apiserver:集群的前端服务,是集群所有组件通信的入口点。

  2. etcd:一个轻量级、分布式键值存储系统,用于存储集群的所有数据。

  3. kube-scheduler:负责决定将新的Pods(容器的集合)调度到哪个节点上运行。

  4. kube-controller-manager:运行集群中的各种控制器,例如节点控制器、副本控制器等。

  5. cloud-controller-manager:如果集群运行在云平台上,这个组件会运行云平台特定的控制循环。

2.2.1.1 kube-apiserver: api请求入口

该组件负责公开了 Kubernetes API,负责处理接受请求的工作。

  • 这是Kubernetes API的服务器,作为集群的前端服务,提供集群管理的RESTful API接口。

  • 所有Kubernetes组件(包括kubectl命令行工具、调度器、控制器管理器等)都通过API服务器与etcd通信。

  • 它是集群所有组件通信的中心点,负责处理外部请求并代理到其他组件。

2.2.1.2 etcd:信息存储服务

一致且高可用的键值存储,用作 Kubernetes 所有集群数据的后台数据库。

Kubernetes使用etcd存储所有集群数据,包括Pods、服务(Services)、节点(Nodes)等信息。

2.2.1.3 kube-scheduler:调度器

kube-scheduler 负责监视新创建的、未指定运行节点(node)的 Pods, 并选择节点来让 Pod 在上面运行。

调度决策考虑的因素包括单个 Pod 及 Pods 集合的资源需求、软硬件及策略约束、 亲和性及反亲和性规范、数据位置、工作负载间的干扰及最后时限。

2.2.1.4 kube-controller-manager:控制器

控制器管理器运行集群中的各种控制器进程。控制器是Kubernetes中的后台进程,负责运行集群中的各种自动化

任务,例如节点故障检测、Pod副本管理等。

从逻辑上讲, 每个控制器都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在同一个进程中运行。

有许多不同类型的控制器。以下是一些例子:

  • 节点控制器(Node Controller):负责在节点出现故障时进行通知和响应

  • 任务控制器(Job Controller):监测代表一次性任务的 Job 对象,然后创建 Pod 来运行这些任务直至完成

  • 端点分片控制器(EndpointSlice controller):填充端点分片(EndpointSlice)对象(以提供 Service 和 Pod 之间的链接)。

  • 服务账号控制器(ServiceAccount controller):为新的命名空间创建默认的服务账号(ServiceAccount)。

2.2.1.5 cloud-controller-manager:云平台管理器

云控制器管理器(Cloud Controller Manager)允许将你的集群连接到云提供商的 API 之上, 并将与该云平台交互的组件同与你的集群交互的组件分离开来。

cloud-controller-manager 仅运行特定于云平台的控制器。 因此如果你在自己的环境中运行 Kubernetes,或者在本地计算机中运行学习环境, 所部署的集群不需要有云控制器管理器。

与 kube-controller-manager类似,cloud-controller-manager将若干逻辑上独立的控制回路组合到同一个可执行文件中, 供你以同一进程的方式运行。 你可以对其执行水平扩容(运行不止一个副本)以提升性能或者增强容错能力。

下面的控制器都包含对云平台驱动的依赖:

  • 节点控制器(Node Controller):用于在节点终止响应后检查云提供商以确定节点是否已被删除

  • 路由控制器(Route Controller):用于在底层云基础架构中设置路由

  • 服务控制器(Service Controller):用于创建、更新和删除云提供商负载均衡器

2.2.2 工作节点:Worker Node:

工作节点(Worker Node)在Kubernetes集群中负责运行容器化的应用。

工作节点的主要任务是执行主节点的指令,比如启动、停止容器,以及根据主节点的调度决策来运行应用。

工作节点上运行着以下3个关键的Kubernetes组件:

  1. kubelet:在每个节点上运行,负责维护容器的生命周期,如启动容器、监控容器健康状态等。

  2. kube-proxy:在每个节点上运行,负责网络代理,确保集群内部的网络通信。

  3. Container Runtime:负责实际运行容器。可以是Docker或者其他任何容器运行时。

2.2.2.1 kubelet:工作代理:

Kubelet是Kubernetes集群中每个节点的代理,负责维护容器的生命周期。它与主节点上的API Server通信,并执行以下主要任务:

  1. 容器生命周期管理:Kubelet接收来自API Server的指令,启动、停止、更新或删除容器。

  2. 健康检查:Kubelet定期对容器进行健康检查,以确保它们按照预期运行。如果容器失败,Kubelet会根据配置尝试重启容器。

  3. 资源管理:Kubelet监控节点的资源使用情况,如CPU和内存,并确保容器不会超出其分配的资源限制。

  4. 容器镜像管理:Kubelet负责拉取容器镜像,并在需要时更新它们。

  5. 数据卷管理:Kubelet挂载Pods所需的数据卷,包括持久化卷和配置映射。

  6. 节点状态报告:Kubelet定期向API Server报告节点的状态,包括资源使用情况、容器状态和节点健康状况。

2.2.2.2 Kube-proxy:网络代理:

是Kubernetes集群中的网络代理,负责在集群内部和外部之间路由网络流量。它在每个节点上运行,并执行以下主要任务:

  1. 服务发现:Kube-proxy监听API Server,以了解服务和端点的变化,并相应地更新其路由规则。

  2. 负载均衡:Kube-proxy在多个Pods之间分配进入的网络流量,确保服务请求被均匀地分发到后端的Pods。

  3. 网络策略实施:Kube-proxy根据定义的网络策略规则,控制Pods之间的网络通信。

  4. IPTABLES规则管理:Kube-proxy使用IPTABLES规则来管理网络流量,确保流量正确地路由到目标Pods。

  5. 服务和端点同步:Kube-proxy确保其内部的路由表与API Server中定义的服务和端点保持同步。

  6. 连接跟踪:Kube-proxy跟踪每个连接的状态,以支持连接的持久化和正确的负载均衡。

Kubelet和kube-proxy共同工作,确保Kubernetes集群的节点能够高效地运行和管理容器,同时提供稳定和可预测的网络通信。通过这些组件,Kubernetes能够实现其核心功能,如服务发现、负载均衡和自动扩展。

2.2.2.3 Container Runtime:容器运行时:

容器运行时负责运行容器、管理容器生命周期和提供容器隔离环境。

Kubernetes支持多种容器运行时,包括Docker、containerd、CRI-O等。

任何满足Kubernetes CRI (容器运行环境接口)的容器运行时工具都可以被接受。

2.2.2.3.1 CRI:

在 Kubernetes 中,CRI 是指容器运行时接口(Container Runtime Interface)。CRI 是一个插件接口,用于使 Kubernetes Kubelet 与不同的容器运行时(如 Docker、containerd、CRI-O 等)进行交互。它定义了一组标准化的 API,使 Kubernetes 可以管理和运行容器,而不必依赖特定的容器运行时实现。

组成部分:

1.gRPC API:CRI 使用 gRPC 定义其 API。gRPC 是一种高性能、开源和通用的 RPC 框架,便于不同语言之间的通信。

2.Protocol Buffers:gRPC API 使用 Protocol Buffers(protobuf)作为其接口定义语言(IDL)。这使得接口既高效又便于版本控制。

组件:

Container Runtime:实际运行和管理容器的底层软件,如 Docker、containerd 和 CRI-O。

Image Service:负责拉取、管理和删除容器镜像。

Container Manager:负责容器的生命周期管理,包括创建、启动、停止和删除容器。

主要 CRI 实现:

1.Docker Shim:最初的实现,允许 Kubelet 通过 dockershim 直接与 Docker 容器运行时交互。已经逐步淘汰。

2.containerd:一个高性能的容器运行时,专为 Kubernetes 设计。

3.CRI-O:一个轻量级的容器运行时,专为 Kubernetes 环境设计,完全实现了 CRI。

作用:

解耦:通过定义标准接口,Kubernetes 可以与多种容器运行时兼容,使其更具灵活性和可扩展性。

简化集成:开发者只需实现 CRI 接口,就可以将新的容器运行时集成到 Kubernetes 中。

总的来说,CRI 是 Kubernetes 体系结构中的一个重要组成部分,它使得 Kubernetes 能够灵活地支持多种容器运行时,实现容器的标准化管理和操作。

2.3 工作流程:

工作流程图如下:

image-20240614164723471

Kubernetes的工作流程通常如下:

  • 用户通过Kubernetes API与集群交互,创建或更新资源对象,如Pods、Services等。

  • API Server接收请求,并与etcd通信,更新集群状态。

  • Scheduler根据资源需求和集群的当前状态,决定将Pods调度到哪个节点。

  • Controller Manager监控集群状态,确保实际状态与期望状态一致。

  • Kubelet在各个节点上根据调度结果创建或删除Pods,并通过CRI与容器运行时进行交互。

  • Kube-proxy在每个节点上配置网络规则,确保Pods可以被访问。

整个流程是自动化的,Kubernetes会持续监控集群状态,并自动进行必要的调整以维持系统的稳定性和可用性。

2.3.1 CRI工作原理:

Kubelet 是 Kubernetes 中运行在每个节点上的核心组件,负责管理该节点上的容器。Kubelet 与容器运行时之间的交互通过容器运行时接口(CRI)实现,确保 Kubelet 可以控制和监控容器的生命周期。下面是 Kubelet 与容器运行时之间交互的详细说明:

CRI 的工作原理

1. Kubelet 调用 CRI API:当需要执行容器管理操作(如启动、停止容器)时,Kubelet 会调用 CRI 提供的 gRPC API。这些操作包括创建容器、运行容器、停止容器、拉取镜像等。

2. 容器运行时实现 CRI:容器运行时(如 containerd、CRI-O 或 cri-dockerd)实现了 CRI API,并提供相应的 gRPC 服务器。这些运行时监听特定的 Unix 套接字(例如 /var/run/containerd/containerd.sock)。

3. 通过 Unix 套接字通信:Kubelet 与容器运行时之间通过本地 Unix 套接字进行通信。这些套接字通常位于 /var/run 目录下,例如 /var/run/containerd/containerd.sock。

4. 处理请求:容器运行时接收到 Kubelet 的请求后,执行相应的操作,例如创建一个新的容器、拉取镜像或获取容器状态,并将结果返回给 Kubelet。

交互过程示例

以下是一个具体的交互过程示例:

1.启动容器

Kubelet 发起请求:Kubelet 调用 RunPodSandbox 和 CreateContainer gRPC 接口,向容器运行时发送请求,要求启动一个新容器。

容器运行时执行操作:容器运行时接收请求后,拉取所需的镜像,并创建和启动容器。

返回结果:操作完成后,容器运行时将结果返回给 Kubelet。

2.获取容器状态

Kubelet 发起请求:Kubelet 调用 ListContainers 或 ContainerStatus gRPC 接口,向容器运行时发送请求,获取当前运行的容器列表或某个特定容器的状态。

容器运行时返回状态:容器运行时查询容器状态,并将结果返回给 Kubelet。

关键组件

Kubelet:Kubernetes 中的节点代理,负责与容器运行时通信并管理容器的生命周期。

容器运行时:实现 CRI 的组件,例如 containerd、CRI-O、cri-dockerd 等,负责实际的容器管理操作。

Unix 套接字:用于本地进程间通信(IPC),Kubelet 和容器运行时通过 Unix 套接字进行 gRPC 通信。

3.概念:

3.1 对象:

在 Kubernetes 系统中,Kubernetes 对象是持久化的实体。 Kubernetes 使用这些实体去表示整个集群的状态。

Kubernetes 对象是一种“意向表达(Record of Intent)”。一旦创建该对象, Kubernetes 系统将不断工作以确保该对象存在。通过创建对象,你本质上是在告知 Kubernetes 系统,你想要的集群工作负载状态看起来应是什么样子的, 这就是 Kubernetes 集群所谓的期望状态(Desired State)。

操作 Kubernetes 对象 —— 无论是创建、修改或者删除 —— 需要使用 Kubernetes API。 比如,当使用 kubectl 命令行接口(CLI)时,CLI 会调用必要的 Kubernetes API; 也可以在程序中使用客户端库, 来直接调用 Kubernetes API。

每个 Kubernetes 对象包含两个嵌套的对象字段,它们负责管理对象的配置: spec(规约)和对象 status(状态)。

3.1.1 spec:

从面向对象程序设计的角度,spec可以理解为对象的属性。

spec是Kubernetes对象的规范或声明性配置,它描述了用户希望集群如何运行和管理该对象。spec是不可变的,意味着一旦创建,用户不能直接修改它。但是,可以通过更新对象来更改其spec,这会导致Kubernetes创建一个新版本的资源对象。以下是spec的一些关键特性:

  • 声明性:用户只需声明期望的状态,Kubernetes负责实现这一状态。

  • 不可变性:直接修改spec是不可能的,需要通过更新操作来实现。

  • 版本控制:每次更新spec,Kubernetes都会创建一个新的对象版本,这有助于追踪变更历史。

例如,一个Pod的spec可能包括以下信息:

  • 容器的镜像名称和版本。

  • 容器所需的CPU和内存资源限制。

  • 环境变量。

  • 容器的端口映射。

3.1.2 status:

status是Kubernetes对象的当前运行状态,由Kubernetes系统根据对象的实际运行情况自动更新。status提供了对象的实时信息,如是否正在运行、是否就绪、以及任何错误或警告。以下是status的一些关键特性:

  • 实时性:status字段由Kubernetes系统根据对象的实际运行状态实时更新。

  • 只读:用户不能直接修改status,它由系统自动维护。

  • 详细性:status可能包含多种信息,如对象的健康状态、条件、资源使用情况等。

例如,一个Pod的status可能包括:

  • Pod的当前阶段(如Pending、Running、Succeeded、Failed、Unknown)。

  • 容器的重启次数。

  • Pod的IP地址。

  • 条件(如Ready、Initialized)。

通过分离spec和status,Kubernetes提供了一种强大的方式来管理集群资源,允许用户专注于定义期望状态,而由系统负责实现和管理这些状态。这种模式简化了集群管理,并提高了自动化和可扩展性。

3.1.3 对象的描述:清单:

在Kubernetes中,描述一个对象通常使用YAML或JSON格式的清单文件(manifest file)。这些清单文件定义了Kubernetes资源对象的期望状态,包括它们的配置、行为和依赖关系。以下是描述Kubernetes对象的基本步骤和组成部分:

  1. API版本(apiVersion)

    • 指定Kubernetes API的版本,这决定了清单文件的结构和可用的字段。

  2. 类型(Kind)

    • 指定要创建的Kubernetes对象的类型,例如Pod、Service、Deployment等。

  3. 元数据(metadata)

    • 包含对象的识别信息,如名称(name)、命名空间(namespace)、标签(labels)和注解(annotations)。

  4. 规约(spec)

    • 描述了对象的期望状态和行为。这是用户定义的部分,Kubernetes将根据这些规约来创建和维护对象。对每个 Kubernetes 对象而言,其 spec 之精确格式都是不同的,包含了特定于该对象的嵌套字段。

状态(status):通常不需要在清单文件中指定,因为这是Kubernetes系统根据对象的实际运行情况自动更新的。

以下是一个简单的Pod清单示例,使用YAML格式:

apiVersion: v1  # 指定API版本
kind: Pod        # 指定资源类型为Pod
metadata:
  name: mypod    # Pod的名称
  namespace: default  # Pod所在的命名空间
  labels:
    app: myapp  # 用于标识Pod的标签
spec:
  containers:  # 定义容器列表
  - name: mycontainer  # 容器的名称
    image: nginx:1.14.2  # 容器使用的镜像
    ports:
    - containerPort: 80  # 容器内部监听的端口

以下是一个简单的Deployment清单示例子,同样使用YAML格式:

apiVersion: apps/v1 #指定API版本
kind: Deployment  #指定资源类型为Deployment
metadata:			
  name: nginx-deployment 	#Deployment的名称
spec:
  selector:   # 选择器,用于确定Deployment控制哪些Pods
    matchLabels:
      app: nginx  # 选择具有app: nginx标签的Pods。
  replicas: 2 # 告知 Deployment 运行 2 个与该模板匹配的 Pod
  template:   # 一个Pod模板,定义了Deployment创建Pods时使用的规格
    metadata: # 包含Pod的元数据,如labels。这里的labels用于与Deployment的selector匹配。
      labels:
        app: nginx  # 用于标识Pod的标签
    spec:
      containers:  # 定义容器列表
      - name: nginx # 容器的名称
        image: nginx:1.14.2 # 容器使用的镜像
        ports:
        - containerPort: 80 # 容器内部监听的端口

3.1.4 对象的控制:控制器:

在机器人技术和自动化领域,控制回路(Control Loop)是一个非终止回路,用于调节系统状态。

这是一个控制环的例子:房间里的温度自动调节器。

当你设置了温度,告诉了温度自动调节器你的期望状态(Desired State)。 房间的实际温度是当前状态(Current State)。 通过对设备的开关控制,温度自动调节器让其当前状态接近期望状态。

在 Kubernetes 中,控制器通过监控集群的公共状态,并致力于将当前状态转变为期望的状态。

一个控制器至少追踪一种类型的 Kubernetes 资源。这些对象有一个代表期望状态的 spec 字段。 该资源的控制器负责确保其当前状态接近期望状态。

如前文架构中所说,控制器部署在控制平面中的Controller Manager中。

Controller Manager会运行多个不同类型的Controller,这些Controller是逻辑上的,不是每个Node节点上都有独立的实例。它们通过API Server与Kubernetes集群通信,获取资源对象的状态信息,并根据这些信息执行操作。

在Kubernetes中,不是所有的对象都由特定的Controller来控制,但许多核心对象确实有与之对应的Controller来管理其生命周期和行为。

以下是一些常见的Kubernetes对象和它们对应的Controller:

  1. Pod

    • Pod本身不是由Controller直接控制,但Pod的副本和更新通常由其他对象管理,如Deployment、StatefulSet、DaemonSet等。

  2. Deployment

    • Deployment对象由Deployment Controller管理,它负责Pod的声明式更新和副本的维护。

  3. StatefulSet

    • StatefulSet对象由StatefulSet Controller管理,它确保有状态应用的顺序性、唯一性和持久性。

  4. DaemonSet

    • DaemonSet对象由DaemonSet Controller管理,它确保所有符合条件的节点上都运行指定数量的Pod副本。

  5. ReplicaSet

    • ReplicaSet对象由ReplicaSet Controller管理,它确保Pod副本的数量始终符合期望值。Deployment对象内部使用ReplicaSet来管理Pod副本。

  6. Namespace

    • Namespace对象通常不由特定的Controller管理,但Namespace的某些清理工作可能由Controller Manager中的某些进程处理。

  7. Service

    • Service对象本身不由特定的Controller管理,但Service的网络特性由kube-proxy负责实现。

  8. Ingress

    • Ingress对象由Ingress Controller管理,如Nginx Ingress Controller,它负责实现基于HTTP的路由规则。

  9. Horizontal Pod Autoscaler (HPA)

    • HPA对象由HPA Controller管理,它根据CPU使用率或其他度量指标自动调整Pod副本的数量。

  10. Custom Resource Definitions (CRDs)

    • 自定义资源可以由用户定义的Operator或Controller管理,这些Operator或Controller根据CRD的规范执行特定的自动化任务。

3.2 Pod:

Pod可以翻译为豌豆荚,是一组(一个或多个) 容器,它是可以在 Kubernetes 中创建和管理的、最小的可部署的

计算单元。

这些容器共享存储、网络、以及怎样运行这些容器的声明。

Pod 中的内容总是并置(colocated)的并且一同调度,在共享的上下文中运行。 Pod 所建模的是特定于应用的 “逻辑主机”,其中包含一个或多个应用容器, 这些容器相对紧密地耦合在一起。 在非云环境中,在相同的物理机或虚拟机上运行的应用类似于在同一逻辑主机上运行的云应用。

Pod 的共享上下文包括一组 Linux 名字空间、控制组(cgroup)和可能一些其他的隔离方面, 即用来隔离容器的

技术。 在 Pod 的上下文中,每个独立的应用可能会进一步实施隔离。

3.2.1 创建:

如我们上文所提到的,要使用(部署)一个对象,首先要编写其对象描述清单:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

然后使用命令创建此清单:

kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml

这样你就拥有了一个运行着nginx容器的pod。

3.2.2 使用:

通常你不需要直接创建 Pod,甚至单实例 Pod。相反,你会使用诸如Deployment 或 Job这类工作负载资源来创建 Pod。 如果 Pod 需要跟踪状态,可以考虑StatefulSet资源。

你很少在 Kubernetes 中直接创建一个个的 Pod,甚至是单实例(Singleton)的 Pod。 这是因为 Pod 被设计成了相对临时性的、用后即抛的一次性实体。 当 Pod 由你或者间接地由控制器创建时,它被调度在集群中的节点上运行。 Pod 会保持在该节点上运行,直到 Pod 结束执行、Pod 对象被删除、Pod 因资源不足而被驱逐或者节点失效为止。

3.2.3 模版:

通常使用 Pod 模板(Pod Template)来替你创建 Pod 并管理它们。

一个带有模版的Pod清单如下:

apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  template:
    # 这里是 Pod 模板
    spec:
      containers:
      - name: hello
        image: busybox:1.28
        command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
      restartPolicy: OnFailure
    # 以上为 Pod 模板

修改 Pod 模板或者切换到新的 Pod 模板都不会对已经存在的 Pod 直接起作用。 如果改变工作负载资源的 Pod 模板,工作负载资源需要使用更新后的模板来创建 Pod, 并使用新创建的 Pod 替换旧的 Pod。

3.2.4 存储:

一个 Pod 可以设置一组共享的存储卷。 Pod 中的所有容器都可以访问该共享卷,从而允许这些容器共享数据。

卷还允许 Pod 中的持久数据保留下来,即使其中的容器需要重新启动。

3.2.5 网络:

每个 Pod 都在每个地址族中获得一个唯一的 IP 地址。 Pod 中的每个容器共享网络名字空间,包括 IP 地址和网络端口。

Pod 内的容器可以使用 localhost 互相通信。不同 Pod 中的容器的 IP 地址互不相同,如果没有特殊配置,就无法通过 OS 级 IPC 进行通信。

当 Pod 中的容器与Pod 之外的实体通信时,它们必须协调如何使用共享的网络资源(例如端口)。

3.3 工作负载:

最终,应用以容器的形式在Pods中运行; 但是,直接管理单个 Pod 的工作量将会非常繁琐。例如,如果一个 Pod 失败了,你可能希望运行一个新的 Pod 来替换它。Kubernetes 可以为你完成这些操作。

可以使用 Kubernetes API 创建工作负载对象, 这些对象所表达的是比 Pod 更高级别的抽象概念,Kubernetes 控制平面根据你定义的工作负载对象规约自动管理 Pod 对象。

与工作负载相关的核心对象主要有下面这几个,接下来一一介绍。

3.3.1 ReplicaSet:

ReplicaSet 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合。 因此,它通常用来保证给定数量的、完全相同的 Pod 的可用性。

ReplicaSet 是通过一组字段来定义的,包括一个用来识别可获得的 Pod 的集合的选择算符、一个用来标明应该维护的副本个数的数值、一个用来指定应该创建新 Pod 以满足副本个数条件时要使用的 Pod 模板等等。 每个 ReplicaSet 都通过根据需要创建和删除 Pod 以使得副本个数达到期望值, 进而实现其存在价值。当 ReplicaSet 需要创建新的 Pod 时,会使用所提供的 Pod 模板。

ReplicaSet是Deployment的底层实现。

清单如下:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # 按你的实际情况修改副本数
  replicas: 3
  selector:  # 定义了如何匹配Pod,这里使用了matchLabels来选择具有tier: frontend标签的Pod。
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3

3.3.2 Deployments:

Deployment 为Pod和ReplicaSet提供声明式的更新能力。

清单如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

3.3.3 StatefulSet:

StatefulSet 是用来管理有状态应用的工作负载 API 对象。

StatefulSet 用来管理某Pod集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。

如果希望使用存储卷为工作负载提供持久存储,可以使用 StatefulSet 作为解决方案的一部分。 尽管 StatefulSet 中的单个 Pod 仍可能出现故障, 但持久的 Pod 标识符使得将现有卷与替换已失败 Pod 的新 Pod 相匹配变得更加容易。

清单如下:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # 必须匹配 .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # 默认值是 1
  minReadySeconds: 10 # 默认值是 0
  template:
    metadata:
      labels:
        app: nginx # 必须匹配 .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi

3.3.4 DaemonSet:

DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。

DaemonSet 的一些典型用法:

  • 在每个节点上运行集群守护进程

  • 在每个节点上运行日志收集守护进程

  • 在每个节点上运行监控守护进程

清单如下:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # 这些容忍度设置是为了让该守护进程集在控制平面节点上运行
      # 如果你不希望自己的控制平面节点运行 Pod,可以删除它们
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
      # 可能需要设置较高的优先级类以确保 DaemonSet Pod 可以抢占正在运行的 Pod
      # priorityClassName: important
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log

3.4 网络:

3.4.1 模型概述:

16700771818249

Kubernetes网络模型设计的一个基础原则是:每个Pod都拥有一个独立的IP地址,并假定所有Pod都在一个可以直接连通的、扁平的网络空间中。所以不管这些Pod是否运行在同一个Node中,都要求它们可以直接通过对方的IP进行访问。

IP-per-Pod模型是一个简单的网络模型。从该模型的网络的端口分配、域名解析、服务发现、负载均衡、应用配置和 迁移等角度来看,Pod都能够被看作一台独立的虚拟机或物理机。按照这个网络抽象原则,所有Pod都可以在不用NAT的方式下同别的Pod通信

与网络模型相关的核心资源主要有两个:Service和Ingress.

3.4.2 Service:

Service是标准k8s资源,本质是 节点 上的iptable或ipvs规则,利用规则实现流量的转发到pod上,且也基于selector标签选择器选择、标识后端pod;

本质上,Servcice是一种四层负载均衡的抽象。

四层负载均衡示例:

假设有一个L4负载均衡器在TCP层进行负载均衡。客户端请求 http://example.com,负载均衡器根据源IP地址将请求分发到后端服务器1或服务器2。

客户端 -> 负载均衡器 (检查IP和端口) -> 服务器1或服务器2

如果你使用Deployment来运行你的应用, Deployment 可以动态地创建和销毁 Pod。 在任何时刻,你都不知道有多少个这样的 Pod 正在工作以及它们健康与否; 你可能甚至不知道如何辨别健康的 Pod。 Kubernetes Pod的创建和销毁是为了匹配集群的预期状态。 Pod 是临时资源(你不应该期待单个 Pod 既可靠又耐用)。

每个 Pod 会获得属于自己的 IP 地址(Kubernetes 期待网络插件来保证这一点)。 对于集群中给定的某个 Deployment,这一刻运行的 Pod 集合可能不同于下一刻运行该应用的 Pod 集合。

这就带来了一个问题:如果某组 Pod(称为“后端”)为集群内的其他 Pod(称为“前端”) 集合提供功能,前端要如何发现并跟踪要连接的 IP 地址,以便其使用负载的后端组件呢?

这时候就要使用Service对象, 它是将运行在一个或一组Pod上的网络应用程序公开为网络服务的方法。

3.4.2.1 创建:

资源清单如下:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376

应用上述清单时,系统将创建一个名为 "my-service" 的、 服务类型默认为 ClusterIP 的 Service。 该 Service 指向带有标签 app.kubernetes.io/name: MyApp 的所有 Pod 的 TCP 端口 9376。

3.4.2.2 类型:

Service有几种不同的类型,每种类型提供了不同的访问策略和使用场景。以下是Kubernetes中Service的主要类型:

  1. ClusterIP

    • 这是默认的Service类型。

    • 它为Service提供一个仅在Kubernetes集群内部可访问的虚拟IP地址(ClusterIP)。

    • 适合用于集群内部的服务发现和通信。

  2. NodePort

    • NodePort类型的Service在每个节点的特定端口(NodePort)上暴露Service。

    • 通过<nodeIP>:<NodePort>可以从集群外部访问Service。

    • 适合用于测试或当需要从外部网络访问内部服务时。

  3. LoadBalancer

    • 这种类型的Service使用云提供商的负载均衡器,为Service提供一个外部可访问的IP地址。

    • 适合于公开暴露服务到互联网,例如Web应用。

  4. ExternalName

    • ExternalName类型的Service不路由流量,也不维护任何后端Pod的列表。

    • 它将Service名称映射到一个CNAME记录或外部DNS名称,允许Kubernetes集群内的客户端通过Service名称访问外部服务。

  5. Headless Service

    • 这是一种特殊类型的ClusterIP Service,没有指定ClusterIP。

    • 它允许直接通过Service名称和DNS服务发现Pod的IP地址,而不需要通过负载均衡。

    • 适合于不需要负载均衡的简单服务发现场景。

3.4.2.3 使用:

举个例子说明service的使用。

假设我们有一个简单的web应用,它由两个微服务组成:一个前端服务和一个后端服务。我们将使用Kubernetes Service来使这些服务对内部和外部都可访问。

他们的deployments部署清单如下:

前端:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: nginx
        ports:
        - containerPort: 80

后端:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: nginx
        ports:
        - containerPort: 80

他们的service资源如下:

前端:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: nginx
        ports:
        - containerPort: 80

后端:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: nginx
        ports:
        - containerPort: 80

当我们访问的时候,由于frontend-service的类型是LoadBalancer,云提供商会为这个Service分配一个外部可访问的IP地址或域名。你可以通过这个IP或域名在浏览器中访问前端服务。

backend-service的类型是ClusterIP,这意味着它只能在集群内部访问。前端服务可以通过backend-service的名称和端口(8080)来访问后端服务,例如,如果前端服务需要调用后端服务的API,它可以通过http://backend-service:8080/api来访问。

3.4.2.4 实现:

Service的底层逻辑确实涉及到选择Pod并根据访问请求进行负载均衡,以下是Service的工作原理的详细解释:

  1. 选择Pod:Service定义了一个选择器(selector),这个选择器用于匹配一组具有特定标签(labels)的Pod。Service负责监控这些Pod的状态,如果Pod被添加或移除,Service会自动更新其内部的Pod列表。

  2. IP地址:每个Service在创建时会被分配一个唯一的虚拟IP地址(ClusterIP),这个IP地址是内部的,只在Kubernetes集群内部可用。Pod通过这个IP地址和Service定义的端口来通信。

  3. DNS解析:Kubernetes集群内部有一个DNS服务,它允许Pod通过Service的名称来解析对应的ClusterIP。当Pod尝试连接到Service时,集群的DNS服务会将Service名称解析为Service的ClusterIP。

  4. 负载均衡:Kubernetes使用一种称为kube-proxy的组件来实现Service的负载均衡。kube-proxy在每个节点上运行,负责将发往Service的流量转发到后端的Pod。

  5. kube-proxy工作原理

    • kube-proxy监控Service和Endpoints资源的变化。

    • 当Service或Endpoints(包含实际Pod IP和端口信息)发生变化时,kube-proxy会更新其内部的路由规则。

    • 当有流量到达Service的ClusterIP和端口时,kube-proxy会根据其内部的路由规则,选择一个Pod进行流量转发。

    • kube-proxy使用随机选择、轮询、最小连接数等策略来进行负载均衡。

  6. Service类型

    • ClusterIP:默认类型,Service仅在集群内部可访问。

    • NodePort:在每个节点上打开一个端口,允许外部流量通过任意节点的这个端口访问Service。

    • LoadBalancer:通过云提供商的负载均衡器,为Service提供一个外部可访问的IP地址。

    • ExternalName:将服务映射到一个外部的服务,不涉及IP地址的分配。

  7. 访问Service:无论是从集群内部还是外部访问Service,客户端都不需要知道后端Pod的具体IP地址。它们只需要知道Service的名称和端口,然后通过Kubernetes的DNS服务或外部负载均衡器来访问Service。

3.4.3 Ingress:

本质上,Ingress是一种七层负载均衡的抽象。

假设有一个L7负载均衡器在HTTP层进行负载均衡。客户端请求 http://example.com/api/v1 或 http://example.com/images,负载均衡器根据URL路径将请求分发到不同的后端服务器组。

请求 http://example.com/api/v1 -> 负载均衡器 (检查URL路径) -> API服务器

请求 http://example.com/images -> 负载均衡器 (检查URL路径) -> 图像服务器

Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。

下面是 Ingress 的一个简单示例,可将所有流量都发送到同一 Service:

image-20240618170833135

3.4.3.1 部署:

清单如下:

# 指定 Kubernetes API 的版本
apiVersion: networking.k8s.io/v1
​
# 指定资源的类型
kind: Ingress
​
# 包含资源的元数据
metadata:
  # 资源的名称
  name: minimal-ingress
  # 包含额外的配置注解
  annotations:
    # 用于 Nginx Ingress 控制器的重写目标路径配置
    nginx.ingress.kubernetes.io/rewrite-target: /
​
# 指定 Ingress 的规范
spec:
  # 指定 Ingress 控制器的名称
  ingressClassName: nginx-example
​
  # 定义路由规则
  rules:
  - http:
      # 定义 HTTP 协议的路由规则
      paths:
      - path: /testpath
        # 路径匹配类型,Prefix 表示匹配以 /testpath 开头的路径
        pathType: Prefix
        backend:
          # 定义请求的后端服务
          service:
            # 服务的名称
            name: test
            # 服务的端口号
            port:
              number: 80

这个配置文件可以用来设置一个简单的 Ingress 规则,使得所有访问到 /testpath 的 HTTP 请求都会被转发到名为 test 的服务的端口 80 上,并且请求的路径会被重写为 /

3.4.3.2 控制器:

为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。

为了使Ingress工作,集群中需要有支持Ingress资源的对象,通常是Ingress Controller,如Nginx Ingress Controller或类似的组件。此外,Ingress资源的配置可能会依赖于特定的Ingress Controller和它的注解。

与作为 kube-controller-manager 可执行文件的一部分运行的其他类型的控制器不同, Ingress 控制器不是随集群自动启动的。可选择最适合你的集群的 ingress 控制器实现。目前支持和维护AWS。GCE和NginxIngress 控制器。

3.4.4 实现:

3.4.4.1 linux网络模型:

通常,我们将虚拟机中的网络通信视为直接与以太网设备交互。

在 Linux 中,每个正在运行的进程都在一个网络命名空间内进行通信,该命名空间为逻辑网络堆栈提供了自己的路由、防火墙规则和网络设备。本质上,网络命名空间为命名空间内的所有进程提供了一个全新的网络堆栈。作为 Linux 用户,可以使用 ip 命令创建网络命名空间。

image-20240627172737453

默认情况下,Linux 将每个进程分配给根网络命名空间以提供对外部世界的访问。

image-20240627172842539

3.4.4.1.1 eth:

th0 是在类 Unix 操作系统(如 Linux 和 BSD)中常见的网络接口名称,表示第一个以太网网络接口。网络接口是连接计算机与网络的设备或端口,负责处理计算机与外部网络的通信。eth0 通常用于有线以太网连接,而在同一系统上,第二个以太网接口通常被命名为 eth1。

3.4.4.1.2 veth:

veth(Virtual Ethernet)是虚拟网络接口的一种,用于在网络命名空间之间创建虚拟网络连接。veth 设备成对出现,类似于一个虚拟网线的两端,一个接口的数据包会出现在另一个接口上。

3.4.4.2 同一pod内不同容器通信:

k8s创建Pod时永远都是首先创建Infra 容器,也可以被称为pause容器。这个容器为其他容器提供了一个共享的基础设施,包括网络和存储功能,其他业务容器共享pause容器的网络栈和Volume挂载卷。

pause 容器被创建后会初始化Network Namespace网络栈,之后其他容器就可以加入到pause 容器中共享Infra容器的网络了。而对于同一个 Pod 里面的所有用户容器来说,它们的进出流量,认为都是通过 pause 容器完成的。

pause 容器会创建并管理虚拟以太网(veth)接口。在容器启动之前,pause 容器会为每个容器创建一个虚拟以太网接口,一个保留在宿主机上(称为 vethxxx),另一个保留在容器网络命名空间内并重命名为 eth0,如下图所示。这两个虚拟接口的两端是连接在一起的,从一端进入的数据会从另一端出来。 image-20240627173236588

所以,在Kubernetes中,同一个Pod内的容器间通信就如同在同一台机器上,甚至可以用localhost地址访问彼此的端口。

3.4.4.3 同一节点,不同Pod之间的通信:

Kubernetes为每个Pod都分配了唯一的IP地址,称之为Pod IP,这个IP也是Pause容器的IP。

同一节点,不同Pod之间的通信是通过根命名空间上的网桥实现的。

Linux 以太网网桥是一个虚拟的第 2 层网络设备,用于联合两个或多个网段,透明地工作以将两个网络连接在一起。网桥通过检查通过它的数据包的目的地并决定是否将数据包传递到连接到网桥的其他网段来维护源和目标之间的转发表来运行。桥接代码通过查看网络中每个以太网设备的唯一 MAC 地址来决定是桥接数据还是丢弃数据。

image-20240627173645058

3.4.4.4 不同节点,不同pod之间的通信:

Kubernetes 网络模型要求 Pod IP 可以通过网络访问,但它没有指定必须如何完成。所以,不同网络插件的处理方式不同

通常,集群中的每个节点都分配有一个 CIDR 块,指定该节点上运行的 Pod 可用的 IP 地址。一旦流向 CIDR 块的流量到达节点,节点就有责任将流量转发到正确的 Pod。图 7 说明了两个节点之间的流量流,假设网络可以将 CIDR 块中的流量路由到正确的节点。

前半部分的流程与同节点 pod 间通信类似,当请求到达网桥,网桥询问哪个 pod 拥有该 IP 但是没有得到回应。流程进入主机的路由寻址过程,到更高的集群层面。

在集群层面有一张路由表,里面存储着每个节点的 Pod IP 网段(节点加入到集群时会分配一个 Pod 网段(Pod CIDR),比如在 k3s 中默认的 Pod CIDR 是 10.42.0.0/16,节点获取到的网段是 10.42.0.0/2410.42.1.0/2410.42.2.0/24,依次类推)。通过节点的 Pod IP 网段可以判断出请求 IP 的节点,然后请求被发送到该节点。

image-20240627173855431

图 7 以与图 6 相同的请求开始,但这次,目标 Pod(以绿色突出显示)与源 Pod(以蓝色突出显示)位于不同的节点上。数据包首先通过 Pod 1 的以太网设备发送,该设备与根命名空间 (1) 中的虚拟以太网设备配对。最终,数据包最终到达根命名空间的网桥 (2)。ARP 将在网桥上失败,因为没有设备连接到网桥并具有正确的数据包 MAC 地址。失败时,网桥将数据包发送到默认路由——根命名空间的 eth0 设备。此时路由离开节点并进入网络 (3)。我们现在假设网络可以根据分配给节点的 CIDR 块将数据包路由到正确的节点 (4)。数据包进入目标节点的根命名空间(VM 2 上的 eth0),在那里它通过网桥路由到正确的虚拟以太网设备 (5)。最后,路由通过位于 Pod 4 的命名空间 (6) 中的虚拟以太网设备对来完成。一般来说,每个节点都知道如何将数据包传递给在其中运行的 Pod。一旦数据包到达目标节点,数据包的流动方式与在同一节点上的 Pod 之间路由流量的方式相同。

可以简单总结为:

1.发送到根命名空间,在根命名空间里找

2.没找到,进入网络

3.根据网络转发的实现找到目标pod

4.转换为同一节点之间的情况

3.4.4.5 pod与service之间的网络通信:

Pod IP 地址不是持久的,并且会随着扩展或缩减、应用程序崩溃或节点重启而出现和消失。这些事件中的每一个都可以使 Pod IP 地址在没有警告的情况下更改。Service被内置到 Kubernetes 中来解决这个问题。

Service充当对 Pod 的抽象,并将单个虚拟 IP 地址分配给一组 Pod IP 地址。任何发往 Service 虚拟 IP 的流量都将被转发到与虚拟 IP 关联的 Pod 集。这允许与 Service 关联的 Pod 集随时更改——客户端只需要知道 Service 的虚拟 IP即可,它不会更改。

创建新的 Kubernetes Service时,会为您创建一个新的虚拟 IP(也称为集群 IP)。在集群中的任何地方,发往虚拟 IP 的流量都将负载均衡到与服务关联的一组支持 Pod。实际上,Kubernetes 会自动创建并维护一个分布式集群内负载均衡器,将流量分配到服务相关联的健康 Pod。

3.4.4.5.1 netfilter:

为了在集群中执行负载平衡,Kubernetes 依赖于 Linux 内置的网络框架——netfilter。Netfilter 是 Linux 提供的一个框架,它允许以自定义处理程序的形式实现各种与网络相关的操作。

3.4.4.5.2 iptables:

iptables 是一个用户空间程序,它提供了一个基于表的系统,用于定义使用 netfilter 框架操作和转换数据包的规则。

在 Kubernetes 中,iptables 规则由 kube-proxy 控制器配置,该控制器监视 Kubernetes API 服务器的更改。

当对 Service 或 Pod 的更改更新 Service 的虚拟 IP 地址或 Pod 的 IP 地址时,iptables 规则会更新以正确地将指向 Service 的流量转发到正确的Pod。

iptables 规则监视发往 Service 的虚拟 IP 的流量,并且在匹配时,从可用 Pod 集中选择一个随机 Pod IP 地址,并且 iptables 规则将数据包的目标 IP 地址从 Service 的虚拟 IP 更改为选定的 Pod。

3.4.4.5.3 通信过程:

数据包首先通过连接到 Pod 的网络命名空间 (1) 的 eth0 接口离开 Pod。然后它通过虚拟以太网设备到达网桥 (2)。网桥上运行的 ARP 协议不知道 Service,因此它通过默认路由 eth0 (3) 将数据包传输出去。在这里,发生了一些不同的事情。在 eth0 接受之前,数据包会通过 iptables 过滤。iptables 收到数据包后,使用 kube-proxy 安装在 Node 上的规则响应 Service 或 Pod 事件,将数据包的目的地从 Service IP 重写为特定的 Pod IP(4)。数据包现在注定要到达 Pod 4,而不是服务的虚拟 IP。iptables 利用 Linux 内核的 conntrack 实用程序来记住所做的 Pod 选择,以便将来的流量路由到同一个 Pod(除非发生任何扩展事件)。本质上,iptables 直接在 Node 上做了集群内负载均衡。然后流量使用我们已经检查过的 Pod 到 Pod 路由流向 Pod (5)。

image-20240627174746946

3.4.4.5.4 DNS:

Kubernetes 可以选择使用 DNS 来避免将服务的集群 IP 地址硬编码到您的应用程序中。

ubernetes DNS 作为在集群上调度的常规 Kubernetes 服务运行。它配置在每个节点上运行的 kubelet,以便容器使用 DNS 服务的 IP 来解析 DNS 名称。集群中定义的每个服务(包括 DNS 服务器本身)都被分配了一个 DNS 名称。

3.4.4.6 与外部网络通信:Ingress:

第 7 层网络 Ingress 在网络堆栈的 HTTP/HTTPS 协议范围内运行,并构建在 Services 之上。

Ingress 方法将根据 Kubernetes 云提供商控制器的实现方式而有所不同。

在 AWS 环境中,ALB 入口控制器使用 Amazon 的第 7 层应用程序负载均衡器提供 Kubernetes 入口。下图详细介绍了此控制器创建的 AWS 组件。它还演示了 Ingress 流量从 ALB 到 Kubernetes 集群的路由。

创建后,(1) Ingress Controller 监视来自 Kubernetes API 服务器的 Ingress 事件。当它找到满足其要求的 Ingress 资源时,它会开始创建 AWS 资源。AWS 将 Application Load Balancer (ALB) (2) 用于Ingress 资源。

image-20240628141817727

image-20240628141848711

参考资料:https://www.51cto.com/article/714336.html

3.5 存储:

3.5.1 卷:Volume

容器中的文件在磁盘上是临时存放的,这给在容器中运行较重要的应用带来一些问题。 当容器崩溃或停止时会出现一个问题。此时容器状态未保存, 因此在容器生命周期内创建或修改的所有文件都将丢失。 在崩溃期间,kubelet 会以干净的状态重新启动容器。

Kubernetes 支持很多类型的卷。 Pod可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同, 但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。

Kubernetes支持多种Volume类型,包括空目录(emptyDir)、主机路径(hostPath)、NFS、云存储提供商(如AWS EBS、Azure Disk)、持久卷(PersistentVolume)等。

3.5.2 持久卷:Persistent Volumes:

PersistentVolume(PV)是集群中由管理员配置的一块存储资源,它独立于Pod存在。PV是集群级别的资源,可以被多个Pod使用。

PV与实际存储后端(如AWS EBS卷、GCE PD卷、Azure Disk、NFS卷等)绑定,由管理员在集群中静态配置或通过存储类动态分配。

3.5.3 持久卷声明:PersistentVolumeClaim:

PersistentVolumeClaim(PVC)是Pod对存储资源的申请,它请求PV以便Pod可以使用持久存储。PVC存在于命名空间内,并且与Pod的生命周期绑定。

Pod通过声明PVC来请求存储资源,而不必关心具体的PV细节。管理员可以设置存储类(StorageClass),使得PVC可以动态地分配PV。

3.5.4 存储类:StorageClass:

StorageClass定义了动态分配PV的策略,是管理员提供给用户的抽象层。用户通过指定StorageClass在创建PVC时,Kubernetes根据其规则动态分配PV。

存储类可以定义复制策略、卷大小、IOPS要求等参数,以及如何处理卷的动态回收。

3.5.5 临时卷:Ephemeral Volume:

有些应用程序需要额外的存储,但并不关心数据在重启后是否仍然可用。 例如,缓存服务经常受限于内存大小,而且可以将不常用的数据转移到比内存慢的存储中,对总体性能的影响并不大。

另有些应用程序需要以文件形式注入的只读数据,比如配置数据或密钥。

临时卷就是为此类用例设计的。因为卷会遵从 Pod 的生命周期,与 Pod 一起创建和删除, 所以停止和重新启动 Pod 时,不会受持久卷在何处可用的限制。

临时卷在 Pod 规约中以内联方式定义,这简化了应用程序的部署和管理。

3.6 配置:

3.6.1 ConfigMaps:

ConfigMap 是一种 API 对象,用来将非机密性的数据保存到键值对中。使用时, Pod可以将其用作环境变量、命令行参数或者存储卷中的配置文件。

ConfigMap 将你的环境配置信息和容器镜像解耦,便于应用配置的修改。

3.6.1.1 文件结构:

这是一个 ConfigMap 的示例,它的一些键只有一个值,其他键的值看起来像是 配置的片段格式。

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # 类属性键;每一个键都映射到一个简单的值
  player_initial_lives: "3"
  ui_properties_file_name: "user-interface.properties"
​
  # 类文件键
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5    
  user-interface.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true    

3.6.1.2 读取方式:

有四种方式读取configmap中的数据:

  1. 在容器命令和参数内

  2. 容器的环境变量

  3. 在只读卷里面添加一个文件,让应用来读取

  4. 编写代码在 Pod 中运行,使用 Kubernetes API 来读取 ConfigMap

3.6.1.2.1 在容器命令和参数内:

编写pod yml文件的时候使用。

apiVersion: v1
kind: Pod
metadata:
  name: demo-pod-cmd
spec:
  containers:
    - name: app-container
      image: my-app-image
      command: ["/bin/sh", "-c", "echo $(cat /etc/config/app.properties) && sleep 3600"]
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
          readOnly: true
  volumes:
    - name: config-volume
      configMap:
        name: my-configmap

echo $(cat /etc/config/app.properties) 命令将会输出 ConfigMap 中 app.properties 的内容。

3.6.1.2.2 容器的环境变量:
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod-env
spec:
  containers:
    - name: app-container
      image: my-app-image
      env:
        - name: KEY1
          valueFrom:
            configMapKeyRef:
              name: my-configmap
              key: app.properties
  volumes:
    - name: config-volume
      configMap:
        name: my-configmap
3.6.1.2.3 在只读卷里面添加一个文件,让应用来读取:
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod-volume
spec:
  containers:
    - name: app-container
      image: my-app-image
      command: ["/bin/sh", "-c", "cat /etc/config/app.properties && sleep 3600"]
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
          readOnly: true
  volumes:
    - name: config-volume
      configMap:
        name: my-configmap

在这个示例中,应用程序可以通过 /etc/config/app.properties 路径读取 ConfigMap 中 app.properties 的内容。

3.6.1.2.4 编写代码在 Pod 中运行:

由于你是直接使用 Kubernetes API,因此只要 ConfigMap 发生更改, 你的应用就能够通过订阅来获取更新,并且在这样的情况发生的时候做出反应。 通过直接进入 Kubernetes API,这个技术也可以让你能够获取到不同的名字空间里的 ConfigMap。

package main
​
import (
  "context"
  "flag"
  "fmt"
  "os"
​
  "k8s.io/client-go/kubernetes"
  "k8s.io/client-go/tools/clientcmd"
  "k8s.io/client-go/util/homedir"
  "k8s.io/client-go/util/retry"
  "path/filepath"
)
​
var (
  namespace    string
  configmapName string
)
​
func main() {
  flag.StringVar(&namespace, "namespace", "default", "Namespace where the ConfigMap is located")
  flag.StringVar(&configmapName, "configmap", "my-configmap", "Name of the ConfigMap to read")
  flag.Parse()
​
  // 通过 kubeconfig 文件或者集群内部配置加载 Kubernetes 配置
  config, err := clientcmd.BuildConfigFromFlags("", getKubeConfigPath())
  if err != nil {
    fmt.Fprintf(os.Stderr, "Error building kubeconfig: %v\n", err)
    os.Exit(1)
  }
​
  // 创建 Kubernetes 客户端
  clientset, err := kubernetes.NewForConfig(config)
  if err != nil {
    fmt.Fprintf(os.Stderr, "Error creating Kubernetes client: %v\n", err)
    os.Exit(1)
  }
​
  // 读取 ConfigMap 数据
  err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
    cm, err := clientset.CoreV1().ConfigMaps(namespace).Get(context.Background(), configmapName, metav1.GetOptions{})
    if err != nil {
      return err
    }
    fmt.Printf("ConfigMap data found:\n%s\n", cm.Data["app.properties"])
    return nil
  })
  if err != nil {
    fmt.Fprintf(os.Stderr, "Error reading ConfigMap: %v\n", err)
    os.Exit(1)
  }
}
​
// 获取 kubeconfig 文件路径
func getKubeConfigPath() string {
  if home := homedir.HomeDir(); home != "" {
    return filepath.Join(home, ".kube", "config")
  }
  return ""
}

3.6.2 Secret:

Secret 类似于ConfigMap但专门用于保存机密数据。

Secret 是一种包含少量敏感信息例如密码、令牌或密钥的对象。 这样的信息可能会被放在Pod规约中或者镜像中。 使用 Secret 意味着你不需要在应用程序代码中包含机密数据。

由于创建 Secret 可以独立于使用它们的 Pod, 因此在创建、查看和编辑 Pod 的工作流程中暴露 Secret(及其数据)的风险较小。 Kubernetes 和在集群中运行的应用程序也可以对 Secret 采取额外的预防措施, 例如避免将敏感数据写入非易失性存储。

默认情况下,Kubernetes Secret 未加密地存储在 API 服务器的底层数据存储(etcd)中。 任何拥有 API 访问权限的人都可以检索或修改 Secret,任何有权访问 etcd 的人也可以。 此外,任何有权限在命名空间中创建 Pod 的人都可以使用该访问权限读取该命名空间中的任何 Secret; 这包括间接访问,例如创建 Deployment 的能力。

3.6.2.1 创建:

3.6.2.1.1 命令行:
kubectl create secret generic my-secret \
    --from-literal=username=myusername \
    --from-literal=password=mypassword
3.6.2.1.2 yaml文件:
apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  username: dXNlcm5hbWU=  # base64 编码的用户名
  password: cGFzc3dvcmQ=  # base64 编码的密码

3.6.2.2 使用:

3.6.2.2.1 环境变量:
apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: nginx
      env:
        - name: SECRET_USERNAME
          valueFrom:
            secretKeyRef:
              name: my-secret
              key: username
        - name: SECRET_PASSWORD
          valueFrom:
            secretKeyRef:
              name: my-secret
              key: password
3.6.2.2.2 卷:
apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: nginx
      volumeMounts:
        - name: secret-volume
          mountPath: "/etc/secret-volume"
          readOnly: true
  volumes:
    - name: secret-volume
      secret:
        secretName: my-secret

Secret 中的数据会以 base64 编码存储,但并不提供加密功能,因此需要谨慎处理敏感信息。

使用 Secret 时,需要确保 Pod 的 ServiceAccount 具有足够的权限来访问指定的 Secret。

当更新 Secret 后,相关的 Pod 需要重启才能应用新的数据。

3.7 API:

Kubernetes API 使你可以在 Kubernetes 中查询和操纵 API 对象 (例如 Pod、Namespace、ConfigMap 和 Event)的状态。

大部分操作都可以通过kubectl命令行接口或类似 kubeadm这类命令行工具来执行, 这些工具在背后也是调用 API。不过,你也可以使用 REST 调用来访问这些 API。 Kubernetes 为那些希望使用 Kubernetes API 编写应用的开发者提供一组客户端库.

3.7.1 类型:

Kubernetes API 遵循 RESTful 原则,每个资源都是通过 URI 来表示的。

支持标准的 HTTP 方法,如 GET、POST、PUT、PATCH 和 DELETE。

比如 pod的api如下:

  1. 创建 Pod

    • 使用 POST 请求在指定的 Namespace 中创建一个新的 Pod。

    POST /api/v1/namespaces/{namespace}/pods
  2. 获取单个 Pod 详情

    • 使用 GET 请求获取特定 Namespace 中 Pod 的详细信息。

    GET /api/v1/namespaces/{namespace}/pods/{podname}
  3. 获取 Pod 列表

    • 使用 GET 请求获取指定 Namespace 中所有 Pod 的列表。

    GET /api/v1/namespaces/{namespace}/pods
  4. 删除 Pod

    • 使用 DELETE 请求删除特定 Namespace 中的 Pod。

    DELETE /api/v1/namespaces/{namespace}/pods/{podname}
  5. 更新 Pod

    • 使用 PUT 或 PATCH 请求更新特定 Pod 的规格或状态。

    PUT /api/v1/namespaces/{namespace}/pods/{podname}/status
    PATCH /api/v1/namespaces/{namespace}/pods/{podname}
  6. 观察 Pod

    • 使用 WATCH 机制通过 GET 请求监视 Pod 的变化。

    GET /api/v1/namespaces/{namespace}/pods/{podname}?watch=true
  7. 获取 Pod 日志

    • 使用 GET 请求获取 Pod 的容器日志。

    GET /api/v1/namespaces/{namespace}/pods/{podname}/log
  8. 执行命令在 Pod 中

    • 使用 POST 请求在 Pod 的容器中执行命令。

    POST /api/v1/namespaces/{namespace}/pods/{podname}/exec
  9. 端口转发

    • 使用 POST 请求为 Pod 建立端口转发。

    POST /api/v1/namespaces/{namespace}/pods/{podname}/portforward
  10. 编辑 Pod 状态

    • 使用 PUT 请求更新 Pod 的状态。

    PUT /api/v1/namespaces/{namespace}/pods/{podname}/status
  11. 获取 Pod 的 Exec 路径

    • 使用 GET 请求获取 Pod 中容器的 Exec 路径。

    GET /api/v1/namespaces/{namespace}/pods/{podname}/exec
  12. 获取 Pod 的 Attach 路径

    • 使用 GET 请求获取 Pod 中容器的 Attach 路径。

    复制
    GET /api/v1/namespaces/{namespace}/pods/{podname}/attach

3.7.2 执行过程:

image-20240624173821585

所有在 Kubernetes 集群中的组件交互都是通过 RESTful API 的形式完成的,包括第一步的控制器监听操作,以及第二步中 kubectl 发送的指令。

虽说我们执行的是 kubectl create -f ns-my-workspace.yaml指令,但其实 kubectl 会向「API服务器」发送一个 POST 请求:

curl --request POST \
  --url http://${k8s.host}:${k8s.port}/api/v1/namespaces \
  --header 'content-type: application/json' \
  --data '{
    "apiVersion":"v1",
    "kind":"Namespace",
    "metadata":{
        "name":"my-workspace"
    }
}'

如上面的 cURL 指令,Kubernetes API 服务器接受的其实是 JSON 数据类型,而并非是 YAML。 然后所有这些创建的 resources 都会持久化到 etcd 组件中,「API服务器」也是 Kubernetes 集群中与「etcd」交互的唯一一个组件。 之后被创建的 my-workspace resource 就会被发送给监听了 namespaces resource 变更的 「Namespace控制器」中,最后就由「Namespace控制器」执行创建 my-workspace 命名空间的具体操作。

同理,当创建 ReplicaSet resource 时就会由「ReplicaSet控制器」做具体执行,当创建 Pod 时,则会由「Pod控制器」具体执行,其他类型的 resource 与之类似,这些控制器共同组成了上图中的「Kubernetes API 控制器集合」。

3.7.3 拓展:

当自定义资源的时候,确定好了资源的类型,同时也就是以restful的形式确定了这个所有的api。

比如我有一个名为account的资源,当这个资源被定义好时,同时也就定义好了如下的api端点:

当你定义了自定义资源(CRD)如 AccountAccountList 并使用 Kubebuilder 框架进行注册时,你实际上已经定义了这些资源的数据模型和 API 规范,但并没有直接定义 RESTful API 的具体实现。Kubebuilder 框架和 Kubernetes API 服务器会基于你的 CRD 定义来提供相应的 RESTful API。

以下是 Kubebuilder 和 Kubernetes API 服务器为你的自定义资源 Account 自动提供的 RESTful API 端点:

  1. 资源列表

    • GET /apis/{group}/{version}/namespaces/{namespace}/accounts 获取特定命名空间下 Account 资源的列表。

  2. 命名空间资源

    • GET /apis/{group}/{version}/namespaces/{namespace}/accounts/{name} 获取特定命名空间下指定名称的 Account 资源详情。

    • POST /apis/{group}/{version}/namespaces/{namespace}/accounts 在特定命名空间中创建一个新的 Account 资源。

    • PUT /apis/{group}/{version}/namespaces/{namespace}/accounts/{name} 更新特定命名空间下指定名称的 Account 资源(完整更新)。

    • PATCH /apis/{group}/{version}/namespaces/{namespace}/accounts/{name} 部分更新特定命名空间下指定名称的 Account 资源。

    • DELETE /apis/{group}/{version}/namespaces/{namespace}/accounts/{name} 删除特定命名空间下指定名称的 Account 资源。

  3. 集群范围资源(如果 CRD 不限制在特定命名空间):

    • 与命名空间资源相似,但使用 clusters 代替 namespaces

  4. 状态子资源(如果使用 Kubebuilder 的 status 注解):

    • PUT /apis/{group}/{version}/namespaces/{namespace}/accounts/{name}/status 更新特定 Account 资源的状态。

  5. 规模子资源(如果使用 Kubebuilder 的 scale 注解):

    • GET、PUT 和 PATCH 可用于操作资源的规模。

  6. Watch API

    • WATCH /apis/{group}/{version}/watch/namespaces/{namespace}/accounts 监视特定命名空间下 Account 资源的变化。

其中 {group}{version}{namespace}{name} 是占位符,对应于你的 CRD 的 API 组、版本、命名空间和资源名称。

Kubebuilder 还提供了一些代码生成注解来进一步自定义和控制 API 的行为,例如:

  • //+kubebuilder:object:root=true 指示这是一个根 Kubernetes 对象。

  • //+kubebuilder:subresource:status//+kubebuilder:subresource:scale 指示是否为 CRD 生成状态和规模子资源。

通过这些自动提供的 API,你可以使用 kubectl、客户端库或直接的 HTTP 请求来操作你的自定义资源,就像操作 Kubernetes 内置资源一样。

3.8 Webhook:

在 Kubernetes 中,"Webhook" 这个词可以指代不同的概念,但通常在讨论 API 拦截和自动化处理的上下文中,"Webhook" 特指 "Admission Webhook"。

Adminssion Webhook是APIServer接收到资源准入请求后执行的回调钩子。

可以定义两种类型的准入 Webhook, 即验证性质的准入Webhook(validating admission webhook)和变更性质的准入 Webhook(mutating admission webhook)。 变更性质的准入 Webhook 会先被调用。它们可以修改发送到 API 服务器的对象以执行自定义的设置默认值操作。

在完成了所有对象修改并且 API 服务器也验证了所传入的对象之后, 验证性质的 Webhook 会被调用,并通过拒绝请求的方式来强制实施自定义的策略。

Adminssion Webhook可以用作:在资源对象实例持久化之前,将 Kubernetes APIServer 的请求进行拦截,加入自定义的逻辑再处理之后,再返回给APIServer,更形象一点说,即是对APIServer接收的请求进行拦截和“篡改”。例如使用mutating admission webhook修改pod的结构,为pod注入sidecar容器,istio就是这么干的。

除此之外,Kubernetes 还有其他类型的 Webhook,例如:

  • Audit Webhook:用于审计目的,可以记录所有请求的详细信息。

  • Authentication Webhook:用于自定义认证过程,允许集群管理员定义自己的认证逻辑。

3.8.1 Webhook与controller:

二者都是针对于资源而言的,符合k8s“一切皆资源“的设计哲学。

Webhook 和 Controllers 在 Kubernetes 中扮演不同的角色,它们可以共存并互补,处理不同类型的任务。理解它们的区别和用途有助于确定何时使用哪一个,或者是否同时使用它们。

Webhook:

  • 定义:Webhook 是一种回调机制,允许在特定事件发生时(如资源的创建、更新或删除)触发预定义的操作。

  • 用途:通常用于实现 Admission 控制,即在资源对象实际被 Kubernetes API 服务器接受之前,对其进行校验或修改。Webhook 可以作为集群策略的强制执行者,或者用于跨多个资源的复杂操作。

  • 触发:由 Kubernetes API 服务器在资源操作时自动调用。

Controllers:

  • 定义:Controller 是 Kubernetes 中的一种控制循环,它监视集群的状态,并确保集群的实际状态与期望状态保持一致。

  • 用途:用于运行业务逻辑,处理复杂的协调任务,如复制 Pod 副本、处理服务发现、自动扩展等。Controllers 可以响应 Kubernetes 事件,但它们的主要作用是持续地管理资源对象的状态。

  • 触发:由 Kubernetes 集群中的事件(如资源对象的更改)触发。

是否需要同时使用 Webhook 和 Controllers?

  • 补充使用:在很多情况下,Webhook 和 Controllers 可以一起工作,以提供更完整的自动化解决方案。例如,Webhook 可以在资源创建时立即设置默认值或执行快速校验,而 Controller 可以管理更复杂的逻辑,如根据 Pod 的状态更新 ConfigMap 或 Secret。

  • 独立使用

    • 如果你的任务主要是在资源对象操作之前进行校验或修改,可能只需要 Webhook。

    • 如果你需要持续地监视和响应集群状态的变化,可能只需要 Controller。

  • 复杂场景:在一些复杂的场景中,可能需要 Webhook 和 Controller 的组合来实现完整的自动化。例如,Webhook 可以用于在资源创建时立即设置必要的注解,而 Controller 可以用于根据这些注解来执行更复杂的业务逻辑。

3.9 KubeConfig:

Kubeconfig 是 Kubernetes 集群的配置文件,它被 Kubernetes 命令行工具(如 kubectl)使用,以便与集群进行通信和管理。Kubeconfig 文件包含了集群的认证信息、服务器地址以及可选的命名空间上下文等信息。

以下是 kubeconfig 的一些关键特点:

  1. 认证信息:Kubeconfig 包含用于与 Kubernetes API 服务器进行认证的信息,这可能包括用户名、密码、客户端证书和密钥等。

  2. 集群地址:包含 Kubernetes 集群 API 服务器的 URL,kubectl 通过这个地址与集群通信。

  3. 上下文:上下文是集群、命名空间、用户和认证信息的组合。Kubeconfig 可以包含多个上下文,用户可以在它们之间切换,以便在不同的环境或集群中工作。

  4. 命名空间:虽然不是 kubeconfig 的直接组成部分,但用户可以在上下文中指定一个默认的命名空间,kubectl 命令将在该命名空间中执行,除非指定了其他命名空间。

  5. 多集群支持:Kubeconfig 允许配置多个集群,用户可以轻松地在它们之间切换。

  6. 配置文件路径:默认情况下,kubectl 工具使用 ~/.kube/config 文件作为 kubeconfig 的配置文件。用户也可以通过设置 KUBECONFIG 环境变量来指定其他路径。

  7. 合并配置:用户可以拥有多个 kubeconfig 文件,KUBECONFIG 环境变量可以包含多个文件路径,kubectl 将按顺序合并它们的内容。

  8. 安全性:Kubeconfig 文件可能包含敏感信息,如密码和私钥,因此应该妥善保护,避免未授权访问。

Kubeconfig 提供了一种灵活的方式来管理与多个 Kubernetes 集群的连接和认证,使得开发者和管理员可以轻松地在不同集群之间切换和工作。

3.9.1 产生:

  1. 创建集群:当您创建一个 Kubernetes 集群时(例如,使用 kubeadm、minikube 或其他工具),集群创建过程会生成初始的 kubeconfig 文件。

    比如,当使用 Minikube 创建一个 Kubernetes 集群时,Minikube 自动生成一个适用于该集群的 kubeconfig 文件。默认情况下,这个文件会被保存在 ~/.kube/config 路径下。

  2. 获取凭据:集群创建后,您需要获取访问集群所需的凭据,这可能包括:

    • 管理员凭据(通常是 admin.conf 文件)。

    • 用户凭据(可能通过服务账号或 kubeconfig 文件)。

  3. 生成 kubeconfig 条目:使用 kubectl 命令行工具,您可以为不同的用户或服务账号生成 kubeconfig 条目。例如,使用 kubectl config set-credentials 命令为特定用户设置认证信息。

  4. 编辑 kubeconfig 文件:Kubeconfig 文件通常位于用户的主目录下的 .kube 文件夹中,名为 config。您可以手动编辑这个文件来添加或修改集群、用户或上下文条目。

  5. 使用 kubectl 配置kubectl 提供了多个子命令来管理 kubeconfig 文件,例如 kubectl config current-contextkubectl config use-context 等。

3.9.2 工作:

  1. 存储集群信息:kubeconfig 文件包含了集群的 API 服务器地址和其他连接信息。

  2. 存储用户认证信息:它包含了用户的认证信息,如证书、密钥或令牌,这些信息用于与 Kubernetes API 服务器进行 TLS 认证或 Bearer 令牌认证。

  3. 定义上下文:kubeconfig 文件可以包含一个或多个上下文,每个上下文关联到特定的集群、用户和命名空间。用户可以通过选择不同的上下文来切换工作环境。

  4. kubectl 读取 kubeconfig:当您运行 kubectl 命令时,它会读取 kubeconfig 文件,使用当前活动的上下文(由 kubectl config current-context 命令显示)来确定与哪个集群通信以及使用哪些认证信息。

  5. 建立安全连接:kubectl 使用 kubeconfig 中的信息来建立与 Kubernetes API 服务器的安全连接。

  6. 执行资源操作:一旦连接建立,kubectl 就可以执行资源操作,如创建、更新、删除或查询 Kubernetes 资源。

  7. 支持多集群管理:如果您管理多个集群,kubeconfig 允许您轻松切换上下文,从而在不同集群之间进行操作。

  8. 环境变量:Kubernetes 客户端工具(如 kubectl)也支持通过环境变量(如 KUBECONFIG)指定 kubeconfig 文件的路径,这允许在不同环境或配置文件之间切换。

3.8 拓展:

Kubernetes 系统的扩展和增强既包括扩展 API Server 所支持的资源类型及相关声明式功能的实现,以及消除集群的单点以实现集群的高可用等,也包括如何将系统增强为一个完整意义上的 PaaS 平台,并以 DevOps 文化为驱动改善工作流程等。

Kubernetes API 默认提供的众多的功能性资源类型可用于容器编排以解决多数场景中的编排需求。但是,有些场景也需要借助于额外的或更高级别的编排抽象,例如引入集群外部的一些服务并以资源对象的形式进行管理,或者把 Kubernetes 的多个标准资源对象合并为一个单一的更高级别的资源抽象等等。而这类的 API 扩展抽象通常也应该兼容 Kubernetes 系统的基本特性,如支持 kubectl 管理工具、CRUD 及 watch 机制、标签、etcd 存储、认证、授权、RBAC 及审计,等等,从而使得用户可将精力集中于构建业务逻辑本身。

目前,扩展 Kubernetes API 的常用方式有三种:

  • 使用 CRD(CustomResourceDefinitions)自定义资源类型。

  • 开发自定义的 API Server 并聚合至主 API Server。

  • 定制扩展 Kubernetes 源码。

其中,CRD 最易用但限制较多,自定义 API Server 更富有弹性但代码工作量偏大,而仅在必须添加新的核心类型才能确保专用的 Kubernetes 集群功能正常时才应该定制系统源码。

CRD 并非设计用来取代 Kubernetes 的原生资源类型,而是用于补充一种简单易用的更为灵活和更高级别的自定义 API 资源的方式。虽然目前在功能上仍存在不少的局限,但对于大多数的需求场景来说,CRD 的表现已经足够好,因此在满足需求的前提下是首选的 API 资源类型扩展方案。

3.8.1 CRD(CustomResourceDefinitions):

3.8.1.1 介绍:

CRD的全称为 CustomResourceDefinitions,即自定义资源。

k8s拥有一些内置的资源,比如说Pod,Deployment,ReplicaSet等等,针对每种资源的每个api,k8s都内置了自己的逻辑。

但是有些复杂的逻辑,想要放在k8s中去运行,就要进行一些的拓展。

而CRD则提供了这种拓展的方式,使用户可以自定义新的资源,以扩展k8s的功能。

使用CRD可以在不修改k8s源代码的基础上方便的扩展k8s的功能。

本质上说,crd的根本目的是在k8s的平台上,运行编写的具体的逻辑。

3.8.1.1.1 错误理解:

crd不是帮助你整合deployments,service,ingress等等的玩意儿,它本质上是自定义逻辑的k8s分布式部署。

在 Kubernetes (k8s) 上部署 MySQL 通常不需要自定义资源,因为可以通过 Kubernetes 提供的标准资源来完成部署。以下是一些常用的 Kubernetes 资源,它们可以用于部署和管理 MySQL 数据库:

  1. ConfigMap:用于存储配置文件,比如 MySQL 的配置文件 my.cnf 14。

  2. Secret:用于存储敏感信息,比如 MySQL 的密码 1。

  3. PersistentVolume (PV) 和 PersistentVolumeClaim (PVC):用于数据持久化,确保数据库数据在 Pod 重启后依然可用 14。

  4. Deployment:定义了应用的声明周期,确保指定数量的 Pod 副本始终运行 47。

  5. StatefulSet:适用于需要持久化存储和唯一网络标识的应用,比如数据库 2。

  6. Service:定义了访问应用的方式,比如通过 ClusterIP、NodePort 或 LoadBalancer 47。

使用这些标准资源,你可以创建一个 MySQL 部署,它将包括配置文件、密码、存储、部署和网络访问。如果你有特殊的需求,比如特定的存储类型或高级网络配置,你可能需要配置特定的参数或使用第三方解决方案,但这些通常也通过 Kubernetes 的标准资源来实现,而不需要自定义资源。

3.8.1.2 控制器:Controller:

如果说只是对 CRD 实例进行 CRUD 的话,不需要 Controller 也是可以实现的,只是只有数据,没有针对数据的操作。此时,crd就是单纯的数据,相当于把k8s当数据库用。

控制器是一段控制循环逻辑,负责监视 Kubernetes 资源的状态并根据需要采取行动,以使集群的当前状态符合期望状态。控制器通常与特定类型的资源(包括自定义资源)相关联。

3.8.1.2.1 执行逻辑:

可以把每一个自定义的控制器抽象为一个Reconcile方法来看待。

Reconcile 方法是由 Kubernetes 控制器触发的,通常与 API 调用是解耦的。

  1. 事件触发:当 Kubernetes 中的自定义资源(CRD)或相关资源发生变化时(例如创建、更新、删除),会生成事件。

  2. Informer 监听:控制器使用 Informer 监听这些事件。Informer 是 Kubernetes 中的一个机制,用于在资源变化时提供实时更新。

  3. 调用 Reconcile:当 Informer 接收到事件时,它会触发调用 Reconcile 方法。Reconcile 方法会包含逻辑来判断状态是否需要更新,并执行必要的操作来使实际状态与期望状态一致。

  4. 周期性同步:除了事件触发外,Reconcile 还可以周期性地被调用,这是通过设置一个定时器或使用 Kubernetes 的定时任务来完成的。

3.8.1.2.2 Operator模式:

Operator=CRD+Controller。

Operator 模式是一种设计模式,用于扩展 Kubernetes 的功能,使其能够自动化管理和运维复杂的应用程序和服务。Operator 模式的核心思想是通过自定义资源(Custom Resources,CR)来描述和扩展应用程序的特定状态和行为,同时结合自动化控制器(Controller)来实现对这些自定义资源的生命周期管理和操作。

3.8.1.2 使用:

3.8.1.2.1 编写crd定义文件:

编写一个 YAML 文件来描述 CRD 的 API 版本、种类(kind)、名字、分组(group)、命名空间(namespace)等属性。例如,创建一个名为 myresources.mydomain.com 的 CRD。

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: myresources.mydomain.com
spec:
  group: mydomain.com
  names:
    kind: MyResource
    plural: myresources
    singular: myresource
  scope: Namespaced
  version: v1
3.8.1.2.2 应用crd文件:

使用 kubectl apply -f 命令来创建 CRD:

kubectl apply -f myresource-crd.yaml
3.8.1.2.3 编写自定义控制器:

自定义控制器是一个独立的应用程序,它使用 Kubernetes 客户端库来监听 CRD 资源的变化。控制器的逻辑是你自己编写的,它定义了当 CRD 资源发生变化时应该执行的操作。

以下是几种编写crd控制器可能所需要的工具:

  1. Kubebuilder:

Kubebuilder 是一个基于 Go 语言的项目,用于快速开发 Kubernetes 控制器。尽管 Kubebuilder 本身是用 Go 编写的,但它提供了一个生成器工具,可以帮助你通过简单的命令行操作生成控制器的代码。你可以使用 Kubebuilder 来生成基本的控制器框架,然后通过定义自定义资源的 API 规范和控制逻辑来自定义控制器行为。

2.Operator SDK:

Operator SDK 是另一个流行的工具,它支持使用 Go、Ansible 或者 Helm 来编写 Kubernetes 的自定义操作符(Operators)。你可以通过 Operator SDK 生成基础代码框架,然后在生成的框架基础上,通过配置和定制来实现自定义资源的控制器逻辑。

3.Helm Charts:

尽管 Helm 主要用于应用程序的打包和部署,但它也可以在 Chart 中包含自定义资源的定义。通过编写 Helm Chart,你可以定义和管理包含自定义资源的 Kubernetes 应用程序,而无需编写额外的控制器代码。

4.Operator Framework:

Operator Framework 是一个开源的框架,支持使用多种语言和工具来编写和部署 Kubernetes 操作符。你可以使用 Operator Framework 的相关组件来定义和管理自定义资源的控制逻辑,而无需自行编写控制器代码。

具体的编写方式后文有叙述。

3.8.1.2.4 部署自定义控制器:

将编写的自定义控制器作为一个 Deployment 部署到 Kubernetes 集群中。这个 Deployment 负责运行你的应用程序逻辑。

需要自己构建镜像,打包等。把这个控制器当单纯的应用程序去操作。

3.8.1.2.5 创建cr实例:

一旦 CRD 创建成功,就可以创建对应的定制资源对象了。例如,创建一个名为 myresourceinstance 的实例:

apiVersion: mydomain.com/v1
kind: MyResource
metadata:
  name: myresourceinstance
spec:
  # 定义具体的 spec 字段

使用 kubectl apply -f 命令创建实例:

kubectl apply -f myresourceinstance.yaml
3.8.1.2.6 进行操作:

使用api或者kubectl等工具访问api。编写的控制器会发生工作,运行自己编写的相关的逻辑,比如部署特定的资源或者进行某种运算等等。

CRD 的创建和使用涉及到较为复杂的 Kubernetes API 和编程知识,因此建议ubernetes 集群管理经验的情况下进行操作。此外,Kubernetes 的版本可能会影响 CRD 的 API 版本和可用特性。

4.使用:

4.1 手动搭建集群:

部署服务到 Kubernetes 前,你需要确保 Kubernetes 集群已经正确配置并运行。

4.1.1 机器互通:

要求三台机器互通.

4.1.2 配置服务:

4.1.2.1 linux版本检查:

此方式下安装kubernetes集群要求Centos版本要在7.5或之上:cat /etc/redhat-release

4.1.2.2 主机名检查:

  • 如果是vagrant启动的,那其实在vagrantFile中已经配置过了,在这里无需重复配置

    • 查看主机名:hostnamectl

    • 修改主机名: hostnamectl set-hostname <hostname>

4.1.2.3 同步机器时间:

  • kubernetes要求集群中的节点时间必须精确一致,这里直接使用chronyd服务从网络同步时间。企业中建议配置内部的时间同步服务器

  • 安装时间同步插件:yum install -y ntpdate

  • 查看时间:timedatectl

  • 修改时间为上海:timedatectl set-timezone Asia/Shanghai

  • 修改同步服务器地址为阿里云:sed -i.bak '3,6d' /etc/chrony.conf && sed -i '3cserver ntp1.aliyun.com iburst' /etc/chrony.conf

  • 启动chronyd及加入开机自启:systemctl start chronyd && systemctl enable chronyd

  • 查看同步结果:chronyc sources

  • 为了检查chrony是否同步:chronyc tracking

4.1.2.4 禁用iptables和firewalld服务:

  • kubernetes和docker在运行中会产生大量的iptables规则,为了不让系统规则跟它们混淆,直接关闭系统的规则

  • 关闭firewalld服务

    • systemctl stop firewalld

    • systemctl disable firewalld

  • 关闭iptables服务(在CentOS7以上,防火墙的管理由firewalld来管理)

    • systemctl stop iptables

    • systemctl disable iptables

4.1.2.5 禁用selinux:

  • selinux是linux系统下的一个安全服务,如果不关闭它,在安装集群中会产生各种各样的奇葩问题

4.1.2.6 禁用swap分区:

  • swap分区指的是虚拟内存分区,它的作用是在物理内存使用完之后,将磁盘空间虚拟成内存来使用

  • 启用swap设备会对系统的性能产生非常负面的影响,因此kubernetes要求每个节点都要禁用swap设备

  • 但是如果因为某些原因确实不能关闭swap分区,就需要在集群安装过程中通过明确的参数进行配置说明

4.1.2.7 配置linux的内核参数:

  • 添加网桥过滤和地址转发功能,编辑/etc/sysctl.d/kubernetes.conf文件,添加如下配置:

sudo vi /etc/sysctl.d/kubernetes.conf
sh复制代码net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
  • 重新加载配置,加载网桥过滤模块: modprobe br_netfilter && sysctl -p /etc/sysctl.d/kubernetes.conf

  • 查看网桥过滤模块是否加载成功 : lsmod | grep br_netfilter

4.1.2.8 配置ipvs功能:

在kubernetes中service有两种代理模型:

是基于

ipvs

(选这个):

  • ipvs的性能明显要高一些,但是如果要使用它,需要手动载入ipvs模块

  • 1.安装ipset和ipvsadm : yum install ipset ipvsadmin -y

  • 2 添加需要加载的模块写入脚本文件:

sh复制代码cat > /etc/sysconfig/modules/ipvs.modules <<EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF
  • 3 为脚本文件添加执行权限: chmod +x /etc/sysconfig/modules/ipvs.modules

  • 4 执行脚本文件: /bin/bash /etc/sysconfig/modules/ipvs.modules

  • 5 查看对应的模块是否加载成功:lsmod | grep -e ip_vs -e nf_conntrack_ipv4

  • 基于

    iptables

    • 也有配置iptables的,将桥接的 IPv4 流量传递到 iptables 的链:

      sh复制代码cat > /etc/sysctl.d/k8s.conf <<EOF 
      net.bridge.bridge-nf-call-ip6tables = 1 
      net.bridge.bridge-nf-call-iptables = 1 
      EOF
    • 重新加载配置: sysctl -p

4.1.2.9 安装ipvsadm:

 yum -y install ipset ipvsadm

4.1.2.10 重启服务器:

上面步骤完成之后,需要重新启动linux系统: reboot

4.1.3 安装docker:

所有命令在所有机器操作.

4.1.4 设置k8s仓库:

所有命令在所有机器操作.

配置阿里云的yum源告诉k8s在哪里加速下载安装

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

4.1.5 安装kubeadm,kubelet,kubectl:

kubeadmkubeletkubectl 是 Kubernetes 生态系统中的三个核心组件,它们分别用于集群的初始化、节点的管理和集群的操作。以下是它们的简要介绍:

  1. kubeadm:

    • 作用: kubeadm 是 Kubernetes 的集群引导工具,用于初始化、配置和管理 Kubernetes 集群的第一个主节点(Master Node)。

    • 具体功能:

      • 部署 Kubernetes 控制面板组件(API Server、Controller Manager、Scheduler 等)。

      • 自动化集群初始化过程,使得集群的搭建变得更加简单。

      • 提供可插拔的机制,允许用户自定义集群初始化的过程。

  2. kubelet:

    • 作用: kubelet 是每个 Kubernetes 节点上运行的主要组件,负责管理节点上的容器和与 Master Node 通信。

    • 具体功能:

      • 监视容器运行时(如 Docker)。

      • 根据 Master Node 分配的 PodSpec 启动和停止容器。

      • 提供容器的健康检查、日志收集和监控等功能。

  3. kubectl:

    • 作用: kubectl 是 Kubernetes 集群的命令行工具,用于与 Kubernetes 集群进行交互和操作。

    • 具体功能:

      • 创建、删除、管理 Kubernetes 中的资源(如 Pod、Service、Deployment 等)。

      • 与集群交互,检查集群状态、获取日志、执行命令等。

      • 配置 kubectl 使用的集群、上下文和用户身份。

这三个工具共同协作,使得用户能够轻松地初始化、管理和操作 Kubernetes 集群。通常,kubeadm 用于集群的初始化,kubelet 运行在每个节点上以管理容器,而 kubectl 则是用户与集群进行交互的命令行工具。

  • 该章节所有命令在所有机器操作

  1. 安装:yum install -y kubelet-1.22.4 kubeadm-1.22.4 kubectl-1.22.4

  2. 开机启动:systemctl enable kubelet && systemctl start kubelet(此时还是activating状态不用管)

  3. 查看版本:kubelet --version

4.1.6 部署master节点:

  • 先下载初始化kubeadm需要的相关镜像,不然执行kubeadm init 命令下载很久,也不知道下载到哪里

  1. 由于官方镜像地址被墙,所以我们需要首先获取所需镜像以及它们的版本。然后从国内镜像站获取。查看指定版本需要镜像版本:kubeadm config images list --kubernetes-version=v1.22.4

  2. 编写下载镜像脚本,版本参考上面命令:

  • mkdir /app/k8s

  • vi /app/k8s/master_images.sh

sh复制代码#!/bin/bash

## 下面的镜像应该去除"k8s.gcr.io/"的前缀,版本换成上面获取到的版本
images=(
	kube-apiserver:v1.22.4
        kube-proxy:v1.22.4
	kube-controller-manager:v1.22.4
	kube-scheduler:v1.22.4
	coredns:v1.8.4
	etcd:3.5.0-0
        pause:3.5
)

for imageName in ${images[@]} ; do
    docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
#下面是默认外国源,下载慢
#   docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName  k8s.gcr.io/$imageName
done
  1. 脚本权限:chmod +x /app/k8s/master_images.sh

  2. 执行下载镜像:./master_images.sh

  3. 查看下载的镜像:docker images

image.png

初始化kubeadm

由于上面已经从阿里云仓库下载了所需镜像,下面执行初始化命令就不会去国外下载镜像了。在master节点执行

sh复制代码kubeadm init \
--apiserver-advertise-address=10.0.2.9 \
--image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \
--kubernetes-version   v1.22.4 \
--service-cidr=10.96.0.0/16  \
--pod-network-cidr=10.244.0.0/16
  • apiserver-advertise-address:apiserver地址填写要当master机器ip(修改自己的mater的ip

  • image-repository:由于默认拉取镜像地址k8s.gcr.io国内无法访问,这里指定阿里云镜像仓库地址。可以手动按照我们的/app/k8s/master_images.sh先拉取镜像,在配置--image-repository属性就可以用到我们刚才下载好的镜像。地址变为 registry.aliyuncs.com/google_containers 也可以,省去上面5.2.2一步骤(但是初始化命令可能等很久,要下载镜像,所有分开两步骤,确保需要的镜像已经下载)。(不用改)

  • kubernetes-version:指定版本要和安装时一致即可(修改自己的版本

  • service-cidr:设置由pods组成的services通信ip段的子网(不用改)

  • pod-network-cidr:pods的通信ip段的子网(不用改)

  • 科普:无类别域间路由(Classless Inter-Domain Routing、CIDR)是一个用于给用户分配IP地址以及在互联网上有效地路由 IP 数据包的对 IP 地址进行归类的方法。

问题: 出现该问题请关闭集群的虚拟内存交换

sh复制代码[ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables]: /proc/sys/net/bridge/bridge-nf-call-iptables contents are not set to 1
[ERROR Swap]: running with swap on is not supported. Please disable swap

4.1.7 加入worker节点:

  • master节点获取名称空间: kubectl get ns

  • master节点查看所有命名空间下所有pods: kubectl get pods --all-namespaces

  • master节点查看集群节点:kubectl get nodes image.png

  • 等待master节点状态为Ready,flannel状态为Running。再在每个worder节点执行join命令加入集群(该命令有时间限制,过期会失效)

worker节点执行:

kubeadm join 10.0.2.9:6443 --token gp34b0.p7258upebcqb7zy3 \         --discovery-token-ca-cert-hash sha256:7435c7011942ad6a754c743218ee5f1cd4ea2842305c3aa3b75666008af567fb 

image.png

  • 再次检查是否已经加入到集群节点:kubectl get nodes

  • 如果worker节点状态为NotReady,监控 pod 进度等3-10分钟: watch kubectl get pod -n kube-system -o wide。等待pod变为Running状态

  • 如果网络出现问题,关闭 cni0,重启虚拟机继续测试:ip link set cni0 down

  • 如果出现下面内容,k8s搭建完成 image.png

4.2 使用sealos部署:

安装命令:

curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.0-beta5/scripts/cloud/install.sh -o /tmp/install.sh && bash /tmp/install.sh   --cloud-version=v5.0.0-beta5   --image-registry=registry.cn-shanghai.aliyuncs.com --zh   --proxy-prefix=https://mirror.ghproxy.com

这个命令的解释如下:

curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.0-beta5/scripts/cloud/install.sh -o /tmp/install.sh

  • curl:这是一个用来从服务器获取内容的命令行工具。

  • -sfL

    :这是curl的几个选项:

    • -s:静默模式,不显示下载过程中的信息。

    • -f:失败时不输出错误信息,而是返回非零状态码。

    • -L:如果请求的资源已移动到新位置,curl会跟随重定向。

  • https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.0-beta5/scripts/cloud/install.sh:这是要下载的脚本文件的URL。

  • -o /tmp/install.sh:将下载的文件保存到/tmp/install.sh路径。

&&:逻辑与操作符,确保前面的命令成功执行后再执行后面的命令。

bash /tmp/install.sh:使用bash命令运行下载到/tmp/install.sh的脚本文件。

--cloud-version=v5.0.0-beta5:指定安装的Sealos版本为v5.0.0-beta5

--image-registry=registry.cn-shanghai.aliyuncs.com:指定镜像仓库地址为registry.cn-shanghai.aliyuncs.com

--zh:安装过程中使用中文语言。

--proxy-prefix=https://mirror.ghproxy.com:指定代理前缀,可能用于加速下载和访问。

2.1.2 报错:

一开始,可能会出现的报错:

QQ20240613-153041@2x

无法连接到指定的IP和端口(47.115.229.45:5000)

这意味着在拉取sealos.hub:5000/pause:3.9这个镜像时,无法通过网络请求连接到47.115.229.45:5000这个地址,导致连接超时。

在阿里云上安全组打开5000后该问题不会再出现,而是转换为:

image-20240613182944941

社区同样有人出现此问题,尝试解决,验证拉取的镜像:

docker pull registry.cn-shanghai.aliyuncs.com/google_containers/kube-apiserver:v1.20.4

Error response from daemon: pull access denied for registry.cn-shanghai.aliyuncs.com/google_containers/kube-apiserver, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

命令似乎有误 使用别人给的命令:

sealos pull registry.cn-shanghai.aliyuncs.com/labring/kubernetes:v1.27.7

出错之后使用sealos reset --force初始化文件

最终因内存不足而失败

尝试本地话部署sealos因为bash来失败。

curl -sfL https://mirror.ghproxy.com/https://raw.githubusercontent.com/labring/sealos/v5.0.0-beta5/scripts/cloud/install.sh -o /tmp/install.sh && bash /tmp/install.sh \
  --cloud-version=v5.0.0-beta5 \
  --image-registry=registry.cn-shanghai.aliyuncs.com --zh \
  --proxy-prefix=https://mirror.ghproxy.com
mkdir: /root: Read-only file system

还有可能会出现的报错有:

image-20240618175515349

4.3 部署应用程序:

4.3.1 部署流程:

在Kubernetes中部署项目通常涉及以下步骤:

  1. 创建和配置容器镜像: 首先,将你的应用程序打包成一个容器镜像。这通常包括编写Dockerfile来定义容器的环境和依赖项,并使用Docker构建工具构建镜像。确保将该镜像推送到一个容器注册表(如Docker Hub、Google Container Registry、或私有注册表)。

  2. 定义Kubernetes配置: 创建一个或多个Kubernetes配置文件,用于描述你的应用程序的部署、服务、持久卷、和其他资源。这些配置文件通常以YAML格式编写,包括Deployment、Service、ConfigMap等资源。

    • Deployment: 定义应用程序的部署规范,包括副本数、容器镜像、环境变量等。

    • Service: 定义如何暴露应用程序的网络服务,可以通过ClusterIP、NodePort、或LoadBalancer类型。

    • ConfigMap: 存储应用程序的配置信息,以便在容器内使用。

    • 其他资源:根据需要,还可以包括如Ingress、Secrets等其他配置。

  3. 应用配置文件: 如果应用程序有一些需要动态配置的参数,可以将它们放在ConfigMap或Secrets中,并在Pod的环境变量或文件中引用这些配置。

  4. 应用部署: 使用kubectl命令行工具或Kubernetes API,将配置文件应用到Kubernetes集群中。例如:

    bashCopy code
    kubectl apply -f your-deployment.yaml

    这将创建Deployment、Service等资源,并在集群中启动你的应用程序。

  5. 监控和日志: 部署后,监控应用程序的运行状况是至关重要的。Kubernetes提供了各种监控和日志工具,你可以选择适合你的需求的工具进行配置和使用。

  6. 扩展和更新: 使用Kubernetes的伸缩功能,可以根据需要增加或减少Pod的数量。升级应用程序通常涉及更新Deployment中的镜像版本,并通过滚动更新确保零宕机更新。

  7. 清理和维护: 如果不再需要某个应用程序或资源,可以使用kubectl delete命令删除相应的资源。定期审查和清理无用的资源有助于维持集群的整洁和性能。

apiVersion: apps/v1
kind: Deployment #部署
metadata:
  name: springboot-app
spec:
  replicas: 2 #2个副本
  selector:
    matchLabels:
      app: springboot-app
  template:
    metadata:
      labels:
        app: springboot-app
    spec:
      containers:
      - name: springboot-app
        image: registry.cn-hangzhou.aliyuncs.com/askajohnny/k8s:v1 #刚刚push到阿里云上的镜像地址
        ports:
        - containerPort: 8080 #默认springboot端口 
​
---
​
apiVersion: v1
kind: Service
metadata:
  name: springboot-app
spec:
  selector:
    app: springboot-app #选中上面的 Deployment
  ports:
  - port: 7003 #对外7003端口
    targetPort: 8080
​
​
--- 
#Ingress 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress
  labels:
    name: myingress
spec:
  IngressClass: nginx
  rules:
  - host: springboot.demo.com #所有的host这个域名请求 转发到上面的 Service= springboot-app
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: springboot-app # 转发到 这个Service 
            port: 
              number: 7003 

这是一个典型的Kubernetes配置文件,用于定义一个包含Deployment、Service和Ingress的应用程序的部署。以下是对这个文件的详细解释:

4.3.2 部署文件:

4.3.2.1 Deployment 部分:

yamlCopy codeapiVersion: apps/v1
kind: Deployment
metadata:
  name: springboot-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: springboot-app
  template:
    metadata:
      labels:
        app: springboot-app
    spec:
      containers:
      - name: springboot-app
        image: registry.cn-hangzhou.aliyuncs.com/askajohnny/k8s:v1
        ports:
        - containerPort: 8080
  • apiVersion: API 版本,这里使用的是 apps/v1

  • kind: 资源类型,这里是 Deployment

  • metadata: 部署的元数据,包括部署的名称。

  • spec: 部署的规格,包括副本数量、选择器和 Pod 模板。

    • replicas: 指定副本数为 2。

    • selector: 使用标签选择器来匹配属于该 Deployment 的 Pod。

    • template

      : 定义创建 Pod 的模板。

      • metadata: 定义 Pod 的标签。

      • spec: 定义 Pod 的规格,包括容器的镜像、名称和端口。

4.3.2.2 Service 部分:

yamlCopy codeapiVersion: v1
kind: Service
metadata:
  name: springboot-app
spec:
  selector:
    app: springboot-app
  ports:
  - port: 7003
    targetPort: 8080
  • apiVersion: 使用的是 v1 版本。

  • kind: 资源类型,这里是 Service

  • metadata: Service 的元数据,包括服务的名称。

  • spec: Service 的规格,指定了服务的选择器和端口映射。

    • selector: 使用标签选择器来匹配属于该 Service 的 Pod。

    • ports: 指定服务的端口配置,这里将容器端口 8080 映射到服务端口 7003

4.3.2.3 Ingress 部分:

yamlCopy codeapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress
  labels:
    name: myingress
spec:
  IngressClass: nginx
  rules:
  - host: springboot.demo.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: springboot-app
            port: 
              number: 7003 
  • apiVersion: 使用的是 networking.k8s.io/v1 版本。

  • kind: 资源类型,这里是 Ingress

  • metadata: Ingress 的元数据,包括 Ingress 的名称和标签。

  • spec: Ingress 的规格,定义了 Ingress 规则和后端服务。

    • IngressClass: 指定 Ingress 使用的 Ingress 类别,这里是 nginx

    • rules

      : 定义 Ingress 规则,指定了主机和路径的映射。

      • host: 定义 Ingress 的主机名。

      • http

        : 定义了 HTTP 路由规则。

        • paths

          : 定义了路径映射规则。

          • pathType: 指定路径的类型。

          • path: 定义路径映射规则。

          • backend: 指定请求的后端服务,这里将请求转发到 springboot-app 服务的 7003 端口。

4.4 minikube:

minikube是一个单机版的k8s,可以理解为将k8s所有的节点都部署在一个服务器上的应用程序,安装过程如下图:

image-20240614181722563

4.5 可视化操作:lens:

4.6 CRD具体实现:

主要按照https://developer.aliyun.com/article/749901文章来进行操作。

4.6.1 安装kustomize:

直接使用brew安装 未发现明显错误

4.6.2 安装kubebuilder:

容易出现各种各样的问题。

最好的方式是知道各个命令的具体作用。

完整脚本,mac无法使用

os=$(go env GOOS)
arch=$(go env GOARCH)
​
# download kubebuilder and extract it to tmp
curl -L https://go.kubebuilder.io/dl/2.2.0/${os}/${arch} | tar -xz -C /tmp/
​
# move to a long-term location and put it on your path
# (you'll need to set the KUBEBUILDER_ASSETS env var if you put it somewhere else)
sudo mv /tmp/kubebuilder_2.2.0_${os}_${arch} /usr/local/kubebuilder
export PATH=$PATH:/usr/local/kubebuilder/bin

网上搜索到的另外的教程:

#查看go环境变量
go env GOOS
go env GOARCH

下载:

curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/替换go env GOOS 字段/替换go env GOARCH字段

一般能正常下载成功

赋予权限:

sudo chmod +x kubebuilder

移动到/usr/local/bin/目录:

mv kubebuilder /usr/local/bin/

这两个命令不要连起来使用,否则有可能会报错:

chmod +x kubebuilder && mv kubebuilder /usr/local/bin/

然后按理说就可以运行了,但是还是运行不了,因为我goos显示的是linux,应该正常显示darwin。

下面尝试直接使用brew下载:

image-20240624161726735

似乎可以直接安装成功。

4.6.3 创建工程:

image-20240624161943625

4.6.4 部署实践:

编写完一个 CRD(自定义资源定义)和它的控制器之后,要将其应用到 Kubernetes 集群中,你需要执行以下步骤:

  1. 部署 CRD

    • 使用 kubectl apply -f <crd.yaml> 命令将 CRD YAML 文件应用到 Kubernetes 集群中。这会注册你的自定义资源。

  2. 构建和推送控制器镜像

    • 构建你的控制器应用程序的 Docker 镜像。

    • 将镜像推送到容器镜像仓库,如 Docker Hub、Google Container Registry 或私有仓库。

  3. 编写 Deployment 和 Service

    • 创建一个 Deployment YAML 文件来定义如何部署你的控制器应用程序。

    • 如果需要,创建一个 Service YAML 文件来暴露你的控制器应用程序。

  4. 配置 RBAC

    • 编写 Role 和 RoleBinding YAML 文件,为控制器应用程序定义所需的 Kubernetes 角色和权限。

  5. 应用 Deployment 和 Service

    • 使用 kubectl apply -f <deployment.yaml> 命令部署控制器应用程序。

    • 如果有 Service 文件,使用 kubectl apply -f <service.yaml> 命令应用它。

  6. 验证 CRD

    • 使用 kubectl get crd 命令检查你的 CRD 是否已成功注册。

    • 使用 kubectl get <custom-resource> 命令尝试获取你定义的自定义资源实例。

  7. 创建自定义资源实例

    • 编写并应用一个 YAML 文件,定义你自定义资源的实例。

  8. 监控和调试

    • 使用 kubectl logs <pod-name> 命令查看控制器的日志。

    • 使用 kubectl describe <custom-resource> 命令检查自定义资源的状态和事件。

  9. 测试

    • 测试你的自定义资源和控制器是否按预期工作。

  10. 更新和维护

    • 如果需要更新 CRD 或控制器,修改相应的 YAML 文件并重新应用它们。

以下是一个简化的示例流程,展示如何将 CRD 和控制器部署到 Kubernetes 集群:

复制
# 部署 CRD
kubectl apply -f mycrd.yaml
​
# 构建并推送控制器镜像到容器仓库
# docker build -t my-controller-image .
# docker push my-controller-image
​
# 应用 Deployment 和 Service
kubectl apply -f mycontroller-deployment.yaml
kubectl apply -f mycontroller-service.yaml
​
# 应用 RBAC 配置
kubectl apply -f mycontroller-rbac.yaml
​
# 验证 CRD 是否注册成功
kubectl get crd
​
# 创建自定义资源实例
kubectl apply -f mycustomresource.yaml
​
# 检查控制器日志
kubectl logs -f <pod-name>
​
# 检查自定义资源的状态
kubectl describe <custom-resource-name>

4.7 编写webhook :

使用k8s生成资源的时候,会选择是否要添加webhook到你的 API。你可以选择 "Yes" 并按照提示进行操作。

在生成的 config/webhook 目录下,你可以找到用于 Webhook 的配置文件。编辑 MutatingWebhookConfigurationValidatingWebhookConfiguration 文件来指定你的 Webhook 应该拦截哪些资源。

---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: mutating-webhook-configuration
webhooks:
- admissionReviewVersions:
  - v1
  clientConfig:
    service:
      name: webhook-service
      namespace: system
      path: /mutate-networking-k8s-io-v1-ingress
  failurePolicy: Ignore
  name: mingress.sealos.io
  namespaceSelector:
    matchExpressions:
      - key: user.sealos.io/owner
        operator: Exists
  rules:
  - apiGroups:
    - networking.k8s.io
    apiVersions:
    - v1
    operations:
    - CREATE
    - UPDATE
    resources:
    - ingresses
  sideEffects: None
- admissionReviewVersions:
  - v1
  clientConfig:
    service:
      name: webhook-service
      namespace: system
      path: /mutate--v1-namespace
  failurePolicy: Ignore
  name: mnamespace.sealos.io
  namespaceSelector:
    matchExpressions:
      - key: user.sealos.io/owner
        operator: Exists
  rules:
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE
    - UPDATE
    resources:
    - namespaces
  sideEffects: None
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: validating-webhook-configuration
webhooks:
- admissionReviewVersions:
  - v1
  clientConfig:
    service:
      name: webhook-service
      namespace: system
      path: /validate-networking-k8s-io-v1-ingress
  failurePolicy: Ignore
  name: vingress.sealos.io
  namespaceSelector:
    matchExpressions:
      - key: user.sealos.io/owner
        operator: Exists
  rules:
  - apiGroups:
    - networking.k8s.io
    apiVersions:
    - v1
    operations:
    - CREATE
    - UPDATE
    - DELETE
    resources:
    - ingresses
  sideEffects: None
- admissionReviewVersions:
  - v1
  clientConfig:
    service:
      name: webhook-service
      namespace: system
      path: /validate--v1-namespace
  failurePolicy: Ignore
  name: vnamespace.sealos.io
  namespaceSelector:
    matchExpressions:
      - key: user.sealos.io/owner
        operator: Exists
  rules:
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE

在你的 Go 代码中,需要注册 Webhook。这通常是在 setupwebhookwithmanager.go 文件中完成的,例如:

复制
func (r *MyResource) SetupWebhookWithManager(mgr ctrl.Manager) error {
    return ctrl.NewWebhookManagedBy(mgr).
        For(r).
        Complete()
}

在生成的webhook文件中,主要需要以下方法并提供实现:

在 Kubernetes 中定义一个 Admission Webhook 通常涉及实现特定的接口方法,这些方法属于 admission.Handler 接口,由 kubebuilder 提供的框架抽象。对于 Mutating Webhook 和 Validating Webhook,核心方法如下:

4.7.1 对于 Mutating Webhook:

  • Default 方法:这个方法用于设置请求资源的默认值。例如,可以在这个方法中为资源添加或修改注解、标签或其他字段。

    func (m *YourMutatingHandler) Default(ctx context.Context, obj runtime.Object) error {
        // 实现默认值设置逻辑
    }

4.7.2 对于 Validating Webhook:

  • ValidateCreate 方法:这个方法在创建资源之前被调用,用于验证新资源是否符合要求。

    func (v *YourValidatingHandler) ValidateCreate(ctx context.Context, obj runtime.Object) error {
        // 实现创建时的验证逻辑
    }
  • ValidateUpdate 方法:这个方法在更新资源之前被调用,用于验证新旧资源之间的差异是否符合要求。

    func (v *YourValidatingHandler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error {
        // 实现更新时的验证逻辑
    }
  • ValidateDelete 方法(可选):这个方法在删除资源之前被调用,用于执行删除前的验证逻辑。

    func (v *YourValidatingHandler) ValidateDelete(ctx context.Context, obj runtime.Object) error {
        // 实现删除时的验证逻辑
    }

4.7.3 其他通用方法:

  • InjectDecoder 方法:这个方法用于注入一个解码器,它能够将 AdmissionReview 请求解码成具体的资源对象。

    func (h *YourHandler) InjectDecoder(d *admission.Decoder) error {
        h.decoder = d
        return nil
    }
  • Handle 方法:这是 Webhook 的核心处理方法,它接收 AdmissionReview 请求并返回 AdmissionResponse。

    func (h *YourHandler) Handle(ctx context.Context, req admission.Request, resp *admission.Response) {
        // 实现 Webhook 处理逻辑
    }

4.9 使用client-go库操作集群:

Go 语言和 Kubernetes (k8s) 结合的 SDK 主要用于简化 Go 语言编写的应用程序与 Kubernetes 集群的交互。这些 SDK 提供了一系列的库和工具,使得开发者能够更容易地实现以下功能:

  1. 客户端库:SDK 通常提供了 Kubernetes API 的客户端库,允许开发者以编程方式访问 Kubernetes API,包括但不限于创建、查询、更新和删除 Kubernetes 资源。

  2. 资源操作:简化了对 Kubernetes 资源(如 Pods、Services、Deployments、CRDs 等)的操作,开发者可以使用 Go 语言编写的代码来管理这些资源。

  3. 配置和认证:SDK 帮助处理与 Kubernetes 集群的认证和配置,例如使用 kubeconfig 文件或服务账号进行认证。

  4. 监听和响应事件:开发者可以使用 SDK 来监听 Kubernetes 集群中的事件,例如资源的创建、更新或删除,并根据这些事件触发相应的逻辑。

  5. 集成和自动化:在编写 Kubernetes Operators 或其他自动化工具时,SDK 可以简化集成过程,使得自动化脚本和应用程序能够更自然地与 Kubernetes 集群交互。

  6. 调试和日志记录:SDK 可能提供工具来帮助开发者在与 Kubernetes 集群交互时进行调试和记录日志。

  7. 扩展性:允许开发者扩展 Kubernetes 的功能,例如通过编写自定义的控制器来管理自定义资源。

  8. 模板和示例:SDK 通常提供代码模板和示例,帮助开发者快速开始项目并理解如何使用 Kubernetes API。

使用 Go 语言的 Kubernetes SDK(例如 client-go 库)来实现资源的自动化管理,通常涉及以下几个步骤:

  1. 设置客户端:首先,你需要设置一个 Kubernetes 客户端,这通常涉及到加载 kubeconfig 文件,以便客户端能够正确地认证并连接到 Kubernetes 集群。

  2. 资源操作:使用客户端库来创建、查询、更新或删除 Kubernetes 资源。这包括但不限于 Pods、Services、Deployments、StatefulSets、CRDs 等。

  3. 监听资源变化:通过 Kubernetes 的 watch 机制,你可以监听资源的变化。这允许你的应用程序响应资源的创建、更新或删除事件。

  4. 实现业务逻辑:基于监听到的事件,实现你的业务逻辑,例如自动扩展 Deployments、更新 ConfigMaps 或 Secrets、管理自定义资源等。

  5. 健康检查和重试机制:实现健康检查逻辑,确保资源处于期望的状态。如果发现问题,可以自动重试或采取其他恢复措施。

  6. 错误处理:妥善处理与 Kubernetes API 交互过程中可能出现的错误,例如 API 调用失败、资源不存在等。

  7. 使用控制器模式:在 Kubernetes 中,控制器是一种模式,用于运行一个后台进程来监视集群的状态,并确保实际状态与期望状态保持一致。使用 client-go 库,你可以实现自定义的控制器。

  8. 集成 RBAC:确保你的应用程序遵循 Kubernetes 的 RBAC 策略,为应用程序分配适当的角色和权限。

  9. 日志记录和监控:记录操作日志并集成监控,以便跟踪应用程序的行为和性能。

下面是一个简化的示例,展示如何使用 Go 语言的 Kubernetes SDK 来创建一个简单的 Pod:

package main
​
import (
    "context"
    "fmt"
    "log"
​
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)
​
func main() {
    // 加载 kubeconfig 文件
    config, err := clientcmd.BuildConfigFromFlags("", "~/.kube/config")
    if err != nil {
        log.Fatal(err)
    }
​
    // 创建 Kubernetes 客户端
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        log.Fatal(err)
    }
​
    // 创建一个新的 Pod 对象
    pod := &v1.Pod{
        ObjectMeta: metav1.ObjectMeta{
            Name: "example-pod",
        },
        Spec: v1.PodSpec{
            Containers: []v1.Container{
                {
                    Name:  "example-container",
                    Image: "nginx",
                },
            },
        },
    }
​
    // 在默认的命名空间中创建 Pod
    _, err = clientset.CoreV1().Pods("").Create(context.TODO(), pod, metav1.CreateOptions{})
    if err != nil {
        log.Fatal(err)
    }
​
    fmt.Println("Pod created successfully")
}

4.9.1 与使用crd(kubebuilder)的区别:

使用 Go 语言的 Kubernetes SDK(例如 client-go 库)创建 CRD(自定义资源定义)和使用 Kubebuilder 创建 CRD 有一些区别和联系:

  1. SDK 手动创建:使用 client-go SDK 创建 CRD 通常需要手动编写 Go 代码来定义 CRD 的结构,并且需要手动编写脚本来注册和处理 CRD。这个过程比较繁琐,需要对 Kubernetes API 和 Go 语言有较深的理解。

  2. Kubebuilder 框架:Kubebuilder 是一个更高级的框架,它提供了一套简化的流程来创建、管理和部署 CRD 以及相关的控制器。Kubebuilder 通过提供脚手架和自动化工具来减少开发者的工作量,使得创建 CRD 和控制器变得更加容易和快捷。

  3. 自动化代码生成:Kubebuilder 使用 controller-gen 工具来自动生成 CRD 的定义文件和相关的代码,这包括了 Kubernetes 对象的 YAML 文件和 Go 客户端代码。这样开发者就不需要手动编写这些代码。

  4. 声明式 API:Kubebuilder 支持声明式 API 的开发模式,允许开发者以声明的方式来定义期望的资源状态,而由控制器来实现这些状态。

  5. 控制器模式:无论是使用 client-go 还是 Kubebuilder,CRD 都需要与控制器(Controller)配合使用。控制器是响应资源变化并确保资源状态符合预期的后台进程。

  6. 多版本支持:Kubebuilder 支持在 CRD 中定义多个版本,并可以使用 webhook 来处理不同版本之间的转换。这是通过 +kubebuilder:storageversion 标签来实现的。

  7. 社区和维护client-go 是 Kubernetes 官方提供的客户端库,而 Kubebuilder 则是由 Kubernetes 社区维护的框架。使用 Kubebuilder 可以确保更好的社区支持和与 Kubernetes 生态系统的兼容性。

总的来说,Kubebuilder 提供了一种更高效、更简化的方式来创建和管理 CRD,而使用 client-go SDK 则需要更多的手动操作和对 Kubernetes API 的深入理解。两者都可以用来实现 CRD 的自动化管理,但 Kubebuilder 通过自动化和简化流程,使得开发更加快速和容易。