Fork me on GitHub

Hugo部署历程

情景描述

使用 WordPress 写博客有一个学期了,博客没写几篇,Nginx 和 WordPress 倒是折腾了不少。也是嘛,如果不是折腾了一个学期的 Nginx,这次学校网络更新架构,要给城市热点的服务分配域名,然后那个端口问题还真搞不定了。

不过,WordPress 的写作体验是真的不太好。不仅没有 MarkDown 写着舒服,每天还要担心有没有人进行网络渗透。WordPress 的速度的确不算快,我上了缓存插件,直接生成静态页面,然后还配置了 tmpfs。

现在,我决定迁移到 Hugo,纯静态网站,你来入侵啊。Hugo 生成的网站文件,我还给放到了 tmpfs 里,起飞吧,哔u哔u。

安装相关软件

首先是安装 Hugo,这个建议不要使用包管理器直接安装,建议到 GitHub 下载编译好的二进制安装包,实在不行,自己编译也好。

可以到 Hugo Release 页面 下载。

以 DEB 系的 32位 Linux 为例,首先下载 deb 包:

wget https://github.com/gohugoio/hugo/releases/download/v0.47.1/hugo_0.47.1_Linux-32bit.deb

然后进行安装

sudo dpkg -i hugo_0.47.1_Linux-32bit.deb

然后安装 Git、Nginx,这个直接安装就好

sudo apt install -y git nginx

配置 MathJax

我使用的 Hugo 主题已经支持了 MathJax,不过是从 CDN 那里加载的 MathJax 文件。而我想让所有的文件都在我自己的服务器上,于是我就魔改了一下下主题。这里对其他 Hugo 用户应该也有些参考价值。

首先是下载 MathJax,从 GitHub 下载。

wget https://github.com/mathjax/MathJax/archive/master.zip

解压这个压缩包,然后将这个压缩包里 unpacked文件夹移动到你的 Hugo 网站的目录下,最好这个文件夹重命名为 MathJax

然后,移动压缩包里的 fonts 文件夹,到 Hugo 网站的目录下,应该与 MathJax 文件夹在同一级目录里。

我将这两个文件夹放在了 static/lib/ 下面,最终生成网站后,应该在 /lib/ 下面。也就是 /lib/ 下面应该存在两个文件夹,/lib/MathJax/lib/fonts

然后你要想办法在你的生成的静态 Html 文件里加上如下几行:

<script type="text/javascript">
    window.MathJax = {
      tex2jax: {
            inlineMath: [['$','$'], ['\\(','\\)']],
            displayMath: [['$$','$$'], ["\\[","\\]"]],
            processEscapes: true
        },
      TeX: {equationNumbers: {autoNumber: "AMS"}},
      showProcessingMessages: true,
      messageStyle: 'normal'
    };
  </script>
  <script type="text/javascript" async src="/lib/MathJax/MathJax.js?config=TeX-AMS_HTML"></script>

注意最后那个引用 MathJax 的链接,建议你查查 MathJax 的文档。这里的配置是使用 Tex 语法书写公式,使用 Html 和 Css 渲染公式。

以下是一个测试,数学公式: \sum_1^2

行内显示公式:$ \sum_1^2 $

块显示公式:

$$ \sum_1^2 $$

使用 Git 发布文章

首先配置一下服务器端的 Git。我的做法是直接上传本地的 Git 配置文件。毕竟这个配置用了两年了。但是,仅仅这样还是不够的。

在服务器上找个目录放你的网站,然后:

git init
git add -A
git commit -m "迁移博客到 Hugo"

这些都是常规操作了。不过这个时候,本地的 push 操作是失败的。没错,之所以说使用本地的配置不够是因为,本地的配置少了一些。

[core]
  autocrlf = input
[receive]
  denyCurrentBranch = ignore

第一个配置是转换 Linux 的换行符与 Windows 的换行符,否则就会一直提示,很烦的。第二个配置是关键。进行 push 时,如果远程库是非 bare 库,本地 push 会被禁止。所以,加上这个配置,本地的 push 会同步过去,但是远程仓库工作区的文件内容不会更改。

其实,到了这里就结束了。

