前言
最近在排查consul拿不到锁的问题.报错信息failed to acquire lock: lock not available
我们consul跑在k8s当中, 数据持久化是用nfs做的,但是最近新部署的consul一直卡在拿锁卡主, 或者直接拿不到锁.
排查思路一直在为什么拿不到锁上。
拿不到锁的原因是:
通常没有启动rpc-statd
nfs的锁依赖于rpcbind服务,如果rpcbind服务不可用,那么nfs的锁也就不可用。 但是我们宿主机上的rpcbind服务是正常的. 通过执行命令rpcinfo -p可以看到rpcbind服务的进程. 发现缺少一个关键服务nlockmgr,这个服务是nfs的锁服务,如果这个服务不可用,那么nfs的锁也就不可用。
但是本地用过flock测试过nfs的锁是可以用的,所以这个问题就很奇怪了。
flock -e ./a.txt echo 1
问题
后来发现, 是两个问题重叠了.
问题一
内核机制导致rpcbind启动报错, 通过rpcinfo -p localhost查看rpcbind服务的进程,发现nlockmgr服务没有启动. 重启机器, 关闭selinux之后发现nlockmgr服务启动了.
问题二
consul拿不到锁, 但是此时不会报错了, 而是一直卡在拿锁的地方. 期间看了源码, 就是普通文件锁.
此时怀疑consul没有释放锁,导致重启后第二个进程也拿不到锁。 因为如果是本地文件, 假如锁没有释放内核会自动管理释放。但是nfs的锁是依赖于rpcbind服务的,所以如果没有释放锁,那么其他进程就拿不到锁。
解决方案
既然这样, 我们还是不要用nfs网络锁了. 用本地文件锁就好了.
通过修改nfs的挂载参数, 使其支持nolock参数, 这样就可以使用本地文件锁了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package main
import ( "context" "flag" "fmt" "log" "strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" )
func main() { kubeconfig := flag.String("kubeconfig", "/path/to/your/kubeconfig", "kubeconfig file") flag.Parse() config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { log.Fatalln("init Cluster Config failed ", err) }
clientset, err := kubernetes.NewForConfig(config) if err != nil { log.Fatalln("init Cluster Config failed ", err) }
pvList, _ := clientset.CoreV1().PersistentVolumes().List(context.TODO(), metav1.ListOptions{}) for _, pv := range pvList.Items { if pv.Spec.ClaimRef != nil && strings.Contains(pv.Spec.ClaimRef.Name, "consul") { if pv.Spec.MountOptions == nil { pv.Spec.MountOptions = []string{"nolock,tcp,noresvport", "vers=3"} _, err := clientset.CoreV1().PersistentVolumes().Update(context.TODO(), &pv, metav1.UpdateOptions{}) if err != nil { panic(err.Error()) } fmt.Printf("Updated PV: %s\n", pv.Name) } } } }
|