跟随,学习,进步

详解 Kubernetes Job 和 CronJob 的实现原理

2019-03-05 01:28:00 Draveness

之前介绍了 Kubernetes 中用于长期提供服务的 ReplicaSet、Deployment、StatefulSet 和 DaemonSet 等资源,但是作为一个容器编排引擎,任务和定时任务的支持是一个必须要支持的功能。


详解 Kubernetes DaemonSet 的实现原理

2019-03-01 14:30:00 Draveness

Deployment 是 Kubernetes 中用于处理无状态服务的资源,而 StatefulSet 是用于支持有状态服务的资源,这两种不同的资源从状态的角度对服务进行了划分,而 DaemonSet 从不同的维度解决了集群中的问题 — 如何同时在集群中的所有节点上提供基础服务和守护进程。


print不是函数

2019-03-01 09:59:47 Laruence

这个源自于一个看似很诡异的问题: if (print("1\n") && print("2\n") && print("3\n") && print("4\n")) { ; } 你期待这段代码输出什么呢?

tags: PHP应用,随笔,if,language constructs,PHP,print


每周分享第 45 期

2019-02-28 16:08:18 阮一峰

这里记录过去一周,我看到的值得分享的东西,每周五发布。 欢迎投稿,或推荐你自己的项目,请前往 GitHub 的 ruanyf/weekly 提交 issue。 美国科幻小说大师阿西莫夫,写过一本回忆录《人生舞台》。他在里面提到,成名后,他的收入一年比一年高,完稿时的1990年是收入最高的一年。 很不幸,1992年他就去世了,享年72岁。据他的妻子说,那一年的收入比1990年还要高。也就是说,去世时,他达到了一生最高的年收入。我当时心想,这种收入模式真是太好了。老了以后,收入反而更多了,根本不用为养老发愁。 大多数人的收入模式恰恰相反:工作时达到收入的高峰,退休停止工作以后,收入就逐渐减少,人到老年,手头越来越紧。所以,大多数人为了养老,年轻时必须足够储蓄。要是年轻就背负了很多房贷和卡债,老了以后,往往会陷入困境。 为什么阿西莫夫老了还有大量收入,其他大多数人就没有呢?原因是普通人的收入,来自出卖自己的时间,老了不工作,自然就没收入了。但是,阿西莫夫的收入来自于他的书,这些著作一再重版,为他带来了一年比一年多的版税收入。再加上,他勤奋写作,每年都有新书问世,自然就收入高了。 这件事的启示就是,如果退休以后,还想有稳定的收入保障,最好的方式就是你必须拥有资产。在阿西莫夫的例子里,就是他拥有版权,版权就是一种产生收入的资产。其他类型的资产包括房产、专利、股权等等。总之,年轻时就必须明确,你的工作目标不完全是高收入,更重要的是必须积累资产。 新闻 1、密码只有创始人知道 加拿大加密货币交易所 QuadrigaCX 的创始人,30岁的杰拉德·科滕(Gerald Cotten),由于意外于2018年12月在印度突然去世。公司钱包的密钥只有他一个人知道。 现在公司无法打开钱包,大约1.9亿美元的客户资产无法转出,这意味客户的加密货币都会损失掉。 2、无人船只远航夏威夷 美国海军的研究部门2016年建造了一艘无人船只 Trimaran,长40米,没有任何船员,可以执行多种任务。最近,该船成功完成了加州圣地亚哥到夏威夷珍珠港的往返航行,跨越了半个太平洋。 无人船只的建造成本仅为载人船只的一小部分。这次航行帮助美国海军评估这项技术,下一步将从实验阶段转向高级任务测试。 3、快乐广场 快乐广场(Happy Place)是美国一家新成立的展览公司,在北美各大城市轮流布展,每个城市大约停留3个月左右。它的展览没有主题,就是提供布置得非常梦幻和美丽的场景,供参观者拍照,然后发到社交媒体上面。每人的门票价格是30美元左右,每个场景每次有45秒的独占拍照时间,据说相当受欢迎。 4、外星人飞船 Avi Loeb 是哈佛大学天文系主任。2017年底,夏威夷的天文学家偶然发现第一个已知的星际物体 Oumuamua,正在快速地穿越太阳系。由于移动速度太快,而且与太阳引力的方向相反,它只可能来自另一个星系。 其他天文学家都认为,它可能是数亿年前恒星爆发产生的小行星,或是冰彗星。但是,Avi Loeb 公开发表论文,提出它可能是外星人的飞船,因为它的运动轨迹,好像是有什么东西从后面推动它。如果是被太阳风吹动,就意味着它非常薄,不超过1毫米厚,好比一块长度为一公里的雪花,这几乎是不可能的。"虽然不能肯定绝对是外星人,但除了外星人,没法想出别的解释。" 这个观点受到广泛的质疑和攻击,但是 Avi Loeb 坚持自己的观点,并且到处宣传。谈到科学界认为他在胡说八道时,他说:"最糟糕的结果是我被免职,这将使我有更多的时间专注于科学。我的所有头衔,我都可以放弃。事实上,我可以回到自己的农场过日子。" 5、马桶座圈监控健康 人体健康的有些指标,需要随时随地的持续监控,定期记录健康信息。现有的设备都做不到这一点,比如智能手表能够搜集的健康信息,就非常有限。 纽约罗切斯特理工学院 (RIT)的一个团队,提出了一种新的方法:马桶座圈监控患者的心脏健康。患者坐上马桶的时候,马桶座里面的传感器就会跟踪血压、血氧水平和其他心脏数据,然后自动上传到服务器。如果程序发现患者的心脏健康状况正在恶化,就会提示要去看医生, 6、AR 眼镜 2019年的消费电子展 CES 传出消息,Facebook 和微软都提交了 AR 眼镜专利文件,朝着 AR 眼镜更小更好的方向努力。这两种眼镜都带有小型投影设备,可以在镜片上叠加图像,使得用户可以在当前场景上,看到一些文字信息,包括电子邮件、通知,以及其他的现实增强功能。 7、Safari 废除 Do not track 功能 当代浏览器都有一个 Do not track (不要追踪)的选项。一旦选中,浏览器会向网站发出一个请求,告诉网站不要追踪用户。但是,没有网站认真对待这个请求,而是继续追踪用户,因此苹果公司决定,最新的 Safari 浏览器将去除这个功能,另一方面,Safari 内置了智能阻止 Cookie 的功能,实际上使得 Do not track 变得多余了。 8、深度学习选衣服 有时,你出门前会花很多时间思考,今天到底穿什么衣服。美国一家创业公司正试图使用深度学习,解决这个问题,让计算机帮你选择穿什么衣服。它的思路是,从 Pinterest 里面上传的图片,找出目前的流行时尚,然后根据你的现有服装、以及出门的场合等因素,推荐搭配。这个系统的下一步目标,是为每个人设计最适合他/她的服装。 9、GitHub 年度报告 GitHub 发布年度报告,显示用户国别的前三位分别是美国、中国、印度。 比较厉害的是加拿大,只有3千万人口,可以排到第六位。 编程语言排行榜(根据 GitHub 仓库数量统计)中,最值得注意的是 TypeScript 从去年的第十名,跃升为第七名。另外,有点意外的是,PHP 其实一直非常流行,毫无衰退迹象。 10、一句话新闻 Spotify 如果发现免费用户使用了广告拦截器,现在会终止对该用户提供服务。你不看广告,就无法享受免费服务,除非付费。 加州宣布考虑取消洛杉矶到旧金山的高铁计划,原因是成本不断上升,完工遥遥无期。 澳大利亚法官判决,不得开采一个煤矿,因为是煤炭会释放温室气体。 Chrome OS 73 新增了 PDF 手动注释功能,以后应该也会加到 Chrome 浏览器。 关哥说险 我们这份周刊主要针对 IT 开发者和爱好者,大家都对风险比较关注,因此对保险产品有较高的需求,但是相对缺乏保险知识。有鉴于此,保险公众号 《关哥说险》 联系我,希望在周刊上推广一下自己。 该公众号的作者"关哥",其实是一个复旦毕业的东北妹子,在大陆和香港两地已经有十余年的银行和保险从业经验,从事过不少岗位,亲历过大量案例。她结合自己的经历,在公众号上向普通读者介绍,应该如何购买保险产品,以及购买时的注意事项。 比如,很多人不知道"犹豫期"和"等待期"是什么。简单说,"犹豫期"是你购买保险后可以反悔的时间,通常是签订合同后的10~15天之内。 "等待期"则是保险公司可以免赔的时间,通常是3个月到6个月。比如,购买了医疗险以后的第二个月,你就发病了,这种情况保险公司是不陪的,理由是购买保单时,你可能隐瞒了病情。 下面是她的一些保险箴言。 保险的本质是风险管理,你都不肯承认风险的存在,你怎么可能去做风险管理呢? 保险是射幸合同,本质就是不确定性、偶然性。 任何一种理财产品,都要从安全性、收益性、灵活性这三点来评估。 先确定自己担心的风险,再确定购买的种类,最后才是选产品。 不要用获利思维去评价一个止损工具。 《关哥说险》的文章目录可以看这里,里面有整理好的系列文章。对保险有兴趣的朋友,欢迎微信扫描下面的二维码订阅。 教程 1、Finally 代码块的解释(英文) 随着 JS 引入 async/await ,开发者不可避免地会更多使用 try/catch/finally ,本文解释 finally 代码块的一些容易混淆的点。 2、一段混淆过的 JS 脚本剖析(英文) 作者收到一封诈骗 Email,比较特别的是,它不是引诱用户点击链接,而是提供了一个网页附件,让用户打开,里面是一段混淆过的 JS 脚本。 3、apt 与 apt-get 的差异(英文) Debian 系统安装软件包有 apt 和 apt-get 两种命令,本文介绍了它们的异同。 4、TensorFlow.js 介绍(英文 PDF) 本文描述了TensorFlow.js 的设计、API 和实现,并重点介绍了一些使用实例。 5、编程语言都有中央包存储库吗?(英文) 许多语言都有中央软件包存储库,比如 npm、PyPI 和 CRAN。但是,每种编程语言都有这些吗?答案是只有39种语言有。 6、你应该记住的 DNS 地址(英文) 作者介绍了几个常用的 DNS 服务器地址,简单谈了它们之间的差异。 7、磁极的改变意味着什么?(英文) 最近,地球磁极的 N 极突然加速改变,这篇《国家地理》的报道详细介绍了背景知识。 8、Jsonnet 比 Yaml 更适合配置文件(英文) Jsonnet 是谷歌推出的 JSON 配置文件生成工具,基本上解决了 JSON 格式的所有痛点,比 Yaml 格式更优越。 9、为什么我们从 Go 转向 PHP?(英文) 作者谈了 PHP 过去三年的改进,以及为什么更适合架设他们的网站。 10、文件系统的过去,现在和未来(英文) 文件系统的历史回顾,介绍各种文件系列的来历和特点。 工具 1、FP-Glossary 一个 Chrome 浏览器插件,每次打开一个空白页,会显示一个函数式编程的概念。 2、香港地铁站的颜色 一个 CSS 颜色库,使用每一个香港地铁站的颜色命名 CSS 变量。 3、rbx 基于 CSS 框架 Bluma 的 React 组件库。 4、pagedraw 一个 UI 原型设计的桌面软件,可以输出 JSX 代码。 5、B4X Basic 语言开发工具,支持几乎所有平台,源码会被转译为对应平台的代码,比如Java、Objective-C、JavaScript。(@xulihang 投稿) 6、colorSpace 一个网页工具,可以去除图像里面的用户指定的颜色,对去除背景很有用。 7、archivarix 该工具可以用来从 Achive.org 的 Wayback Machine 里面,下载某个网站在指定日期的所有网页。 8、ExplainShell.com Bash 命令的可视化解释工具。遇到复杂的 Bash 命令,可以输入到这个网站,查看该命令的解释。(@weineel 投稿) 9、Project Showcase 该工具根据你的 GitHub 个人项目,自动生成一张个人介绍页面。(@lanffy 投稿) 10、rrweb 一个可以录制网页操作的 JS 库,不是录制成视频,而是将用户的每一个操作,录制成可复现的脚本。(@rolitter 投稿) 11、gitsome 一个 Git / GitHub 的命令行客户端,提供强大的自动补全功能。(@ChungZH 投稿) 资源 1、线性代数 美国本科生的线性代数教材,免费下载。 2、计算机科学的自学方案 本文对于计算机科学各门课程的自学,给出了一个完整的方案。 3、科技资讯的聚合网站 该网页聚合多个英文科技咨询网站的消息,一个地方就能看到所有资讯。(@shouldsimple 投稿) 4、Vim Adventure 通过游戏学习 Vim,看上去比较有趣。(@lenkenlau 投稿) 5、LeetCode Animation 使用动画的形式呈现解 LeetCode 题目的思路。(@nivance 投稿) 6、前端开发精选工具库 收集前端开发各方面的工具。(@xiaohesong 投稿) 7、笨办法学 Vimscript 开源的中级 Vim 教程,将 Vimscript 作为一门编程语言,帮助读者掌握。(@douchuan 投稿) 8、public-apis 这个仓库收集免费的 API,已经有100多个了。 9、Last-Statement-of-Death-Row 美国得州的政府网站,有该州死刑犯的遗言数据库。周刊读者编写了 Python 脚本,去抓取所有遗言,输出 CSV 文件。(@wansho 投稿) 文摘 1、通过拥有东西致富 小时候,我对于经济的最大误解是,人们通过高工资致富。实际上,虽然有一些例外(比如娱乐明星),高工资并不足以让你发财,福布斯富豪榜上,几乎没有人是领工资的。 真正富裕起来的人,都是通过拥有某种价值迅速增长的东西而致富。 这种价值迅速增长的东西,可以是股权、房地产、自然资源、知识产权或其他类似的东西。你需要拥有一些这样的东西来获取收入,而不能仅仅依靠出卖自己的时间换取收入,因为时间是一种线性资源。想要要多的收入,只能出卖更多的时间,这对你不利。 使你的东西的价值迅速增长,最好方法就是让大量的人想要你的东西。 2、新武器对第一次世界大战的影响 以下摘自斯科特·安德森的传记《阿拉伯的劳伦斯》。 1914年,第一次世界大战刚刚爆发的时候,大多数人都预测,这将是一场非常短暂的战争,因为19世纪的战争都很短暂。 但是,绝大多数人都忽略了一个关键的细节:过去的40年中,武器已经发生了翻天覆地的变化,关于武器的老观念都已经过时了。机枪、长射程炮弹、带刺铁丝网,这些新武器都已经诞生了。就是因为这个疏忽,这场战争将变成与大多数人的预期大相径庭的大屠杀。 欧洲列强之所以会疏忽,一个原因是,此前这些新式武器,几乎完全用来对付没有这些新式武器的人,特别是那些试图抵抗帝国主义侵略的非欧洲人。在这些情况下,新式武器使得欧洲人可以对落后民族实施一边倒的大屠杀,这是欧洲各个殖民帝国能够在19世纪下半叶在亚非迅速扩张的最重要原因。那些列强自身,还从来没有遭受这些新武器带来的伤害。 英国陆军大臣基钦纳勋爵,是少数对这场战争的残酷性有正确估计的人。他多次执行过这种一边倒的大屠杀,1898年在苏丹的恩图曼战役中,基钦纳用马克沁重机枪对付挥舞长矛冲锋的骑兵;仅仅一个上午的时间,英军就消灭了1万名敌人,己方仅有47名士兵阵亡。但如果敌人也有马克沁重机枪,会发生什么情况?基钦纳心知肚明。8月7日的内阁会议上,有些大臣认为战争只会持续几个月,甚至几周。他却预测说,战争会持续几年。他告诉同僚们:"到我国只剩最后100万人时,战争才会结束。" 这种话自然很少有人愿意去听,更不会有人去注意了。 随后的四年,欧洲变为一个屠场,约1000万军人和约600万平民死于这场战争。 本周图片 1、联邦调查局的衣物鉴定 很多时候,犯罪现场的线索就是一些监视录像。美国联邦调查局开发出了一套方法,判断某件衣物是否为监视录像里面的衣物。 1996年,华盛顿发生了一件银行抢劫案。警方后来抓了几个犯罪嫌疑人,从他们的衣橱里面一共搜到了27条牛仔裤。FBI 后来发表了一篇论文,介绍他们怎么根据监视录像,从27条牛仔裤里面找出犯罪时穿的那一条。 另一张照片是找出犯罪分子穿的那件格子衬衫。 2、乐器博物馆 意大利克雷莫纳,历史上是一个著名的小提琴制作城市。著名的斯特拉迪瓦里小提琴,就是在那里制作的。下图是当地的小提琴博物馆。 3、Windows 98 的图标 Windows 98 的图标非常经典,有坚实的边缘,柔和的色彩和易于识别的符号。 新奇 1、此人不存在 访问该网站会随机显示一张人像照片。这些人像都是 AI 生成的,并不是真实的人。 本周金句 1、 你不应该进入那种行业,做了两年的人可以和那些已经做了二十年的人,具有一样的工作效率。 -- 《如何成功》 2、 你的目标应该是,为你的现状 ---- 财产、销售额、影响力等等----添加一个零。我总希望,我的下一个工作,将使得职业生涯的其余部分,看起来只是这个工作的一个脚注。 -- 《如何成功》 3、 自信很重要。我认识的最成功的人,几乎都自信到妄想的地步。这是因为如果你不相信自己,就很难产生跟别人不一样的想法,而逆向的想法恰恰是创造大多数成功的地方。 -- 《如何成功》 4、 如果人体可以进行光合作用,那么合成的葡萄糖,只能满足我们所需能量的1%。如果要满足每天所需的约700克葡萄糖,我们需要有更大的皮肤,准确说大约160平方米左右,约为一个网球场的大小。 --《为什么人类不能进行光合作用》 5、 大多数人都高估了他们一天能做的事情,但低估了他们一年能做的事情。 -- 《关于"我没有足够的时间"》 欢迎订阅 这个专栏每周五发布,同步更新在我的个人网站、微信公众号和语雀。 微信搜索"阮一峰的网络日志"或者扫描二维码,即可订阅。 (完) 文档信息 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证) 发表日期: 2019年3月 1日


