Bowen's Blog

Respect My Authorita.

Supressing an Warning Log

| Comments

客户的主站系统是一个基于Spring的单块系统,每天的流量大概在几千万,其所有的应用日志以及访问日志,都会上传到Splunk服务器上。随着服务数量的增加,日志的数量也越来越多,对应的Splunk的费用也越来越感人,鉴于主站的系统对日志的贡献最多,所以就从它入手降低无用日志的上传。

其中的一条无用的warning日志信息显示如下:

1
WARN org.apache.commons.httpclient.HttpMethodBase - Going to buffer response body of large or unknown size. Using getResponseBodyAsStream instead is recommended.

一周内这条warning的数量超过百万条,还是比较可观的。简单查询下其原因是httpclient里面的getResponseBody()调用触发的。

1
2
3
4
5
            int limit = getParams().getIntParameter(HttpMethodParams.BUFFER_WARN_TRIGGER_LIMIT, 1024*1024);
            if ((contentLength == -1) || (contentLength > limit)) {
                LOG.warn("Going to buffer response body of large or unknown size. "
                        +"Using getResponseBodyAsStream instead is recommended.");
            }

看完这个后我们的理解是,有些请求的response body过大,超过缺省的1M(代码会从Content-Length header中获取这个大小),就会触发这个warning,当时没有意识到还有可能是确实不知道response body的长度。 相关的方法调用在代码中有10几处,当时我们也无法定位那段代码引发了这个问题,无脑修改的话,成本比较高,可能要增加一些测试用例,以及做回归测试。所以当时就想着用成本最低的方式修改,从配置文件中给BUFFER_WARN_TRIGGER_LIMIT赋一个更大的值,如20M,毕竟这是个遗留项目,熟悉代码的人以及比较少了。没有选择调整日志的级别是因为HttpMethodBase类是个超类,粗暴调整可能会掩盖其它有用的warning日志。 部署完成后比较日志数量发现并没有太大变化,不得不让我们重新回来审视这个问题的根本原因在哪里。幸好当时系统加了一个transactionID的功能,每次的请求过来时,在应用中用UUID生成一个transactionID写入应用日志,response返回时再写入access log。这样我们就在请求和对应的代码调用之间建立了联系。 功能上线后重新在splunk中搜索,立刻就定位了是在请求Google Map API时触发了这个warning,而且在本地可以稳定重现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 ~> curl -I   "https://maps.google.com.au/maps/api/geocode/json?address=Sunnydale%2C+SA+5354&language=en_AU&sensor=false"
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Date: Fri, 18 Nov 2016 23:20:16 GMT
Expires: Sat, 19 Nov 2016 23:20:16 GMT
Cache-Control: public, max-age=86400
Access-Control-Allow-Origin: *
Server: mafe
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Alt-Svc: quic=":443"; ma=2592000; v="36,35,34"
Transfer-Encoding: chunked
Accept-Ranges: none
Vary: Accept-Encoding

然后发现原来返回是没有Content-Length的:(。 Content-Length header是客户端用于了解服务器返回body的大小,从而在获得等大的内容后,结束连接,节省开销。但在实际的应用中,Content-Length有可能无法准确反映返回body的大小,其值过大会导致pending,过小内容又会被截断。 Transfer-Encoding: chunked 是用来分块编码传输内容,每个分块中包含了长度值和数据,最后一个分块长度值是0,这样就可以准确知道边界了。 定位到问题在哪里之后就很容易解决了,最后只要改动一行代码:

1
2
-            String response = query.getResponseBodyAsString();
+            String response = IOUtils.toString(query.getResponseBodyAsStream());

回过头来反思整个过程,因为是遗留系统,所以处理的方式有些粗糙,如果当时我们遵循下面的过程也许会更好些: 1) 定位问题,找到根本原因(有transactionID的配合会更方便),而不是盲目用生产环境来测试配置的正确性; 2) 在本地重现问题,应用解决方案,并与熟悉遗留系统的同事沟通 3) 回归测试后上线

