K8S入门-概念篇(下)

ronald8个月前职场2120

一. volume

    volume解决的是pod上不同容器之间共享文件的问题和容器文件持久化的问题;K8S提供了以下几类volume:

1. hostPath

    hostPath是一种通过pod所在node上的文件系统指定的文件或者目录实现文件共享,如下所示,先在Pod内定义一个volume,类型定义为hostPath,并指定挂载到node上的指定路径下,然后pod内各个容器指定对应volume挂载到自己文件系统即可,如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod2
spec:
  containers:
  - image: busybox
    name: test-hostpath
    volumeMounts:
    - mountPath: /test-data
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      path: /data
      type: Directory

    在上面的示例中,我们创建了一个名字叫做test-volume且类型为hostPath的卷,在容器中volumeMounts的name字段指明使用这个test-volume这个卷,并挂载到容器的/test-data目录下。

    hostPath类型卷有一个优点是,volume内文件的存在周期是脱离于pod的生命周期的,即Pod终止之后,hostPath卷下面的文件不会消失。但使用的时候需要注意,容器会在启动之初将挂载点下的目录清空,所以若是存在同一个容器共享一个挂载文件,需要在容器启动之后再去填充指定的目录。

2. emptyDir

    hostPath因为是挂载到node本地路径下,所以对于被调度节点的目录结构是有要求的。还有一种node本地存储卷方式叫做emptyDir,这种映射机制不需要指定映射目录,不仅可以映射到磁盘甚至可以映射到内存,对于被调度节点目录结构无要求,配置示例如下:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}
    medium: Memory

    如上图示例,我们创建了一个emptyDir类型的volume cache-volume,Pod上的容器test-container使用这个卷,并映射到容器内的/cache路径下,甚至愿意的话,我们可以指定存储介质为Memory,表示借助内存来构建卷。

    需要注意的是,emptyDir里面的文件在Pod重新调度会丢失,且当选择内存作为挂载点时,需要关注内存的消耗(这部分内存会归为容器的内存消耗)。

3. nfs

    无论是hostPath还是emptyDir,都面临一个问题,即Pod被重新调度后原有磁盘上的文件丢失了,有时我们希望无论Pod被如何调度,都可以恢复在磁盘上的数据,NFS volume提供了这样一种机制,NFS volume可以把网络文件系统映射到容器内部,以保证Pod调度不会导致磁盘数据丢失。配置格式如下:

apiVersion: apps/v1 
kind: Pod
metadata:
  name: redis
spec:
  selector:
    matchLabels:
      app: redis
    spec:
      containers:
      # 应用的镜像
      - image: redis
        name: redis 
        # 持久化挂接位置,在docker中 
        volumeMounts:
        - name: redis-persistent-storage
          mountPath: /data
      volumes:
      # 宿主机上的目录
      - name: redis-persistent-storage
        nfs:
          path: /k8s-nfs/redis/data
          server: 192.168.8.150

4. persistent volume claim

    从上面通过NFS实现持久化存储,我们看到开发人员需要关注底层的存储介质是NFS,且对应文件系统的IP地址,不仅如此,如果我们在另外一个K8S集群部署服务的时候,对应K8S集群需要支持NFS的存储介质,这不仅增加了开发人员的负担也降低了部署的灵活性,而PVC(Persistent Volume Claim)就是用来实现底层存储技术和Pod的解耦,使得开发人员不需关注底层的存储技术,也不需要了解应该使用哪种类型服务器运行Pod。

    那么具体是如何操作的呢?

    首先系统管理员创建某类或者某几类网络存储,这种网络存储可能是NFS、也可能是mongoDB等,创建好之后系统管理员通过K8S的API传递PV声明(这个PV声明包含存储能力、访问模式、存储类型等信息)通过这个PV声明K8S集群将存储介质抽象成一个个预分配的持久卷;PV创建如下所示:

kind: PersistentVolume     #指定为PV类型
apiVersion: v1
metadata:
  name: pv-statefulset     #指定PV的名称
  labels:                  #指定PV的标签
    release: stable
spec:
  capacity:
    storage: 0.1Gi          #指定PV的容量
  accessModes:
    - ReadWriteOnce         #指定PV的访问模式,简写为RWO,只支持挂在1个Pod的读和写
  persistentVolumeReclaimPolicy: Recycle  #指定PV的回收策略,Recycle表示支持回收,
  hostPath:                 #指定PV的存储类型,本文是以hostpath为例
    path: /data/pod/volume1 #指定PV对应后端存储hostpath的目录


    当用户需要申请PV的时候,会申请一个PVC,一种用户存储的声明,里面含有存储容量大小、存储类型等,由K8S在系统中查找对应项进行匹配,实现PVC和PV的绑定如下所示:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mppvc-01                  # 指定PVC的名称
  namespace: default
spec:
 accessModes: ["ReadWriteOnce"]    # 指定PVC的访问模式
 resources:
   requests: 
     storage: 0.05Gi               # PVC申请的容量

    用户创建Pod通过volume的配置引用PVC,实现PV的使用。

    通过PV和PVC的形式,底层存储介质的申请和分割由系统管理员完成,上层开发人员对底层存储介质和地址无感知,只需向系统申请一块指定大小和访问模式的存储空间即可。

但是,是否我们可以不用预先创建一系列的PV,这样既麻烦又不灵活,而storage class提供了一种可以动态创建PV的。

