LAMP 学习文档

LAMP

一、什么是 LAMP

​ LAMP 是一种常用的开源软件组合,用于构建和部署动态网站和 Web 应用程序。LAMP 代表的是 Linux、Apache、MySQL 和 PHP

  1. Linux
    Linux 是一种开源的操作系统,提供了稳定、安全和高效的环境。它是 LAMP 架构的基础,负责管理硬件资源和提供必要的系统服务

  2. Apache
    Apache 是一个开源的 HTTP 服务器,广泛用于提供网页服务。它能够处理大量的并发连接,支持多种编程语言和模块扩展,具有高性能和稳定性。Apache 负责接收用户请求并将其传递给相应的应用程序处理

  3. MySQL
    MySQL 是一个关系型数据库管理系统,用于存储和管理网站的数据。它支持 SQL 语言,能够高效地处理大量数据查询和操作。MySQL 在 LAMP 架构中负责存储用户信息、产品信息等各种数据,并提供数据的增删改查功能

  4. PHP
    PHP 是一种服务器端脚本语言,常用于生成动态网页内容。它可以嵌入到 HTML 中,能够与 MySQL 数据库进行交互,生成用户请求的动态内容。PHP 在 LAMP 架构中负责处理业务逻辑,并将结果返回给 Apache 服务器

  • LAMP 架构的工作流程:
    1. 用户通过浏览器发送请求到 Apache 服务器
    2. Apache 服务器接收到请求后,将其传递给 PHP 脚本进行处理
    3. PHP 脚本根据业务逻辑,可能需要从 MySQL 数据库中读取或写入数据
    4. PHP 脚本处理完成后,将生成的动态内容返回给 Apache 服务器
    5. Apache 服务器将生成的网页内容发送回用户的浏览器进行显示

二、LAMP 的作用

​ LAMP 架构在 Web 开发和部署中有着广泛的应用和重要作用

  1. 动态网站和 Web 应用程序的开发:LAMP 提供了一个完整的开发环境,支持动态网站和 Web 应用程序的开发。通过 PHP 脚本语言,可以生成动态网页内容,并与 MySQL 数据库进行交互,存储和管理用户数据。

  2. 高效的服务器环境:LAMP 组合中的 Linux 操作系统和 Apache 服务器提供了一个高效、稳定的服务器环境。Apache 服务器能够处理大量并发连接,支持多种编程语言和模块扩展,适合各种规模的网站和应用。

  3. 数据存储和管理:MySQL 数据库管理系统在 LAMP 架构中负责存储和管理数据。它支持 SQL 语言,能够高效地处理大量数据查询和操作,适用于各种类型的数据存储需求。

  4. 开源和成本效益:LAMP 组件都是开源软件,意味着它们可以免费使用和修改。这使得 LAMP 成为一个成本效益高的解决方案,特别适合中小型企业和个人开发者。

  5. 跨平台和扩展性LAMP 具有良好的跨平台兼容性,可以在不同的操作系统上运行。此外,LAMP 组件的模块化设计使得系统具有良好的扩展性,可以根据需求添加或替换组件。

  6. 社区支持和资源丰富:由于 LAMP 组件都是开源软件,拥有庞大的用户社区和丰富的资源。开发者可以方便地找到文档、教程和支持,快速解决问题和提升开发效率。

​ 总的来说,LAMP 架构为 Web 开发提供了一个高效、稳定、灵活的解决方案,广泛应用于各种类型的网站和 Web 应用程序。

三、部署 LAMP

1. 部署 MySQL

1.1 安装 MySQL

我们可以从 MySQL官网 里找到并下载源码包/可以使用包管理器安装的软件包,我们这里使用二进制免编译包

1
$ wget http://mirrors.163.com/mysql/Downloads/MySQL-5.6/mysql-5.6.47-linux-glibc2.12-x86_64.tar.gz -P /usr/local/src

发现阿铭提供的 MySQL 的二进制免编译包不可用,又从网上找了一个

​ 将二进制免编译包上传到 /usr/local/src 目录下,然后进行如下操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ cd /usr/local/src/ #自己通过源码包配置安装的软件包都推荐放到这个位置

# 解压后挪动位置
$ sudo tar -zxf mysql-5.6.49-linux-glibc2.12-x86_64.tar.gz
$ sudo mv mysql-5.6.49-linux-glibc2.12-x86_64 /usr/local/mysql

# 创建一个不能用于登录的账户 mysql,因为启动 MySQL 需要用到此账户
$ sudo useradd -s /sbin/nologin mysql

# 创建 MySQL 数据目录,然后授予权限
$ sudo mkdir -p /data/mysql
$ sudo chown -R mysql:mysql /data/mysql/
$ sudo chmod -R 755 /data/mysql

# 初始化 MySQL 数据目录:指定 MySQL 服务的用户是 mysql (创建的文件和目录的 owner 都会是 mysql),,并指定 MySQL 数据目录
$ cd /usr/local/mysql
$ sudo ./scripts/mysql_install_db --user=mysql --datadir=/data/mysql

此处出错

1
2
3
4
5
6
7
8
$ sudo ./scripts/mysql_install_db --user=mysql --datadir=/data/mysql                                   
FATAL ERROR: please install the following Perl modules before executing ./scripts/mysql_install_db:
Data::Dumper

或者是

$ sudo ./scripts/mysql_install_db --user=mysql --datadir=/data/mysql
-bash: ./scripts/mysql_install_db: /usr/bin/perl: 坏的解释器: 没有那个文件或目录

提示缺少 perl-Module-Install 软件,通过 yum install -y perl-Module-Install 来安装

​ 安装后再次执行 ./scripts/mysql_install_db --user=mysql --datadir=/data/mysql

1
2
3
4
5
6
7
8
9
10
$ sudo ./scripts/mysql_install_db --user=mysql --datadir=/data/mysql
Installing MySQL system tables...
2024-08-06 19:06:28 1887 [Note] InnoDB: Shutdown completed; log sequence number 1625987
OK

Filling help tables...
2024-08-06 19:06:30 1910 [Note] InnoDB: Shutdown completed; log sequence number 1625997
OK

...
  • 检查是否安装成功

    ​ 如果向上面一样看到了两个 OK 且在 /data/mysql 下看到了生成的文件和目录,说明执行正确

    ​ 或者是在执行完最后一条命令江批,马上执行 echo $? 看输出的结果为 **0**,说明执行正确
    3.1.1

1.2 配置 MySQL

​ 首先复制配置文件,然后打开配置文件并把配置文件改成如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ cp support-files/my-default.cnf /etc/my.cnf
cp:是否覆盖"/etc/my.cnf"? y
# 这里提示是否覆盖是因为系统内默认就有 /etc/my.cnf,直接按y就行

$ sudo vi support-files/my-default.cnf

# 替换成以下内容
[mysqld]

innodb_buffer_pool_size = 128M #第11行

log_bin = moka #第15行

basedir = /usr/local/mysql #第18行,是MySQL包的所在地
datadir = /data/mysql #第19行,存放数据的地方
port = 3306 #第20行,默认端口号就是3306
server_id = 128 #第21行,服务的ID号
socket = /tmp/mysql.sock #第22行,MySQL服务监听的嵌套字地址

## 关于内存的配置参数,保持默认即可
join_buffer_size = 128M #第27行
sort_buffer_size = 2M #第28行
read_rnd_buffer_size = 2M #第29行
  • 备注:
    嵌套字地址:在 Linux 系统下,很多服务不仅可以监听一个端口(通过 TCP/IP 的方式通信),也可以监听 socket,两个进程就可以通过这个 socket 文件通信

​ 然后复制启动脚本并修改其属性

1
2
$ sudo cp support-files/mysql.server /etc/init.d/mysqld
$ sudo chmod 755 /etc/init.d/mysqld

​ 然后修改启动脚本,并把启动脚本加入系统服务项,然后设置开机启动并启动 MySQL

1
2
3
4
5
6
7
8
$ vi /etc/init.d/mysqld

# 修改的内容
datadir=/data/mysql #第47行

$ sudo chkconfig --add mysqld #把mysqld服务加入系统服务列表中
$ sudo chkconfig mysqld on #设置开机启动
$ sudo service mysqld start #启动服务
  • 备注:

    1. 上面加入到系统服务项的步骤是《跟阿铭学Linux 第三版》所给出的步骤,实际上 chkconfig 命令现在已经被 systemctl 命令所替代,所以也可以这么做

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      # 将服务文件复制到 /etc/systemd/system/ 目录
      $ sudo cp /usr/local/mysql/support-files/mysql.server /etc/systemd/system/mysqld.service

      # 重新加载 systemd 配置
      $ sudo systemctl daemon-reload

      # 设置 MySQL 为开机启动
      $ sudo systemcrl enable mysqld

      # 启动 MySQL 服务
      $ sudo systemctl start mysqld
    2. 如果启动不了,就到 data/mysql 目录下查看错误日志,这个日志名通常是 [主机名].err
      检查 MySQL 是否启动的命令为:

      1
      2
      3
      4
      5
      6
      7
      8
      # 检查 mysqld 服务的状态
      $ sudo systemctl status mysqld

      # 查看 mysqld 相关进程(结果应该大于2行)
      $ sudo ps aux | grep mysqld

      # 看看有没有监听3306端口
      $ sudo netstat -lnp | grep 3306
      • 最后一个要是提示 -bash: netstat: 未找到命令 的,就是用命令 sudo yum install -y net-tools ,安装之后就可以用了
1.3 问题与解决

​ 在最后使用 service mysqld start 启动服务的时候,我遇到了如下图所示的问题
3.1.3_1

尝试的解决方法:

  1. /etc/init.d/mysqld 里,把之前没填上的【第46行 basedir= /usr/local/mysql】给填上,发现还是出现这个情况
  2. 发现上面有一条命令错误
    误:sudo mv mysql-5.6.49-linux-glibc2.12-x86_64 mysql
    正:sudo mv mysql-5.6.49-linux-glibc2.12-x86_64 /usr/local/mysql
    修改后成功运行
    3.1.3_2

2. 部署 Apache

2.1 安装 Apache

