k8s基础知识概览

[top]

k8s基础知识概览


什么是k8s?

Kubernetes

| Kubernetes, also known as K8s, is an open source system for automating deployment, scaling, and management of containerized applications.

| Kubernetes 也称为 K8s,是用于自动部署、扩缩和管理容器化应用程序的开源系统。

因为英文单词 K 和 S 中间包含 8 个字母,所以简称 K8S,是一个全新的基于容器技术的分布式架构领先方案,是谷歌严格保密十几年的秘密武器----Borg 系统的一个开源版本,于 2014 年 9 月发布第一个版本,2015年 7 月发布第一个正式版本。

kubernetes 的本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。目的是实现资源管理的自动化,主要提供了如下的主要功能

  • 自我修复:一旦某一个容器崩溃,能够在1秒中左右迅速启动新的容器
  • 弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整
  • 服务发现:服务可以通过自动发现的形式找到它所依赖的服务
  • 负载均衡:如果一个服务起动了多个容器,能够自动实现请求的负载均衡
  • 版本回退:如果发现新发布的程序版本有问题,可以立即回退到原来的版本
  • 存储编排:可以根据容器自身的需求自动创建存储卷

k8s架构简介

k8s架构图

image

核心组件

  • 主节点
    • ApiServer : 资源操作的唯一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制
    • Scheduler : 负责集群资源调度,按照预定的调度策略将 Pod 调度到相应的 node 节点上
    • ControllerManager : 负责维护集群的状态,比如程序部署安排、故障检测、自动扩展、滚动更新等
    • Etcd :负责存储集群中各种资源对象的信息,相当于 K8S 的数据库
  • node
    • Kubelet : 负责维护容器的生命周期,即通过控制docker,来创建、更新、销毁容器
    • KubeProxy : 负责提供集群内部的服务发现和负载均衡

推荐的插件

  • CoreDNS :k8s内部的dns组件,服务名的解析者,与KubeProxy配合工作,它负责对内,对外的解析,这个可以理解为超微型的dns服务器
  • ingress : 想要外面的流量访问进来,需要这个组件,类似nginx的反代,它会按照编排好的规则进行服务发现以及对外服务
  • Dashboard : web操作页面,点点点控制一切,或者用三方提供的
  • Flannel 或者 Calico : CNI插件,为k8s的网络模型提供特定的支持

以部署一个 nginx 服务来说明 kubernetes 系统各个组件调用关系:

1. 首先要明确,一旦 kubernetes 环境启动之后,master 和 node 都会将自身的信息存储到 etcd 数据库中;
2. 一个 nginx 服务的安装请求会首先被发送到 master 节点的 apiServer 组件;
3. apiServer 组件会调用 scheduler 组件来决定到底应该把这个服务安装到哪个 node 节点上,在此时,它会从 etcd 中读取各个 node 节点的信息,然后按照一定的算法进行选择,并将结果告知 apiServer;
4. apiServer 调用 controller-manager 去调度 Node 节点安装 nginx 服务;
5. kubelet 接收到指令后,会通知 Container Runtime,然后由 Container Runtime 来启动一个 nginx 的 pod,pod 是 kubernetes 的最小操作单元,容器必须跑在 pod 中;
6. 当Pod启动后,一个 nginx 服务就运行了,如果需要访问 nginx,就需要通过 kube-proxy 来对 pod 产生访问的代理  

k8s与docker的爱恨情仇

docker k8s
单个容器的快速构建与管理 管理多个容器组成的集群
小/中规模 大规模

容器格式

在启动容器之前,我们需要构建或下载一个容器镜像,这是一个文件系统,里面装满了应用程序所需的一切:代码、二进制文件、配置文件、库和依赖项。

容器的普及表明需要开放的镜像标准。因此,Docker 公司和 CoreOS 于 2015 年建立了开放式容器计划(OCI) ,其使命是生产供应商中立格式。这一努力的结果是创造了两项标准:

  1. 定义镜像二进制格式的镜像规范。
  2. 描述如何拆开和运行容器的运行时规范。

OCI 维护称为runc的参考实现。容器和 CRI-O 都使用背景中的流体生成容器。OCI 标准带来了不同容器解决方案之间的互操作性。因此,一个系统内置的图像可以在任何其他合规堆栈中运行。
OCI 标准带来了不同容器解决方案之间的互操作性。因此,一个系统内置的镜像可以在任何其他合规堆栈中运行。

