本文详细叙述从服务器配置,到安装博客程序的全过程。博客程序以Movable Type(以下简称MT)为例。如果您安装WordPress、Typecho等,方法与此相同。

为什么选择安装MT?因为它曾经和WordPress一样较为出名,而且我没折腾过。

MT的主要特点是:不开源、不免费、动态博客纯静态化。开发者为了在开源市场占有一席之地,曾发布过开源版本,最新的开源版本为2015年更新的5.2.13。后来,MT的商业化版本针对单用户、单网站实行免费策略,如需在程序内建立多个用户,或建立多个网站,则需要付费。

MT动态博客纯静态化,是一个特色。在MT后台修改模板、修改设置、发布日志等,如需让修改生效,需要对网站进行手动编译(Build)。编译后,会在指定的目录(默认为根目录)生成html、css等文件。访客访问时,读取的是这些html文件,这就是动态博客纯静态化。当访客提交评论后,MT会自动编译生成新的html文件,并将评论写入数据库。著名的独立博客之一“阮一峰的网络日志”便是使用MT搭建的。

写作本文时,我使用的是MT 7.0.1,已提前打好此版本的修复补丁。

准备安装

我使用的VPS操作系统为Debian 9.5,是当前Debian Stable的最新版本。我选定这个操作系统,原因有二:第一,我的电脑使用的操作系统便是Debian 9.5,我对这个系统较为熟悉,软件仓库的软件版本,我在物理机的新立得软件包管理器便可查询(我知道还有其他办法可以查询,但我不会操作);第二,这个系统的软件仓库包含的软件,虽然版本大多不是最新的,但却是经过足够的测试,保证是稳定的,不容易出现问题。

为保证稳定性,并节省安装时间,我采用的安装方法是逐个安装网站所需环境。这样安装的优点是,通过软件仓库直接安装软件包,可节省大量的时间,且运行足够稳定。缺点是无法切换版本。如果您不想逐个安装,可以使用LAMP或LNMP一键安装包,再安装一键安装包中不包含的软件包。注意:宝塔面板为用户安装的各种网站环境与此不兼容。

安装Webmin

Webmin的特点是,可以使用操作系统的系统用户(包括普通用户和root)的用户名和密码登录,在面板内进行文件管理、数据库管理、邮件服务器管理、模块管理等众多操作,简化了大量的操作。

本文写作时,Webmin最新版本为1.890。如需查看最新版本或历史版本信息,可进入https://sourceforge.net/projects/webadmin/files/webmin/ 查看。

apt install perl libnet-ssleay-perl openssl libauthen-pam-perl libpam-runtime libio-pty-perl apt-show-versions python
wget http://prdownloads.sourceforge.net/webadmin/webmin_1.890_all.deb 
dpkg --install webmin_1.890_all.deb

部分命令,可使用TAB补全,具体不予赘述。

安装完成后,可使用

https://ip:10000

访问。务必手动填写https://,否则会无法访问。

访问时,会提示你证书错误。忽略此错误,继续访问即可。Firefox用户可添加安全例外。

如果手动填写https://也无法访问,请确认VPS对应的安全组是否打开了10000端口。

如果面板访问速度较慢,可在侧边栏找到Webmin Configuration,将主题换成Gray Framed theme。为保证使用方便,可将语言改为简体中文。

软件版本

为保证运行MT,需要安装nginx、mysql、php。

这几个软件的版本如图所示。

软件版本

图:mysql(mariadb)、nginx、php版本(2018年10月31日查询)

数据库可选择安装mysql或mariadb。安装mysql-server,实际上安装的是mariadb,版本相同。如果安装mariadb 10.1.26,建议VPS的RAM不低于2G。

如需安装更高的版本,可以使用Ubuntu系统(建议);也可以使用Debian的testing或sid分支(命令行下面操作繁琐,因此不建议)。

为何不用Apache

实际上apache可以支撑MT的运行,我在apache的虚拟主机上面试验过。

但我为何不使用apache呢?因为我参考的资料使用的nginx。我使用过apache环境的主机,但我用的是虚拟主机,不是vps。

所以,如果我在vps上使用apache,有些地方我不会配置,2333。

如何对vps上的apache进行配置,是我的下一个学习目标。

安装Nginx

apt install nginx

安装结束后,使用ip访问,即可弹出nginx默认页。

安装Mysql(Mariadb)