Issue Raised by DNS

| Comments

最近在客户现场出差,见证了不少有趣的线上事故,下面要讲的就是其中之一。 一段时间依赖,某个微服务在生产环境的response的延迟陡然增加了几百毫秒,而部署的代码并不是造成延迟原因。从Newrelic的监控可以发现,该API的延迟增大的主要原因是它依赖的一个服务响应时间增大了。

我们暂且把这个外部的服务称为service.mycompany.com,这个服务分别部署在澳洲和欧洲的两个数据中心,入口处是Akamai,做负载均衡,尽可能的按照访问来源去分发请求。

该微服务部署在AWS悉尼的数据中心,所以理论上来讲,当它请求service.mycompany.com时,Akamai应该返回的是位于悉尼的edge节点的IP,同时其访问的origin服务器也应该位于悉尼。但是通过在该微服务的服务器debug,发现ping值以及traceroute的值都比较高,办公室访问却都一切正常。当时怀疑是Akamai的GEOIP判断出了问题,把来自亚马逊悉尼的请求当成了来自美国的IP的请求,于是用部署于欧洲的数据中心的服务处理请求。和基础设施部门管理网络的人讨论,再次调查后结论类似。

问题出在这个AWS账户下的VPC的DHCP options的配置。因为是比较早期使用的share的AWS账户,所以下面的网络配置比较复杂,配置有Direct Connect 连往其他数据中心,以及很多VPC Peering。不知道因为什么原因,这个微服务部署的Cloudformation template里面选择了包含google DNS 8.8.8.88.8.8.4的DHCP Options。我们都知道对于在Akamai上注册的服务service.mycompany.com来说,如:

1
2
3
4
 ~> host service.mycompany.com
service.mycompany.com is an alias for mycompany.generic.edgekey.net.
mycompany.generic.edgekey.net is an alias for e8888.g.akamaiedge.net.
e8888.g.akamaiedge.net has address 104.116.190.24

第一次DNS请求返回的记录是CName,之后进一步返回Akamai动态DNS的CName,也就是edge server的CName,之后再根据DNS服务器返回对应的edge服务器的IP地址,如果查询的是Google的DNS,那么它会返回美国的edge服务器地址……。我们可以测试下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 ~> dig @8.8.8.8 service.mycompany.com

; <<>> DiG 9.8.3-P1 <<>> @8.8.8.8 service.mycompany.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44304
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;service.mycompany.com.   IN  A

;; ANSWER SECTION:
service.mycompany.com. 86399 IN   CNAME   mycompany.generic.edgekey.net.
mycompany.generic.edgekey.net. 263    IN  CNAME   e8888.g.akamaiedge.net.
e8888.g.akamaiedge.net.   19  IN  A   23.53.156.156

;; Query time: 603 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Tue Nov 15 22:15:52 2016
;; MSG SIZE  rcvd: 130

查询下IP地址信息,

1
2
 ~> whois 23.53.156.156 | grep Country
Country:        US

所以,这个微服务的请求先到Akamai美国的edge服务器,之后很有可能请求被发送到了欧洲的origin服务器,这个延迟不增加才👻了……。

解决的办法很简单,更新配置,DHCP Options选择Amazon提供的DNS就可以了,响应时间就降下去了。

这个事情给我们的教训就是,不管怎么样都不能崇洋媚外,虽然澳洲一直follow美国,但是DNS还是得用自己的。

Don't Put Emoji in Commit Message

| Comments

随着项目上越来越多的使用Slack以及Emoji的流行,很多人情不自禁的会在各种地方使用emoji表情。比如 在channel里面发:bicyclist::skin-tone-2: :house: :thunder_cloud_and_rain: :disappointed:。更甚者会在git commit message中添加emoji。比如像这样

1
finish story xxxx. :pear: xiao.