Docker Vs Kubernetes

初期k8s使用docker作为容器的运行时,在其第一个原始设计,Kubernetes是离不开docker的,因为docker是唯一的运行时支持。

然而,Docker从未被设计成在Kubernetes内运行。意识到这个问题,Kubernetes开发人员最终实现了一个名为容器运行时间接口(CRI) 的 API。此界面允许我们在不同的容器运行时之间进行选择,使平台更加灵活,对 Docker 的依赖性更小。

这一变化给Kubernetes团队带来了新的困难,因为Docker不知道CRI或支持CRI 。因此,在引入 API 的同时,他们不得不编写一个名为Dockershim的适配器,以便将 CRI 消息转换为 Docker 特定命令。

弃用Dockershim

KubernetesV1.20弃用了dockershim,拉开了离开docker的过渡的序幕。

image

(bgm: 他不止一次骗了你 不值得你再为他伤心~ 他不懂你的心假装冷静 他不懂爱情把它当游戏~ 他不懂表明相爱这件事 除了对不起就只剩叹息~ 他不懂你的心为何哭泣 窒息到快要不能呼吸 喔喔~)

image

工作负载(Workloads)

pod

k8s之pod

Pod是指一个或多个容器,具有共享存储和网络资源,以及如何运行容器的规范。Pod 的内容始终位于同一位置、共同调度,并在共享上下文中运行。

Pod是kubernetes的最小管理单元,在kubernetes中,按照pod的创建方式可以将其分为两类:

  • 自主式pod:kubernetes直接创建出来的Pod,这种pod删除后就没有了,也不会重建
  • 控制器创建的pod:kubernetes通过控制器创建的pod,这种pod删除了之后还会自动重建

工作负载是管理pod的中间层,使用Pod控制器之后,只需要告诉Pod控制器,想要多少个什么样的Pod就可以了,它会创建出满足条件的Pod并确保每一个Pod资源处于用户期望的目标状态。如果Pod资源在运行中出现故障,它会基于指定策略重新编排Pod。

Deployment and ReplicaSet

ReplicaSet

ReplicaSet的主要作用是保证一定数量的pod正常运行,它会持续监听这些Pod的运行状态,一旦Pod发生故障,就会重启或重建。同时它还支持对pod数量的扩缩容和镜像版本的升降级

image

ReplicaSet的资源清单
apiVersion: apps/v1 # 版本号
kind: ReplicaSet # 类型       
metadata: # 元数据
  name: # rs名称 
  namespace: # 所属命名空间 
  labels: #标签
    controller: rs
spec: # 详情描述
  replicas: 3 # 副本数量
  selector: # 选择器,通过它指定该控制器管理哪些pod
    matchLabels:      # Labels匹配规则
      app: nginx-pod
    matchExpressions: # Expressions匹配规则
      - {key: app, operator: In, values: [nginx-pod]}
  template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80

Deployment

为了更好的解决服务编排的问题,kubernetes在V1.2版本开始,引入了Deployment控制器。值得一提的是,这种控制器并不直接管理pod,而是通过管理ReplicaSet来间接管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。所以Deployment比ReplicaSet功能更加强大

image

Deployment主要功能有下面几个:

  • 支持ReplicaSet的所有功能
  • 支持发布的停止、继续
  • 支持滚动升级和回滚版本

因此,Deployment适合无状态的服务部署

Deployment的资源清单
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型       
metadata: # 元数据
  name: # rs名称 
  namespace: # 所属命名空间 
  labels: #标签
    controller: deploy
spec: # 详情描述
  replicas: 3 # 副本数量
  revisionHistoryLimit: 3 # 保留历史版本
  paused: false # 暂停部署,默认是false
  progressDeadlineSeconds: 600 # 部署超时时间(s),默认是600
  strategy: # 策略
    type: RollingUpdate # 滚动更新策略
    rollingUpdate: # 滚动更新
      maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数
      maxUnavailable: 30% # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
  selector: # 选择器,通过它指定该控制器管理哪些pod
    matchLabels:      # Labels匹配规则
      app: nginx-pod
    matchExpressions: # Expressions匹配规则
      - {key: app, operator: In, values: [nginx-pod]}
  template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80