每周分享第 45 期

2019-02-28 16:08:18 阮一峰

这里记录过去一周,我看到的值得分享的东西,每周五发布。...


#DevOps的前世今生# 2. Dev和Ops矛盾缘何而来 ?

2019-02-28 01:53:42 李强

通过追溯 DevOps 活动产生的历史起源,我们发现了 DevOps 是敏捷思想从软件开发端(Dev)到系统维护端(Ops)的延伸。无论是 DevOpsDays 的创始人 Patrick Debois,还是同时期的 The Agile Admin。都想通过敏捷来改进传统的系统维护工作以及软件开发部门和系统维护部门的合作关系。但是,DevOps 的矛盾从何而来?这还要从 Dev 和 Ops 的起源开始讲起。

tags: DevOps


详解 Kubernetes StatefulSet 实现原理

2019-02-28 01:31:00 Draveness

在 Kubernetes 的世界中,ReplicaSet 和 Deployment 主要用于处理无状态的服务,无状态服务的需求往往非常简单并且轻量,每一个无状态节点存储的数据在重启之后就会被删除,虽然这种服务虽然常见,但是我们仍然需要有状态的服务来实现一些特殊的需求,StatefulSet 就是 Kubernetes 为了运行有状态服务引入的资源,例如 Zookeeper、Kafka 等。


