kubernetes设计理念

Kubernetes集群中,有Master和Node这两种角色。Master管理Node,Node管理容器。

Master主要负责整个集群的管理控制,相当于整个Kubernetes集群的首脑。它用于监控、编排、调度集群中的各个工作节点。

Master的主要组件分别为kube-apiserver(负责实际操作)、etcd(负责存储)、kube-scheduler(负责Pod调度)、kube-controller-manager(负责对象管理)。

Node由Master管理,提供运行容器所需的各种环境,对容器进行实际的控制,而这些容器会提供实际的应用服务。

Node的主要组件分别为kubelet(值守进程)、kube-proxy(负责服务发现)和容器运行时(负责操作容器)。

以Pod的创建为例,当使用kubectl创建Pod时,会相继发生以下事件

k8s运行机制

Pod创建流程具体发生的事件如下。(1)kubectl命令将转换为对API Server的调用。(2)API Server验证请求并将其保存到etcd中。(3)etcd通知API Server。(4)API Server调用调度器。(5)调度器决定在哪个节点运行Pod,并将其返回给API Server。(6)API Server将对应节点保存到etcd中。(7)etcd通知API Server。(8)API Server在相应的节点中调用kubelet。(9)kubelet与容器运行时API发生交互,与容器守护进程通信以创建容器。(10)kubelet将Pod状态更新到API Server中。(11)API Server把最新的状态保存到etcd中。

pod

Pod是Kubernetes处理的最基本单元。容器本身并不会直接分配到主机上,而是会封装到名为Pod的对象中。Pod通常表示单个应用程序,由一个或多个关系紧密的容器构成。这些容器拥有同样的生命周期,作为一个整体一起编排到Node上。这些容器共享环境、存储卷和IP空间。尽管Pod基于一个或多个容器,但应将Pod视作单一的整体、单独的应用程序。Kubernetes以Pod为最小单位进行调度、伸缩并共享资源、管理生命周期。

#pod模板详解
 apiVersion: v1  #版本,v1为稳定版本
kind: Pod        #类型pod
metadata:        #元数据,表示资源的标识信息
  labels:        #元数据标签列表
    name: scheduler-platform   #标签名
  name: scheduler-platform-lsw  #pod名称
  namespace: default    #pod命名空间
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: data-dolphinscheduler-platform-deploy-7fcd8dd4bb
    uid: cfef7e97-0f04-4143-83af-f77ecd031edb
  resourceVersion: "4907875"
  selfLink: /api/v1/namespaces/default/pods/data-dolphinscheduler-platform-deploy-7fcd8dd4bb-lwsm5
  uid: 980e993e-3f71-4cfd-b4e5-f37961313a2d
spec:    #pod中容器详细定义
  containers:  #容器列表,可有多个
  - env:
    - name: TZ
      value: Asia/Shanghai
    - name: JAVA_OPTIONS
      value: -Dspring.dataSource.url=jdbc:mysql://10.194.188.37:3306/dolphinscheduler?useSSL=false&useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8                                                    -Dspring.dataSource.username=root                                                    -Dspring.dataSource.password=Mysql@123                                                    -Dspring.profiles.active=k8s
    image: bmsoft:5000/images #镜像地址
    imagePullPolicy: Always #拉取镜像库方式
    name: data-dolphinscheduler-platform #名称
    resources: {}  #资源
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:  #挂载到容器内部存储卷设置
    - mountPath: /opt/dolphinscheduler/volumfile  #存储卷在容器中挂载的绝对路劲
      name: config-volume #引用pod定义的共享存储卷的名称,要用volume[]部分定义的卷名
      readOnly: true    #是否是读写模式
  dnsPolicy: ClusterFirstWithHostNet
  enableServiceLinks: true
  imagePullSecrets:
  - name: registry-key
  nodeName: host-10-198-246-69
  priority: 0
  restartPolicy: Always  #重启策略
  schedulerName: default-scheduler
  securityContext: {}

控制器

一般来说,用户不会直接创建Pod,而是创建控制器,让控制器来管理Pod。在控制器中定义Pod的部署方式(如有多少个副本、需要在哪种Node上运行等),根据不同的业务场景,Kubernetes提供了多种控制器