StatefulSet

StatefulSet作为Controller为Pod提供唯一的标识, 它可以保证部署和scale的顺序
StatefulSet是为了解决有状态服务的问题(对应Deployments和Replica Sets是为无状态服务而设计)

特点
  • 适合部署有状态应用,解决Pod的独立生命周期,保持Pod启动顺序和唯一性
  • 稳定,唯一的网络标识符,持久存储(例如:etcd配置文件,节点地址发生变化,将无法使用)
  • 有序,优雅的部署和扩展、删除和终止(例如:mysql主从关系,先启动主,再启动从)
  • 有序,滚动更新
应用场景
  • 稳定的持久化存储, 即Pod重新调度后还是能访问到相同的持久化数据, 基于PVC来实现
  • 稳定的网络标志, 即Pod重新调度后其Pod Name和HostName不变, 基于Headless Service(即没有ip地址和端口的ClusterIP) 来实现
  • 有序部暑, 有序扩展, 即Pod是有顺序的, 在部若或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1, 在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态) , 基于initcontainers来实现
  • 有序收缩,有序删除(即从N-1到0)
无状态与有状态对比
#无状态服务的特点:
1)deployment 认为所有的pod都是一样的
2)不用考虑顺序的要求
3)不用考虑在哪个node节点上运行
4)可以随意扩容和缩容

#有状态服务的特点:
1)实例之间有差别,每个实例都有自己的独特性,元数据不同,例如etcd,zookeeper
2)实例之间不对等的关系,以及依靠外部存储的应用

DaemonSet

DaemonSet类型的控制器可以保证在集群中的每一台(或指定)节点上都运行一个副本。一般适用于日志收集、节点监控等场景。也就是说,如果一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建

image

特点
  • 每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上
  • 当节点从集群中移除时,Pod 也就被垃圾回收了
DaemonSet的资源清单
apiVersion: apps/v1 # 版本号
kind: DaemonSet # 类型       
metadata: # 元数据
  name: # rs名称 
  namespace: # 所属命名空间 
  labels: #标签
    controller: daemonset
spec: # 详情描述
  revisionHistoryLimit: 3 # 保留历史版本
  updateStrategy: # 更新策略
    type: RollingUpdate # 滚动更新策略
    rollingUpdate: # 滚动更新
      maxUnavailable: 1 # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
  selector: # 选择器,通过它指定该控制器管理哪些pod
    matchLabels:      # Labels匹配规则
      app: nginx-pod
    matchExpressions: # Expressions匹配规则
      - {key: app, operator: In, values: [nginx-pod]}
  template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80

Job and CronJob

Job

Job 负载批处理任务,仅执行一次的任务,它能够保证批处理任务的一个或者多个 Pod 成功执行结束

image

  • 当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量
  • 当成功结束的pod达到指定的数量时,Job将完成执行
Job的资源清单
apiVersion: batch/v1 # 版本号
kind: Job # 类型       
metadata: # 元数据
  name: # rs名称 
  namespace: # 所属命名空间 
  labels: #标签
    controller: job
spec: # 详情描述
  completions: 1 # 指定job需要成功运行Pods的次数。默认值: 1
  parallelism: 1 # 指定job在任一时刻应该并发运行Pods的数量。默认值: 1
  activeDeadlineSeconds: 30 # 指定job可运行的时间期限,超过时间还未结束,系统将会尝试进行终止。
  backoffLimit: 6 # 指定job失败后进行重试的次数。默认是6
  manualSelector: true # 是否可以使用selector选择器选择pod,默认是false
  selector: # 选择器,通过它指定该控制器管理哪些pod
    matchLabels:      # Labels匹配规则
      app: counter-pod
    matchExpressions: # Expressions匹配规则
      - {key: app, operator: In, values: [counter-pod]}
  template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
    metadata:
      labels:
        app: counter-pod
    spec:
      restartPolicy: Never # 重启策略只能设置为Never或者OnFailure
      containers:
      - name: counter
        image: busybox:1.30
        command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"]

PS:关于重启策略设置的说明:
如果指定为OnFailure,则job会在pod出现故障时重启容器,而不是创建pod,failed次数不变
如果指定为Never,则job会在pod出现故障时创建新的pod,并且故障pod不会消失,也不会重启,failed次数加1
如果指定为Always的话,就意味着一直重启,意味着job任务会重复去执行了,当然不对,所以不能设置为Always

