Kubernetes 入门

一、K8s基本概念

基础概念

Iaas:Infrastructure as a Service 基础设施即服务;比如在腾讯云、阿里云租云服务器就是使用了laas

Pass:Platform as a Service 基础平台即服务;比如集成开发环境 IDEA,容器 Docker 等都属于 PaaS

Saas:Software as a Service 基础软件即服务;比如 WPS 就是属于SaaS

在这里插入图片描述
aPass(application Platform as a Service,即应用程序平台即服务) 是什么:参考这篇文章https://developer.aliyun.com/article/718714

apass 和 pass 的区别是什么?
aPaaS 和 PaaS 都可以完成软件的开发和部署,都支持云端访问。而两者的差异主要体现在用户人群和使用环境不一样:
PaaS 包含所有平台级别的服务,需要技术人员在本地完成应用程序的开发和数据提供,然后部署到PaaS平台上,再分发给用户使用
aPaaS 是 PaaS 的一种子形式,在 aPaaS 模式下,非技术人员可以直接在云端完成应用程序的搭建、部署、使用、更新和管理
使用 aPaaS 的解决方案,非技术人员就能构建业务应用程序
这些 aPaaS 厂商把开发软件所需要的权限、界面、数据、交互、流程抽象出来,组成一套简单好用配置页面。即使没有丰富的计算机专业知识和代码能力,在这些 aPaaS 平台中,任何人都可以创建一个有用的应用程序在这里插入图片描述

云原生基本概念

为什么要使用云平台

答:环境统一,按需付费,即开即用,稳定性强

国外常见云平台:亚马逊 AWS,微软 Azure

公有云:购买云服务商提供的公共服务器(或者公共资源)

公有云是最常见的云计算部署类型;公有云资源(例如服务器和存储空间)是由第三方云服务提供商拥有和运营,这些资源通过 Internet 提供;在公有云中,所有硬件、软件和其他支持基础结构均为云提供商所拥有和管理

公有云优势
  1. 成本更低:无需购买硬件或者软件,仅对使用的服务付费
  2. 无需维护:维护由服务提供商提供
  3. 近乎无限制的缩放性:提供按需资源,可满足业务需求
  4. 高可靠性:具备众多服务器,确保免受故障影响

私有云:自己搭建平台或者购买

私有云专供一个企业或者使用的云计算资源构成;私有云可在物理上位于组织的现场数据中心,也可由第三方服务商托管;但是在私有云中,服务和基础结构始终在私有网络上进行维护,硬件和软件专供组织使用

这样私有云可使组织更加方便地自定义资源,从而满足特定地 IT 需求;私有云的使用对象通常为政府机构、金融机构以及其他具备业务关键性运营并且希望对环境拥有更大控制权的中型到大型组织

私有云优势
  1. 灵活性更强:组织可定义云环境以满足特定业务需求
  2. 控制力更强:资源不与其他组织共享,因此能获得更高的控制力以及更高的隐私级别
  3. 可伸缩性更强:与本地基础结构相比,私有云通常具有更强的可伸缩性

没有一种云计算类型适用于所有人;多种不同的云计算模型,类型和服务已得到发展,可以满足组织快速变化的技术需求

部署云计算资源有三种不同的方法:公有云,私有云和混合云;采用的部署方法取决于业务需求


K8s 是一个用于容器化应用程序的编排工具,它负责:

  • 部署镜像和容器
  • 管理容器和集群的扩展
  • 容器和集群的资源管理
  • 服务的流量管理

组件说明

etcd:官方将它定义为一个可信赖的分布式键值存储服务,它能够为整个分布式集群存储一些关键数据,协助分布式集群的正常运转

coredns:可以为集群中的 SVC 创建一个域名 IP 的对应关系解析

ingress controller:官方 service 只能实现四层代理,ingress 可以提供七层代理

fedetation:提供一个可以跨集群中心多 K8s 统一管理功能

prometheus:提供 K8s 集群的监控能力

ELK:提供 K8s 集群日志统一分析接入平台

集群中的每一个应用称为一个 Pod

K8s 资源创建方式:命令行和 YAML 文件

Namespace:名称空间用来对集群资源进行隔离划分,默认只隔离资源,不隔离网络

Pod:运行中的一组容器,Pod 是 K8s 中应用的最小单位

K8s 主要安装的就是三个东西:Kubeadm,Kubelet,Kubectl

  • Kubeadm:用来初始化集群的指令
  • Kubelet:在集群中的每个节点上用来启动 pod 和 container 等
  • Kubectl:用来与集群通信的命令行工具

k8s 中基本所有资源的一级属性都是相同的,主要包含五个部分:

apiVersion 版本:由 k8s 内部定义,可以使用 kubectl api-versions 查询到

kind 类型:由 k8s 内部定义,可以使用 kubectl api-resources 查询到

metadata 元数据:主要是资源标识和说明,常用的有 name, namespace, labels 等

spec 描述:这是配置中最重要的部分,里面是对各种资源的详细描述

status 状态信息:里面的内容不需要定义,由 k8s 自动生成

spec 参数的子属性:

containers <[]Object> 容器列表:用于定义容器的详细信息

nodeName <String> 根据 nodeName 的值将 Pod 调度到指定的 Node 节点上

nodeSelector <map[]> 根据 NodeSelector 中定义的信息选择将该 Pod 调度到包含这些 label 的 Node 上

hostNetwork <boolean> 是否使用主机网络模式,默认为false, 如果设置成true,则表示使用宿主机网络

volumes <[]Object> 容器卷:用于定义 Pod 上挂载的存储信息

restartPolicy <string> 重启策略:表示 Pod 在遇到故障的时候的处理策略

k8s 在主容器启动之后和停止之前提供了两个钩子函数:

  • post start: 容器创建之后执行,如果失败了会重启容器
  • pre stop: 容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作

钩子处理器支持使用以下三种方式定义动作

  • Exec 命令: 在容器内执行一次命令
  • TCPSocket: 在当前容器尝试访问指定的 socket
  • HTTPGet: 在当前容器中向某 URL 发起 http 请求

Namespace

如果有多个用户或项目组使用同一个 Kubernetes Cluster,如何将他们创建的 Controller、Pod 等资源分开呢?

使用 Namespace 可以将一个物理上的 Cluster 逻辑上划分成多个虚拟 Cluster,每个 Cluster 就是一个 Namespace。不同 Namespace 里的资源是完全隔离的

K8s 集群在启动后,会默认创建几个 namespace (用来对集群资源进行隔离划分,默认只隔离资源,不隔离网络)

default                所有未指定 Namespace 的对象都会被分配到 default 空间
kubde-node-release     集群节点之间的心跳维护
kube-public            此命名空间下的资源所有人都可以访问(包括未认证用户)
kube-system            系统资源存放的命名空间
# 对于每个Pod,k8s都为其分配了一个IP,这样每个Pod都可以用IP访问到
# 使用Pod的ip+pod里面运行的容器端口,就可以访问Pod里面的容器
curl [ip]

