文章推荐群体:前端Dev、桌面端Dev、DevOps与Github羊毛党
2016年6月,Github发布了GitHub Pages功能,它允许将静态网页文件存放在github仓库上,并将其发布成web网站。借助Hexo博客框架及其周边丰富、优美的博客主题,可以让程序员使用markdown语法搭建出精美、简洁的博客网站。随后,Github page还支持自定义域名、https… 无需服务器、免费https证书,无需操心运维,一时间,不少博客主纷纷放弃wordpress,转战Github Page。 私以为这可谓广大博客主们“薅github羊毛”的迈出的第一步。
为什么说“薅github羊毛”,是因为我发现越来越多的项目在极致地开发出github的潜能,比如将github gist作为前端应用的存储后端、homebrew还将github作为包发布和审核平台
Github Page已成历史,而今天我这里介绍的除了Github Page,还将介绍如何在Github上持续发布个人全平台应用作品。
这意味你开发的个人作品将支持同时发布在以上各操作系统或运行平台上,想起来是不是有点些小激动呢?
鉴于“怎么在Github上部署自己的网站?怎么绑定自定义域名”可以在Google上搜到成千上万条结果,本博客便不赘述相关细节。在这里主要介绍如何将Github Page的发布过程自动化。 在这之前,我们应该有这样一个基本的共识,Github Page只支持纯前端项目。所以我们这里不妨以React.js
项目为例: React.js
采用如下脚本完成打包(包括JS/CSS构建、JS混淆minify、HTML模板渲染、manifest清单文件更新等)
1 |
|
运行yarn build
命令后,在build文件夹下将会生成上述构建产物,而使用nginx或者apache这样的HTTP服务器将该目录发布,就可以搭建一个静态网站。 通常情况下,我们常常将github repo的master分支根目录和docs目录作为github的发布目录。实际上github还支持将“gh-pages”分支作为github page发布分支。 这里推荐一个npm库gh-pages
,它可以将指定目录文件发布到gh-pages
分支的npm库,项目主页:https://github.com/tschaub/gh-pages 使用yarn install gh-pages
后在package.json
添加发布脚本
1 |
|
运行 yarn release:web
即可自动发布。
Electron是由Github开发,同时包含Chromium和Node.js运行时环境的跨平台桌面应用平台。它可以打包生成运行在Windows、Mac、Linux等多种操作系统的安装包。常用的Virtual Studio Code、Slack APP就是基于Electron开发。
建议采用electron-builder npm库打包electron应用
yarn add -D electron-builder
打包脚本electron-builder.yml
1 |
|
electron-builder可以帮助我们完成electron应用的安装包打包,文件名加版本号,以及混淆加密。
Dockerhub除了是一个公用的docker镜像仓库,同时帮助我们在无需CI/CD的情况下免费、自动地构建docker镜像,具体可以参考https://baiyuan.wang/using-dockerhub-continually-build-container-mirroring.html
release-it npm库可以实现发布的自动化,实现以下功能
1 |
|
现在的NodeJs web项目比较流行地采用express作为服务器,axios也是一款比较流行的HTTP客户端。微服务的特点之一,自己作为服务端的同时,也为作为客户端访问其它服务。所以集成调用链追踪的关键在于:
1 |
|
1 |
|
由于nodejs是主线程为单线程的服务器语言,高并发由异步消息队列完成。所以保证从服务器端传递tracing信息和客户端tracing信息在同一个“上下文”,不能简单地采用一个全局变量来保存tracing信息。此处的isolateZipkinContextByRequest至关重要,
1 |
|
详见:https://github.com/openzipkin/zipkin-js/tree/master/packages/zipkin-instrumentation-axiosjs
相比传统的K8S架构,由于service mesh使用sidecar的方式代理了pod的所有网络请求,构造请求头、和记录请求路径、请求返回码等操作可以在sidebar等istio组件上实现。 所以在istio语境下我们只需要实现HTTP header中的tracing信息从服务端到客户端的传递。同样的原因,nodejs是单线程语言,不能通过一个thread级别隔离的全局变量来保存tracing信息,同时通过层层函数调用栈的方式来传递request的tracing信息则过于麻烦。express库本身没有提供类似于spring或者PHP中的session的会话对象,这里会引入一个“express-http-context”的npm库完成request之间的数据“隔离”。
1 |
|
express的集成代码:
1 |
|
参考资料
1 |
|
1 |
|
为什么是K8S部署?理论上K8S部署的服务我们一般不会关心服务的docker镜像会不会把机器占满的问题,那么为什么我还要写K8S部署呢? 这主要用于 CI Agent跑在K8S上的情况。以微软Azure DevOps K8S Agent为例,https://github.com/Azure/helm-vsts-agent,其agent会将宿主机的docker.sock挂载在K8S node上,如果agent存在构建docker镜像的行为,就会导致agent构建出来的docker 镜像已经“脱离”了K8S的控制
1 |
|
文章 https://lennilobel.wordpress.com/2018/10/15/adding-a-user-to-your-azure-subscription-with-resource-group-access/ 介绍了在Azure后台UI上实现这一需求,主要是在Resource Group级别进行了IAM控制。 同时,极客人将其进行了代码化:https://github.com/tf-module/azure-user-resource-group ,它实现过程如下(详情请移步开源地址)
一个可运行的软件项目通常包括两个要素:代码和密钥。我们通常会使用无版本控制的FTP和有版本控制的SVN、git等成熟的工具进行代码管理;而在我参加的大大小小、许许多多的项目中,密钥管理似乎缺乏成熟或标准的实践。本文将历数一下笔者在各个使用过的密钥管理实践并分析他们的优缺点。最后给大家推荐一款密钥管理工具:vault。
在软件项目开发中,密钥常常应用于下面四个场景:
当我们加入一个团队时,通常会有一个Readme文档告诉你项目代码库的下载链接。除此之外它会告诉你需要向团队“前辈”索要密钥文件,不然你的代码是不能在本地启动的。同时有人告诉你,这个密钥文件千万不要加入到git仓库中。 这种“薪火相传”的密钥管理方式,是最原始也是最常见的方式。它常常会伴随这样几个问题:
比如你会听到这样的对话:
- A: “我拉了一下最近的代码,怎么就跑不起来了?”
- 坐在旁边的B突然想起了什么:“好吧,我想起来了!我改了一下数据库密码,忘记告诉你了,我把最新的密钥发给你。”或者“我新加了一个功能因为使用API-KEY要访问消息队列,我在自己本地的环境变量里面加上了这个KEY,忘记告诉你们了”
- 随后B把最新的密钥文件传给了A。几天后,同在项目的C也遇到了同样的问题……
相信已经不止一次地听人提醒:千万不要将密钥文件明文提交到git。但是密钥泄露在代码仓库的问题依旧时有发生。
密钥和代码一样,在团队项目中同样需要进行共享、同步。密钥放在git仓库中本来是可以解决团队协作问题的,只不是不能被明文存储。那么,如果是将密钥加密后再提交到git仓库呢? git-crypt便是这样一款可将git仓库中的密钥文件进行透明加密和解密的工具。它可以将密钥文件在push时加密,在pull下来后解密。更多介绍和使用说明可以参考:https://github.com/AGWA/git-crypt 借助git版本控制工具,它可以实现:
问题:
在现在的Web项目的CI/CD流程中,通常会将项目代码经过构建打包生成docker镜像(制品);在部署阶段,不同环境会采用相同的docker镜像,但是会使用不同的环境变量(比如集群、域名、数据库地址密码等)传入到docker的运行时,从而完成在不同环境的部署。 环境(变量)在不同的CI/CD中有不同形式,比如的Jenkins的Credential、GoCD的Environment、CircleCI的Context。 如果将所有的部署与运行时所需要的密钥数据都保存到pipeline上,会导致下面的问题
解决的办法一般是在pipeline上保存尽量少的密钥字段,我们通过一次认证就可以具备获取所有密钥数据的权限。
在云和基础设施自动化时代,我们应该知道一家名为Hashcorp的公司,其代表作有知名的terraform、consul、packer、vagrant。vault也是这家公司的产品之一,它通过API将密码以服务的方式暴露出去。它可以提供:
服务化后的vault可以做到
向进度落后的项目中增加人手,只会使进度更加落后
Brooks法则并不是像数据公式一样有严谨的推理,而且也没有介绍一个解决进度落后、除了增加人手以外还有什么通用方法。
实际上,敏捷会议中有一个叫做Cycle time。Cycle time的初衷应该就是解决这个问题,找出估算和实际时间出入很大的卡,对着这些卡,实际时间花的比估算的多,原因是什么,有什么可以采取的action;花的时间比估算的少,是为什么,有什么可以分享的东西。
还有一个作用就是让团队知道产生估算有出入的具体细节,做这张卡的人可以做一个澄清,一方面可以寻求团队帮助,另一方面当事人吐槽一下,可能他也很委屈,大家都抱怨这个做慢了,却不晓得是因为我趟了好多坑,这些坑是之前没遇到的;或者是之前估卡的scope变大了。
没有银弹,许多事情都需要case by case地进行分析,而Cycle time就是提供这种流程的实践,帮助团队发现估算有出入问题的原因。一切敏捷流程都是为了尽早暴露问题、增加沟通和加速反馈。
]]>脑洞一下:中台以后各个部门的数据以微服务API形式放在API store里面供其它部分消费,为了避免部门打架、中台成本谁来出、费用怎么收的问题。在想能不能基于istio开发为请求计费的插件(计费链),技术实现的大概思路就是就是像zipkin每个调用单元(span)加一个价格,一次API的价格等于调用链路中所有单元(span)价格的求和。
最后的大概可视化效果就是这张图的时间ms,换成人民币单位(估计不要一分钱)
比如调用一次A服务某API价格为a,一次B服务某API价格为b,调一次C服务需要调A和B,那么C调用费用就是a + b + c。其中C分别要给别的服务a和b的费用,c是给自己的。上面为了介绍思想,这里只是做一个简单的求和,其实也许我们可以做更复杂的方案。就像卖商品一样卖自己的API请求。
比如每个服务可以这样定价:
以上可称为API as Product, Data as Product的按量收费方案。
不是从基础架构方面计费,按业务价值;中台没有业务价值,怎么给它定义价值,而且做到每一个API请求他的价格不一样,不是简单地按调多少次来收费;
每一次API请求需要多少钱,是它自己本身花费的费用,加上它调用其它一些服务的成本
比如服务A定价取一次user信息2分钱,服务B定价取一次商品信息3分钱),订单服务C调了一次user和商品,就要分别给A和B付2分钱和3分钱,然后D调C,可能它要花8分钱,然后它想知道为什么要花8分钱,于是点开调用链可以看到收费清单,上面明明白白地告诉它8毛钱花在哪里,分别给哪些服务付了多少钱。
]]>有必要。 第一点,我在公司内部运营部门呆过,因为免费,发现做的API各种被调,不被限制地被调。通过只是想通过收费手段来降低API的调用压力。也有些API服务可以机械性地限流,调用多少次限流,而这种模式是市场化调节。收费高你自然谨慎地调。 第二点,公司高层往往觉得公司的运营部门是一个纯成本消耗的部门,很难去衡量一个内部部门的价值。通过这种方式,可以让高层知道每个部门掌握的业务数据到底值多少钱。为其对内部部门的投资决策提供参考。
第三点,其实在一个公司里面,内部还是各种算账啊,不展开赘述。
]]>
软件授权被禁,国内的市场就大了:用不了国外的,就会自己做,对于中国是机会也是挑战
程序员的岗位就多了,中国盗版用习惯了,要是都用正版,愿意为软件付钱,然后又买国产软件,需求就多了,市场就大了,就能让程序员多吃几年饭;现在拿来主义惯了,就不用这个程序员来做,不仅操作系统,像PS MATLAB 这种东西中国都缺啊。
现在音乐都规范版权了,如果软件方面也规范版权了,就要花钱买,有人买就要人去开发卖,外国的太贵了,中国就可以开发个便宜点的。反正中国市场那么大,便宜点也有钱赚;而要开发这些东西,就要程序员,就业就好了
现在的中国的软件行业都窝在互联网,一个个产品都做死了,来来回回就那么些东西,哪个火就搞哪个。不管做啥BAT都要搞啥,小厂搞出来,BAT跟着搞就把他灭了;中国软件行业这几年钱全放在互联网,哪像中国全是BAT在玩,而且玩的也是上层应用,搞个UI啥的,技术含量不足
美国真正厉害的是即使是软件这一块,各个领域都很牛逼,各种小公司都是世界顶尖的,旧金山随便抓一个公司都是世界顶级的;公司之前互相付license费用,做的好,一小块领域就可以让自己活下来了;在中国没有看到这种技术上的商业生态
小公司活的好,国家就业也就会好;毕竟BAT员工数量对于整个行业还是少部分,众多的小企业都能活下来带来都的是更多的就业数量;中国那么大市场,市场就是资源,做好一小块就可以活的够好,可惜资源都是大厂被把持住
]]>中国科技是缺乏自驱力的,而从来都是自上向下的号召和政府的引导。在政府关注的领域,我相信这个领域一定能发展的好,比如航天、高铁。现在开始关注操作系统和芯片了,我相信也能发展得好,那么其他领域呢?很难看到百花齐放的氛围,这缺乏行业自己来推动技术发展的自驱力,我一直认为创新来自于自己的自驱力而是上面的压力和重视(就像好的学生是自己想学习,而不是应付老师检查作业或者应付考试)。像旧金山随便找一个公司都是世界顶尖的事情在中国还没有看到曙光。
所谓脚手架,就是在初始化代码库时,脚手架可以帮助自动生成一些代码和项目结构,注入一些框架。对于一个NodeJS项目,不需要我们从npm init初始化起,自己一步步安装一些依赖。
Express是目前最流行的NodeJS web框架。全局安装一个express-generator,用来初始化express项目。
全局安装命令:npm install express-generator -g
新建一个名为hello-express项目: express hello-express
当使用NodeJS 开发Web API时,强烈建议使用Swagger进行API构建与管理,以及提供API文档服务。全局安装swagger命令也可以实现初始化一个swagger项目。swagger命令可以让你在浏览器上实时直接编辑你的API定义和调试API。
npm install swagger -g
swagger project create hello-swagger
,在这过程中会让你选择使用哪种Web服务器,当选择express时就可以自动引入express框架.
├── README.md
├── api
│ ├── controllers
│ │ ├── README.md
│ │ └── hello_world.js
│ ├── helpers
│ │ └── README.md
│ ├── mocks
│ │ └── README.md
│ └── swagger
│ └── swagger.yaml
├── app.js
├── config
│ ├── README.md
│ └── default.yaml
├── package-lock.json
├── package.json
└── test
└── api
├── controllers
│ ├── README.md
│ └── hello_world.js
└── helpers
└── README.md
swagger project edit
, 此时会打开系统浏览器,在浏览器中可以直接编辑swagger文档,并进行实时语法检查,同时浏览器里面的编辑变更会回写到代码。Swagger实时编辑和语法校验
上图右侧部分,就是类似于 postman
的API调试工具。
Swagger是一个最流行的的API构建与管理工具,在各种语言和框架都有相应的库可以支持,同时安装swagger-ui扩展进行API文档管理和在线调试。 其遵循OpenAPI标准,OpenAPI定义了诸如路由转发、参数定义与校验等一整套API规范。
上面的swagger命令适合在本地编辑、调试使用,当在产品(Production)环境发布文档服务时,适合引入 swagger UI 中间件
app.use(SwaggerUi(swaggerExpress.runner.swagger));
访问http://localhost:10010/docs/#/即可查看API文档:
Swagger UI
完整代码如下:
1 |
|
ECMAScript 是 JS 的语言标准,ES6是新的JS语法标准。在没有其它配置的情况下使用ES6语法会出现一下错误。我们需要引入babel做语法转换。
1 |
|
Babel 是一个 JavaScript 编译器,工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。更多文档可参考:https://www.babeljs.cn/docs/
安装依赖:
1 |
|
在根目录创建.babelrc文件,内容如下
1 |
|
1 |
|
cjs-to-es6
可以做一些简单的ES6语法转化:npm install -g cjs-to-es6
每次修改代码时我们需要重启Express来查看效果,nodemon
可以在指定的文件发生修改后,帮助我们自动重启服务,提高开发效率。
npm i -D nodemon
{
“exec”: “npm run dev”,
“watch”: [“src/“, “public/“],
“ext”: “js, html, css, json”
}
ES Lint是一款代码风格扫描工具,尤其是在团队开发时可以帮助我们规范我们的代码风格,并提供与IDE的集成做到代码纠错。
npm i -D eslint
常常有这样的场景,持续集成要求我们在提交代码之前测试在本地是可以通过的。这个时候我们可以在注册“钩子”的方式,在代码提交之前在本地运行测试,如果测试不通过则不允许提交。那么使用husky
可以这一需求:
“husky”: {
“hooks”: {
“pre-push”: “npm run coverage && npm run pact:test”
}
},
“husky”: {
“hooks”: {
“pre-commit”: “npm lint”
}
},
开启gzip压缩可以显著提高HTTP的服务的访问速度,安装compression
中间件可以非常方便地启用:
import compression from ‘compression’;
…
app.use(compression());
logback的介绍. Logback是由log4j创始人设计的另一个开源日志组件。 为了避免日志记录会给服务带来性能问题,在项目中决定采用“异步记录日志”进行记录。这里就要使用到Logback的AsyncAppender组件。简而言之,就是AsyncAppender会在请求处理的主线程以外新建一个子线程 -“日志记录线程”进行日志输出。避免日志输出造成主线程阻塞。 在运行过程中,主线程不会直接输出日志,而是将日志以Event的方式发给日志线程,Event在日志线程中会形成一条队列。而AsyncAppender的配置项很多都是对队列的配置,比如:
需要注意到点:
为了在JVM虚拟机退出之前,能将日志线程的队列中的日志处理完毕,需要注册一个shutdown hook 详见: https://logback.qos.ch/manual/appenders.html#AsyncAppender
按照Logback的文档实现异步日志很简单,那么怎么验证异步日志有没有生效就是个问题。这里引入一个“VisualVM”工具,它可以列出一个Java进程所包含的线程。可以看到下面的AsyncAppender-Works-xxx就是日志收集线程。
1 |
|
这种方式常常用于配置nginx作为负载均衡时,可以控制流量导向不同应用服务器的比重,当然也可以通过用于新旧版本的流量切分。但这种方式的一个问题是用户在多次请求时,被导向的页面是随机的。比如反复刷新浏览器,有时被导向新版网站,有时被导向旧的,这显然不是我们希望发生的。
所以我们希望服务能“记住”每个用户第一次访问的版本,后续的请求依旧导向到改版本。而HTTP请求是无状态的,即每个HTTP请求实际上是独立的,而不是和用户建立一个长久的会话。我们只有设置里一个“特定的标记”,服务器才能知道你是不是同一个用户,所以我们需要使用cookie来标记用户。使用cookie而不是localstorage的原因是cookie是每次都会被浏览器发往服务器端。 这里,我们使用cookie来记录用户第一次访问的版本。后续访问时读取该cookie字段来控制nginx的转发行为。
1 |
|
2019年度“315”晚会人工智能拨打骚扰电话的情节,让大众了解到在信息时代,保护个人隐私的重要性。本篇文章分享了在日志记录中保护用户隐私数据的七个最佳实践。
与“中国人愿意用隐私交换便利性”的心态完全不同,欧美国家在个人隐私保护方面明显走得更早也更远一些。在2018年5月GDPR发布前后的一段时间里,保护个人隐私相关的需求被迅速提高了优先级,而像我这样一个开发国际化产品的普通程序员,日常工作也因此受到影响,我们放下手中的业务需求卡(Story),转而去做GDPR相关的安全需求。 一般在医疗保健或金融行业中,限制访问客户的敏感数据有着非常严格的规定,尤其欧洲GDPR颁布之后,公司泄露个人数据的后果也非常严重。在个人隐私保护方面,国内目前在法律和意识方面处于滞后的状态,但是许多人或多或少都感受到个人信息泄露给自己带来的麻烦,比如骚扰电话的增多就是最明显的例证。比较乐观的是网络安全法的发布,以及网民意识的觉醒,表明我们的个人信息保护正在路上。 对于一些面向欧美的项目,从公司最高层面,自上而下,我们采取了一系列相关动作,比如梳理我们基础设施架构图、数据流图、API数据字段分析等,其中包括保护日志中的个人信息。
个人隐私安全和其它安全问题一样,是一个永远做不完的需求。你不能说你的网站是绝对安全的,只能说“我检查了所有目前已发现的安全漏洞列表(Checklist),并且采取了相应的防御措施,做到尽量安全”,或者说我们采取了一些很好的安全实践,比如采取了动态密码、在nginx上安装了防攻击防SQL注入插件等等。 现在的Web系统一般都配备了日志系统用于记录访问请求、分析线上事故等,比如开源的有ELK,SaaS的有DataDog、Sumo Logic 等。 在日志记录过程记录下一些用户隐私信息往往是不可避免的。诚然,开发者的个人隐私保护意识是很重要的,但有时并不一定是开发者的主动想偷窥用户信息。比如这里举一个很常见的情况,有些程序异常如果没有合理捕获,往往会输出调用堆栈,这些调用堆栈里面某些方法的参数可能就包含有个人隐私信息; 虽说没有一种一劳永逸的方式来避免个人信息出现在日志中,但我们可以通过下面的实践来尽量规避,并将这些内建在自己平时的开发工作中。下面的实践,一些涉及到了代码层面的技术实践、团队流程的优化,还有的是测试、运维上的一些措施。
在我们深入讨论怎样避免个人隐私数据出现在日志之前,我们来界定什么是_隐私数据_:
个人隐私信息不一而足,其界定工作可能需要与熟悉GDPR的安全专家合作来完成,根据实际情况彻查应用内的数据,来确定什么是敏感的。
处理隐私数据时,应尽量减少系统使用这些数据的频率。比如在数据库表设计时,使用电子邮件地址Email,或者极端一点的例子,使用身份证号码(下称PID)来作为“用户”表的主键。这意味系统在访问用户数据时,都需要使用Email或者PID来建立关联关系,这样做可能会非常省事,而且系统也是完全可以工作的,但是这极大地提高了敏感字段的曝光率,出现的地方越多,意味着被日志记录下来的几率越大。 所以更好的方法是解耦出隐私数据,只在在必要时才使用它。一种常见的解决方案是将随机生成的字符串作为用户表的ID,同时建立一个“1对1”的数据库表来存储用户ID与用户数据库表主键的关系。例如:
PID | 外键
————————-
42-12xxxx-345 |5a2_cXKrt32DcWOJpJlyhr7FhTcLPfvlEAb1eA2Hza
在用户表以外的所有数据库表,都应该使用这个随机ID进行查询,这种随机ID即使被暴露也不会泄露任何个人数据。
比如你有一个RESTful API,通过Email来查找用户信息,则可能很容易拥有这样一个Endpoint,如:**/user/
为了定位问题或者debug的方便,开发经常会在日志中添加一个调试信息。因为追求方便的缘故,可能写出这样的代码(将User直接打印,而不是user.username):
logger.info(“为用户$ {user}更新电子邮件);
一些程序语言,比如Java、Javascript,如果将一个对象直接进行打印,它其实是打印 toString方法返回的字符串,这样我们可以重写对象的toString方法来避免打印对象时出现的个人信息泄漏问题。
class UserAccount {
id:string
username:string
passwordHash:string
firstName:string
lastName:string
…
public toString(){
return “UserAccount (${this.id})”;
}
如果开发人员实在“作死”的话,比如直接打印对象的字段就没有办法了,例如: logger.info("The user's details are: ${user.firstName} ${user.lastName}");
为了日志方便查看,我们常常将日志以Json字符串的形式上传到日志服务器,这样我们查看日志可以清晰看到键值对结构。 我们可以在应用的日志输出中,遍历所有键值对信息,如果“键”存在firstName这样的字段,或者“值”中能匹配到Email,那么将对应的值替换成“
Blacklist = [“firstName”, “lastName”]
EmailRegex = r”.+@.+”;
class Logger {
log(details: Map<string,string>) {
const cleanedDetails = details.map( (key, value) => {
if (Blacklist.contains(key) || EmailRegex.match(value)) {
return (key, “
}
return (key, value);
}
console.log(JSON.stringify(cleanedDetails));
}
}
Code Review是开发过程中可以保证代码质量的部分,比如在Code Review中常常会被别人指出程序漏洞、健壮性问题、改进建议等等。将日志代码的检查作为Code Review中各个成员关注的一部分。这个方面不是技术层面,而是团队Code Review流程上的改善。 如果使用的是Pull Request Template 来进行合并代码,则可能需要在模板中设置一个复选框,提示reviewer进行检查。
虽说目前大部分公司的实践,并没有把个人隐私泄露测试纳入测试或者QA人员的工作范围,但是这部分的工作不仅需要测试来做,而且甚至可以自动化。 比如一个用户注册的场景,测试人员可以模仿用户在Web前端表单填写姓名、Email后,检查服务器日志中是否含有这些信息。而这部分工作可以使用Selenium、Cypress等端到端测试工具,然后调用日志服务器的API来搜索这些信息是否存在,来实现自动化。 自动化的个人隐私泄露测试也可以将其纳入到CI/CD持续集成流水线中。
在我们的项目中,一般存在两种日志收集方式
日志收集工具是日志到达日志中心的必经之地,在这个关口做好信息屏蔽,就可以对来自所有服务(多个微服务的情况下)的日志做集中式的处理。Datadog Agent直接提供了屏蔽隐私数据的配置,而AWS Lambda的代码则是我们可控的,可以自己实现代码层面的正则替换。
即使有了上面的实践,我们依旧不能保证个人隐私绝对不会出现在日志中,一方面我们可以在平时Debug、查看应用日志时有意识地检查有没有含有隐私信息,另一方面我们还是可以通过一些技术手段将这一检测工作自动化,并通过告警系统通知到团队成员进行处理。 在监控系统配置Email告警 这已经在笔者所在的团队中得到实践。我们使用Datadog作为日志、监控系统,成功实现在日志中出现Email信息时,Datadog能自动发送邮件通知。但是需要指出的一点是,因为Email可以很好地通过正则表达式进行匹配,同时被很多日志系统所支持。但是对于姓名这些信息,可能只能交给人工智能了。
PII Protection 从上面的阐述中可以看到,个人隐私信息的保护,已经不是请一个安全专家就能简单解决的问题,也不是单独的某个角色的工作,而是需要整个团队各个角色的通力合作。这就是DevSecOps理念。
]]>Availability of ModSecurity 2.9.1
web服务器已有的日志功能已经足够进行访问请求分析,但是就web的应用分析还有些不足,特别是大多情况下没办法记录下请求体。你的对手很清楚这 一点,所以很多时候的攻击是通过POST请求产生,并导致您的系统失明。ModSecurity充分的获取HTTP交互中的所以内容,并记录完整的请求和 响应。其日志功能可以允许您更细致的做出判断究竟什么是登录的时候,并确保相关的数据都被记录下来。一些请求和响应中的某些关键字段可能包含敏感数 据,ModSecurity可以被配置成在记录这些审计日志前隐藏它。
除了提供记录日志功能外,ModSecurity还能实时的监控HTTP的流量以检测攻击。在某些时候,ModSecurity做为一个WEB入侵检测工具,可以让你对发生在WEB系统上的一些可疑事件做出响应。
ModSecurity能够立即针对你的WEB应用系统进行攻击防御,有三种通用的方法: 1、消极(negative)安全模型:消极安全模型监控那些异常的、不常用的和通用的WEB攻击类请求。它统计每个请求的有关IP地址、应该连接、和用户帐户的异常分数,当出现较高的异常分数时,会记录日志并完全的阻止访问。 2、积极安全模开型:部署积极安全模型后,只有那些明确的请求被允许通过,其它的一律禁止。这个模式要求你对需要保护的WEB应用要非常的了解。因此积极安全模式最好是用于那种大量访问却很少更新的系统,这样才能使这种模型的维护工作量降到最低。 3、已知漏洞攻击:其规则语言使ModSecurity成为一个理想的外部修补工具,外部修补(有时是指虚拟修补)可以减少机会之窗。一些 组织修补这些应用的漏洞通常需要几周的时间,使用ModSecurity,应用系统可以从外部修补,根本不用改应用的源码(甚至时不用去管它),可以保证 你的系统安全直到有一个合适的补丁来应用到系统中。
灵活的规则引擎是ModSecurity的核心,其实现了ModSecurity的规则语言,这是一个专用的程序语言设计的用于处理HTTP的传输 数据。ModSecurity规则语言被设计的简单易用,非常的灵活:通用的操作是简单的,而复杂的操作也是可以实现的。经过认证的 ModSecurity规则,放在ModSecurity中,包含了一整套规则,它实现了通用目的强化、协议正规化和对一些通用web应用安全问题的检 测。大量评论认为,这些规则可以用于学习研究使用。
ModSecurity是一个可嵌入式的WEB应用防火墙,意思就是它可以做为以apache为基础的已经提供WEB服务的WEB服务器的一部分。这样的部署译意风一些特殊的优势: 1、不改变已有的网络结构。只需要花几分钟就可以为你的WEB服务器添加ModSecurity,而且由于它默认被设计为完全的被动方式,你可以自由的逐步部署并且只使用你需要的特性。同样也可以根据你的需要轻松的删除或停用它。 2、不存在单点故障。与网络设备部署方式不同,你不会给你的系统带来新的故障点。 3、绝对支持负载均衡。因为它以嵌入方式运行在WEB服务器上,ModSecurity会自动的利用附加的负载均衡特性。你不需要考虑负载均衡,除非你的系统本来就需要它。 4、极少开销。因为它在WEB服务器进程内工作,不会带来网络间接通信的负载,而且只进行最小的分析和数据交换开销。 5、加密或压缩内容没问题。许多IDS系统分析SSL流量的时候很困难,但对于ModSecurity没有麻烦,因为它工作于已解密和解压的数据环节。
在基于apache的反向代理模式上ModSecurity同样能工作的很好,我们很多客户选择这样做。在这种情形下,装了ModSecurity的可以保护任一一种WEB服务器(即使它不是apache的)。
FROM wordpress:4.9.8-php5.6-apache
RUN apt-get update && apt-get install -y libapache2-mod-security2
RUN a2enmod security2
SecRuleEngine On
<VirtualHost *:80>
ServerName baiyuan.wang
ServerAdmin blog@mail.baiyuan.wang
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log cdn_combined
curl ‘https://baiyuan.wang/?q=">‘
curl “https://baiyuan.wang/?q='1 OR 1=1”
Adds Zipkin tracing support for the axios JS HTTP client library. It supports all features of axios
.
npm install zipkin-js-instrumentation-axios –save
You need to use wrapAxios
fucntion to wrap the native axios
instance, and the axios
instance’s type/functions/attributes are not affected. As a result, you can use zipkinAxios
the same as axios
For example:
1 |
|
You can intercept requests or responses before they are handled by then or catch.
1 |
|
1 |
|
健康检查(Health Check)可用于服务运行的状态监控,比如腾讯旗下的DNSPOD的D监控,要求配置一个访问路径以判断网站是否可以正常访问实际上就是一个健康检查,当发现健康检查失败时会发送一个邮件通知或者短信来告知网站管理员进行维修。
而在现代一些分布式系统中,用户访问不再是单台主机,而是一个由成百上千台实例组成的集群,用户请求通过负载均衡器分发到不同的实例,负载均衡帮助解决单台服务器的访问压力,同时提高了系统的高可用性,而健康检查常常作为当前实例是否“可用”的判断标准。即:当系统发现某台实例健康检查不通过,负载均衡器将不会把流量导向该实例。 现在的云服务厂商比如AWS一般都为负载均衡配备了健康检查,而Kubernetes提供了两种探针来检查容器的状态,Liveliness和Readiness,根据官方文档,Liveliness探针是为了查看容器是否正在运行,翻译为存活探针(livenessProbe),Readiness探针是为了查看容器是否准备好接受HTTP请求,翻译为就绪探针(readinessProbe)。 在Kubernetes中,Pod是Kubernetes创建及管理的最小的可部署的计算单元,一个Pod由一个或者多个容器(Docker,rocket等等)组成,这些容器共享内存,网络以及运行容器的方式。 在Kubernetes上下文中存活探针和就绪探针被称作健康检查。这些容器探针是一些周期性运行的小进程,这些探针返回的结果(成功,失败或者未知)反映了容器在Kubernetes的状态。基于这些结果,Kubernetes会判断如何处理每个容器,以保证弹性,高可用性和更长的正常运行时间。
就绪探针旨在让Kubernetes知道你的应用是否准备好为请求提供服务。Kubernetes只有在就绪探针通过才会把流量转发到Pod。如果就绪探针检测失败,Kubernetes将停止向该容器发送流量,直到它通过。
Liveness探测器是让Kubernetes知道你的应用是否活着。如果你的应用还活着,那么Kubernetes就让它继续存在。如果你的应用程序已经死了,Kubernetes将移除Pod并重新启动一个来替换它。
让我们看看两个场景,来看看就绪探针和存活探针怎样帮助我们构建更高可用的的系统。
一个应用往往需要一段时间来预热和启动,比如一个后端项目的启动需要连接数据库执行数据库迁移等等,一个Spring项目的启动也需要依赖Java虚拟机。即使该过程已启动,您的服务在启动并运行之前也无法运行。应用在完全就绪之前不应接收流量,但默认情况下,Kubernetes会在容器内的进程启动后立即开始发送流量。通过就绪探针探测,直到应用程序完全启动,然后才允许将流量发送到新副本。
就绪探针的工作过程
让我们想象另一种情况,当我们的应用在成功启动以后因为一些原因“宕机”,或者遇到死锁情况,导致它无法响应用户请求。 在默认情况下,Kubernetes会继续向Pod发送请求,通过使用存活探针来检测,当发现服务不能在限定时间内处理请求(请求错误或者超时),就会重新启动有问题的pod。
存活探针的工作过程
探针类型是指通过何种方式来进行健康检查,K8S有三种类型的探测:HTTP,Command和TCP。 HTTP HTTP探测可能是最常见的探针类型。即使应用不是HTTP服务,也可以创建一个轻量级HTTP服务器来响应探测。比如让Kubernetes通过HTTP访问一个URL,如果返回码在200到300范围内,就将应用程序标记为健康状态,否则它被标记为不健康。 更多关于HTTP探测可参考这里。 命令 对于命令探测,是指Kubernetes在容器内运行命令。如果命令以退出代码0返回,则容器将标记为正常。否则,它被标记为不健康。 更多关于命令探测可参考这里。 TCP 最后一种类型的探测是TCP探测,Kubernetes尝试在指定端口上建立TCP连接。如果它可以建立连接,容器被认为是健康的; 如果它不能被认为是不健康的。这常用于对gRPC或FTP服务的探测。 更多关于TCP探测可参考这里。
我们可以配置K8S健康检查运行的频率,检查成功或失败的条件,以及响应的超时时间。可参考有关配置探针的文档。 存活探针探测失败会导致pod重新启动,所以配置初始探测延迟 initialDelaySeconds十分重要,要确保在应用准备之后探针才启动。否则,应用将无限重启! 我建议使用p99启动时间作为initialDelaySeconds,或者取平均启动时间外加一个buffer。同时根据应用程序的启动时间更新这个值。
以下面的一个K8S的配置代码为例,
apiVersion: apps/v1beta1
kind: Deployment
…
…
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 120
timeoutSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 10
periodSeconds: 5
参考资料
]]>飞机降落前的旧金山,青山如黛,江山如画,山在飘渺虚无间。
山在缥缈虚无间
鸟瞰下鳞次栉比的街道,就像美国地图一样,被切分得规规整整。
在旧金山的街头闲逛,没有想象中的繁华,与国内的街道对比,最大的感受就是人太少,仿佛城市的背景噪音都小了几分贝,不自觉地从内心里感觉到静谧。
估摸着旧金山的城市基建不像中国一样都是近几年修建,或者翻新重建的,整体上没有中国感觉那么新,道路坑洼不平,低低的电线也是缠绕在城市美国角落,不过建筑充满历史和艺术感,不像国内建筑千篇一律。
陈旧而意蕴十足的街头
一进入旧金山的唐人街,熟悉感和亲切感是由内而外的。走进商店,和中国的小卖部别无二致,有各色蔬菜的菜市场,有红枣、枸杞、花生干活铺,空气中都是“中国”的味道。这是一个神奇的感觉,仿佛在异国中一处家的孤岛。路上的广告牌看起来和香港街道一样,显然不是大陆的style,店主听口音大多数上台湾人和香港人。
国内品牌的方便面
在“南京小馆”吃了第一顿饱饭
金门大桥
]]>AWS CloudFormation 云资源服务可使开发人员和系统管理员轻松有序地创建、管理和更新相关 AWS 云资源模板。使用 AWS 模板或创建您自己的云资源。
为了最快地对Cloudformation在心中稍微有一个概念,我为大家提炼出下面的关键词:
联想起自己以前使用云服务的体验,初学者首先应该能想到,我是不是可以通过Cloudformation来申请主机、创建Loadbalance呢,而不是手动在AWS的控制台(Console)上“点点点”呢?
是的。如果说Cloudformation仅仅使可视化的界面操作代码化,这或许只能激起代码爱好者的兴趣,我们知道代码化的许多好处,比如效率更高、可被机器自动执行。
然而因为不是所有人都喜欢代码。对于患“代码恐惧症”的人,或者习惯在界面上配置的专业运维人员来说,界面操作显然比代码更直观、拥有更友好的操作引导。
对于追求高效的工程实践来说,代码化还是界面操作并非“萝卜青菜,各有所爱”的兴趣爱好问题,而是生产力问题。
这年头流行一个词语,叫DevOps,维基百科的定义如下:
DevOps(Development和Operations的组合词)是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。
而我个人的理解就是,DevOps要求能把运维纳入软件开发体系,比如运用敏捷等软件开发方法、实现自动化持续集成、测试、交付、可被代码管理等等;其次,要求开发具备运维意识,将交付基础设施作为交付软件的一部分,借助云计算给开发人员赋能从而达到端到端交付的全栈能力,从而极大地提高研发效能。
在这个意义上,Cloudformation就是这样一个工具,可以将基础设施作为代码纳入到软件开发的一部分,并运用软件工程方法管理基础架构。
在笔者所在的团队里,能写DevOps的代码已经作为开发的标配技能。在新起一个Codebase的时候,我们往往需要考虑一下内容:
一个项目的文件结构截图
在一些大型的互联网公司来说,运维和开发的职权是完全分离的,开发可能仅仅只用关注业务代码部分。
实际上在一个项目启动后的前期,首先介入的可能是运维人员搭建出一套线上的开发环境(Dev)、测试环境(QA)、生产环境(Prod)用于开发的日常调试、测试人员测试以及发布项目;如果使用持续集成的方式的话,还要搭建持续集成(CI/CD)流水线。
借助AWS平台及其提供的Cloudformation(或者第三方工具如terraform),使用基础设施代码,我们就可以快速地构建一套完整的云环境。同时使用同样一套基础设施代码,再配置不同的环境变量,就可以快速地复制出不同的线上环境。
以Cloudformation为例:
编写Cloudformation代码,比如编写通过以下的代码,通过AWS提供的aws cloudformation命令,就可以实现在AWS创建一台可以弹性伸缩的实例。
ECSAutoScalingGroup:
Type: ‘AWS::AutoScaling::AutoScalingGroup’
Properties:
VPCZoneIdentifier: !Ref InstanceSubnetIds
LaunchConfigurationName: !Ref ContainerInstances
MinSize: ‘1’
MaxSize: !Ref MaxSize
DesiredCapacity: !Ref DesiredCapacity
ContainerInstances:
Type: ‘AWS::AutoScaling::LaunchConfiguration’
Properties:
ImageId: !FindInMap
- AWSRegionToAMI
- !Ref ‘AWS::Region’
- AMIID
InstanceType: !Ref InstanceType
IamInstanceProfile: !Ref EC2InstanceProfile
KeyName: !Ref KeyName
同时我们可以使用AWS Cloudformation designer可视化地设计我们的基础架构及其之前的拓扑结构和关联关系,并导出代码。
AWS Cloudformation designer
代码化后的基础设施,意味着可被git等版本控制工具进行管理,这意味着对基础设施的任何改动,比如更改服务器内存、更换可用区、修改域名、安全组等等,都可以通过review代码的方式进行跟踪。
而在过去,在控制台界面上进行的误操作导致线上环境出问题很难被记录下来,同时如果因为修改服务器环境配置导致的问题,可以很快通过git回滚代码的方式进行快速回滚服务器环境。
在Cloudformation之前,AWS已经推出了命令行工具或者SDK来管理AWS的资源,与它们“命令式”操作不同的是,Cloudformation提供一种“声明式”的特性。
“命令式”操作希望你去一步步编写程序以达到最终所期望的状态,而声明式只关心你想要的资源与资源的状态,Cloudformation会自动分析达到想要的状态需要进行怎样的操作。(理解声明式和命令式的不同,可参考《声明式编程和命令式编程的比较》)
Cloudformation的声明式操作为其提供了幂等性的特性,以为我们在任何时刻、任何版本的基础架构运行新的Cloudformation代码,AWS每次都会帮助你生成相同的基础架构。
大部分人在数据库事务中了解到“原子性”。Cloudformation同样支持一种原子操作,要么成功,要么失败。失败的话可以自动回滚,而在一次失败操作中产生的资源会在回滚时被自动删除掉。
“基础设施即代码”的代码化、自动化,为CI/CD流水线管理基础设施提供了可能。
一种理想的方式是,和修改业务代码一样,当我们要对基础设施进行变更时,通过修改基础设施的代码,提交到git仓库,触发CI/CD流水线运行基础设施代码,然后达到更新基础设施的目的。
持续集成
同时根据云计算服务商提供的反馈,更新CI/CD流水线的部署状态:红则失败绿则失败。
QA或者团队可以根据测试结果和发布计划通过流水线,将基础设施和业务代码一起推向下一阶段。
GoCD持续集成流水线
2018年 ThoughtWorks 技术雷达19期为我们介绍了一款名为LocalStack的云服务的Mock框架,这意味着我们可以在本地调试基础设施代码,或者为基础架构代码做单元或集成测试。原文如下:
]]>使用云服务时面对的一个挑战是如何在本地进行开发和测试。 LOCALSTACK 为 AWS 解决了这个问题。它提供了各种 AWS 服务的本地 测试替身 实现,包括 S3 、 Kinesis 、Dynamodb 和 Lambda 等。它基于现有的最佳工具如Kinesalite 、 Dynalite 、Moto 等构建,并增加了进程隔离与错误注入的功能。 LocalStack 的使用很简单,并附带了一个简单的 JUnit 运行器以及 JUnit 5扩展。我们在一些项目中使用过 LocalStack ,并对它印象深刻。
而对于喷子,不能太在意了,有些想法如果是自己某一时刻的真知灼见,大可写下来,因为它在这一时刻的语境下是对的,如果假以时日这些想法的确是错的,也无需菲薄自己,因为这是一种成长与完善自己想法的过程。如果过分在意别人的评论,就什么也不敢说了,就永远缺乏自信了。 跟很多人深入交流以后发现,同龄人一般心智差不多,首先不要想自己把别人强很多,然后先认为别人比自己聪明,如果认为别人说错的,那看看别人有没有可取的点。带着学习的态度,而不是上来就喷的态度。那要不要纠正呢,要但不强势。纠正之前首先确定自己真的理解别人的意思,这样可能更耐心平和一点。 当然了,判定喷子的标准是对方是不是带着探讨问题的善意,这种善意遵循 “倾听-反馈-融合” 的过程,先倾听对方的观点,拉齐双方进入相同的上下文,这个时候结合自己的生活阅历或者知识储备发表自己的观点,不天马行空,扩大scope。不是抱着一股求胜心来企图“碾压”对方,而是带着“求知心”,或者是“Develop others”的心态。 所以,现在不喜欢在朋友圈发表一些观点性的文字,因为很难在朋友圈短小的文字下介绍一个完整的上下文。
]]>