Category Archives: 默认分类

kube-proxy –proxy-mode=iptables 与 rp_filter 冲突

2015年12月16日,朱鹏安装了新版kubernetes master版本(比1.1新,为1.2alpha**),然后发现,访问 clusterIP:clusterPort 会发生无法连接的故障。 从集群内Node上访问 分别在Node rz-ep19和container里执行curl访问,发现container里curl可以成功访问,但Node上一般不行(偶尔成功,几率很低)。查看iptables规则,发现新版kube-proxy已经不再将请求REDIRECT到本机kube-proxy端口,而是: 先把PREROUTING、OUTPUT无条件指向KUBE-SERVICES链;如匹配不上KUBE-SERVICES链,则再尝试匹配发给docker0 在KUBE-SERVICES链里匹配clusterIP:clusterPort条件,然后发到KUBE-SVC-***链 在KUBE-SVC-***链中,用-m statistic –mode random –probability这样的条件将流量按等比例分给多个KUBE-SEP-***链 然后再在KUBE-SEP-***链中,将数据包DNAT给endpoint 为了减少干扰,我们缩减了kube-system/elasticsearch-logging 10.16.59.73:9200服务的规模,到只有一个endpoint 172.16.86.48:9200运行在rz-ep10上;rz-ep19 10.16.49.16作为运行curl的客户端 在rz-ep19的flannel接口上抓包,抓到了 10.16.49.16->172.17.86.48的TCP SYN,但没有收到回应。 在rz-ep10(endpoint Pod所在的Node)的flannel接口上抓包,抓到了和上述相同的包,也没有收到回应。 在rz-ep10的docker0接口上抓包,没有抓到 由此判断,rz-ep10的内核在转发时主动丢弃掉了 10.16.49.16->172.17.86.48的SYN,以至于无法建立TCP连接。查看/proc/sys/net/ipv4/conf/{all,flannel.7890}/rp_filter,发现flannel.7890/rp_filter内容为1,即在此网卡上执行“根据回溯路由检查数据包是否为伪造”的检查。因为源IP 10.16.49.16在rz-ep10看来理应出现自eth0而非flannel.7890接口,所以被判定为假造包,丢弃。 将此参数改为0,再去docker0上抓包,可以收到172.17.86.48发回10.16.49.16 SYNACK包;但rz-ep19上curl仍显示无法建立连接。 在rz-ep10的角度考虑,这个172.17.86.48->10.16.49.16的包应该从eth0发出,也就是在rz-ep19的eth0上收到。而在rz-ep19的角度考虑,源IP 172.16.86.48不应来自eth0,也会被rp_filter参数影响,丢弃掉,所以无法建立连接。把rz-ep19的eth0/rp_filter参数改为0,终于可以正常访问了。     从集群外访问 从办公区我的笔记本电脑 172.30.26.169 访问 10.16.59.67:80 服务,该虚IP被手工绑在rz-ep01上,ping可以通,但访问不通。 … Continue reading

Posted in 默认分类 | Tagged | Leave a comment

Kubernetes内的网络通信问题

首先复习一下Kubernetes内的对象类型 Node:运行kubelet(古代叫minion)的计算机 Pod:最小调度单位,包含一个pause容器、至少一个运行应用的容器 RC:复本控制器,用于保持同类Pod的并行运行的数量 Svc:暴露服务的可访问通信接口   对象之间的通信关系 客户端 服务器 访问方式 master kubelet Node的10250/TCP端口。Node报到后,Master得知Node的IP地址 kubelet & kube_proxy apiserver master:8080/TCP HTTP 配置文件写明master地址 kubectl命令行 apiserver localhost:8080/TCP或命令行参数指定 other ALL apiserver ClusterIP_of_kubernetes:443/TCP HTTPS 上述IP和端口号通过环境变量通知到容器 需要出示身份信息 kubernetes是一个ClusterIP模式的Service。参见下面详述 pod pod 跨Node的多个pod相互通信,需要通过overlay network,下面详述 ALL service 三种模式,下面详述   overlay … Continue reading

Posted in 默认分类 | Tagged | Leave a comment

Docker in docker的一些故障检查过程

