Category Archives: 默认分类

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

免签过境韩国、坐船去日本手记

我总有一种不愿意走回头路的情结,虽然给自己找了许多麻烦,依然乐此不疲。 这一次,经@tigerWu 提醒,我打算从韩国坐船去日本,以期获得一个日本的“上陆许可”。虽说日语里上陆就是入境的意思,但在汉语看来,有明确的从下到上的方向指示,也就是说得从海里上来才算。 去韩国驻华大使馆网站看看,中国公民持日本旅游非团体签证可以免签证过境韩国并给予30天入境期,查验标准是下一段“机票”。但我想坐船啊!那会不会是翻译有问题呢?要知道韩国大使馆上的汉语文件基本上行文都狗屁不通。再去看看韩国出入境管理局的网站,其英文版写了“flight ticket”。 这么看来是没戏了?我不甘心,就给“国民申闻鼓”写信,等不及回答又给出入境管理局的immigration contact center打电话,第一次问仁川机场进、釜山海港出,被拒了。不过比较有意思的是这个呼叫中心并不是直接做出行政解释,而是客人报告了自己的行程计划之后,她们代为向入境口岸当局咨询。考虑到中国对外国人的72小时免签证政策限定外国人不能离开口岸城市,我就尝试咨询釜山机场进、釜山口岸出,获得了允许,遂立刻买票,但为时已晚,只买到了没有头等舱的一架飞机的商务舱。(插播八卦:东航卖大韩航空的票比大韩自己卖的便宜)与此同时,我之前向韩国信访局“国民申闻鼓”递交的信访“是否可以空进海出,没指定具体口岸咨询”也收到了回答,我还到处求爷爷告奶奶找人翻译,后来发现其实有汉语翻译版的回答,就是:不限定转乘空路或海路。我把这一页打印出来了。 2月4日,大韩KE830,办理登机牌遇到了麻烦,地服代理请示大韩航空工作人员,我出示联程船票和信访记录之后拿到登机牌;中国边检出境,按规定应该查验联程客票,但都没理我就直接把中国出境章盖在了日本签证旁边。 到釜山机场,iPad丢了,于是落了单,在后面跟机组一起,机组走专用通道,我走外国乘客通道,是一位大妈入境官。入境官翻了半天,我主动提醒我是visa free transit资格,然后出示票。该女入境指着班次号问:这是flight吗?我说是ship,她拿不准,就问韩国人通道那个男入境官,然后我就被提到那个通道受审去了。因为JR九州beetle航运公司的船票确认页只写“釜山发”没说去哪儿,男入境官不能确认这是正确的票,我又重申了我从Busan international ferry Terminal走,要去日本的Fukuoka,但他还是将信将疑,又问我是不是crew,我说不是之后,被拉出来去条凳上坐着(传说中的边检小黑屋?),入境现场指挥官,一位大妈过来问我,入境期间准备去哪儿玩,我说JSA(也就是板门店)她说板门店很远啊你知道吗,我说我做坐韩国高铁KTX,我有KR PASS,一种外国人专用的票。然后各位入境官扔下我不理,带着护照走了!!!这时船票在他们手里,信访函在我手里。过了一会儿,指挥官和男入境官把我带到入境通道,先盖章,然后手写了截止日期,又写了个不知道是TS还是TIS的字在入境章下面,这时还扣着,又说了我几句应该详细说明啊什么什么的,然后才用摄像头拍照,又说了我几句,最后主动和我握手,这事入境官的表情是很友好的。 2月8日,我提了行李到釜山火车站,经案内所工作人员指点,到站后去坐去往ferry的shuttle bus,但是没找到,只好打了个出租车去。一打吓一跳,原来google maps标的那个terminal现在已经不用了,新的在站后的左前方(东北),出租车到南边路口掉头从我到港口。巧的是,好像韩国人找零算法和美国人一样,出租车费2800、咖啡4800,我给整几千加三百,他们很难理解我是要一个500硬币找零。值得注意的是,港口的指示牌写boarding为“登机、搭乘”,写“便名”为“flight”,甚至有个指示牌写“快速船 登机口 flyer gate”我一直以为flight是飞机航班,而船班叫voyage呢!! 日本入境,边检极其顺利,其中问了我要去哪儿,我说福冈、仙台、东京,问我是飞过去吗,我说我有JR PASS,坐火车去,就给我贴了从海里上博多港陆地的“上陆许可”一枚,其实和上次大阪关西机场的只差下边的地名不一样。海关,又跟上次一样搜我的箱子,我长得有那么像走私犯吗?而且那个女关员还钓鱼执法,问我是不是住本地,我说不是啊我是中国人,然后就问那海关卡上那个地址是什么,我说那是hotel;然后又问我去哪些地方玩;最后甚至指着签证问这是什么,我说那是签证啊!!然后就过关了。他们是在考察什么? 结论:由于韩国出入境管理局各口岸部门执行政策不一致,目前这么走并不是一个很靠谱的方案,而且边检入境问话有时会有陷阱,比如问我是不是crew、问境内怎么交通之类的,稍有不慎就有可能被认为是有问题的。韩国边检把我扔在冷板凳好几分钟,谁知道是去打电话核实船票还是打电话请示政策去了…… 《中国合伙人》教导我们:真实、自信、具体、合理,是签证的基本思想基本哲学,我的行程是真实的,就不怕核查、我的行程是符合信访回信解释的,也应该能进去。 不过我觉得,我得以自己的真实经历,去督促船运公司把票写清楚,不要只写起点站。 2月8日,写于博多

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 参考资料:https://pythonhosted.org/setuptools/pkg_resources.html #!/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 … Continue reading

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

某种情况下PATH不生效的问题

今天遇到的情况,我同事在 virtualenv 里执行 gunicorn 运行 Django app,出来的 Django 错误信息却是 /usr/local/ 路径下面那套 Python 里的: 2015-09-18 15:32:47 [11538] [ERROR] Error handling request Traceback (most recent call last): File “/usr/local/lib/python2.7/dist-packages/gunicorn/workers/sync.py“, line 131, in handle_request respiter = self.wsgi(environ, resp.start_response) File “/usr/local/lib/python2.7/dist-packages/django/core/handlers/wsgi.py“, line 236, in … Continue reading

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