目录

  • local volume
  • 创建一个storage class
  • 静态创建PV
  • 使用local volume PV
  • 动态创建PV

local volume

kubernetes从1.10版本开始支持local volume(本地卷),workload(不仅是statefulsets类型)可以充分利用本地快速SSD,从而获取比remote volume(如cephfs、RBD)更好的性能。

在local volume出现之前,statefulsets也可以利用本地SSD,方法是配置hostPath,并通过nodeSelector或者nodeAffinity绑定到具体node上。但hostPath的问题是,管理员需要手动管理集群各个node的目录,不太方便。

下面两种类型应用适合使用local volume。

  • 数据缓存,应用可以就近访问数据,快速处理。
  • 分布式存储系统,如分布式数据库Cassandra ,分布式文件系统ceph/gluster

下面会先以手动方式创建PV、PVC、Pod的方式,介绍如何使用local volume,然后再介绍external storage提供的半自动方式,最后介绍社区的一些发展。

创建一个storage class

首先需要有一个名为local-volume的sc。

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-volume
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

sc的provisioner是 kubernetes.io/no-provisioner

WaitForFirstConsumer表示PV不要立即绑定PVC,而是直到有Pod需要用PVC的时候才绑定。调度器会在调度时综合考虑选择合适的local PV,这样就不会导致跟Pod资源设置,selectors,affinity and anti-affinity策略等产生冲突。很明显:如果PVC先跟local PV绑定了,由于local PV是跟node绑定的,这样selectors,affinity等等就基本没用了,所以更好的做法是先根据调度策略选择node,然后再绑定local PV。

静态创建PV

通过kubectl命令,静态创建一个5GiB的PV;该PV使用node ubuntu-1的 /data/local/vol1 目录;该PV的sc为local-volume。

apiVersion: v1
kind: PersistentVolume
metadata:
name: example-local-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-volume
local:
path: /data/local/vol1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- ubuntu-

Retain(保留)是指,PV跟PVC释放后,管理员需要手工清理,重新设置该卷。

需要指定PV对应的sc;目录/data/local/vol1也需要创建。

kubectl get pv example-local-pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
example-local-pv 5Gi RWO Retain Available local-volume 8d

使用local volume PV

接下来创建一个关联 sc:local-volume的PVC,然后将该PVC挂到nginx容器里。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: local-volume
---
kind: Pod
apiVersion: v1
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: nginx
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim

进入到容器里,会看到挂载的目录,大小其实就是上面创建的PV所在磁盘的size。

/dev/sdb         503G  235M  478G   % /usr/share/nginx/html

在宿主机的/data/local/vol1目录下创建一个index.html文件:

echo "hello world" > /data/local/vol1/index.html

然后再去curl容器的IP地址,就可以得到刚写入的字符串了。

删除Pod/PVC,之后PV状态改为Released,该PV不会再被绑定PVC了。

动态创建PV

手工管理local PV显然是很费劲的,社区提供了external storage可以动态的创建PV(实际仍然不够自动化)。

local volume provisioner的官方编排在local-volume/provisioner/deployment/kubernetes/example/default_example_provisioner_generated.yaml目录里,不过官方文档一会fast-disk,一会local-storage,有点混乱。我这里统一都用local-volume

---
apiVersion: v1
kind: ConfigMap
metadata:
name: local-provisioner-config
namespace: default
data:
storageClassMap: |
local-volume:
hostDir: /data/local
mountDir: /data/local
blockCleanerCommand:
- "/scripts/shred.sh"
- ""
volumeMode: Filesystem
fsType: ext4
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: local-volume-provisioner
namespace: default
labels:
app: local-volume-provisioner
spec:
selector:
matchLabels:
app: local-volume-provisioner
template:
metadata:
labels:
app: local-volume-provisioner
spec:
serviceAccountName: local-volume-admin
containers:
- image: "silenceshell/local-volume-provisioner:v2.1.0"
imagePullPolicy: "Always"
name: provisioner
securityContext:
privileged: true
env:
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
volumeMounts:
- mountPath: /etc/provisioner/config
name: provisioner-config
readOnly: true
- mountPath: /data/local
name: local
mountPropagation: "HostToContainer"
volumes:
- name: provisioner-config
configMap:
name: local-provisioner-config
- name: local
hostPath:
path: /data/local
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: local-volume-admin
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: local-volume-provisioner-pv-binding
namespace: default
subjects:
- kind: ServiceAccount
name: local-volume-admin
namespace: default
roleRef:
kind: ClusterRole
name: system:persistent-volume-provisioner
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: local-volume-provisioner-node-clusterrole
namespace: default
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: local-volume-provisioner-node-binding
namespace: default
subjects:
- kind: ServiceAccount
name: local-volume-admin
namespace: default
roleRef:
kind: ClusterRole
name: local-volume-provisioner-node-clusterrole
apiGroup: rbac.authorization.k8s.io