之后,本地的更改直接 push 就好了,然后 ssh 到服务器,直接检出、合并、构建就完事了。

这里提一下具体操作

git log
git status
git reset --hard dca8b26
hugo

首先查看我们提交的历史,以及版本号。然后我们看看此时仓库的状态。可以看到我们提交成功了,并且已经到了暂存区里,但是工作区还未同步。现在我们既可以让暂存区同步到工作区,也可以直接回滚到提交记录中去。

所以我们使用 reset --hard 强制使工作区回到 dca8b26 这次提交的状态去。

事实上,这里这样做更方便,操作是完全等价的。而且还可以做成钩子,实现 commit 后自动构建。

git reset --hard HEAD
hugo

因为 HEAD 是指向最新的 commit 的,所以 reset 到 HEAD 和 reset 到指定的 commit 在我们这里的情形下是完全等价的,因为我们指定的 commit 就是最新的那一个。

使用 Nginx 部署网站

一个静态网站,有啥好配置的。直接贴出来。

server {
    listen 0.0.0.0:443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_certificate /website/nginx/sslcert/www.pem;
    ssl_certificate_key /website/nginx/sslcert/www.key;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;

    server_name www.huanggefan.cn;
    root /website/www.git/public;
    index index.html;

    access_log /website/log/www-access.log main;
    error_log /website/log/www-error.log;

    location ~* \.(jpg|jpeg|png|icon|bmp|gif|tty|js|css)$ {
        access_log off;
        log_not_found off;
    }

    error_page  404 403 500 502 503 504  /404.html;
}

使用内存进行加速

Hugo 默认会把构建的静态网站文件放到 public 目录下,我觉得这样挺好的,也没有更改路径。Ubuntu 有一个特性,就是 /dev/shm 其实是个目录,而且,是在内存里的一个目录,这个目录有内存的一半大。所以,我就直接在这里创建一个目录,通过软链接,链接到 public。这样,public 其实就在内存里了。Hugo 也就是把静态网站文件放到了内存里了。

阿里云 ECS 的磁盘性能不是很好,不过内存性能很好,这样算是进行了最大化的性能优化。

Nginx代理城市热点

情景说明

学校部署了城市热点的服务,但是,领导不想让学生看到服务器的 IP,想给服务一个域名。但是,服务部署在 8080 端口。领导也不想让学生看到端口。

城市热点的人员考虑采用重新部署应用的方式,不过,似乎他用了很长的时间也没有搞定。然后我就里所应当的用了他从学校那里申请的虚拟机了。

嘛,虚拟机不是给我玩的,我的方案是采用 Nginx 进行反向代理。随便加上缓存、限速啥的。

在 CentOS 上安装 Nginx

在 CentOS 上安装 Nginx 可真麻烦,也许是虚拟机的 CentOS 版本太旧。反正没有 Debian、Ubuntu 用的舒服。

使用以下命令进行安装:

yum install -y nginx

恭喜,这是不可行的。你会被提示没有 Nginx 这个包。如果你不甘心,使用了以下命令:

yum search nginx

你会发现根本没有 Nginx,即使你随便安装了一个长的比较像的,也是不对的。

你要在 /etc/yum.repos.d 下添加一个 nginx.repo 文件,里面的内容如下:

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

然后就可以开心的执行安装命令了。

yum install -y nginx

配置代理

这里直接贴上我的配置文件,我是不是泄露了一些信息。嘛,我也不担心你会渗透到服务器里,反正是学校内网,你又进不来。除非你也是安农的。话说,自己人黑自己人有意思吗。

