Kubernetes API Server对象修改的乐观锁控制
一 背景
项目需要在OpenShift管理的Prometheus的基础上包装通过Web界面告警规则管理的功能,OpenShift 3.11版所内置的Prometheus支持通过一种叫做PrometheusRule的Custom Resource(CR)的对象管理来管理告警规则。业务上存在多人管理同个CR的可能性,所以需要防止对同个CR进行并发竞争修改,造成结果错误。
Kubernetes API Server是支持乐观锁(Optimistic concurrency control)
的机制来防止并发写造成的覆盖写问题,详见此文章。通过给资源对象赋予版本号,并且API Server在更新时检查用户上传的对象中的metadata.resourceVersion
来核对是否此次修改已经过时来保证修改的正确性。
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
labels:
prometheus: k8s
role: alert-rules
name: arule
namespace: openshift-monitoring
resourceVersion: "2218687"
...
spec:
.....
客户端获取到一个带resourceVersion字段的对象后进行修改,然后上传修改时必须同时将resourceVersion字段送回,这样API Server就会自行防止并发更新错误。
二 oc edit的行为
打开两个terminal A和B,都同时运行oc edit prometheusrule arule
,在Terminal A进行一个改动,比如将下面的spec里面的user改为otherone,然后保存修改。
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
resourceVersion: "2322087"
spec:
groups:
- name: general.rules
rules:
- alert: TargetDown-serviceprom
........
for: 33m
labels:
severity: warning
user: someone ----> 改为otherone。
在Terminal A执行oc get prometheusrule arule
的话,可以看到修改生效。
然后在Terminal B在同样的对象上对for做修改后保存,最后再把对象读回来可以发现在Terminal A对user的修改丢失了。
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
resourceVersion: "2322087"
spec:
groups:
- name: general.rules
rules:
- alert: TargetDown-serviceprom
........
for: 33m
labels:
severity: warning
user: someone ----> 改为otherone。
三 HTTP PUT API的乐观锁
在OpenShift集群中的有访问Custom Resource对象权限的容器里,先用curl将对象下载下来保存成文件rule1.json和rule2.json, 分别对rule1做上一节Terminal A的修改,对rule2.json做Terminal B的修改,
> curl -k -X GET -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://$KUBERNETES_PORT_443_TCP_ADDR:$KUBERNETES_SERVICE_PORT_HTTPS/apis/monitoring.coreos.com/v1/namespaces/openshift-monitoring/prometheusrules/arule > /tmp/rule1.json
<