以安装5.5.9为例。

apt install mysql-server

此时,在Webmin左侧边栏最下方点击“刷新模块”,刷新后可在侧边栏“服务器”一栏看到Mysql的有关设置。在这里可新建、管理、删除数据库,可操作数据库的授权用户等。这至关重要,安装博客程序时,需要这些信息。

安装PHP

从上面的图中可以知道,Debian 9.5稳定版的软件仓库中,php版本为7.0。

apt install -y php7.0 php7.0-fpm php7.0-cli php7.0-common php7.0-mbstring php7.0-gd php7.0-intl php7.0-xml php7.0-mysql php7.0-zip php7.0-json php7.0-curl

如需查看已安装php版本,可执行

php -v

查看php7.0-fpm是否运行

service php7.0-fpm status

程序配置

下面进入繁琐、闹心的配置过程。

假设您的域名是domainexample.com,VPS默认普通用户是username,ip地址为23.231.232.233。此处仅作为举例,实际安装过程中,请务必使用真实数据。

DNS

首先,请在DNS服务商那里,将你的域名指向VPS的IP地址。DNS全球生效的时间大约在10分钟~72小时,取决于DNS服务商的服务质量及你的网络情况。如果着急,可修改本机hosts文件,将域名直接指向该IP地址。具体修改方法,针对不同操作系统而有所区别。

假如您的物理机使用的Linux系统,安装的是Mate桌面,可以使用pluma打开hosts文件。如果您使用的Gnome或Unity桌面,可以使用gedit。如果您使用的KDE桌面,可使用Kedit(好像是这个名字,很久没用KDE了有点忘了)。以pluma文本编辑器为例,切换到root用户,执行

pluma /etc/hosts

加入

23.231.232.233 domainexample.com

保存并关闭,重启系统。

Nginx

VPS由于不具有图形化界面,为保证操作简便,这里使用nano作为命令行界面的文本编辑器,不建议使用vi或vim。但如果您偏爱vi或vim,请嘴下留情,谢谢。

nginx的配置相当麻烦。

在普通用户主目录新建文件夹,文件夹名字为“domainexample.com”,便于识别。后面网站文件全部上传到这里。可在Webmin面板的File Manager里操作。

新建主机配置文件:

nano /etc/nginx/conf.d/domainexample.conf

写入如下内容:

log_format   main '$remote_addr - $remote_user [$time_local]  $status '
    '"$request" $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

server {
    listen       80;
    server_name  domainexample.com www.domainexample.com;
    access_log  /var/log/nginx/host.access.log  main;

    root   /home/username/domainexample.com;
    index  index.php index.html index.htm;
    
    location / {
        try_files $uri $uri/ = 404;
    }

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    location ~ /\.ht {
        deny  all;
    }
}

请根据你实际的域名、目录位置和php版本,修改上述代码。

用下面命令查看是否配置正确。

nginx -t

如果出现下列内容,配置就是正确的。

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

重启nginx:

service nginx restart

检测是否配置成功,方法如下。

在/home/username/domainexample.com下新建文件info.php,写入下列内容:

<?php
phpinfo();

打开桌面浏览器,访问domainexample.com/info.php,如果出现php信息列表页面,则访问正常。

如果出现错误,可查看如下配置文件:

  • /var/log/nginx/host.access.log
  • /var/log/nginx/error.log

根据配置文件内提示的错误内容进行排查。

SSL证书

使用Neilpang的脚本安装。此处使用文件验证,申请的证书为DV型。如需申请免费的通配符证书,或使用DNS验证,请参考官方文档进行操作。

curl https://get.acme.sh | sh

新建目录。

mkdir -p /home/username/domainexample.com/.well-known/acme-challenge

将下列代码,添加到nginx个人配置文件最后一个“}”前面。

location /.well-known/acme-challenge/ {
    alias /home/username/domainexample.com/.well-known/acme-challenge;
}

重启nginx服务,命令在本文前面有。

此时需要重启一下VPS,使acme.sh命令生效,否则执行时会出现命令不存在的情况。可以在主机商网站后台管理页面重启,也可以在VPS内执行

reboot

重启。重启时,SSH会被断开连接,等待1~3分钟后重新连接即可。

申请证书。最后一个参数是采用椭圆函数加密,具体啥叫椭圆函数我也不知道,反正申请下来是ECC的证书,不是传统的RSA。

