当前位置:首页 » 服务存储 » k8spod共享存储
扩展阅读
webinf下怎么引入js 2023-08-31 21:54:13
堡垒机怎么打开web 2023-08-31 21:54:11

k8spod共享存储

发布时间: 2023-01-31 05:47:06

① 9. K8s存储

Kubernetes的Volume是Pod的一部分,Volume不是单独的对象,不能独立创建,只能在Pod中定义。
Pod中的所有容器都可以访问Volume,但必须要挂载,且可以挂载到容器中任何目录。
实际中使用容器存储如下图所示,将容器的内容挂载到Volume中,通过Volume两个容器间实现了存储共享。

Volume的生命周期与挂载它的Pod相同,但是Volume里面的文件可能在Volume消失后仍然存在,这取决于Volume的类型。

Kubernetes的Volume有非常多的类型:

这个Volume挂载后就是一个空目录,应用程序可以在里面读写文件,emptyDir Volume的生命周期与Pod相同,Pod删除后Volume的数据也同时删除掉。
emptyDir的一些用途:

配置示例

emptyDir也可以设置存储介质为内存

HostPath存储的内容与节点相关,如果Pod被调度到其他Node,HostPath无法提供跨Node的数据。
配置示例

ConfigMap存储的是键值对,在Volume中应用时键值对表示的是 文件名 和 文件内容 ,代表将ConfigMap的每条数据填入Volume。ConfigMap的配置数据在 data 字段下定义。

ConfigMap文件示例

Volume中引用ConfigMap

与ConfigMap类似,在data字段中存储key-value键值对形式,不过存储的value不是明文,是Base64编码的加密值。
在Volume中引用后,文件中的值是Base64解码后的值,而非加密值。

Kubernetes抽象了PV(PersistentVolume)和PVC(PersistentVolumeClaim)来解耦网络存储种类多样的问题,从而让使用者不用关心具体的基础设施,当需要存储资源的时候,只要像CPU和内存一样,声明要多少即可。
PV是集群级别的资源,并不属于某个命名空间(在 default 命名空间下),而PVC是命名空间级别的资源,PV可以与任何命名空间的PVC资源绑定。

Kubernetes管理员设置好网络存储的类型,提供对应的PV描述符配置到Kubernetes,使用者需要存储的时候只需要创建PVC,然后在Pod中使用Volume关联PVC,即可让Pod使用到存储资源,它们之间的关系如下图所示。

PV 持久卷是用 插件 的形式来实现的,目前支持以下插件:

CSI
Container Storage Interface,容器存储接口,基于CSI这套接口,可以开发定制出CSI插件,从而支持特定的存储,达到解耦的目的。

PVC创建示例:

使用StorageClass可以自动创建PV,StorageClass包含 provisioner 、 parameters 和 reclaimPolicy 字段, 这些字段会在 StorageClass 需要动态分配 PersistentVolume 时会使用到。在声明PVC时加上StorageClassName,就可以自动创建PV,并自动创建底层的存储资源。

provisioner: PV配置器,用来决定使用哪个卷插件制备 PV。 该字段必须指定。
reclaimPolicy: 回收策略,可以是 Delete 或者 Retain ,默认是 Delete

使用StorageClassName创建PVC示例:

② Kubernetes存储

在 Docker的设计 实现中, 容器中的数据是临时性的,当容器销毁或重新启动时存储在容器内部的数据将会全部丢失 ,但实际上很多容器化应用是需要持久化保存的数据,这就需要使用Docker数据卷挂载宿主机上的文件或者目录到容器中以保证数据的持久化存储。在 Kubernetes中Pod重建如同Docker销毁一样,数据就会丢失 ,Kubernetes也通过挂载数据卷方式为Pod数据提供持久化能力,这些数据卷以Pod为最小单位进行存储,通过共享存储或分布式存储在各个Pod之间实现共享。

Kubernetes是由Master节点及Node节点组成的,在Master节点中通过etcd存储了Kubernetes集群的节点信息、Pod信息、容器信息、配置信息。Node节点主要对外提供容器服务,着重描述Node节点与存储相关的内容。

Kubernetes以Pod为单位对外提供容器服务,真正的服务是通过Service进行访问的。 Kubernetes中的服务按类型分成三种:无状态服务(stateless)、普通有状态服务、有状态集群服务 。

无状态服务:不需要持久化存储的,即使Pod重建也不会受影响,只要确保服务的可靠性便可,Kubernetes通过ReplicationSet来保证某个服务的实例数量。

普通有状态服务:这类服务需要保留服务的状态,通常通过Kubernetes提供的Volume及Persistent Volume、Persistent Volume Claim来保存状态。

有状态的集群服务:这类服务除了保存服务状态的同时还需要提供集群管理的功能,集群管理过程中也涉及临时数据的保存、集群内数据共享等。

Kubernetes中涉及存储的主要使用场景 :

1) 容器集群相关配置信息及运行时信息保存,这类信息存储在etcd中。

2) 服务的基本配置文件及证书文件。

3) 服务的状态存储、数据存储信息。

