在 Debian 中启用 TCP BBR 算法

博客恢复之后第一件事就是升级了一下主机。现在最新的 Linux 内核是 4.9,并且带了一个新的 TCP 算法,称为 BBR (Bottleneck Bandwidth and Round-trip)。

BBR 算法由 Google 提出,原先主要用于 Google 内部网络的速度提升,现在 Google 把它提交到了 Linux 内核,所有人都可以使用了。从 Google 的报告来看,这一新的算法可以明显降低网络延迟。Youtube 全球的延迟比之前的 CUBIC 算法下降了 50% 以上。从大众的讨论来看,这一新的算法主要被用于不可告人的活动。当然,由于我用的是 Google 的主机,现在如果又用了 Google 算法,是不是会更快呢?

开启的方法如下:

1. 先升级到最新的内核(适用于 64 位的 Debian Jessie):

2. 开启 BBR

打开 /etc/sysctl.conf文件,在文件末尾添加两行:

其中第一行 default_qdisc指的是默认的 TCP 队列算法,fq 是 Google 推荐的算法,更适用于 BBR。第二行则是开启 BBR 算法。

3. 重启并验证

直接重启主机是可以的。如果不想重启的话,可以使用以下命令来加载新的配置:

然后使用下面的命令来验证 BBR 已生效:

结果会显示 BBR 加一串数字,说明 BBR 算法已启动。

4. 效果

由于我的博客已经很快了(笑),一时半会看不出什么效果。不过启用了之后心理感觉还是不错的。

投靠 Ubuntu

经过一番纠结之后,我还是把博客换到了 Ubuntu 之上。

之前提到过,从 AWS 转到 GCE 的时候,被迫选择了 RHEL 7(Redhat Enterprise Linux),因为只是 RHEL 7 是完全兼容 AmazonLinux 的。虽然 RHEL 需要另外付费,但为了平滑过渡,我还是以谨慎为先。

RHEL 有非常完善的商业支持,文档很齐全,有什么问题一查就可以明白。唯一的问题是,它的收费太高了,GCE 上面用一个月需要 50 刀以上,且不像主机一样用的时间长会打折,这样算下来,年费和一个 Radhat 的订阅差不多价格。但是我并不需要什么客户支持,技术问题都可以靠 Stackoverflow 搞定,这个订阅费用就白白浪费了。这也是我转向 Ubuntu 的主要原因。

还有一个原因是 HHVM。HHVM 是 Facebook 的 PHP 引擎实现,完全兼容 PHP 5.5 (及以上)。WordPress 从 3.9 开始,完整兼容 HHVM。

HHVM 的响应时间,据说是 PHP-FPM 的一半,也就是提升了一倍的吞吐量。原生 PHP 要做到这个效果,需要等 PHP 7 发布才行。看着各种发行版对于新软件的保守程度,不知道 PHP 7 要到什么时候才能成为主流。另一方面,Facebook 的研发能力肯定要比 Zend 来得强,等 PHP 7 发布了,HHVM 的性能肯定会更强。于是我就抛弃了原生 PHP,转投 HHVM。

当然,我也可以在 RHEL 上自己编译安装 HHVM。我试过,GCE 的机器编译了一个小时,我都快睡着了才编译完成,但安装(make install)的时候,告诉我某个文件不存在 -_-。我还是老老实实用官方的安装包吧,而 HHVM 官方只提供了 Debian 系列的安装包。

为什么没有选 Debian?Debian 保守到现在还只提供了 Apache 2.2,无语。

那也可以转投 Nginx 呀?我不高兴重写那一堆的 RewriteRule,并且 PageSpeed 在 Nginx 上也需要自己编译安装,不想再重蹈 HHVM 的覆辙。

由于 HHVM 和 PHP-FPM 的用法几乎一模一样,转换过程没出现任何问题,我做的唯一的工作就是重新设置了 Apache。花了几个小时,不过没什么难度,转换过程很顺利。

现在博客已经运行在 Ubuntu 14.04 + Apache 2.4 + HHVM 3.5 + MySQL 5.6 之上,欢迎测试,有问题请留言。

配置 SendMail 通过 SMTP 发送邮件

今天不知怎么的,SendGrid 插件突然抽风了,导致博客后台完全打不开,显示为 503 Service Unavailable。一怒之下就把 SendGrid 插件给删了。

但是不用 SendGrid 插件的话,就要自己配置邮件服务了,没辙,硬着头皮上吧。

先说一下背景,主机环境是 Google Compute Engine,RHEL 7 系统,使用 SendGrid.net 作为 SMTP 服务。Linux 中有个邮件工具叫作 SendMail,它是一个命令行工具,PHP 也是用它来发送邮件的。所以把 SendMail 配置好了,不仅 WordPress / PHP 可以正常发送邮件,其它基于 SendMail 的工具也可以正常工作了,一举两得。以下命令假设已有管理员权限。

首先,安装必须的系统组件,其中的 cyrus-sasl-plain 是一个加密组件,如果没有安装的话,会出现错误“AUTH=client, available mechanisms do not fulfill requirements”:

设置 SMTP 服务器,在 /etc/mail/sendmail.mc 中找到下面的第一行,然后如下添加第 2、3、4 行:

需要注意的是其中端口指定为 2525,这是 Compute Engine 和 SendGrid 的约定,一般的端口是 25 或者 587。

然后在 /etc/mail/access 中添加 SMTP 帐号:

其中 Username 和 Password 要替换成真实的用户名和密码,在 SendGrid 中,用户名/密码和登录时候用的一样。

然后编译相关配置并重启 SendMail 服务:

这样就配置好了,为了检验 SendMail 能正常工作,我们来做一个简单的测试:

先创建一个文本文件,mail.txt:

Subject: SendMail Test
Random Email Content

然后执行命令:

如果能收到测试邮件,就说明配置成功了;如果不能,可以检查 /var/log/maillog 文件看是哪里出错了。

没有商业公司支持的开源软件就是一个笑话

这件事要从几个月之前博客升级到 Apache 2.4 说起。

7月份的一天,我发现 AWS 提供了一种新的机型,比我当时用的要便宜,而且性能更好。由于硬件的变动,AWS 不能提供无缝地迁移,一定要手动操作,于是我就计划着迁移一下。迁移之前我研究了一下所需的各个软件的最新版本,我有个习惯,新装系统的时候,都会装上最新版本的软件,我相信新的版本更安全,性能更好。博客所用的软件主要就是三个,其最新版本分别是:Apache 2.4、PHP 5.5 和 MySQL 5.5。

装软件嘛,没什么复杂的。

我事先调查了一下,最新版本和我当时用的版本之间的差别,主要是 Apache 2.4 和 2.2 的配置文件格式有比较大的差别,还有就是 PHP 5.5 中自带了 PHP-FPM,它其实也和 Apache 有关系。说到底也就是 Apache 的配置有比较大的改动,其它都正常。具体的改动这里不多说,详细的说明请参见《在EC2中安装 Apache 2.4、PHP 5.5 和 MySQL 5.5 并运行 WordPress》一文。

Apache 2.4 和 2.2 的差别(业界称为 breaking change)我就不抱怨了,这和下面要说的东西真是小巫见大巫了。它的差别大到了一个程度,使得目前 2.4 在全球的使用率只占了 Apache 全部的 10% 不到,而 2.4 第一版是在 2012 年 2 月发布的。一个发布了 2 年多的软件只有 10% 的使用率,Windows Vista 见到了也会乐开了怀。

差别很大不要紧,哥搞定了。

之后过了几个月,就是最近,公司给了一些员工福利,Google Cloud 的优惠。为了充分利用这些个优惠,同时也体验一下 Google Cloud,我就把博客整体迁移到了过来。

Google Compute Engine 所支持的 Linux 发行版有:RHEL 7、CentOS 7、SUSE 12、openSUSE 13.1、Debian 7、Ubuntu 1410等。和 AWS 不同的是,AWS 提供了一种称为 AmazonLinux 的系统,虽然它不是出自名门,但看名字是 Amazon 重点支持的对象,毫不犹豫就选了它。而 Compute Engine 没有类似的设定,于是我就把所有的版本都试了一下。接下来就说一下它们之间的异同。

首先是安装包的管理,RHEL 和 CentOS 用的是 yum、SUSE 和 openSUSE 用的是 zypper、Debian 和 Ubuntu 用的是 apt-get。这倒也没什么,虽然名字不一样,指令都大同小异,搜索一下 5 分钟就学会了。

接下来安装三大软件。装软件嘛,没什么复杂的。

之前在 AmazonLinux 上见识过,Apache 的安装包分为 httpd 和 httpd24,因为 2.2 过于流行,httpd 一直都被用于安装 2.2,那 2.4 只好用了另外的名字。回到Compute Engine 这一边,CentOS 的 httpd 直接指向了 2.4,表扬一下;RHEL 和 AmazonLinux 类似,只是 2.4 的名字是 httpd24-httpd,这也好理解,问题不大;而 Ubuntu 中的名字是 apache2,这也没什么;但是,SUSE、openSUSE和Debian 默认都没有带 Apache 2.4 的安装包,这…………。openSUSE 要升到 13.2 才有 2.4 的安装包,两年都没把 2.4 加入官方软件库,这算是几个意思。

于是我只剩下了四个选择,httpd24 和 apache2 各两个,于是我就都安装了一下。虽然它们都是 Apache 2.4,应该都是编译自相同的源文件,但是安装完之后的情况截然不同,httpd 装完是这个样子的(目录结构取自 tree -d,下同):

而 Ubuntu 的 apache2 是这个样子的:

openSUSE 中是这个样子的:

直接吓尿了。

由于 Apache 并不是装完就可以用,还要配置一大堆的东西,比如 vHost、SSL,还有和 PHP-FPM 的通信等等,我相信它们只是目录结构不同,配置方法还是大同小异的,但是我不想再重新走一遍当时 2.2 升 2.4 的时候的各种坑,于是果断选择了 RHEL 或者 CentOS。

另外还有一个问题是 PHP 5.5。由于 PHP-FPM 在 5.5 中才成为官方插件,并且只有 PHP-FPM 才能体现出 Apache 2.4 的 Event MPM 的效率。于是我非常希望在新的系统中继续使用 5.5 或后续版本。而因为这样,最终被迫使用了 RHEL。RHEL 7 是唯一一个满足我所有需求的版本。当然 RHEL 也有坑,就是 SCL,它和传统软件会产生兼容性问题,但想想这种方式长远来看也算不错,毕竟还有 Redhat 在背后支持,于是就用了。

当然,你可能会说这几个软件在第三方软件库中都能找到,那好,我找给你看。比如有个软件库叫“REMI”,貌似用的人还蛮多,但是,CentOS 的官方 wiki 把 REMI 列在了 “Known Problem Repositories”,就是“有问题的软件库”,而另一个软件库,webtatic,在 wiki 中就压根没有提到。CentOS 的 wiki 写得还蛮详细的,其它几个发行版连类似的 wiki 都没有。你说我敢用么。

事情还没有结束。

Compute Engine 提供了一个功能:startup script。意思是写一段 bash 脚本,放在比如 github 上面,然后在创建虚拟机的时候指定运行那段脚本,做一些自动化部署的工作。看上去蛮好用的一个功能,但实际用起来问题一堆。对,又是 Apache 的问题。

Apache 有自己的配置文件格式,扩展名是 .conf,github 上对应的格式是 apacheconf。这是一种纯文本的格式,类似 INI + XML,样例可以看之前的文章。而 Apache 官方并没有提供 API 来修改 .conf 文件,大家都只能用文本编辑器来手动修改。且不说这种方式效率差并且非常容易出错,问题是它没法写在 bash 脚本中啊。比如我要把 /etc/httpd/conf/httpd.conf 中的第xx行从下面这样:

修改成

肯定有人会嘲笑我不会用 Linux 中的工具,比如 sed。sed 是一个很强大的工具,它能以各种花哨的方式来替换文本内容,我看到过 stackoverflow 上各种答案都用它。但是,sed 固然强大,但它无法预知未来啊。现在 ServerName 是写在 httpd.conf 中的,万一它某一天被移到 servername.conf 中了呢?现在它叫 ServerName,万一之后被改成 HostName 了呢?不要笑,这些变动在 Apache 的历史上都发生过,难说之后不会再发生。

PHP 和 MySQL 也没好到哪里去,它们都用纯文本的配置文件,文件名、文件内容都根据 Linux 和自身的版本不同而不同。

虽然我可以花个几个月编写并调试出一份完美的 bash 脚本,但这也并不能保证它在未来的版本中继续可用。于是我就放弃了 startup script,依然手工配置了所有的东西。

各种问题都是小 case,哥都搞定了,博客继续稳定运行。

这段经验不禁让我想起一个视频,视频作者从 Windows 1.0 逐版本一直升级到 Windows 8,在 Windows 95 的时候装了一个游戏 DOOM 2,升到 Windows 8 之后,DOOM 2 还可以继续玩。

上述的各种软件和 Windows 比起来,就是一个笑话。

在RHEL 7中安装mod_pagespeed

Redhat 在它的 Linux 发行版 RHEL(Redhat Enterprise Linux)中引入了一个新功能:Software Collections,简称 SCL。SCL 的工作原理和其它的软件部署工具不太一样,SCL 不会把软件装到系统目录中,而是做了一个虚拟的目录结构;而安装的软件默认不启用,需要使用特定命令开启。

拿 PHP 5.5 举例,安装完成之后,PHP 5.5 会出现在 /opt/rh/php55/ 目录下,使用 scl enable php55 可以把它开启。比如查看 PHP 的版本:

使用 PHP 貌似没什么太问题,但是 Apache 和 mod_pagespeed 就有点不和谐了。Apache 2.4 装好之后,在 /opt/rh/httpd24 中。但是安装 mod_pagespeed 的时候,RPM 会抱怨说:

httpd >= 2.2 is needed by mod-pagespeed-beta

而使用了 scl enable httpd24 之后,问题依旧。我还没有仔细看 scl enable 的原理是什么,但基本上它和 RPM 的沟通不太正常。

不过不要紧,RPM 有一个 --nodeps 选项,即不检查所需的依赖库。把原先的命令改成这样即可:

但是呢,还是有一点点的小问题。mod_pagespeed 默认的安装目录是 /etc/httpd,但真正的 httpd 还在 /opt/rh/httpd24 下面,于是我们要么把装好的文件复制过去,要么在安装之前做一个符号链接(Symbolic Link)。我倾向于后者,因为这样可以解决后续的升级问题。制作符号链接的命令如下:

之后再安装 mod_pagespeed 就没有问题了。

启用真正的 WP Cron

Cron job 是 *nix 系统中的一种定时任务,可以指定以某个固定周期来运行,相当于 Windows 中的“计划任务”。Wordpress 中也有类似的实现,用于执行 WordPress(包含插件)特定的任务,比如检查更新等,称为 WP Cron,由 wp-cron.php 负责。和 Cron job 不同的是,Wordpress 中没有一个“守护进程”来触发 WP Cron,WP Cron 是由每一次用户访问页面来触发的。换句话说,每次有用户访问的时候,Wordpress 都会调用 wp-cron.php 来检查是否有后台任务需要执行,有的话就创建一个后台进程去执行一下。

虽然 WordPress 官方一直宣称 WP Cron 并不是影响用户打开页面的时间,但有很多民间调查显现 WP Cron 会增加 200ms 到 500ms 的页面打开时间。与其让每个用户都等待,还不如设置一个专门的 Cron job 来做这件事。

具体做法如下:

1. 禁用 WP Cron。在 wp-config.php 中添加一行:

2. 在 crontab 中添加一条新记录(不需要管理员权限):

其中“*/10”表示每10分钟执行一次,你也可以改成其它周期,curl 是下载指定 url 的 linux 工具。修改 crontab 的命令是

注意,如果你用多个 WordPress 站点,比如使用了 WordPress multisite,你需要对每一个站点都 curl 一下,而 wp-config.php 只需要改一次。

参考资料:

EBS上超高频率的IO写入

这几天注意到我的EC2机器上有非常大量的IO,导致AWS的总体开销增加了很多。比如9月份的帐单中:

I/O requests     120,076,984 IOs     费用$14.41

30天有超过1亿次IO,也就是每秒钟有40几次IO,这个频率也高了一点吧……

于是尝试了以下优化的方法:

  • 把mod_pagespeed的缓存移到/dev/shm中。缓存嘛,无所谓的。
  • 把apached的日志文件也移到了/dev/shm中,反正我也不经常看log。

然后发现IO次数没有明显下降。后来开了iotop监测进程的io,也没有发现明显的异样。

再后来实在没办法的时候,随便尝试了一个优化,把/tmp做成tmpfs,就是在/etc/fstab中添加一行:

然后IO就神奇地降下来了…下图是36小时内EBS的VolumeWriteOps数据,可以看出,把/tmp做成tmpfs后,WriteOps的数量明显下降:

虽然还不知道到底是哪个进程在频繁写入/tmp,不过钱已经省下来了,还算蛮开心的……