术语约定: Host:外层运行操作系统的机器 外层daemon:Host上的docker daemon 外层容器:外层daemon下辖的container,镜像启动时加–privileged参数。这个镜像的准备步骤是从docker下载当前1.9.1版安装(并固化到镜像里)CMD是一个脚本,先启动带debug选项的docker daemon 并放后台运行,然后pull并运行centos:7 一次,最后开启一个不停ping的命令,保持容器持续运行。通过docker exec 进入另行执行docker run命令测试内层是否可以正常启动 内层daemon:外层容器里的docker daemon 内层容器:内层daemon下辖的container   宋传义最近几周在尝试docker in docker,报告过几个问题,我在这里简要记录一下。因为在此docker in docker研究过程中我只是顾问的身份,并非主研人员,所以记述内容难免有缺乏背景介绍、阶段靠后等问题。宋传义报告的大量现象都是“最后一句错误信息”,但我的工作方式是从“第一条错误信息开始看”。 启动内层docker daemon时报告缺cgroup mount 宋传义报告在1.9上可以成功的在外层容器里运行内层的docker daemon,但1.7的报告缺cgroup mount。检查发现,Docker 1.7 并不会给内层容器 mount /sys/fs/cgroup/* 目录。只需要手工补mount即可混过去,满足启动docker daemon的需求。 在Docker 1.8.0的changelog里 Runtime 小节记述了这个变动: Add cgroup bind mount … Continue reading

Posted in 默认分类 | Tagged , | Leave a comment

Sentry新版SSO Provider讲解

从GH-1372 issue完成时开始,Sentry 7.x转向使用自家定义的SSO Provider,逐渐抛弃django-social-auth结构。因为缺乏文档,我在此事上消耗了大量的时间。现在写这篇Wiki用于记录: 基类:sentry/auth/provider.py 中的 Provider 虚基类 样例:sentry/auth/providers/ 目录下的dummy.py和oauth2.py 两个,以及sentry-sso-google 基于Sentry自带的OAuth2Provider   基本执行流程: 通过django机制加载app,在初始化时调用sentry.auth.manager的register方法,注册自己的名字和class 管理员在 Organization 的Auth页开启SSO时,会生成上述类的一个实例。Sentry的helper.py负责此流程,先调用get_auth_pipeline()函数,取得一个成员为多个 AuthView的超类 的list,然后对着这个list依次执行成员的 dispatch()函数 每个步骤按照pipeline的顺序执行一个AuthView.dispatch(self,request,helper)函数,其中request可以读到当前的django request内容。因为URL都为/auth/sso/ 所以需要根据querystring来判断执行到哪一步了,并收集本步的信息,调用 helper.bind_state() 保存起来,然后return help.next_step()跳到下一步执行 最后build_identify()会被执行,需要返回一个至少包含id、email、name的dict,此dict被Sentry用来构造用户信息。

Posted in 默认分类 | Tagged , | Leave a comment

sentry-公司内IM插件

这插件被大家催了很久了,到1月底终于决定动手做。 准备知识 公众号Pub服务API(内网wiki,链接已删除) 签名请求规范(内网wiki,链接已删除) New Forms of Authentication 插件组成 setup.py 安装脚本。插件注册发现机制请参见 sentry_IM/__init__.py 确保这个目录是python package的文件。顺便查询一下VERSION供引用 sentry_IM/requests_APISign.py 上述New Forms of Authentication文档里提到的requests auth class,提供公司的Authencation验证header sentry_IM/send_IM.py 发消息用的Class sentry_IM/plugin.py 插件本身 程序讲解 插件本身继承自sentry.plugins.bases.notify.NotificationPlugin类,需要实现其notify(self, notification)方法。因基类里notify()调用了notify_users(),所以pylint会有提示。 参数notification为一个对象,可以从中取得event,然后从event取得所属的group以及group所属的project。根据project可以取得要通知的人员列表。(这里抄袭了MailPlugin的发送列表)因为这个函数得到的人员列表是Sentry里的User id,所以还需要再根据id查询到User,然后从其Email字段中取得localpart 最后,根据上述project.name、event.message、group.get_absolute_url() 组装其通知消息文本(URL在IM客户端渲染时才变成超链接,我这里只发URL本身),生成send_IM.py 的实例,对着上述人员列表发送消息文本。 IM插件所需的四个参数,在总的配置文件中列出,由插件生成send_IM实例时传递,然后由其传递给requests_APISign的实例,完成对HTTP请求的签名 配置是否开启 本来是想像MailPlugin那样直接在Account/Notification页做个列表供勾选。但阅读代码发现,这个功能并不是MailPlugin自己的功能,而是sentry做了特殊处理,先获得用户订阅的projects_list,然后循环生成ProjectEmailOptionsForm的list,随后再执行从各插件获取的ext_forms[]配置表单。ext_forms里每一个插件配置表单的初始化参数只有plugin、user和prefix,要想表达“所有可订阅的projects”这个概念会相当啰嗦。因此,最终确定为:表单里只勾选per-User 级别是否开启;而用户是否属于具体某个event/project的发送目标,则直接共用MailPlugin那边的选择,然后再用上述per-User级别开关做一遍过滤。这里两个插件之间略有粘连,主要是由于User-Project之间只有单向对应关系,逆向查询太罗嗦导致的。 最终发送效果 Github地址 https://github.com/julyclyde/sentry-IM

Posted in 默认分类 | Tagged , | Leave a comment

Sentry 新版插件基类讲解

根据 sentry/plugins/bases/__init__.py 的说明,新版插件分类四类,其中常见的: NotificationPlugin 比如sentry_mail、sentry_webhook插件 NotificationPlugin的基类定义参见 sentry/plugins/bases/notify.py

Posted in 默认分类 | Tagged , | Leave a comment

Sentry整理杂记

本讨论均基于Sentry 7.7版本 插件机制 自带插件 src/sentry/plugins/ 每插件一个目录 自带插件loader:src/sentry/conf/server.py 里的INSTALLED_APPS tuple 外装插件loader:utils/runner.py 里的 install_plugins()函数,对iter_entry_points()遍历并将其加入INSTALLED_APPS tuple中 外装插件注册:插件的setup.py里执行注册entry_points的过程。例https://github.com/getsentry/sentry-groveio/blob/master/setup.py 参考setuptools:pkg_resources 参考资料:https://pythonhosted.org/setuptools/pkg_resources.html 列出本Python安装的Sentry插件 #!/usr/bin/env python import pkg_resources for ep in pkg_resources.iter_entry_points(‘sentry.apps’):     print str(ep) for ep in pkg_resources.iter_entry_points(‘sentry.plugins’):     print str(ep)   sentry-jira插件 插件基本配置 每Project分别配置,需要输入jira的instance URL、用户名和密码。以上内容base64存在数据库sentry_projectoptions表里 where `key` … Continue reading

Posted in 默认分类 | Tagged , | 1 Comment

SSO跳回sentry失败的解决方法

从某月开始,公司SSO回传信息改用POST方式,放弃了之前的通过querystring传递的做法。具体到Sentry这里,因为 /auth/sso 页面受CSRF保护,拒绝接受POST回来的不含CSRF token的数据,从而无法登录。 解决这个问题,可能有三种方法吧: 让SSO回传csrf token:前提是得先把csrf token发给SSO,且SSO方愿意配合修改。考虑到现在跳转到SSO去是直接302的,CSRF如果放在querystring上,其实和他们回传时把token放在querystring上风险相当了,所以这做法不行 Sentry/auth/sso不验证CSRF:/auth/sso 对应AuthProviderLoginView,该View继承自BaseView,而BaseView的dispatch()方法是@method_decorator(csrf_protect)过的,因为还有其它很多View都这么继承,想改基类还怕有别的风险。想在配置文件里monkey patch掉AuthProviderLoginView,但是import sentry.auth.helper的时候就失败了 Sentry整体不验证CSRF:试了试,csrf view middleware可以去掉,但是,csrf_protect decorator依然生效。关于此处,django文档说的不太精确。该middleware除了负责种cookie,还负责验证,但实际上验证工作并不是以middleware的身份来做的,而是以decorator的身份来做的。单单禁止middleware的加载只是去掉了种cookie的行为而已。网上也有其他人遭遇过这个问题 http://www.douban.com/group/topic/11555679/ 最后,参照3里帖子的做法,做了一个django middleware插在csrf view middleware前面,如当前请求URI为/auth/sso/ 就取消CSRF保护。

Posted in 默认分类 | Tagged , | Leave a comment

记一次Sentry的性能调优过程

大概从6月底开始,我们的Sentry(错误日志收集、聚合和报警系统 http://getsentry.com )遭遇了性能问题,每分钟只能处理200个事件了,经常有20多万待处理的任务积压在events队列里,延迟超过一小时,我不得不丢弃这些任务,清空队列,以促进时效。重启一下celery worker会有瞬间的改善,但很快就又不行了,似乎worker的性能会衰减。为此,我给celery worker增加了–time-limit参数,使worker不会在执行不下去的时候无限等待,而是超时出错退出,迎接新的任务,情况略有改善。 因为Sentry的结构是前端web-消息队列-后端celery这样的结构,给调试也造成了一些困难。 我先开启了celery worker的DEBUG级别日志,从中搜索包含succeed的行,然后把其中UUID给过滤掉,只保留任务名字和耗时,整理后得到任务和耗时的对应关系,发现save_event这个任务的耗时很有意思,经常是6秒的整倍数。 然后去这个任务里添加时间打点代码,发现在它调用的EventManager.save()函数里,带事务执行的三次数据库插入(调用_save_aggregate、创建EventMapping、保存Event)的耗时,不是零点几秒,就是六秒的整倍数,而完全没有2、3秒、7、8秒这种情况。于是检查的方向就被引导到了数据库方面。折腾了DBA同事几天,删了几个索引和外键约束,也没什么改善。 想到之前为了Sentry的下一个版本v8准备过一套PostgreSQL数据库用于测试,我就拿过来,另外建了一套Sentry 7的部署,在上面运行,没想到性能极其丝滑……事情似乎越来越明了,就是数据库的问题。不过当我停止使用用于测试的128G内存真机PostgreSQL,而自己申请了一个和当前MySQL一样配置的16G机器的时候,发现低配置时pgsql的性能好像还不如MySQL……不过就在我从真实环境往测试环境export/import数据的时候,发现了Sentry在执行import时,如果测试环境共用生产系统的Redis,就会速度极慢,几乎卡死,用strace上去看一下发现慢在Redis操作上,开销在数据库上的时间极少;而如果用专用的一套Redis就速度极快。 于是我的注意力又转向了Redis。尝试FLUSHDB,发现性能立刻恢复了每分钟4000条、峰值8000条的处理能力,但redis的内存碎片率上涨了不少。看了看SLOWLOG,发现有一条几乎占满了整个SLOWLOG排行榜的命令 ZRANGE b:p 0 -1 手工试了试,这条命令得好几秒种才能返回,一次就能返回数据好几万条。搜了一下源代码,在sentry/buffer/redis.py文件发现了b:p这个key名字。原来,这是把高速更新的计数器的多次更新合并起来,减轻数据库压力的一个组件。但这个组件本身因为把sorted set用到了极限,所以性能不佳。考虑到不能再让碎片率上涨,我只好偶尔手工删除这个key,放弃一些计数器更新操作:放弃更新计数器总比放弃整个任务看起来要好些吧。 去软件原厂求助(https://github.com/getsentry/sentry/issues/3870),作者说我该上供啦,不要自己架服务啦,还是买原厂服务吧…… 现在,我更改了worker的配置,把这个任务单独拉出来,用六台worker伺候着,终于基本解决了问题。   顺便说一下我在Redis 2.8.7上遇到的极限值: sorted set的长度在100万左右就会性能崩溃。如果有依赖这个特性的,请大家注意有可能会引起整体系统的性能雪崩。

Posted in 默认分类 | Tagged , | 3 Comments

etcd的discovery URL是怎么回事

今天玩 CoreOS,得知他们提供了一个 etcd discovery service https://discovery.etcd.io/new?size=初始数量 当足够数量的etcd按照这个URL相互发现之后,就能成功组成集群。 问题是: 这个数量是怎么被各个etcd知道的呢?搜了一些文章,也谈到了自建 discovery service的方法,但URL似乎都是/v2/keys/random_ID/_config/size 这样的,而不像 discovery.etcd.io 这样是 /random_ID 。不过后来试了试访问 https://discovery.etcd.io/random_ID/_config/size 发现能得到那个值,就明白了,这个网址其实有rewrite,把 /random_ID 给proxy_pass到后面etcd的/v2/keys/random_ID 了,让这个URL看起来更友好,但却更迷惑了。 参考资料:https://github.com/coreos/etcd/blob/master/discovery/discovery.go#L214-L217

Posted in 默认分类 | Tagged | Leave a comment