4) 集群内不同服务交换共享的数据信息。

1) 临时文件形式 :同一个Pod内不同容器间通过共享内存方式访问,会创建一个空目录,交换完信息后会删除这个空目录。

2) HostPath方式 :同一个Node内不同的Pod间进行信息共享使用HostPath方式(比如时区timezone)。如果Pod配置了EmptyDir数据卷,则它在Pod的生命周期内都会存在。当Pod被分配到Node上的时候,会在Node上创建EmptyDir数据卷,并挂载到Pod的容器中。

3) PV及PVC: Kubernetes的持久化存储机制的核心是PV(Persistent Volume)、PVC (Persistent Volume Claim)。PV是Volume插件,关联到真正的后端存储系统,PVC是从PV中申请资源,而不需要关心存储的提供方。PVC和PV的关系就如同Pod和Node一样,Pod是消费Node提供的资源,PVC是消费PV提供的存储资源。PVC和PV通过匹配完成绑定关系,PVC可以被Pod里的容器挂载。

4) 网络方式 :不同Node节点之间的数据共享通过网络方式使用,通常采用分布式存储方式。开源的分布式文件系统比较流行的选择有GlusterFS和Ceph,还有一些其他的分布式文件系统(NFS)选择。

5) 自定义插件方式 :Kubernetes提供了丰富的Volume的插件来进行存储管理。如果存储管理接口不够用,用户可以通过CSI或Flex Volume进行扩展。

存储管理组件(存储组件)主要是接收北向API收到的Rest请求,维护持久卷的生命周期管理。如创建、删除卷, 存储组件负责与后端存储软件交互完成实际的创建、删除卷等操作;并负责调用Kubernetes原生接口创建对应的PVC和PV 。

存储后端系统提供数据文件的实际持久化能力,不仅需要实现数据文件的读写、副本复制等存储操作,通常还需具备多节点高可用的能力。

当需要为自己的微服务应用挂载持久卷时,只需要通过存储组件创建持久卷,存储组件会在Kubernetes业务集群创建PVC/PV,并到后端存储系统(如GlusterFS)上创建真正的物理Volume,同时维护好PVC/PV/Volume之间的一一映射对应关系。这样,用户部署容器时就可以选择挂载相应的持久卷,部署后相应的卷就可以挂载到对应的容器。应用挂载持久卷后,进行读写时就类似于本地目录的读写操作。

在Pod进行重建或者迁移到其他节点时,Pod可以自动挂回原来对应的持久卷,继续使用原先的数据。多个Pod可以共享一个持久卷,从而达到容器间文件共享的目的。

摘抄自陆平的《基于Kubernetes的容器云平台实战》一书的第11章Kubernetes存储

③ 深入剖析k8s中pod的意义和用法

本文是《深入剖析k8s》学习笔记的第二篇,主要解析pod的意义及其使用方法。

pod,是k8s中最小的API对象,是原子调度单位。是超亲密关系容器之间组织和部署的单位。类比地说,pod就是虚拟机,其中的容器就是这个虚拟机里面运行的用户进程。

pod中的所有容器共享network、volume、IP地址,在pod启动的时候,需要先启动一个Infra中间容器,而其它容器都是通过join的方式加入到Infra容器的资源中的。Infra容器是一个用汇编语言编写的,永远处于暂停状态的容器,其唯一的作用就是hold住资源,和pod同生命周期。

initcontainers是一种容器类型,相较于containers类型,前者总是先于后者启动,initcontainers如果有多个,则会按照定义的顺序先后启动,只有当所有的initcontainers都启动成功且退出了,containers用户容器才会启动。

sidecar,是一种容器设计模式,指的是我们可以在一个pod中,启动一个辅助容器来完成一些独立于主容器之外的工作。比如initcontainers容器、Infra容器,都属于sidecar。

在进行上云工作的时候,我们可以把虚拟机类同为一个pod,把里面的进程类同为容器镜像,把有顺序关系的容器,定义为initcontainers。如此才是合理的、松耦合的容器编排诀窍,也是传统应用架构演变到微服务架构最自然的过渡方式。

pod有如下几个重要的属性需要掌握。

pod有如下几个状态需要掌握:

有几种特殊的volume,它们并不是为了存放容器中的数据,也不是为了进行容器之间或者和宿主机之间进行数据共享,而是为了给容器提供预先定义好的数据。这种数据卷被称为”投射数据卷“,projected volume。

pod可以为其中的容器配置探针(probe),用以监控容器的健康检查,而不是以容器镜像是否运行来作为健康检查的依据,因为会存在很多情况,容器是正常运行的,但是无法对外提供服务了,因此探针的健康检查方式更加准确。k8s一旦检测到容器探针发生异常,就会根据设置好的pod恢复机制进行操作,恢复机制restartPolicy有如下几种:

默认情况下pod的恢复机制是always,但并不是所有场景下都是合适的,比如initContainers初始化容器执行任务之后就结束了,就不应该设置为always。

如下文档提供了全量的yaml属性,特别是关于PodSpec的属性可以在3050行看到: api/types.go at master · kubernetes/api · GitHub