kubectl exec -it mynginx -- /bin/bash  #进入Pod里面的容器

#启动一个pod
kubectl run mynginx --image=nginx      #这个pod利用nginx镜像

#查看pod的日志信息
kubectl desrcibe pod [pod-name] -n [namespace]

#删除Pod
kubectl delete pod [podname]

Pod 和 Deployment

创建一个Pod,很少单独使用Pod,一般优先使用 Deployment

apiVersion: v1    #版本
kind: Pod         #资源类型
metadata:         #资源元数据
  name: mynginx #Pod的名称
  labels:         #Pod上的标签(每个Pod必须要有一个标签)
    app: web
spec:            #容器详细信息
  containers:    #容器信息
  - name: nginx    #容器名称
    image: nginx   #容器使用的镜像
    ports:         #容器访问端口
    - containerPort: 80

创建一次部署(Deployment)

Deployment 用于管理一个无状态应用,对应一个 Pod 的集群,每个 Pod 的地位都是对等的,对Deployment 来说只是用于维护一定数量的 Pod,这些 Pod 有着相同的 Pod 模板

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-deployment
spec:
  replicas: 3
  selector:        #标签选择器,表示这个部署要匹配到哪个标签的label上,也就是Deployment如何查找要管理的Pod
    matchLabels:
      app: demo    #匹配标签为{"app"-"demo"}的pod
  template:        #可以看到这个template就是上面pod的spec信息
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - name: nginx
        image: nginx:1.12
        ports:
        - containerPort: 80

k8s Deployment 相关操作

1.直接编辑deployment配置文件
kubectl edit deployment/nginx-deployment  

2.检查Deployment的修订历史(就是回滚版本)
kubectl rollout history deployment/nginx-deployment

3.查看修订历史的详细信息
kubectl rollout history deployment/nginx-deployment --revision=3

4.回滚到特定的版本
kubectl rollout undo deployment/nginx-deployment --to-revision=4

5.命令行修改deployment里面的pod的镜像(也可使用edit编辑配置文件)
kubectl set image deployment/nginx-deployment nginx=nginx:1.17

Pod 能够具有多个容器,应用运行在容器里面,但是它有可能有一个或多个先于应用容器启动的 Init 容器

Init 容器与普通的容器非常像,除去以下两点

  • Init 容器总是运行到成功为止
  • 每个 Init 容器 都必须在下一个 Init 容器启动之前成功完成

如果 Pod 的 Init 容器启动失败,K8s 会不断地重启该 Pod,直到 Init 容器成功为止,然而如果 Pod 对应的 restartPolicy 为 Never,则它不会重新启动

因为 Init 容器具有与应用容器分离的单独镜像,所以它们的启动相关代码具有如下优势:

  1. 它们可以包含并运行使用工具,但是出于安全考虑,是不建议在应用程序镜像中包含这些工具
  2. 他们可以包含使用和定制化代码来安装,但是不能出现在应用程序镜像中。例如,创建镜像没必要 FORM 另一个函数,只需要在安装过程中使用类似 Pod、awk、python、或者 dig 这样的工具
  3. 应用程序镜像可以分离出创建和部署的角色,而没有必要联合它们构建一个单独的镜像
  4. Init 容器使用 Linux Namespace,所以相对来说应用程序容器具有不同的文件系统视图,因此,它们能具有访问 Secret 的权限,而应用程序容器则不能
  5. 它们必须在应用程序容器启动之前来完成,而应用程序容器是并行执行的,所以 Init 容器提供了一种简单的阻塞或延迟应用容器的启动方法,直到满足一组先决条件

每个 Pod 里面都可以包含一个或者多个容器,这些容器可以分为两类:

  1. 用户程序所在的容器,数量可多可少
  2. Pause 容器,这是 Pod 都会有的一个根容器,它的作用有两个:
  • 可以以它为依据,评估整个 Pod 的状态
  • 可以在根容器上设置IP地址,其他容器都以此IP来实现Pod内部的网络通信(Pod内部的通讯都是通过虚拟二级网络实现的)

Pod 的生命周期在这里插入图片描述

  1. 在 Pod 启动过程中,Init 容器会按顺序在网络和数据卷初始化后启动,每个容器必须在下一个容器启动之前成功退出
  2. 如果由于运行时或失败退出,将导致容器启动失败,它会根据 Pod 的 restartPolicy 进行重试,然而如果 Pod 的 restartPolicy 设置为 Always,Init 容器失败时就会使用 restartPolicy 策略
  3. 在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态。Init 容器的端口将不会在 Service 中进行聚集。正在初始化中的 Pod 处于 Pending 状态,但应该会将 Initializing 状态设置为 true
  4. 如果 Pod 重启,所有 Init 容器必须重新执行
  5. 对 Init 容器 spec 的修改被限制在容器 image 字段,修改其他字段都不会生效。更改 Init 容器的 image 字段,等价于重启该 Pod
  6. Init 容器具有应用容器的所有字段;除了 readinessProbe,因为 Init 容器无法定义不同于完成(Completion)的就绪(readiness)之外的其他状态,这会在验证过程中强制执行
  7. 在 Pod 中的每个 app 和 Init 容器的名称必须唯一;与其他任何共享同一个名称,会在验证时抛出错误

Pod 的状态
在这里插入图片描述
Pod 是 Kubernetes 中应用的最小单位,一个 Pod 中可以运行多个容器

命令行方式创建 Pod

kubectl run mynginx --image=nginx   #利用nginx镜像创建一个Pod
kubectl delete pod mynginx          #删除pod
kubectl logs myngix                 #得到启动日志

# 进入pod中的容器查看
# kubectl exec (POD | TYPE/NAME) [-c CONTAINER] [flags] -- COMMAND [args...]
kubectl exec -it pod-name -n dev -c busybox  -- cat /tmp/hello.txt 

#如果pod中只有一个容器,直接
kubectl exec -it myredis -n kube-system -- /bin/bash

Pod 调度

在默认情况下,一个 Pod 在哪个 node 节点上运行,是由 Scheduler 组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用过程中,有时这并不满足实际需求,因为很多情况下,我们想控制某些 Pod 运行在指定 node 上,K8s 提供了四大类调度方式:

  • 自动调度:运行在哪个节点上完全由 Scheduler 经过一系列的算法计算得到
  • 定向调度:NodeName、NodeSelector
  • 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
  • 污点(容忍)调度:Tains、Toleration

定向调度指的是利用在 Pod 上声明 NodeName 或者 NodeSelector,以此将 Pod 调度到期望的 Node 节点上。这里的调度是强制的,也就意味着即使调度的目标 Node 不存在,也会向上面进行调度,只不过 Node 运行失败而已

污点和容忍

污点(Taints)