二. Deployment

    Deployment是比Pod更高阶的资源,解决的是Pod声明式更新的问题;通过Replication controller,K8S集群可以将Pod的个数维持在期望的水平,而Deployment资源在创建时,也会创建一个Replica Set/Replication Controller借助它的功能达到节点数目维持在一定数目的能力。

    那么既然Deployment是解决Pod声明式的更新问题,那么它的解决方案是怎样的呢?

    第一,升级时机

    当我们修改deployment的描述文件的镜像信息并提交K8S 的API Server,K8S的deployment controller发现Pod的image的版本更新了,则触发版本升级流程。

    第二,升级策略

    deployment支持两种版本升级策略:

       一种是Recreate策略,所谓recreate策略,是先把现网Pod实例都停了,然后统一进行版本升级,这种升级策略相对比较简单,但是期间会导致短暂的服务不可用。

        一种是RollingUpdate,这也是deployment默认的升级策略,滚动更新默认分批停止Pod实例,分批更新,分批重启;整个过程中,服务不会中断,但实践过程中,需要业务方关注版本兼容性的问题。

    第三,升级步长

    所谓升级步长即定义批次的大小,每一批次更新多少Pod实例,在deployment的配置过程中,通过maxSurge(决定了除期望副本数之外最多允许超过的Pod数目)和maxUnavailable(决定了版本更新期间最多多少Pod不可用)属性定义一次替换多少Pod;以maxSurge=1为例

image-20210914112003648.png

    第四,安全升级

    在deployment升级过程中,我们可以通过就绪探针+minReadySeconds来避免升级过程中错误扩散,具体来说,就是通过就绪探针,我们可以确保Pod实例并稳定工作minReadySeconds之后,我们才会执行下一个节点的停止和升级更新操作,避免因为版本问题导致的更新错误扩散。

    那么deployment进行版本更新的原理是什么呢?

image-20210914112415484.png

    简单来说,就是发现需要版本升级了,deployment controller会创建一个新版本的replicaSet,通过协调两个replicaSet的replicas属性,逐步实现老版本的替换

三. statefulset

    Deployment用于解决无状态节点的部署问题,但是这类节点没有稳定的网络标识:节点无法预知自己的网络标识,重新拉起之后网络标识会发生改变;这对于有状态的现网服务来说十分的不友好,因此K8S提供了StatefulSet作为有状态节点重启的解决方案。

    具体来说,StatefulSet提供以下几点保证:

       1)stateful pod的每个Pod有一个从零开始的索引,Pod名称可预知

        2)Pod重新调度之后保留其标识和状态不变

        3)Stateful 提供at most one服务,即当现有Pod故障时,除非控制面能确定Pod已经停止服务了,否则不会重新调度

    StatefulSet使用时需要注意的点:

        1)扩容的时候,会按顺序使用下一个索引值,不能跳着用

        2)缩容时,按照节点索引号从大到小的缩容,且缩容时是逐个进行,所以缩容比较慢

四. nodeport

    NodePort解决的是集群服务对外暴露的问题;基本原理是给集群内的机器开一个端口,外网机器拿到这台机器的外网IP和对外暴露的端口号,给指定端口发包,集群内机器的指定端口的流量都会被据此转到后端对应的service上,再由service转发到各个Pod上;如下图所示:

image-20210914152230720.png

    这里多提一句,都是服务暴露的方式,Service和NodePort,以及后面说到的ingress有什么区别?

    service是内网服务暴露的机制,本质上就是一条转发规则,其IP事实上是不存在的,既不存在于外网也不存在于内网,service请求端需要是集群内的节点,装有kube-proxy,并运行于集群内;而NodePort则是服务对外网暴露的,其IP和端口是事实存在并可以被外网访问的,请求NodePort暴露的服务的客户端,可以是任意机器,不需要运行于K8S集群内。

五. ingress

    通过NodePort进行服务暴露是K8S比较原始的机制,且不灵活,为什么呢?

    首先,一个端口只能暴露一个服务;其次,若是节点的外网IP变了,则对应客户端也需要感知到这种变化。所以,对于七层协议服务,K8S提供了ingress这种服务暴露方式,具体有哪些优势?

    第一,是支持一个地址暴露多个服务,服务之间通过路径进行区分;

    第二,提供名字服务,机器地址改变无需客户端做出变化。

    如下图所示:

image-20210914153749885.png

参考资料

    https://www.qikqiak.com/k8strain/k8s-basic/pod/

    http://docs.kubernetes.org.cn/683.html:kubectl命名表

    https://www.cnblogs.com/gdut1425/p/13046507.html:PV和PVC原理

   https://blog.csdn.net/qq_36807862/article/details/106068871:iptables vs ipvs

    http://dockone.io/article/4884:nodeport、ingress、loadbalance


标签: K8S

相关文章

K8S入门-原理篇

K8S入门-原理篇

一. K8S总体架构及组件功能    K8S总体架构图如下所示:    在K8S中,整个集群划分为控制节点和工作节点,其中控制节点分为:    ·ETCD        一个分布式的数据库,用于存储集...

K8S背景

    在深入了解K8S之前,我们先了解一下K8S产生的背景,看下是为了解决怎样的问题一步步衍生出K8S这样一套系统。一. 微服务化    随着需求的发展,单体应用的复杂度越来越高,大大增加了系统现网的运维成本,主要包括以下几个方面。    1. 模块耦合度提升,维护成本高&nb...

K8S入门-概念篇(上)

K8S入门-概念篇(上)

    认识到K8S的产生背景之后,我们开始进一步了解K8S,基于对K8S里面一些概念的了解之后,我们再去探讨K8S的一些原理:一. node    如前所述,K8S是一个容器编排平台,即容器的自动部署、扩展和管理;其最终的落点是把容器调度到一个运行他的节点上,在K8S中这个运行容器的节点就是node,但是需要注意的是...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。