ReplicationController(RC)和ReplicationSet(RS)控制器

在使用Kubernetes时,通常要管理的是由多个相同Pod组成的Pod集合,而不是单个Pod。例如,ReplicationController或ReplicaSet控制器基于Pod模板进行创建,能够很好地支持水平伸缩。

ReplicationController可定义Pod模板,并可以设置相应控制参数以实现水平伸缩,以调节正在运行的相同的Pod数。ReplicationController负责保证在集群中部署的Pod数量与配置中的Pod数量一致。

ReplicaSet控制器可以看作ReplicationController的另一种版本,其Pod识别功能使它在Pod管理上更具灵活性。由于ReplicaSet控制器具有副本筛选功能,因此ReplicaSet控制器才有逐渐取代ReplicationController的趋势。

ReplicationController和ReplicaSet控制器都是很少直接使用的对象。虽然它们都是基于Pod而设计的,增加了水平伸缩功能,提高了可靠性,但它们缺少一些在其他复杂对象中具有的更细粒度的生命周期管理功能。

Deployment控制器

Deployment控制器以ReplicaSet控制器为基础,是更高级的概念,增加了更灵活的生命周期管理功能。

Deployment控制器是一种高级对象,旨在简化Pod的生命周期管理。只要简单更改Deployment控制器的配置文件,Kubernetes就会自动调节ReplicaSet控制器,管理应用程序不同版本之间的切换,还可以实现自动维护事件历史记录及自动撤销功能

# deploy模板
apiVersion: apps/v1  #版本
kind: Deployment  #类型
metadata:     #元数据,资源标识符
  annotations:  #  自定义注解列表
    deployment.kubernetes.io/revision: "8"  #key-value类型,可有多个定义
    kubectl.kubernetes.io/last-applied-configuration: |
      {"name":"data-dolphinscheduler-platform"}
    meta.helm.sh/release-name: datacenter
    meta.helm.sh/release-namespace: default
    generation: 14
  labels:  #标签列表
    app.kubernetes.io/managed-by: Helm
    name: data-dolphinscheduler-platform
  name: scheduler-platform-deploy #deployment控制器名称
  namespace: default #命名空间
spec: #deployment控制器详细信息
  progressDeadlineSeconds: 600
  replicas: 1  #指定所需的pod数量
  revisionHistoryLimit: 10 #指定要保留的允许回滚的旧ReplicaSet的数量
  selector:   #必填,用于指定次deployment控制器针对的pod 标签选择器,需要与template中的标签匹配
    matchLabels:  #定义需要匹配的标签集合
      name: data-dolphinscheduler-platform  #需要匹配标签,可定义多个key-value
  strategy:  #更新时替换旧pod的策略
    rollingUpdate: 
      maxSurge: 25%  #滚动更新时,在所需数量的pod上允许创建的最大的pod数
      maxUnavailable: 25% #滚动更新时,同时存在最大不可用pod数
    type: RollingUpdate 
  template:  #pod模板,跟pod具有完全相同的结构,是嵌套到里面的
    metadata:
      creationTimestamp: null
      labels:
        name: scheduler-platform
    spec:
      containers:
      - env:
        - name: TZ
          value: Asia/Shanghai
        image: bmsoft:5000/images #镜像位置
        imagePullPolicy: Always
        name: scheduler-platform
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /opt/dolphinscheduler/volumfile
          name: config-volume
      dnsPolicy: ClusterFirstWithHostNet
      imagePullSecrets:
      - name: registry-key
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - configMap:
          defaultMode: 420
          name: dolphinschedulers-configmap
        name: config-volume

服务于存储

Service组件和Ingress

Service是内部负载均衡器中的一种组件,会将相同功能的Pod在逻辑上组合到一起,让它们表现得如同一个单一的实体。

各个工作负载对象只保证了支撑服务的微服务Pod的数量,但是没有解决如何访问这些服务的问题。Pod只是一个运行的应用示例,随时可能在一个节点上停止,并在另一个节点使用新的IP地址启动新的Pod,Pod根本无法以固定的IP地址和端口号提供服务