意思是完成这个开发的需求是和xiao结对做的。pull request发出后,review的人除了会发:+1:这样的表情表示支持外,还会用:shipit:,:ship:之类的表示赞同,可以merge。 这样的emoji为开发增添了乐趣,但是有时候也会带来麻烦,比如我今天就遇到了这样的情况。 在清理完一些旧代码后,我在提交信息里面➕了下面的消息:

1
clean up the :older_man::skin-tone-2: code.

提交merge后,过了一段时间看了下build,还没有到运行阶段就挂了。查看了下原因,发现是bamboo在保存提交信息时遇到了一个复杂字符出错了。

1
(org.springframework.jdbc.UncategorizedSQLException : Hibernate flushing: Could not execute JDBC batch update; uncategorized SQLException for SQL [insert into USER_COMMIT (REPOSITORY_CHANGESET_ID, AUTHOR_ID, COMMIT_DATE, COMMIT_REVISION, COMMIT_COMMENT_CLOB, FOREIGN_COMMIT, COMMIT_ID) values (?, ?, ?, ?, ?, ?, ?)]; SQL state [HY000]; error code [1366]; Incorrect string value: '\xF0\x9F\x91\xB4 ...' for column 'COMMIT_COMMENT_CLOB' at row 1; nested exception is java.sql.BatchUpdateException: Incorrect string value: '\xF0\x9F\x91\xB4 ...' for column 'COMMIT_COMMENT_CLOB' at row 1)

当时就感觉是这个emoji出问题了,搜了下提示的编码的十六进制,果然是这个原因……。没办法只好reset下,push force,再重新修改提交信息再push。 同事告诉了我这个事故的根本原因是mysql的utf-8对Emoji的支持不够,解决的办法就是把数据库的charset设置为utf8mb4,详见这篇文章. 所以,以后玩emoji的时候一定得先确认系统支持,否则可能会带来一些:shit:。

Gpgand Keybase Introduction and Usage

| Comments

.— layout: post title: “GPG and keybase.io introduction/usage” date: 2016-08-19 17:29:49 +0800 comments: true

categories: [“GPG”, “keybase.io”, “Security”]

什么是GPG

How to Write Useful Git Commit Message

| Comments

相信大家在自己项目的历史提交里面看到类似的提交记录 我还见过更加糟糕的,类似这样

1
2
53ee0c7 fix build again
7a63a11 fix build

这样的提交信息的问题在于不表意,没有简要的说明修改的内容,为什么要这样的修改,别人只能去查看具体的代码改动才能知道发生了什么,但是可能无法知道为什么这样修改。当然,这样的提交我自己也写过,原因包括 1. 语法、拼写错误,羞于示人 2. 解释原因得写很长,懒的敲键盘 3. 无法解释为什么这样的修改就能work 这其实算是一种比较不负责任的行为,估计别人看到会比较崩溃,幸运的是还没有领导看到,所以至今没有被开除。举个例子,假设某个提交引起了产品环境的错误,别人需要迅速定位是哪个提交引起的问题,但是如果提交都是类似Perfectly complete a new story,而且每次代码修改的量都比较庞大,那就得花很多时间才能定位。相反如果提交信息很清晰,BAU-1008 add xxx form in xxx page. :pear: Kevin。你用git log --oneline --after "Aug 10 2016"可以迅速看到对应的提交,进一步的可以查看修改内容再查找具体的问题。

今天早上客户跟我们一起做了一个关于如何有效的提交git commit信息。他提到了git commit message的7个规则。他认为从项目维护性的角度考虑,应当注意提交的信息以及规范。一个项目的提交信息首先得从下面三个方面达成一致:

  1. 格式。消息体的格式,如Markdown,语法应该是什么样子,大写的规则等。
  2. 内容。提交的信息中应当包含什么,不应当包含什么。
  3. 元数据。问题跟踪的ID(Jira,Leankit等)要不要引用,PR的sha code要不要引用。

具体的规则有下面7点:

  1. 用空行将内容和主题分开
  2. 提交的主题限制在50个字符
  3. 主题首字母大写
  4. 主题结尾不要使用句号
  5. 主题需要使用祈使/肯定语气
  6. 内容每72个字符换行
  7. 在内容中解释清楚修改的原因及方式

