Amazon EC2 网站搭建笔记:Apache 2.4 的配置与优化
摘要 TL;DR
Amazon Web Services(AWS)的各种服务虽然通过 AWS 管理控制台可以方便地进行管理,但是对于最初上手的人而言还是有很大的难度的,所以这一系列笔记就是来记录与此相关的一些内容。由于内容比较多,所以分阶段来写。
最初博客是搭建在 Linode 所提供的 VPS 上,最近尝试迁移到了 Amazon EC2。尽管 Linode 在价格方面已经非常有利,但是考虑到 Amazon 所提供的例如 Amazon S3、Amazon Route 53、Amazon Cloud Front 等等这一系列的配套服务,以及比较合理的计费模型(当然是有 AWS 免费套餐1的),还是从 Linode 迁移了过来。
首次登录到 Amazon EC2 服务器
当我们在 EC2 Management Console 将已创建的实例启动后,即可通过 SSH 登录到 Amazon EC2 服务器了。以下以使用 macOS 或者 Linux 为例,如果是 Windows 用户可以使用 PuTTY 作为你的 SSH 客户端。
在 terminal 中执行以下命令,请注意这里作为示例的 EC2 的公有 DNS 是 ec2-123-456-78-90.ap-northeast-1.compute.amazonaws.com,请将其替换为你自己的域名或者 IP 地址。
$ ssh -i /path/to/ec2/cert.pem ubuntu@123.456.78.90
由于这是第一次登录到该服务器,SSH 客户端尚未存储服务器密钥的指纹,所以会在 terminal 中看到类似下方的警告内容。输入 yes
并按下回车以继续连接。
The authenticity of host 'ec2-123-456-78-90.ap-northeast-1.compute.amazonaws.com (123.456.78.90)' can't be established.
RSA key fingerprint is 11:eb:57:f3:a5:c3:e0:77:47:c4:15:3a:3c:df:6c:d2.
Are you sure you want to continue connecting (yes/no)?
这时 terminal 会返回类似下方的结果:
Warning: Permanently added 'ec2-123-456-78-90.ap-northeast-1.compute.amazonaws.com' (RSA) to the list of known hosts.
Amazon EC2 使用证书进行登录验证,并不需要再输入密码。稍后就会看到已经成功使用 ubuntu 用户登录到了此前启动的 EC2 实例。
完成基础配置
安装软件更新
在登录到 EC2 后首先需要做的就是对软件进行更新。执行以下命令并在需要确认的时候输入 Y
并按下回车。
$ sudo apt-get update && sudo apt-get upgrade
设定 Hostname
现在需要设置 hostname 和 FQDN(fully qualified domain name,完全限定域名)2。注意将 example_hostname
替换为自己所要设置的 hostname。
$ sudo echo "example_hostname" > /etc/hostname
$ sudo hostname -F /etc/hostname
更新 /etc/hosts
更新 /etc/hosts
文件,以创建 IP 地址与域名之间的关联,比 DNS 有更高的优先级。hosts
文件要以 127.0.0.1 localhost.localdomain localhost
为开头,并接下来追加 EC2 的公开 IP 地址、hostname 和 FQDN 信息。
$ sudo vim /etc/hosts
如果你不了解如何使用 vim 进行编辑,也可以选择 nano。上述命令可以通过
sudo nano /etc/hosts
完成。
这里的示例是使用 vim 进行编辑,你也可以使用其他的编辑器来处理文件。
在修改完成后,记住在命令模式(command mode)中使用
:wq
保存并退出 vim,否则你的修改无法被保存。
将其中的 123.456.78.90
替换为你的 IP 地址,将 example_hostname
替换为此前输入在 /etc/hostname
文件中的 hostname
,将 example.com
替换为你的域名。
127.0.0.1 localhost.localdomain localhost
123.456.78.90 example_hostname.example.com example_hostname
需要注意的是,你应当该域名的 DNS 中添加一条解析到该 IP 地址的 A 记录。即将 example.com
解析到 123.456.78.90
。
调整时区
默认地,EC2 所使用的是 UTC 时间,即格林尼治(平均)时间。执行以下命令后,选择 Asia
,然后再选择 Shanghai
。
$ dpkg-reconfigure tzdata
使用 date
以验证成功地修改了时区。
$ date
安装并配置 Apache 2.4
安装 Apache
接下来就可以安装 Apache 2.4 并且在此基础上根据需要做相关的配置了。首先安装 Apache 2.4(apache2
)及其文档(apache2-doc
)和实用工具(apache2-utils
)。
$ sudo apt-get install apache2 apache2-doc apache2-utils
在此过程中,如果遇到需要确认安装的提示,则输入 Y
并按下回车。
配置 apache2.conf
编辑 Apache 配置文件以关闭 KeepAlive
设置。在编辑之前建议备份原始的 apache2.conf
文件,以便在出现问题的时候可以恢复。
$ sudo cp /etc/apache2/apache2.conf /etc/apache2/apache2.backup.conf
$ sudo vim /etc/apache2/apache2.conf
在打开的 /etc/apache2/apache2.conf 中查找到 KeepAlive On
一行并将其修改为如下。
KeepAlive Off
你可以在 vim 中使用
/
来做查找操作。例如要在文档中查找上述内容,则可以在命令模式(command mode)中输入/KeepAlive On
。
配置 MPM(多处理模块)
Apache 提供了可以选择的多处理模块(MPM,Multi-Processing Module)3,用来绑定到网络端口上,接受请求, 以及调度子进程处理请求。默认的 MPM 使用 event 模块,我们将其更换为 prefork 模块。
Apache 2.4 的预置可用模块被放置于 /etc/apache2/mods-available/
路径下,编辑 prefork 模块的配置文件 mpm_prefork.conf
。
$ sudo vim /etc/apache2/mods-available/mpm_prefork.conf
按照如下内容对配置文件作修改。
<IfModule mpm_prefork_module>
StartServers 4
MinSpareServers 20
MaxSpareServers 40
MaxRequestWorkers 200
MaxConnectionsPerChild 4500
</IfModule>
该配置文件是针对 EC2 的免费套餐所提供通用型 t2.micro 类型实例(1 个 vCPU、1 GiB 内存)进行优化的。
因为在 Ubuntu 14.04 中默认启用的是 event 模块。在此将其禁用,然后启用刚刚配置好的 prefork 模块。
$ sudo a2dismod mpm_event
$ sudo a2enmod mpm_prefork
配置虚拟主机(Virtual Host)并设置用户与文件权限
Apache 支持基于名称的虚拟主机,这就使得我们可以在仅有一个 IP 地址的服务器上使用多个域名来运行服务。
使用如下命令禁用默认的虚拟主机:
$ sudo a2dissite 000-default.conf
然后我们创建网站文件存储路径。将 example.com
替换为你自己的域名,并确保该域名已被解析到该服务器的 IP 地址。
$ cd /var/www/html/
$ sudo rm index.html
$ sudo mkdir example.com
$ sudo mkdir -p example.com/public_html
$ sudo mkdir -p example.com/log
$ sudo mkdir -p example.com/backups
默认地,Apache 使用 /var/www/html
路径作为默认路径,即该路径下的文件可以被访问者通过 Apache 服务访问到。我们有可能还会将其他域名解析到该服务器,所以建议在该路径下为不同的域名创建一个单独的文件夹,以便通过不同域名可以访问到不同的内容。这里在 /var/www/html
路径下创建了一个名为 example.com
的子路径,以存储与 example.com 域名相关的一系列文件。
在其中,public_html
文件夹用以存储可被访问者访问到的文件,即作为该域名的根路径。log
存储 Apache 所产生的关于该域名的日志文件。backups
可以存储相关的备份文件(如果有)。
该路径是属于 root 所拥有的,我们登录所使用的用户 ubuntu 是没有权限进行修改的。要允许用户 ubuntu 操作此目录中的文件,需要修改其所有权和权限。在这里我们将赋予 www-data
组4 /var/www
目录的所有权并为该组添加写入权限。随后,该组的所有成员都将能够为 Web 服务器添加、删除和修改文件。然后将用户 ubuntu 添加到 www-data 组。这样,用户 ubuntu 就可以成功地访问这些路径和其中的文件了。
$ sudo usermod -a -G www-data ubuntu
我们先使用 exit
命令退出,再重新登录,这样才能接受新组。
$ exit
重新连接到实例,然后运行以下命令,以验证用户 ubuntu 是否为 www-data 组的成员。
$ groups
ubuntu adm dialout cdrom floppy sudo audio dip www-data video plugdev netdev
将 /var/ 及其内容的组所有权更改到 www-data 组。
$ sudo chown -R root:www-data /var/www/
更改 /var/www 及其子目录的目录权限,以添加组写入权限和设置未来子目录上的组 ID。
$ sudo chmod 2775 /var/www/
$ sudo find /var/www/ -type d -exec chmod 2775 {} \;
递归更改 /var/www 及其子目录的文件权限,以添加组写入权限。
$ sudo find /var/www/ -type f -exec chmod 0664 {} \;
现在,ubuntu(以及 www-data 组的任何未来成员)可以在 Apache 根目录中添加、删除和编辑文件了。
接着,为域名 example.com 创建配置文件。
$ sudo vim /etc/apache2/sites-available/cherysunzhang.com.conf
其内容如下,注意将 example.com
替换为自己的域名。并且酌情对文件内容进行修改。
# /etc/apache2/sites-available/example.com.conf
# domain: example.com
# public: /var/www/html/example.com/public_html/
<VirtualHost *:80>
# Admin email, Server Name (domain name), and any aliases
ServerAdmin cherysun.zhang@gmail.com
ServerName example.com
ServerAlias www.example.com
# Index file and Document Root (where the public files are located)
DirectoryIndex index.html index.php
DocumentRoot /var/www/html/example.com/public_html
# Log file locations
LogLevel warn
ErrorLog /var/www/html/example.com/log/error.log
CustomLog /var/www/html/example.com/log/access.log combined
<IfModule rewrite_module>
RewriteEngine on
RewriteCond %{HTTPS} !=on
RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R=301,L]
</IfModule>
</VirtualHost>
其中
<IfModule rewrite_module>…</IfModule>
代码片段是将所有的 HTTP 访问重定向到 HTTPS,以强制所有内容均使用 HTTPS。即将http://…
重定向到https://…
。
执行以下命令使我们刚刚创建的配置文件生效。Apache 会为 /etc/apache2/sites-available/cherysunzhang.com.conf
文件在 /etc/apache2/sites-enabled/
路径下创建一个符号链接。
$ sudo a2ensite cherysunzhang.com.conf
启用 HTTPS
前提是你已经为你的域名向 CA 申请了一张证书。这时我们需要由 CA 颁发的证书文件,以及私钥文件和中间证书文件。在这个示例中,我们将证书文件存储在 /etc/ssl/localcerts
路径中,因此我们需要新建该文件夹。你也可以选择存放在其他安全的路径中。
$ sudo mkdir -p /etc/ssl/localcerts
将本地的证书文件(example.com.crt
)、私钥文件(privatekey.pem
)和中间证书文件(intermediate.crt
)上传到服务器的该路径中。
$ scp -i /path/to/ec2/cert.pem example.com.crt privatekey.pem intermediate.crt ubuntu@1.2.3.4:/etc/ssl/localcerts/
注意不要混淆 Amazon 提供的用于 SSH 的身份验证证书
/path/to/ec2/cert.pem
和 CA 颁发的证书文件。
将证书上传完成后,需要确保证书和私钥具有高度限制的权限(所有者根权限、组根权限、仅面向所有者的读取/写入权限),以确保证书和私钥的安全。
$ cd /etc/ssl/localcerts/
$ sudo chown root:root example.com.crt
$ sudo chmod 600 example.com.crt
$ sudo chown root:root privatekey.pem
$ sudo chmod 600 privatekey.pem
$ sudo chown root:root intermediate.crt
$ sudo chmod 644 intermediate.crt
中间证书文件的权限并不严格(所有者根权限、组根权限、所有者可写权限、任何人可读权限)。
上述命令应生成类似以下的结果,可以通过执行 ls -al
查看:
-rw------- 1 root root 2159 Aug 23 18:26 example.com.crt
-rw-r--r-- 1 root root 2106 Aug 23 18:26 intermediate.crt
-rw------- 1 root root 1675 Aug 23 18:26 privatekey.pem
完成以上操作后,就可以为使用 HTTPS 的域名访问创建相关的配置文件了。
$ sudo vim /etc/apache2/sites-available/example.com-ssl.conf
与 cherysunzhang.com.conf
文件所不同的是,cherysunzhang.com-ssl.conf
文件所配置的端口并非 80 而是 443。
# /etc/apache2/sites-available/example.com-ssl.conf
# domain: example.com
# public: /var/www/html/example.com/public_html/
<VirtualHost *:443>
# Admin email, Server Name (domain name), and any aliases
ServerAdmin webmaster@example.com
ServerName example.com
ServerAlias www.example.com
# Index file and Document Root (where the public files are located)
DirectoryIndex index.html index.php
DocumentRoot /var/www/html/example.com/public_html/
# Log file locations
LogLevel warn
ErrorLog /var/www/html/example.com/log/error.log
CustomLog /var/www/html/example.com/log/access.log combined
<IfModule ssl_module>
SSLEngine on
SSLCertificateFile /etc/ssl/localcerts/example.com.crt
SSLCertificateKeyFile /etc/ssl/localcerts/privatekey.pem
SSLCertificateChainFile /etc/ssl/localcerts/intermediate.crt
SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCipherSuite ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5
SSLCompression off
</IfModule>
<IfModule rewrite_module>
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www.example.com$ [NC]
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]
</IfModule>
<IfModule headers_module>
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</IfModule>
<IfModule deflate_module>
<IfModule filter_module>
AddOutputFilterByType DEFLATE text/html text/plain text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/xml application/rss+xml
AddOutputFilterByType DEFLATE image/jpeg image/png image/svg+xml image/x-icon
</IfModule>
</IfModule>
<IfModule expires_module>
ExpiresActive On
ExpiresDefault "access plus 3 months"
ExpiresByType text/html "modification plus 2 weeks"
ExpiresByType text/css "modification plus 2 weeks"
ExpiresByType text/plain "modification plus 2 weeks"
ExpiresByType application/javascript "modification plus 2 weeks"
ExpiresByType application/xml "access plus 1 month"
ExpiresByType application/rss+xml "access plus 1 week"
ExpiresByType application/x-font-woff "access plus 6 months"
ExpiresByType application/x-font-ttf "access plus 6 months"
ExpiresByType application/x-font-otf "access plus 6 months"
ExpiresByType application/vnd.ms-fontobject "access plus 6 months"
ExpiresByType image/jpeg "access plus 2 month"
ExpiresByType image/png "access plus 2 month"
ExpiresByType image/svg+xml "access plus 2 month"
ExpiresByType image/x-icon "access plus 6 month"
</IfModule>
<IfModule pagespeed_module>
ModPagespeedRewriteLevel CoreFilters
ModPagespeedEnableFilters responsive_images,combine_heads,outline_css,outline_javascript,move_css_above_scripts,make_google_analytics_async,make_show_ads_async,inline_google_font_css,local_storage_cache,inline_preview_images,resize_mobile_images,remove_comments,collapse_whitespace,trim_urls,insert_dns_prefetch
ModPagespeedMapOriginDomain "http://localhost" "https://example.com"
ModPagespeedLoadFromFile "https://example.com" "/var/www/html/example.com/public_html/"
ModPagespeedFetchHttps enable
</IfModule>
</VirtualHost>
其中
<IfModule rewrite_module>…</IfModule>
代码片段是将 HTTPS 访问中的所有 www 访问重定向到非 www(non-www)。即将 https://www.example.com 重定向到 https://example.com。
然后即可使用该配置文件生效了。
$ sudo a2ensite cherysunzhang.com-ssl.conf
可以注意到,该配置文件中包含有几个 <IfModule>…</IfModule>
代码片段,这些是使用相关的 Apache 模块所进行的优化。因而需要启用这些模块以使 Apache 能够正常工作。
$ sudo a2enmod ssl
$ sudo a2enmod rewrite
$ sudo a2enmod headers
$ sudo a2enmod deflate
$ sudo a2enmod expires
现在,只需要重启 Apache 服务即可完成所有的配置工作。
$ sudo service apache2 restart
使用 PageSpeed Module 进行优化
额外地,我们可以使用 PageSpeed 模块对网站进行优化。PageSpeed Module 是由 Google 开发的用于优化网站性能的模块。这里我们不对其作详细的介绍,如果需要了解如何使用并合理地进行配置,请访问:https://developers.google.com/speed/pagespeed/module/。
$ cd ~
$ wget https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-stable_current_amd64.deb
$ sudo dpkg -i mod-pagespeed-*.deb
$ sudo apt-get -f install
$ sudo service apache2 restart
可以注意到,在 /etc/apache2/sites-available/example.com-ssl.conf
配置文件中已经包含有 <IfModule pagespeed_module>…</IfModule>
的代码片段。如有需要,可以再对其进行修改。
上传网站文件到服务器
最后,将本地创建好的的网站文件上传到服务器的 /var/www/html/example.com/public_html
路径下,然后访问域名即可看到一切可以正常工作了。
-
AWS 免费套餐服务/产品包括自 AWS 注册之日起 12 个月内可供免费使用的服务,以及在 AWS 免费套餐的 12 个月期限到期后不自动过期的其他服务/产品:https://aws.amazon.com/cn/free/ ↩
-
Wikipedia 关于 Fully qualified domain name 的解释:https://en.wikipedia.org/wiki/Fully_qualified_domain_name ↩
-
Apache HTTP 服务器版本 2.4 对于 Multi-Processing Module 的说明:http://httpd.apache.org/docs/2.4/en/mpm.html ↩
-
Apache 服务默认是由
www-data
用户运行的,该用户是在通过 Debian 的 APT(高级软件包工具)安装 Apache 时所产生的。一般地,我们习惯将搭载 web 服务的文件和文件夹设置为归属于www-data
用户组。 ↩