通过Service组件可以发布服务,可以跟踪并路由到所有指定类型的后端容器。内部使用者只需要知道Service组件提供的稳定端点即可进行访问。无论Service组件具体路由到哪个Pod,其IP地址都保持稳定。通过Service组件,可以轻松获得服务发现的能力

k8sServer机制

每当需要给另一个应用程序或外部用户提供某些Pod的访问权限时,就可以配置一个Service组件。比如,假设需要从外网访问Pod上运行的应用程序,就需要提供必要的Service组件抽象。同样,如果应用程序需要存储或查询数据,则可能还需要配置一个内部Service组件抽象,使应用程序能访问数据库Pod。

Ingress可来整合Service组件。还可以通过Ingress来整合Service组件。Ingress并不是某种服务类型,可以充当多个Service组件的统一入口。Ingress支持将路由规则合并到单个资源中,可以通过同一域名或IP地址下不同的路径来访问不同的Service组件。

存储卷和持久存储卷

解决容器可靠地共享数据并保证这些数据在容器重启的间隙始终是可用的。

存储卷会因Pod终止后,共享的存储卷会被销毁。

持久存储卷(persistent volume)是一种更健壮的抽象机制,不依赖于Pod的生命周期。持久存储卷允许管理员为集群配置存储资源

命名空间

命名空间(namespace)的主要作用是对Kubernetes集群资源进行划分。这种划分并非物理划分而是逻辑划分,用于实现多租户的资源隔离

标签与注解

Kubernetes中的标签(label)是一种语义化标记,可以附加到Kubernetes对象上,对它们进行标记或划分。

标签的形式是键值对,每个单元可以拥有多个标签,但每个单元对于每个键只能拥有一个值。

注解(annotation)也是一种类似的机制,用于将任意键值信息附加到某一对象中。相对于标签,注解更灵活,可以包含少量结构化数据。

pod基本操作

可通过配置yml文件创建pods,运行以下命令,通过模板创建Pod。

kubectl apply -f examplepod.yml

apply是一种声明式对象配置命令。这里应用了之前创建的模板,-f参数表示使用文件名作为参数

查询当前运行的所有pod

kubectl get pods

在查询命令中带上参数-w,以对Pod状态进行持续监控。只要Pod发生了变化,就会在控制台中输出相应信息

kubectl get pod -w

在查询命令中带上-o wide参数,输出Pod的更多概要信息(如调度到哪台机器上,Pod本身的虚拟IP等信息)

kubectl get pod -o wide

get命令除了可以显示简要的运行信息外,还可以输出完整信息。它支持多种格式的输出,如可以用yaml和Json方式输出.

kubectl get pod {pod名称} --output yaml
kubectl get pod {pod名称} --output json

查询Pod更详细的信息(包括状态、生命周期和执行情况等),除了将其输出为yaml或json格式,还可以用describe命令查看详情

kubectl describe pods {pod名称}

查询Pod本身输出的日志信息,还可以使用logs命令

kubectl logs {pod名称}

可以用replace命令来修改原先设置的Pod属性

kubectl replace -f {pod模板路劲}

删除pod只需要执行以下命令

kubectl delete pod {pod名称}

service

在Kubernetes中,Service是充当基础内部负载均衡器的一种组件。Service会将相同功能的Pod在逻辑上组合到一起,一般会采用标签选择器进行组合,让它们表现得如同单个实体。无论Service具体路由到哪个Pod,其IP地址都保持稳定。

和Pod一样,在Kubernetes中Service也属于虚拟网络,只有Master节点和Node属于实体网络,Pod和Service的IP地址只在Kubernetes集群(即Master和Node)内能访问,集群外部的机器是无法访问的。如果要想让外部机器能访问,对于Pod,可通过之前讲过的将Pod映射到HostPort上的方法来实现;而对于Service,通常的办法是配置NodePort或LoadBalancer的Service,或者给Service配置ExternalIP,以便将Service映射到Master或Node上,供外部机器访问。

service模板详解