CronJob

CronJob控制器以Job控制器资源为其管控对象,并借助它管理pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行job任务

image

CronJob的资源清单
apiVersion: batch/v1beta1 # 版本号
kind: CronJob # 类型       
metadata: # 元数据
  name: # rs名称 
  namespace: # 所属命名空间 
  labels: #标签
    controller: cronjob
spec: # 详情描述
  schedule: # cron格式的作业调度运行时间点,用于控制任务在什么时间执行
  concurrencyPolicy: # 并发执行策略,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业
  failedJobHistoryLimit: # 为失败的任务执行保留的历史记录数,默认为1
  successfulJobHistoryLimit: # 为成功的任务执行保留的历史记录数,默认为3
  startingDeadlineSeconds: # 启动作业错误的超时时长
  jobTemplate: # job控制器模板,用于为cronjob控制器生成job对象;下面其实就是job的定义
    metadata:
    spec:
      completions: 1
      parallelism: 1
      activeDeadlineSeconds: 30
      backoffLimit: 6
      manualSelector: true
      selector:
        matchLabels:
          app: counter-pod
        matchExpressions: 规则
          - {key: app, operator: In, values: [counter-pod]}
      template:
        metadata:
          labels:
            app: counter-pod
        spec:
          restartPolicy: Never 
          containers:
          - name: counter
            image: busybox:1.30
            command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 20;done"]

补充了解

Horizontal Pod Autoscaler (HPA)

我们已经可以实现通过手工执行命令实现Pod扩容或缩容,但是这显然不符合Kubernetes的定位目标–自动化、智能化。 Kubernetes期望可以实现通过监测Pod的使用情况,实现pod数量的自动调整,于是就产生了Horizontal Pod Autoscaler(HPA)这种控制器

HPA可以获取每个Pod利用率,然后和HPA中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现Pod的数量的调整。其实HPA与之前的Deployment一样,也属于一种Kubernetes资源对象,它通过追踪分析RC控制的所有目标Pod的负载变化情况,来确定是否需要针对性地调整目标Pod的副本数,这是HPA的实现原理

image

服务暴露

Service

k8s 集群中的每一个 Pod 都有自己的 IP 地址,那么是不是有 IP 了,访问起来就简单了呢,其实不然。

因为在 k8s 中 Pod 不是持久性的,摧毁重建将获得新的 IP,客户端通过会变更 IP 来访问显然不合理。另外 Pod 还经常会通过多个副本来实现负载均衡,客户端如何高效的访问哪个副本的问题也显现出来了

为了解决这个问题,Service 对象应运而生,它提供了一种抽象的方式,通过它可以访问一组 Pod。

image

Service 的类型

ClusterIP

k8s默认的ServiceType,通过集群内的ClusterIP在内部发布服务

  • Cluster IP仅仅作用于Kubernetes Service这个对象,并由Kubernetes管理和分配P地址
  • Cluster IP无法被ping,他没有一个“实体网络对象”来响应
  • Cluster IP只能结合Service Port组成一个具体的通信端口,单独的Cluster IP不具备通信的基础,并且他们属于Kubernetes集群这样一个封闭的空间
  • 在不同Service下的pod节点在集群内可以通过 Cluster IP 相互访问

image

NodePort

通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 NodeIP:NodePort ,可以从集群的外部访问一个 NodePort 服务

image

LoadBalance

在NodePort的基础上,借助Cloud Provider创建一个外部负载均衡器,并将请求转发到NodePort

image

ExternalName

把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 Kubernetes 1.7或更高版本的kube-dns才支持

无头Service

无头服务,不需要cluster-IP,直接绑定具体的Pod的IP,无头服务经常用于statefulset的有状态部署
Headless Service在Kubernetes中提供了一种机制,允许通过DNS解析获得后端Pod的IP列表,并且不执行任何网络层的负载均衡,使得客户端可以根据需求自行管理与后端Pod之间的连接和交互

  • Kubernetes DNS系统会为Headless Service创建一组A记录而不是CNAME记录,这些记录包含该服务所选择的Pod的所有IP地址。这意味着可以通过Pod名称和Headless Service的DNS后缀来直接访问到Pod,而不需要通过服务名称来访问。例如:pod-name.headless-service-name.namespace.svc.cluster.local
  • 由于没有ClusterIP作为代理目标,kube-proxy不会对流向Headless Service的请求进行任何转发或负载均衡操作
  • 客户端可以直接与后端Pod建立连接,这在需要直接访问特定Pod、使用原生协议(如基于主从复制的数据库同步)、或者需要客户端自己决定连接哪个后端实例的情况下非常有用
  • Headless Service常与StatefulSet结合使用,为每个Pod提供稳定的、可预测的网络标识符,这对于有状态应用非常重要,比如分布式数据库、消息队列等