#DevOps的前世今生# 1. DevOps编年史

2019-02-27 10:01:44 李强

Patrick意识到开发团队和运维团队的工作方式和思维方式有巨大的差异:开发团队和运维团队生活在两个不同的世界,而彼此又坚守着各自的利益,所以在这两者之间工作到处都是冲突。作为一个敏捷的簇拥者,他渐渐的明白如何在这种状况下改进自己的工作。

tags: 软件工程


谈谈我的“三观”

2019-02-26 08:02:07 陈皓

也许是人到了四十多了,敢写这么大的命题,我也醉了,不过,我还是想把我的想法记录下来,算是对我思考的一个snapshot,给未来的我看看,要么被未来的我打脸,要么... Read More Read More

tags: 杂项资源,Programmer,程序员


Istio知识图谱 v0.1 发布及 Istio Handbook 社区图书启动孵化

2019-02-26 06:09:04 Jimmy Song

2019年2月15日晚,我在朋友圈里发起了 Istio 知识图谱项目。 而后获得 ServiceMesher 社区成员的热烈响应,在此后的一周内陆续有151参与进来。 经过10天的孵化


详解 Kubernetes Deployment 的实现原理

2019-02-24 13:55:00 Draveness

如果你在生产环境中使用过 Kubernetes,那么相信你对 Deployment 一定不会陌生,Deployment 提供了一种对 Pod 和 ReplicaSet 的管理方式,每一个 Deployment 都对应集群中的一次部署,是非常常见的 Kubernetes 对象。


