开启了新的博客https://kCAT.io,欢迎大家来访。

搭建这个博客也是一时兴起,想起来就干,结果一共花了不到3、4个小时的时间就完全搞定了,把基本的过程记录下来以便后续参考使用(比如重做系统时)。

博客选型

过往使用过的博客系统主要是Wordpress,Wordpress优势是功能全面、受众广泛、社区活跃,不足就是整体有一些繁重不够轻量。之前就了解到国内有一款轻量级的开源博客系统是Typecho,和Wordpress一样是PHP语言开发,使用MySQL数据库,同样的支持皮肤和插件。Typecho整体特别的简洁,代码一共不到500kb,对于比较专注在内容上喜欢简单的用户来说是个很不错的选择。选好博客产品后接下来就看如何部署了。

第一步我们需要一台服务器

硬件及操作系统

云厂商

现如今的云服务都很成熟,需要购买服务器已是非常的便利。无论是选择阿里云、腾讯云还是华为云,头部几家厂商的购买流程、价格、服务都大同小异,大家可以按各自的喜好选择。我这里选择的是腾讯云(个人更喜欢小马哥的形式风格)。

硬件

Typecho需要的硬件环境要求并不高,出于成本的因素我购买的是最小型的实例(1核1G内存),另外选购了一块50G的数据盘(额外选购数据磁盘主要是担心系统出问题时候数据能不受影响),网络方面选择了5M带宽按流量付费的方式(个人博客访问量很小,按流量付费相对按带宽付费划算)。

操作系统

操作系统我选择了64位Centos7.9版本,这里注意Centos6及以下版本不支持Docker官方的自助安装脚本,只能通过rpm或编译的方式安装,操作门槛较高。起初我也是因为更熟悉Centos6而选了旧版本操作系统,在后续安装Docker的时候遇到困难才回头重装为Centos7.9,好在重装系统非常的便捷,几乎2、3分钟就解决了。

装好系统之后修改一下几个安全项:

  • 新建一个普通用户,并设置密码,日常咱们只需要使用普通用户做登录
# 添加一个普通用户
useradd -g users 用户名
# 设置用户密码
passwd 用户名
  • 修改ssh端口号和禁止使用root用户直接登录
# 备份/etc/ssh/sshd_config
cp /etc/ssh/sshd_config /etc/ssh/sshd_config_bak
# 编辑 /etc/ssh/sshd_config
# 找到Port 22这一行,去掉前面的#号,并修改22端口为其他端口号
# 找到PermitRootLogin yes,去掉前面的#号,并修改yes为no
vim /etc/ssh/sshd_config
# 修改好之后按输入`:wq`保存
# 重启sshd服务
systemctl restart sshd
  • 调整了ssh的端口号记得需要修改服务器的安全组,放通我们修改的端口
  • 可以顺便把即将要使用的80和443端口也一并放通

装好操作系统后需要把数据磁盘挂载到系统上才能用上数据磁盘,并且云厂商都支持磁盘的自动化快照,这样就算磁盘出现了故障也可以从快照中快速恢复,保护数据的安全。

挂载磁盘的几个操作:

  • 格式化数据磁盘
# 使用fdisk命令查看挂载的磁盘文件符,通常系统盘是/dev/vda,而数据盘是/dev/vdb
fdisk -l
# 对数据磁盘进行分区(通常只挂载一个目录的话只需要分一个区即可)
fdisk /dev/vdb
# 按交互依次输入 n, p, 1, 回车, 回车, wq

# 再次查看磁盘分区情况,可以看到新建了一个路径为/dev/vdb1的分区
fdisk -l
# 格式化新的分区
mkfs.ext4 /dev/vdb1
  • 挂载数据磁盘
# 重建挂载点(通常把数据盘挂为/data)
mkdir /data/
# 挂载分区到目标路径
mount /dev/vdb1 /data
# 查看挂载详情
df -h
# 这时应该可以看到/data目录和/dev/vdb1的映射关系

# 查看磁盘uuid,centos7 自动挂载磁盘需要磁盘的uuid
blkid

# 编辑/etc/fstab使得系统重启时能自动挂载数据盘,把以下内容添加到文件中(UUID值使用前面查询到的数据盘uuid)
UUID=c4a82bd7-bf45-43cd-81c8-7cdafa270dc4 /data ext4 defaults 0 0