acme.sh --issue -d domainexample.com -d www.domainexample.com -w /home/username/domainexample.com --keylength ec-384

签发后的证书在~/.acme.sh/domainexample.com_ecc/ 下面。准备把证书安装到 /etc/nginx/ssl/domainexample.com。

新建目录,授予权限:

mkdir -p /etc/nginx/ssl/domainexample.com
chmod 777 /etc/nginx/ssl/domainexample.com

安装证书:

acme.sh --install-cert -d domainexample.com \
--cert-file /etc/nginx/ssl/domainexample.com/cert \
--key-file /etc/nginx/ssl/domainexample.com/key \
--fullchain-file /etc/nginx/ssl/domainexample.com/fullchain \
--reloadcmd "systemctl reload nginx.service" --ecc

生成一个 4096 位的 dhparam 文件。如果没有这个文件,安全性会降低。

这个过程时间很长,需要等待。为了保证SSH操作正常稳定,建议使用screen执行。

screen -S name
cd /etc/nginx/ssl
openssl dhparam -out dhparam.pem 4096

执行过程中,先按住键盘上的Ctrl,再依次按a d,即可退出screen,此时不影响上述命令执行,即便关闭SSH连接也不影响。你可以过一会儿重新连接SSH,然后执行

screen -r name

即可回到这个操作过程页面。

确认nginx和openssl版本:

root@ip:~# nginx -v
nginx version: nginx/1.10.3
root@ip:~# openssl version
OpenSSL 1.1.0f  25 May 2017

Mozilla SSL Configuration Generator生成配置文件,需要用到版本信息。推荐Modern方式,不要开启HSTS,否则可能出现nginx服务启动失败。

此时,根据需要编辑nginx配置文件。

log_format   main '$remote_addr - $remote_user [$time_local]  $status '
    '"$request" $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name  domainexample.com www.domainexample.com;
    access_log  /var/log/nginx/host.access.log  main;

    # certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
    ssl_certificate /etc/nginx/ssl/domainexample.com/fullchain;
    ssl_certificate_key /etc/nginx/ssl/domainexample.com/key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;

    # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;

    # intermediate configuration. tweak to your needs.
    ssl_protocols TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
    ssl_prefer_server_ciphers on;

    # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
    add_header Strict-Transport-Security max-age=15768000;

    # OCSP Stapling ---
    # fetch OCSP records from URL in ssl_certificate and cache them
    ssl_stapling on;
    ssl_stapling_verify on;

    ## verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /etc/nginx/ssl/domainexample.com/fullchain;

    resolver 8.8.8.8;

    root   /home/username/domainexample.com;
    index  index.php index.html index.htm;
    
    location / {
        try_files $uri $uri/ = 404;
    }

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    location ~ /\.ht {
        deny  all;
    }
    location /.well-known/acme-challenge/ {
        alias /home/username/domainexample.com/.well-known/acme-challenge/;
    }
}

配置好后,去SSL Labs检测,结果是A+。

安装Perl-Fastcgi

如果使用WordPress、Typecho且不使用perl模块,这个步骤可以省略。如果使用MT,则需要进行这个步骤。

apt install libfcgi-perl spawn-fcgi fcgiwrap

然后执行

service fcgiwrap start

将如下内容写入nginx个人配置文件,注意写在最后一个“}”前面:

location ~ \.pl|cgi$ {
   fastcgi_pass  unix:/var/run/fcgiwrap.socket;
   fastcgi_index index.pl;
   fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
   include fastcgi_params;
   }

保存,重启nginx服务。

检测是否配置成功,方法如下。

在/home/username/domainexample.com下,新建index.pl,写入如下内容:

#!/usr/bin/perl

print "Content-type:text/html\n\n";
print <<EndOfHTML;
<html><head><title>Perl Environment Variables</title></head>
<body>
<h1>Perl Environment Variables</h1>
EndOfHTML

foreach $key (sort(keys %ENV)) {
    print "$key = $ENV{$key}<br>\n";
}

print "</body></html>";

赋予权限,否则会出现403错误:

chmod a+x index.pl

在浏览器中访问该文件。如果可正常访问,则配置成功。

其他配置

(1)301跳转:下面的配置文件中,将www跳转到@,http强制跳转到https。

(2)调整文件上传大小:调整文件上传大小为100M(如不调整,默认2M)。