前面的调度方式都是站在 Pod 的角度上,通过在 Pod 上添加属性,来确定 Pod 是否要调度到指定的Node 上,其实也可以站在 Node 的角度上,通过在 Node 上添加污点属性,来决定是否允许 Pod 调度过来

Node 上被设置污点之后就和 Pod 之间存在了一种相斥的关系,进而拒绝 Pod 调度进来,甚至可以将已存在的 Pod 驱逐出去

污点的格式为:key=value:effect , key 和 value 是污点的标签,effect 描述污点的作用,支持如下三个选项:

  • PreferNoSchedule: k8s 尽量避免把 Pod 调度到具有该污点的 Pod 上,除非没有其他节点可调度
  • NoSchedule: k8s 将不会把 Pod 调度到具有该污点的 Node 上,但也不会影响当前 Node 上已经存在的Pod
  • NoExecute: k8s 将不会把 Pod 调度到具有该污点的 Node 上,同时也会将 Node 上已存在的 Pod隔离
打污点
kubectl taint node k8s-node-01 key1=value1:NoSchedule  
kubectl taint node k8s-node-01 key2=value2:NoExecute   

删除污点
kubectl taint node k8s-node-01 key1:NoSchedule-
kubectl taint node k8s-node-02 key2:NoExecute-

使用 kubeadm 搭建的集群,默认就会给 master 节点添加一个污点标记,所以 Pod 就不会调度到master 节点上,如下图所示:

容忍(Toleration)

上面介绍了污点的作用,我们可以在 node 上添加污点用于拒绝 Pod 调度上来,但是如果就是想将一个 Pod 调度到一个有污点的 node 上去,就可以使用容忍

容忍的详细配置

tolerations:
- key: "tag"          # 对应着要容忍的污点的键
operator: "Equal"     # key-value的运算符,支持Equal和Exists
value: "heima"        # 对应着要容忍的污点的值
effect: "NoExecute"   # 对应污点的effect,空意味着匹配所有影响
tolerationSeconds:    # 容忍时间,当effect为NoExecute时生效,表示Pod在Node上的停留时间

RC

apiVersion: apps/v1   # 版本号
kind: ReplicaSet      # 类型
metadata:             # 元数据
  name:               # rs名称
  namespace:          # 所属命令空间
  labels:             # 标签
    controller: rs   
spec:                 # 详细描述
  replicas: 3         # 副本数量
  selector:           # 选择器
    matchLabels:      # Labels匹配规则
      app: nginx-pod
    matchExpressions:   # Expression 匹配规则
      - {key: app, operator: In, value: {nginx-pod}}
  template:             # 模板,就是当前控制器创建Pod所使用的模板,就是Pod的定义
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
kubectl edit rs pc-replicaset                        # 直接修改配置文件                
kubectl scale rs pc-replicaset --replicas=2 -n dev   # 扩缩容
kubectl delete rs pc-replicaset -n dev               # 删除rs,建议直接用yaml文件删除

Service 和 Ingres

在这里插入图片描述

从上面可以看出 Ingress 作为 service 的代理,service 又作为 Pod 的代理进行负载均衡

Service 的使用:假设有一组 Pod ,它们对外暴露了 9376 端口,同时还被打上了 "app=MyApp"标签

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - protocol: TCP
    port: 80          #service的端口(代理端口)
    targetPort: 9376  #pod的端口(被代理端口)

上述配置将创建一个名为 “my-service” 的 Service 对象,它会将请求代理到使用 TCP 的9376端口并且具有标签 “app=MyApp” 的 Pod 上。这个 Service 将会被指派一个 IP 地址 (通常称为 “Cluster IP”),它会被服务的代理使用,总结就是 Cluster IP 的 80 端口代理到 Pod 中的 9376 端口

k8s 默认有一个 SVC 名为 kubernetes,是 apiserver 的入口地址,这个地址就是 10.96.0.1

Endpoints 是指 master IP

Label 和 Annotation

与 Name 和 UID 不同,标签不需要有唯一性,一般来说我们期望许多对象具有相同的标签。但是每一个对象每个标签的 key 值必须是唯一的

通过标签选择器 (Label Selectors),客户端/用户能方便的识别出一组对象

API 目前支持了两种选择器:equality-based(基于平等)和 set-based (基于集合)的

#equality-based 
env = production    1.选择所有key等于env,值为production的资源
tier != fronted     2.选择所有key为tier,且值不等于fronted的资源

#set-based
env in (production,qa)         1.选择所有key等于env,且值为production或qa的资源
tier notin (fronted,backend)   2.选择所有key等于tier,且值不是fronted和backend的所有资源
partition                      3.选择所有key为partition的资源,不会检查value

RC (Replica Contoller)RS (Replication Set) 的区别在于 RS 是 RC 的升级版本

RC 只支持 equality-based 选择器,而 RS 支持 set-based 选择器,二者没有本质的区别,只是名字不一样

RC 和 RS 的作用都是用来确保运行指定数量的 Pod,目前推荐使用 Deployment ,不要用 RC 和 RS

可以使用 label 和 Annotation 将元数据附加到 k8s 对象。label 可以用于选择对象并查找某些满足条件的对象集合。而 Annotation 不用于标识和选择对象。Annotation 就和 label 一样,也是由 key-value 组成

"annotation" {
    "key1" : "value1",
    "key2" : "value2"
}

Annotation 通常用于记录:

  • 构建、发布的镜像信息,如时间戳、发行ID、git分支
  • 一些日志记录,一些工具信息,如名称,版本和构建信息

注:Annotation 不会被 k8s 直接使用,其主要目的是为了方便用户阅读查找

Controller 和 Operator

k8s 通过控制器 (Controller) 来实现应用的运维,如伸缩、升级等,控制器决定了创建 Pod 资源的方式和类型,在集群上管理和运行容器的对象通过 label-selector 相关联

5种控制器类型:Deployment、StatefulSet、DaemonSet、Job、CronJob 五种控制器类型总结

  1. 第一种控制器类型:Deployment

特点:

  • 部署无状态应用,只关心数量,不论角色等,称无状态;
  • 管理 Pod 和 ReplicaSet
  • 具有上线部署、副本设定、滚动升级、回滚等功能
  • 提供声明式更新,例如只更新一个新的 image

应用场景:Web 服务

  1. 第二种控制器类型:StatefulSet

StatefulSet 是用来管理有状态应用的对象。和 Deployment 相同的是,StatefulSet 管理了基于相同容器定义的一组Pod。但和 Deployment 不同的是,StatefulSet 为它们的每个 Pod 维护了一个固定的 ID。这些 Pod 是基于相同的声明来创建的,但是不能相互替换,无论怎么调度,每个 Pod 都有一个永久不变的 ID

特点:

  • 部署有状态应用
  • 解决 Pod 独立生命周期,保持 Pod 启动顺序和唯一性
  • 有序,优雅的部署和扩展、删除和终止(例如: mysql 主从关系,先启动主,再启动从)

应用场景:数据库