server{
    listen 0.0.0.0:80;
    listen [::]:80;
    server_name ss.ahau.edu.cn;

    proxy_set_header Host $host:$server_port;
    proxy_set_header Remote_Addr $remote_addr;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Access-Control-Allow-Origin *;
    proxy_http_version 1.1;
    add_header Access-Control-Allow-Origin *;

    location / {
        access_log off;
        log_not_found off;
        rewrite ^.*$ http://ss.ahau.edu.cn/Self/;
    }
    location ~* \.(css|js|png|jpg|jpeg|ico|icon|gif) {
        access_log /var/log/nginx/access-static.log;
        error_log /var/log/nginx/error-static.log;
        proxy_cache web;
        proxy_cache_valid 200 304 7d;
        proxy_cache_valid 404 500 10m;
        add_header ProxyCache "$upstream_cache_status";
        proxy_pass http://114.213.145.140:8080;
    }
    location ~^/Self/login/verify$ {
        access_log /var/log/nginx/access-login.log;
        error_log /var/log/nginx/error-login.log;
        proxy_pass http://114.213.145.140:8080;
    }
    location ~^/Self.*$ {
        access_log /var/log/nginx/access-other.log;
        error_log /var/log/nginx/error-other.log;
        proxy_pass http://114.213.145.140:8080;
    }
}

我在这里配置缓存,除了缓存、日志那里我明白一切,其他部分感觉都有问题。但是,只有这样它工作了,如预期一般工作了。你可以进行各种更改尝试,反正只有这样写,location 的顺序也是这样的,它才会工作。

配置缓存

配置缓存,将各种静态文件缓存到虚拟机里,而且缓存到虚拟机的内存里。没错,内存里,内存大就是可以为所欲为。

不仅可以加速学生的访问,而且可以非常明显的减少真实服务器的压力。虽然它的抗压能力估计比虚拟机更好。

proxy_temp_path /var/lib/nginx/cache_temp;
proxy_cache_path /var/lib/nginx/cache levels=1:2 keys_zone=web:200m max_size=900m inactive=7d;

上面的一段要在 http 字段里设置,然后在 http 字段里的 server 下进行如下配置

proxy_cache web;
proxy_cache_valid 200 304 7d;
proxy_cache_valid 404 500 10m;
add_header ProxyCache "$upstream_cache_status";
proxy_pass http://114.213.145.140:8080;

配置限速

配置限速是为了防止有人进行大规模的密码爆破行为。因为城市热点的系统传输数据没有加密。没有加密不仅意味着可以进行嗅探,还可以进行密码爆破。一旦这个系统的密码被破解了,校园网的密码也就被破解了。这是绝对不能发生的。

嘛,估计没人会这样做,除了我……

limit_conn_zone $binary_remote_addr zone=proxy_conn:10m;
limit_conn proxy_conn 8;
limit_rate 200k;
limit_rate_after 10m;

不知道为啥,只能限制每个 IP 的连接数,不能限制并发数。一旦限制并发数,页面就无法加载。

踩坑

proxy_set_header Host $host:$server_port;

看到这一行了没,注意,没了它就完了。而且,我现在也不知道为啥要这样写。

Nginx反向代理Google

情景简述

国内用不了 Google 对吧,但是,作为程序员必须要用 Google 对吧(当然,百度啥的也能用,就是有点难用)。所以,咋办呢。

翻墙。不管用什么方法翻墙,总要花钱的对吧。免费的 VPN 一点也不稳定。租国外的服务器,自己搭建 VPN 服务器,你确定,你愿意花那么多钱就为了翻个墙。

所以呢,我有一台服务器对吧,阿里云的 ECS。然后呢,我已经让它支持 IPv6 对吧。然后,之前在给 ECS 配置 IPv6 时,已经确定,在国内,可以使用 IPv6 ping 通 Google 的。那么,使用 IPv6 访问 Google 呢。

分析思路

现在我们已经确定。使用 IPv6 可以访问 Google,那么我们的策略是:本地电脑访问 ECS 的 Nginx 代理,ECS 使用 IPv6 访问 Google,然后将结果返回给本地电脑。

这不就是反向代理吗,Nginx 不是经常这样用吗。平时用 Nginx 反向代理内网的服务,这次是代理外网的而已,都一样嘛。

操作方法

编辑 /etc/nginx/conf.d/ 下的配置文件,我们创建一个 google.conf,添加如下配置

upstream google {
    server [2404:6800:4012:1::2004]:443;
}
server {
    listen 0.0.0.0:80;
    listen [::]:80;

    server_name xxx.xxx.xxx;
    proxy_set_header Host www.google.com.hk;
    proxy_set_header x-forwarded-for $remote_addr;
    location /{
        proxy_pass https://google;
    }
}

