记录最近碰到的一次 confd 和 inotifywait 配合踩的坑
由来 版本快发版的时候,测试测出来经常部署应用阶段 sql 初始化服务偶现连接数据库 mysql 超时,导致没有创建库或者表,而业务功能不正常。然后临近发版测试人员上报了高风险。
过程 排查 复现非常麻烦,不是一直超时是偶先。没办法询问了 sql 初始化业务的开发人员,和多个环境得到以下对比信息:
最近无改动,初始化服务回退到上个版本依旧
每次超时是不同的库,并不是固定的,慢 sql 看了没有
k8s/docker 部署均会发生
就在一筹莫展之际,另一个部门的测试人员找过来,他们部署后,浏览器上测试很多接口报错超时(主要是内部涉及到的调用链长),有了稳定复现的环境真好。上去排查了下,发现 ipset 的条目被频繁清空和创建导致的。
背景 我们 toB 和 toG 为了避免客户现场漏扫和安全相关,每台机器起了容器,利用 ipset 和 iptables 做白名单端口策略。相关规则为如下:
1 2 3 4 5 iptables -w -N BASE-RULE iptables -w -A BASE-RULE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT iptables -w -A BASE-RULE -m set ! --match-set whiteiplist src -m set --match-set whiteportlist dst -j DROP iptables -w -A BASE-RULE -j RETURN iptables -A INPUT -j BASE-RULE
默认行为是 DROP,也就是白名单实现。在每个节点机器都部署有 ipset 容器,内部 entrypoint.sh
内容大致如下:
1 2 3 4 5 6 exec inotifywait -mrq \ --timefmt '%d/%m/%y/%H:%M' \ --format '%T %w %f' \ -e modify,delete,close_write,move /data/kube/ | while read line;do bash /iptables.sh done
/iptables.sh
内部就是检查链和每次清空 ipset 条目和创建。之前的逻辑是 ansible 分发 rule 文件,上个版本被其他同事修改成使用 confd 从 redis 内读取更新到该文件:
1 2 3 4 5 6 7 8 [template] src = "ipsets-whiteiplist.tmpl" dest = "/root/kube/rule/whiteiplist.txt" keys = [ "/ipsets/whiteiplist" , "/ipsets/mark" , ]
根源 然后我在 ipset 容器内使用 inotifywait 观察了下发现一直产生临时文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ inotifywait -mrq --timefmt '%d/%m/%y/%H:%M' --format '%T %w %f' -e modify,delete,close_write,move /data/kube/ 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101 26/11/24/14:51 /data/kube/ .whiteportlist.txt3298417101
看这个文件名就是 confd 生成的对比临时文件,查看了下文档并没有发现有设置临时文件的 dir 相关参数,然后找到了相关逻辑:
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 42 43 func (t *TemplateResource) createStageFile() error { log.Debug("Using source template " + t.Src) if !util.IsFileExist(t.Src) { return errors.New("Missing template: " + t.Src) } log.Debug("Compiling source template " + t.Src) tmpl, err := template.New(filepath.Base(t.Src)).Funcs(t.funcMap).ParseFiles(t.Src) if err != nil { return fmt.Errorf("Unable to process template %s, %s" , t.Src, err) } temp, err := ioutil.TempFile(filepath.Dir(t.Dest), "." +filepath.Base(t.Dest)) if err != nil { return err } if err = tmpl.Execute(temp, nil ); err != nil { temp.Close() os.Remove(temp.Name()) return err } defer temp.Close() os.Chmod(temp.Name(), t.FileMode) os.Chown(temp.Name(), t.Uid, t.Gid) t.StageFile = temp return nil } func (t *TemplateResource) sync() error { staged := t.StageFile.Name() if t.keepStageFile { log.Info("Keeping staged file: " + staged) } else { defer os.Remove(staged) }
主要是这行 ioutil.TempFile(filepath.Dir(t.Dest), "."+filepath.Base(t.Dest))
相当于 目录下 + .文件名字 + TempFile随机后缀
,在下面的 sync 方法里对比后再删掉 stageFile。
从源码了解到并没有相关 StageFileDir 参数,没办法让同事把文件放其他地方, 在 reload_cmd cp 过去,大概类似这样:
1 2 3 4 5 6 7 8 9 [template] src = "ipsets-whiteiplist.tmpl" dest = "/root/kube/whiteiplist.txt" keys = [ "/ipsets/whiteiplist" , "/ipsets/mark" , ] check_cmd = "" reload_cmd = "cp -f /root/kube/whiteiplist.txt /root/kube/rule/whiteiplist.txt"
后续 重写了那个 while 内过滤掉.开头的文件,然后 ipset 清空逻辑使用 swap 方式。