每周分享第 44 期

2019-02-22 02:26:40 阮一峰

这里记录过去一周,我看到的值得分享的东西,每周五发布。...


Go 语言数组和切片的原理

2019-02-20 11:51:00 Draveness

数组和切片是 Go 语言中常见的数据结构,很多刚刚使用 Go 的开发者往往会混淆这两个概念,数组作为最常见的集合在编程语言中是非常重要的,除了数组之外,Go 语言引入了另一个概念 — 切片,切片与数组有一些类似,但是它们的不同之处导致使用上会产生巨大的差别。


ORM 实例教程

2019-02-18 21:29:06 阮一峰

一、概述 面向对象编程和关系型数据库,都是目前最流行的技术,但是它们的模型是不一样的。...


详解 Kubernetes 垃圾收集器的实现原理

2019-02-17 19:44:00 Draveness

垃圾收集器在 Kubernetes 中的作用就是删除之前有所有者但是现在所有者已经不存在的对象,例如删除 ReplicaSet 时会删除它依赖的 Pod,虽然它的名字是垃圾收集器,但是它在 Kubernetes 中还是以控制器的形式进行设计和实现的。


详解 Kubernetes ReplicaSet 的实现原理

2019-02-16 23:14:00 Draveness

Kubernetes 中的 ReplicaSet 主要的作用是维持一组 Pod 副本的运行,它的主要作用就是保证一定数量的 Pod 能够在集群中正常运行,它会持续监听这些 Pod 的运行状态,在 Pod 发生故障重启数量减少时重新运行新的 Pod 副本。