首先,我们在 upstream google 字段,配置了一个名为 google 的上游服务,这里把上游服务器指向 Google 服务器的 IPv6 地址,由于 Google 使用了 https 所以,要指定 443 端口。也就是说,所有发向 google 这个上游代理的数据,都会发向 2404:6800:4012:1::2004 的 443 端口。也就是说,我们把上游代理设置成了 Google 的服务器,有点不厚道。

然后,在 server 字段那里,配置了 nginx 的一个监听服务。它会监听 IPv4 和 IPv6 的 80 端口,然后把所有的数据都发向 google 这个上游代理。如果你想使用 https,请添加 ssl 支持。

注意

server_name xxx.xxx.xxx;

这一行,请把 xxx.xxx.xxx 换成你的服务器域名或 IP。

proxy_pass https://google;

这一行,表示,转发所有的数据到 google 这个上游代理。

然后 service nginx restart 吧。

后记

使用了一个星期后,被查封了,现在我的 ECS 已经无法通过 IPv6 访问 Google 了。伟大的祖国啊。

阿里云ECS开启IPv6

情景说明

首先说明我们要干什么:我们要让自己的网站支持 IPv6,谁让国家给 IPv6 下了文件呢。我们要确保,用户可以在 IPv6 网络环境下无障碍的访问我们的网站。

然后说明我们的系统环境:服务器是阿里云的 ECS,操作系统是 Ubuntu。我们用的是万网的域名解析。阿里一家的嘛。

获取 IPv6 地址

可以从 Tunnel Details 获取 IPv6。你需要注册一个账号,这没啥好说的。注意,你需要一个公网 IPv4 地址。然后,你就可以用一个公网 IPv4 领取到你的 IPv6 了。

阿里云ECSIPv6-1.png



阿里云ECSIPv6-2.png

配置 ECS 系统参数

现在配置系统参数,启动 IPv6。

修改 /etc/sysctl.conf (注意备份)更改如下,把这三个参数改为 0 就可以了:

net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
net.ipv6.conf.lo.disable_ipv6 = 0

配置 ECS 网络

下面就是配置你的 ECS 让它使用 IPv6 地址了(注意,这里用的是 Ubuntu 系统,阿里云支持其他的 Linux 操作系统,请自行调整)。

只要使用 Tunnel Details 给出的示例配置,稍加修改就好。看到给的那段英文提示没,注意,local 那里要填你的 ECS 的内网 IPv4,不是公网的哦。然后添加到你的 /etc/network/interfaces 文件里(注意备份),需要修改的也就只有 local 那里了。

阿里云ECSIPv6-3.png



阿里云ECSIPv6-4.png

配置 Nginx 监听 Ipv6

现在配置 Nginx,监听 Ipv6 请求

server {
    listen 0.0.0.0:80;
    listen [::]:80;
    …………
    …………
}

把监听参数改成这个,就可以同时监听 IPv4 和 IPv6 了: 想使用 https 就使用 ssl 监听 443 端口呗,这个和本文没啥关系。 使用 Apache 的嘛,…………

配置 IPv6 DNS 解析

添加一条 AAAA 记录,没啥好说的吧,至于你的公网 IPv6 地址是啥,重启 ECS 后使用 ifconfig 命令看看呗,肯定和你的 /etc/network/interfaces 里配置的 address 一致的。当然,这个 address 肯定也和你申请 IPv6 时的“Client IPv6 Address”一致的。

踩坑:IPv6 无法访问

注意,当你重启 ECS 后,应该可以看到自己的 IPv6 已经启动了,使用 ifconfig 命令查看。然后你在你的 ECS 里使用 ping6 命令去 ping6 ipv6.baidu.com 应该是可以的,但是嘛……

估计你是不能用自己的电脑 ping6 同你的 ECS 的,也是不能用 IPv6 访问到你的网站的,那么其他使用 IPv6 的用户也不能访问你的网站喽。(ping6 不同的一部分原因估计是你的电脑没 IPv6 地址)

