生米煮成熟饭:LEMP是这样编译出来的

以前常有人说,这是一个快餐时代。

饿了么和美团踏入江湖后,快餐时代变成了点餐时代。

点餐固然方便,开门接下快递小哥的外卖盒子打开便可品尝各式搭配好了的美味,却还是有人愿意从买菜、洗菜开始,一步步的动手,直到把一桌菜肴摆上台前,满足自己的口味。

如果说通过在线敲一两行命令安装软件相当于点餐,那么将源代码编译安装进系统、构建一套完整的服务套件则相当于自己下厨房——爱吃什么就做什么、爱怎么吃就怎么吃。

编译安装方式的优点

之前在博客文章中介绍过比较简单的自己动手架设LEMP的方案,通过软件包管理器,例如CentOS下的YUM、Debian下的APT-GET等,能够相当快捷的完成服务器所需软件环境的架设。但包管理工具不是在任何时候都这样完美,有时回归本源,采用从源代码编译的方式可能是更好的选择。

通过编译来配置一台服务器的软件环境不但在某些时候是必要的,对于硬件配置较低的服务器,通过编译生成的软件环境相对而言更能有效利用其硬件资源,而且在多数情形下,编译作为一种可选方案,还有着以下一些原因[1]

First, the package may not be available in the enabled repositories of your Linux distribution. In addition, the
repositories that offer to download and install Nginx automatically often contain outdated versions. More importantly, you need to configure a variety of significant compile-time options.

总的来说,自己编译(而非采用二进制安装包或软件管理工具)LEMP架构有以下几方面优势:

  1. 从源码编译入手可进行软件的功能、性能方面调整。这样的调整是“事先”的,某种意义上相当于软件生产的“胎儿优生”,也就是在出生之前就从基因层面处理掉母体中胎儿的隐疾,或增强其某些方面的功能。例如PHP官方网站在介绍其数据库连接扩展插件时称

Building PHP from source allows you to specify the MySQL extensions you want to use, as well as your choice of client library for each extension

  1. 从源代码编译出来的软件在系统中安装更适配所在系统的软硬件环境,能够有效利用服务器资源
  2. 在特定情况下,比如离线状态,软件包管理器无法使用,就只能从源代码编译入手
  3. 对于喜欢体验或试用新功能和特色的用户来说,系统自带的软件包管理器通常无法安装到最新版的软件,特别是对CentOS、红帽子这样的以系统稳健为特点的Linux发行版来说,通过软件包管理器安装的软件甚至可能是数年前的版本

本篇日志将从一个业余爱好者的角度来介绍如何编译出一套可用的LEMP环境,在此之上,动态网站的用户和设计者可自由的安装使用各类不同的应用服务程序,如phpBB、Nextcloud、SugarCRM等。在开始前先说明以下几点:

  1. 网上绝大多数的资料只介绍了LEMP架构中三大服务程序自身的编译,本篇日志将从LEMP的依赖包编译开始介绍
  2. 个别依赖包因官方不再开发或有系统推荐版本等原因未选择编译时(2018年8月)的最新版本,如bzip2和re2c
  3. 因硬件配置所限,除CMAKE外的编译相关工具如GCC、GCC-C++等并未使用从源代码编译的方式安装[2],而采用软件包管理工具安装
  4. 预设的LEMP编译工作目录是/usr/local/src,其它依赖包和软件的编译安装目录均可采用默认值而不去设置,一般会安装在/usr/local
  5. 安装环境为512MB内存的Linux CentOS 6.10 64位版,内核版本4.17+
  6. LEMP中的PHP使用7.2系列版本(自带Opcache缓存执行效率高)、Nginx使用官方稳定版系列(即stable版)、MySQL采用5.5版系列(更高版本均需要至少1G物理内存)
  7. LEMP架构搭建完成,通常应能够运行一些主流的动态网站程序,本文中为Nextcloud和phpBB3搭建LEMP环境,因而参考它们的运行要求来安装相关依赖包。[3]

编译准备工作

首先进行用户设置,增加www与mysql的用户与用户组。从Linux的系统安全策略考虑,不同的进程、任务最好能在不同的用户权限下运行。Nginx与PHP-FPM用www用户身份执行,MySQL用mysql用户身份执行。