④ K8S——Pod入门理解

1Pod的含义?

官方解释:在Kubernetes集群中,Pod是所有业务类型的基础,它是一个或多个容器的组合。这些容器共享存储、网络和命名空间,以及如何运行的规范。在Pod中,所有容器都被同一安排和调度,并运行在共享的上下文中。对于具体应用而言,Pod是它们的逻辑主机,Pod包含业务相关的多个应用容器。Kubernetes不只是支持Docker容器,它也支持其他容器。

个人理解:Pod是容器组的一个抽象,类似于一栋出租楼里面的房子,房子的其他小房间像容器,房间里的水,电充当应用服务。出租屋内的小房间门跟容器端口差不多,出租屋大门像pod上的端口,整栋楼大门像service对外暴露的端口。

2使用Pod的原因?

pod是K8s最小的运行,部署单位。用K8s作容器管理,比如Mysql,Redis等服务运行需要创建Pod,才能对外提供服务。

3Pod操作相关命令

3.0 pod创建

kubectl run httpd--image=httpd -n 空间名

3.1 查看所有空间上的pod节点

kubectlget pods --all-namespaces

3.2 查看指定空间上的pod

kubectlgetpod -n 空间名

3.3 查看pod详情

kubectlget pod -o wide -n  空间名kubectldescribe pod name

3.4 指定变更pod空间清单脚本

kubectledit pod  pod名 -n 空间名

3.5 删除pod

删除某个空间下pod

kubectldeleteall--all -n   空间名

删除默认空间 pod

kubectldeletepodsname

删除指定空间下pod

kubectldeletepodsname-n 空间名

3.6 登录Pod里面容器 (container-name 通过3.3 可以获取)

kubectl exec -ti-c/bin/bash    container-name 有多个是要指定 只有一个去掉

kubectl exec-cdate        container-name 有多个是要指定 只有一个去掉      指定pod里面某个容器执行命令

3.7 查看Pod里面容器日志

kubectl logs -f-c

4脚本创建Pod

apiVersion: v1

kind: Pod

metadata:

name: test-pd

spec:

containers:

  - image: tomcat:8.0

    name: test-container

    ports:

  - containerPort: 8080

    protocol: TCP

⑤ Pod的特性

Pod是k8s系统中可以创建和管理的最小单元,是资源对象模型中由用户创建或部署的最小资源对象模型,也是在k8s上运行容器化应用的资源对象,其他的资源对象都是用来支撑或者扩展Pod对象功能的,比如控制器对象是用来管控Pod对象的,Service或者Ingress资源对象是用来暴露Pod引用对象的,PersistentVolume资源对象是用来为Pod提供存储等等,k8s不会直接处理容器,而是Pod,Pod是由一个或者多个container组成的。

每个Pod都是运行应用的单个实例,如果需要水平扩展应用(例如,运行多个实例),则应该使用多个Pods,每个实例一个Pod。在Kubernetes中,这样通常称为Replication。Replication的Pod通常由Controller创建和管理。

Pod可以被理解成一群可以共享网络、存储和计算资源的容器化服务的集合。再打个形象的比喻,在同一个Pod里的几个Docker服务/程序,好像被部署在同一台机器上,可以通过localhost互相访问,并且可以共用Pod里的存储资源(这里是指Docker可以挂载Pod内的数据卷,数据卷的概念,后文会详细讲述,暂时理解为“需要手动mount的磁盘”)。笔者总结Pod如下图,可以看到:同一个Pod之间的Container可以通过localhost互相访问,并且可以挂载Pod内所有的数据卷;但是不同的Pod之间的Container不能用localhost访问,也不能挂载其他Pod的数据卷。

1.1、为什么需要pod
我们先谈谈为什么k8s会使用pod这个最小单元,而不是使用docker的容器,k8s既然使用了pod,当然有它的理由。

1、更利于扩展
k8s不仅仅支持Docker容器,也支持rkt甚至用户自定义容器,为什么会有这么多不同的容器呢,因为容器并不是真正的虚拟机,docker的一些概念和误区总结,此外,Kubernetes不依赖于底层某一种具体的规则去实现容器技术,而是通过CRI这个抽象层操作容器,这样就会需要pod这样一个东西,pod内部再管理多个业务上紧密相关的用户业务容器,就会更有利用业务扩展pod而不是扩展容器。

2、更容易定义一组容器的状态

如果我们没有使用pod,而是直接使用一组容器去跑一个业务呢,那么当其中一个或者若干个容器出现问题呢,我们如何去定义这一组容器的状态呢,通过pod这个概念,这个问题就可以很好的解决,一组业务容器跑在一个k8s的pod中,这个pod中会有一个pause容器,这个容器与其他的业务容器都没有关系,以这个pause容器的状态来代表这个pod的状态.

3、利于容器间文件共享,以及通信。
pod里的多个业务容器共享pause容器的ip和存储卷Volume,pod中的其他容器共享pause容器的ip地址和存储,这样就做到了文件共享和互信。

