1. Record architecture decisions

Date: 30/03/2017

Status

Accepted

Context

We need to record the architectural decisions made on this project.

Decision

We will use Architecture Decision Records, as described by Michael Nygard in this article: http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions

Architecture Decisions, what are they ?: http://reasonofone.blogspot.hk/2014/08/architecture-decisions-what-are-they.html

Architecture Decisions: Demystifying Architecture: https://www.utdallas.edu/~chung/SA/zz-Impreso-architecture_decisions-tyree-05.pdf

We will use adr-tools, as this file generated by https://github.com/npryce/adr-tools

Consequences

See Michael Nygard's article, linked above.

2. 团队中的密码管理

Date: 30/03/2017

Status

Accepted

Context

视频链接:http://v.youku.com/v_show/id_XMjk5ODQ3NzA3Mg==.html

我们工作中使用的密码有 100 + 左右,其中包括 服务器,数据库,测试账号,第三方服务(阿里云,Dnspod,邮箱,等)

我们所有的密码都是分散的管理着,每个人都自行对这 20 到 100 个密码进行管理,并且方式各种各样。

当前存在以下问题:

  1. 使用容易记但很不安全的密码,例如,123456
  2. 在每一个服务上使用相同的非安全密码;
  3. 当团队成员有变动时,密码没有改变;
  4. 无加密的将密码存在本地文件或是云笔记中;
  5. 通过邮箱或是微信进行密码分享。

我们需要一个工具管理我们的密码,包括如下功能:

  1. 生成复杂密码;
  2. 密码被加密后保存;
  3. 分发方式安全;
  4. 使用起来简单方便;
  5. 支持团队分组管理。

Refs:

How to Manage Passwords in a Team: https://medium.com/altcademy/how-to-manage-passwords-in-a-team-3ed010bc3ccf

Decision

LastPass(功能繁杂,使用人数最多,费用 1450 USD / 50 users) PassPack(功能简单,小众,不支持附件,费用 144 USD / 80 users) 1Password(功能简单,使用方式友好,费用 7200 USD / 50 users)

Consequences

  1. 需要考虑兼容性,Win/Mac/Linux;
  2. 费用需要考虑;
  3. 需要可存储附件(比如,ssh 公私钥)。

3. 使用 ssh key 替换用户名密码方式登录

Date: 30/03/2017

Status

Accepted

Context

当前我们使用的是用户名、密码方式进行服务器登录,存在以下问题

  1. 安全性问题,密码面临被破解的风险;
  2. 易用性问题,无法使用 config 记录密码,可以使用第三方软件解决,如,SecureCRT,ZOC7;
  3. 无法充分使用 local terminal,如 iTerm2;

Refs:

Decision

禁用用户名、密码登录,使用 ssh key 进行登录

Consequences

  1. 团队成员使用新方式需要适应;
  2. key 的管理需要统一(需要引入堡垒机)。

4. Ubuntu vs CentOS

Date: 30/03/2017

Status

Accepted

Context

我们的服务器用的都是阿里云的 ECS,历史原因多数操作系统是 centos(6.5),内部得到的信息是稳定,当然目前阿里云已不提供此版本镜像。

从个人使用经历看,自 10 年那会起,我所经历的公司选用的都是 ubuntu,当然我们应该知其所以然,而不是盲目的接受。

  1. centos 系统初始化,软件安装基本都走的是手动编译,效率低,但这点往往被大家作为稳定性的佐证;
  2. centos 系统之上的所有软件都过于老旧,想装第三方软件的最新稳定版,很困难(难道一定要手动编译才好);
  3. 从开始使用 ubuntu,就看重他的简单易用,当然当时业界的看法是,不专业,不稳定;
  4. ubuntu 的社区比 centos 更活跃,网上的资料也比 centos 多;
  5. ubuntu 相关的问题很容易在网上找到解决方案,但 centos 不是。

这些都是自己的使用体验