1
2
3
# 添加相关用户组及用户,不设置用户目录且取消其远程登录权限
groupadd www && useradd www -M -g www -s /sbin/nologin
groupadd mysql && useradd -r -g mysql -s /bin/false mysql

然后安装相关的编译软件和它们的依赖包,在不同的主机及版本环境下,有的(如make、wget)可能已经随系统附带了。

1
yum install -y gcc gcc-c++ make autoconf automake libtool bzip2-devel libXpm-devel ncurses-devel wget 

依赖包的安装

在Linux下编译安装软件大体分三步。第一步是预编译,简单的说就是让源代码先“熟悉”和“分析”一下硬件环境,第二步是编译,这一步相对来说花的时间会比较长,会将源代码制作成可执行文件,最后就是安装,即把可执行文件复制到预先指定的目录中去。预编译时,最简单的方式就是什么参数都不设置,但多数情况下,可通过随机文档查看各参数的说明,以便配置出符合自己要求的编译方案。

此外,依赖包的安装也存在一个软件包管理器所没有的劣势,那就是要自己掌握各依赖包、软件包之间的依赖关系,安装顺序错误会导致LEMP架构最后无法正常运行甚至无法安装的问题。

典型的软件编译安装步骤如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 进入工作目录,Linux下典型的编译安装工作目录如/usr/local/src
cd /usr/local/src
# 从官方网站下载源代码压缩包,此处假设某软件app,版本为1.2.3
# 官方网站的下载链接路径是http://app.domain.tld/download
# 源码包文件名是app-1.2.3.tar.gz,这意味着可通过tar命令加gz格式对应的参数z解压
wget http://app.domain.tld/download/app-1.2.3.tar.gz
# 解压源码包并进入解压后的目录,如果想下载后直接解压进入安装目录,可使用后文实例中的管道符
tar xzvf app-1.2.3.tar.gz && cd app-1.2.3
# 预编译大多使用configure命令,相关参数(如定制安装目录)一般紧随其后
# ./configure --prefix=/path/to/install/dir
./configure
# 编译及安装
make && make install

接下去正式开始编译和安装LEMP架构的依赖包。

安装cmake

1
2
3
4
5
cd /usr/local/src
curl https://cmake.org/files/v3.5/cmake-3.5.2.tar.gz | tar xz
cd cmake-3.5.2 && mkdir bld && cd $_
../bootstrap
make && make install

安装bison

1
2
3
4
5
cd /usr/local/src
curl http://ftp.gnu.org/gnu/bison/bison-3.0.5.tar.gz | tar xz
cd bison-3.0.5
./configure
make && make install

安装re2c

1
2
3
4
5
cd /usr/local/src 
curl https://iweb.dl.sourceforge.net/project/re2c/1.0.1/re2c-1.0.1.tar.gz | tar xz
cd re2c-1.0.1
./configure
make && make install

安装zlib

1
2
3
4
5
cd /usr/local/src
curl https://zlib.net/zlib-1.2.11.tar.gz | tar xj
cd zlib-1.2.11
./configure
make && make install

安装pcre

1
2
3
4
5
6
7
8
cd /usr/local/src
curl https://ftp.pcre.org/pub/pcre/pcre-8.42.tar.bz2 | tar xj
cd pcre-8.42
./configure \
--enable-utf --enable-unicode-properties \
--enable-jit \
--enable-pcregrep-libz --enable-pcregrep-libbz2
make && make install

安装OpenSSL

1
2
3
4
5
6
7
8
9
10
11
cd /usr/local/src
# 写作博客时OpenSSL最新版本分别是1.02(LTS版)和1.10
# 官方在九月份发布了版本号为1.1.1的LTS版并建议用户更新,因而文中实例亦采用该版本
wget -qO - https://www.openssl.org/source/openssl-1.1.1.tar.gz | tar xz
cd openssl-1.1.1
# OpenSSL的预编译命令是config或Configure,与通用的命令不同
./config \
--prefix=/usr/local/openssl --openssldir=/usr/local/openssl \
no-ssl2 no-ssl3 no-comp no-weak-ssl-ciphers \
-Wl,-rpath=/usr/local/openssl/lib
make && make install_sw