1.2 Pod 特性:
1 资源共享:IP和Volume
一个Pod里的多个容器可以共享存储和网络IP,可以看作一个逻辑的主机。共享的如 namespace,cgroups或者其他的隔离资源。

多个容器共享同一个network namespace,由此在一个Pod里的多个容器共享Pod的IP和端口namespace,所以一个Pod内的多个容器之间可以通过localhost来进行通信,所需要注意的是不同容器要注意不要有端口冲突即可。不同的Pod有不同的IP,不同Pod内的多个容器之前通信,不可以使用IPC(如果没有特殊指定的话)通信,通常情况下使用Pod的IP进行通信。

k8s要求底层网络支持集群内任意两个pod直接的TCP/IP直接通信,这通常才有虚拟二层网络技术来实现,例如Flannel,Openswitch等。

一个Pod里的多个容器可以共享存储卷,这个存储卷会被定义为Pod的一部分,并且可以挂载到该Pod里的所有容器的文件系统上。

2 生命周期短暂
Pod属于生命周期比较短暂的组件,比如,当Pod所在节点发生故障,那么该节点上的Pod会被调度到其他节点,但需要注意的是,被重新调度的Pod是一个全新的Pod,跟之前的Pod没有半毛钱关系。

3 平坦的网络
K8s集群中的所有Pod都在同一个共享网络地址空间中,也就是说每个Pod都可以通过其他Pod的IP地址来实现访问。

1.3 Pod使用和管理
1、核心原则是:将多个应用分散到多个Pod中 原因:基于资源的合理应用;扩缩容,不同应用应该有不同的扩缩容策略等。

结论:单Pod单容器应用,除非特殊原因。
你很少会直接在kubernetes中创建单个Pod。因为Pod的生命周期是短暂的,用后即焚的实体。当Pod被创建后(不论是由你直接创建还是被其他Controller),都会被Kubernetes调度到集群的Node上。直到Pod的进程终止、被删掉、因为缺少资源而被驱逐、或者Node故障之前这个Pod都会一直保持在那个Node上。

Pod不会自愈。如果Pod运行的Node故障,或者是调度器本身故障,这个Pod就会被删除。同样的,如果Pod所在Node缺少资源或者Pod处于维护状态,Pod也会被驱逐。Kubernetes使用更高级的称为Controller的抽象层,来管理Pod实例。虽然可以直接使用Pod,但是在Kubernetes中通常是使用Controller来管理Pod的。

1.4、Pod和Controller
Controller可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力。例如,如果一个Node故障,Controller就能自动将该节点上的Pod调度到其他健康的Node上。

包含一个或者多个Pod的Controller示例:

Deployment
StatefulSet
DaemonSet
通常,Controller会用你提供的Pod Template来创建相应的Pod。

在用户定义范围内,如果pod增多,则ReplicationController会终止额外的pod,如果减少,RC会创建新的pod,始终保持在定义范围。例如,RC会在Pod维护(例如内核升级)后在节点上重新创建新Pod。

对Pod的定义可以通过Yaml或Json格式的配置文件来完成。关于Yaml或Json中都能写哪些参数,参考官网 http://kubernetes.io/docs/user-guide/pods/multi-container/

Pod的yaml整体文件内容及功能注解如下:

我们来看一个段实际的例子

在使用docker时,我们可以使用docker run命令创建并启动一个容器,而在Kubernetes系统中对长时间运行的容器要求是:其主程序需要一直在前台运行。如果我们创建的docker镜像的启动命令是后台执行程序,例如Linux脚本:

nohup ./startup.sh &

则kubelet创建包含这个容器的pod后运行完该命令,即认为Pod执行结束,之后根据RC中定义的pod的replicas副本数量生产一个新的pod,而一旦创建出新的pod,将在执行完命令后陷入无限循环的过程中,这就是Kubernetes需要我们创建的docker镜像以一个前台命令作为启动命令的原因。

对于无法改造为前台执行的应用,也可以使用开源工具supervisor辅助进行前台运行的功能。

Pod可以由一个或多个容器组合而成

场景1:单个应用多个容器
spring boot web:

kubectl create -f springboot-deployment.yml

kubectl get pods -o wide

加入 –o wide参数 查看额外信息:包括node和ip

pod处于pending的原因:通过 kubectl describe pods springbootweb 进一步查找问题。

可以看到pod的镜像信息写错了:

先删除pod,然后再创建: kubectl delete pod springbootweb

由于创建的端口号是9081,可以直接访问:curl 10.0.86.2:9081

# curl 10.0.86.2:9081
Hello world

场景2:Pod不同应用多个容器组合而成
例如:两个容器应用的前端frontend和redis为紧耦合的关系,应该组合成一个整体对外提供服务,则应该将这两个打包为一个pod.

配置文件frontend-localredis-pod.yaml如下:

属于一个Pod的多个容器应用之间相互访问只需要通过localhost就可以通信,这一组容器被绑定在一个环境中。

使用kubectl create创建该Pod后,get Pod信息可以看到如下图:

#kubectl get gods