通过自定义Istio Mixer Adapter在JWT场景下实现用户封禁

2019-02-15 17:09:24 Yisaer

介绍互联网服务离不开用户认证。JSON Web Token(后简称JWT)是一个轻巧,分布式的用户授权鉴权规范。和过去的session数据持久化的方案相比,JWT有着分布式鉴权的特点,避免了session用户认证时单点失败引起所有服务都无法正常使用的窘境,从而在微服务架构设计下越来越受欢迎。然而JWT单点授权,分布鉴权的特点也给我们带来了一个问题,即服务端无法主动回收或者BAN出相应的Token,使得即使某个服务主动封禁了一个用户时,这个用户同样可以使用之前的JWT来从其他服务获取资源。本文我们将阐述利用Istio Mixer Adapter的能力,来将所有请求在服务网格的入口边缘层进行JWT检查的例子,从而实现用户封禁与主动逐出JWT等功能。背景在我之前的投稿中,描绘了一个非常简单的基于K8S平台的业务场景,在这里我们将会基于这个场景来进行讨论。对于一个简单的微服务场景,我们有着三个服务在Istio服务网格中管理。同时集群外的请求将会通过nginx-ingress转发给istio-ingressgateway以后,通过Istio VirtualService的HTTPRoute的能力转发给对应的服务,这里不再赘述。从下图的架构模式中,我们可以看到所有的请求在进入网格时,都会通过istio-ingressgateway这个边缘节点,从而涌现出了一个非常显而易见的想法,即如果我们在所有的请求进入服务网格边缘时,进行特定的检查与策略,那么我们就能将某些不符合某种规则的请求拒绝的网格之外,比如那些携带被主动封禁JWT的HTTP请求。了解Istio Mixer为了达到我们上述的目的,我们首先需要了解一下Istio Mixer这个网格控制层的组件。Istio Mixer 提供了一个适配器模型,它允许我们通过为Mixer创建用于外部基础设施后端接口的处理器来开发适配器。Mixer还提供了一组模版,每个模板都为适配器提供了不同的元数据集。在我们的场景下,我们将使用Auhtorization模板来获取我们每个请求中的元数据,然后通过Mixer check的模式来将在HTTP请求通过istio-ingressgateway进入服务网格前,通过Mixer Adapter来进行检查。在Istio Mixer的描述中,我们可以发现每个请求在到达数据层时,都会向Mixer做一次check操作,而当请求结束后则会向Mixer做一次report操作。在我们的场景中,我们将会在请求到达istio-ingressgateway时检查这个请求中的JWT鉴权,通过JWT的Payload中的信息来决定是否要将请求放行进入网格内部。得益于Mixer强大的扩展能力,我们将通过经典的Handler-Instances-Rule适配模型来一步步展开,同时也意味着我们将要编写一个自定义的Istio Mixer Adapter。Mixer适配模型那么怎么通俗易懂的理解Handler-Instances-Rule这三者的关系呢?在我的理解下,当每个请求在服务网格的数据层中游走时,都会在开始与结束时带上各种元信息向Mixer组件通信。而Mixer组件则会根据Rule来将特定的请求中的特定的数据交给特定的处理器去检查或者是记录。那么对于特定的请求,则是通过Rule去决定;对于特定的数据,则是通过Instances去决定;对于特定的处理器,则是通过Handler去决定。最终Rule还把自己与Instances和Handler绑定在一起,从而让Mixer理解了将哪些请求用哪些数据做哪些处理。在这里我们可以通过Istio Policies Task中的黑白名单机制来理解一下这个模型。在这里appversion.listentry作为instances,通过将list entry作为模版,获取了每个请求中的source.labels[“version”]的值,即特定的数据。whitelist.listchecker作为handler,则是告诉了背后的处理器作为白名单模式只通过数据是v1与v2的请求,即特定的处理器。最后checkversion.rule作为rule,将appversion.listentry和whitelist.listchecker两者绑定在一起,并通过match字段指明哪些请求会经过这些处理流程,即特定的请求。123456789101112131415161718192021222324252627282930## instancesapiVersion: config.istio.io/v1alpha2kind: listentrymetadata: name: appversionspec: value: source.labels["version"]---## handlerapiVersion: config.istio.io/v1alpha2kind: listcheckermetadata: name: whitelistspec: # providerUrl: ordinarily black and white lists are maintained # externally and fetched asynchronously using the providerUrl. overrides: ["v1", "v2"] # overrides provide a static list blacklist: false---## ruleapiVersion: config.istio.io/v1alpha2kind: rulemetadata: name: checkversionspec: match: destination.labels["app"] == "ratings" actions: - handler: whitelist.listchecker instances: - appversion.listentryJWT Check 的架构设计当我们理解了以上的Mixer扩展模型以后,那么对于我们在文章开头中的JWT封禁需求的Handler-Instances-Rule的模型就非常显而易见了。在我们的场景下,我们需要将所有带有JWT并且从istio-ingressgateway准备进入网格边缘的请求作为我们特定的请求,然后从每个请求中,我们都要获取request.Header[“Authorization”]这个值来作为我们特定的数据,最后我们通过特定的处理器来解析这个数据,并在处理器中通过自定义的策略来决定这个请求是否通过。当我们搞清楚了这么一个模型以后,那么之后的问题就一下子迎刃而解了。在我们的设计中,我们将要自定义一个JWTAdapter服务来作为特定的处理器,JWTAdapter将会通过HTTP通信把数据转交给Adapter-Service来让Adapter-Service来判断这个请求是否合法,而Adapter-Service的凭证则是通过与业务服务的通信所决定。在我们的场景中,假设每个请求所携带的JWT的Payload中有一个email属性来作为用户的唯一标识,当业务领域中的账户服务决定封禁某个用户时,他将会通知Adapter-Service,后者将会把这个信息存于某个数据持久服务中,比如Redis服务。当JWT-Adapter服务向Adapter-Service服务询问这个请求是否合法时,Adapter-Service将会通过Payload中Email属性在Redis中查询,如果查询到对应的数据,则代表这个用户被封禁,即这个请求不予通过,反之亦然。如何自定义编写一个Adapter?说实话,自定义编写Adapter是一个上手门槛较为陡峭的一件事情。我在这里因为篇幅原因不能完全一步步细说自定义Adapter的步骤。在这里我推荐对自定义编写Adapter有兴趣的人可以根据官网的自定义Mixer Adapter开发指南和自定义Mixer Adapter详细步骤来进行学习和尝试。在这里我给出在我的JWT-Adapter中的关键函数来进行描述。123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354func (s *JwtAdapter) HandleAuthorization(ctx context.Context, r *authorization.HandleAuthorizationRequest) (*v1beta1.CheckResult, error) { log.Infof("received request %v\n", *r) props := decodeValueMap(r.Instance.Subject.Properties)var Authorization interface{}if len(props["custom_token_auth"].(string)) 0 {Authorization = props["custom_token_auth"]} else { // 没有获取到JWT,直接将请求放行return &v1beta1.CheckResult{Status: status.OK,}, nil}cookie := props["custom_request_cookie"]host := props["custom_request_host"]if host == "www.example.com" {url := userService + "/check"request, err := http.NewRequest("GET", url, nil)if err != nil { //出现异常时,直接将请求放行return &v1beta1.CheckResult{Status: status.OK,}, nil}request.Header.Add("Content-Type", "application/json; charset=utf-8")request.Header.Add("cookie", cookie.(string)) request.Header.Add("Authorization", Authorization.(string)) // 发送请求给Adapter-Serviceresponse, _ := client.Do(request)if response != nil && response.StatusCode == http.StatusOK {body, err := ioutil.ReadAll(response.Body)if err != nil { //如果有异常 log.Infof(err) //记录异常即刻} else { log.Infof("success to get response from adapter-service")var value map[string]interface{}json.Unmarshal(body, &value)if value["pass"] == false {//当用户确实返回处于封禁状态中时,才返回封禁结果return &v1beta1.CheckResult{Status: status.WithPermissionDenied("Banned"),}, nil}}}}log.Infof("jwtadapter don't have enough reason to reject this request")return &v1beta1.CheckResult{Status: status.OK,}, nil}通过以上描述可以发现的是,在我们的场景下,我们当且仅当从Adapter-Service中确实得到了不允许通过的结果才将这个请求进行拒绝处理,而对其他情况一律进行了放行处理,即使发生了某些错误与异常。由于我们的错误处理会直接影响到这些请求能否在网格中通行,所在做Istio Mixer Check时需要时刻记住的到底是放行特定的请求,还是拒绝特定的请求,在这一点处理上需要十分谨慎与小心。Handler-Instances-Rule当我们将自己的Adapter上线以后,我们只要通过声明我们得的Mixer扩展模型让Mixer识别这个Adapter并且正确处理我们想要的请求即可。这里我们再回顾一下我们之前所提到的特定的请求,特定的数据,特定的处理器。对于特定的请求,我们需要将网格边缘的请求筛选出来,所以我们可以通过host是www.example.com并且携带了JWT作为条件将请求筛选出来。对于特定的数据,我们选用authorization作为模版,取出header中的JWT数据,最后通过特定的处理器,将这个check请求交给jwt-adapter。至此,我们通过Istio Mixer Aadapter来进行JWT封禁的需求场景算是基本完成了。12345678910111213141516171819202122232425262728293031323334353637# handler adapterapiVersion: "config.istio.io/v1alpha2"kind: handlermetadata: name: h1 namespace: istio-systemspec: adapter: jwtadapter connection: address: "[::]:44225"---## instancesapiVersion: "config.istio.io/v1alpha2"kind: instancemetadata: name: icheck namespace: istio-systemspec: template: authorization params: subject: properties: custom_token_auth: request.headers["Authorization"]---# rule to dispatch to handler h1apiVersion: "config.istio.io/v1alpha2"kind: rulemetadata: name: r1 namespace: istio-systemspec: match: ( match(request.headers["Authorization"],"Bearer*") == true ) && ( match(request.host,"*.com") == true ) actions: - handler: h1.istio-system instances: - icheck---扩展阅读网格边缘层验证JWT的可行性?既然在网格边缘层能对JWT进行检查,那么能否可以做成在网格边缘层同时也进行JWT的验证?答: 在我最初做Mixer Check时确实想到过这件事情,并且无独有偶,在PlanGrid在Istio中的用户鉴权实践这篇文章中,PlanGrid通过EnvoyFilter实现了在网格边缘层进行JWT以及其他鉴权协议的鉴权。但对此我的看法是,对于JWT鉴权的场景,我并不推荐这么做。因为微服务场景中,我们使用JWT的初衷就是为了分布式鉴权来分散某个服务的单点故障所带来的鉴权层的风险。当我们将用户鉴权再一次集中在网格边缘时,我们等于再一次将风险集中在了网格边缘这个单点。一旦istio-ingressgateway挂了,那么背后所有暴露的API服务将毫无防备,所以鉴权必须放在每个微服务内。另一方面,在我的《深入浅出istio》读后感中提到,对于生产环境使用Istio,必须拥有一套备用的不使用Istio的环境方案,这意味着当Istio出现故障时,可以立即通过切换不使用Istio的备用环境来继续提供服务。这同时意味着Istio所提供的能力与服务不应该与业务服务所强绑定在一起,这也是为什么我在上文中将Jwt-Adapter与后面的Adapter-Service成为插件服务的原因。JWT封禁用户这个能力对我们就像一个插件一样,即装即用。即使当我们切换为备用环境时无法使用Istio,暂时失去用户封禁这个能力在我们的产品层面也完全可以接受,但对于用户鉴权则不可能。所以这意味着当我们使用Istio的能力时,一定要时刻想清楚当我们失去Istio时我们该如何应对。关于作者从去年毕业以后一直对服务网格与CloudNative领域充满兴趣,在工作中使用Istio在生产环境中也将近有了半年多的时间,写作分享则是平时的业余爱好之一。如果你对服务网格或者是CloudNative领域有兴趣,或者是对我的技术文章写作有想法与建议的话,欢迎联系我交流。Github 博客RSS订阅


