手把手带你搭建个人博客
潘忠显 / 2022-10-19
一、背景
为了敦促自己不断学习总结并提高个人影响力,我在 2020 年 5 月搭建起了自己的博客,至今已经两年多了。当前部署方案基于 Markdown 本地编写、迁移、转载到 KM 都非常灵活方便,更新网站内容也只需本地一次 Git Push 操作。
方便易用的个人博客方案,能够提高个人分享的积极性,至少不会成为你分享道路上的绊脚石。
最近总结下我个人博客的详细方案,包括部署方式、内容组织、网站被索引等内容,供诸君参考。
*部署过程要求对 Linux 基本操作有些了解。*文末也提供了其他的替代方案简介和对比,也有说明个人为什么选择了当前的方案。
二、Hugo 博客框架
本节介绍 MacBook 上安装 Hugo,创建网站和第一篇博客,编写内容、插入图片,并在本地启动 HTTP 服务可以通过浏览器访问。
2.1 QuickStart
参考 Hugo 官方文档 - Quick Start
# 安装 Hugo
brew install hugo
# 创建一个新的网站(本地)
hugo new site quickstart && cd quickstart
# 拉取并设置主题
git init
git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
echo theme = \"ananke\" >> config.toml
# 创建一篇新文章
hugo new posts/my-first-post.md
# 构建静态网站文件,并启动 HTTP 服务
hugo server -D
执行完上述操作,就可以通过浏览器访问 http://localhost:1313/ 。可以看到自己刚创建的网站与第一篇文章:
2.2 推送到远端仓库
在 Coding 上创建一个项目,在项目下再创建一个仓库,默认参数即可(私有仓库)。这里会获得一个仓库地址:git@e.coding.net:panzhongxian/blog/quickstart.git
我仓库有多个 remote,一个是 Github,一个是 Gitee,一个 Coding。GitHub 在国内服务器访问速度差;而 Gitee 偶尔会出现故障;目前用的 Coding。
cd quickstart
git remote add origin git@e.coding.net:panzhongxian/blog/blog.git
git push -u origin "master"
2.3 页面中插入图片
以 Markdown 格式插入图片:
wget /images/landscape.png -qO static/landscape.png
echo '![img](/landscape.png)' >> ./content/posts/my-first-post.md
上边的操作分成了两步:先将图片放到了 static 目录下,然后在 Markdown 文件中指定图片,这里是要切掉 static 目录的。
[DIY] 文末提供了一个简单的工具处理图片:使用 Typora 工具可以随便贴图,通过该工具可以将所有图片复制或下载到指定的目录,而无需手动一张张处理。
2.4 使用 HTML 标签
Hugo 使用的 Markdown 引擎是 Goldmark,它默认是不会渲染原始的 HTML 语句的。需要执行在配置文件中打开一个开关:
[markup]
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = true
插入网页图片 + HTML 宽度设置为 50%:
echo '<img src="/images/wxmp/qrcode-search.png" alt="qrcode-search" style="width:50%;" />' >> ./content/posts/my-first-post.md
操作完上边两种方式的图片插入,效果如下:
2.5 Hugo 文件组织形式简要说明
上边的Quick Start 和插入图片的介绍中,文件统一放在了 content/post 目录下,图片统一放在 static 目录下。这种组织方式不是很清晰,比如一篇博客中就可能有很多图片,如果多篇的文章的图片都放在一个目录下,就会很乱。
要理解 Hugo 只是一个静态的网站框架,如果你不指定特殊配置的情况下,他就像个文件系统,在服务器上创建文件夹,就只是对应到网址中的一层地址而已。我自己是这么安排的:
- 将中文日志放在 content/cn/log 中
- 将图片放在 static/images 目录中,而且每篇文章都会创建对应的目录
比如本文的地址是 content/cn/log/2022-10-19-build-personal-blog-step-by-step.md
,图片目录是 static/images/build-personal-blog-step-by-step/
。
三、部署方案
上一节实际已经在个人主机上搭建起了个人博客的服务,通过 http://127.0.0.1:1313 进行本地访问。本节介绍如何将这种服务进行部署,让所有人都能访问到。
我的博客是部署在【腾讯云】(2核2G 低至50元/1年) 的 CVM 上的,使用 HTTPs 服务访问。具体的进程和文件信息如下:
具体地:
- 使用 CVM 托管网站内容
- 使用 Git 进行文件同步
- 使用 Hugo 静态网站框架
- 使用 Envoy 作为 HTTPS 代理
- 使用 Docker 镜像管理 Hugo 和 Envoy 的部署
3.1 基本资源购置
这种部署方式,只需要在【腾讯云】 简单购置和配置就能满足需求。
购置 CVM:要让别人能够访问到你的博客内容,不一定非得自己租服务器,但是使用 CVM 是最灵活、最直接的方式。如果后期要执行个相关的脚本,找个远程机器做点别的事,也非常的方便。
购置了 CVM 会有外网 IP,通过外网 IP 我们可以 SSH 上去,后期如果部署了服务,也可以通过 IP 访问。
我之前有使用 CentOS 相关发行版的经历,所以这里的镜像也是选择的 CentOS。选择不同的操作系统,主要区别在包管理软件的使用上,比如是用 yum 还是用 apt-get。
购置域名:如果想被人记住,肯定不能每次都通过 IP 访问,而是需要申请域名。腾讯云上购置域名之后,需要进行实名认证。
如果要搭建自己的博客网站,还需要网站备案,这将耗费1个月左右的时间。备案完成后,会有一个网站备案号,需要在主页下方展示。但是网站备案并不影响你域名的解析和网站上线(不影响域名解析,但是影响访问,详见最后一小节分析)。
配置域名解析:配置 A
记录类型,将记录值修改为 CVM 外网地址,TTL 10 分钟,主机记录可以加个 www
和 @
两种。配置之后,过一会可以通过 nslookup
等指令进行域名解析,看能否解析出自己的地址。
申请免费 SSL 证书:直接通过 HTTP 是没有加密的明文访问,浏览器也会提示不安全。你是不是也不想让自己的网站看上去很 Low?那就为域名申请一个免费的证书。产生的免费证书有多种形式,我这里选择的 Nginx 使用的证书类型,后边配置 HTTPS 服务会用到。
3.2 安装 Git & Docker
前面介绍了,服务器上的文件同步使用 Git,这里也需要安装一下:
yum install git
为了避免后边迁移服务时因为平台差异带来麻烦,所以几个服务都是选择使用 Docker 进行部署。
在安装其他部署之前,就需要先安装 Docker Engine,可以参考官方文档-Install Docker Engine on CentOS,其他的操作系统也有相关的文档。
sudo yum install -y yum-utils
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin
3.3 Git 定时任务
存储用户凭证,并拉取网站仓库内容:
git config http.postBuffer 524288000
git config --global credential.helper store
git clone git@e.coding.net:panzhongxian/blog/blog.git
## 上边语句 Clone private 仓库需要输入用户名密码
cd blog
git pull
在命令行执行 crontab -e
增加每分钟定时任务:
*/1 * * * * cd /root/blog && git pull
如果确定不会上机器修改什么文件,完全使用 仓库中的内容,可以强制使用远程分支:
*/1 * * * * cd /root/blog && git fetch --all && git reset --hard origin/master
这样,一旦你在本地更新了博客文件,并 Push 到主分支上,服务器就会接近实时地拉取到最新内容。
3.4 部署 Hugo 容器
直接使用 Docker 部署 hugo,这里将容器内的 1313 端口映射到主机的 13131 端口。另外,需要手动指定 Blog 的目录(替换下边的 $(blog_path)
):
docker run --rm -v $(blog_path):/src -p 0.0.0.0:13131:1313 klakegg/hugo "server --bind=0.0.0.0"&
拉起服务后可以看看本机的 13131 端口是否正在监听。可以直接通过 {CVM 外网IP}:13131
进行一下访问测试。
3.5 部署 Envoy 容器
我这里用到的 HTTPS 网关选择了 Envoy,选择一个自己熟悉的即可。我将我的 Dockerfile 和配置文件上传到了仓库: https://github.com/panzhongxian/https-blog-envoy-docker
clone 下该仓库之后,需要先替换配置中的 your-site.cn
为你自己的域名,另外有几项配置我这里固定的,如果有需要,可以做手动修改:
- 指向的 Hugo 服务是配置的
13131
端口 - HTTPS 使用的
certificate_chain
命名为{域名}_bundle.pem
- HTTPS 使用的
private_key
命名为{域名}.key
最后启动时,指定存储证书文件的文件映射到容器内部的目录:
git clone git@github.com:panzhongxian/https-blog-envoy-docker.git
cd https-blog-envoy-docker
# 替换配置中的网址
sed -i "s/your-site.cn/panzhongxian.cn/g" envoy.yaml
# 构建镜像
docker build -t envoy:v1 .
# 运行容器
docker run -d --add-host host.docker.internal:host-gateway \
-v /etc/ssl/panzhongxian.cn_nginx:/etc/ssl/panzhongxian.cn \
-p 9901:9901 -p 443:443 envoy:v1
至此,外网用户已经可以通过 https://www.{你的域名}
访问你的网站了。
3.6 开机启动
CVM 不太容易直接毁掉,但是毕竟还是存在重启的风险。如果期望在重启 CVM 之后,原来拉起的服务能够自动拉起,则需要结合 Linux 的 systemctl
添加开机启动服务。
这里暂时不做具体介绍。
四、辅助工具
4.1 让引擎搜索到
每种静态网站框架都会提供 sitemap 的功能,sitemap 可以提交给主流的搜索引擎,让他们的用户能够搜索到。
配置 Hugo sitemap
在 config.toml 文件中添加以下配置,本地测试的话,会在 http://localhost:1313/sitemap.xml 中展示网站中有哪些页面:
[sitemap]
changefreq = 'daily'
filename = 'sitemap.xml'
priority = 0.5
在 Google / Baidu 中配置和效果
Google 搜索控制台网址是 https://search.google.com/search-console。首先要将自己的域名添加到搜索站点中。该过程中,需要证明这个网站是属于你的,是通过让你给域名 DNS 解析中添加一条特别的记录来实现的。这个过程按照各个搜索引擎的指引即可。
然后,通过【网站地图】页面可以提交我们的 sitemap URL。
添加上自己的网址之后,Google 稍后就会收录,用户搜索、展示、点击的效果也会在 Google 搜索控制台上展示:
Baidu 也有类似的页面进行配置,地址:https://ziyuan.baidu.com/dashboard/index。我博客的访问来源基本都是 Google 过来的,不知道我在百度的配置有什么问题,这里就不列举具体的操作了。
4.2 页面访问量统计
页面访问量统计工具的工作原理是:浏览器加载页面,执行页面的 Javascript 脚本,上报相关信息到网站访问量统计的服务。上报的信息包括用户的网址、地区、来源等信息。
看页面访问统计主要是能帮助你了解网站是否健康,哪些页面比较受欢迎,受众主要如何分布等,当然,知道有不少人访问自己的网站,也能带来一些成就感。
各个平台的统计工具类似,不用每个平台都加,不然会给用户加载带来负担。
我这里主要使用的是 https://tongji.baidu.com,可以按照他的指引,添加网站、将需要执行的脚本贴到 Hugo 的模板中即可。
Google 也有类似的服务叫谷歌分析,地址 https://analytics.google.com/。
4.3 自动处理图片路径工具
我平时使用 Typora 编写 Markdown 文档,通常有插入图片的需求,会直接贴进去。这带来了一个问题,图片往往是绝对路径、相对路径或者 HTTP URL 地址,在我正式发布之前需要将这些图片归档到 博客静态目录中,如前边所说,会放到 static/images/{特定文章相关命名文件夹}
。
我这里用 Python 自己实现了一个工具,可以自动化的下载、拷贝、替换 Markdown 中图片的内容,实现上边的操作。工具托管在 PyPI,使用和安装都非常简单。其中,--dst_md_file
选项如果省略,则默认为替换当前文件,有一定风险,建议产生新文件后比较,没问题再替换:
python3 -m pip install markdown-image-replacer
python3 -m markdown_image_replacer \
$HOME/quickstart/content/posts/my-first-post.md \
--src_img_root $HOME/quickstart/static \
--dst_md_file $HOME/quickstart/content/posts/my-first-post2.md \
--dst_img_root $HOME/quickstart/static \
--dst_img_rel_dir images/my-first-post
替换之后,可以查看 my-first-post2.md 文档中内容变化,以及 static 目录的变化。
工具还有很多需要完善的地方,有兴趣的话,可以提 PR 修改。https://github.com/panzhongxian/markdown_image_replacer
4.4 留言工具
国内留言小插件基本都被禁用了,目前只能使用 utterances 利用 GitHub ISSUE 的方式进行,国内用户也是无法使用的。
五、其他部署方式简介与比较
- 【腾讯云】手动搭建 WordPress 个人站点(Linux)https://cloud.tencent.com/document/product/213/8044
- 【腾讯云】对象存储(COS)托管静态网站 https://cloud.tencent.com/document/product/436/9512
- 【腾讯云】云函数(SCF) + API 网关实现 Web 静态页面托管 https://cloud.tencent.com/document/product/583/32996
- Github Pages https://pages.github.com/
- 个人主机部署
以上五种,我都有折腾过,下面来说说相比于我当前部署,这几种方式的限制:
- WordPress 搭建并不简单,依赖 MySQL 等服务,内容迁移到别的框架比较困难,不方便本地修改同步
- COS 则需要在本地产生好所有的文件上传 COS,这意味着在本地需要使用 Hugo 先构建出所有静态网页,然后使用 COS 工具上传
- SCF 本质是将一个 Docker 镜像在云上运行,有与COS类似的限制,需要本地先构建再发布。另外需要配置 API 网关触发 SCF
- GitHub Pages 最大的缺点是国内访问容易出问题
- 个人主机可以使用固定 IP(需要电话电信申请), 但是目前 80和 443 端口都无法开,使用其他端口不利于别人访问。另外固定IP只是一段时间固定,会偶尔变更,因此还需要创建脚本及时更新 DNS 解析。 费电不稳定。
六、网站不备案是否能够访问?
之前的文章被选入了看一看精选,其中关于备案的问题,没有做详细的说明。本文先回答网站不备案是否能够访问,主要篇幅介绍背后隐含的网络知识。
结论先行-国内服务不备案不能访问
如果服务器在国内,比如从腾讯云、阿里云购置的CVM,是必须备案的。两年前搭建博客网站的时候,在未备案阶段还可以访问。最近(2023年1月)刚购买一个新域名,发现如果不备案,HTTP(特指80端口) 和 HTTPS(特指443端口) 两种方式都是无法访问的。
根据国务院令第292号《互联网信息服务管理办法》和《非经营性互联网信息服务备案管理办法》规定,国家对经营性互联网信息服务实行许可制度,对非经营性互联网信息服务实行备案制度。未获取许可或者未履行备案手续的,不得从事互联网信息服务,否则属于违法行为。 – From 腾讯云
接下来通过三个问答的方式,来介绍一下**“不备案无法访问”背后的网络知识**。
Q1: 为什么无法通过未备案的域名进行访问?
从浏览器到服务器之间,会经过不少路由器/交换机,比如云服务商为了符合国家规定,可以在自己的网关入口做一些设置,如果检测到 HTTP/HTTPS 请求,是可以配置一些网络策略,以达到终止的目的的。
从服务端抓包(下边附图),可以清楚地看到:
- 未备案的域名 DNS 是没有问题,因为一个域名不光是 HTTP 服务,你也可以启用别的服务
- 网关在检测到未备案的域名 HTTP 请求会给 Server 发 RST,这会让 Server 主动关闭连接
- 网关对于已经备案的域名 HTTP 请求会正常转发
Q2: 为什么访问未备案域名站点,使用 HTTP/HTTPS 显示不同界面?
以下两张图中,上图是通过 HTTP 协议访问未备案域名的网站(跳转到腾讯云网站),下图是通过 HTTPS 协议访问的结果(ERR_CONNECTION_CLOSED):
我这里从客户端抓包来解释这一现象:
- HTTP 请求和返回都是明文的。网关在收到 HTTP 请求之后,检测到 Host 是未注册的域名,会直接回复给 Client 一个的 HTTP 回包(Client 会以为是 Server 回复的),302 让用户跳转到腾讯云的提示页面
- HTTPS 是建立在 TLS/SSL 之上的 HTTP,而 TLS 的握手是需要服务端私钥的,这个是云服务商无法得到的,所以网关无法跟 Client 握手成功,更不可能在这之上回复一个 HTTP 的返回,所以就索性(也只能)直接关闭连接,页面上会显示连接被关闭
Q3: 为什么要用 HTTPS 建站?
上边已经简单介绍了 HTTPS 工作原理:使用 SSL/TLS 协议对通信进行加密传输 HTTP 协议。从而防止假冒。通过上边的抓包和分析,你大概也能理解以下几点客户端和服务端都应该选择 HTTPS 的理由:
- 服务端提供 HTTPS 服务,在浏览器是可以显示是安全的,让用户觉着这个网站更易信赖
- HTTPS 数据在传输的两个方向上都会得到加密,请求和返回中的敏感数据或个人数据不会在传输中被泄漏
- SSL/TLS 还可确认网站服务器是其真实身份,从而防止DNS劫持攻击(假冒网站)