k8s在1.14版本开始,GA支持本地存储[1],这对于我们来说,是个非常好的消息,因为我们现在的虚机环境,就是采用的本地存储,如果k8s能够较好的支持本地存储的分配策略,对我们的迁移工作,将是非常大的一个帮助,这里要感谢k8s,GA的真是时候。
但是在使用本地存储的时候,还是发现了一个问题,我个人认为在设计上有待考量,即如标题所说,k8s的pvc(PersistentVolumeClaim)在被Pod挂载的状态下,如果对其执行删除动作,将会变为Terminating状态,且这是一个几乎不可逆的过程,完全无法回滚(后面我会细讲为什么),只能够等待Pod被删除的时候,这个pvc会跟着一起删除。大家理解我的意思吧,也就是一旦pvc被执行了删除动作,那么它的死期就已经被定了下来,那就是挂载它的那个Pod被删除的时候,这个设计逻辑,不是很有意思吗?一个Persistent Volume Claim,永远无法再Persistent了,只能等着跟Pod一起被干掉,实在是让人有点无法理解,但是经过本文记录的一系列查证之后发现,原来k8s就是这么设计的(详见issue评论[2],liggitt应该是k8s pv相关设计的leader吧,直接就把这位仁兄提的issue给close了),顿时无语,只能上知乎吐槽一下了。
宣泄完情绪,回归主题,先把问题复现一下:
//首先创建了本地存储的pv和使用本地存储pv的statefulset,如下:【这块创建工作略过,本文的问题与pv是何种类型无关】
# kubectl get pods
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4d14h
web-1 1/1 Running 0 10d
web-2 1/1 Running 0 10d
# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Terminating local-pv-40faed3c 5584Gi RWO local-disks 4d14h
www-web-1 Bound local-pv-310d5bcb 5584Gi RWO local-disks 10d
www-web-2 Bound local-pv-a23634e8 4467Gi RWO local-disks 10d
//对于www-web-0,在之前执行了kubectl delete pvc www-web-0的操作,所以状态从Bound变成了Terminating。然后这个状态就再不能通过任何方式回滚了,
//只有等web-0被删除以后,www-web-0与其一同被删除。
//【而且删除web-0之后,web-0的重建会失败,报错www-web-0 not found,只有再次删除web-0,新的pvc才会被创建出来,
//详见https://github.com/kubernetes/kubernetes/issues/74374,主要是因为删除pod和删除pvc是两个go程在做的,存在一个先后冲突问题。这个不是本文重点,就不多说了。
因为试图回滚,也看了一下pvc在正常Bound状态下和Terminating状态下的区别:
分别kubectl get pvc [pvc] -o yaml了一下,发现唯一的区别就是Terminating状态下pvc的metadata多了如下内容:
deletionGracePeriodSeconds: 0
deletionTimestamp: "2019-06-06T10:44:44Z"
看来这就是Terminating的原因,第一反应就是是否可以修改或者删除deletionTimestamp这个值呢?试了一下用kubectl patch修改成“”或者很久以后的某个时间,发现不能,没反应。
# kubectl patch pvc www-web-0 -p '{"metadata":{"deletionTimestamp":""}}'
Error from server: parsing time "" as "2006-01-02T15:04:05Z07:00": cannot parse "" as "2006"
# kubectl patch pvc www-web-0 -p '{"metadata":{"deletionTimestamp":"2019-07-06T10:44:44Z"}}'
persistentvolumeclaim/www-web-0 patched (no change)
时间还是原来的,没改变
然后想看看是否可以直接删掉,同样。
# kubectl patch pvc www-web-0 --type=json -p='[{"op": "remove", "path": "/metadata/deletionTimestamp"}]'
persistentvolumeclaim/www-web-0 patched (no change)
依旧没有反映。
这就奇怪了,其他的内容,都可以通过patch修改和删除,只有deletionTimestamp和deletionGracePeriodSeconds不能,我在想,难道是代码层做了限制?话说,中途,我还想通过etcd直接修改value值,但是因为etcd里存的value是protobuf协议编码[3],还得转换写入,实在麻烦,但这个方式最后看来,是唯一可以回滚的途径了=_=!!。
接下来,看代码,从patch的api开始跟,发现这么一段【代码是v1.14.2版本】:
这回清楚了,为什么怎么改都改不了?因为代码写死了,不让改。好吧,那为什么不让改呢?看看blame吧,发现这次提交就是Jordan Liggitt提交的。
找到对应的Merge PR。
看PR[4]里面全部commit的内容,应该只是把原来的限制,挪了一下位置,从很早之前就一直有。
好吧,代码还是不继续跟了,看看issue,搜一下"deletionTimestamp pvc undo"试试,果然搜到一个关键issue,所以英文好还是很重要啊,之前搜没加"undo"这个词,就没搜出什么正相关的issue。
这个也就是文章最开始提到的那个issue[2]。
得出结论了,看回复原文吧,人家就是不想让你undo回滚,连workaround的机会都不给你。好吧,这回死心了。
结论:
1、原生k8s不允许执行删除后的回滚动作。——所以删除任何东西前,要想好,k8s的最终一致性可是原则,虽然我认为这个原则不应该在这个管理层面上强制执行的。
2、如果真的删除了,想挽留,在不改k8s原生代码的前提下,只能有以下两种方式挽回:
1)将pvc的persistentVolumeReclaimPolicy从Delete改为Retain,这样起码删除pod之后,pvc即使被删除,底层数据也不会被删。
2)通过修改etcd里面的value值,删除deletionTimestamp和deletionGracePeriodSeconds两个key。(需要有protobuf协议到yaml的相互转换,还没试过)
3、还有一个比较简单的方式,就是修改k8s的代码,将上面截图的那部分限制代码删掉,允许kubectl patch删除deletionTimestamp和deletionGracePeriodSeconds这两个key,个人感觉,这个可以有。
附注参考:
https://stackoverflow.com/questions/tagged/kubernetes【有些求助类问题,比搜github issue靠谱,类似本文的问题,搜pvc terminating,就查到一个相关的提问Cancel or undo deletion of Persistent Volumes in kubernetes cluster】
https://stackoverflow.com/questions/45744534/etcd-v3-cant-read-encoded-values
kubectl patcth用法:
https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/
https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
参考
- ^本地存储provider https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner
- ^abUndelete/lazy delete resources https://github.com/kubernetes/kubernetes/issues/69980#issuecomment-431035255
- ^etcdctl v3: k8s changes its internal format to proto, and the etcdctl result is unreadable. #44670 https://github.com/kubernetes/kubernetes/issues/44670
- ^Deprecate repair-malformed-updates flag, move object meta mutation into BeforeCreate #61455 https://github.com/kubernetes/kubernetes/pull/61455