附:有状态和无状态的区别:

  • 无状态:1. deployment 认为所有的 pod 都是一样的 2.不用考虑顺序的要求 3.不用考虑在哪个 node 节点上运行 4. 可以随意扩容和缩容
  • 有状态:1.实例之间有差别,每个实例都有自己的独特性,元数据不同,例如 etcd, zookeeper 2.实例之间不对等的关系,以及依靠外部存储的应用
  1. 第三种控制器类型:DaemonSet

特点:

  • 在每一个节点 (Node) 上运行一个Pod
  • 新加入的 Node 也同样会自动运行一个Pod,当旧节点被删除时,它上面的Pod也会相应的被回收掉

应用场景:Agent (Agent是一种在分布式系统或者协作系统中,能够自主发挥作用的计算实体,简称为"智能体")、监控组件、日志组件等

  1. 第四种控制器类型:Job

Job 是用来控制批处理型任务的对象。批处理业务与长期伺服业务(Deployment)的主要区别是批处理业务的运行有头有尾,而长期伺服业务在用户不停止的情况下永远运行。Job 管理的 Pod 根据用户的设置把任务成功完成就自动退出(Pod自动删除)

应用场景:离线数据处理、视频解码等业务

  1. 第五种控制器类型:CronJob

CronJob 是基于时间控制的Job,类似于 Linux 系统的 crontab,在指定的时间周期运行指定的任务。

应用场景:通知、备份

Operator 是一个特定应用程序的 Controller,它扩展了一个 k8s API,替代运维工程师或SRE工程师来创建、配置和管理复杂的应用程序。在 k8s 官方文档中对其描述如下

Operator 是 k8s 的软件扩展,它利用自定义资源来管理应用程序及其组件。Operator 遵循 k8s 的原则,尤其遵循 controller loop

Secret 和 Configmap

configmap 和 secret 的区别就在于:configmap 用来存储一些公开的信息,而 secret 用来存储一些敏感数据

configmap是一种API对象,用来将非机密性的数据保存到键值对中。

使用方法: Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件

kubelet 在每次周期性同步时都会检查已挂载的 ConfigMap 是否是最新的。 但是,它使用其本地的基于 TTL 的缓存来获取 ConfigMap 的当前值。 因此,从更新 ConfigMap 到将新键映射到 Pod 的总延迟可能等于 kubelet 同步周期 (默认 1 分钟) + ConfigMap 在 kubelet 中缓存的 TTL(默认 1 分钟)

k8s secrets 用于存储和管理一些敏感数据,比如密码,token,密钥等敏感信息。它把 Pod 想要访问的加密数据存放到 Etcd 中。然后用户就可以通过在 Pod 的容器里挂载 Volume 的方式或者环境变量的方式访问到这些 Secret 里保存的信息了

通过 Volume 挂载到容器内部时,当该 Secret 的值发生变化时,容器内部具备自动更新的能力,但是通过环境变量设置到容器内部该值不具备自动更新的能(以环境变量的方式使用的ConfigMap数据不会被自动更新,更新这些数据需要重启Pod)。所以一般推荐使用Volume挂载的方式使用Secret

使用 Volume 挂载的方式时,当 secret 的值发生变化时,必须先把之前旧的 secret 的值删除,然后再 kubectl apply -f secret.yml,否则会报错

Secret 的几种类型:

  • Opaque: base64 编码格式的 Secret,用来存储密码、密钥等;但数据也可以通过 base64 –decode 解码得到原始数据,所以加密性很弱。
  • kubernetes.io/service-account-token: 用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中
  • kubernetes.io/dockerconfigjson: 用来存储私有 docker registry 的认证信息

Q:如果有 Pod 在使用 Cofigmap 或者 Secret,这时候把 configmap 或者 Secret 删掉会报错吗?

A:不会,configmap 和 Secret 的使用可分为通过环境变量或者挂载卷这两种方式

  • 如果使用环境变量的方式,则由于pod在启动过程中已经读入了configmap 的配置信息,所以删除configmap不会对pod产生什么影响;注:通过环境变量的方式使用configmap,如果configmap更新了则pod是无法察觉的,相当于一锤子买卖
  • 如果是通过挂载卷的方式使用,则pod会以configmap中的key为文件名在挂载目录创建一个文件,文件的内容就是configmap中的key对应的value值;这种方式如果configmap更新了则pod是会察觉到的,原因就在于kubelet 在每次周期性同步时都会检查已挂载的 ConfigMap 是否是最新的。 但是,它使用其本地的基于 TTL 的缓存来获取 ConfigMap 的当前值。 因此,从更新 ConfigMap 到将新键映射到 Pod 的总延迟可能等于 kubelet 同步周期 (默认 1 分钟) + ConfigMap 在 kubelet 中缓存的 TTL(默认 1 分钟); 这种方式删除了configmap也不会对pod产生什么影响,因为文件已经在pod内部创建了,删除了configmap只会导致这个对应的文件不会再更新了,不会产生其他影响

集群配置

k8s 中的网络

k8s 集群一共有三种 IP 地址

Node IP: Node节点的IP地址,也就是物理网卡的IP地址
Pod IP: Pod的IP地址  
Cluster IP:Service的IP地址

在这里插入图片描述

这就是 Node IP,Node IP 是 k8s 集群中每个节点物理网卡的 IP 地址,这是一个真实的物理网络(也可能是虚拟网络),每个节点(服务器)之间通过这个网络通信,集群外的机器与集群内的机器通信也是通过这个网络

在这里插入图片描述

这就是 Pod IP
在这里插入图片描述

这就是Service IP,注意 CLUSTER-IP 无法被ping通,是一个虚拟IP地址,只有 k8s 集群内部可以访问使用,CLUSTER-IP 只能结合 Service Port 组成一个具体的通信端口来通信,单独的 CLUSTER-IP 不能通信

从上图可以看出 ClusterIP 和 NodePort 是 Service 的两种类型,这二者区别在于:

通过 ClusterIP 暴露的服务只能在集群内部访问;NodePort 是通过每个 Node 节点上的 IP 和静态端口来暴露服务,NodePort 服务会路由到 ClusterIP 服务,这个从后面的端口映射就可以看出

也就是 ClusterIP 只对集群内可见,NodePort 对外部可见

二、安装 K8s

  1. 安装过程

安装预备环境

#1.修改主机名便于区分(这里本质是修改的/etc/hostname)
hostnamectl set-hostname k8s-master
hostnamectl set-hostname k8s-node-01
hostnamectl set-hostname k8s-node-02

2.关闭防火墙(必须设置)
ufw disable  #Ubuntu下临时关闭,这样关闭之后重启又会自动打开(查看是否开启了防火墙 ufw status)
systemctl disable ufw.service   #永久关闭防火墙(重新开启使用enable)
systemctl stop firewalld.servive    #Centos下关闭

3.关闭swap分区(必须设置)
swapoff -a                #临时关闭
sed -i 's/.*swap.*/#&/' /etc/fstab        #永久关闭

