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

这件事要从几个月之前博客升级到 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 比起来,就是一个笑话。

在 EC2 中安装 Apache 2.4、PHP 5.5 和 MySQL 5.5 并运行 WordPress

0 前言

在更换主机的同时,我顺便也升级了一下软件。之前使用的是 Apache 2.2 + PHP 5.3.5,没有 MySQL。Apache 2.2 依然是世界上使用最多的网络服务器,只不过它已经很老了,目前已停止新功能的开发,只进行安全更新。只是它一直都运行地很好,多数网站都没有动力去升级,以至于已经推出了 2 年的 Apache 2.4 目前还只有 3.5% 的使用率。同样,由于 PHP 5.3 到 5.4 有一些不兼容的更新,使得 PHP 5.4+ 也普及得比较慢。

新版本的使用人数少,相关的问题解答和文档也相应较少,这里就记录一下我的升级步骤,或许对其他人也有帮助。本文针对 EC2 中的 Amazon Linux(基于 Fedora),如果你使用的是其它的 Linux 发行版,部分命令需要相应更改。

1 安装和配置 Apache

1.1 安装 Apache

1.1.1 安装 SSL 模块(可选)

如果你的站点不需要 HTTPS,可以不装。

1.1.2 安装 Pagespeed 模块(可选)(详细介绍

推荐但不是必需的,Pagespeed 可以极大地提升页面打开速度,只要安装好就可以看到效果,不需要额外配置。

1.2 配置 Apache

Apache 的目录是/etc/httpd,2.4 的目录结构和 2.2 有很大的差别,不建议直接把 2.2 的配置文件直接复制过来,还是重新配置为好:

1.2.1 修改 /etc/httpd/conf/httpd.conf

1.2.2 修改 /etc/httpd/conf.modules.d/00-mpm.conf

1.2.3 新建 /etc/httpd/conf.d/y-common.conf

1.2.4 新建/etc/httpd/conf.d/z-leonax.conf

如果你已经有 vHost 的配置,可以直接复制过来,如果没有,可以按下面的方法创建一个简单的 vHost。vHost 的作用是告诉 Apache 域名和本地文件的对应关系。

2 安装和配置 PHP-FPM

PHP-FPM 是一个 PHP 的运行环境,FPM 全称 FastCGI Process Manager。它相当于一个独立的 PHP 服务器,专门用于解释 PHP 文件。和 Apache 2.2 中的 PHP-Fcgid 不一样的是,PHP-FPM 不再依赖于 Apache。它的性能更好,而且完全兼容 PHP 5.5。

2.1 安装 PHP-FPM

其中 php55-mysqlnd 是 MySQL 的连接库,php55-opcache 是新的 PHP 缓存组件,用于取供之前的 APC。

2.2 配置 PHP-FPM

2.2.1 修改 /etc/php-fpm-5.5.d/www.conf

2.2.2 修改 /etc/php-5.5.d/opcache.ini

2.2.3 修改 /etc/httpd/conf.d/php-5.5.conf

2.2.4 删除 /etc/httpd/conf.modules.d/10-php-5.5.conf

这个文件原本是用来加载 mod_php 模块的,但我们已经改用 PHP-FPM 了,所以就把它删掉。

2.2.5 在 vHost 中添加 PHP-FPM 的Proxy

这段指令让 Apache 把所有的 PHP 文件都交由 PHP-FPM 处理,可以把它写在 vHost 的最后一行。

3 安装和配置 MySQL

3.1 安装 MySQL 5.5

其中 mysql55 是 MySQL 的命令行工具,mysql55-server 是数据库的运行环境。

3.2 配置 MySQL 5.5

3.2.1 修改 /etc/my.cnf

3.2.2 添加新的用户

为了安全起见,新的数据库可以设置一个单独的帐号来访问。帐号出问题也只影响这一个数据库。当然你也可以用 root 直接访问,并跳过这一部分。

3.2.3 把之前 MySQL 的数据导出

如果不需要导入之前的 WordPress 数据,可以跳到 3.2.5。

3.2.4 导入备份的数据

3.2.5 修改 wp-config.php

假设你已经复制好了 WordPress 的源文件,如果还没有的话,请参考附录 5.1。

4 主机配置

4.1 启动各项服务

之后你的 WordPress 博客就可以访问了。

4.2 自动启动各项服务

如果不设置这一步,机器重启之后,各项服务是不会自动启动的。

4.3 把 /tmp 移到 tmpfs (原因

修改 /etc/fstab 文件,在最后添加一行:

4.4 创建 swap 分区

swap 分区是在内存不够用的情况下,把一些进程暂时写到文件中,以达到释放内存的效果。通常来说 t2.micro 的内存够用了,一般用量在 800M 左右,但以防万一,我还是做了一个 swap 分区。以下代码会创建一个 1GB 大小的 swap 文件:

继续修改 /etc/fstab 文件,在最后添加一行:

5 附录

5.1 下载并安装 WordPress

6 更新历史

  • 2014.07.31 初版
  • 2015.12.18 修复一些小错误

保护 WordPress 的帐号安全

使用 WordPress 写文章,无论是在 WordPress 官方,还是自建的博客,都需要至少一个帐号。而 WordPress 到目前(3.9.1)为止,都只提供了弱智的用户名/密码的登录方式。这种方式可以被数不清的方式来破解。最简单的一种是暴力破解,就是一个一个的尝试可能的密码组合,如果密码的长度不够,比如少于8位,可能会在一、两天之内就破解掉。由于博客的是公开并一直在线的,暴力破解也没有高深的技术含量,任何人只要有一个暴力破解工具,都可以进行尝试。于是 WordPress 的帐号安全性相当差。

以前我试过一个插件,它提供了登录时的两步验证,两步验证本身固然是非常安全的,但是插件就不一定了,就像上一篇文章所说,如果插件的设计有漏洞,也可能被用来进行攻击。所以任何用于保护帐号安全的插件,都是一把双刃剑,在提供多重验证的同时,又打开了另一道门……

那有什么不用插件的保护方式么?答案是有的,只是不那么方便。

WordPress 的登录入口是 wp-login.php,所有的登入登出的逻辑都在这个文件中,而一旦登入完成之后呢,之后的操作则依靠 wp-admin/ 目录下的其它文件。于是,只要防御好 wp-login.php,Wordpress 的帐号就不会被暴力破解。我的方法是这样的:在 Apache 中把 wp-login.php 设为不可访问,只有当自己需要登录的时候才把权限打开。示例代码如下:

由于 WordPress 的登录会持续一段时间(好像是一个月?),于是在这一个月中都不需要去动 Apache 的设置,只有在一个月后浏览器的 cookie 失效的时候,才要麻烦一点去重启一下 Apache。使用虚拟主机的朋友们也可能通过修改 .htaccess 文件来达到类似的效果,比如使用 mod_rewrite 把 wp-login.php 重写到根目录。至于其它的帐号安全,比如 SSH 的密码,或者虚拟主机的 CPanel 帐号等,那又是另外一回事了,以后再说。

这样做的好处是不需要安装任何的插件,简洁、高效,更不用担心插件的升级维护问题,同时也达到了和插件一样的安全防护的目的。一举两得。

<2014.07.31更新> 如果你的博客启用了 xmlrpc 的话,还需要把 xmlrpc.php 禁用掉,因为 xmlrpc 中允许 WordPress(手机)客户端登录,也可以被用来暴力破解。

<2014.08.04更新> 如果你的博客中有需要密码才能访问的博文,在 wp-login.php 被禁用之后,那些博文将不能被访问。因为验证访问密码的逻辑也在 wp-login.php 中。

使用 Worker MPM 和 mod_fcgid 加速解析 PHP

什么是 Worker MPM?
MPM(多处理模块,Multi-Processing Modules)是 Apache 中的一种特殊模块,它被用来绑定到网络端口上,接受请求, 以及调度子进程处理请求。在 Apache 的运行环境中,只能使用一种 MPM,即不能混用,而 MPM 又和处理请求直接相关,于是 MPM 的效率对网站的响应速度有很大的影响。

Linux/Unix 上的 Apache 可以使用三种 MPM:Prefork、Worker 和 Event。其中 Event 在2.4之后才稳定下来,而2.4还没什么人用,这里暂时不作讨论。Prework 和 Worker MPM 的主要区别是:

  1. Prefork MPM 使用多个子进程,每个子进程只用一个线程,一次处理一个请求。
  2. Worker MPM 使用多个子进程,每个子进程创建多个线程,可以同时处理多个请求。

对于单独一个请求来说,Prefork 和 Worker的性能没有太大差异,但如果并发请求数量很多,Prefork 会产生出更多的进程,从而消耗更多的内存。对于 AWS 上的小型机来说,内存是影响网站性能的关键,所以在 AWS 上推荐使用 Worker MPM。

如何开启 Worker MPM?
Apache 中默认使用的是 Prefork,因为它是线程安全的(只用一个线程)。要切换到 Worker MPM 需要进行一些额外的设置。

首先检查一下 Worker MPM 有没有被安装上:

在输出当中能看到“worker.c”字样的话,就说明可以使用 Worker MPM。大多数情况都是有的。

接下来打开 /etc/sysconfig/httpd,把 HTTPD=/usr/sbin/httpd.worker 之前的#号删掉,重启 Apache 就可以了。

如何配置 Worker MPM?
Worker MPM 的配置在 Apache的配置文件中,打开 /etc/httpd/conf/httpd.conf,可以找到类似下面的内容:

这是我使用的配置,可以根据实际情况进行修改。详细解释可以参考 Apache 官方文档

什么是 mod_fcgid?
Apache 默认使用 mod_php 来解析 PHP 代码。mod_php 的弱点是它不是线程安全的。这在 Prefork 中不成问题(因为它没有多线程),但是一旦使用了 Worker,mod_php 就不能再用了,Apache 会经常崩溃。解决方案是使用 mod_fcgid 来解析 PHP。

如何安装和启用 mod_fcgid?
首先,运行命令:yum install php php-cli mod_fastcgi

然后打开 /etc/httpd/conf.d/php.conf,把其中的内容替换为:

收尾
最后,重启一下 Apache 就可以了:

以上的所有命令都适用于(基于 Redhat 的)Amazon AMI,如果你使用的是其它的 Linux 分支(比如 Ubuntu),可能需要适当地修改一些指令,比如把 yum install 换成 apt-get。

故障时自动重启Apache

最近不知道为什么博客总是莫名其妙地挂掉,重启Apache就好了,我也懒得去研究到底是哪里出了问题。只是每次都需要手工SSH上去重启Apache,有点麻烦。而且有时候在夜里挂掉,一晚上博客就都不能访问了。

后来终于忍无可忍,写了一段脚本来做这件事,代码如下:

原理很简单,就是尝试访问一下博客(第三行),如果有什么问题,就重启Apache(第七行)。其中wget的参数--spider表示只是访问一下,并不下载内容。如果你喜欢的话,还可以在else中加一段发邮件的代码,把Apache的error log直接发到邮件,方便分析,不过我就懒得做了。

然后把这段代码保存为check_apache.sh,添加到crontab中:

其中*/5表示每5分钟运行一下check_apache.sh。注意打开crontab的时候要使用sudo,因为重启Apache需要sudo权限。

用Apache模拟域名转向

作为向Google Cloud转移的一部分,我最近开始尝试使用App Engine。

Google Cloud最大的缺点在于不自带DNS,所有的域名设置都是通过CName来做的。据说Windows Azure也是这样,三大云服务里只有AWS有自己的DNS。使用CName的缺点是无法绑定裸域名(Naked Domain)。裸域名就是形如leonax.net一样的域名,而www.leonax.net则是一个二级域名。裸域名的好处很明显,可以少打几个字符。现在的主流网站基本都使用裸域名,则些年流行的“www”现在渐渐没有价值了。由于Google Cloud只支持CName转发,用户就只能通过类似www.leonax.net的地址访问,直接访问leonax.net会得到一个“网址无法解析”的错误。这对于用户体验有很大的负作用。

于是乎,GoDaddy就提供了一个域名转向(Domain Forwarding)的功能。其功能就是把裸域名自动中转到一个自定义的二级域名上,这样用户访问裸域名的时候,也可以看到实际的内容。不过没关系,我们可以通过Apache自己来搞个转向功能。

看上去GoDaddy的作法是把裸域名解析到了一个特定的IP地址,估计是GoDaddy自己的。只要把这个地址改成一个可以自己操控的,比如leonax.net现在用的54.92.46.8,然后去主机上使用.htaccess进行下转向就行了,代码如下:

这样所有的leonax.net的访问请求,都会被自动中转到www.leonax.net,无须用户操心。估计GoDaddy自己也是这么干的

WordPress导入文章时碰到'DOMDocument'未找到错误

今天在玩Wordpress的导入功能的时候,发现上传完文件就卡住了,显示了一个空白页。

百思不得其解之后,去看了Apache的log,发现了下面这段话:

mod_fcgid: stderr: PHP Fatal error:  Class 'DOMDocument' not found in /var/www/html/wp-content/plugins/wordpress-importer/parsers.php on line 61

然后意识到前一阵子似乎觉得php-xml这个组件没什么用就给删了,于是现在还要装回来……

sudo yum install php-xml

然后就可以正常导入文章了,真汗……