安装cURL

1
2
3
4
5
6
cd /usr/local/src
wget -qO - https://curl.haxx.se/download/curl-7.61.1.tar.gz | tar xz
cd curl-7.61.1
LDFLAGS="-Wl,-rpath=/usr/local/openssl/lib" \
./configure --with-zlib=/usr/local --with-ssl=/usr/local/openssl
make && make install

安装icu(PHP编译过程中intl参数的依赖项)

1
2
3
4
5
6
7
cd /usr/local/src
wget -qO - http://download.icu-project.org/files/icu4c/58.2/icu4c-58_2-src.tgz | tar xz
mkdir icu/trunk-dev && cd $_
# 根据icu官方说明,其推荐的预编译命令是runConfigureICU
# icu的预编译程序在源码目录下的/source/子目录下
../source/runConfigureICU Linux/gcc
make && make install

安装libxml

1
2
3
4
5
cd /usr/local/src
curl ftp://xmlsoft.org/libxml2/libxml2-2.9.8.tar.gz | tar xz
cd libxml2-2.9.8
./configure --with-zlib=/usr/local --without-python
make && make install

安装libzip

1
2
3
4
5
6
7
8
cd /usr/local/src
wget -qO - https://libzip.org/download/libzip-1.5.1.tar.gz | tar xz
cd libzip-1.5.1 && mkdir bld && cd $_
cmake .. \
-DOPENSSL_ROOT_DIR=/usr/local/openssl \
-DOPENSSL_LIBRARIES=/usr/local/openssl/lib \
-DOPENSSL_INCLUDE_DIR=/usr/local/openssl/include
make && make install

安装jpeg

1
2
3
4
5
cd /usr/local/src
wget -qO - http://www.ijg.org/files/jpegsrc.v9c.tar.gz | tar xz
cd jpeg-9c
./configure
make && make install

安装libpng

libpng和jpeg都是PHP编译中GD模块的必要依赖项,libpng还依赖于zlib

1
2
3
4
5
cd /usr/local/src
wget -qO - https://download.sourceforge.net/libpng/libpng-1.6.35.tar.gz | tar xz
cd libpng-1.6.35
./configure --enable-hardware-optimizations=on
make && make install

安装libwebp

PHP官方说明文档中未列入这个模块,也可不安装这个依赖包

1
2
3
4
5
cd /usr/local/src
wget -qO - https://github.com/webmproject/libwebp/archive/v1.0.0.tar.gz | tar xz
cd libwebp-1.0.0 && ./autogen.sh
./configure --enable-silent-rules
make && make install

安装freetype

1
2
3
4
5
6
cd /usr/local/src
wget -qO - https://download.savannah.gnu.org/releases/freetype/freetype-2.9.1.tar.gz | tar xz
cd freetype-2.9.1
# freetype专门为替代PKGCONFIG提供了--enable-freetype-config参数
./configure --enable-freetype-config --with-zlib=auto --with-bzip2=auto --with-png=auto
make && make install

至此,搭建LEMP环境的基本依赖包均已编译安装到位,下面进行PHP、Nginx和MySQL的编译安装。

安装PHP