可以看到READY信息为2/2,表示Pod中的两个容器都成功运行了.
2.3 集群外部访问Pod
上面的例子,在k8s集群的安装有kube-proxy的node节点上,可以直接通过curl 10.0.86.2:9081 访问集群的pod。但在集群外的客户端系统无法通过Pod的IP地址或者Service的虚拟IP地址和虚拟端口号访问到它们。为了让外部客户端可以访问这些服务,可以将Pod或Service的端口号映射到宿主机,以使得客户端应用能够通过物理机访问容器应用。

1、将容器应用的端口号映射到物理机

(2)通过设置Pod级别的hostNetwork-true,该Pod中所有容器的端口号都将被直接映射到物理机上。设置hostNetwork-true时需要注意,在容器的ports定义部分如果不指定hostPort,则默认hostPort等于containerPort,如果指定了hostPort,则hostPort必须等于containerPort的值。

静态pod是由kubelet进行管理的仅存在于特定Node的Pod上,他们不能通过API Server进行管理,无法与ReplicationController、Deployment或者DaemonSet进行关联,并且kubelet无法对他们进行健康检查。静态Pod总是由kubelet进行创建,并且总是在kubelet所在的Node上运行。

创建静态Pod有两种方式:配置文件或者HTTP方式

1)配置文件方式
首先,需要设置kubelet的启动参数"--config",指定kubelet需要监控的配置文件所在的目录,kubelet会定期扫描该目录,并根据目录中的 .yaml或 .json文件进行创建操作

假设配置目录为/etc/kubelet.d/配置启动参数:--config=/etc/kubelet.d/,然后重启kubelet服务后,再宿主机受用docker ps或者在Kubernetes Master上都可以看到指定的容器在列表中

由于静态pod无法通过API Server直接管理,所以在master节点尝试删除该pod,会将其变为pending状态,也不会被删除

#kubetctl delete pod static-web-node1

要删除该pod的操作只能在其所在的Node上操作,将其定义的.yaml文件从/etc/kubelet.d/目录下删除

#rm -f /etc/kubelet.d/static-web.yaml

#docker ps

Volume类型包括:emtyDir、hostPath、gcePersistentDisk、awsElasticBlockStore、gitRepo、secret、nfs、scsi、glusterfs、persistentVolumeClaim、rbd、flexVolume、cinder、cephfs、flocker、downwardAPI、fc、azureFile、configMap、vsphereVolume等等,可以定义多个Volume,每个Volume的name保持唯一。在同一个pod中的多个容器能够共享pod级别的存储卷Volume。Volume可以定义为各种类型,多个容器各自进行挂载操作,讲一个Volume挂载为容器内需要的目录。

如下图:

如上图中的Pod中包含两个容器:tomcat和busybox,在pod级别设置Volume “app-logs”,用于tomcat想其中写日志文件,busybox读日志文件。

配置文件如下:

busybox容器可以通过kubectl logs查看输出内容

#kubectl logs volume-pod -c busybox

tomcat容器生成的日志文件可以登录容器查看
#kubectl exec -ti volume-pod -c tomcat -- ls /usr/local/tomcat/logs

应用部署的一个最佳实践是将应用所需的配置信息于程序进行分离,这样可以使得应用程序被更好的复用,通过不用配置文件也能实现更灵活的功能。将应用打包为容器镜像后,可以通过环境变量或外挂文件的方式在创建容器时进行配置注入。ConfigMap是Kubernetes v1.2版本开始提供的一种统一集群配置管理方案。

6.1 ConfigMap:容器应用的配置管理
容器使用ConfigMap的典型用法如下:

ConfigMap以一个或多个key:value的形式保存在Kubernetes系统中共应用使用,既可以用于表示一个变量的值,也可以表示一个完整的配置文件内容。

通过yuaml配置文件或者直接使用kubelet create configmap 命令的方式来创建ConfigMap

6.2 ConfigMap的创建
举个小例子cm-appvars.yaml来描述将几个应用所需的变量定义为ConfigMap的用法:

# vim cm-appvars.yaml

执行kubectl create命令创建该ConfigMap

#kubectl create -f cm-appvars.yaml

查看建立好的ConfigMap:

#kubectl get configmap
kubectl describe configmap cm-appvars
kubectl get configmap cm-appvars -o yaml

另:创建一个cm-appconfigfile.yaml描述将两个配置文件server.xml和logging.properties定义为configmap的用法,设置key为配置文件的别名,value则是配置文件的文本内容:

在pod "cm-test-app"定义中,将configmap "cm-appconfigfile"中的内容以文件形式mount到容器内部configfiles目录中。

Pod配置文件cm-test-app.yaml内容如下:

创建该Pod:
#kubectl create -f cm-test-app.yaml
Pod "cm-test-app"created

登录容器查看configfiles目录下的server.xml和logging.properties文件,他们的内容就是configmap “cm-appconfigfile”中定义的两个key的内容
#kubectl exec -ti cm-test-app -- bash
root@cm-rest-app:/# cat /configfiles/server.xml
root@cm-rest-app:/# cat /configfiles/ logging.properties

6.3使用ConfigMap的条件限制
使用configmap的限制条件如下:

Pod在整个生命周期过程中被定义为各种状态,熟悉Pod的各种状态有助于理解如何设置Pod的调度策略、重启策略

Pod的状态包含以下几种,如图:

Pod的重启策略(RestartPolicy)应用于Pod内所有的容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某哥容器异常退出或者健康检查石柏师,kubelet将根据RestartPolicy的设置进行相应的操作

Pod的重启策略包括Always、OnFailure及Nerver,默认值为Always。

kubelet重启失效容器的时间间隔以sync-frequency乘以2n来计算,例如1、2、4、8倍等,最长延时5分钟,并且成功重启后的10分钟后重置该事件。

Pod的重启策略和控制方式息息相关,当前可用于管理Pod的控制器宝库ReplicationController、Job、DaemonSet及直接通过kubelet管理(静态Pod),每种控制器对Pod的重启策略要求如下:

RC和DaemonSet:必须设置为Always,需要保证该容器持续运行
Job:OnFailure或Nerver,确保容器执行完成后不再重启
kubelet:在Pod失效时重启他,不论RestartPolicy设置什么值,并且也不会对Pod进行健康检查

对Pod的健康检查可以通过两类探针来检查:LivenessProbe和ReadinessProbe

LivenessProbe探针:用于判断容器是否存活(running状态),如果LivenessProbe探针探测到容器不健康,则kubelet杀掉该容器,并根据容器的重启策略做响应处理
ReadinessProbe探针:用于判断容器是否启动完成(ready状态),可以接受请求。如果ReadinessProbe探针探测失败,则Pod的状态被修改。Endpoint Controller将从service的Endpoint中删除包含该容器所在的Pod的Endpoint。
kubelet定制执行LivenessProbe探针来诊断容器的健康状况。LivenessProbe有三种事项方式。

1)ExecAction:在容器内部执行一个命令,如果该命令的返回值为0,则表示容器健康。例:

(2)TCPSocketAction:通过容器ip地址和端口号执行TCP检查,如果能够建立tcp连接表明容器健康。例:

3)HTTPGetAction:通过容器Ip地址、端口号及路径调用http get方法,如果响应的状态吗大于200且小于400,则认为容器健康。例:

对于每种探针方式,都需要设置initialDelaySeconds和timeoutSeconds两个参数,它们含义如下:

initialDelaySeconds:启动容器后首次监控检查的等待时间,单位秒
timeouSeconds:健康检查发送请求后等待响应的超时时间,单位秒。当发生超时就被认为容器无法提供服务无,该容器将被重启
九.玩转Pod调度
在Kubernetes系统中,Pod在大部分场景下都只是容器的载体而已,通常需要通过RC、Deployment、DaemonSet、Job等对象来完成Pod的调度和自动控制功能。

9.1 RC、Deployment:全自动调度
RC的主要功能之一就是自动部署容器应用的多份副本,以及持续监控副本的数量,在集群内始终维护用户指定的副本数量。

在调度策略上,除了使用系统内置的调度算法选择合适的Node进行调度,也可以在Pod的定义中使用NodeName、NodeSelector或NodeAffinity来指定满足条件的Node进行调度。

1)NodeName
Pod.spec.nodeName用于强制约束将Pod调度到指定的Node节点上,这里说是“调度”,但其实指定了nodeName的Pod会直接跳过Scheler的调度逻辑,直接写入PodList列表,该匹配规则是强制匹配。

2)NodeSelector:定向调度
Kubernetes Master上的scheler服务(kube-Scheler进程)负责实现Pod的调度,整个过程通过一系列复杂的算法,最终为每个Pod计算出一个最佳的目标节点,通常我们无法知道Pod最终会被调度到哪个节点上。实际情况中,我们需要将Pod调度到我们指定的节点上,可以通过Node的标签和pod的nodeSelector属性相匹配来达到目的。

Pod.spec.nodeSelector是通过kubernetes的label-selector机制进行节点选择,由scheler调度策略MatchNodeSelector进行label匹配,调度pod到目标节点,该匹配规则是强制约束。

启用节点选择器的步骤为:

kubectl label nodes <node-name> <label-key>=<label-value>

例: #kubectllabel nodes k8s-node-1 zonenorth

运行kubectl create -f命令创建Pod,scheler就会将该Pod调度到拥有zone=north标签的Node上。 如果多个Node拥有该标签,则会根据调度算法在该组Node上选一个可用的进行Pod调度。

需要注意的是:如果集群中没有拥有该标签的Node,则这个Pod也无法被成功调度。

3)NodeAffinity:亲和性调度
该调度策略是将来替换NodeSelector的新一代调度策略。由于NodeSelector通过Node的Label进行精确匹配,所有NodeAffinity增加了In、NotIn、Exists、DoesNotexist、Gt、Lt等操作符来选择Node。调度侧露更加灵活。

9.2 DaemonSet:特定场景调度
DaemonSet用于管理集群中每个Node上仅运行一份Pod的副本实例,如图:

这种用法适合一些有下列需求的应用:

在实际生产环境中,我们经常遇到某个服务需要扩容的场景,也有可能因为资源精确需要缩减资源而需要减少服务实例数量,此时我们可以Kubernetes中RC提供scale机制来完成这些工作。

以redis-slave RC为例,已定义的最初副本数量为2,通过kubectl scale命令可以将Pod副本数量

#kubectl scale rc redis-slave --replicas=3
ReplicationController"redis-slave" scaled
#kubectl get pods

除了可以手工通过kubectl scale命令完成Pod的扩容和缩容操作以外,新版本新增加了Horizontal Podautoscaler(HPA)的控制器,用于实现基于CPU使用路进行启动Pod扩容缩容的功能。该控制器基于Mastger的kube-controller-manager服务启动参数 --horizontal-pod-autoscler-sync-period定义的时长(默认30秒),周期性监控目标Pod的Cpu使用率并在满足条件时对ReplicationController或Deployment中的Pod副本数量进行调整,以符合用户定义的平均Pod Cpu使用率,Pod Cpu使用率来源于heapster组件,所以需预先安装好heapster。

当集群中的某个服务需要升级时,我们需要停止目前与该服务相关的所有Pod,然后重新拉取镜像并启动。如果集群规模较大,因服务全部停止后升级的方式将导致长时间的服务不可用。由此,Kubernetes提供了rolling-update(滚动升级)功能来解决该问题。

滚动升级通过执行kubectl rolling-update命令一键完成,该命令创建一个新的RC,然后自动控制旧版本的Pod数量逐渐减少到0,同时新的RC中的Pod副本数量从0逐步增加到目标值,最终实现Pod的升级。需要注意的是,系统要求新的RC需要与旧的RC在相同的Namespace内,即不能把别人的资产转到到自家名下。
例:将redis-master从1.0版本升级到2.0:

需要注意的点:

运行kubectl rolling-update来完成Pod的滚动升级:

#kubectl rolling-update redis-master -f redis-master-controller-v2.yaml

另一种方法就是不使用配置文件,直接用kubectl rolling-update加上--image参数指定新版镜像名来完成Pod的滚动升级

#kubectl rolling-update redis-master --image=redis-master:2.0

与使用配置文件的方式不同的是,执行的结果是旧的RC被删除,新的RC仍然使用就的RC的名字。

如果在更新过程总发现配置有误,则用户可以中断更新操作,并通过执行kubectl rolling-update-rollback完成Pod版本的回滚。

⑥ k8s之存储

k8s的存储常用的就是上面几种模式,分为临时存储,半持久化存储,与持久化存储这三类,本章我们着重讲解emptydir与hostpath与pvc跟pv等

当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中。空卷变成了一个临时卷

供pod内的容器读取和写入数据,一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁

一般来说emptydir的用途都是用来充当临时存储空间,例如一些不需要数据持久化的微服务,我们都可以用emptydir来当做微服务pod的存储方案

k8s存储emptdir实战例子:以之前的myapp应用为例

创建应用

观察是否生产data目录,并在/data目录创建文件test.txt

手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod

这时候在看我们之前创建的data.txt已经不见了

hostPath类型则是映射node文件系统中的文件或者目录到pod里。在使用hostPath类型的存储卷时,也可以设置type字段,支持的类型有文件、目录、File、Socket、CharDevice和BlockDevice(我只映射过文件与目录)。

其实这个功能就相当于docker中的-v 目录映射,只不过在k8s中的时候,pod会漂移,当pod漂移到其他node节点的时候,pod不会跨节点的去读取目录。所以说hostpath只能算一种半持久化的存储方式

实战例子

创建应用

在node节点可以看到生成了/data/volume目录,在里面创建测试文件

检验pod里面的/data是否存在这个映射目录的文件

可以看到刚才创建的文件已经映射到我们的目录里边了

为了验证是否映射到容器里面的目录也会随着pod生命周期的消失而消失,我们手动删除pod模拟容器终止

可以看到容器被删除后,新建的pod也可以看到我们映射的目录,而且里面数据test.txt还在。

这有个缺点就是不能够跨容器去读取数据,如果删除后的pod被调度到其他节点的话,原来的数据也就没有了,如果能不受节点的影响,并且挂载的数据不会随生命周期的结束而结束,我们应该怎么解决呢?就是我们后面讲到的持久化存储了

上面介绍了俩种临时存储于半持久化存储的方案。在k8s实际生产环境中,一般会选用私有云持久化存储方案还有公有云持久化存储方案,私有云存储方案包括nfs,ceph,glusterfs等方案。公有云存储会用到AWS等方案

存储方案各有各的优缺点,可参考 https://www.cnblogs.com/yswenli/p/7234579.html 这篇文章。今天我们主要讲解pvc,pv,nfs之间的关系。

简单来说,要使用持久化存储,就需要使用pvc去跟pv去申请,然后pv查看自己有没有合适的存储空间卷,有合适的就与pvc进行绑定。pv与pvc是一一对应绑定的。现在我们用一幅图来说明pvc,pv,nfs的关系

实战例子

准备存储服务安装nfs

接下来创建pv与nfs绑定

创建pvc与pv关联