#4.允许 iptables 检查桥接流量(这个可以先不设置)
cat << EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

cat << EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

#5.让以上的配置生效
sudo sysctl --system         

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

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

安装 kubelet, kubectl, kubeadm

这一步主从节点都需要安装(实际上从节点只需要安装对应的 kube-proxy 就行)

  1. 更新 apt-get
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
  1. 换源
  • 更换阿里源
添加GPG密钥
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
 
add the Kubernetes apt repository: (换源)
cat << EOF >/etc/apt/sources.list.d/kubernetes.list
> deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
> EOF

sudo apt-get update
apt-get install -y kubelet=1.23.8-00 kubeadm=1.23.8-00 kubectl=1.23.8-00
sudo apt-mark hold kubelet kubeadm kubectl

上面这些步骤 master 和 slave 都需要执行

master 节点执行:

  1. 初始化,注意这里的 kubernetes-version 要和上面安装的 kubeadm、kubelet、kubectl 一致
kubeadm init  --pod-network-cidr 172.16.8.0/24 --service-cidr 172.16.9.0/24 --image-repository='registry.cn-hangzhou.aliyuncs.com/google_containers' --kubernetes-version v1.23.8  
# 镜像仓库切换成阿里云镜像仓库

如果 init 执行过程中出现

在这里插入图片描述

参考这篇文章进行解决

如果需要将pod调度到master节点上,可以执行如下命令

kubectl taint nodes --all node-role.kubernetes.io/master-

flannel 配置:

curl -O https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
vim kube-flannel.yml

修改network配置:
在这里插入图片描述

然后创建配置

kubectl apply -f kube-flannel.yml

Notes: 如果发现 flannel 的 pod 一直起不来,则需要替换镜像为本地镜像试试即可

slave节点

按照 master 节点上的 kubeadm token create --print-join-command 直接加入即可

安装失败需要重新执行:kubeadm reset

安装报错

  1. 从节点加入到了 k8s 集群中

在这里插入图片描述

但是运行当 pod 分配到了从节点上时却出错
在这里插入图片描述

解决方法:需要在从节点配置 flannel 网络文件

FLANNEL_NETWORK=172.16.0.0/16
FLANNEL_SUBNET=172.16.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
  1. 不要装太新的版本,之前一直尝试装1.25.0版本一直报错,就用上面的1.23.8版本,亲测可用

k8s 1.24 之后默认的运行时就是 containerd,就不要再安装 docker 了

apt-get install -y kubelet=1.25.0-00 kubeadm=1.25.0-00 kubectl=1.25.0-00

  1. Yaml 文件

介绍:YAML 仍是一种标记语言。为了强调这种语言以数据作为中心,而不是以标记语言为重点,YAML 是一个可读性高,用来表达数据序列的格式

k8s 集群中对资源管理和资源对象编排部署都可以通过声明样式(YAML)文件来解决,也就是可以把需要对资源对象的操作编辑到 YAML 格式文件中,我们把这种文件叫做资源清单文件,通过 kubectl 命令直接使用资源清单文件就可以实现对大量的资源对象进行编排部署

kubectl create -f xxx.yaml      # 以yaml文件创建
kubectl deploy -f xxx.yaml      # 重新部署yaml文件

k8s 中资源可以使用 YAML 描述,其内容可以分为如下四个部分:

  • typeMeta:对象类型的元信息,声明对象使用哪个API版本,哪个类型的对象
  • objectMeta:对象的元信息,包括对象名称,使用的标签等
  • spec:对象的期望状态,例如对象使用什么镜像,有多少副本等
  • status:对象的实际状态,只能在创建对象后才能看到,创建对象时无需指定
apiVersion: apps/v1           当前配置的版本
kind: Deployment              要创建的资源类型
metadata:                     资源的元数据
  name: nginx-deployment        
spec:                         该Deployment的规格说明(spec就是期望的状态)
  replicas: 2                 副本数
  selector:
    matchLabels:
      app: web_server
  template:                   定义Pod的模板
    metadata:                 定义Pod的元数据,至少要定义一个label,且label的key和value可以任意指定
      labels:
        app: web_server
    spec:                     描述pod的规格,此部分定义pod中每一个容器的属性
      containers:
      - name: nginx
        image: nginx:1.12
  1. 排错

注意:K8s 任何问题排错的两大前提

  • 必须关闭 swap 分区
  • 必须关闭防火墙 Ubuntu下使用 ufw disable,Centos 下使用 systemctl stop firewalld.service

查看日志命令

journalctl -fu kubelet
-f 表示跟踪日志文件 (读取最新条目)
-u 表示查看指定服务的日志
-r 表示按照时间倒序查看

journalctl -r --since "10 min ago"  倒序查看最近十分钟的日志信息

从2012年开始,大部分 linux 发行版本开始从传统的 systemv 初始化系统移植到一个叫做 systemd 的全新系统。systemd 用来启动系统并管理进程。systemd 包含了一个叫做 journalctl 的辅助组件,其主要作用是管理系统的事件日志记录

journalctl 可以查看所有的系统日志文件

添加命令补全功能

kubectl 命令补全

三、K8s 二次开发

非 root 用户如何使用 kubectl 管理 k8s 集群

sudo -i chao               1.切换到目标非 root 用户
mkdir -p $HOME/.kube       2.在当前用户家目录下创建一个 .kube 文件夹
sudo cp -i /etc/kubernetes/admin.conf  $HOME/.kube/config  3.拷贝admin.conf文件
sudo chown chao:chao $HOME/.kube/config    4.重设文件所有者和所属组

k8s 架构

首先架构中主要的组件有 kubectl、kube-apiserver、kube-controller-manger、kubelet、kube-proxy 和 container 等。作为开发者,还需要深入了解 client-go 库

k8s 不同组件之间是松耦合架构,各组件之间各司其职,保证整个集群的稳定运行

在这里插入图片描述
在这里插入图片描述

k8s 集群由 master 节点和 Node (worker) 节点组成

api-server:所有服务访问的统一入口(资源操作的唯一入口),并提供认证、授权、访问控制、API注册等机制

  • Master:节点指的是集群控制节点,管理和控制整个集群,基本上 k8s 所有控制指令都发送给它,它负责具体的执行过程。在 Master 节点上运行着以下三个组件
  1. Controller-Manager :k8s 集群中所有资源对象的自动化控制中心,维护管理集群的状态,比如故障检测,自动扩展,滚动更新等
  2. Scheduler :负责资源调度,按照预定的策略将 Pod 调度到相应的机器上 (注意这里不说 Node 是因为 Master 节点上也可以运行 Pod) ;Scheduler 只是负责计算应该运行在哪一个节点上,实际调度的是 Controller Manager
  3. etcd:键值对数据库,存储 K8s 集群的所有重要信息,保存整个集群的状态;这是 k8s 默认使用的数据库,用户也可以更改成 MySQL,Redis 这些数据库
  • Node 节点会被 Master 分配一些工作负载 (Docker 容器),当某个 Node 宕机时,该节点上的工作负载会被 Master 自动转移到其他节点上,在 Node 上主要运行着以下三个组件
  1. kubelet:负责 Pod 对应容器的创建,启停等任务(直接跟容器引擎 docker engine 交互实现容器的生命周期管理,同时也负责 Volume 和网络的管理),同时与 Master 密切合作实现集群管理的基本功能;可以理解为 kubelet 是用来操作容器引擎进行容器的创建
  2. kube-proxy:负责写入规则至 IPTABLES、IPVS 实现服务映射访问,负责为 service 提供集群内部的服务发现和负载均衡
  3. docker (Docker Engine):容器引擎,负责本机容器的创建和管理

