php-fpm chroot 迁移至 CentOS 7.2 版本的 segfault 问题

一直是在用 php-fpm 内置的 chroot 功能在跑手头的几个 php 论坛站点。
但是最近将整个 chroot 环境迁移至新的地方之后,出现问题了:
有一个论坛一直是返回 502 状态码。看了下,发现 dmesg 输出有一些信息:

...
[444346.942769] php-fpm[13096]: segfault at 1fae98fc ip 00007f53655429df sp 00007f5347847d80 error 6 in libresolv-2.17.so[7f5365537000+16000]
[444500.294150] php-fpm[13149]: segfault at 1fae98fc ip 00007f53655429df sp 00007f5347847d80 error 6 in libresolv-2.17.so[7f5365537000+16000]
[444500.407066] php-fpm[13151]: segfault at 1fae98fc ip 00007f53655429df sp 00007f5347c07d80 error 6 in libresolv-2.17.so[7f5365537000+16000]
...

chroot 里面的根目录结构是以前从 CentOS 7.1 拷贝创建的,看来和 7.2 不兼容。libresolv 里面发生了 segfault,说明问题应该是和 DNS 解析有关。
试了几次之后,把 7.2 的 libnss 相关 so 文件拷贝到 chroot 里面后,问题解决了。以防万一,接下来干脆把 chroot 里面的所有 so 更新了一次。

HEVC/H.265

仔细想了想,最近一年的时间里,对我的生活改善最为明显的技术是——HEVC/H.265 编码的视频。

现阶段唯一的缺陷是硬件解码的支持并不广泛,Intel 也只有从 Skylake 才开始支持 HEVC/H.265 的硬件编码、解码,不过也只是 8-bit 色深的,10-bit 的基本没有效果,仍然只能靠软解码,吃 CPU。就拿手头的 Intel NUC6I5SYH 来说,它的 CPU —— Core i5-6260U 碰到 HEVC 10-bit 的 4K 视频文件,是无法流畅播放的。(这是当然的,其实笔记本的 i7-4700HQ 都没法流畅播放的,基本没试过其它什么场景能把 CPU 负载跑到这么高的。)

不是说一定要看 10-bit 而不是 8-bit 的视频文件,只不过多数能找到的片源都是 10-bit 的了。

Fxxk HHVM

hhvm 大概是我见过的稳定性最差的开源软件之一了,也不知道这项目到底有没有QA的,反正我是碰到过很多次一更新,这破东西就挂了,起不来

Kibana 4 仍然不支持“其它”聚合

因为 Kibana 3 早就停止开发和维护了,也不支持新版本的 Elasticsearch,现在无论愿不愿意其实都该使用 Kibana 4 了,但是对于我来说 Kibana 4 有很大的问题——无法在饼图里面添加 Top N 之后,把其它的东西统统归入“其它(other)”值里面去。缺乏这个功能,很多时候都导致图表的实际意义并不大了,因为没有正确显示某个值的百分比。

Github 上面早有 issue 提交和跟踪了,但是还没有解决。

Kibana 4 另外的一个失望之处是矢量地图被抛弃了。

只能希望慢慢修复好吧。。。

CentOS 7 上面 curl 的响应时间增大问题

前段时间一同事在问为甚么同样是 curl ,在 CentOS 7 上面抓取一个网页的域名解析延迟总是会多 150ms 左右?甚至在抓取 localhost 时候也是一样。具体说来,使用的命令是:

[[email protected] ~]# curl -o /dev/null -s -w \
'%{time_namelookup}:%{time_connect}:%{time_starttransfer}: \
%{time_total}:%{speed_download}\n' http://localhost
0.150:0.151:0.151:0.151:47442.000

对比了一下 CentOS 6 上面的结果,确实差距很大

[[email protected] ~]# curl -o /dev/null -s -w \
'%{time_namelookup}:%{time_connect}:%{time_starttransfer}: \
%{time_total}:%{speed_download}\n' http://localhost
0.001:0.001:0.001:0.001:114975.000

同事怀疑是 CentOS 7 的域名解析方式有变化,然而我换做用 wget 去抓发现 CentOS 7 延迟也是很低的,排除了这个可能。原因应该是出在 curl 上面。

排查方式:
用 ltrace 加上 -T 参数打印时间戳跟踪了一下,发现是 curl_easy_perform() 这个函数这里出了问题:

Google 了一番之后,发现这个问题其实已经有了修复,将 50ms, 100ms, 150ms… 这样的延迟增长替换为 4ms, 8ms, 16ms… 这样的了。只不过 CentOS 7 里面的版本较老,还没有加入这个补丁。

今天因为 RHEL 7.2 的发布,看了下 Changelog,也已经合入这个补丁了。
https://bugzilla.redhat.com/show_bug.cgi?id=1130239

国内网站全站 HTTPS 逐渐流行

刚才浏览豆瓣的时候,发现从 Google 结果页跳的豆瓣音乐页面已经是 https 开头了,于是再试了一次它的主站点(https://www.douban.com)也已经可以访问,支持了 TLS 协议,终于不是再像不久前那样跳回到未加密的 http 页面。

用浏览器控制台稍微看了下豆瓣的资源加载情况(https://music.douban.com/subject/26651208/):
(图片可以右键打开看原图)

对比通过 http 协议请求的时候:

两者的区别其实有些有意思的地方。

现如今一个 web 页面已经不太可能只是引用单一域名下面资源(因为性能、扩展等诸多方面的原因),而这点恰好成为现实中部署 https 的最大阻碍之一——你需要将页面包含的所有资源都走 https 才行!在 CDN 时代,这意味着你需要 CDN 的支持才行。举例来说就是 http://music.douban.com/subject/26651208/ 这样的 url,如果你想将它部署到 https,那么页面里面加载的 img#.douban.com 域名也同样要部署 https!

看起来走 https 的豆瓣的部署是通过将js, css, 图片等静态资源放到一个新的域名(doubanio.com)下面来提供的,而不是像一般的做法那样——直接在原来的 img#.douban.com 静态资源域名下面加 https。这在实现上需要后端根据不同协议在页面插入不同域名。这点来说,推测应该是这样:豆瓣为了尽量不影响 http 协议的访问,引入了新的 doubanio.com 域名来发布静态资源。实际上通过查看 doubanio.com 的 DNS CNAME 记录,可以看到这个域名是指向了腾讯云的 CDN,豆瓣静态资源站点是通过腾讯云来支持 CDN 的。说实话,看到国内 CDN 终于也开始支持 https,其实挺意外的(当然,也许是我在这点的认知已经太落伍了,12306 的“证书污染”可能是过去式了吧)。如果没有记错的话,以前 img#.douban.com 这些域名是在用蓝汛的 CDN,至于为啥不用了,本人当然是不知道的:)。目测豆瓣在 https 部署稳定后,也会将 http 页面的静态资源转向 doubanio.com (其实目前观察来看已经有这个趋势了)。这样,整个 https 化的过程就是平滑而用户感知不到的了。

腾讯云CDN 用了 spdy/3.1 协议,这点对于性能来说还是挺不错的。不过也许为了安全性,豆瓣正式推出后应该加个 HSTS 响应头的,毕竟,这么做的本意也就是为了用户的安全,默认的 http 还是会让用户被运营商劫持个遍。

另外一个观测到也正在部署 https 的站点是京东。

已经使用了通配证书,但是静态资源站点却并没有走 https,因此浏览器拒绝加载了。看了下,仍然是 CDN 不支持,直接访问 https 的话,是 12306 的证书。

这么多年以来,国内的一些大网站终于开始部署 https,此前有百度、阿里,现在豆瓣、京东正在部署。对于用户来说这是个好事,至少可以少很多中间人攻击/劫持,运营商插入广告等行为(这个应该才是电商网站部署的初衷)。但是更重要的也许还是数据库里面用户信息的保护。

Varnish: How do You Know Your Backend Responses Properly?

You might use varnishstat to monitor backend healthy status, but that is not so directly, instead you can do this:

# varnishlog -g session -C -q 'ReqHeader:Host ~ "example.com"' | grep Status
--  RespStatus     200
--  RespStatus     200
--  RespStatus     200
--  RespStatus     200
--- BerespStatus   200
--- ObjStatus      200
--  RespStatus     200
--  RespStatus     200
--  RespStatus     200
--  RespStatus     200
--- BerespStatus   200
--- ObjStatus      200
--  RespStatus     200
--- BerespStatus   200
--- ObjStatus      200
--  RespStatus     200
--- BerespStatus   200
--- ObjStatus      200
--  RespStatus     200
--- BerespStatus   200
...

Convenient, right? you can tail and grep whatever you want of varnishlog’s output.

Whitelisting IPs in Limiting Request Rate of Nginx and Varnish

Sometimes we want to exclude IP blocks from limited request rate zone of web servers, here is how we can do it in Nginx and Varnish, the Nginx way needs crappy hacks, on the other hand, Varnish handles it really elegant.

The Nginx way:

# in the http block, we define a zone, and use the geoip
# module to map IP addresses to variable
http {
    ...
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
    # geo directive maps $remote_addr to $block variable.
    # limited as default
    geo $block {
        default          limited;
        10.10.10.0/24    blacklist;
        192.168.1.0/24   whitelist;
        include more_geoip_map.conf;
    }
}

# server block
server {
    ...
    location /wherever {
        if ($block = whitelist) { return 600; }
        if ($block = limited)   { return 601; }
        if ($block = blacklist) { return 403; }
        # error code 600 and 601 goes to each's internal location.
        error_page 600 = @whitelist;
        error_page 601 = @limited;
    }

    # @whitelist have no limiting, it just passes
    # the requests to backend.
    location @whitelist {
        proxy_pass http://backend;
        # feel free to log into other file.
        #access_log /var/log/nginx/$host_whitelist.access.log;
    }

    # insert limit_req here.
    location @limited {
        limit_req zone=one burst=1 nodelay;
        proxy_pass http://backend;
        # feel free to log into other file.
        #access_log /var/log/nginx/$host_limited.access.log;
    }
    ...
}

The Varnish way:

vcl 4.0;

import vsthrottle;

acl blacklist {
    "10.10.10.0/24";
}

acl whitelist {
    192.168.1.0/24;
}

sub vcl_recv {
    # take client.ip as identify to distinguish clients.
    set client.identity = client.ip;
    if (client.ip ~ blacklist) {
        return (synth(403, "Forbidden"));
    }
    if ((client.ip !~ whitelist) && vsthrottle.is_denied(client.identity, 25, 10s)) {
        return (synth(429, "Too Many Requests"));
    }
}

As you can see, unlike Nginx, Varnish has the powerful if directive, it works just like you’d expect.