从主机市场分析数据(https://www.digitalocean.com/community/questions/whats-is-better-and-why-linux-centos-or-ubuntu?answer=30514)看,Simple Hosting, VPS, Dedicated & Clouds 这三个领域,centos 占有率均低于 ubuntu。尤其在云市场,centos 只有 ubuntu 的 1/20(http://thecloudmarket.com/stats#/by_platform_definition)。

更多的对比信息请查看:

Decision

逐步将系统从 centos 切换至 ubuntu,保证系统的一致,为后期的运维自动化做准备。

Consequences

5. 使用 Slack 替代微信、邮件和短信

Date: 31/03/2017

Status

Accepted

Context

  1. 当前使用微信、邮件作为工作中的沟通工具,工作与生活混合;
  2. 各种告警信息需分别查看邮件、短信或是监控平台;
  3. 随着任务管理平台、Bug 管理平台、wiki、文档分享网站、聊天工具的流行,邮件的使用场景越来越小,并且邮件信息并不及时;
  4. 通过微信进行沟通,新人无法了解历史信息,经常需要把发过的内容重复发送。

Decision

Slack 支持的功能

  1. 公开与私有的频道,频道可随时加入或退出
    1. 按项目,project-crm, project-sms, 等;
    2. 按技术话题,tech-restful-api, tech-queue 等;
    3. 可以邀请相关人,任何人也可随意加入。
  2. 消息支持表情快速回复
    1. 表情是附加在消息上的,上下文内聚很高。
  3. 消息支持收藏;
    1. 一些不错的重要信息,可以收藏起来随时查看。
  4. 支持各种文件的分享;
    1. pdf 等文件均可预览。
  5. 分享的链接支持预览
    1. 分享的链接,不用点开也知道大体内容。
  6. 搜索功能强大,可通过快捷方式,搜索同事,消息记录,文件等;
  7. 多种程序代码支持高亮;
    1. 代码高亮预览,自动折叠,不影响整体效果。
  8. 强大的第三方集成,将所有消息、通知汇聚在一处,例如,Trello,Github,NewRelic, Sentry,Jenkins 等;
  9. 新加入者可查看群组历史信息
    1. 信息再也不用重复发了。
  10. 开放性非常好
    1. 任何人都可以方便申请开发者 KEY,建立自己的机器人。

研发部频道设计

  1. CI/CD - 用于接收测试、部署结果,测试覆盖率,PR 等信息,也用于发起测试、部署等;
  2. NewRelic - 用于接收应用性能报警等信息;
  3. Sentry - 线上实时错误信息,可根据项目单独拆出来;
  4. Team-X - 用于组内沟通,一个 Scrum 组,包括研发,产品及测试;
  5. Knowledge - 用于所有研发人员进行沟通与分享;
  6. Product - 用于跨 Team 的产品进行沟通与分享;
  7. Backend - 用于所有后端人员进行问题咨询及分享;
  8. Leads(私密) - 用于所有 leader 进行沟通、安排周会等;
  9. Frontend, UI, Mobile, Devops, QA etc

我们用了如下第三方软件

Slack vs BearyChat

最终,我们给国产软件(服务)一个机会。

经过两周的 BearyChat 试用,目前已准备转用 Slack - 2017-08-03

  1. BearyChat 的 Teambition 集成失败,沟通后说是 TB 的接口调整;
  2. 集成数的限制,都是 10 个,但同一个第三方多次集成,Slack 算一个,BearyChat 算多个,比如 Github, Sentry;
  3. 经过对各个国内公司的了解,发现使用中 Slack 的国内稳定性还好。

Consequences

6. 使用 Git 替换 SVN

Date: 06/04/2017

Status

Accepted

Context

当前我们用的是 SVN 做代码、产品文档、UI 设计图的管理 我们在此只说其中的代码管理

  1. 代码仓库过大,新分支,新Tag 代码都是一份完整的拷贝;
  2. 必须联网提交;
  3. SVN 代码合并方式低效,目前使用 Beyond Compare 做代码合并,分支使用方式落后;
  4. 无法很好的做 code review(只能 patch 或第三方工具);
  5. 面试者看到是这么落后,以此类别其他技术栈,综合理解就是,我能学到啥。

Decision

使用全球最流行的分布式管理工具 Git 及平台 Github 分布式,目录结构简单,代码无冗余,可 review with PR。

Refs:

https://help.github.com/articles/what-are-the-differences-between-subversion-and-git/ http://stackoverflow.com/questions/871/why-is-git-better-than-subversion

Consequences

方式一:

git svn clone svn project url -T trunk 将 SVN 项目的 trunk 转为 git 项目的 master,仅保留了 trunk 分支的提交记录,此方式适用于所有代码都规整到了一个分支,并且不需要其他分支的提交记录。

方式二:

使用命令 https://github.com/nirvdrum/svn2git ,它可以保留所有branch, tags,以及所有分支的提交历史。 svn2git http://svn.example.com/path/to/repo --trunk trunk --tags tag --branches branch git push --all origin git push --tags

use --revision number to reduce the commit history.

目前生产环境使用的 centos 版本过低,导致 git 也无法升级的处理方法: yum install http://opensource.wandisco.com/centos/6/git/x86_64/wandisco-git-release-6-1.noarch.rpm yum update git

Refs: https://www.atlassian.com/git/tutorials/migrating-overview https://www.atlassian.com/git/tutorials/svn-to-git-prepping-your-team-migration https://www.git-tower.com/learn/git/ebook/cn/command-line/appendix/from-subversion-to-git https://tecadmin.net/how-to-upgrade-git-version-1-7-10-on-centos-6/

7. 使用 PassPack 进行密码管理

Date: 07/04/2017

Status

Accepted

Context

step zero: 管理员以 company-name 注册付费管理员账号

step one: 公司成员在 https://www.passpack.com/ 使用公司邮箱注册免费账号,用户名统一:company-name-username

step two: 点击 People tab 激活自己的账号,使用相同的用户名

step three: 邀请管理员账号

step four: 创建或导入自己管理的所有用户名密码,批量 transfer 给 company-name(个人密码也可以用它管理,就无需 transfer 给管理员了)

PassPack 使用介绍:https://www.passpack.com/getting-started/PasspackGettingStarted_Admin_EN.pdf

Decision

鉴于管理员一人处理非常琐碎,下放权限至 group owner 可编辑相应密码

用户名命名规范:公司名简称-姓名 Name 命名规范:简单描述所用的服务,用于搜索,层级间使用冒号:隔开,例如,aliyun:oss:contract-doc:ak

Consequences

  1. 推动各个 team leader 使用并管理;
  2. 向 team member 分发密码走这个渠道;
  3. 列入新人入职注册项。

8. 使用堡垒机加强服务器安全

Date: 08/04/2017

Status

Accepted

Context

  1. 旧文写了为了禁止外网暴力破解,我们将用户名、密码登录方式改为了 ssh key 登录;
  2. 为了管理方便,当时只区分了生产 key 和测试 key,内部大家共用 key, 并且有 sudo 权限,导致存在内部风险,而针对每个人手动生成 ssh key 又会有管理上的问题;
  3. 每个人需要对所有机器配置 ssh config,使用不方便;
  4. 当前成员行为无法区分,有成员在系统上执行了 vim 大文件操作导致系统负载急剧升高,也有成员将系统配置改动导致其他项目运行出现问题,当然也存在数据安全上的问题。

未使用堡垒机时:

Decision

目标:简化并统一管理线上服务器的登录并审计操作日志

结构图:

Refs:

经过多个堡垒机项目的调研,我们最终选择了 https://github.com/jumpserver/jumpserver,源于使用人数多,并且项目活跃度高。

Jumpserver v0.4.0 新版本预览 https://github.com/jumpserver/jumpserver/issues/350

Consequences

9. 结构化 Django 项目

Date: 10/04/2017

Status

Accepted

Context

We have 20+ projects which based on Django, but we don’t have the same structure which make our project hard to read.

We need handle those things:

  1. store all apps in one place;
  2. support different environments;
  3. store project related configs.

Decision

make a skeleton for django: https://github.com/huifenqi/django-project-skeleton

Consequences

  1. make all new projects with this skeleton;
  2. apply this structure to the old projects when refactoring.

10. Git 基础及风格指南

Date: 11/04/2017

Status

Accepted

Context

We use git and GitHub in different ways, it’s emergency to teach team members with the git basics and style guide we will use.

Decision

git basics

  1. setup ssh keys (https://github.com/settings/keys) or GUI;
  2. clone repo into local system: git clone git@github.com:huifenqi/django-project-skeleton.git;
  3. files store in three stages:
    1. Working Directory: holds the actual files;
    2. Index: staging area which store your changed files;
    3. HEAD: points to the last commit you've made.
  4. Working Directory -> Index: git add <filename> or git add *;
  5. Index -> HEAD: git commit -m "Commit message";
  6. push updates to Github: git push origin master;
  7. create branch: git checkout -b feature/x
  8. push branch to Github and others can see: git push origin <branch_name>;
  9. sync local with Github: git pull;
  10. make a new tag: git tag <tag_name>;
  11. show history: git log;
  12. show changes: git diff <previous_commit_hash>;
  13. others: git status, git branch, git tag, etc.

git style guide

Branches

Commits

Messages

Refs

Consequences

Make sure everyone follow the guide, check the Github feeds often.

11. Apply Github workflow to our team

Date: 11/04/2017

Status

Accepted

Context

Context here...

Decision

Github workflow

There are various workflows and each one has its strengths and weaknesses. Whether a workflow fits your case, depends on the team, the project and your development procedures.

TBD

Consequences

TBD

12. Think about micro service

Date: 13/04/2017

Status

Proposed

Context

  1. 已拆分为各种服务(1. 接口不统一、无监控、无统一日志管理);
  2. API 文档管理不够;
  3. 服务的部署纯手动;

Decision

Decision here...

Consequences

  1. 跨语言;
  2. 提供 server 和 client 端;
  3. 统一上线过程:独立部署、运行、升级;
  4. 统一日志记录与审计:第三方日志服务;
  5. 统一风格:REST API or RPC;
  6. 统一认证和鉴权:权限管理、安全策略;
  7. 统一服务测试:调度方式、访问入口;
  8. 资源管理(虚拟机、物理机、网络);
  9. 负载均衡:客户端 or 服务端;
  10. 注册发现:中心话注册 or hardcode;
  11. 监控告警;

Refs

Why

How

服务注册

REST API vs RPC

13. The sense of Done

Date: 14/04/2017

Status

Proposed

Context

We face this situation very often?

  1. I’m already fixed this, it’s in testing;
  2. I’m already add this monitoring plugin in the repo, it’s deployed on staging, let’s watch for a few days, then deploy on production;
  3. I’m already move this service to new machine, I will clean the old machine later.

Sounds familiar? Yes, very often.

Is that all done? No, it doesn’t.

Decision

  1. talk this all the time, let team member have this sense;
  2. make specifications for each domain.

Consequences

Create specifications from our experience.

An Done-Done example with User Story:

  1. stories and AC reviewed (DEV);
  2. All AC implemented (DEV);
  3. Code reviewed (Dev);
  4. Tests passed (Dev+QA+CI);
  5. No bugs left (QA);
  6. Deployed to production (Dev);
  7. Accepted (PM).

Refs:

14. How to restart a server

Date: 19/04/2017

Status

Accepted

Context

Context here...

Decision

Decision here...

Consequences

Consequences here...

15. Case: How to find the root reason of high load

Date: 20/04/2017

Status

Accepted

Context

Context here...

Decision

Decision here...

Consequences

Consequences here...

16. 故障记录报告

Date: 20/04/2017

Status

Accepted

Context

事故频发,处理速度慢,流程不清晰,并且一直未做记录,导致同样的问题会再次复现。

Decision

定义故障报告模板:

  1. 对故障进行描述,以便查询并对我们的系统稳定性做评估;
  2. 对故障做分析,便于未来可快速处理同样问题;
  3. 加监控,以便问题出现前就做处理;
  4. 解决并升级方案,完全避免此类问题。

Consequences

故障报告模板

问题描述

时间线

原因分析

做错的事情

做对的事情

将来如何避免此类事情再次发生

Refs: Outage Post-Mortem – June 14 https://www.pagerduty.com/blog/outage-post-mortem-june-14/ How to write an Incident Report / Postmortem https://sysadmincasts.com/episodes/20-how-to-write-an-incident-report-postmortem

17. Incident classify and recovery

Date: 20/04/2017

Status

Proposed

Context

对服务的可用等级不清晰,处理问题的优先级不足,临时分析现象及寻找解决方案,耗时过长

Decision

划分服务等级,做好预案

Consequences

故障分类和预案

自然灾害

  1. 灾备,常见的有,两地三中心;

服务运营商

DNS 解析问题

  1. 联系 DNS 提供商,确定故障恢复的时间;
  2. 所需恢复时间大于 TTL,实施切换 DNS 运营商的预案(DnsPod to Aliyun etc)。

Aliyun 内部问题

CDN 运营商

外部人为

人为攻击

正常请求

架构内部

数据库级故障

内部网络级别

系统级别故障

系统服务级别故障

应用服务级别故障

应用虚拟环境级别故障

应用代码级别故障

18. iOS 持续发布

Date: 20/04/2017

Status

Accepted

Context

目前使用测试同学的工作电脑做 iOS 的打包与分发,对测试同学有一定的影响。

Decision

基本流程不变,将打包工作交由第三方服务 buddybuild 处理并分发。 buddybuild 本可以直接分发,但由于其分发网络在国内比较慢,所以继续使用 fir.im 进行分发

Consequences

免费版单次打包过程限制在 20 分钟内,无限次打包但会优先打包付费用的的应用,未来长期的高频次使用需要购买付费版。

Refs:

Custom Build Steps: http://docs.buddybuild.com/docs/custom-prebuild-and-postbuild-steps

19. 服务器申请与升级 - 容量评估

Date: 21/04/2017

Status

Accepted

Context

  1. 部分机器的 CPU,内存,硬盘,使用率均在 90% 左右,另一些机器各项指标使用率在 1% 左右;
  2. 部分机器的 CPU,内存,硬盘搭配不合理,CPU 使用率 1%,但内存使用率在 90% 左右;
  3. 一些对磁盘读写要求高的服务,使用的是普通云盘,比如,数据库,SVN等;
  4. 申请机器时,无法提出配置要求,基本靠拍脑门决定;
  5. 对服务的发展没有思考,配置要了 12 个月后才能使用到的配置。

Decision

  1. 压力测试;
  2. 分析业务各个指标的使用情况,CPU 密集型,内存密集型还是有其他的特点;
  3. 鉴于 Aliyun ECS 随时可以扩展,可以先用低配机器,根据使用情况,进行单指标垂直升级;
  4. 水平扩展,即提升了服务的处理能力又做了高可用;
  5. 对于不合理的内存使用,要分析自己程序中是否有内存泄漏或大数据加载。

Consequences

Refs:

20. 日志管理

Date: 24/04/2017

Status

Accepted

Context

  1. 记录位置混乱,有系统默认路径,有自定义路径;
  2. 日志未切割,日志一直记录在一个文件中,导致文件巨大;
  3. 记录方式有问题,一个请求量不大的服务,每日存在 1G 的日志数据,发现存在将整个 html 写入日志的问题;
  4. 日志记录格式不统一,涉及日志规范有 nginx, python 和 java;
  5. 不容易访问,需要给开发者提供对应机器的登录与日志查看权限;
  6. 使用 grep 等查询方式,上下文使用不方便,查询效率低;
  7. 临时的统计需要写一次性的脚本;
  8. 无法对异常日志做监控与报警;
  9. 归档日志太多,需要定时清理。

附加需求:

  1. 日志根据项目区分查看权限;
  2. 可报警;
  3. 可产生图表。

Decision

  1. 日志记录统一至 /data/logs 目录下,针对每个服务创建日志目录(包括系统服务),如 nginx;
  2. 所有需要文件切割的地方,我们使用系统的 /etc/logrotate.d, copy /etc/logrotate.d/nginx 的设置进行改动即可。(系统的默认切割时间各个系统不一致,所以我们删除 /etc/cron.daily/logrotate,改为在 /etc/crontab 加入 59 23 * * * root /usr/sbin/logrotate /etc/logrotate.conf(使用日志服务后无需此步));
  3. 针对大日志文件进行调研,查看日志内容的必要性;
  4. 规范化日志记录格式及内容,格式可以参考 Aliyun 采集方式及常见配置示例 https://help.aliyun.com/document_detail/28981.html
  5. 使用日志管理服务可用解决如上 5 - 9 的问题。

可选的日志服务:

  1. Aliyun 日志服务 (功能强大(复杂),价格计算复杂,阿里云体系内速度应该快些,服务提供没多久,可用性待验证,展示效果很一般,价格适中);
  2. sumologic (查询功能强大,上下文展示方便,费用高);
  3. logentries (使用很方便,展示效果很棒,上下文展示一般,费用高);

综合考虑后,选用 Aliyun 日志服务

整体功能强大

查询功能够用,虽然界面一般

Consequences

创建一个项目,然后创建各自服务的日志库,如下

未来需处理好 Aliyun 子账号与日志的权限关系

Refs:

21. Redis 使用按业务分离并上云

Date: 26/04/2017

Status

Accepted

Context

  1. redis server 部署在业务机上,业务与数据存储耦合;
  2. 所有业务的数据存在一个 db 0 中,业务之间很容易产生 key 冲突,业务扩展时需顾虑其他业务数据;
  3. 单点,业务中存有流程数据,风险比较大;
  4. 如果切到独立的机器,资源利用率不高。

Decision

  1. 将自建 redis server 迁移至 aliyun redis 中;
  2. 理清业务对 redis 的使用情况,按业务划分指定 redis db index 或 独立 redis 实例;
  3. aliyun redis 做了高可用;
  4. aliyun 目前可选的范围很多,最小实例是 256M,价格也合理。

使用 aliyun redis 后,额外得到一个数据管理与监控功能功能,如图

Consequences

  1. 针对缓存数据:

    我们无需做数据迁移,直接指定预分配的 aliyun redis db index 即可;

  2. 对于存在需要迁移的数据:

    测试 -> 更新连接配置 -> 暂停业务服务 -> 导入数据(AOF) -> 启动业务服务

    redis-cli -h xxx.redis.rds.aliyuncs.com -p 6379 -a xx--pipe < appendonly.aof

    1. 确保 AOF 已打开(未打开的话,需更新配置及使用 bgrewriteaof 手动触发);
    2. 当前使用的版本(3.0.4)不支持单 db 导入及 db swap,所以只能将整个 instance 数据导入(对于按 db index 区分的业务,需要注意数据不被搞乱)。

Refs:

22. 防 DDOS 攻击

Date: 28/04/2017

Status

Accepted

Context

  1. DDOS 导致业务全线中断,购买高防 IP ,仍然需要重启机器进行 IP 更换(nginx 和业务系统在同一台机器上);
  2. 入口过多,针对 DDOS,需要多线解决;
  3. 机器出问题,业务中断,单点;
  4. 新功能部署,业务中断,没有对多实例进行管理,每次都是杀掉所有,然后重新启动。

Decision

Consequences

需要保证所有业务机器都是无状态的,即无数据库(mysql, redis, mongodb 等服务在此机器运行),没有用这台机器存储业务用的文件等。

Refs:

23. 应用性能监控

Date: 28/04/2017

Status

Accepted

Context

  1. 服务 A,用户访问搜索页面时,请求了 10 分钟,还没返回内容;
  2. 服务 B,这个页面请求怎么有 1000 次数据库查询;
  3. 服务 C,这条数据库查询怎么耗时 5 分钟。
  4. 我们的这个项目做了半年,使用效果如何呀,哦,页面平均响应时间是 5 秒,啊!;
  5. 用户反馈平均需要需要 1 天以上的时间;
  6. 测试很难将所有边界场景测试到。

Decision

使用 Newrelic 对我们线上主要业务做性能监控,包括响应时长、吞吐量、错误率,慢查询,查询分析等,他可以做到实时定位和通知,并指出具体的代码行及 SQL 语句。

Consequences

Refs:

24. 实时错误跟踪

Date: 28/04/2017

Status

Accepted

Context

  1. 用户 A 使用功能 B,咦,出错了,怎么办呢,再试,还是不行,算了,不用了;又或者我报告一下吧,写邮件?;
  2. 项目 A 昨晚上线后出现了很多问题,怎么办呢,回滚还是赶快去解决呢?
  3. 用户发邮件说使用我们应用时出现错误了,错误描述不清晰,我们和用户沟通下吧,1小时过去了,还是重现不了,那我们去看看日志吧,翻出很久之前的日志,使用二分法慢慢查询,3 个小时过去了,终于定位出错误行啦,但是没有上下文及当时的环境变量等信息;
  4. 异常使用不规范。

Decision

使用 Sentry 对我们线上主要业务做异常监控。

规范化异常

使用异常的优势

  1. 隔离错误处理代码和常规代码;
  2. 在调用栈中向上传播错误;
  3. 归类和区分错误类型。

异常处理实践原则

  1. 使用异常,而不使用返回码;
  2. 利用运行时异常设定方法使用规则;
  3. 消除运行时异常;
  4. 正确处理检查异常;
  5. 使主流程代码保持整洁;
  6. 尽量处理最具体的异常,不能处理的异常请抛出来;
  7. 无法处理的异常却不能抛出时,通过日志记录详细异常栈,并返回给调用方友好的错误信息。

Consequences

Refs:

25. 按 RESTful 风格设计更新服务接口

Date: 04/05/2017

Status

Accepted

Context

  1. 当前 URL 定义类似这样,add_message, report_exception, check_pwd 等,函数式定义,导致接口数量增长过快,管理麻烦;
  2. 所有的请求都走 POST 方法;
  3. 所有请求返回状态都是 200,使用晦涩难懂的自定义状态码;
  4. 函数式编程,代码重用度不高;
  5. 自定义接口文档,无法及时更新;
  6. 返回结构差异化大,过滤,排序和分页各式各样,无统一的规范;
  7. 无 API 控制台可供测试;
  8. 更多。

Decision

  1. URL的设计应使用资源集合的概念;
    • 每种资源有两类网址(接口):
      • 资源集合(例如,/orders);
      • 集合中的单个资源(例如,/orders/{orderId})。
    • 使用复数形式 (使用 ‘orders’ 而不是 ‘order’);
    • 资源名称和 ID 组合可以作为一个网址的节点(例如,/orders/{orderId}/items/{itemId});
    • 尽可能的让网址越短越好,单个网址最好不超过三个节点。
  2. 使用名词作为资源名称 (例如,不要在网址中使用动词);
  3. 使用有意义的资源描述;
    • “禁止单纯的使用 ID!” 响应信息中不应该存在单纯的 ID,应使用链接或是引用的对象;
    • 设计资源的描述信息,而不是简简单单的做数据库表的映射;
    • 合并描述信息,不要通过两个 ID 直接表示两个表的关系;
  4. 资源的集合应支持过滤,排序和分页;
  5. 支持通过链接扩展关系,允许客户端通过添加链接扩展响应中的数据;
  6. 支持资源的字段裁剪,运行客户端减少响应中返回的字段数量;
  7. 使用 HTTP 方法名来表示对应的行为:
    • POST - 创建资源,非幂等性操作;
    • PUT - 更新资源(替换);
    • PATCH - 更新资源(部分更新);
    • GET - 获取单个资源或资源集合;
    • DELETE - 删除单个资源或资源集合;
  8. 合理使用 HTTP 状态码;
    • 200 - 成功;
    • 201 - 创建成功,成功创建一个新资源时返回。 返回信息中将包含一个 'Location' 报头,他通过一个链接指向新创建的资源地址;
    • 400 - 错误的请求,数据问题,如不正确的 JSON 等;
    • 404 - 未找到,通过 GET 请求未找到对应的资源;
    • 409 - 冲突,将出现重复的数据或是无效的数据状态。
  9. 使用 ISO 8601 时间戳格式来表示日期;
  10. 确保你的 GET,PUT,DELETE 请求是幂等的,这些请求多次操作不应该有副作用。
  11. PUT、POST、PATCH 请求参数通过 application/json 传递;
  12. 正确返回格式:
    • 单个资源:{field1: value1, …}
    • 资源集合:[{field1: value1, …}]
    • 资源集合(带分页):
{
 "count": 0,
 "next": null,
 "previous": null,
 "results": [{"field1": "value1", …}]
}
  1. 错误返回格式:
    • 非特定字段错误
{
 "non_field_errors": [
  "该手机号码未注册!"
 ]
}
{
 "phone_number": [
  "该字段不能为空。"
 ],
 "address": [
  "该字段不能为空。"
 ]
}
  1. 使用 swagger 做 API 展示 与调试。

Consequences

Refs:

26. 文件服务与业务服务隔离

Date: 06/05/2017

Status

Accepted

Context

  1. 当前存储方案有 3 种(FastDFS, nginx static, OSS),各自分别维护,人力成本高,都是单点,资源有效性无法保障;
  2. 文件存储和业务服务共享服务器资源(CPU, 内存,带宽,磁盘 IO),服务器资源使用不合理,文件服务会占用大量内存和磁盘 IO 及网络 IO,影响业务,并且业务服务无法做高可用,遇到 DDOS 只能傻眼;
  3. 面向用户的资源无法做加速;
  4. 所有文件都是公开可访问,存在严重的安全问题(用户身份证照片、合同及报表数据的泄露)。
  5. 所有敏感信息可被任何人全网下载;

Decision

  1. 将所有文件存储和业务机器分离,将 FastDFS 配置单独机器提供服务;
  2. 所有静态文件集中上云,录音,电子合同等静态文件上云;
  3. 文件分权限访问,分配临时 token 去获取文件;
  4. css/js/images 等上云并配置 CDN,加速用户端访问。

Consequences

综合考虑了 Aliyun OSS 及七牛云,最终选择 OSS 是因为我们的服务都是基于 Aliyun ECS 的,使用 OSS 内网数据同步速度会快很多,并且内网通讯不收费。

  1. 本地文件迁移至 Aliyun OSS (使用 ossimport2)注意事项:
    • 宿主机内存消耗量大,磁盘读 IO 很高;
    • 需要写脚本进行文件比对,确认所有文件都同步成功。
  2. FastDFS 迁移注意事项:
    • 同过配置多节点来进行新机器得同步;
    • 新机器需要 IO 优化的磁盘,否则整个数据同步瓶颈将在写磁盘操作上。

Refs:

27. 消息队列

Date: 10/05/2017

Status

Accepted

Context

  1. 我们的很多业务都有这个需要,当前的一些定时任务已导致机器的产生瓶颈,很多业务之间的耦合性也很高;
  2. 当前只有少量的业务在使用消息队列服务(activeMQ);
  3. activeMQ 对非 java 语言的支持并不友好;
  4. 自己需要维护队列服务,做监控,高可用等。

Decision

我们急需一个多语言支持且可靠的服务可以解决如上问题,即

Aliyun 的 MNS 和 ONS 功能的重合导致选择起来很困难,最终选择 MNS 源于以下几点。

  1. 支持消息优先级;
  2. 可靠性更高;
  3. 文档更完善;
  4. 服务更成熟;
  5. 使用起来简单方便,定时消息不支持这点比较遗憾;
  6. ONS 使用起来有些复杂,监控方面的功能确实强大许多,但多数在开发与公测中;
  7. 不好的点是各自都加了一些私活,MNS 加了自家的短信服务,ONS 加入了 MQTT 物联网套件,让 Q 看起来不单纯。

Consequences

使用 MNS 时需要确认以下几点:

Refs:

28. What should we do when we setup a new server

Date: 11/05/2017

Status

Accepted

Context

Context here...

Decision

Decision here...

Consequences

Refs:

29. 本地 hosts 管理

Date: 18/05/2017

Status

Accepted

Context

  1. 我们有多个测试环境(开发、测试、预发布及线上);
  2. 我们的客户端为了避免针对不同环境做多次打包,使用了和线上一致的域名,所以当我们的 QA 需要测试不同环境时,需要手动更新每个 QA 的本地 hosts 文件;
  3. 由于服务器及对应服务的调整,无法实时做到 hosts 的及时更新,也没法保证每个测试人员都做了更新(当前无通知,出问题后主要依靠问其他 QA 成员或咨询 Dev);
  4. 部分线上服务未配置外网域名,比如 Boss 系统、Wiki 等,需要包括运营人员在内的所有人各自去配置本地 hosts。

Decision

  1. 方案一:使用 SwitchHosts(Chosen)
    • 可快速切换 hosts;
    • 支持集中式的 hosts 管理(在线);
    • 支持多 hosts 合并;
    • 支持多客户端。

主界面如下,默认会将旧的 hosts 文件保存为 backup

可以新建 hosts,比如测试环境一

可以使用远程集中式管理的 hosts

最重要的是这些 hosts 可以通过同时打开各自开关将其合并使用。

  1. 方案二:使用路由器
    • 只需切换不同的路由器即可进行不同环境的测试;
    • 配置办公网络路由器,需要 IT 人员来维护(暂无);
    • 只能办公室内进行访问。

Consequences

软件基于 Electron 开发,跨平台,Windows/Mac/Linus 客户端下载地址: https://pan.baidu.com/share/link?shareid=150951&uk=3607385901#list/path=%2Fshare%2FSwitchHosts!&parentPath=%2Fshare

Refs:

SwitchHosts Github: https://github.com/oldj/SwitchHosts

30. 容量评估 - 存储

Date: 2017-05-25

Status

Accepted

Context

  1. 一些需要高 IOPS 的服务,磁盘使用的是普通云盘,如,数据库,备份服务,图片服务等。

Decision

  1. 明确存储的使用场景;
  2. 关注吞吐量,IOPS和数据首次获取时间。

我们的存储都是基于 Aliyun 的,他有以下类别及特点:

Consequences

  1. 加粗的信息可以重点查看下,同类别里比较推荐;
  2. 我们选择时还要考虑价格及存储时长要求。

Refs:

31. 容量评估 - 内存

Date: 2017-05-25

Status

Accepted

Context

  1. 机器内存太小了,需要升级一下;
  2. 我们之前公司服务需要用多少多少;
  3. Java 程序最大最小堆的指定无标准。

Decision

Consequences

这篇文章的缘起是因为我们的一个同事发现自己使用的测试环境内存不够用,导致其他服务无法启动,并要求升级机器配置。当时的测试环境配置是:2核4G。出现问题前的操作是将 Xmx 由 1G 调整为 2G,通过分析发现是程序中加载了一个 7万条数据的大表导致,调整批量加载数据将以原先一半的内存解决此问题。

Refs:

32. TCP 长连接高可用

Date: 2017-05-25

Status

Accepted

Context

  1. 我们的一些智能设备,通过 TCP 和后端进行通信,上报数据及接收指令;
  2. 智能设备由第三方提供,我们实现后端服务,当前支持域名和 IP 请求;
  3. 随着未来设备数量的增长,我们的单台服务预计将无法满足;
  4. 原方案是针对不同批次的智能设备,我们绑定不同的域名,已做客户的负载;
  5. 存在设备使用的不可控引起服务端的使用率会存在问题;
  6. 服务器出问题后服务的恢复时间受限于 DNS 更新的时间(切换新机器)。

Decision

Consequences

Refs:

33. 容量评估 - MySQL

Date: 2017-05-25

Status

Accepted

Context

QPS: 每秒钟查询量。如果每秒钟能处理 100 条查询 SQL 语句,那么 QPS 就约等于 100 TPS: 每秒钟事务处理的数量

Decision

和 MySQL 性能相关的几个指标

影响数据库性能的因素及应对方式

Consequences

鉴于我们使用了 Aliyun MySQL,他有完善的监控项,CPU、内存、磁盘、连接数及网络流量,关注各个指标并针对其做好报警策略即可

Refs:

34. DNS 笔记

Date: 2017-06-03

Status

Accepted

Context

  1. 网站被 DDOS 需要考虑切换 DNS 解析;
  2. 负载均衡服务切换服务器,需要更新 DNS 解析;
  3. 新加子域名,需要配置 DNS 解析。

Decision

DNS 解析的同步需要时间,涉及以下几点:

  1. DNSPod 生效时间,一般在 30s 以内;
  2. 各个 ISP DNS 服务器刷新时间;
  3. 域名 TTL (用户本地缓存时间)。

针对全球性业务,保险起见,我们等待一天以上。DNSPod 给出的说法是,域名解析通常需 24 小时至 72 小时才能全球完全同步。大多数业务正常情况以 TTL 为依据即可。

Consequences

一种域名解析更新策略是,现将旧的域名解析 TTL 设为最小值,等待一天,确保解析都已完全同步,这个完全不影响现有业务。将 DNS 解析更新到新的记录,这个时候 DNS 服务器将以最快的速度进行同步更新;确认都更新完毕后,我们将 TTL 设为较长时间,确保 DNS 解析的稳定性。

Refs:

35. 关于灾难恢复

Date: 2017-06-05

Status

Proposed

Context

当前我们使用的是华北2可用区A机房(即北京昌平区的一个机房)部署了所有服务,存在以下几个问题,出现概率逐级减少:

  1. 服务本身部署在单台机器,单机的故障会导致服务的不可用,这个我们的业务服务频频出现;
  2. 所有服务部署于一个机房,机房电力,网络出现故障,将导致服务完全不可用,这个 2016 年中旬我们使用的 Aliyun 机房网络设备出现过一次问题,导致服务停服 1 小时左右(官方),实际对我们的影响在 12 个小时左右;
  3. 北京发生各种灾害,殃及所有机房,导致服务不可用。

基础概念

灾难恢复(Disaster recovery,也称灾备),指自然或人为灾害后,重新启用信息系统的数据、硬件及软体设备,恢复正常商业运作的过程。灾难恢复规划是涵盖面更广的业务连续规划的一部分,其核心即对企业或机构的灾难性风险做出评估、防范,特别是对关键性业务数据、流程予以及时记录、备份、保护。

地域,即城市,不同的地域可以做到自然灾害级别的灾备,之间延迟较高

可用区,即机房,不同的可用区可以做到电力和网络设备互相独立,之间有少量延迟

两地三中心,业界目前最可靠的解决方案,即在两个城市共三个机房中部署服务

灾备的两项指标

RTO - Recovery Time Objective,它是指灾难发生后,从 IT 系统宕机导致业务停顿之时开始,到 IT 系统恢复至可以支持各部门运作、恢复运营之时,此两点之间的时间段称为 RTO

RPO - Recovery Point Objective,是指从系统和应用数据而言,要实现能够恢复至可以支持各部门业务运作,系统及生产数据应恢复到怎样的更新程度,这种更新程度可以是上一周的备份数据,也可以是上一次交易的实时数据

两地三中心

Decision

  1. 同机房的服务高可用(进行中),这个是目前最高优先级;
  2. 同城双活(提议中),可以解决大部分我们遇到的机房问题;
  3. 异地灾备(暂不考虑),针对支付业务,当涉及合规性时,我们得考虑下;
  4. 明确我们各个服务的重要程度,分服务针对性的做高可用及灾备策略。

Consequences

Refs:

36. 关于 MySQL 高可用

Date: 2017-06-06

Status

Proposed

Context

  1. 数据库版本 5.1,太旧,性能,安全,主从复制都存在问题;
  2. 数据库部署在 ECS 上,但磁盘使用的是普通云盘,IOPS 已到阈值(优先级最高);
  3. 数据库一主两从,但无高可用;
  4. 业务端使用 IP 连接主数据库。

Decision

  1. 提交 Aliyun 工单,尝试是否能申请下 5.1 版本的 MySQL,迁移数据至 RDS,解决 2,3,4 问题(沟通后,5.1 版本已不再提供,PASS);
  2. 将部分数据库迁移出,缓解当前 MySQL 服务器压力,维护多个数据库实例(并未解决实际问题,PASS,当前压力最终确认是慢查询原因);
  3. ECS 上自建 HA,并启用新的实例磁盘为 SSD,切换新实例为 Master,停掉旧实例(根本问题未解决,技术债一直存在,自行维护仍然存在风险点);
  4. 调研 5.5 和 5.1 的差异,直接迁移自建数据库至 Aliyun RDS MySQL 5.5。

鉴于查看文档后, 5.1 到 5.5 的差异性影响不大,Aliyun 官方也支持直接 5.1 到 5.5 的迁移,所以计划直接迁移至 RDS 的 5.5 版本。

为了杜绝风险:

  1. 按业务分数据库分别迁移;
  2. 所有迁移先走测试数据库,由 QA 做完整的测试。

ECS self built MySQL 5.1 to RDS 5.5 with DTS 迁移流程:

  1. 在 RDS 中创建原 MySQL 数据库对应的账号(各个项目账号独立);
  2. 更新白名单:添加项目所部署的服务器;
  3. 明确数据规模,对同步时间做个预期;
  4. 同步(全量 or 增量),明确无延迟状态;
  5. 更新数据库连接配置文件;
  6. 明确无延迟状态,停服;
  7. 确定数据量一致(由预先写好的脚本判断)(1min);
  8. 关闭迁移服务(10s);
  9. 重启服务器(10s)。

6 至 9 步决定我们的停服时间。

鉴于我们使用从库作为迁移的数据源,需更新如下配置:

Consequences

同步过程中出现如下问题,请周知:

  1. 判断脚本出问题,未准备好脚本命令;
  2. 终端出问题,更改命令麻烦;
  3. 配置信息直接在生产环境更改,应走 github 提交;
  4. 停服那步影响其他业务;
  5. 从库使用者是否受影响,如数据组。

Refs:

37. 通过代理解决白名单问题

Date: 2017-06-07

Status

Accepted

Context

  1. 我们的支付及财务业务,需要向第三方金融机构发起请求,第三方机构出于安全性考虑,需要将我们的服务器 IP 地址进行报备;
  2. 第三方机构较多,针对服务器扩容,更换,报备一次比较麻烦;
  3. 第三方机构审核时间不定,1 天到多天不定,关键时刻影响业务。

Decision

正向代理是一个位于客户端和目标服务器之间的代理服务器(中间服务器)。为了从原始服务器取得内容,客户端向代理服务器发送一个请求,并且指定目标服务器,之后代理向目标服务器转交并且将获得的内容返回给客户端。正向代理的情况下客户端必须要进行一些特别的设置才能使用。

常见场景:

反向代理正好相反。对于客户端来说,反向代理就好像目标服务器。并且客户端不需要进行任何设置。客户端向反向代理发送请求,接着反向代理判断请求走向何处,并将请求转交给客户端,使得这些内容就好似他自己一样,一次客户端并不会感知到反向代理后面的服务,也因此不需要客户端做任何设置,只需要把反向代理服务器当成真正的服务器就好了。

常见场景:

方案:

  1. 使用正向代理;
    • 这个场景最容易想到的方案,使用起来直观,易懂;
    • 需要每个客户端进行配置,或是在应用中对单个请求做代理配置;
    • 需要维护一个高可用的代理服务,并备案此代理服务器。
  2. 使用反向代理。
    • 在我们的服务器上做对方服务的反向代理(听起来有点绕,不直观)
    • 维护简单,就像是我们用 nginx/slb 为对方做了个负载均衡,但配置稍有不同;
server {

     server_name {{ proxy_info.server_name }};
     listen {{ proxy_info.ssl_listen }};

     location / {
         proxy_pass_header Server;
         proxy_pass {{ proxy_info.proxy_url }};
         proxy_redirect off;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Scheme $scheme;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     }
}
  1. iptables
    • 类似局域网共享上网;
    • 对 iptables 配置有要求;
    • 目标域名对应的 ip 地址改变,需要更新配置。

最终我们通过 aliyun slb 的4层负载接两台部署了 ss5 的机器提供高可用的代理服务

Consequences

Refs:

  1. 云服务器 ECS Linux 系统通过 Squid 配置实现代理上网 https://help.aliyun.com/knowledge_detail/41342.html
  2. 正向代理与反向代理的区别 http://www.jianshu.com/p/208c02c9dd1d
  3. 设置 iptables 使用linux做代理服务器 https://www.l68.net/493.html
  4. SS5 http://ss5.sourceforge.net/project.htm

38. 数据脱敏

Date: 2017-06-13

Status

Accepted

Context

  1. 数据目前交由服务后台或前端进行敏感信息处理,原始数据存在多种风险(外部脱裤,内部人员非必要性的查看等);
  2. 各个 Team 数据加密策略不一致(有 MD5, AES etc);
  3. 数据掩码方式也不统一。

Decision

使用四个策略的结合:

  1. 掩码 - 业务系统需要查看部分内容,已核对信息,日志中为了便于定位;
  2. 替换 - 字段实际不被使用,但保留字段,并将数据替换为空内容;
  3. 可逆加密 - AES(ECB/PKCS5Padding - 128)
  4. 不可逆加密 - SHA1

策略的使用需考虑,使用场景(生产、测试等环境),成本(对人员、服务器的需求),是否易用(影响开发效率),维度(目前就分两种:机密和公开)

掩码规则:

两个原则:

  1. remain meaningful for application logic(尽可能的为脱敏后的应用,保留脱敏前的有意义信息)
  2. sufficiently treated to avoid reverse engineer(最大程度上防止黑客进行破解)

Consequences

Refs:

39. 秘钥管理

Date: 2017-06-14

Status

Accepted

Context

  1. 目前有相当多的账户、密码等信息存储在项目配置文件中;
  2. 部分项目将敏感信息和项目分离,但所部署的服务器还是能被所有人登录查看;
  3. 将服务器登录权限限制在运维手中,需要运维人员维护所有敏感信息的存储与管理,数量线性增长,尤其是支付组涉及的敏感信息更多,每一个新项目都需要运维人员的参与和维护。

Decision

  1. 将服务器登录权限限制在个别人的手中;
  2. 使用密码管理服务,确保运维人员只需维护一个秘钥;
  3. 使用 Aliyun KMS 而不是自己搭建,节约运维成本。

直接使用KMS加密、解密

结合我们的需求,我们选用这种方式,使用方式如下

import json
from aliyunsdkcore.client import AcsClient
from aliyunsdkkms.request.v20160120 import EncryptRequest, DecryptRequest

OLD = 'password'
NEW = 'M2U5YzZlNGEtZTczZS00NmM4LWE0YmQtZjI3ODI0MmU4YWJjcEVDZW5SMEtWYjJsdWovdU5ibFNhSk5KS0RqbE9ENTRBQUFBQUFBQUFBQXJOd2dGc2l4S1JpV0tPRUgvbkwvSXVHYU5heCt5eHlFPQ=='

client = AcsClient('id', 'secret', 'cn-beijing')


def en():
    request = EncryptRequest.EncryptRequest()
    request.set_KeyId('e6116a43-9926-4a66-a781-55fce623c2cb')
    request.set_Plaintext(OLD)
    response = client.do_action_with_exception(request)
    print json.loads(response)['CiphertextBlob']


def de():
    request = DecryptRequest.DecryptRequest()
    request.set_CiphertextBlob(NEW)
    response = client.do_action_with_exception(request)
    print json.loads(response)['Plaintext'] == OLD


if __name__ == '__main__':
    de()

使用信封加密在本地加密、解密

Consequences

  1. 直接使用KMS加密、解密会影响启动速度;
  2. 一个明文多次加密,产生的密文不同,但所有密文都可以解密为明文。

Refs:

40. Agile - Daily standup meeting

Date: 2017-06-16

Status

Accepted

Context

  1. Team devops set up recently, we need some change to improve the performance;
  2. No communication in the team, always person to person;
  3. Tasks not organized well which spread among different projects;
  4. Some teams require daily reports for their management, but lose the communication between team members.

Decision

We're going to have 10 minutes daily standup meetings with each team so that We're on the same page about what everyone in the team is working on and we can strategize on how to approach the work items on the board for the day to come.

Effective standups are all about communication and commitment, not about status updates. So before every scrum take 5 minutes to actually think and note down what you were planning to accomplish yesterday, did you actually accomplish what you've committed to, if not why?

Take some time to plan your day and decide what you're going to accomplish today, commit to it and state it to the team (not the management), so the team members will keep each other accountable for the commitments they make to make the whole team improve together and make sure to remove impediments that prevent team members to accomplish what they plan.

Scrum master should pay more attention to the status of each card, encourage team members to deploy in time.

We change scrum master every two week to make sure all team members understand the details as an owner.

Keynotes

Consequences

Everyone in the team will know each other better.

Refs:

41. MySQL 迁移 RDS 总结

Date: 2017-07-17

Status

Accepted

Context

  1. 当前数据库为 Master, Slave 模式;
  2. Master 实例上创建了 16 个数据库,全部需要迁移;
  3. 部分业务存在跨库查询;
  4. 有一个数据库被近 10 个业务进行查询等数据库操作;
  5. 当前数据库为 MySQL 5.1,存在主从同步性能问题;
  6. RDS 仅支持 MySQL 5.5, 5.6, 5.7 版本;

Decision

  1. 调研 MySQL 5.1 与 5.5 之间的差异,并周知各个项目组成员;
  2. 将数据库与业务之间的引用关系用二维表列举出来;
  3. 选出被业务单独或少量引用的数据库,先通过将对应测试数据库进行迁移,并交由测试人员进行测试;
  4. 测试完成后对正式环境直接进行迁移;
  5. 针对被10多个业务引用的数据库迁移,我们不止要做测试环境的迁移,还要做线上环境的测试
    1. 为了保证线上测试可回滚,我们需限定只有测试人员进行操作;
      1. 限制我们的服务器安全组,外网只能通过办公网络访问;
      2. 限制我们的入口 slb,只能通过办公网络访问。
    2. 我们的服务之间存在调用关系,部分业务走的外网域名;
      1. 准备我们所有外网业务的域名及其内网 ip 映射,通过 ansible 分发并追加其至所有的线上机器 hosts
    3. 所有业务准备好分支 feature/db-config-update-to-rds,此分支与 master 分支差异只有数据库配置改动,冻结 master 新代码合入,如此确保随时可上线,随时可回滚;
    4. 创建数据库迁移任务: 结构迁移+整体数据+增量数据迁移;
    5. 停止并备份任务计划调度(/etc/crontab,cron.d,/var/spool/cron);
    6. 停止所有业务服务;
    7. 停止所有 nginx;
    8. 脚本验证所有机器上的服务和任务计划,确保无运行中相关程序;
    9. 验证无数据库连接;
    10. 锁定原数据库写操作(验证确认确实不可写)
    11. 网络隔离;
    12. 检查源数据库与目的数据库 数据一致性;
    13. 停止数据迁移任务;
    14. 停止主db数据库服务;
    15. 启动RDS到从库的数据同步任务;
    16. 重启所有服务;
    17. 重启 nginx;
    18. 测试团队开始全面验证;
    19. 网络隔离解除;
    20. 恢复任务计划;
    21. 重新执行停服期间计划任务。

Consequences

  1. RDS 磁盘预留过低,导致同步中途停止,无法进行写操作;
  2. 一些业务需要回调,测试不完整;
  3. 如果有完整的预发布环境,可保证服务零停机时间;

42. Agile - Retrospective meeting

Date: 2017-07-31

Status

Accepted

Context

  1. Team members know little about last iteration and don’t know which part is good, bad or need improve;
  2. New team members know nothing about the team’s history and don’t know the best practices for this team;
  3. We need learn from what we done.

Decision

Description

After each iteration we'll spend one hour discussing relevant topics we add during the iteration in our retrospective board.

The idea is to spend quality time and get the most value out of this meeting, therefore each team member is encouraged to bring discussion topics as well as suggestion for improvement.

Preparation

Process

Notice

Consequences

Everyone will learn from history, and not make same error twice.

43. 支持过滤的消息队列

Date: 2017-08-18

Status

Accepted

Context

最近需要优化这样的场景,我们的合同状态有多种(待完善,待审核,审核通过,审核不通过等),每种状态的改变来自多个地方(中介后台,运营后台,风控系统等),每种状态的处理也来自多个地方(SAAS系统,中介后台,运营系统等),且处理者需要处理的状态不相同;

当前实现方案是:

  1. 定时任务,各个系统定时处理状态为 X 的合同(处理不及时);
  2. 使用回调,状态更新时回调至处理者处(扩展不方便);

Decision

  1. 消息服务(MNS) 主题模型

    • 可订阅,并且可以通过 Tag 进行消息过滤;
    • 只支持推送模式,每个消费者需要创建回调接口;
    • 只支持外网地址推送,对内部服务来说,网络耗时太高。
  2. 消息服务(MNS) 队列模型

    • 根据消费者创建队列,几个消费者就创建几个队列;
    • 生产者根据消费者所需向其队列发送消息,即生产者需要发送相同消息至多个队列;
    • 每增加一个消费者,都需更新所有对应生产者的代码,维护性太低。
  3. 消息服务(MNS) 主题+队列模型

    • 根据消费者创建队列,几个消费者就创建几个队列;
    • 生产着只需向主题队列发送消息,<del>消费者队列订阅所有主题队列的消息</del>;
    • <del>消费者需要接收所有消息,并在业务逻辑中过滤出自己需要处理的消息</del>;
    • 消费者队列可以按 tag 进行过滤;
    • 消费者完整消费自己的队列即可。
  4. 消息队列(ONS) MQ

    • 总共只需一个 topic 队列,各个消费者通过 Tag 进行过滤;
    • 完美支持我们的使用场景;
    • 不提供 Python SDK。

Consequences

官方不建议用方案 4,我们尝试调试也不顺畅,所以决定使用方案 3。

Refs:

44. 泛域名解析

Date: 2017-08-25

Status

Accepted

Context

随着会找房平台第三方公寓的入驻,我们需要手动添加大量的公寓二级域名于 DNS 中,如,ayk.huizhaofang.com, jingyu.huizhaofang.com 等。

  1. 整个流程依赖域名管理者;
  2. DNS 管理控制台维护这些记录很麻烦,并将原有专用域名淹没在大量记录中;
  3. 网站做了前后端分离,起始页永远返回状态为 200 的页面。

Decision

  1. DNS 记录添加泛解析;
  2. Nginx server name 添加泛解析;
  3. 不受约束的泛解析,会使所有子域名返回状态为 200 的页面,导致搜索引擎降权;
  4. 使用 include /path/to/server_names; 可以通过独立的文件解决此问题,并可对新添加的子域名做 code review;
  5. 上面的方案需要每次更改后做 nginx reload;有个想法是通过 lua 从 redis 中获取支持的子域名,进行判断并过滤;

Consequences

  1. include 方案:当前最简单,且有审核机制,但运维参与较重,需经常重启 nginx;
  2. lua + redis 方案:
    • 每次请求需查询 redis;
    • 需做一个对应的管理系统进行子域名的管理。

Refs: