为 Oneinstack 开启 QUIC

虽然不知道是个啥,但我最终还是为本站添加上了 HTTP/3 QUIC 的特性支持。听说速度会快一点。

首先开头是我写博文一如既往扯淡环节:

折腾了三天,本来只是想做个影视站解决自己看剧问题,结果 MacCMS 后台实在太反人类,另一个 SeaCMS 开发者又死活不支持 php7 。好嘛,拉倒。在按照开发者的教程尝试给 Oneinstack 安装两个 php 的时候我直接把原本的 php7.3 给编译炸了,一时间不知所措。不过还好,建站多年 删库经验丰富,立马上命令行备份数据库和网站文件,本来我在傻不愣登地通过 XFtp 把文件手动下载到本地,后来实在受不住这个网速,直接 OSSUtil 把打包成 .zip 的文件上传到阿里云 OSS 里了。

又到了重装系统的时候,有时候遇到服务器环境问题解决方法很简单,看什么这个报错那个日志,上控制台就是重装一把梭!换了一个干净的 CentOS7.3 之后我开始考虑要不要换个环境,比如大佬们都在用的 Docker ? i-meto 的 docker-lemp 项目还直接支持了 Quic 呢!那就它了,我在 Docker 上折腾了两天,奈何最后有两个问题劝退:一是多站点,lemp 虽好,但却好像不能配置多个 http/2 站点,证书总是显示 localhost 的无效证书,查了一下好像说要支持 TLS SNI 什么的,估计镜像里没有。二是数据库配置,数据库默认地址不再是 localhost 而是 mysql 但是我在安装 Typecho 时还是无法连接,不知道和 root 用户组什么的是不是有关系。

昨天下午我终于放弃了 Docker 的念想,像我这种懒狗果然还是适合一键包。再次安装编译 Oneinstack 后我开始寻找资料为 Nginx 重新编译以支持 QUIC 。哦,还有 Brotli 模块我也顺手给编译上去了。

本站使用 Quic 依赖于 BoringSSL ,原先的 OpenSSL 不再使用。所以 OCSP Stapling 将不再受支持,因为 Nginx 在实现 OCSP Stapling 这部分逻辑时,直接使用了 OpenSSL 库 从无法开启 OCSP Stapling 说起 - JerryQu。当然不破不立, BoringSSL 支持了一种名为「等价加密算法组(Equal preference cipher groups)」的配置,用竖线隔开的两种算法,会被自动应用于最合适的场景。配置时就像这样(换行以方便查看):

ssl_ciphers [TLS13+AESGCM+AES128|TLS13+AESGCM+AES256|TLS13+CHACHA20]:
[EECDH+ECDSA+AESGCM+AES128|EECDH+ECDSA+CHACHA20]:
EECDH+ECDSA+AESGCM+AES256:
[EECDH+aRSA+AESGCM+AES128|EECDH+aRSA+CHACHA20]:
EECDH+aRSA+AESGCM+AES256;

不多说了,梭!

有用的参考资料

菜鸡的实践结果

开梭第一步

请一定要先做好备份,比如数据库、重要的文件。阿里云轻量服务器可以在控制台建立快照,回滚也很方便(推荐)。

本教程环境使用 CentOS7.3 镜像,已经事先安装了 Oneinstack 最新版本,Nginx 编译信息如下。

# ln -s /usr/local/nginx/sbin/nginx /usr/bin/
# nginx -V
nginx version: nginx/1.16.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) 
built with OpenSSL 1.1.1d  10 Sep 2019
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --user=www --group=www --with-http_stub_status_module --with-http_v2_module --with-http_ssl_module --with-http_gzip_static_module --with-http_realip_module --with-http_flv_module --with-http_mp4_module --with-openssl=../openssl-1.1.1d --with-pcre=../pcre-8.44 --with-pcre-jit --with-ld-opt=-ljemalloc --with-ld-opt=-Wl,-rpath,/usr/local/lib --add-module=../lua-nginx-module --add-module=../ngx_devel_kit

环境准备