# 验证挂载,执行没有报错即可
mount -a

第二步需要解决运行环境

容器环境

容器的出现极大的提高了部署环境的效率和一致性,简直是划时代的制作。首先我们要安装docker,推荐使用官方的自动脚本安装

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

# 安装好之后把docker的数据路径转移到数据磁盘
mv /var/lib/docker /data/; ln -sf /data/docker /var/lib/;

# 启动docker服务
systemctl start docker
# 查看运行中的docker容器(初次使用应该没有运行中的容器)
docker ps

# 创建一个网络,方便需要互通的容器加入同一个网络
docker network ls
docker network create -d bridge my-net

数据库

推荐使用MySQL5.6及以上版本,这里我选了MySQL作者推出的完全兼容MySQL的mariadb

# 一行代码,构建数据库环境(记得替换里面的密码r*xt)D2b31)
docker run -d \
--name mariadb \
--network my-net \
--restart=always \
-v /data/mariadb:/var/lib/mysql \
-e MYSQL\_ROOT\_PASSWORD=r*xt)D2b31 \
-e TZ=Asia/Shanghai \
-p 3306:3306 \
mariadb:latest \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci

# 执行ps 查看容器运行情况(这时候应该能查到刚创建的数据库容器)
docker ps

# 为了方便Typecho的部署,我们先创建好一个空的数据库(typecho安装程序不能自动创建数据库)
# 使用容器内的mysql客户端连接db(注意输入创建容器时设置的密码)
docker run --rm -it mariadb:latest mysql -h<替换成云服务器内网IP> -uroot -p
# 创建数据库备用
CREATE DATABASE `db_kcat_typecho` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */

# 查看数据库列表,确认创建成功
show databases;
# 退出连接
\q

博客容器

Typecho官方有做好的镜像在Dockhub上,可以直接使用,非常方便。官方构建了3个不同的镜像,分别是joyqi/typecho:nightly-php7.4-apachejoyqi/typecho:nightly-php7.4-fpm,没有找到相关版本说明,只能从字面上理解分别是apache版本和php-fpm版本。我首先尝试的是FPM版本,启动后无法访问,也没有找到相关日志,只好尝试apache版本,apache版本是能顺利使用的。

如果不需要使用https和不想使用nginx做流量接入的话可以把-p 8080:80修改成-p 80:80直接把服务器的80端口给typecho容器使用,然后把域名解析到云服务器的公网ip上就可以访问了。

docker run -d \
--name typecho \
--network my-net \
--restart=always \
-v /data/typecho/config.inc.php:/app/config.inc.php \
-v /data/typecho:/app/usr \
-e TYPECHO_SITE_URL=https://kcat.io \
-e TIMEZONE=Asia/Shanghai \
-p 8080:80 \
joyqi/typecho:nightly-php7.4-apache

第三步做流量接入

Https证书

Https证书我使用的是免费的Let's Encrypt。可以通过支持ACME协议的工具来获取证书,比如acme.sh。acme.sh有很多优点,比如:

  • 执行脚本不需要root权限(对权限敏感的用户非常友好)
  • 支持获取类似*.kcat.io的泛域名证书
  • 支持用TXT解析的方式验证域名权限
  • 支持自动更新证书(如果是TXT解析方式 则需要提供域名API)
  • 项目有中文说明(简直不要太友好)

acme.sh详细的使用说明可以查看这里https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E

# 下载安装acme.sh,默认安装到用户的`home/.acme.sh`目录下
# 支持非root用户安装,也建议使用非root用户
curl  https://get.acme.sh | sh -s email=my@example.com

# 发出泛域名证书申请
acme.sh --issue -d '*.kcat.io' --dns --yes-I-know-dns-manual-mode-enough-go-ahead-please
# 然后, acme.sh 会生成相应的解析记录显示出来, 你只需要在你的域名管理面板中添加这条 txt 记录即可

# 大约过个2、3分钟(或者自己用nslookup检查txt解析是否已生效)可以继续获取证书(第二次用的是--renew)
acme.sh --renew -d '*.kcat.io' --yes-I-know-dns-manual-mode-enough-go-ahead-please