同样,我们也可以从 Apache官网 里找到并下载源码包

由于这里使用的是 Apache(httpd) 2.4 版本的,所以需要额外下配套的 apr 和 apr-util 源码包并进行编译,apr 和 apr-util(统称 apr)可以理解为一个通用的函数库,主要为上层应用提供服务,在这里,httpd 是依赖 apr 的,如果不安装 apr,httpd 就无法工作

​ 将下载好的源码包上传到 /usr/local/src 目录下,然后进行如下操作

1
2
3
4
5
6
7
8
9
10
# 解压
$ cd /usr/local/src/
$ sudo tar -zxf httpd-2.4.57.tar.gz
$ sudo tar -zxf apr-1.6.5.tar.gz
$ sudo tar -zxf apr-util-1.6.3.tar.gz

# 安装 apr
$ cd apr-1.6.5
$ sudo ./configure --prefix=/usr/local/apr
$ sudo make && make install

在运行 ./configure --prefix=/usr/local/apr 的时候出现以下错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ./configure --prefix=/usr/local/apr
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking target system type... x86_64-pc-linux-gnu
Configuring APR library
Platform: x86_64-pc-linux-gnu
checking for working mkdir -p... yes
APR Version: 1.6.5
checking for chosen layout... apr
checking for gcc... no
checking for cc... no
checking for cl.exe... no
configure: error: in `/usr/local/src/apr-1.6.5':
configure: error: no acceptable C compiler found in $PATH
See `config.log' for more details

上网查到需要安装必要的工具和依赖库

1
$ sudo yum install -y gcc autoconf libtool openssl openssl-devel

都安装了之后,正常解决

3.2.3_1

​ 然后编译 apr-util

1
2
3
$ cd /usr/local/src/apr-util-1.6.3
$ sudo ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr
$ sudo make && make install

在运行 make && make install 的时候遇到了如下问题

1
2
3
4
5
6
7
8
9
$ sudo make && make install
......
xml/apr_xml.c:35:19: 致命错误:expat.h:没有那个文件或目录
#include <expat.h>
^
编译中断。
make[1]: *** [xml/apr_xml.lo] 错误 1
make[1]: 离开目录“/usr/local/src/apr-util-1.6.3”
make: *** [all-recursive] 错误 1

缺少 expat 库,需要安装 expat-devel 来解决

1
$ sudo yum install -y expat-devel expat

安装之后重新运行 make && make install 成功解决

3.2.3_2

​ 安装完 apr 和 apr-util 之后,继续安装 httpd,配置编译参数,如下所示

1
2
3
4
5
6
7
$ cd /usr/local/src/httpd-2.4.57
$ sudo ./configure \
--prefix=/usr/local/apache2.4 \ #指定安装目录
--with-apr=/usr/local/apr \
--with-apr-util=/usr/local/apr-util \
--enable-so \ #启用DSO(把某些功能以模块的形式呈现出来)
--enable-mods-shared=most #以共享的方式安装大多数模块,安装后会在 modules 目录下看到这些文件,因为选择了 most,所以后面编译的时间会大大增加

​ 为了避免出错,提前安装好一些库文件 pcrepcre-devel

  • 注意:有些时候可能不安装 pcrepcre-devel 反而会编译成功
1
$ sudo yum install -y pcre pcre-devel

​ 然后开始编译

1
$ sudo make && make install
  • 检查是否安装成功:使用命令 echo $? 如果输出 0 那就是成功

在最后 make && make install 的时候出现了以下错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ sudo make && make install
...
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_GetErrorCode'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_SetEntityDeclHandler'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_ParserCreate'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_SetCharacterDataHandler'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_ParserFree'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_SetUserData'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_StopParser'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_Parse'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_ErrorString'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_SetElementHandler'
collect2: error: ld returned 1 exit status
make[2]: *** [htpasswd] 错误 1
make[2]: 离开目录“/usr/local/src/httpd-2.4.57/support”
make[1]: *** [all-recursive] 错误 1
make[1]: 离开目录“/usr/local/src/httpd-2.4.57/support”
make: *** [all-recursive] 错误 1

网上搜到说主要问题是 libaprutil-1.so 中缺少 expat 库相关的符号引用。这个问题通常是由于 apr-util 在编译时没有正确链接到 expat

目前发现问题:

  1. 在 apr 的 make && make install 的时候出现了如下情况

    1
    2
    3
    4
    5
    $ sudo make && make install #在apr的安装
    ...
    config.status: executing libtool commands
    rm: cannot remove 'libtoolT': No such file or directory
    config.status: executing default commands
    • 缺少 libtoolT 尝试安装后重新编译
    1
    $ sudo yum install -y libtool

    ​ 但是系统已安装最新版的 libtoolT

    1
    2
    3
    4
    5
    6
    $ sudo yum install -y libtool
    已加载插件:fastestmirror
    Loading mirror speeds from cached hostfile
    * epel: d2lzkl7pfhq30w.cloudfront.net
    软件包 libtool-2.4.2-22.el7_3.x86_64 已安装并且是最新版本
    无须任何处理
  2. 重复”从头到尾”的操作的时候发现成功
    检查步骤的时候发现,并没有提前安装 pcrepcre-devel

  • 安装完后,可以执行如下命令,查看 httpd 目录结构以及 modules 目录下面的木块文件:
    1
    2
    3
    4
    5
    6
    7
    $ ls /usr/local/apache2.4/
    bin build cgi-bin conf error htdocs icons include logs man manual modules
    $ ls /usr/local/apache2.4/modules/
    httpd.exp mod_cache_disk.so mod_log_debug.so mod_sed.so
    mod_access_compat.so mod_cache.so mod_logio.so
    ...
    # 省略了许多绿色的.so文件,这就是上面提到的木块,之所以有这么多,就是因为上面定义了一个most参数
    这些模块不会全部加载,如果想要使用哪个模块,在配置文件里配置即可
    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
    $ sudo /usr/local/apache2.4/bin/apachectl -M
    AH00558: httpd: Could not reliably determine the servers fully qualified domain name, using localhost.localdomain. Set the 'ServerName' directive globally to suppress this message
    Loaded Modules:
    core_module (static)
    so_module (static)
    http_module (static)
    mpm_event_module (static)
    authn_file_module (shared)
    authn_core_module (shared)
    authz_host_module (shared)
    authz_groupfile_module (shared)
    authz_user_module (shared)
    authz_core_module (shared)
    access_compat_module (shared)
    auth_basic_module (shared)
    reqtimeout_module (shared)
    filter_module (shared)
    mime_module (shared)
    log_config_module (shared)
    env_module (shared)
    headers_module (shared)
    setenvif_module (shared)
    version_module (shared)
    unixd_module (shared)
    status_module (shared)
    autoindex_module (shared)
    dir_module (shared)
    alias_module (shared)
    ​ 前面以 AH00558 开头的行,并不是错误,而是警告,可以在配置文件中定义”ServerName”使其消失,稍后将会说到配置文件
    ​ 这些带 shared 字样的,表示该模块为动态共享模块
    ​ 还有带 static 字样的,表示以静态的形式存在
    ​ 动态和静态的区别在于:
    ​ 静态模块直接和主程序 ( /usr/local/apache2.4/bin/httpd ) 绑定在一起,我们看不到
    ​ 而动态 模块都是一个个独立存在的文件 ( modules 目录下的 .so 文件即是)
2.2 问题与解决

​ 1. 在运行 ./configure --prefix=/usr/local/apr 的时候出现以下错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ./configure --prefix=/usr/local/apr
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking target system type... x86_64-pc-linux-gnu
Configuring APR library
Platform: x86_64-pc-linux-gnu
checking for working mkdir -p... yes
APR Version: 1.6.5
checking for chosen layout... apr
checking for gcc... no
checking for cc... no
checking for cl.exe... no
configure: error: in `/usr/local/src/apr-1.6.5':
configure: error: no acceptable C compiler found in $PATH
See `config.log' for more details

​ 上网查到需要安装必要的工具和依赖库

1
$ sudo yum install -y gcc autoconf libtool openssl openssl-devel

​ 都安装了之后,正常解决

  1. 在运行 make && make install 的时候遇到了如下问题
1
2
3
4
5
6
7
8
9
$ sudo make && make install
......
xml/apr_xml.c:35:19: 致命错误:expat.h:没有那个文件或目录
#include <expat.h>
^
编译中断。
make[1]: *** [xml/apr_xml.lo] 错误 1
make[1]: 离开目录“/usr/local/src/apr-util-1.6.3”
make: *** [all-recursive] 错误 1

​ 缺少 expat 库,需要安装 expat-devel 来解决

1
sudo yum install -y expat-devel expat

​ 安装之后重新运行 make && make install 成功解决