apiVersion: v1
kind: Service
metadata: #元数据
  name: string #Service的名称
  namespace: string #Service所属的命名空间
  label: #Service的标签
   - name: string
  annotations: #Service的注解
   - name: string
  spec:
    selector: [] #标签选择器,将所选具有指定标签的pod作为管理范围,
    #selector会将=寻找匹配的所有pods,并将它们组织到service中
    type: string #Service的类型,分为clusterIP,NodePort,LoadBanlancer,ExeternalName
    clusterIP: string #虚拟服务的地址
    sessionAffinity: string #指定是否支持session,[ClientIP|Node]表示将同一个客户端的访问请求都转发到同一个后端
    ports: #Service需要暴露的端口
    - name: string #端口名称,区别同应用的端口
      protocol: string #使用的协议
      port: int #Service监听的端口
      targetPort: int #发送到后端应用的端口
      nodePort: int #当spec.type=NodePort时,使用指定映射到物理机的端口
    status: #当spec.type=LoadBalacer时,设置外部负载均衡器的地址
    loadBalancer:
      ingress:
        ip: string #外部负载的IP地址
        hostname: string #外部负载均衡的主机名

Service目前可定义为5个大类。通过spec.type属性可定义ClusterIP、NodePort、LoadBalancer、ExternalName这4类Service,而ClusterIP类服务还可以分为普通Service和无头Service两类,所以总共分为5类。它们分别适用于各种向外发布的场景和向内发布的场景。

可通过先编写deployment控制器yml文件,并使用以下命令进行发布deployment

kubectl apply -f exampledeploy.yml

再运行以下命令进行模板创建service

kubectl apply -f exampleservece.yml

通过以下命令可查看service

kubectl get svc或者kubectl get service

查看service的具体信息

kubectl describe service {service 名称}

其中查询到的最重要的信息是Endpoints属性,可以看到这里列出了所有Pod的IP地址与公布的端口。

向外发布

3种向外发布的方式

  • ClusterIP:默认方式,在Kubernetes集群内部发布服务时,会为Service分配一个集群内部可以访问的固定虚拟IP(即ClusterIP)地址。集群中的机器(即Master和Node)以及集群中的Pod都可以访问这个IP地址,集群外部无法访问服务。
  • NodePort:基于ClusterIP方式,生成一个ClusterIP地址,然后将这个IP地址及端口映射到各个集群机器(即Master和Node)的指定端口上。Kubernetes集群外部的机器就可以通过“NodeIP:Node端口”方式访问Service,**配置文件中的nodePort属性即为外部访问的端口
  • LoadBalancer:基于ClusterIP方式和NodePort方式。会申请使用外部负载均衡器,由负载均衡器映射到各个“NodeIP:端口”上。这样,Kubernetes集群外部的机器就可以通过负载均衡器访问Service。

向内发布

两种向内发布的方式分别如下。

  • ClusterIP-无头Service(headless service):这种方式不会分配ClusterIP地址,也不通过kube-proxy进行反向代理和负载均衡,而是通过DNS提供稳定的网络ID来进行访问。DNS会将无头Service的后端直接解析为Pod的IP地址列表。这种类型的Service只能在集群内的Pod中访问,集群中的机器无法直接访问。这种方式主要供StatefulSet使用。
  • ExternalName:和上面提到的3种向外发布的方式不太一样,在那3种方式中都将Kubernetes集群内部的服务发布出去,而ExternalName则将外部服务引入进来,通过一定格式映射到Kubernetes集群,从而为集群内部提供服务。

service负载均衡实现原理:在每个节点中都有一个叫作kube-proxy的组件,这个组件识别Service和Pod的动态变化,并将变化的地址信息写入本地的IPTables中。而IPTables使用NAT等技术将virtualIP的流量转至Endpoint。默认情况下,Kubernetes使用的是IPTables模式,如图6-8所示。

kubeproxy负载均衡实现原理图

存储与配置

为提供一些机制来将存储附加到容器上,这类容器的存留时间超过其他容器的生命周期。kubernetes定义了自己的存储卷(volume)抽象,可通过配置将数据注入pod中,在pod内部,容器之间还可以共享数据,而对于不同机器的pod,可通过定义存储卷来实现数据共享。

数据存储与共享的抽象就是存储卷。在Kubernetes中定义的存储卷主要分为4种类型。

□本地存储卷:主要用于Pod中容器之间的数据共享,或Pod与Node中的数据存储和共享。

□网络存储卷:主要用于多个Pod之间或多个Node之间的数据存储和共享。