Ingress

基于Service的实现只能支持四层代理转发,如果K8S集群规模较大运行的业务服务较多,NodePort端口管理成本会很高,因此Ingress出现了,Ingress是Kubernetes中实现七层负载均衡的一种方式,它可以将外部请求路由到集群内部的服务。

Ingress对象,其实就是对“反向代理”的一种抽象,简单的说就是一个全局的负载均衡器,可以通过访问URL定位到后端的Service

在Kubernetes中,Ingress是一种网络资源,它可以将外部请求路由到集群内部的服务。Ingress可以实现以下功能:

  • 实现基于域名的路由:Ingress可以根据请求的域名将请求路由到不同的后端服务
  • 实现基于路径的路由:Ingress可以根据请求的路径将请求路由到不同的后端服务

在Kubernetes中,Ingress资源是通过Ingress Controller来实现的。Ingress Controller是一个独立的进程,它负责监听集群外部的请求,并将请求路由到集群内部的服务。Ingress Controller可以使用不同的实现方式,例如Nginx、HAProxy、Envoy、Traefik等。

使用svc的服务发现

image

使用ingress的服务发现

image

数据持久化

Volume类型

  • emptyDir:一种简单的空目录,主要用于临时存储
  • hostPath:将宿主机的目录挂载到Pod中
  • nfs:NFS协议挂载
  • ConfigMap:存储配置文件
  • Persistent:数据卷类型

emptyDir

emptyDir类型的数据卷在创建pod时分配给该pod,并且直到pod被移除,该数据卷才被释放。该数据卷初始分配时,始终是一个空目录。同一个pod中的不同容器都可以对该目录执行读写操作,并且共享其中的数据。当pod被删除后,emptyDir数据卷中的数据将被永久删除。(注:容器奔溃时,kubelet并不会删除pod,而仅仅是将容器重启,因此emptyDir中的数据在容器崩溃并重启后,仍然是存在的)

默认情况下,emptyDir数据卷存储在node节点的存储介质(机械硬盘、SSD或网络存储)上

emptyDir的使用场景如下:

  • 空白的初始空间,例如合并/排序算法中,临时将数据保存在磁盘上
  • 长时间计算中存储检查点(中间结果),以便容器崩溃时,可以从上一次存储的检查点(中间结果)继续进行,而不是从头开始
  • 作为两个容器的共享存储

hostPath

HostPath 类型的数据卷将 Pod(容器组)所在节点的文件系统上某一个文件或目录挂载进容器组(容器内部),类似于docker中的bind mount挂载方式。这种数据持久化的方式,使用场景不多,因为它增加了pod与节点之间的耦合。

nfs

nfs 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享

ConfigMap

configMap 卷提供了向 Pod 注入配置数据的方法。 ConfigMap 对象中存储的数据可以被 configMap 类型的卷引用,然后被 Pod 中运行的容器化应用使用

PV (Persistent Volume)

PersistentVolume(PV存储卷)是集群中的一块存储空间,由集群管理员管理或者由Storage class(存储类)自动管理,PV和pod、deployment、Service一样,都是一个资源对象。

既然有了PV这个概念,那么PVC(PersistentVolumeClaim)这个概念也不得不说一下,PVC代表用户使用存储的请求,应用申请PV持久化空间的一个申请、声明。

比如说,pod是消耗node节点的计算资源,而PVC存储卷声明是消耗PV的存储资源。Pod可以请求的是特定数量的计算资源(CPU或内存等),而PVC请求的是特定大小或特定访问模式(只能被单节点读写/可被多节点只读/可被多节点读写)的存储资源

image

posted @ 2024-10-18 16:13  大胡萝卜没有须  阅读(56)  评论(0编辑  收藏  举报