组件调用流程

以部署一个Nginx服务来说明k8s各个组件之间的调用关系

  1. 一旦 k8s 环境启动之后,master 和 node 都会将自身的信息存储到 etcd 数据库中去
  2. 一个 Nginx 服务的安装请求会首先发送到 master 节点上的 apiServer 组件
  3. apiServer 组件会调用 Scheduler 组件来决定到底应该把这个服务安装到哪个 node 节点上,它会从 etcd 中读取各个 node 节点的信息,然后按照一定的算法进行选择,并将结果告知 apiServer
  4. apiServer 调用 controller-manager 去调度 node 安装 Nginx 服务
  5. Kubelet 接收到指令后,会通知 docker,然后由 docker 来启动一个 Nginx 的 Pod
  6. 这样一个 Nginx 服务就运行了,如果需要访问 Nginx,就需要通过 kube-proxy 来对 Pod 产生访问的代理;这样外界用户就可以访问集群中的 Nginx 服务了

创建一个 Pod 的流程

  1. 用户或者控制器通过 kubectl、Rest-API 或者支持的客户端库向 API-Server 提交 Pod 创建请求
  2. API-Server 将 Pod 对象写入 etcd 中进行永久性存储。如果写入成功,那么 API-Server 就会收到来自 etcd 的确认信息并将创建结果返回给客户端
  3. API-Server 处就能反映出 Pod 资源发生的变化,一个新的 Pod 被创建出来
  4. Scheduler 监听到 API-Server 处新的 Pod 被创建。它首先会查看该 Pod 是否已经被调度 (spec.nodeName 是否为空)。如果该 Pod 并没有被调度到任何节点。那么 Scheduler 便会给它分配一个最优节点,并把它更新到 spec.nodeName 中,从而完成 Pod 的节点绑定
  5. Scheduler 对 Pod 的更新也将被 API-Server 写回到 etcd 中。Scheduler 同样会监听到 Pod 对象发生了变化。但是由于它已经调度过该 Pod (spec.nodeName 不为空) ,所以它将不做任何处理
  6. Kubelet 也会一直监听 API-Server 处 Pod资源的变化。当其发现 Pod 被分配到自己所在的节点上 (自身节点名称和 Pod 的 spec.nodeName相等)时,kubelet 将调用 CRI gRPC 向容器运行时申请启动容器
  7. Kubelet 首先会调用 CRI 的 RunPodSandbox 接口。Containerd 要确保 PodSandbox(即 Infra 容器)的镜像是否存在。因为所有 PodSandbox 都使用同一个 pause 镜像,如果节点已经有运行的 Pod,那么这个 Pause 镜像就已经存在。接着会创建一个新的 Network NameSpace,调用 CRI 接口为 Network NameSpace 设置容器网络,Containerd 会使用这个 Network NameSpace 启动 PodSandbox
  8. 当 PodSandbox 启动后,kubelet 才会在 PodSandbox 下请求创建容器。这里 kubelet 会首先检查容器镜像是否存在,如果容器镜像不存在,则调用 CRI 的 PullImage 接口并通过 Containerd 将容器镜像记录下来
  9. 当容器镜像下载完成后,kubelet 调用 CRI 的 CreateContainer 接口向容器运行时发送一条 CreateContainerRequest 消息,请求创建容器
  10. 当容器创建成功后,kubectl 会调用 CRI 的 StartContainer 接口向容器运行时请求启动容器
  11. 无论容器是否创建和启动成功,kubelet 都会将最新的容器状态更新到 Pod 对象的 Status 中,让其他控制器也能监听到 Pod 对象的变化,从而采取相应的措施

kubeconfig 文件解析

用于配置集群访问的文件称为 kubeconfig 文件,这是引用配置文件的常用方法,但并不是说有一个名为 kubeconfig 的文件

kubectl 命令行根据使用 kubeconfig 文件来查找选择集群所需的信息,并与集群的API服务通信,只要为 kubectl 提供链接 apiserver 的配置(kubeconfig),则 kubectl 可以在任何地方操作该集群

apiVersion: v1  #apiVersion和下面的kind标识客户端解析器的模式
clusters:       #配置要访问的k8s集群
- cluster:      #cluster包含k8s集群的端点数据,包括集群的证书颁发机构以及完整的URL
    certificate-authority-data: ...
    server: https://10.7.18.198:6443
  name: kubernetes  #集群的名字
    
contexts:        #配置访问k8s集群的具体上下文环境
- context:  
    cluster: kubernetes                #cluster是k8s集群的名称代号
    user: kubernetes-admin            #user是访问集群的用户账号代号
  name: kubernetes-admin@kubernetes  #上下文的名称代号
  
#current-context记录当前kubectl默认使用的上下文信息
current-context: kubernetes-admin@kubernetes        
kind: Config
preferences: {}  #preference 指定当前可选(或当前未使用)的 kubectl 首选项

users:
- name: kubernetes-admin
  user:   #配置用户访问的信息,用户名以及证书信息,是客户端向k8s集群进行身份认证的凭据
    #用于访问k8s集群的客户端证书
    client-certificate-data: ....
    #客户端证书对应的私钥
    client-key-data: ....

开发组件

cobra

Kubectl 是 k8s 官方提供的命令行工具(CLI),用户可以通过 kubectl 以命令行交互的方式(cobra)、对 kubernetes API Server 进行操作,通信协议使用 HTTP/JSON

Kubectl 发送相应的 HTTP 请求,请求由 kubernetes API Server 接收、处理并将结果反馈给 kubectl。kubectl 接收到响应并展示结果。至此,kubectlkube-apiserver 的一次请求周期结束

client-go

Kubectl 是通过命令行交互的方式与 kubernetes API Server 进行交互的,k8s 还提供了通过编程的方式与 kubernetes API Server 进行通信。

client-go 是从 k8s 的代码中单独抽离出来的包,并作为官方提供的 Go 语言的客户端发挥作用。client-go 简单、易用,k8s 系统的其他组件与 kubernetes API Server 通信的方式也基于 client-go 实现

dashboard

直接通过 Web UI 与 k8s 进行交互