最终得到的总体nginx配置文件如下:

log_format   main '$remote_addr - $remote_user [$time_local]  $status '
    '"$request" $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

server {
        listen 80;
        server_name domainexample.com;
        return 301 https://$server_name$request_uri;
}

server {
        listen *:80;
        listen *:443 ssl; 
        listen [::]:80;
        listen [::]:443 ssl; 
        server_name www.domainexample.com;

        ssl_certificate /etc/nginx/ssl/domainexample.com/fullchain;
        ssl_certificate_key /etc/nginx/ssl/domainexample.com/key; 
        return 301 https://domainexample.com$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name  domainexample.com;
    access_log  /var/log/nginx/host.access.log  main;
    client_max_body_size 100M;

    # certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
    ssl_certificate /etc/nginx/ssl/domainexample.com/fullchain;
    ssl_certificate_key /etc/nginx/ssl/domainexample.com/key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;

    # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;

    # intermediate configuration. tweak to your needs.
    ssl_protocols TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
    ssl_prefer_server_ciphers on;

    # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
    add_header Strict-Transport-Security max-age=15768000;

    # OCSP Stapling ---
    # fetch OCSP records from URL in ssl_certificate and cache them
    ssl_stapling on;
    ssl_stapling_verify on;

    ## verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /etc/nginx/ssl/domainexample.com/fullchain;

    resolver 8.8.8.8;

    root   /home/username/domainexample.com;
    index  index.php index.html index.htm;
    
    location / {
        try_files $uri $uri/ = 404;
    }

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    location ~ /\.ht {
        deny  all;
    }
    location /.well-known/acme-challenge/ {
        alias /home/username/domainexample.com/.well-known/acme-challenge/;
    }

    location ~ \.pl|cgi$ {
        fastcgi_pass  unix:/var/run/fcgiwrap.socket;
        fastcgi_index index.pl;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

(3)安装imagemagick模块,保证MT后台不报错:

apt install imagemagick libimage-magick-perl

(4)安装sendmail:

apt install sendmail

配置可在Webmin中操作。

(5)调整perl编码识别,以防出现错误:

在Webmin面板,以root登录,在File Manager中,修改/usr/lib/x86_64-linux-gnu/perl/5.24/Encode.pm,在第201行else {后面,添加

Encode::_utf8_off($octets);

点击最下方Save and close(保存并关闭)即可。

好了,配置完成。

接下来,上传文件、设置数据库,以至于后期的备份数据库、修改文件等,均可在Webmin面板操作。

清除缓存软件包

从Debian和Ubuntu的软件仓库下载的软件包并不会默认删除。为释放被占用的空间,可执行

apt clean

即可删除软件仓库下载的软件包。

安装Movable Type

安装步骤可参见官方文档,但官方文档说的太复杂了。此处则简略的一下。

将mt-static目录独立出来,剩余的文件放在同一个目录,命名为cgi-bin。将cgi-bin和mt-static上传到网站根目录。

访问domainexample.com/cgi-bin/mt/mt.cgi,进入安装过程。

安装过程中,有一步是发送邮件设置。如果使用sendmail,需要输入路径。本文前面安装的sendmail所在路径为:

/usr/lib/sendmail

最后写入数据库这一步需要注意,如果在浏览器端,可能长时间没有反应。解决方法:使用手机浏览器访问,即可正常进行最后写入数据库这一步。这可能是bug。

部分文件夹需要将权限设置为777,待安装结束后,进入后台,按警告内容提示操作。

示例站点:https://iyfy.top 。注:如果示例站点不能访问,可能的原因有:

  1. VPS宕机、过期或被删除
  2. 后期折腾时操作出现错误,或网站运行中出现我无法解决的错误
  3. 我主动关停了这个站点
  4. 我被迫关停了这个站点
  5. 网络原因或中国大陆电信运营商原因
  6. ip、域名或DNS服务器被墙
  7. 域名被注册局(中国-江苏邦宁科技有限公司)或注册商(阿里云)停止解析
  8. 域名过期

最后,感谢某大佬提供了为期一年的VPS供我折腾。

善意的提醒一下:VPS属于无服务模式,除非付费寻求技术服务,一切使用和维护工作均只能由用户独立完成。当出现故障时,需用户自行保证数据安全。如果没有足够的VPS管理经验,却也想自己折腾一下,请不要将重要数据放到VPS上。