本着够用就好的原则,此处仅安装PHP-FPM,PHP命令行工具等均不编译或安装

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
31
32
33
34
35
36
37
38
cd /usr/local/src
curl http://php.net/distributions/php-7.2.9.tar.gz | tar xz
cd php-7.2.9
# 设定OpenSSL库目录路径
LDFLAGS="-Wl,-rpath=/usr/local/openssl/lib" \
# 设定64位系统下各依赖包的库路径搜索目录
PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig \
# 预编译
./configure \
# 设定安装目录为/usr/local/php
--prefix=/usr/local/php \
# 仅安装PHP-FPM模块,取消其它模块的编译或安装
--enable-fpm --disable-cli --disable-cgi --disable-phpdbg --disable-phpdbg-webhelper --disable-phpdbg-debug --without-pear \
# 设定用户及用户组为www
--with-fpm-user=www --with-fpm-group=www \
# 启用OpenSSL并设定其搜索路径
--with-openssl --with-openssl-dir=/usr/local/openssl \
--with-pcre-regex=/usr/local --with-pcre-jit \
# 启用压缩功能相关依赖包支持
--enable-zip --with-zlib --with-bz2 --with-libzip \
--with-curl \
--with-libxml-dir \
--enable-ftp \
--enable-shmop \
--enable-sockets \
--enable-mbstring \
# 启用图形图像功能相关依赖包支持
--enable-exif --with-gd --with-jpeg-dir --with-png-dir --with-xpm-dir --with-freetype-dir --with-webp-dir \
# 启用国际化功能相关依赖包支持并设定ICU安装路径
--enable-intl --with-icu-dir=/usr/local \
# 启用数据库及数据库连接相关依赖包支持
--enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --without-pdo-sqlite --without-sqlite3 \
# 启用内置缓存功能
--enable-opcache --enable-opcache-file \
--disable-posix \
--disable-fileinfo
# 编译及安装,该步骤中建议的测试功能依赖于PHP命令行工具,且测试时间较长,因此跳过
make && make install

安装完成之后,还需要进行一些调整工作,主要涉及到PHP-FPM的运行设置,比较直观的方式是登录进主机,查找相关文档并手工修改几个参数。这里我采用另一种更快捷粗暴的方式——用SED命令修改。

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
# 出于安全原因,将pathinfo相关参数修改为0
sed 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/g' /usr/local/src/php-7.2.9/php.ini-production > /usr/local/php/lib/php.ini
# 分别配置php-fpm和php的设置文件
cp /usr/local/src/php-7.2.9/sapi/fpm/php-fpm.conf /usr/local/php/etc/php-fpm.conf
cp /usr/local/src/php-7.2.9/sapi/fpm/www.conf /usr/local/php/etc/php-fpm.d/www.conf
sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 18M/g' /usr/local/php/lib/php.ini
# 开启并激活opcache缓存功能以提升PHP-FPM的执行效率
sed -i '/\[opcache\]/a\zend_extension=opcache.so' /usr/local/php/lib/php.ini
sed -i 's/;opcache.enable=1/opcache.enable=1/g' /usr/local/php/lib/php.ini
sed -i 's/;opcache.enable_cli=0/opcache.enable_cli=1/g' /usr/local/php/lib/php.ini
sed -i 's/;opcache.memory_consumption=128/opcache.memory_consumption=128/g' /usr/local/php/lib/php.ini
sed -i 's/;opcache.interned_strings_buffer=8/opcache.interned_strings_buffer=8/g' /usr/local/php/lib/php.ini
sed -i 's/;opcache.max_accelerated_files=10000/opcache.max_accelerated_files=10000/g' /usr/local/php/lib/php.ini
sed -i 's/;opcache.validate_timestamps=1/opcache.validate_timestamps=1/g' /usr/local/php/lib/php.ini
sed -i 's/;opcache.revalidate_freq=2/opcache.revalidate_freq=1/g' /usr/local/php/lib/php.ini
sed -i 's/;opcache.save_comments=1/opcache.save_comments=1/g' /usr/local/php/lib/php.ini
# 设置以UNIX SOCKET方式而非TCP SOCKET方式完成与FASTCGI网络进程之间的通信
sed -i 's/listen = 127.0.0.1:9000/listen = \/tmp\/php-fpm.sock/g' /usr/local/php/etc/php-fpm.d/www.conf
sed -i 's/;listen.owner = www/listen.owner = www/g' /usr/local/php/etc/php-fpm.d/www.conf
sed -i 's/;listen.group = www/listen.group = www/g' /usr/local/php/etc/php-fpm.d/www.conf
sed -i 's/;listen.mode = 0660/listen.mode = 0660/g' /usr/local/php/etc/php-fpm.d/www.conf
sed -i 's/;listen.allowed_clients = 127.0.0.1/listen.allowed_clients = 127.0.0.1/g' /usr/local/php/etc/php-fpm.d/www.conf
# 设置PHP-FPM随系统启动并将其加入系统服务模块
cp /usr/local/src/php-7.2.9/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm
chmod +x /etc/init.d/php-fpm
chkconfig --add php-fpm
service php-fpm start