架构组件

kube-apiserver

Kube-apiserver 组件,也称 Kubernetes API Server。它负责将 Kubernetes “资源组/资源版本/资源” 以 RESTful 风格的形式对外暴露并提供服务。Kubernetes 集群中的所有组件都通过 kube-apiserver 组件操作资源对象。kube-apiserver 组件也是集群中唯一与 Etcd 集群进行交互的核心组件。

例如,开发者通过 kubectl 创建了一个 Pod 资源对象,请求通过 kube-apiserver 的 HTTP 接口将 Pod 资源对象存储至 Etcd 集群中。

Etcd 集群是分布式键值存储集群,其提供了可靠的强一致性服务发现。Etcd 集群存储 k8s 系统集群的状态和元数据,其中包括所有的 k8s 资源对象信息、集群节点信息等。k8s 将所有数据存储到 Etcd 集群中的前缀为 /registry 目录下

Kube-apiserver 属于核心组件,对于整个集群至关重要,他具有以下重要特性

  • 将 k8s 系统中的所有资源对象都封装成 RESTful 风格的API接口进行管理
  • 可进行集群状态管理和数据管理,是唯一与 Etcd 集群交互的组件
  • 拥有丰富的集群安全访问机制,以及认证、授权及准入控制器
  • 提供了集群各组件的通信和交互功能

如果使用的是 kubeadm 安装 Kubernetes 集群,Kubernetes apiserver 通过 static pod ****启动,其 yaml 文件的位置在 /etc/kubernetes/manifests/kube-apiserver.yaml

Kubelet 会监听该文件的变化,当配置发生修改时,kubelet 会自动终止之前的pod,并自动创建一个使用新的配置参数的Pod作为替代

可以发现,不止 kube-apiserver ,还有 etcd 以及 kube-controller-manager 以及 kube-scheduler 都是 static pod
在这里插入图片描述

kubelet

Kubelet 组件,用于管理节点,运行在每个 k8s 节点上;kubelet 组件用来接收、处理、上报 kube-apiserver 组件下发的任务。kubelet 进程启动时会向 kube-apiserver 注册节点自身信息。它主要负责所在节点 (Node) 上的 Pod 资源对象管理,例如 Pod 资源对象的创建、修改、删除、驱逐以及 Pod 生命周期管理等

Kubelet 组件会定期监控所在节点的资源使用状态并上报给 kube-apiserver 组件,这些资源数据可以帮助 kube-apiserver 调度器为 Pod 资源对象预选节点。kubelet 也会对所在节点的镜像和容器做清理工作,保证节点的镜像不会占满磁盘空间、以及让删除的容器释放相关资源

Kubelet 开发接口
在这里插入图片描述

  • Container Runtime Interface:简称 CRI (容器运行时接口),提供运行时通用接口服务。CRI 定义了容器和镜像服务的接口。CRI 将 kubelet 组件与容器运行时进行解耦,将原来完全面向 Pod 级别的内部接口拆分成 Sandbox 和 Container 的 gRPC 接口,并将镜像管理和容器管理分离给不同的服务
  • Container Network Interface:简称 CNI(容器网络接口),提供网络通用插件接口服务。CNI 定义了 k8s 网络插件的基础、容器创建时通过 CNI 插件配置网络
  • Container Storage Interface:简称 CSI(容器存储接口),提供存储通用插件接口服务。CSI 定义了容器存储卷标准规范、容器创建时通过 CSI 插件配置存储卷

如下图所示,在 kubectl 请求链路上, kubelet 将扮演服务器端,负责处理由 kube-apiserver(KAS) 转发来的请求,这就要求 KAS 和 kubelet 之间需要存在一条网络通路,允许 KAS 主动访问 kubelet
在这里插入图片描述

Operator

k8s 中的 operator 就是控制器(确切的说是一种自定义控制器)

在机器人技术和自动化领域,控制回路(Control Loop) 是一个非终止回路,用于调节系统状态。将其应用于 k8s 中,控制器的含义是通过监控集群的公共状态,并致力于将当前状态转变为期望的状态。至于如何转变,由控制器的控制循环处理相应的逻辑,一个控制器至少追踪一种类型的 k8s 资源。该资源的控制器负责确保其当前状态(Status)接近期望状态 (Spec)。不同的控制器可以相互配合完成一项负责的任务。

在这里插入图片描述

此图就可以理解为一个 Controller Loop

Operator 会持续跟踪与特定类型的自定义资源相关的集群事件,可以跟踪的关于这些自定义资源的事件类型有:Add、Update、Delete

当 Operator 接收到任何消息时,它将采取行动将 k8s 集群或外部系统调整到所需的系统,作为其在自定义 Controller 中的和解循环(reconciliation loop)的一部分

控制器逻辑:

  • 观察:通过监控 k8s 资源对象的变化事件来获取当前对象状态,我们只需要注入 EventHandler 让 client-go 将变化的事件对象信息放入到 WorkQueue 中
  • 分析:确定当前状态和期望状态的不同,由 Worker 完成
  • 执行:执行能够驱动对象当前状态变化的操作,由 Worker 完成
  • 更新:更新对象的当前状态,由 Worker 完成

总结:k8s 的 controller 其实就是一个 reconcilation process,其实现可以简单抽象为如下:

for {
    desired := getDesiredState()
    current := getCurrentState()
    makeChanges(desired, current)
}

自定义资源(CRD)的 Operator 开发工具选择:

  • Kubebuilder:由 k8s-sigs 维护,包含 CRD 和 Controller开发
  • Operator-sdk:由 coreOS 维护,早期不包含 CRD 开发,在往 kubebuilder 方向进行开发

本质上它们都是在k8s控制器运行时上进行的封装,主要都是脚手架的生成,使用体验相差不大

定义自定义资源CRD时不要使用group为 *.k8s.io 或者 *kubernetes.io命名后缀,这两个后缀受到k8s的保护,避免自己定义的Group和k8s定义的Group混淆,不要使用这两个后缀

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: students.bolingcavalry.k8s.io  #像这种就是错误的,不要以这个后缀命名
spec:
kubectl get crd    #获取自定义资源对象,crd默认是namespace级别的,不需要加上-n筛选出ns
kubectl delete crd {} #删除自定义资源对象

Kubernetes Controller编写规范

Operator 架构(或者说 client-go 的内容)
在这里插入图片描述
在这里插入图片描述

WorkQueue 就是因为怕事件产生的速度大于 Controller 消费的速度,所以先暂放到这个 WorkQueue 中

Informer

为什么需要 informer

我们知道Controller的基本逻辑就是一个 reconcilation process,其实现可以简单抽象为如下:

for {
    desired := getDesiredState()
    current := getCurrentState()
    makeChanges(desired, current)
}

这样一个 reconcilation 需要不断获取 resource 信息,如果由 controller 来轮询获取 resource 信息则需要不断请求API-Server,无疑会降低 API-Server 的性能,而且每次查询的话还不一定有变更的 event 发生