每周分享第 43 期

2019-02-15 03:51:20 阮一峰

这里记录过去一周,我看到的值得分享的东西,每周五发布。...


京东PLUS会员项目前端性能优化实践

2019-02-15 01:46:32 甄玉磊

作者:Frans 京东PLUS会员项目是国内第一个电商付费会员项目,正式开通的会员数量已破千万。我团队从2016年接手这个项目的前端开发工作,一路见证了它的高速成长,也为此贡献了自己的力量。 这个项目有几个特点: 第一,需求多。移动端使用 H5 开发,曾有人问为什么不用原生或者 RN 开发? 我觉得吧,以这个项目的需求数量和迭代速度来看,连 H5 都难以 hold 的住,还是不要奢望原生和 RN 了。 第二,产品经理多。一般的项目对接一两个产品经理,这个项目我们需要对接一个异地的产品经理“团队”;一般的项目换产品经理一个一个的换,这个项目一批一批的换……我们已经送走好几届PLUS会员产品经理了。铁打的研发,流水的产品经理。 所以说,PLUS会员项目是业务方滴,也是项目经理滴,还是产品经理滴,但终归是俺们研发滴。每念及此,我的耳边总会响起叶倩文的那首老歌:“天地悠悠,过客匆匆,潮起又潮落…”。 书归正传。用户众多和需求迭代频繁,确保线上安全稳定始终是第一要务。所以在架构调整和性能优化方面我们一直都小心翼翼,以一些小修小补为主,只有到大规模改版的时候才会有大的升级改造。不过,平时我们对这些问题的思考和实践却不曾停止过,我们验证了一些行之有效的优化方案,在下一波改版中将会得到应用。 我虽然不完全认同“前端开发每十八个月难度翻一倍”的说法,但这一行发展迭代速度快却是不争的事实。若等到这些优化方案全都应用上再出来念叨,可能就显得不那么新鲜了。所以,我决定先把这些方案拿出来分享,和感兴趣的小伙伴一起讨论,进一步完善。 这些方案主要针对移动端,优化核心方向是提高首页的加载速度,特别是首屏和弱网络环境下的加载速度。从持久化缓存、削减代码量、优化接口请求、提升主观感受等方面下手,比较大的改动是应用 PWA 和升级架构。 PWA 离线缓存可以极大的提升用户体验,不过它对于首次加载速度并无提升作用,还得靠其他优化手段,这是一套组合拳。我们先从架构升级说起吧。 架构升级 项目计划迁移到 Gaea4.0 脚手架[1],这是我们团队基于 webpack 4 开发的一套通用 Vue 单页面应用脚手架,此前的系列版本已经过数十个项目的验证,还是比较稳定的。近期新推出4.0版相较之前版本有着不小的改进。 webpack 升级到了 4.0 Babel 升级到了 7.0 Vue-loader 升级到了 15 重构了上传插件,一键上传到测试服务器更快更稳定 针对我厂手机和电脑位于不同局域网无法互访的问题,集成了自主研发的 Carefree 解决方案[2],方便真机测试调试 集成了 NutUI 组件库[3],可按需加载需要的UI组件 集成了自主研发的基于swagger的数据mock工具SMOCK[4] 支持自动生成骨架屏[5] 支持 PWA … 迁移有几个主要目的: […]

tags: 前端开发


记录一次mysql升级之后rails遇到的问题

2019-02-14 11:14:12 邹超

mysql升级之后数据文件夹共用, 但是无法使用db:migrate 错误信息大致如下: E, [2019-02-14T11:06:23.650106 #68912] ERROR -- : Mysql2::Error: Table 'performance_schema.session_variables' doesn't exist: SHOW VARIABLES LIKE 'character_set_database' rake aborted! ActiveRecord::StatementInvalid: Mysql2::Error: Table 'performance_schema.session_variables' doesn't exist: SHOW VARIABLES LIKE 'character_set_database' 解决方式: mysql_upgrade -u 用户名 -p --force 然后一定记得重启mysql服务