​ 3. 在最后 make && make install 的时候出现了以下错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ sudo make && make install
...
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_GetErrorCode'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_SetEntityDeclHandler'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_ParserCreate'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_SetCharacterDataHandler'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_ParserFree'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_SetUserData'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_StopParser'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_Parse'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_ErrorString'
/usr/local/apr-util/lib/libaprutil-1.so: undefined reference to `XML_SetElementHandler'
collect2: error: ld returned 1 exit status
make[2]: *** [htpasswd] 错误 1
make[2]: 离开目录“/usr/local/src/httpd-2.4.57/support”
make[1]: *** [all-recursive] 错误 1
make[1]: 离开目录“/usr/local/src/httpd-2.4.57/support”
make: *** [all-recursive] 错误 1

​ 通过不安装 pcrepcre-devel 就可以解决该问题

3. 部署 PHP

3.1 安装 PHP

同样,我们也可以从 PHP官网 里找到并下载源码包

(这里以阿铭所使用的 PHP 5.6 举例)

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
# 下载并解压
$ cd /usr/local/src
$ sudo wget https://www.php.net/distributions/php-5.6.30.tar.gz
$ sudo tar -zxf php-5.6.30.tar.gz

# 开始编译
$ cd php-5.6.30
$ sudo ./configure \
--prefix=/usr/local/php \
--with-apxs2=/usr/local/apache2.4/bin/apxs \
--with-config-file-path=/usr/local/php/etc \
--with-mysql=/usr/local/mysql \
--with-libxml-dir \
--with-gd \
--with-jpeg-dir \
--with-png-dir \
--with-freetype-dir \
--with-iconv-dir \
--with-zlib-dir \
--with-bz2 \
--with-openssl \
--with-mcrypt \
--enable-soap \
--enable-gd-native-ttf \
--enable-mbstring \
--enable-sockets \
--enable-exif

在这里遇到了错误

  1. $ sudo ./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2.4/bin/apxs --with-config-file-path=/usr/local/php/etc --with-mysql=/usr/local/mysql --with-libxml-dir --with-gd --with-jpeg-dir --with-png-dir --with-freetype-dir --with-iconv-dir --with-zlib-dir --with-bz2 --with-openssl --with-mcrypt --enable-soap --enable-gd-native-ttf --enable-mbstring --enable-sockets --enable-exif
    ...
    configure: error: xml2-config not found. Please check your libxml2 installation.
    
    1
    2
    3
    4
    5

    缺少对应组件,通过安装 `libxml2-devel` 解决

    ```shell
    $ sudo yum install -y libxml2-devel
  2. $sudo ./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2.4/bin/apxs --with-config-file-path=/usr/local/php/etc --with-mysql=/usr/local/mysql --with-libxml-dir --with-gd --with-jpeg-dir --with-png-dir --with-freetype-dir --with-iconv-dir --with-zlib-dir --with-bz2 --with-openssl --with-mcrypt --enable-soap --enable-gd-native-ttf --enable-mbstring --enable-sockets --enable-exif
    ...
    checking for BZip2 in default path... not found
    configure: error: Please reinstall the BZip2 distribution
    
    1
    2
    3
    4
    5

    缺少对应组件,通过安装 `bzip2` 和 `bzip2-devel` 解决

    ```shell
    $ sudo yum install -y bzip2 bzip2-devel
  3. $ sudo ./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2.4/bin/apxs --with-config-file-path=/usr/local/php/etc --with-mysql=/usr/local/mysql --with-libxml-dir --with-gd --with-jpeg-dir --with-png-dir --with-freetype-dir --with-iconv-dir --with-zlib-dir --with-bz2 --with-openssl --with-mcrypt --enable-soap --enable-gd-native-ttf --enable-mbstring --enable-sockets --enable-exif
    ...
    configure: error: jpeglib.h not found.
    
    1
    2
    3
    4
    5
    6
    7

    系统中缺少 `jpeglib.h` 头文件,这是 JPEG 图像处理库的一部分

    需要安装 `libjpeg-devel` 包来解决这个问题

    ```shell
    $ sudo yum install -y libjpeg-devel
  4. $ sudo ./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2.4/bin/apxs --with-config-file-path=/usr/local/php/etc --with-mysql=/usr/local/mysql --with-libxml-dir --with-gd --with-jpeg-dir --with-png-dir --with-freetype-dir --with-iconv-dir --with-zlib-dir --with-bz2 --with-openssl --with-mcrypt --enable-soap --enable-gd-native-ttf --enable-mbstring --enable-sockets --enable-exif
    ...
    checking whether to enable JIS-mapped Japanese font support in GD... no
    If configure fails try --with-vpx-dir=<DIR>
    checking for jpeg_read_header in -ljpeg... yes
    configure: error: png.h not found.
    
    1
    2
    3
    4
    5

    缺少对应组件,通过安装 `libpng` 和 `libpng-devel` 解决

    ```shell
    $ sudo yum install -y libpng libpng-devel
  5. $ sudo ./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2.4/bin/apxs --with-config-file-path=/usr/local/php/etc --with-mysql=/usr/local/mysql --with-libxml-dir --with-gd --with-jpeg-dir --with-png-dir --with-freetype-dir --with-iconv-dir --with-zlib-dir --with-bz2 --with-openssl --with-mcrypt --enable-soap --enable-gd-native-ttf --enable-mbstring --enable-sockets --enable-exif
    ...
    configure: error: freetype-config not found.
    
    1
    2
    3
    4
    5

    缺少对应组件,通过安装 `freetype` 和 `freetype-devel` 来解决

    ```shell
    $ sudo yum install -y freetype freetype-devel
  6. $ sudo ./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2.4/bin/apxs --with-config-file-path=/usr/local/php/etc --with-mysql=/usr/local/mysql --with-libxml-dir --with-gd --with-jpeg-dir --with-png-dir --with-freetype-dir --with-iconv-dir --with-zlib-dir --with-bz2 --with-openssl --with-mcrypt --enable-soap --enable-gd-native-ttf --enable-mbstring --enable-sockets --enable-exif
    ...
    configure: error: mcrypt.h not found. Please reinstall libmcrypt.
    
    1
    2
    3
    4
    5

    缺少对应组件,通过安装 `libmcrypt-devel` 来解决

    ```shell
    $ sudo yum install -y libmcrypt-devel
    (安装 `libmcrypt-devel` 的前提是,系统里得有 `epel-release` 才可以,根据具体情况,可能首先需要安装 `epel-release` 才可以)
    1
    $ sudo yum install -y epel-release
    以上全都做完之后,就可以编译好 ![3.3.1](https://moka.anitsuri.top/images/LAMP/3.3.1.png)

​ 然后开始编译

1
2
$ sudo make #这里单独写出来是因为这里的 make 要在5min以上,请耐心等候
$ sudo make install
  • 检查是否安装成功:使用命令 echo $? 如果输出 0 那就是成功

​ 最后复制配置文件

1
$ sudo cp php.ini-production /usr/local/php/php.ini
3.2 问题与解决

​ 在编译时,遇到了这些错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ sudo ./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2.4/bin/apxs --with-config-file-path=/usr/local/php/etc --with-mysql=/usr/local/mysql --with-libxml-dir --with-gd --with-jpeg-dir --with-png-dir --with-freetype-dir --with-iconv-dir --with-zlib-dir --with-bz2 --with-openssl --with-mcrypt --enable-soap --enable-gd-native-ttf --enable-mbstring --enable-sockets --enable-exif
...
configure: error: xml2-config not found. Please check your libxml2 installation.

checking for BZip2 in default path... not found
configure: error: Please reinstall the BZip2 distribution

configure: error: jpeglib.h not found.

configure: error: png.h not found.

configure: error: freetype-config not found.

configure: error: mcrypt.h not found. Please reinstall libmcrypt.

​ 一句话,就是缺少组件,在编译前,通过这么一条命令来安装所有在这次编译中所需要的组件

1
$ sudo yum install -y libxml2-devel bzip2 bzip2-devel libjpeg-devel libpng libpng-devel freetype freetype-devel

然后如果自己的系统没有 epel-release 的话,就先

1
$ sudo yum install -y epel-release

如果要是有,就直接再上面那条的命令后面再加上一个 libmcrypt-devel
否则就是

1
$ sudo yum install -y libmcrypt-devel

4. 配置 httpd 支持 PHP

httpd 的主配置文件为 /usr/local/apache2.4/conf/httpd.conf

编辑 httpd 主配置文件,如下所示

1
$ sudo vi /usr/local/apache2.4/conf/httpd.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
搜索 'ServerName' 把 '#ServerName www.example.com:80' 前面的井号删除,找到以下内容

<Directory /> #第203行
AllowOverride none #第204行
Require all denied #第205行
</Directory> #第206行

改写如下

<Directory />
AllowOverride none
Require all granted #就变了这个
</Directory>

# 修改它的目的是,允许所有请求
# 如果不设置改行,则我们访问的时候会报 403 错误
1
2
3
4
找到下面这行
AddType application/x-gzip .gz .tgz #第391行
在改行下面'添加'一行
AddType application/x-httpd-php .php
1
2
3
4
5
6
7
8
9
10
然后找到下面这一段

<IfModule dir_module> #第253行
DirectoryIndex index.html #第524行
</IfModule> #第255行

将改行修改为
<IfModule dir_module>
DirectoryIndex index.html index.php #就改了这行
</IfModule>

5. 测试 LAMP 是否成功

5.1 测试配置文件是否正确

​ 启动 httpd 之前需要先检验配置文件是否正确

1
2
$ sudo /usr/local/apache2.4/bin/apachectl -t
Syntax OK

​ 如像上面一样显示 Syntax OK 即为配置成功,否则请继续修改配置文件 httpd.conf

​ 然后启动 httpd

1
$ sudo /usr/local/apache2.4/bin/apachectl start

​ 查看是否启动的命令

1
2
$ netstat -lnp | grep httpd
tcp6 0 0 :::80 :::* LISTEN 1833/httpd

​ 如果像上面一样显示了那么一行,则说明已经启动了 httpd

​ 另外,我们也可以使用 curl 命令进行简单的测试

1
2
$ curl localhost
<html><body><h1>It works!</h1></body></html>

​ 如果显示了这么一行,则说明测试成功

3.5.1

5.2 测试是否正确解析 PHP

​ 首先编写一个测试脚本,如下所示

1
$ sudo vi /usr/local/apache2.4/htdocs/1.php

​ 并写入如下内容

1
2
3
<?php
echo "php解析正常";
?>

​ 保存脚本后继续测试,如下所示

1
2
$ curl localhost/1.php
php解析正常

​ 如果能显示以上信息,则说明 PHP 解析正确
3.5.2

四、 配置 LAMP(主要是配置 httpd)

1. 配置文件

1.1 主服务器配置文件

httpd.conf

  • 位置:通常在 /etc/httpd/conf/httpd.conf/usr/local/apache2/conf/httpd.conf
  • 作用:这是 Apache 的主配置文件,包含了服务器的全局设置和主服务器配置。例如:
    • ServerRoot:指定 Apache 安装目录的路径
    • Listen:指定服务器监听的 IP 地址和端口号
    • DocumentRoot:指定网站文件的根目录
1.2 虚拟主机配置文件

httpd.confconf.d/*.conf

  • 位置:虚拟主机配置可以直接在 httpd.conf 文件中进行,也可以在 conf.d 目录下的单独配置文件中进行
  • 作用:配置多个虚拟主机,使得同一台服务器可以托管多个网站。例如:
    • **<VirtualHost>**:定义虚拟主机的配置块,指定虚拟主机的 IP 地址、端口号、文档根目录等
1.3 日志配置文件

httpd.conf

  • 位置:通常在 /etc/httpd/conf/httpd.conf/usr/local/apache2/conf/httpd.conf
  • 作用:配置 Apache 的日志文件。例如:
    • ErrorLog:指定错误日志文件的路径
    • CustomLog:指定访问日志文件的路径和格式
1.4 进程相关配置文件

envvars

  • 位置:通常在 /etc/httpd/envvars/usr/local/apache2/bin/envvars
  • 作用:用于设置 Apache 服务器的环境变量,例如 PATH、LD_LIBRARY_PATH 等

2. Apache 配置内容、功能

2.1 默认虚拟主机

Q. 什么是 虚拟主机 和 默认虚拟主机?

A. 虚拟主机 就像是在一台物理服务器上创建多个独立的小房间,每个房间都可以托管一个独立的网站。这样,多个网站可以共享同一台服务器的资源(如 CPU、内存、存储等),但彼此之间是独立的。每个虚拟主机都有自己的域名、配置和文件目录。
默认虚拟主机 是当服务器接收到一个请求,但无法确定该请求属于哪个虚拟主机时,服务器会将请求发送到默认虚拟主机。就像是一个接待处,当有人进来但没有明确说明要去哪个房间时,接待处会先接待他们。

最后总结成一句话,任何一个域名指向这台服务器,只要是没有对应的虚拟主机,就会由这个默认服务器来处理。

​ 那这个虚拟主机长啥样?我们怎么去配置它?就得来看看 httpd 的配置文件 httpd.conf

1
2
3
$ sudo vi /usr/local/apache2.4/conf/httpd.conf
# 然后搜索关键词 httpd-vhost,找到这行然后把行首的井号删除
Include conf/extra/httpd-vhosts.conf #第481行

​ 保存之后编辑虚拟主机配置文件 httpd-vhosts.conf

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
39
$ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf

# 可以先看看该文件最后面的两段(以 <VirtualHost> 开头,以 </VirtualHost> 结尾),这样一段就是一个虚拟主机,在这里面可以定义网站的域名和对应的网站程序所在目录
# 而默认虚拟主机就是第一个配置段

# 以下内容在第23行~30行
<VirtualHost *:80>
ServerAdmin webmaster@dummy-host.example.com
DocumentRoot "/usr/local/apache2.4/docs/dummy-host.example.com"
ServerName dummy-host.example.com
ServerAlias www.dummy-host.example.com
ErrorLog "logs/dummy-host.example.com-error_log"
CustomLog "logs/dummy-host.example.com-access_log" common
</VirtualHost>

# 以下内容在第32行~38行
<VirtualHost *:80>
ServerAdmin webmaster@dummy-host2.example.com
DocumentRoot "/usr/local/apache2.4/docs/dummy-host2.example.com"
ServerName dummy-host2.example.com
ErrorLog "logs/dummy-host2.example.com-error_log"
CustomLog "logs/dummy-host2.example.com-access_log" common
</VirtualHost>


# 现在把这两段改成这样
<VirtualHost *:80>
ServerAdmin moka@anitsuri.top
DocumentRoot "/data/wwwroot/anitsuri.com"
ServerName anitsuri.com
ServerAlias www.anitsuri.com
ErrorLog "logs/anitsuri.com-error_log"
CustomLog "logs/anitsuri.com-access_log" common
</VirtualHost>

<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
</VirtualHost>
  • 配置:
    • ServerAdmin:指定管理员邮箱(平常没啥用)
    • DocumentRoot:虚拟主机站点的根目录,网站的程序就放在这个目录下面
    • ServerName:网站的域名
    • ServerAlias:网站的第二域名,这里的域名可以写多个,用 空格 分割
    • ErrorLog:网站的错误日志
    • CustomLog:网站的访问日志

​ 这里我们定义了两个站点 anitsuri.com123.com,那么当把第三个域名 abc.com 指向本机的时候,会去访问 anitsuri.com,也就是默认虚拟主机

测试

​ 让我们测试一下

1
2
3
4
5
6
7
8
9
10
11
$ sudo mkdir -p /data/wwwroot/anitsuri.com /data/wwwroot/www.123.com
$ sudo echo "anitsuri.com" > /data/wwwroot/anitsuri.com/index.html
$ sudo echo "123.com" > /data/wwwroot/www.123.com/index.html
$ sudo /usr/local/apache2.4/bin/apachectl -t
Syntax OK #显示这个表示语法正确
$ sudo /usr/local/apache2.4/bin/apachectl start #如果这里是已经启动的状态,那么就把 start 换成 graceful 来重新加载 httpd 服务

$ curl -x 127.0.0.1:80 www.123.com
123.com
$ curl -x 127.0.0.1:80 www.abc.com
anitsuri.com

4.2.1

​ 由此,可以看出默认主机为anitsuri.com,不管把什么域名指向该服务器,只要配置文件中没有标记,就会访问这个默认主机

2.2 用户认证

用户认证这个功能就是在用户访问网站的时候,需要认证(输入用户名和密码)才能顺利访问。一些比较重要的站点或者网站后台通常会加上用户认证,目的是保证安全

​ 先来对 123.com 站点做一个全站的用户认证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf

# 把 123.com 的那个虚拟主机编辑成如下内容(也就是原第32~35行)

<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
<Directory /data/wwwroot/www.123.com> #指定认证的目录
AllowOverride AuthConfig #相当于是打开认证的开关
AuthName "123.com user auth" #自定义认证的名字,作用不大
AuthType Basic #认证的类型,一般是Basic(其他类型的话原作者(阿铭)没用过)
AuthUserFile /data/.htpasswd #指定密码文件所在位置
require valid-user #指定需要认证的用户为全部可用用户
</Directory>
</VirtualHost>

​ 这样 httpd 的配置文件配置完成,但是还需要创建密码文件

1
2
3
4
$ sudo /usr/local/apache2.4/bin/htpasswd -cm /data/.htpasswd moka
New password:
Re-type new password:
Adding password for user moka
  • 配置:
    • htpasswd 命令为创建用户的工具,-c 为 create(创建),-m 为指定密码加密方式为 MD5
    • /data/.htpasswd 为密码文件,moka 为要创建的用户,第一次执行该命令的时候需要加上 -c,第二次再创建新的用户的时候就不用加 -c 了,否则 /data/.htpasswd 文件会被重置,之前的用户被清空
1
2
3
4
# 验证没问题之后重启或者重新加载
$ sudo /usr/local/apache2.4/bin/apachectl -t
Syntax OK
$ sudo /usr/local/apache2.4/bin/apachectl graceful
测试

​ 配置完成后,需要到宿主机(真实的电脑)上去修改一下 hosts 文件,类似于 Linux 上的 /etc/hosts
​ Windows 上的 hosts 文件所在路径为 C:\Windows\System32\drivers\etc\hosts,用记事本/写字板(或者其他支持文本编辑的工具,如:Notepad2)打开
4.2.2_1

​ 保存 hosts 文件后,就可以用 Windows 的浏览器去访问 www.123.com 了。这时会弹出一个用于认证的提示框

这里在访问的时候出现了一个问题 ”我在我的 Windows 主机上能 ping 通 www.123.com(192.168.75.150 --- 主机地址) 但是浏览器却无法连接这个网址

解决方法:记得关闭防火墙

1
$ systemctl stop firewalld

4.2.2_2

​ 输入完用户名和密码后,将会进入 www.123.com 网站

​ 上面的操作是针对整个站点做的认证,其实也可以针对某个目录或者某个文件进行认证
​ 比如说要对 www.123.com/admin/ 目录进行认证,只需要修改一个地方:把 <Directory /data/wwwroot/www.123.com> 改为 <Directory /data/wwwroot/www.123.com/admin>
​ 如果是一个文件,比如 www.123.com/admin.php 则需要这样做

1
2
3
4
5
6
7
8
9
10
11
<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
<FilesMatch admin.php>
AllowOverride AuthConfig
AuthName "123.com user auth"
AuthType Basic
AuthUserFile /data/.htpasswd
require valid-user
</FilesMatch>
</VirtualHost>

​ 但是这样会有一个问题,网址中带有 admin.php 的链接都会弹出认证窗口

2.3 域名跳转

Q. 什么是域名跳转
A. 一个网站可能会有多个域名,比如 我的博客 可以用 blog.anitsuri.top 访问,也可以用 moka.anitsuri.top 访问
而用 blog.anitsuri.top 访问到时候,浏览器里面的网址直接变成了 moka.anitsuri.top ,这就是域名的跳转过程

Q. 域名跳转有什么作用
A.

  1. 用户体验:将用户从旧域名或错误的URL重定向到正确的页面,确保他们能够找到所需的信息
  2. SEO优化:通过重定向旧的或无效的链接到新的URL,保持搜索引擎排名,避免因死链而导致的SEO问题
  3. 网站迁移:在更换域名或网站结构时,通过重定向确保旧链接仍然有效,避免流量损失
  4. 负载均衡:将流量重定向到不同的服务器,以分散负载,提高网站性能和可靠性

​ 那接下来我们将会实现一个需求:把 123.com 域名跳转到 www.123.com,配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
$ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf

# 把 123.com 的那个虚拟主机编辑成如下内容(也就是原第32~42行)
<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
ServerAlias 123.com
<IfModule mod_rewrite.c> #需要 mod_rewrite 模块支持
RewriteEngine on #打开 rewrite 功能
RewriteCond %{HTTP_HOST} !^www.123.com$ #定义 rewrite 的条件:当主机名(域名)不是 www.123.com 时满足条件
RewriteRule ^/(.*)$ http://www.123.com/$1 [R=301,L] #定义 rewrite 规则:当满足上面的条件时,这条规则才会执行
</IfModule>
</VirtualHost>
  • 备注:

    • 在 RewriteRule 里是有【正则表达式】存在的。RewriteRule 后面由空格划分成三个部分

      • 第一个部分是当前的 URL (也就是网址),不过这个 URL 是不把 主机头(也就是域名) 算在内的
      • 第二个部分是 要跳转的目标地址,这个地址可以写全(包含了主机头),当然也可以不加主机头,默认就是前面定义的 ServerName
      • 第三个部分是一些选项,需要用方括号括起来
        • 301 是 状态码,它被称作 “永久重定向” (还有一种跳转用的状态码为 302,叫做 “临时重定向”)
        • L 表示 “last”,意思是跳转一次就结束了
    • 要实现域名跳转,需要有 rewrite 模块支持,所以先查看 httpd 是否已经加载该模块,如果没有还需要配置:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      $ sudo /usr/local/apache2.4/bin/apachectl -M | grep -i rewrite
      # 如果此处没有任何输出,则需要编辑配置文件

      $ sudo vi /usr/local/apache2.4/conf/httpd.conf
      # 搜索 rewrite 找到那行(第153行)把前面的井号删除

      # 重新加载配置文件
      $ sudo /usr/local/apache2.4/bin/apachectl graceful
      $ sudo /usr/local/apache2.4/bin/apachectl -M | grep -i rewrite
      rewrite_module (shared) #有这一行输出,说明正常加载 rewrite 模块

      4.2.3_1

测试

​ 配置完成后,进行测试,这里我们用 curl 测试就可以看到效果,如下所示

1
2
3
4
5
6
$ curl -x 127.0.0.1:80 -I 123.com
HTTP/1.1 301 Moved Permanently
Date: Thu, 08 Aug 2024 07:45:37 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
Location: http://www.123.com/
Content-Type: text/html; charset=iso-8859-1

4.2.3_2

​ 可以看到状态码为 301,跳转后的网址 (Location 那行) 为 http://www.123.com/,则为配置成功

2.4 访问日志

访问日志的作用很大,不仅可以记录网站的访问情况,还可以在网站由异常发生时帮组我们定位问题,比如有攻击的时候,可以通过查看日志看到一些规律的

​ 要配置 httpd 访问日志,首先要在主配置文件中定义访问日志的格式

1
2
3
4
5
$ sudo vi /usr/local/apache2.4/conf/httpd.conf

# 然后找到 LogFormat 的字样
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined #第286行
LogFormat "%h %l %u %t \"%r\" %>s %b" common #
  • 可以看到两个格式的日志,建议用第一个,记录的信息会更安全
    • %h 为访问网站的IP
    • %l 为远程登录名, 这个字段基本上为 -
    • %u 为用户名,当使用用户认证时,这个字段为认证的用户名
    • %t 为时间
    • %r 为请求的动作(比如用 curl -I 时就为 HEADE
    • %s 为请求的状态码,写成 %>s 为最后的状态码
    • %b 为传输数据大小
    • %{Referer}i 为 referer 信息
      请求本次地址上一次的地址就是 referer
      比如在百度中搜索 Rotaeno,然后通过百度的搜索结果页面点击到了 Rotaeno官网,那访问 Rotaeno 官网的 referer 就是 baidu,当然那个地址肯定是很长的
    • %{User-Agent} 为浏览器标识
      比如你用 FireFox 或者 Chrome 浏览器,则该字段显示内容不一样,是带有浏览器标识的
  • 对于这个日志格式,你可以自定义调整各个字段的位置,也可以额外增加其他字段
    比如增加 %D (请求消耗时间) 不过对我来说没啥用,就没整

​ 然后继续编辑虚拟主机配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf

# 把 123.com 的那个虚拟主机编辑成如下内容(也就是原第32~41行)
<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
ServerAlias 123.com
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP_HOST} !^www.123.com$
RewriteRule ^/(.*)$ http://www.123.com/$1 [R=301,L]
</IfModule>
CustomLog "logs/123.com-access_log" combined #只有这行变化(新增)了
</VirtualHost>

​ 保存配置文件后,测试语法并重新加载配置

1
2
3
4
5
6
$ sudo /usr/local/apache2.4/bin/apachectl -t
Syntax OK
$ sudo /usr/local/apache2.4/bin/apachectl graceful
$ curl -x 127.0.0.1:80 123.com
$ sudo tail /usr/local/apache2.4/logs/123.com-access_log
127.0.0.1 - - [08/Aug/2024:18:57:04 +0800] "GET HTTP://123.com/ HTTP/1.1" 301 227 "-" "curl/7.29.0"

4.2.4_1

​ 发现生成了日志,并且有相关的日志记录
​ 另外,用 FireFox 浏览器访问一下,再次查看日志,发现多了一行

1
2
3
$ sudo tail /usr/local/apache2.4/logs/123.com-access_log
127.0.0.1 - - [08/Aug/2024:18:57:04 +0800] "GET HTTP://123.com/ HTTP/1.1" 301 227 "-" "curl/7.29.0"
192.168.75.10 - - [08/Aug/2024:19:01:59 +0800] "GET / HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0"

​ 一个网站会有很多元素,尤其是图片、js、css等静态的文件非常多,用户每请求一个页面就会访问诸多的图片、js等静态元素,这些元素的请求都会被记录在日志中
​ 如果一个站点访问量很大,那么访问日志文件增长会非常快,一天就可以达到几GB。这不仅对于服务器的磁盘空间造成影响,更重要的时会影响磁盘的读写速度
​ 但是访问日志又非常重要,又不能不去记录,还好这些巨量的静态元素请求记录到日志里意义不大

​ 所以可以限制这些静态元素去记录日志,并且需要把日志按天归档,一天一个日志,这样也可以防止单个日志文件过大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf

# 把 123.com 的那个虚拟主机编辑成如下内容(也就是原第32~42行)
<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
ServerAlias 123.com
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP_HOST} !^www.123.com$
RewriteRule ^/(.*)$ http://www.123.com/$1 [R=301,L]
</IfModule>
SetEnvIf Request_URI ".*\.gif$" image-request
SetEnvIf Request_URI ".*\.jpg$" image-request
SetEnvIf Request_URI ".*\.png$" image-request
SetEnvIf Request_URI ".*\.bmp$" image-request
SetEnvIf Request_URI ".*\.swf$" image-request
SetEnvIf Request_URI ".*\.js$" image-request
SetEnvIf Request_URI ".*\.css$" image-request
CustomLog "|/usr/local/apache2.4/bin/rotatelogs -l logs/123.com-access_%Y%m%d.log 86400" combined env=!image-request
</VirtualHost>
  • 先定义了一个 image-request 环境变量,把 gif、jpg、png、bmp、swf、js、css 等格式的文件全部归类到 image-request 里,后面的 env=!image-request 有用到一个 **!**,这相当于取反了,意思是吧 image-request 以外类型的文件记录到日志里
  • 正常应该 CustomLog 后面为日志文件名,但是这里我们用了一个管道,它会把日志内容交给后面的 rotatelogs 命令处理
    这个 rotatelogs 为 httpd 自带切割日志的工具,它会把访问日志按我们定义的文件名格式进行切割
  • 其中 86400 单位是 “秒” 也就是 “一天”
测试

​ 保存配置文件后,测试配置文件是否存在错误,没有错误的话就重新加载服务

1
2
3
4
5
6
7
$ sudo /usr/local/apache2.4/bin/apachectl -t
Syntax OK
$ sudo /usr/local/apache2.4/bin/apachectl graceful
$ curl -x 127.0.0.1:80 -I 123.com
$ sudo ls /usr/local/apache2.4/logs/
123.com-access_20240808.log access_log anitsuri.com-error_log httpd.pid
123.com-access_log anitsuri.com-access_log error_log

​ 这里的 123.com-access_20240808.log 就是新生成的日志,而且以后会每天生成一个按日期命名的日志
​ 下面再来测试一下 “在日志不记录静态元素” 的配置

1
2
3
4
5
6
7
$ sudo touch /data/wwwroot/www.123.com/moka.jpg
$ sudo touch /data/wwwroot/www.123.com/moka.txt
$ curl -x 127.0.0.1:80 www.123.com/moka.jpg
$ curl -x 127.0.0.1:80 www.123.com/moka.txt
$ sudo cat /usr/local/apache2.4/logs/123.com-access_20240808.log
127.0.0.1 - - [08/Aug/2024:19:29:04 +0800] "HEAD HTTP://123.com/ HTTP/1.1" 301 - "-" "curl/7.29.0"
127.0.0.1 - - [08/Aug/2024:19:39:11 +0800] "GET HTTP://www.123.com/moka.txt HTTP/1.1" 200 - "-" "curl/7.29.0"

4.2.4_2

​ 可以看到,123.com-access_20240808.log 日志中只有 moka.txt 的请求日志,但没有 moka.jpg 的请求日志
​ 去除了静态元素的日志,日志文件会瘦身很多

2.5 静态元素过期时间

​ 在上面的日志里,如果你观察的很仔细,会发现有一个状态码为 304,这个状态码标识该文件已经缓存到用户的电脑里了,再次请求它的时候不用从服务器上下载,而是直接访问用户电脑里面的缓存
​ 这样做的目的是降低服务器的资源小号,还可以提升用户访问网站的速度
​ 平时我们访问一个网站时,会有很多元素为静态的小图片,那这些小图片完全可以缓存到咱们的电脑里,这样再次访问该站点时,速度就会更快

​ 那到底能缓存多久呢?如果服务器上某个图片更改了,那么应该访问新的图片才对
​ 这就涉及了一个静态文件缓存市场的问题。也叫做 ”缓存过期时间“

​ 在 httpd 的配置文件中,我们是可以控制这个时间的

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
$ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf

# 把 123.com 的那个虚拟主机编辑成如下内容(也就是原第32~42行)
<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
ServerAlias 123.com
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP_HOST} !^www.123.com$
RewriteRule ^/(.*)$ http://www.123.com/$1 [R=301,L]
</IfModule>
SetEnvIf Request_URI ".*\.gif$" image-request
SetEnvIf Request_URI ".*\.jpg$" image-request
SetEnvIf Request_URI ".*\.png$" image-request
SetEnvIf Request_URI ".*\.bmp$" image-request
SetEnvIf Request_URI ".*\.swf$" image-request
SetEnvIf Request_URI ".*\.js$" image-request
SetEnvIf Request_URI ".*\.css$" image-request
CustomLog "|/usr/local/apache2.4/bin/rotatelogs -l logs/123.com-access_%Y%m%d.log 86400" combined env=!image-request
<IfModule mod_expires.c> #从这里是新增内容
ExpiresActive on
ExpiresByType image/gif "access plus 1 days"
ExpiresByType image/jpeg "access plus 24 hours"
ExpiresByType image/png "access plus 24 hours"
ExpiresByType text/css "now plus 2 hour"
ExpiresByType application/x-javascript "now plus 2 hours"
ExpiresByType application/javascript "now plus 2 hours"
ExpiresByType application/x-shockwave-flash "now plus 2 hours"
ExpiresDefault "now plus 0 min"
</IfModule>
</VirtualHost>
  • 这部分配置用到了 mod_expires 模块,相关配置内容并不难理解,通过字面也可以大概猜到其含义
  • 这里 gif、jpeg、png 格式的文件过期时长为 1天,css、js、flah 格式的文件过期时长为 2 消失,其他文件过期时长为 0,也就是不缓存

​ 保存配置文件,检查配置是否有问日,没问题重新加载服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 检查配置语法是否出现问题
$ sudo /usr/local/apache2.4/bin/apachectl -t
Syntax OK
$ sudo /usr/local/apache2.4/bin/apachectl graceful

# 检查 httpd 是否加载 expires 模块
$ sudo /usr/local/apache2.4/bin/apachectl -M | grep -i expires

# 此处没有任何输出,说明当前 httpd 不支持 expires 模块,所以需要修改配置文件,打开该模块
$ sudo vi /usr/local/apache2.4/conf/httpd.conf

# 搜索 expires 关键词,找到下面的行(第111行),然后把本行最前面的#删除
#LoadModule expires_module modules/mod_expires.so

# 重新加载服务
$ sudo /usr/local/apache2.4/bin/apachectl graceful
$ sudo /usr/local/apache2.4/bin/apachectl -M | grep -i expires
expires_module (shared) #有这行输出,说明已经正确加载 expires 模块

4.2.5_1

测试

​ 下面,使用 curl 来测试它的效果

1
2
3
4
5
6
7
8
9
10
$ sudo curl -x 127.0.0.1:80 -I www.123.com/moka.jpg
HTTP/1.1 200 OK
Date: Fri, 09 Aug 2024 05:53:29 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
Last-Modified: Thu, 08 Aug 2024 11:34:21 GMT
ETag: "0-61f2a68709d3b"
Accept-Ranges: bytes
Cache-Control: max-age=86400
Expires: Sat, 10 Aug 2024 05:53:29 GMT
Content-Type: image/jpeg

4.2.5_2

  • 可以看到 max-age-86400,这说明 jpg 的图片将缓存 86400 秒,也就是一天
  • 上面我们也提到了状态码是 304,按理说既然有缓存,那我们请求该图片的时候也应该是 304 才对,但是这里却是 200
    这是因为我们是使用 curl 工具来请求的,它并不像浏览器那样可以缓存这些文件

​ 另外也可以测试一下 txt 的文件

1
2
3
4
5
6
7
8
9
10
$ sudo curl -x 127.0.0.1:80 -I www.123.com/moka.txt
HTTP/1.1 200 OK
Date: Fri, 09 Aug 2024 05:57:43 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
Last-Modified: Thu, 08 Aug 2024 11:34:25 GMT
ETag: "0-61f2a68a608f5"
Accept-Ranges: bytes
Cache-Control: max-age=0
Expires: Fri, 09 Aug 2024 05:57:43 GMT
Content-Type: text/plain

4.2.5_3

  • 其中 max-age-0,说明没有缓存该类型的文件
2.6 防盗链

讲个故事:(故事来源于《跟阿铭学Linux 第三版》
2009年的时候,阿铭负责运维一个业务5d6d(早已经死翘翘了)为免费论坛,谁都可以申请,其中一个站长申请了一个站点专门来存放图片。由于经验匮乏,阿铭并没有给该业务做防盗链,所以导致这个网站的图片随便被别的网站借用,该站点的图片有点偏色情,所以访问量巨大,最终导致该业务带宽在一个月内飙升了 300 M.当发现带宽异常时,通过抓包找到了对应的站点,随后给服务器做了防盗链,并且删除了有问题的图片,这样我们才把带宽恢复到正常值。但那个月的带宽费用确实让公司出血了。

通过这个真实案例,我们可以体会到做防盗链是多么有价值。
防盗链,通俗讲,就是不让别人到用你网站上的资源。这个资源,通常指的是图片、视频、歌曲、文档等
讲解防盗链配置之前,再说一下 referer 的概念,上面讲解日志格式的时候提到过它
我们通过 A 网站的一个页面 http://a.com/a.html 里面的链接去访问 B 网站的一个页面 http://b.com/b.html,那么这个 B 网站页面的 referer 就是 http://a.com/a.html。也就是说,一个 referer 其实就是一个网站

​ 下面来学习配置防盗链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf

# 把 123.com 的那个虚拟主机编辑成如下内容(也就是原第32~60行)
# 为了节省空间,这里删掉了前面的一些配置
<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
ServerAlias 123.com
CustomLog "|/usr/local/apache2.4/bin/rotatelogs -l logs/123.com-access_%Y%m%d.log 86400" combined env=!image-request
<Directory /data/wwwroot/www.123.com>
SetEnvIfNoCase Referer "http://www.123.com" local_ref
SetEnvIfNoCase Referer "http://123.com" local_ref
SetEnvIfNoCase Referer "^$" local_ref
<filesmatch "\.(txt|doc|mp3|zip|rar|jpg|gif)">
Order Allow,Deny
Allow from env=local_ref
</filesmatch>
</Directory>
</VirtualHost>
  • 首先定义允许访问链接的 referer,其中 ^$ 为空 referer,当直接在浏览器里输入图片地址去访问它时,它的 referer 就为空。然后又使用 filesmatch 来定义需要保护的文件类型,访问 txt、doc、mp3、zip、rar、jpg、gif 格式的文件,当访问这样类型的文件时就会被限制。
测试
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
$ sudo /usr/local/apache2.4/bin/apachectl -t
Syntax OK
$ sudo /usr/local/apache2.4/bin/apachectl graceful

# 使用 -e 来定义referer,这个referer一定要以 http:// 开头,不然不管用
$ curl -x 127.0.0.1:80 -I -e "http://www.123.com/123.txt" http://www.123.com/moka.jpg
HTTP/1.1 200 OK
Date: Fri, 09 Aug 2024 10:12:00 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
Last-Modified: Thu, 08 Aug 2024 11:34:21 GMT
ETag: "0-61f2a68709d3b"
Accept-Ranges: bytes
Content-Type: image/jpeg

# 使用非允许的referer会返回403的状态码
$ curl -x 127.0.0.1:80 -I -e "http://1234.com/1.txt" http://www.123.com/moka.jpg
HTTP/1.1 403 Forbidden
Date: Fri, 09 Aug 2024 10:14:16 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
Content-Type: text/html; charset=iso-8859-1

# 访问 HTML 类型的文件时,也不会被保护
$ curl -x 127.0.0.1:80 -I -e "http://www.1234.com/1.txt" http://www.123.com/index.html
HTTP/1.1 200 OK
Date: Fri, 09 Aug 2024 10:16:06 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
Last-Modified: Thu, 08 Aug 2024 05:28:30 GMT
ETag: "8-61f254c0d6711"
Accept-Ranges: bytes
Content-Length: 8
Content-Type: text/html

4.2.6

2.7 访问控制

​ 对于一些比较重要的网站内容,出来可以使用用户认证限制访问之外,还可以通过其他一些方法做到限制,比如可以限制 IP,也可以限制 user_agent

  • 限制 IP 只的是限制访问网站的来源 IP
  • 限制 user_agent 通常用来限制恶意或者不正常的请求
2.7.1 限制 IP 访问

​ 先来看如何限制 IP 访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf

# 把 123.com 的那个虚拟主机编辑成如下内容(也就是原第32~46行)
<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
ServerAlias 123.com
CustomLog "|/usr/local/apache2.4/bin/rotatelogs -l logs/123.com-access_%Y%m%d.log 86400" combined env=!image-request
<Directory /data/wwwroot/www.123.com/admin/>
Order deny,allow
Deny from all
Allow from 127.0.0.1
</Directory>
</VirtualHost>
  • ​ 使用 <Directory> 来指定要限制访问的目录,order 定义控制顺序,哪个在前面就先匹配哪个规则
    ​ 在本例中 deny 在前面,所以先匹配 Deny from all,这样所有的来源 IP 都会被限制,然后匹配 Allow from 127.0.0.1,这样又允许了 127.0.0.1 这个 IP
    ​ 最终的结果是,只允许来源为 127.0.0.1 的访问
2.7.1 限制 IP 访问 测试

​ 下面为认证过程

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
$ sudo /usr/local/apache2.4/bin/apachectl -t
Syntax OK
$ sudo /usr/local/apache2.4/bin/apachectl graceful

$ sudo mkdir /data/wwwroot/www.123.com/admin
$ sudo echo "admin" > /data/wwwroot/www.123.com/admin/index.html

$ curl -x 192.168.92.128:80 -I www.123.com/admin/index.html
HTTP/1.1 403 Forbidden
Date: Fri, 09 Aug 2024 11:10:57 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
Content-Type: text/html; charset=iso-8859-1

$ curl -x 127.0.0.1:80 -I www.123.com/admin/index.html
HTTP/1.1 200 OK
Date: Fri, 09 Aug 2024 11:11:07 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
Last-Modified: Fri, 09 Aug 2024 11:10:43 GMT
ETag: "6-61f3e31bcb5f7"
Accept-Ranges: bytes
Content-Length: 6
Content-Type: text/html

$ sudo cat /usr/local/apache2.4/logs/123.com-access_20240809.log
192.168.92.128 - - [09/Aug/2024:19:10:57 +0800] "HEAD HTTP://www.123.com/admin/index.html HTTP/1.1" 403 - "-" "curl/7.29.0"
127.0.0.1 - - [09/Aug/2024:19:11:07 +0800] "HEAD HTTP://www.123.com/admin/index.html HTTP/1.1" 200 - "-" "curl/7.29.0"

4.2.7.1

  • 本机有两个IP (其实是三个,但是另一个没用上),一个是 192.168.92.128,一个是 127.0.0.1,通过这两个 IP 都可以访问到站点
    可来源IP分别为 192.168.92.128127.0.0.1,其实和本机IP是一样的,也可以使用 Windows 的浏览器访问
    4.2.7.1_2
    浏览器访问提示 Forbidden 其实就是 403
    再来看看日志,可以查看到对应来源IP为 192.168.75.10,不要把来源IP和本机IP搞混了
    前面的实验中之所以本机IP和来源IP一样,就是因为相当于自己访问自己,而后面用浏览器访问,相当于拿 Windows 机器访问 Linux 服务器

    1
    2
    $ sudo tail -1 /usr/local/apache2.4/logs/123.com-access_20240809.log 
    192.168.75.10 - - [09/Aug/2024:19:19:13 +0800] "GET /admin HTTP/1.1" 403 199 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0"
  • 也可以单独针对某个文件来限制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf

    <Directory /data/wwwroot/www.123.com>
    <Filesmatch "admin.php(.*)">
    Order deny,allow
    Deny from all
    Allow from 127.0.0.1
    </Filesmatch>
    </Directory>
2.7.2 限制恶意或者不正常的请求 1(禁止指定目录进行解析)

对于使用PHP语言编写的网站,有一些目录是由需求上传文件的,比如之前列举的防盗链故事,因为服务器可以上传图片,并且没有做防盗链,所以被人家当成了一个图片存储服务器,并且盗用带宽流量
如果网站代码有漏洞,让黑客上传了一个用PHP代码写的木马,由于网站可以执行 PHP 程序,最终会让黑客拿到服务器权限

​ 为了避免上诉情况发生,我们需要把能上传文件的目录直接禁止解析 PHP 代码(不用担心会影响网站访问,若这种目录也需要解析 PHP,那说明程序员不合格)

1
2
3
4
5
6
7
8
9
10
11
12
$ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf

# 把 123.com 的那个虚拟主机编辑成如下内容(也就是原第32~42行)
<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
ServerAlias 123.com
CustomLog "|/usr/local/apache2.4/bin/rotatelogs -l logs/123.com-access_%Y%m%d.log 86400" combined env=!image-request
<Directory /data/wwwroot/www.123.com/upload>
php_admin_flag engine off
</Directory>
</VirtualHost>
2.7.2 限制恶意或者不正常的请求 1(禁止指定目录进行解析) 测试

​ 认证过程如下

1
2
3
4
5
6
7
8
9
10
$ sudo /usr/local/apache2.4/bin/apachectl -t
Syntax OK
$ sudo /usr/local/apache2.4/bin/apachectl graceful
$ sudo mkdir /data/wwwroot/www.123.com/upload/
$ sudo cp /usr/local/apache2.4/htdocs/1.php /data/wwwroot/www.123.com/upload/

$ curl -x 127.0.0.1:80 www.123.com/upload/1.php
<?php
echo "php解析正常";
?>

4.2.7.2
这说明 1.php 是不能正常解析的

2.7.3 限制恶意或者不正常的请求 2(对特定 user_agent 进行限制)

​ 下面再来研究一下 user_agent,前面讲过 user_agent 为浏览器秃瓢,当用 curl 访问时,user_agent 为 “curl/7.29.0”,用 FireFox 浏览器访问时,user_agent 为 “Mozilla/5.0(Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 FireFox/52.0”
​ 我们在日常生活中经常针对 user_agent 来限制一些访问,比如可以限制一些不太友好的搜索引擎 “爬虫”,你之所以能在百度看到自己的网站,就是因为百度会派一些 “蜘蛛爬虫” 抓取数据。类似于用户用浏览器访问网站,当 ”蜘蛛爬虫“ 太多或者访问太频繁,就会浪费服务器资源
​ 另外,也可以限制恶意请求,这种恶意请求我们通常称作 cc 攻击。它的攻击原理很简单,就是用很多用户的电脑同时访问同一个站点,当访问量或者频率达到一定层次,会耗尽服务器资源,从而使之不能提供正常服务
​ 这种 cc 攻击其实有很明显的规律,其中这些恶意请求的 user_agent 相同或者相似,那我们就可以通过限制 user_agent 发挥防攻击的作用

​ 下面是针对 user_agent 来做访问控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf 

# 把 123.com 的那个虚拟主机编辑成如下内容(也就是原第32~40行)
<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
ServerAlias 123.com
CustomLog "|/usr/local/apache2.4/bin/rotatelogs -l logs/123.com-access_%Y%m%d.log 86400" combined env=!image-request
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP_USER_AGENT} .*curl.* [NC,OR]
RewriteCond %{HTTP_USER_AGENT} .*baidu.com.* [NC]
RewriteRule .* - [F]
</IfModule>
</VirtualHost>
  • 这个需求也用到了 rewrite 模块,%{HTTP_USER_AGENT}user_agent 的内置变量
    在本例中,当 user_agent 匹配 curl 或者 baidu.com 的时候,都会触发下面的规则
  • 方括号中的 OR 标识 ”或者“,NC 表示 ”不区分大小写“,F 相当于 Forbidden
2.7.3 限制恶意或者不正常的请求 2(对特定 user_agent 进行限制)测试

​ 下面进行认证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ sudo /usr/local/apache2.4/bin/apachectl -t
Syntax OK
$ sudo /usr/local/apache2.4/bin/apachectl graceful

$ curl -I -x 127.0.0.1:80 www.123.com/upload/1.php
HTTP/1.1 403 Forbidden
Date: Sat, 10 Aug 2024 10:55:02 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
Content-Type: text/html; charset=iso-8859-1

$ curl -A "123123" -I -x 127.0.0.1:80 www.123.com/upload/1.php
HTTP/1.1 200 OK
Date: Sat, 10 Aug 2024 10:55:34 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
X-Powered-By: PHP/5.6.30
Content-Type: text/html; charset=UTF-8

4.2.7.3

  • curl-A 选项指定 user_agent
    • 第一个请求:user_agent 为 “curl/7.29.0” 匹配了第一个条件,所以会 403
    • 第二个请求:user_agent 为自定义的 “123123” 没有匹配任何条件,所以状态码为 200

3. PHP 配置内容,功能

3.1 PHP 配置

​ 虽然 PHP 是以 httpd 一个模块的形式存在的,但是 PHP 本身也有自己的配置文件
​ 查看 PHP 配置文件所在位置的命令为

1
$ sudo /usr/local/php/bin/php -i | grep -i "loaded configuration file"

此处遇到了问题

1
2
$ sudo /usr/local/php/bin/php -i | grep -i "loaded configuration file"
Loaded Configuration File => (none)

此时使用命令查看 php.ini 的位置,发现在 /usr/local/php/php.ini 里面

1
2
$ sudo find / -name php.ini
/usr/local/php/php.ini

但是,我们在部署 PHP 的时候有这么一个设置 --with-config-file-path=/usr/local/php/etc
所以 PHP 默认在 /usr/local/php/etc 这个位置查找配置文件

  • 解决方法:

    /usr/local/php/php.ini 复制到 /usr/local/php/etc 里面

    1
    $ sudo cp /usr/local/php/php.ini /usr/local/php/etc

    这时候我们再使用命令就出来了

    1
    2
    3
    $ sudo /usr/local/php/bin/php -i | grep -i "loaded configuration file"
    Loaded Configuration File => /usr/local/php/etc/php.ini
    PHP Warning: Unknown: It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in Unknown on line 0

    4.3.1

php.ini 为 PHP 的配置文件,可以看出其在 /usr/local/php/etc/php.ini,第一行的 Warning 为警告信息,可以忽略,取消这个警告需要编辑 php.ini,找到 date.timezone 设置如下

1
2
$ sudo vi /usr/local/php/etc/php.ini
# 找到 date.timezone 并把它修改为 date.timezone = Asia/Shanghai(第936行)

​ 再次执行不再提示警告信息

1
2
$ sudo /usr/local/php/bin/php -i | grep -i "loaded configuration file"
Loaded Configuration File => /usr/local/php/etc/php.ini
3.1.1 PHP 的 disable_functions

​ PHP 有诸多内置的函数,有一些函数(比如 exec)会直接调取 Linux 系统命令,如果开放,将会非常危险
​ 因此,基于安全考虑,应该把一些存在安全风险的函数禁掉

1
2
3
4
$ sudo vi /usr/local/php/etc/php.ini
# 搜索 disable_functions,编辑成如下

disable_functions = eval,assert,popen,passthru,escapeshellarg,escapeshellcmd,passthru,exec,system,chroot,scandir,chgrp,chown,escapeshellcmd,escapeshellarg,shell_exec,proc_get_status,ini_alter,ini_restore,dl,pfsockopen,openlog,syslog,readlink,symlink,leak,popepassthru,stream_socket_server,popen,proc_open,proc_close #第303行
  • 对于这里列出来的这些函数,暂时可以不用细究,将来遇到问题要记得你曾经禁用了一些函数,这些被禁掉的函数是不能在 PHP 代码中调用的

  • 更改完 php.ini 后,由于需要在 httpd 中调用 PHP,所以还需要重启 httpd 服务使其生效

    1
    $ sudo /usr/local/apache2.4/bin/httpd -k restart
3.1.2 配置 error_log

​ PHP 的日志对于程序员来讲非常重要,他是排查问题的重要手段
​ 设置 PHP 错误日志,有诸多步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
$ sudo vi /usr/local/php/etc/php.ini

# 搜索 log_errors,改成如下
log_errors = On #第116行

# 再搜索 error_log,改为
error_log = /var/log/php/php_errors.log #第572行

# 再搜索 error_reporting,改为
error_reporting = E_ALL & ~E_NOTICE #第106行

# 再搜索 display_errors,改为
display_errors = Off #第466行
  • log_errors 可以设置为 on 或者 off,如果想让 PHP 记录错误日志,需要设置为 on
  • error_log 设置错误日志路径
  • error_reporting 设定错误日志的级别
    • E_ALL 为所有类型的日志,不管是提醒还是警告都会记录
      在开发环境下面设置为 E_ALL,可以方便程序员们排查问题,但也会造成日志记录很多无意义的内容
    • & 表示并且,~ 表示排除,所以两个组合在一起就是在 E_ALL 的基础上,排除掉 notice 相关的日志
  • display_error 设置为 on,则会把错误日志直接显示在浏览器里,这样对于用户访问来说体验不好,而且还会暴露网站的一些文件路径等重要信息,所以要设置为 off

​ 设置完 php.ini,还需要做一些额外的操作

1
2
3
4
$ sudo mkdir /var/log/php
# 需要保证 PHP 的错误日志所在目录存在,并且权限为可写
$ sudo chmod 777 /var/log/php
$ sudo /usr/local/apache2.4/bin/apachectl graceful
测试
1
2
3
4
5
6
7
8
9
10
11
12
13
$ sudo vi /data/wwwroot/www.123.com/test.php

# 在这里面写入如下内容,其中古语把结尾的;丢掉
<?php
echo 111

$ curl -A "123" -I -x 127.0.0.1:80 www.123.com/test.php
HTTP/1.0 500 Internal Server Error
Date: Mon, 12 Aug 2024 10:59:40 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
X-Powered-By: PHP/5.6.30
Connection: close
Content-Type: text/html; charset=UTF-8
  • 其中出现了状态码 500,这说明我们访问的页面是存在错误的,此时需要查看 PHP 的错误日志来判断错误原因

    1
    2
    $ cat /var/log/php/php_errors.log 
    [12-Aug-2024 18:59:40 Asia/Shanghai] PHP Parse error: syntax error, unexpected end of file, expecting ',' or ';' in /data/wwwroot/www.123.com/test.php on line 3

    通过日志判断,test.php 文件第三行少了分号

3.1.3 配置 open_basedir

​ 一个服务器上跑很多网站,这是几乎所有小公司为节省成本采用的做法,但这样做也会有一些弊端:多个网站泡在同一个服务器上,如果其中一个网站被黑,很有可能连累到其他站点
​ 我觉得这里用一句俗语形容比较合适 “一颗老鼠屎坏一锅汤”
​ 为了避免这种尴尬的事情发生,我们应当做一些预防手段

​ 还好 PHP 里有一个概念叫做 open_basedir,它的作用是将网站限定在指定目录里,就算该站点被黑了,黑客只能在该目录下面所有作为,而不能左右其他目录
​ 如果你的服务器上只有一个站点,那可以直接在 php.ini 中设置 open_basedir 参数
​ 但如果服务器上跑的站点比较多,那在 php.ini 中设置就不合适了,因为在 php.ini 中只能定义一次,也就是说所有站点都一起定义限定的目录,那这样似乎起不到隔离多个站点的目的

​ 先来看如何在 php.ini 中设置 open_basedir

1
2
3
$ sudo vi /usr/local/php/etc/php.ini
# 搜索 open_basedir,改成如下
open_basedir = /tmp:/data/wwwroot/www.123.com #第298行

open_basedir 可以是多个目录,用 : 分割,先来验证 open_basedir 的作用吧
​ 因为已经限制 PHP 只能在 /tmp/data/wwwroot/www.123.com 两个目录下活动,所以这次拿 anitsuri.com 来掩饰:

1
2
3
4
5
6
7
8
9
$ sudo /usr/local/apache2.4/bin/apachectl graceful
$ sudo cp /usr/local/apache2.4/htdocs/1.php /data/wwwroot/anitsuri.com/
$ curl -x 127.0.0.1:80 -I anitsuri.com/1.php
HTTP/1.0 500 Internal Server Error
Date: Mon, 12 Aug 2024 11:35:29 GMT
Server: Apache/2.4.57 (Unix) PHP/5.6.30
X-Powered-By: PHP/5.6.30
Connection: close
Content-Type: text/html; charset=UTF-8
  • 发现 anitsuri.com/1.php 不能访问,状态码为 500,所以查看一下错误日志,得到信息如下

    1
    2
    3
    4
    5
    $ cat /var/log/php/php_errors.log
    [12-Aug-2024 18:59:40 Asia/Shanghai] PHP Parse error: syntax error, unexpected end of file, expecting ',' or ';' in /data/wwwroot/www.123.com/test.php on line 3
    [12-Aug-2024 19:35:29 Asia/Shanghai] PHP Warning: Unknown: open_basedir restriction in effect. File(/data/wwwroot/anitsuri.com/1.php) is not within the allowed path(s): (/tmp:/data/wwwroot/www.123.com) in Unknown on line 0
    [12-Aug-2024 19:35:29 Asia/Shanghai] PHP Warning: Unknown: failed to open stream: Operation not permitted in Unknown on line 0
    [12-Aug-2024 19:35:29 Asia/Shanghai] PHP Fatal error: Unknown: Failed opening required '/data/wwwroot/anitsuri.com/1.php' (include_path='.:/usr/local/php/lib/php') in Unknown on line 0

​ 下面说如何给单个虚拟主机设置 open_basedir
​ 对于 php.ini 里面的配置,在 httpd.conf 中也是可以设置的

1
2
3
4
5
6
7
8
9
10
$ sudo vi /usr/local/apache2.4/conf/extra/httpd-vhosts.conf

# 原32~43行,编辑成如下内容
<VirtualHost *:80>
DocumentRoot "/data/wwwroot/www.123.com"
ServerName www.123.com
ServerAlias 123.com
CustomLog "|/usr/local/apache2.4/bin/rotatelogs -l logs/123.com-access_%Y%m%d.log 86400" combined env=!image-request
php_admin_value open_basedir "/data/wwwroot/www.123.com/:/tmp/"
</VirtualHost>

​ 起作用的就是这句 php_admin_value,它可以定义 php.ini 里面的参数
​ 除此之外,像 error_log 之类的也可以定义
​ 这样就可以实现一个虚拟主机定义一个 open_basedir

4. PHP 动态拓展模块安装

​ 编译 httpd 是,有涉及动态和静态模块,其实 PHP 也一样有这样的说法
​ 在这篇文章中讲述的 PHP 安装时,所有的木块全部都为静态,并没有任何的模块
​ 所谓动态,就是一个独立存在的 .so 文件,在 httpd 中 PHP 就是以动态模块的形式被加载的
​ PHP 一旦编译完成后,要想再增加一个功能模块的话,要么重新编译 PHP,要么直接编译一个拓展模块(生成一个 .so 文件),然后在 php.ini 中配置一下,就可以加载使用了

​ 首先,应该会查看 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
39
40
41
$ sudo /usr/local/php/bin/php -m
[PHP Modules]
bz2
Core
ctype
date
dom
ereg
exif
fileinfo
filter
gd
hash
iconv
json
libxml
mbstring
mcrypt
mysql
openssl
pcre
PDO
pdo_sqlite
Phar
posix
Reflection
session
SimpleXML
soap
sockets
SPL
sqlite3
standard
tokenizer
xml
xmlreader
xmlwriter
zlib

[Zend Modules]

​ 下面说一个安装 PHP 的 redis 拓展模块的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cd /usr/local/src/
$ sudo wget https://github.com/phpredis/phpredis/archive/refs/tags/2.2.8.zip
$ sudo mv 2.2.8.zip phpredis-develop.zip
$ sudo unzip !$
$ cd phpredis-2.2.8
$ sudo /usr/local/php/bin/phpize #目的是生成 configure 文件
Configuring for:
PHP Api Version: 20131106
Zend Module Api No: 20131226
Zend Extension Api No: 220131226

$ sudo ./configure --with-php-config=/usr/local/php/bin/php-config
$ sudo make
$ sudo make install
Installing shared extensions: /usr/local/php/lib/php/extensions/no-debug-zts-20131226/ #make install的时候会把编译好的 redis.so 放到这个目录下面,这个目录也是拓展模块存放目录

这里遇到问题:

1
2
3
$ sudo unzip !$
unzip phpredis-develop.zip
-bash: unzip: 未找到命令

解决方法:sudo yum install -y unzip,安装完后再运行一遍命令即可

1
2
3
4
5
6
7
8
9
$ sudo make
...
In file included from /usr/local/src/phpredis-develop/php_redis.h:20:0,
from /usr/local/src/phpredis-develop/redis.c:25:
/usr/local/src/phpredis-develop/common.h:13:28: 致命错误:zend_smart_str.h:没有那个文件或目录
#include <zend_smart_str.h>
^
编译中断。
make: *** [redis.lo] 错误 1

这里是因为一开始在 https://codeload.github.com/phpredis/phpredis/zip/develop 下载的为最新版,已不支持 PHP,现已将网址改成 https://github.com/phpredis/phpredis/archive/refs/tags/2.2.8.zip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看拓展模块存放目录,我们可以在 php.ini 中自定义该路径
$ sudo /usr/local/php/bin/php -i | grep extension_dir
extension_dir => /usr/local/php/lib/php/extensions/no-debug-zts-20131226 => /usr/local/php/lib/php/extensions/no-debug-zts-20131226
sqlite3.extension_dir => no value => no value

$ ls /usr/local/php/lib/php/extensions/no-debug-zts-20131226/
opcache.so redis.so #可以看到 redis.ro

$ sudo vi /usr/local/php/etc/php.ini
# 增加一行配置(可以放到文件最后一行)
extension = redis.so

# 查看是否加载了 redis 模块
$ sudo /usr/local/php/bin/php -m | grep redis
redis

4.4

​ 步骤虽然多,但并不复杂
​ 以后遇到增加模块的需求,都可以按照此方案安装
​ 另外,要想在 PHP 网站使用 redis 模块,还需要重启一下 httpd 服务

1
$ sudo /usr/local/apache2.4/bin/httpd -k restart

LAMP 学习文档
https://moka.anitsuri.top/2024/08/12/LAMP/
作者
アニつり
发布于
2024年8月12日
许可协议