kubectl创建后,由于是daemonset类型,每个节点上都会启动一个provisioner。该provisioner会监视 “discovery directory”,即上面配置的/data/local

$ kubectl get pods -o wide|grep local-volume
local-volume-provisioner-rrsjp / Running 5m 10.244.1.141 ubuntu- <none>
local-volume-provisioner-v87b7 / Running 5m 10.244.2.69 ubuntu- <none>
local-volume-provisioner-x65k9 / Running 5m 10.244.0.174 ubuntu- <none>

前面mypod/myclaim已经删除了,我们重新创建一个,此时pvc myclaim是Pending状态,provisoner并没有自动供给存储。为什么呢?

原来external-storage的逻辑是这样的:其Provisioner本身其并不提供local volume,但它在各个节点上的provisioner会去动态的“发现”挂载点(discovery directory),当某node的provisioner在/data/local/目录下发现有挂载点时,会创建PV,该PV的local.path就是挂载点,并设置nodeAffinity为该node。

那么如何获得挂载点呢?

直接去创建目录是行不通的,因为provsioner希望PV是隔离的,例如capacity,io等。试着在ubuntu-2上的/data/local/下创建一个xxx目录,会得到这样的告警。

discovery.go:201] Path "/data/local/xxx" is not an actual mountpoint

目录不是挂载点,不能用。

该目录必须是真材实料的mount才行。一个办法是加硬盘、格式化、mount,比较麻烦,实际可以通过本地文件格式化(loopfs)后挂载来“欺骗”provisioner,让它以为是一个mount的盘,从而自动创建PV,并与PVC绑定。

如下。

将下面的代码保存为文件 loopmount,加执行权限并拷贝到/bin目录下,就可以使用该命令来创建挂载点了。

#!/bin/bash

# Usage: sudo loopmount file size mount-point

touch $
truncate -s $ $
mke2fs -t ext4 -F $ > /dev/null > /dev/null
if [[ ! -d $ ]]; then
echo $ " not exist, creating..."
mkdir $
fi
mount $ $
df -h |grep $

使用脚本创建一个6G的文件,并挂载到/data/local下。之所以要6G,是因为前面PVC需要的是5GB,而格式化后剩余空间会小一点,所以设置文件更大一些,后面才好绑定PVC。

# loopmount xxx 6G /data/local/xxx
/data/local/xxx not exist, creating...
/dev/loop0 .9G 24M .6G % /data/local/x1

查看PV,可见Provisioner自动创建了PV,而kubernetes会将该PV供给给前面的PVC myclam,mypod也run起来了。

# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
local-pv-600377f7 5983Mi RWO Delete Bound default/myclaim local-volume 1s

可见,目前版本的local volume还无法做到像cephfs/RBD一样的全自动化,仍然需要管理员干涉,显然这不是一个好的实现。

社区有人提交了基于LVM做local volume动态供给的Proposal,不过进展很缓慢。作者是huawei的员工,应该huawei已经实现了。

除了基于LVM,也可以基于 ext4 project quota 来实现LV的动态供给。

除了使用磁盘,还可以考虑使用内存文件系统,从而获取更高的io性能,只是容量就没那么理想了。一些特殊的应用可以考虑。

mount -t tmpfs -o size=1G,nr_inodes=10k,mode= tmpfs /data/local/tmpfs

总的来说,local volume本地卷目前不支持动态供给,还无法真正推广使用,但可以用来解决一些特定问题。

Ref:

参考文档:

https://kubernetes.io/blog/2019/04/04/kubernetes-1.14-local-persistent-volumes-ga/

https://ieevee.com/tech/2019/01/17/local-volume.html

