基于 Hexo & Docker 的博客搭建流程
搭建这个 Blog 用到的技术其实都很大众且成熟的,基本都是可以直接拿来用的。
技术框架
博客框架我用的是 Hexo, 模版用的是 Apollo, 运营商选择了 Google Cloud,反向代理还是 Nginx,实现了基于 Python Flask 的 API RESTFul 的自动更新,最后用 Docker 封装了上面这些服务,为的是以后迁移服务器时可以更加快捷和方便。
2018-08-13 更新:运营商换回了 Aliyun HK,Google Cloud 在国内用起来还是不方便,而且整体收费比阿里云要贵一丢丢
评论系统用的 Disqus,之后还会找一个 pv 统计插件。
pv,uv 统计,感谢 busuanzi。
项目 TIMELINE
- 确认使用 Hexo 作为博客框架
- Theme 选择
- 本地部署测试
- 代码托管至 Github
- Google Cloud 服务器部署
- Docker with Nginx 安装
- Nginx 反向代理及禁止 IP 访问
- 将整个 Hexo 项目也打包成 Docker
- 挂载宿主机 /source/_posts/ 目录作为 Docker Hexo 的外部 Volumn,使之可以动态更新
- 开发 API RESTFul 服务,使服务端可通过 HTTP 请求自动更新
- 阿里云万网 DNS 注册及解析
- Disqus 评论系统集成
- 全站部署 HTTPS
Docker
为什么先讲 Docker ?因为我的整个博客和服务的搭建都是基于 Docker 的,下面我给出的代码也是建立 Repository 的 Dockerfile。
PS: 当然你也可以根据 Dockerfile 里的内容在宿主机上跑。
- 从 Docker Hub 拉取需要用到的原生 Repository
1 | # 拉取 Python3 仓库 |
Hexo 框架及 Apollo 模版
Hexo + Apollo 最终呈现的效果就现在博客的样子,目前我是挺喜欢这样的样式和布局的。
基本的 Hexo 和 Theme 配置官网写的很清楚了,再写一遍感觉没啥必要,可以移步 Hexo 官网。
我给出基于 node 的 Dockerfile
1 | FROM node |
- 这里有一个技巧,因为上面的 Dockerfile 中的每一行命令在构建时,就会自动生成一层,底层命令依赖上层。我们可以把 变化较少的命令放在上层,这样如果你的代码修改了之后,最上层的就不会动,加快构建速度。
构建容器:Dockerfile 配置好后,可以在 Dockerfile 路径下使用
docker build -t blog .
来构建容器。Docker 启动 Hexo 博客:
docker run --name=blog -d --rm -p 4000:4000 --privileged -v /root/TechTimeLine/source/_posts:/app/source/_posts blog
-p 4000:4000
暴露 docker 端口--privileged
给这个容器最高的权限,默认--privileged=false
-v /root/TechTimeLine/source/_posts:/app/source/_posts
给容器挂载存储卷,挂载到容器的某个目录( 用于自动更新 )
Nginx 配置
Nginx 放在这里的最重要的用处:
- 隐藏 Blog 服务的真实 IP 地址。
- 负载均衡,后端可以部署多台 Blog 服务,Nginx 默认会使用「轮询」机制。
- 更方便的实现 HTTPS。
给出 nginx.conf
1 | user nginx; |
该 nginx.conf 为 HTTPS 的配置文件,把上面的 nginx.conf 复制到你的 /etc/nginx/nginx.conf 路径下,你需要修改的只有:
- 修改 server_name 为你自己的域名
- 把 upstream 下的服务器地址改成你的 IP 地址
- 将 ssl_certificate 和 ssl_certificate_key 替换成你自己的 Certificate
在配置完 Nginx 之后,我们不需要 Build,因为我们可以直接使用 Docker Hub 上的 Nginx 容器。
Nginx 容器启动
docker run -d --rm --name=nginx -p 443:443 -p 80:80 -v /etc/nginx/nginx.conf:/etc/nginx/nginx.conf nginx
-p 443:443 -p 80:80
暴露 443 和 80 端口-v /etc/nginx/nginx.conf:/etc/nginx/nginx.conf
将宿主机的 nginx.conf 配置文件挂载到 nginx 容器内
API 远程更新
基本的逻辑是在服务端有一个自动更新文章目录的脚本,给这个脚本套一层 API 服务,每次来请求的时候带上一个 API KEY 参数,服务端验证这个值,通过就自动更新。
由于服务端只会做目录更新,所以万一 API KEY 泄露也不会影响什么。
- 给出基于 Flask 写的 API RESTful 代码
1 | import json |
接下来要完成
上线 PV 流量统计功能- 基于 不蒜子 的 访问次数,访问人数 统计,配置很简单,可以直接点进去看官网。
禁止直接访问服务器 IP 地址已经更新在 nginx.conf 上了,需要注意的是在 default server 配置时,也需要添加 ssl_certificate 和 ssl_certificate_key
1
2
3
4
5
6
7
8# 禁止 IP 地址访问服务器,如果是 IP 地址,则返回 500
server {
listen 80 default;
listen 443 ssl default;
ssl_certificate /etc/nginx/cert/*.pem;
ssl_certificate_key /etc/nginx/cert/*.key;
return 500;
}
让墙内的用户知道博客有基于 Disqus 的评论网上很多解决方案,大致思路是自己写样式,由服务器直接加载样式。
数据部分则通过墙外的服务器做反向代理,通过 Disqus 提供的 API 去拿评论数据。
而我更倾向让你知道有 Disqus 这个东西,且让你知道这个东西被墙了,这样如果你有兴趣,可以自己翻墙去看。
具体做法:请求自己搭建的接口,接口服务部署在墙外的服务器,返回内容是 Disqus 的 Javascript 文件,并将加载框的英文注释改为中文注释。
具体代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import requests
from flask import Flask, Response, request
from flask_restful import reqparse
@app.route('/api/disqus')
def disqus():
short_name = request.args.get('short_name', None)
path = request.args.get('path', None)
if short_name is not None and path is not None:
url = 'https://' + short_name + '.disqus.com/' + path + '.js'
notice = '评论如果一直加载不了, 说明被墙了, 自己看着办吧... >_<.'
r = requests.get(url).text \
.replace('Disqus seems to be taking longer than usual.', notice) \
# 默认 15 秒才出现 notice,我改成了 0 秒就出现
.replace('15e3', '0e3')
return Response(r, mimetype='text/javascript; charset=utf-8')