第一条,如果内容和主题没有分开,git log --oneline主题和下面的内容会一起显示。 第二条,主题超过50个字符时超过的部分在github上显示为...,提交PR的时候超过的部分会被折断到comments中,很烦人。用vim去编辑提交信息的时候,如果看到主题的字的颜色变化了,就说明超过了50个字符。 第三、四条不评价,感觉更多是从美观和规范上统一的。 第五条,感觉这样可以少写一些字,而且和git 缺省的提交信息,如revert的提交信息一致。

1
2
3
Revert "Add the thing with the stuff"

This reverts commit cc87791524aedd593cff5a74532befe7ab69ce9d.

第六条,因为git不会帮你wrap文字,所以得手动的来做这个事情,这里可以借助一些编辑器,如VI的帮助。 第七条,个人觉得这个才是最重要的,解释清楚修改的原因以及方式,引用别人文章里面的一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
commit eb0b56b19017ab5c16c745e6da39c53126924ed6
Author: Pieter Wuille <pieter.wuille@gmail.com>
Date:   Fri Aug 1 22:57:55 2014 +0200

   Simplify serialize.h's exception handling

   Remove the 'state' and 'exceptmask' from serialize.h's stream
   implementations, as well as related methods.

   As exceptmask always included 'failbit', and setstate was always
   called with bits = failbit, all it did was immediately raise an
   exception. Get rid of those variables, and replace the setstate
   with direct exception throwing (which also removes some dead
   code).

   As a result, good() is never reached after a failure (there are
   only 2 calls, one of which is in tests), and can just be replaced
   by !eof().

   fail(), clear(n) and exceptions() are just never called. Delete
   them.
 ```
业务相关的代码修改,可以将story ID加在最前面,方便issue track。

 为了让大家统一提交的格式,可以新建一个提交的template,配置git[使用](https://robots.thoughtbot.com/better-commit-messages-with-a-gitmessage-template)。 过程如下:

 1. 在`~/.gitconfig`中加入下面的内容:
 ```
 [commit]
  template = ~/.gitmessage
  1. 新建~/.gitmessage这个template文件并且填入自定义模板:
1
2
3
4
5
6
7
8
Brief here:

Reason to change:
*

Way to change:

*

配置完成后在项目中做修改,git ci -a就可以在模板的基础上修改了。

举一个项目中的一个例子,里面包含了业务需求的github issue的链接:

1
2
3
4
5
6
7
commit 2196e866261dee6d7c17f266cc15987f
Author: Alex Jin <alex.jin@example.com>
Date:   Wed Aug 17 13:24:41 2016 +0800
    Make trend bigger and modify link color. :pear: Luke

    * Reason to change:
      see [lob/project#208]

如果是用Pull Request方式工作的话,麻烦的地方在于修改的原因可能还得在comments里面再写一遍。解决的办法是创建issue/PR的template,参考这里

什么,你问我为什么还没有被开除么? 因为领导不看提交 :)

On Dockerising a Frontend Build Pipeline

| Comments

最近花了一段时间把主站的build pipeline docker化了,时间长到感觉自己的reputation都要被毁了。 在此总结下这个过程以及碰到的问题,希望对大家能有所帮助。

背景

这是一个纯前端的项目,两年前前后端分离的时候的项目,Grunt workflow,测试框架使用Karma,用Phantomjs1.8.2运行headless的测试,开发环境使用Chrome/Safari做功能性测试。开发环境基于node 0.12,一些基础设施的更新,部署的脚本,smoke test是基于Ruby的,版本为2.0

这个前端的工程部署在两个不同的AWS Region的S3上,互为fail over,最前面有Akamai为它们做负载均衡。

持续集成的工具使用Bamboo,其agent需要有ruby 2.0node 0.12Phantomjs 1.8.2的环境才可以运行具体的任务。整个过程已经做到了持续部署,一个完整的build过程如下:

  1. 提交代码
  2. trigger build,执行单元测试和集成测试
  3. 自动部署staging 环境
  4. 自动部署production 环境
  5. 对部署后的产品做性能测试
  6. 上传工程中依赖的第三方类库信息到S3 bucket(出于安全的考虑)