要知道,阿里云的安全组规则不是假的。阿里的安全组规则虽然很好用,但它不支持 IPv6 啊,管你什么协议,什么端口,只要是 IPv6 的全禁了。所以,你只能添加一条入方向的规则:“允许所有的协议,所有的端口,所有的访问来源通过”,然后,嗯,其他的规则还需要存在的意义就有点尴尬。这个希望阿里云能解决一下。

静态链接Ncurses

情景描述

平时调用各种 C 库时,免不了各种链接。记得当初学习 C 语言时,为了使用 math 库,可没少折腾。Ncurses 作为一个字符终端的图形库,是相当实用的。不过,并不是所有的用户都会在他的系统上安装这个库,所以,我们可以考虑把 Ncurses 包含到我们的程序里。这就是静态链接。

先简要说明一下静态链接和动态链接。无论是静态库文件(.a)还是动态库文件(.so),都是二进制文件。不同的是,当我们的程序进行静态链接时,是直接把静态库文件给包含进程序里了。而动态链接不会这样做,程序会在运行时,加载库文件,同时,一个动态库文件,可以被多个程序共享加载。换言之,静态链接,是每一个程序都包含了库文件,动态链接是多个程序从同一个库文件共享代码。

显然,动态链接可以减小程序文件的大小,而且节约内存。毕竟一个库只需要加载到内存一次,其他程序使用时,不需要再次加载。而且程序本身并没有包含库文件,所以程序文件不会很大。但是,如果用户的系统没有这个库,就完了。程序就无法运行了。

静态链接就可以避免用户没有安装依赖库的问题。程序通过直接包含库文件,使得程序在加载到内存时,已经加载了库文件。但是,这种做法,会使程序文件比较大,而且,由于每个程序加载时,都加载了库文件,如果这些程序依赖同一个库,必然会造成内存浪费。不过,这样做不必考虑依赖问题,可以确信程序在用户那里能运行。

啰里啰嗦写了一堆不相关的,算是说明了为啥要静态链接 Ncurses,现在进入重点,如何静态链接。当然,你的系统肯定是要已经安装了 Ncurses 的,注意,不只是运行环境,还有开发环境。

安装开发环境

注意,这里是针对 DEB 系的 Linux 系统,Ncurses的版本是 5

sudo apt install libncurses5-dev ncurses-doc

测试代码

我们写一段测试代码,代码就放在主目录下吧。

// test.c
#include <ncurses.h>

int main(void) {
    initscr();
    printw("Hello world\n");
    refresh();
    getch();
    endwin();
    return 0;
}

常规链接

我们使用常规的编译指令,也是动态链接,编译一下。

gcc test.c -o test -lncurses

我们使用 ldd 命令查看生成的可执行文件的依赖

# xxx @ xxx in ~ [12:52:45]
$ ldd test
    linux-vdso.so.1 (0x00007ffd8990a000)
    libncurses.so.5 => /lib/x86_64-linux-gnu/libncurses.so.5 (0x00007f69d5891000)
    libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f69d5667000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f69d52c8000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f69d50c4000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f69d5cb6000)

静态链接

现在我们使用静态链接,注意指令的链接依赖顺序。

gcc test.c -o test -static -lncurses -ltinfo

注意,先链接 ncurses(-lncurses),然后是 tinfo(-ltinfo)。下面是 ldd 命令的输出。

# xxx @ xxx in ~ [12:55:13]
$ ldd test
    不是动态可执行文件

由于我们使用了静态链接,所以,可执行文件 test 里包含了所有的动态库的静态库版本。也就是,test 里包含了 linux-vdso、libncurses、libtinfo、libc、libdl 这些库。

现在的可执行文件 test 可以在一个没有安装 ncurses 的系统上运行了。

中文支持

如果你想使用 Ncursesw 来支持中文等字符(当然你要安装 ncursesw 对吧),可以参考如下。

// test.c 支持中文
#include <ncurses.h>
#include <locale.h> //setlocale()函数

int main(void) {
    setlocale(LC_ALL,""); //使用本地字符集
    initscr();
    printw("Hello world.\n");
    printw("打得好的.\n");
    refresh();
    getch();
    endwin();
    return 0;
}

编译指令:

gcc test.c -o test -static -lncursesw -ltinfo