编译之前要先更新一些依赖什么的,因为要求一些软件比较高的版本(BoringSSL:cmake 3.0+、perl latest、gcc 4.8+、clanggo latest;Quiche:rust 1.38+ 等)才能编译成功,否则肯定会出错。这个时候有磁盘快照就可以直接回滚从头再来,我习惯在我认为的准备工作都已经做好之后、准备开始编译之前再花点时间新建一个快照。

某些资源在 git clone 时速度十分慢,我的解决方法简单粗暴,直接本地下载然后手动上传。相比让小白改 ENV config 什么的我推荐前者。

  • yum install

能用 yum 安装的绝不多花时间,比如 rust 直接用 yum 安装,省去 rustup 及其他设置。cargo 可能需要配置镜像源,后面正式编译 BoringSSL 时 Update crates.io index 特别慢。参考下面后一段的代码配置 config 文件

yum install git zip unzip
yum install clang go -y
yum install rust cargo -y


sudo mkdir -p $HOME/.cargo
sudo tee $HOME/.cargo/config <<-'EOF'
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
EOF
  • cmake 更新

cmake 下载也很慢, wget 选一个下载即可。先 yum remove 卸载老版本 cmake 然后安装新版本,最后用 lncmake 设置软连接,默认的安装路径是 /usr/local/bin/cmake。使用 cmake -version 检查最终版本。

yum -y remove cmake

wget https://github.com/Kitware/CMake/releases/download/v3.17.0-rc3/cmake-3.17.0-rc3.tar.gz
wget https://cdn.monsterx.cn/resources/http3/cmake-3.17.0-rc3.tar.gz

tar xvf cmake-3.17.0-rc3.tar.gz
cd cmake-3.17.0
./bootstrap
gmake
gmake install

ln -s /usr/local/bin/cmake /usr/bin/
cmake -version
  • gcc 更新 (好像不更新也行?)

将命令中的 devtoolset-8 替换为 devtoolset-7 devtoolset-6 可下载对应大版本的 gcc。其他说明可参考前面 有用的参考资料 部分。

yum install centos-release-scl
yum install devtoolset-8-gcc*
scl enable devtoolset-8 bash
gcc -v
  • perl 更新

根据 Perl 官网 Perl source code 页面的说明安装最新版。下载太慢可以用我上传到阿里云 OSS 的文件。然后备份原来的 perl 并建立新的软连接。

wget https://www.cpan.org/src/5.0/perl-5.30.2.tar.gz
wget https://cdn.monsterx.cn/resources/http3/perl-5.30.2.tar.gz

tar -xzf perl-5.30.2.tar.gz
cd perl-5.30.2
./Configure -des -Dprefix=$HOME/localperl
make
make test
make install

mv /usr/bin/perl /usr/bin/perl.bak
ln -s /usr/localperl/bin/perl /usr/bin/perl

perl -v
  • Cloudflare Quiche 及其子项目

Cloudflare quiche 本身可能并不难下载,但是其中引用的子项目 BoringSSL 下载要很长时间。如果你也等不住可以使用我上传到阿里云 OSS 的完整压缩包。wget unzip 解压后效果和 git clone 的一样,压缩包中一级目录是 quiche 文件夹。
注意要先进入 oneinstack/src 目录下,然后只需要执行前一段或只执行后一段,路径如果不对之后的脚本修改大概率会出错!

git clone --recursive https://github.com/cloudflare/quiche

wget https://cdn.monsterx.cn/resourses/http3/cloudflare-quiche.zip
unzip cloudflare-quiche.zip
  • Brotli 模块

相比 Gzip 压缩,听说 Brotli 压缩效果更好,既然可以兼容那为何不安排一下呢?忘记这个下载慢不慢了,没有上传阿里云。
注意这里执行目录也是 oneinstack/src

cd oneinstack/src
git clone https://github.com/google/ngx_brotli.git
cd ngx_brotli
git submodule update --init

Oneinstack 修改

修改 oneinstack/include 文件夹下 upgrade_web.sh