□持久存储卷:基于网络存储卷,用户无须关心存储卷所使用的存储系统,只需要自定义具体需要消费多少资源即可,可将Pod与具体的存储系统解耦。

□配置存储卷:主要用于向各个Pod注入配置信息。

本地存储卷

本地存储卷有emptyDir和hostPath这两种,它们都会直接使用本机的文件系统,用于Pod中容器之间的数据共享,或Pod与Node中的数据存储和共享。

  • Pod中的容器都可以读写这个目录,其生命周期和Pod完全一致。如果Pod销毁,那么存储卷也会同时销毁。emptyDir主要用于存放和共享Pod的不同容器之间在运行过程中产生的文件。
spec:
  containers:
    - name: containerName  #容器名
     volumeMounts:  #容器存储卷映射
     - name: filedata  # 引用的存储卷名称
      mountPath: /write_dir  #容器内的数据卷地址为/write_dir
  volumes:
  - name: filedata  #引用的存储卷
    emptyDir: {}    #emptyDir类型
  • hostPath主要用于把主机上指定的目录映射到Pod中的容器上。如果Pod需要在主机上存储和共享文件,或使用主机上的文件,就可以使用这种方式。
spec:
  containers:
    - name: containerName  #容器名
     volumeMounts:  #容器存储卷映射
     - name: filedata  # 引用的存储卷名称
      mountPath: /write_dir  #容器内的数据卷地址为/write_dir
  volumes:
  - name: filedata  #引用的存储卷
    hostPath:  #hostPath类型存储卷
      path: /home/k8sadmin/hostpath_test  #主机上指定的目录,容器目录会映射到该目录

tip:使用hostPath进行数据卷映射后,进行kubectl get pods -o wide ,进行查询某个pod的NODE属性被调度都某个机器,进入相应的机器进行查看。在映射的主机目录下添加数据后,pod中会相应的添加数据信息。

网络卷积层

Kubernetes是分布式容器集群,因此如何在多个Pod之间或多个Node之间进行数据存储和共享是非常重要的问题。为了解决这个问题,Kubernetes引入了网络存储卷。网络卷积层需搭配集成各种第三方的存储系统,可见可用NFS搭建存储系统。

kubernetes资源管理及调度

在Kubernetes集群中,资源通常分为以下几种。

  • 计算资源,如CPU、内存、硬盘、网络等计算机物理资源,也包括节点本身。
  • 资源对象,如Pod、Service、Deployment等抽象资源。
  • 外部引用资源,如在使用PV/PVC时,实际上使用的是第三方网络存储卷,这类存储资源即为外部引用资源。

资源管理---为pod设置计算调度

  resource:   #资源限制和请求的设置
    limit:    #资源限制的设置
      cpu: String #cpu的限制,单位为CPU内核数,将用于docker run --cpu-quota参数;
      #也可以是小数,例如0.1,表示为100m
      memory: string #内核限制,单位为Mib/Gib/MB/GB,将用于docker run  --memory参数
      request:   #资源请求的设置
        cpu: string #CPU请求,容器刚启动时的可用CPU数量,将用于docker run --cpu-shares参数
        memory: string #内存请求,容器刚启动时可用的内存数量

requests和limits属性从不同维度保证Pod的资源占用情况。requests表示容器至少可获得的资源大小,也许容器实际上不会使用这么多资源,但Kubernetes在调度时会以此为参照,保证容器能调度到至少满足这些资源的机器上。而limits表示容器能够使用资源的最大限度,如果超过这个值,容器将被终止。

资源管理---命名空间

命名空间(namespace)的主要作用是对Kubernetes集群资源进行划分,这种划分并非物理划分,而是逻辑划分,用于实现多租户的资源隔离。使用命名空间来进行划分,这样集群内部的各种资源对象就可以在不同的命名空间下进行管理。

默认命名空间:

  • default:所有未指定namespace属性的对象都会分配到default命名空间中。
  • kube-node-lease:主要存放各个节点上的Lease对象,用于节点的心跳检测。
  • kube-system:所有由Kubernetes系统创建的资源都在这个命名空间中。
  • kube-public:此命名空间下的资源可以被所有人访问(包括未认证用户)。