存在的问题

太长时间没有人做技术上的升级,导致下面的一些隐患和问题: 1. 开发的工具版本落后,node当前版本已经是6.3了,ruby 2.0的版本应该已经不维护了,同样,对应的Karma,Phantomjs都以及更新了很多 2. 运行build依赖的agent是共用的,如果有人对agent的环境进行修改,会影响该项目的持续集成 3. 未来需要将CI工具从Bamboo迁移到Buildkite,用pipeline as code的方式去构建,每个组自己去管理build agent,使用Docker会更加方便迁移

过程以及遇到的一些难点

测试部分通过的过程及问题 1. 首先做的事情是构建一个基础的docker 镜像,包含最新的node 6.3.1,phantomjs 2.1.1,后来发现其实不用Phantomjs,这个有点多余了。 成果在这里: https://github.com/iambowen/node_on_docker%EF%BC%8C%E5%9B%A0%E4%B8%BA%E8%BF%99%E6%A0%B7%E7%9A%84%E7%8E%AF%E5%A2%83%E6%9B%B4%E5%8A%A0%E9%80%9A%E7%94%A8%E4%BA%9B%EF%BC%8C%E6%89%80%E4%BB%A5%E6%89%8Dpublish%E5%88%B0%E5%AE%98%E6%96%B9%E7%9A%84docker repository里面。 2. 在这个镜像的基础上,构建一个我们工程依赖环境的基础镜像,额外安装了Ruby 2.3,最新的Chrome,git以及一些git的配置,因为需要从企业版github上pull代码。 3. 本地升级node版本,以及相关的grunt,karma,Phantomjs的版本,运行测试通过。 4. 将工程mount容器中,然后运行测试,npm install失败,原因是安装fsevent出错。查看了下这个包,原来只是给OSX下使用的。删除npm-shrinkwrap.json后重新运行可以通过。原因是有人在OSX下运行了npm shrinkwrap去生成的这个锁定版本的文件,真是烦人。于是反其道行之在容器里面生成npm-shrinkwrap.json,在host上运行测试一切完好,就这样解决了这个问题。 5. 在Bamboo创建一个branch,然后针对我的分支代码运行测试 6. 测试里面的一个步骤是做bower install安装第三方js类库,但是比较恶心的是,有些第三方类库是以git的协议去下载,而不是https。本地运行一切都好,但是在Bamboo Agent上运行的时候却出现了连接超时的问题,很有可能是Bamboo所在AWS的network ACL或者是security group没有允许9418端口的TCP访问。不过最后解决的方式并不是修改防火墙或者将协议改为https,而是直接把类库checking到git中,这样对应的修改Gruntfile,不用再运行bower install。check in之后在Bamboo上运行还是失败,本地却可以通过,仔细检查,原来是一部分bower module目录名为dist被git ignore掉了。

通过测试后,接下来就是部署了。部署要解决的问题是,如何让容器拿到AWS role的动态权限去做文件的上传更新操作。ECS好像是支持容器去assume role的操作,但是我们没有用ECS,所以只能考虑其它方式。

我想到的方式在bamboo 的 docker agent上 assume role,拿到对应的credential后,将其作为环境变量传入到容器中。实验证明这样的方式是可行的,万幸bamboo的docker agent支持aws cli命令,不过没有jq稍微增大了点提取credential的难度,脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if [ "$DEPLOY_ENV" = "staging" ]; then
  AWS_ACCOUNT_ID='1111111111'
elif [ "$DEPLOY_ENV" == "production" ]; then
  AWS_ACCOUNT_ID='2222222222'
fi

credentials=$(aws sts assume-role  --role-arn       arn:aws:iam::"$AWS_ACCOUNT_ID":role/roleName \
          --role-session-name roleSessionName \
            --query 'Credentials.[SecretAccessKey, SessionToken, AccessKeyId]'  \
            --output text)