kubernetes支持local volume的更多相关文章

  1. 关于 Kubernetes 中的 Volume 与 GlusterFS 分布式存储

    容器中持久化的文件生命周期是短暂的,如果容器中程序崩溃宕机,kubelet 就会重新启动,容器中的文件将会丢失,所以对于有状态的应用容器中持久化存储是至关重要的一个环节:另外很多时候一个 Pod 中可 ...

  2. Kubernetes中的Volume介绍

    Kubernetes中支持的所有磁盘挂载卷简介发表于 2018年1月26日 Weihai Feb 10,2016 7400 字 | 阅读需要 15 分钟 容器磁盘上的文件的生命周期是短暂的,这就使得在 ...

  3. Kubernetes的Local Persistent Volumes使用小记

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  4. kubernetes对象之Volume

    系列目录 概述 Volume是对各种存储资源的抽象.虚拟化.为管理.控制.使用存储资源提供统一接口.Openstack中的volume为虚拟机提供存储,Docker中的volume为容器提供存储.因为 ...

  5. (转)Docker volume plugin - enabled create local volume on docker host

    原文地址:https://hub.docker.com/r/cwspear/docker-local-persist-volume-plugin/ Short Description Create n ...

  6. [置顶] kubernetes资源类型--Volume

    在Docker的设计实现中,容器中的数据是临时的,即当容器被销毁时,其中的数据将会丢失.如果需要持久化数据,需要使用Docker数据卷挂载宿主机上的文件或者目录到容器中.在K8S中,当Pod重建的时候 ...

  7. 【PV和PVC】kubernetes存储 persistent volume(持久化硬盘)和 persistent volume claim(持久化硬盘请求)

    报错:pod has unbound immediate PersistentVolumeClaims (repeated 11 times) pv没有满足pvc需求 https://www.cnbl ...

  8. k8s local volume 和host path volume的区别

    k8s提供多种volume接口,其中local 和host path是容易混淆的两个接口.下面这篇文章解释了两者的区别: https://groups.google.com/forum/#!topic ...

  9. kubernetes系列(十三) - 存储之Volume

    1. Volume简介 1.1 k8s的volume和docker的volume区别 1.2 kubernetes支持的volume类型 2. 重点的volume类型 2.1 emptyDir 2.1 ...

随机推荐

  1. Python基础(一)--变量、注释、文件头

    一.变量 1.什么是变量 变量是变化的量,用来保存程序执行的状态以及状态的变化 2.变量定义规范 #1. 变量名只能是:字母.数字或下划线的任意组合 #2. 变量名的第一个字符不能是数字 #3. 关键 ...

  2. 使用Deployment控制器创建Pods并使Service发布到外网可访问

    由于NFS支持节点共同读取及写入,所以可使用Deployment控制器创建多个Pod,并且每一个Pod都共享同一个目录 k8s-master kubnet@hadoop2 volumes]$ vim ...

  3. mysql数据库之主从复制+双主--MMM

    mysql复制:在主数据库中,前端用户每执行一个写操作/语句,都会在二进制日志中保存一个事件,把这个事件从mysql的服务器中3306端口发送给从服务器,从服务器把这个事件接受下来,接受下来先保存在本 ...

  4. java static学习

    原创,转载请注明来源sogeisetsu的博客园 static,在类里面定义公共的属性,它可以统一修改,并只占一个内存.从而达到方便修改和少占内存的目的 先放上代码,您可以先越过代码,看后面的讲解内容 ...

  5. Centos6.5 下安装 tmux(免编译)

    环境:Centos6.5是新安装的,没有安装过其它的软件包.思路是安装epel的源后再安装tmux. yum install epel-release # 安装Linux的 epel 的yum源 yu ...

  6. 在windows下安装Superset

    前言 最近想用一下Superset,这个是一个开源项目,可以直接通过写sql来生成图表,有时候对一些图表需求比较多的时候,可以用的上. Superset是由Airbnb(知名在线房屋短租公司)开源BI ...

  7. java.lang.IllegalArgumentException: Invalid character found in the request target. The valid charact

    线上环境中部署的 Tomcat 项目,出现部分页面无法打开的情况,但本地环境是好的.经过排查发现,本地 Tomcat版本为 7.0.77,而线上版本为 7.0.88.报错的具体描述为java.lang ...

  8. luogu p2622关灯问题II

    luogu p2622关灯问题II 题目描述 现有n盏灯,以及m个按钮.每个按钮可以同时控制这n盏灯--按下了第i个按钮,对于所有的灯都有一个效果.按下i按钮对于第j盏灯,是下面3中效果之一:如果a[ ...

  9. tensorflow数据加载、模型训练及预测

    数据集 DNN 依赖于大量的数据.可以收集或生成数据,也可以使用可用的标准数据集.TensorFlow 支持三种主要的读取数据的方法,可以在不同的数据集中使用:本教程中用来训练建立模型的一些数据集介绍 ...

  10. CF1276 D. Tree Elimination

    CF1276 D. Tree Elimination 传送门 CodeForces Solution 考虑树型\(dp\),设\(f_{u,0/1/2/3}\)分别表示点\(u\)被自己父亲边之前的边 ...