申请成功后证书会保存在/home/用户名/.acme.sh/*.kcat.io目录下,我们把fullchain.cer*.kcat.io.key分别拷贝到/data/nginx/ssl.d/kcat.io/fullchain.cer/data/nginx/ssl.d/kcat.io/kcat.io.key路径以备后续nginx容器使用。

Nginx容器

先准备好Nginx所需的配置文件以及域名的证书文件(不需要https的场景可以移除ssl相关的配置)
Nginx主配置文件内容如下,存放到云服务器的/data/nginx/nginx.conf路径

user  nginx;
worker_processes  4;
worker_rlimit_nofile 65535;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    use epoll;
    worker_connections  65535;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

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

    access_log  /var/log/nginx/access.log  main;

    sendfile       on;
    tcp_nopush     on;
    tcp_nodelay    off;
    server_tokens  off;

    keepalive_timeout  60;
    send_timeout       600;


    gzip  on;
    gzip_min_length  1000;
    gzip_buffers     4 8k;
    gzip_types       text/plain application/x-javascript text/javascript text/css application/xml text/shtml text/js;
    gzip_disable     "MSIE [1-6]\.";

    client_header_buffer_size    1k;
    large_client_header_buffers  4 8k;

    proxy_cache_path /tmp/nginx_proxy_cache levels=1:2 keys_zone=STATIC:10m inactive=1d max_size=30m;

    proxy_redirect  off;
    proxy_set_header        Host $host;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $remote_addr;
    client_max_body_size    512m;
    client_body_buffer_size 128k;
    proxy_connect_timeout   8;
    proxy_send_timeout      8;
    proxy_read_timeout      8;
    proxy_buffer_size       4k;
    proxy_buffers      32 32k;
    proxy_busy_buffers_size 64k;
    proxy_temp_file_write_size 64k;
    server_names_hash_bucket_size   128;

    include /etc/nginx/conf.d/*.conf;
}

站点配置文件内容如下(可以按需替换内容中的域名),保存到云服务器的/data/nginx/conf.d/kcat.io.conf路径

server {

    listen       80;
    listen  [::]:80;
    listen  443 ssl;

    server_name  kcat.io;

    access_log  /var/log/nginx/kcat.io.access.log  main;

    location / {
        proxy_set_header    Host                $host;
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_set_header    User-Agent          $http_user_agent;
        proxy_set_header    Connection          "";
        proxy_read_timeout  600;
        proxy_send_timeout  600;

        proxy_pass http://<替换成云服务器的内网IP>:8080;
        
    }

    ssl_certificate /etc/nginx/ssl.d/kcat.io/fullchain.cer;
    ssl_certificate_key /etc/nginx/ssl.d/kcat.io/kcat.io.key;
    include /etc/nginx/ssl.d/options-ssl-nginx.conf;
    ssl_dhparam /etc/nginx/ssl.d/ssl-dhparams.pem;

    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    }

}
# 由于kcat.io 和www.kcat.io 不能用同一个证书,只好分别配置,并把www的流量301到不带www的地址上
server {

    listen       80;
    listen  [::]:80;
    listen  443 ssl;

    server_name  www.kcat.io;

    ssl_certificate /etc/nginx/ssl.d/www.kcat.io/fullchain.cer;
    ssl_certificate_key /etc/nginx/ssl.d/www.kcat.io/kcat.io.key;
    include /etc/nginx/ssl.d/options-ssl-nginx.conf;
    ssl_dhparam /etc/nginx/ssl.d/ssl-dhparams.pem;

    return 301 https://kcat.io$request_uri;

}

配置ssl的options内容如下,保存到/data/nginx/ssl.d/options-ssl-nginx.conf


ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;

ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers off;

ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";

配置ssl的dhparams内容如下,保存到/data/nginx/ssl.d/ssl-dhparams.pem

-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----

配置文件准备好之后,启动nginx容器

docker run -d \
--name nginx \
--network my-net \
--restart=always \
-v /data/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /data/nginx/conf.d:/etc/nginx/conf.d \
-v /data/nginx/ssl.d:/etc/nginx/ssl.d \
-v /data/nginx/log:/var/log/nginx \
-e TZ=Asia/Shanghai
-p 80:80 \
-p 443:443 \
nginx