于是 client-go 提供了 informer 这一机制,Informer 来查询 resource data 并将其存储到一个 local cache,一旦存储当且仅当 infromer 发现 resource 存在变化时才会触发 event 事件

Informer 工作原理

Informer工作原理

A single informer creates a local cache for itself. But in reality, a single resource could be watched by multiple controllers. And if each controller creates a cache for itself, there are synchronisation issues as multiple controllers have a watch on their own cache. client-go provides a Shared Informer which is used so that the cache is shared amongst all controllers. Every built-in Kubernetes resource has an Informer.

Informer 有三个 component

  • Reflector 从 kube-apiserver 中 list & watch 资源对象(注:是先 list 获取全量的对象集合,然后通过 watch 来获取增量的对象,然后更新本地缓存),然后调用 DeltaFIFO 的 Add/Update/Delete/Replace 方法将对象的变化包装成 Delta 并将其丢进 DeltaFIFO 中;简而言之就是将 Etcd 的对象及变化反射到 DeltaFIFO 中
  • DeltaFIFO 中存储着 map[object key]Delta 以及 object key 的 queue,Delta 装有对象以及对象的变化类型(Added/Updated/Deleted/Sync);Reflector 负责处理 DeltaFIFO 的输入,Controller 负责处理 DeltaFIFO 的输出;DeltaFIFO 就是一个存储相关资源事件的 FIFO queue
  • Indexer 中有 Informer 维护的指定的资源对象的一份本地缓存,可通过该缓存来获取资源对象,以减少对 apiserver、对etcd的请求压力
Example: pod Informer

k8s 中的 PodInformer

var kubeconfig *string
if home := homedir.HomeDir(); home != "" {
    kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "")
}

config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
    panic(err)
}

//1.创建一个k8sclient
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
    panic(err)
}

//2.初始化shared informer factory以及pod informer
factory := informers.NewSharedInformerFactory(clientset, 30*time.Second)
podInformer := factory.Core().V1().Pods()
informer := podInformer.Informer()

//3.注册informer的自定义ResourceEventHandlerFuncs
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
    AddFunc:    func(obj interface{}) { fmt.Println("Add") },
    UpdateFunc: func(oldObj, newObj interface{}) { fmt.Println("Update") },
    DeleteFunc: func(obj interface{}) { fmt.Println("Delete") },
})

//4.启动shared informer factory, 开始informer的list & watch操作
stopper := make(chan struct{})
go factory.Start(stopper)

//5.等待 informer 从 kube-apiserver 同步资源完成,即informer的list操作获取的对象都存入到informer的本地缓存indexer中
if !cache.WaitForCacheSync(stopper, informer.HasSynced) {
    runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
}

//6.创建Lister
podLister := podInformer.Lister()
//从informer中的indexer本地缓存中获取对象
podList, err := podLister.List(labels.Everything())
if err != nil {
    panic(err)
}

for _, pod := range podList {
    fmt.Println(pod.Name)
}

time.Sleep(time.Hour)

Client 类型

client-go 支持 4 种 Client 客户端对象与 kubernetes API Server 交互的方式,Client 交互对象如下

在这里插入图片描述

  • RESTClient:最基础的客户端,提供最基本的封装
  • ClientSet:是一个 Client 的集合,在 Clientset 中包含了所有 k8s 内置资源的 Client,通过 Clientset 便可以很方便的操作如 Pod、Service 这些资源
  • DynamicClient:动态客户端,可以操作任意的 k8s 资源,包括 CRD 定义的资源
  • DiscoveryClient:用于发现 k8s 提供的资源组、资源版本和资源信息,比如 kubectl api-resources

RESTClient 的使用

  • RESTClient 为创建 RESTClient 准备 config,比如限速器、编解码器
  • UnversionedRESTClientFor :与 RESTClientFor 类似,只是允许 config.GroupVersion 为空

WorkQueue

使用 WorkQueue 是因为 Event 产生的速度大于 EventHandler 处理的速度,所以需要先暂时放入 WorkQueue 缓存下

Client-go 提供了三种队列类型 – 通用队列、延迟队列、限速队列

通用队列
type Interface interface {
    Add(item interface{})    //添加一个元素
    Len() int                //队列元素个数
    Get() (item interface{}, shutdown bool)    //获取一个队列元素
    Done(item interface{})    //标记一个元素已经处理完
    ShutDown()                //关闭队列
    ShuttingDown() bool       //是否正在关闭
}

延迟队列
type DelayingInterface interface {
    Interface    //延迟队列包含普通队列
    AddAfter(item interface{}, duration time.Duiation)    //延迟添加
}

限速队列
type RateLimitingInterface interface {
    DelayingInterface                    //限速队列包含延迟队列
    AddRateLimited(item interface{})     //往队列里加入一个元素
    Forget(item interface{})             //停止元素重试
    NumRequeues(item interface{}) int    //记录这个元素被处理多少次
}

四、k8s源码

k8s.io 在 vendor 下的件夹底下

源码目录详细说明
.github社区提交问题者通常会提一些问题,而问题的类型分为不同的模板类型,此位置放置一些归类问题的模板
CHANGELOG版本日志目录,比如新发布的1.23版本可以找到对应的二进制包、变更的具体说明
api包含 k8s API 的 openAPI 规范
build存放用于构建相关的脚本,构建编译 k8s 组件等,通用的一个脚本 make-image.sh
cluster早期安装集群的脚本 kube-up(目前已经不再维护)
cmd存放可执行文件的入口代码,每一个入口函数都对应一个main函数,像一些组件的源码位置都在此位置存放
docs存放设计或用户使用的文档
hack存放与构建、测试等相关的脚本,这些脚本可以保证 k8s 的持续开发,增强代码健壮性的一部分
logok8s 的 logo 图标位置
pkg存放核心库的代码,可被项目内部和外部进行使用
plugin存放 k8s 插件代码目录、例如认证、授权等相关插件
staging存放部分核心库的暂存目录
test存放测试工具及测试数据的目录

在 Kubernetes 资源的数据结构定义中,一般都会包含一个 metav1.TypeMeta 和一个 ObjectMeta 成员 TypeMeta里定义了该资源的类别和版本,对应我们平时写 json 文件或者 yaml 文件时的 kind: Pod 和 apiVersion: v1。OjbectMeta 里定义了该资源的元信息,包括名称、命名空间、UID、创建时间、标签组等。

kubelet 的作用是:负责管理和维护在这台主机上运行着的所有容器,维持 pod 的运行状态(status)和它的期望值(spec)一致。kubelet启动时,会加载它本身的配置信息、接入容器运行时(Docker 或 Rkt)、以及定义 kubelet 与整个集群进行交互所需的信息。在启动后,会进行一系列的初始化工作,创建 ContainerManager、设置OOMScoreAdj 值、创建 DiskSpaceManager、PodManager 等等

  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值