SecretAccessKey=$(echo $credentials | cut -d' ' -f1)
SessionToken=$(echo $credentials | cut -d' ' -f2)
AccessKeyId=$(echo $credentials | cut -d' ' -f3)

docker run  -e BUILD_VERSION="$BUILD_VERSION" \
    -e DEPLOY_ENV="$DEPLOY_ENV" -e AWS_SECRET_ACCESS_KEY="$SecretAccessKey" \
    -e AWS_SESSION_TOKEN="$SessionToken" -e AWS_ACCESS_KEY_ID="$AccessKeyId" --rm docker_image bash -c 'grunt deploy'

因为部署是用aws node 的sdk,所以读取的环境变量名字不太一样,要稍微注意下。

在CI上运行后,staging部署通过,手动在bamboo的docker agent上测试下是否能assume产品环境的部署的role,结果可以,那就是说产品环境的部署应该也可以通过了。

总结

  1. npm sucks,更糟糕的是程序员在引入依赖的时候缺乏考虑,我在package.json里面见到了不少无人维护的component,后续的升级维护是一个问题,联想以前的ruby项目也是一样。一旦有版本升级,碰到无人维护的gem时会非常痛苦。
  2. 一个工程里面用了太多的语言,也是一件很糟糕的事情,明明可以用node的aws sdk来做到所有的部署,不知道为何用ruby去实现,无形中增大了维护的成本。
  3. 一般来说,我们认为docker可以保证不同环境的一致性,但是由于一些特殊原因,如我上面提到的防火墙问题,bower module被git ignore掉的问题,在CI环境下才能暴露出来。所以在PR被merge到master之前,一定要保证修改在CI上也运行通过。

One Interesting Docker Issue

| Comments

项目上Akamai的回归测试运行在数据中心一台用Puppet管理的固定的虚拟服务器上,这台服务器是Bamboo Agent,负责运行所有遗留系统的自动化部署任务。 前几天一个客户的Ops找我帮忙一起让这台服务器支持Docker,然后将测试放在docker中运行。我们修改puppet脚本,然后更新了Docker,结果发现2.6的内核最多运行docker 1.7,而运行测试的docker compose需要的docker客户端要高于1.7。 鉴于改动较大,于是我们换一种思路,用在AWS账户下已有的Bamboo docker agent去运行测试。所以revert了Puppet修改,并且在服务器上运行。 以为一切都结束了,没想到过了几天,另一个组的Ops来找我说staging的部署失败了,问我什么原因,提示大意是没有找到NetScaler服务器的路由。我觉得很奇怪,就看了眼服务器上的路由表。结果发现了下面的现象:

1
2
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.17.0.0      *               255.255.0.0     U     0      0        0 docker0

囧,staging的IP range也是172.17,原来是这个原因。 于是,先停止这个网络设备,然后删除,之后再重启网络服务解决问题。

1
2
3
ip link down docker0
ip link del docker0
service network restart

我觉得从这个错误中可以学到两个事情

  1. 配置管理工具的不可靠性,Puppet并没有完整的清理掉所有docker相关的东西
  2. 这种Pet服务器的不可靠性,如果服务器是每天都按照配置重新创建也不会出现这样的问题

Hand Over DNS Resolve to VirutalBox

| Comments

当你用vagrant新建一个虚拟机(driver 为virtualbox)并使用NAT方式让guest虚拟机连接外网时,如果有无线网络的变化,虚拟机中/etc/resolv.conf不会对应的修改,导致域名解析失败。

解决的办法是将DNS解析的任务交给虚拟机管理工具如virtualbox,假设我们要修改名为test的虚拟机的设置:

1
2
3
4
5
6
7
 ~> VBoxManage list vms
"mesos1" {74214693-3477-4386-a9b7-4abc3b7e608d}
.......
"test" {b269c98f-00e8-49a3-a8d0-53629187ea62}