如果只需要安装 Brotli 模块请在文件 57 行后添加 --add-module=../ngx_brotli

    ./configure ${nginx_configure_args} --add-module=../ngx_brotli

编译 QUIC 相关组件则需要在 57 行前添加一行打上 patch 补丁,我这里引用的是 GitHub 上 kn007/patch/nginx_with_quic.patch 补丁。${nginx_configure_args} 需要根据本地配置修改,请不要直接 Copy 我的。
使用 /usr/local/nginx/sbin/nginx -V 记录输出中 “configure arguments:” 后面的内容。 Oneinstack 默认的输出应该如下(方便检查我添加了 \):

--prefix=/usr/local/nginx \
--user=www --group=www \
--with-http_stub_status_module \
--with-http_v2_module \
--with-http_ssl_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-openssl=../openssl-1.1.1d \
--with-pcre=../pcre-8.44 \
--with-pcre-jit \
--with-ld-opt=-ljemalloc \
--with-ld-opt=-Wl,-rpath,/usr/local/lib \
--add-module=../lua-nginx-module \
--add-module=../ngx_devel_kit 

--with-openssl=../openssl-1.1.1d 替换为 --with-openssl=../quiche/deps/boringssl 。新增这两个:--with-http_v3_module--with-quiche=../quiche。我的修改参考(请删除 # 开头的行):

# 在第 57 行前添加一行,打上其中一个 patch 
curl https://cdn.jsdelivr.net/gh/kn007/patch/nginx_with_quic.patch | patch -p1
# curl https://cdn.jsdelivr.net/gh/cloudflare/quiche/extras/nginx/nginx-1.16.patch | patch -p1

# 根据 nginx configure arguments 和需要自行修改
./configure --prefix=/usr/local/nginx --user=www --group=www--build="quiche-$(git --git-dir=../quiche/.git rev-parse --short HEAD)" --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_v3_module --with-http_gzip_static_module --with-http_realip_module --with-http_flv_module --with-http_mp4_module --with-openssl=../quiche/deps/boringssl --with-quiche=../quiche --with-pcre=../pcre-8.44 --with-pcre-jit --with-ld-opt=-ljemalloc --with-ld-opt=-Wl,-rpath,/usr/local/lib --add-module=../lua-nginx-module --add-module=../ngx_devel_kit --add-module=../ngx_brotli --add-module=../ngx_brotli

编译

确认脚本修改无误、软件版本可行之后开始重新编译 Nginx 。

cd oneinstack
./upgrade.sh

选择更新 Nginx 项,输入与当前版本不同的版本号,推荐版本 1.16.11.17.9。等待编译完成即可。

闪耀的金色传说

如果以上全部完成,那就可以开始配置以享用 QUIC 和 Brotli 的快感了!

Nginx

让我们来看一眼编译的成果!!(我这个 gcc 更新的姿势好像不太对嗷)

Nginx Final
Nginx Final

QUIC

如果是 Oneinstack 则修改 /usr/local/nginx/conf/vhost 文件夹下虚拟主机 .conf 文件内容。其他的环境同理。
Cloudflare 示例如下,Quic 443 端口监听、 TLSv1.3 支持、添加 HTTP/3 响应头为必需内容。

server {
    # Enable QUIC and HTTP/3.
    listen 443 quic reuseport;

    # Enable HTTP/2 (optional).
    listen 443 ssl http2;

    ssl_certificate      cert.crt;
    ssl_certificate_key  cert.key;

    # Enable all TLS versions (TLSv1.3 is required for QUIC).
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    
    # Add Alt-Svc header to negotiate HTTP/3.
    add_header alt-svc 'h3-23=":443"; ma=86400';
}

本站配置文件参考

server {
  listen 80;
  listen 443 quic reuseport;
  listen 443 ssl http2;
  
  ssl_certificate /path/to/monsterx.cn.crt;
  ssl_certificate_key /path/to/monsterx.cn.key;
  
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers [TLS13+AESGCM+AES128|TLS13+AESGCM+AES256|TLS13+CHACHA20]:[EECDH+ECDSA+AESGCM+AES128|EECDH+ECDSA+CHACHA20]:EECDH+ECDSA+AESGCM+AES256:[EECDH+aRSA+AESGCM+AES128|EECDH+aRSA+CHACHA20]:EECDH+aRSA+AESGCM+AES256;
  
  ssl_prefer_server_ciphers on;
  ssl_session_timeout 10m;
  ssl_session_cache builtin:1000 shared:SSL:10m;
  ssl_buffer_size 1400;
  
  # add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
  add_header alt-svc 'quic=":443"; ma=2592000; v="49,46,43,27,25,24,23", h3-27=":443"; ma=86400, h3-25=":443"; ma=86400, h3-24=":443"; ma=86400, h3-23=":443"; ma=86400';
  # add_header X-Frame-Options SAMEORIGIN;
  # add_header X-Content-Type-Options nosniff;
  # add_header X-Xss-Protection "1; mode=block";
  
  server_name monsterx.cn;
  # access_log /path/to/nginx.log combined;
  index index.html index.htm index.php;
  root /path/to/monsterx.cn;
  
  if ($ssl_protocol = "") { return 301 https://$host$request_uri; }
  
  include /path/to/typecho.conf;
  #error_page 404 /404.html;
  #error_page 502 /502.html;
  
  location ~ .*\.php(\/.*)*$ {
    # ...
  }

  location ~ .*\.(js|css)?$ {
    expires 7d;
    access_log off;
  }
}

alt-svc 响应头我还没搞懂,反正各种版本都先加着了,反正也不会报错 OuO 。

HTTP3 Check Result
HTTP3 Check Result
HTTP3 Check with AliCDN
HTTP3 Check with AliCDN

检查 QUIC 的一个办法就是这个 HTTP3 CHECK 。本地调试 QUIC HTTP/3 双 Pass ,这几天的折腾总算是没有白费,开心!!不过为了保证速度我还是继续套上了阿里云 CDN ,阿里云 CDN 很早之前就开始了 QUIC 的测试,可以在钉钉测试群中艾特工作人员发送域名申请开启 QUIC 请求,处理也不慢,基本在一个工作日之内就开好了。很早之前我就试过了不过当时并没有编译什么支持就直接请求开通,结果就 502 了 2333。第二次申请开通,Success!

话说不久前我还看到 www.zhihu.com api.zhihu.com 在群里申请了 QUIC 呢,原来知乎在用阿里云服务呢~


为站点的境外解析套上了 CloudFlare CDN !现在境外访问依旧同时支持 QUIC HTTP/3 了。
虽然不知道有谁会挂着国外的 VPN 访问本站,但我还是要说 CFNB

HTTP3 Check with CFCDN
HTTP3 Check with CFCDN

Brotli

如果是 Oneinstack 则修改 /usr/local/nginx/conf 文件夹下 nginx.conf 文件内容,将 55~57 行取消注释,前面 Gzip 配置不需要改动。其他的环境则在 nginx.conf 中添加下面配置。

  #Brotli Compression
  brotli on;
  brotli_comp_level 6;
  brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;

检查文件是否成功 Brotli 压缩:打开控制台 Network 项,刷新页面,选择资源查看 Response Headers 中 content encoding 字段,br 为 Brotli 压缩,gzip 为 Gzip 压缩。

Brotli 压缩验证
Brotli 压缩验证

 


 

三月我更新好多啊!4 5 6 月不如...

添加新评论

本站现启用评论投票,被踩的太多就会自动折叠 QwQ ,快来试试吧 ~
评论提交失败请点Artalk备用评论


已有 5 条评论

4 5 6咕咕咕,百度云加速和cloudflare一样默认开启br,服务器端我就GZIP好了~ HTTP3 还是准备等nginx官方支持,毕竟还没有定稿,

我要是修不好经常 405 Method not allowed 的问题我也要从 QUIC 退坑了 QaQ

毕竟还是草案,我等nginx官方~ |´・ω・) ノ

整合而一下别人的教程而已,大佬不敢当 qaq