创建并查看结果

注意:STATUS为Bound说明该pvc已经与pv绑定了

接下来创建pod来引用pvc

创建pod

接下来验证一下在nfs创建一个测试文件,看是否被映射到容器中

可以看到容器里面也可以看到创建的文件

手动删除容器,或者调度到其他节点原来的文件还是不会被删除的,这里就省略验证了,这就是nfs的好处,不过企业一般不会选用nfs,磁盘IO,性能读取方面不太理想,毕竟是本地存储,还是有一定的风险。推荐用用云存储。

文件存储提供无限扩展的文件系统来给云服务器存取数据实际上相当于nfs

1、已注册阿里云账号,并完成实名认证。

如果没有,请先注册阿里云账号,详情请参见阿里云账号注册流程。

2、已开通NAS服务。

首次登录NAS控制台时,根据页面提示开通NAS服务。

3、在需要创建文件系统的地域,已有可用的云服务器ECS。

k8s应用NAS实战例子

1、打开阿里云NAS控制台确认已创建好文件系统

2、把复制挂载参数把它挂载到服务器中创建测试目录,前提是服务器需要安装nfs客户端。

安装完成挂载到本地目录并创建test目录作为测试目录

并在里面创建一个测试文件1.txt

接下来可以创建pvc和pv了

创建并查看

接下来创建pod去引用pvc

创建pod

检验刚才的1.txt是否挂到容器的/data/nas下

云存储适合于生产环境k8s的存储解决方案

⑦ microk8s上给Pod挂载NFS

团队新开发的区域医疗平台包含一个课件上传与播放模块,其实际的业务包含如下的步骤:

1.  县级和乡镇卫生院的医生们通过在线视频参加培训、并录制视频。

2. 医生上传视频,并共享课件。

3.  平台上的其他医生可以在课件学习栏目学习录制的培训会议。

通常,课件的上传和保存我们都是通过对象存储做的,对象存储的好处显而易见:不担心文件丢失(三份备份), 不担心容量(云服务商集群),https/http访问, 数据安全(对象访问签名);  但是这个平台需要部署到区域医疗机构的机房里,多数情况下是没有对象存储的,外购对象存储也成本过高, 所以我们采用了折中的方案,存储系统换为NFS, 只需要一个大存储量的机器就可以了,大概服务如下:

我们开发了一个uploader服务,用于上传文件,同时使用Nginx提供http/https服务,两个服务之间共享存储,使用NFS存储挂载给他们。

这里我们来看看怎么给microk8s上的pod挂载NFS存储。

首先,我们需要安装NFS server 用于测试

我们使用ubuntu来进行测试,先执行命令:sudo apt install nfs-kernel-server

这个命令将安装服务器端,以及所有相关的包:

在home目录下创建一个nfsshare的文件夹用作共享目录。然后我们来编辑nfs配置文件,配置该共享目录,默认允许所有IP段挂在:

启动nfs: sudo /etc/init.d/nfs-kernel-server start

可以使用服务命令查看服务状态: service nfs-kernel-server   status

使用ip addr命令查看以下当前主机的IP:

现在我们来使用nfsclient端测试一下:

sudo mount 10.0.2.15:/home/nfsshare   /mnt

查询 /mnt 目录,可以看到在 /home/nsshare下面创建的文件和子目录

测试完后umount 挂载点:  sudo umount -v /mnt

1. 先写一个PV, 用来表示可以挂载的NFS存储:

2. 接着写一个PVC,用于绑定PV:

3. 然后写包装了nginx的存储服务的deployment文件:

4. 最后写存储服务对应的service文件:

完成了所有配置文件,使用microk8s kubectl apply -f 命令,依次创建资源对象。

查询创建的pod:

使用exec 登陆docker,

查询/usr/share/storage目录,如下图,nfs已经挂载好了,可以看见我们在test目录下创建了几个文件:

最后,我们来验证一下http方式访问文件

现在storage的nfs目录下创建两个子目录,attachement、video , 对应docker的挂在路径为: /usr/share/storage/atttachment、/usr/share/storage/video。 

在两个文件夹下面,分别创建两个文件 helloA.txt  helloB.txt, 并随意写写内容。

完成后我们通过30080端口,从microk8s节点的浏览器访问两个文件:

至此,在k8s下,给pod挂在nfs的工作,并通过http访问的任务就完了。另外:

1.   后续需要考虑,通过Lua写一个脚本和nginx集成,实现访问资源签名验证,这个机制可以很容易的参考对象存储的验证。

2. 这里没有讨论,如何写upload和制作storage服务的镜像,upload服务也需要挂载nfs,原理是一样的,所以就不再讨论。 storage服务是基于Nginx镜像实现,下面的附录了带上了可参考的配置文件。

==============================================================

附录A: storageservice服务中,nginx的配置文件storage.conf

附录B:  storageservice服务中dockerfile文件

附录C: Storageservice的build.gradle文件

参考 一个Springboot项目的build.gradle和Dockerfile ,  这里的buiild.gradle文件不需要java build, 仅需要用来生成新docker镜像。