#保证vm没有在运行,然后执行
 ~> VBoxManage modifyvm test  --natdnsproxy1 on

重新启动vm,不管怎么切换网络,应该都不会再出现域名解析的问题。 如果是用Vagrantfile管理虚拟机的配置,可以更改vm的配置:

1
2
3
config.vm.provider "virtualbox" do |v|
  v.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
end

Using Akamai Diagnostic tools/API

| Comments

有时候在Akamai上提交应用修改后,因为配置的问题,可能出现错误,像下面这样:

1
#30.657008d1.1452737568.1e40544

通过日志查找的方式去发现具体的问题可能会很耗时,因为需要等待akamai把日志上传。Akamai自己提供了解码错误代码的工具和API,具体的用法如下:

Lunar Control Centre 的 Diagnostic Tools


这个比较容易,从Luna Control Center选择Resolve => Diagnostic Tools。在Service Debugging Tools部分选择Error Translator (Reference#),然后在Error String:的input中输入错误码的字符串,点击Analyze,等待一会就可以看到详细的错误信息以及原因。

使用Akamai Diagnostic API


  1. Akamai提供了Sample Client去调用API,除了clone client的repo,还可以直接使用docker,直接运行docker run -it akamaiopen/api-kickstart /bin/bash既可。
  2. 生成新的client请求的token。首先在Luna Control Center选择CONFIGURE => Manage APIs进入Open API 管理页面。在Luna APIs下面添加新的collection,然后在该collection添加新的client,就可以拿到新的tokens,点击右上角的导出按钮,就可以将其导出到一个文本文件中,如名为api-kickstart.txt的文件。
  3. 在client端设置token。在client的目录下运行python gen_edgerc.py -s default -f api-kickstart.txt, 它会在用户根目录生成~/.edgerc的credential文件。通过python verify_creds.py 可以验证credential的有效性。.edgerc文件中的token其实也就是api请求时authorization的headers。
  4. 测试请求。.edgerc文件设置验证完成后,可以使用python diagnostic_tools.py来测试,它实际请求的API endpoint是/diagnostic-tools/v1/locations/diagnostic-tools/v1/dig,返回如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
root@16119b2d4eb8:/opt/examples/python# python diagnostic_tools.py

Requesting locations that support the diagnostic-tools API.

There are 72 locations that can run dig in the Akamai Network
We will make our call from Adelaide, Australia

; <<>> DiG 9.8.1-P1 <<>> developer.akamai.com -t A
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12919
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 8, ADDITIONAL: 8

;; QUESTION SECTION:
;developer.akamai.com.        IN  A

;; ANSWER SECTION:
developer.akamai.com. 300 IN  CNAME   san-developer.akamai.com.edgekey.net.
san-developer.akamai.com.edgekey.net. 21600 IN CNAME e4777.dscx.akamaiedge.net.
e4777.dscx.akamaiedge.net. 20 IN  A   23.4.164.144

;; AUTHORITY SECTION:
dscx.akamaiedge.net.  4000    IN  NS  n6dscx.akamaiedge.net.
...............

Akamai的diagnostic API的列表在这里。ErrorCode解释的endpoint是/diagnostic-tools/v1/errortranslator{?errorCode},通过重用例子中的python代码即可发起这样的请求,比如把diagnostic_tools.py修改如下(我就是这么懒):

1
2
3
+ location_result = httpCaller.getResult('/diagnostic-tools/v1/errortranslator?errorCode=30.657008d1.1452737568.1e40544')
- location_result = httpCaller.getResult('/diagnostic-tools/v1/locations')
+ print location_result["errorTranslator"]["reasonForFailure"]

之后就可以看到错误的原因是ERR_FWD_SSL_HANDSHAKE&#x7c;err_conn_strict_cert,也就是说我没有在CDN设置正确的certificate,导致它和origin的ssl handshake失败了。

如果没有什么特殊的需求,akamai web console中的diagnostic tool就可以满足需求,逼格较高或者有自动化需求的可以从命令行调用API输出错误原因。