安装Nginx

Nginx Web服务器性能强大,相比于安装,其后的配置十分困难,因此要让Nextcloud或phpBB正常运行,配置文件就采用官方所提供的,此处仅涉及它的编译安装步骤。

1
2
3
4
5
6
7
8
9
10
11
12
13
cd /usr/local/src
wget -qO - http://nginx.org/download/nginx-1.14.0.tar.gz | tar xz
cd nginx-1.14.0
./configure \
--prefix=/usr/local/nginx \
--user=www --group=www \
--with-zlib=../zlib-1.2.11 \
--with-openssl=../openssl-1.1.1 \
--with-pcre=../pcre-8.42 --with-pcre-jit \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_random_index_module \
make && make install

后续配置涉及到自启动与服务管理,Nginx官方网站提供了相关文件可参考使用,将其命令为nginx并复制到/etc/init.d目录,如下赋予执行权限,再加入到系统启动服务中即可。

1
2
3
4
chmod +x /etc/init.d/nginx
chkconfig --add nginx
chkconfig --level 345 nginx on
service nginx start

安装MySQL

除了MySQL外,目前比较流行的数据库服务器软件还有MariaDB,不过两者相似度极高,命令也是通用的,这里以MySQL 5.5版安装举例,更详细的安装参数可查看MySQL官方说明文档[4]

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
31
32
cd /usr/local/src
wget -qO - https://dev.mysql.com/get/Downloads/MySQL-5.5/mysql-5.5.61.tar.gz | tar xz && cd mysql-5.5.61
# 设置专属编译用工作目录
mkdir mysql_build && cd mysql_build
# 编译中涉及到的参数可参考MySQL官方说明文档,例如端口、数据目录、字符集等
cmake .. \
-DCMAKE_INSTALL_PREFIX=/usr/local/mysql \
-DMYSQL_TCP_PORT=3306 \
-DMYSQL_UNIX_ADDR=/tmp/mysql.sock \
-DMYSQL_DATADIR=/usr/local/mysql/data \
-DINSTALL_SECURE_FILE_PRIVDIR=NULL \
-DINSTALL_MYSQLTESTDIR= \
-DINSTALL_SQLBENCHDIR= \
-DSYSCONFDIR=/etc/mysql \
-DDEFAULT_CHARSET=utf8mb4 \
-DDEFAULT_COLLATION=utf8mb4_general_ci \
-DWITH_SSL=bundled \
-DWITH_ZLIB=system \
# 编译及安装
make && make install
# 安装后期工作,包括初始数据安装、目录权限设定、自启动服务管理以及初始安全配置
cd /usr/local/mysql && chown -R mysql:mysql .
scripts/mysql_install_db --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data
# 将MySQL目录下的数据目录权限配至mysql用户,其余目录配至root用户
chown -R root:root . && chown -R mysql:mysql data
# 安装更新数据库配置文件
rm -f /etc/my.cnf && mkdir /etc/mysql && cp /usr/local/mysql/support-files/my-medium.cnf /etc/mysql/my.cnf
# 数据库自启动服务安装及配置
cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysql && chmod +x /etc/init.d/mysql
chkconfig --add mysql && service mysql start
# 初始安全配置(删除匿名用户、设定管理员密码、防止远程登录等)
/usr/local/mysql/bin/mysql_secure_installation

至此,可通过service app status这样的命令检查一下LEMP架构中各个服务是否启动且正常运行

1
2
3
service php-fpm status
service nginx status
service mysql status

如果以上三个服务均正常运行,那么就可以下载安装Nextcloud和phpBB来测试一番并进行实际使用了。


  1. Nedelcu, C. (2015). Nginx HTTP Server - Third Edition. Packt Publishing Ltd. ↩︎

  2. GNU GCC高版本的编译安装极耗主机资源,在低配服务器上易导致违反主机商协议而被强制锁频CPU时钟 ↩︎

  3. Nextcloud是一个网盘应用程序,查看Nextcloud的安装要求;phpBB是全球最大的免费开源PHP论坛程序,查看phpBB3的安装要求 ↩︎

  4. https://dev.mysql.com/doc/refman/5.5/en/source-configuration-options.html ↩︎