引言
之前,我一直在Github Pages上学习和搭建个人网站,它免费而且简单,但在国内访问速度非常慢。
最近偶然有台国内服务器,借此我决定自己搭建一个博客网站,换掉原来的Github Pages,在经历多方搜索和尝试后,我选择了静态网页生成器Hugo
。
实现效果
- 基于
Markdown
进行写作,通过Git
上传到远程仓库,服务器收到更新消息自动更新网站内容。 - 通过
https://example.com
访问网站。
先决条件
在开始前,准备好以下云资源:
- 云服务器,系统环境为Ubuntu,且有公网IP,如
11.4.5.14
,接下来称为<Your-Server-IP>
- 下文中提到的对外端口
80
443
10086
端口需要在服务器控制台设置允许外部访问
- 下文中提到的对外端口
- 域名,并可以访问其 DNS 管理控制台,如
example.com
要在国内服务器上部署站点,需要进行备案,否则无法通过域名访问;
如果服务器和DNS服务商都在海外,则可以不用备案。
部署流程
前言
在开始之前,先了解一下涉及到的几个工具:
Hugo
:一个快速的静态网页生成器,用于将Markdown
文件转换为静态网页。Git
:一个分布式版本控制系统,用于管理网站的源代码。Nginx
:一个高性能的HTTP
和反向代理服务器,用于部署网站。Webhook
:一个用于监听HTTP
请求的工具,用于接收Github
的Webhook
请求,触发网站更新。Go
:一个开源的编程语言,用于编写Webhook
服务。acme.sh
:一个用于申请免费SSL证书的工具。
本文环境:
- 服务器:Ubuntu 22.04 LTS (阿里云)
- 本地环境:Windows 11 Pro 24H2
- Hugo版本:v0.136.1 extended
- Nginx版本:1.24.0
- acme.sh版本:3.1.0
- Go版本:1.22.2
登录服务器
使用SSH
登录到服务器,可以直接使用PowerShell
(Windows),也可以使用PuTTY
等工具。
配置SSH密钥
在服务器生成SSH密钥对:
(如果是第一次生成,一路回车默认即可)
# 生成SSH密钥对,-C参数为注释,可填写自己的邮箱
ssh-keygen -t rsa -b 4096 -C "<your_email@mail.com>"
再将服务器公钥(默认~/.ssh/id_rsa.pub
)的内容添加到Github的SSH Keys中。
准备Github仓库
在Github上创建一个新的仓库,如my-hugo-site
,并将其克隆到服务器。
# 克隆仓库,需要修改为实际仓库地址
git clone git@github.com:<your_username>/my-hugo-site.git
配置Hugo
Hugo
提供了多种安装方式,具体可参考官方文档。
此处以v0.136.1
为例,通过wget
获取Hugo
的二进制版本,并解压主程序到/usr/local/bin
目录下。
# 下载hugo发布包
wget https://github.com/gohugoio/hugo/releases/download/v0.136.1/hugo_extended_0.136.1_linux-amd64.tar.gz
# 解压hugo发布包
tar -zxvf hugo_extended_0.136.1_linux-amd64.tar.gz
# 移动hugo主程序到/usr/local/bin目录
mv hugo /usr/local/bin
# 清理不需要的文件
rm hugo_extended_0.136.1_linux-amd64.tar.gz LICENSE README.md
# 验证安装
hugo version
进入刚才克隆的仓库目录,初始化Hugo
站点,然后创建一个新的文章。
cd my-hugo-site
hugo new site .
hugo new posts/my-first-post.md
查看content/posts/my-first-post.md
文件,该文件是一个Markdown
文件,开头的+++
之间是Front Matter
,用于配置文章的元数据。
编辑Front Matter
,将draft
字段改为false
,表示文章已经发布,并在后面编写文章内容,效果如下:
+++
date = '2024-10-18T09:29:58Z'
draft = false
title = 'My First Post'
+++
# Hello World!
This is my first post.
直接在本目录执行hugo
命令,将Markdown
文件转换为静态网页。
之后my-hugo-site/public
目录下会生成一些文件,其中包括index.html
文件,这是博客的网站首页。
部署Nginx
安装Nginx
,并配置my-hugo-site/public
目录为站点根目录。
# 安装Nginx
sudo apt update
sudo apt-get install nginx
编写一个Nginx
配置文件/etc/nginx/sites-available/my-hugo-site
,示例如下:
# 添加一个server块,用于访问博客网站
server {
listen 80;
listen 8080;
server_name example.com <Your-Server-IP>; # 需要修改为实际域名和服务器IP
location / {
root /path/to/my-hugo-site/public; # 需要修改为实际网站根目录
index index.html;
}
}
启用站点配置文件,并重启Nginx
服务。
sudo ln -s /etc/nginx/sites-available/my-hugo-site /etc/nginx/sites-enabled/
sudo systemctl restart nginx
现在访问http://<Your-Server-IP>
,应该可以看到博客首页。
配置Webhook
在服务器上配置Webhook
,监听Github目标仓库的push
事件,触发git pull
命令并进行自动构建,这样免去每次在更新仓库后手动在服务器执行构建的步骤。
先编写build.sh
脚本,示例如下:
#!/bin/bash
# 日志文件路径,需要修改为实际路径
LOGFILE="/srv/ghpg/log.build.log"
echo "====== Build Started: $(date) ======" >> "$LOGFILE"
# 切换到博客目录,需要修改为实际路径
cd /path/to/my-hugo-site || {
echo "!!!!! Error: Could not change directory !!!!!" >> "$LOGFILE"
exit 1
}
echo "Pulling latest changes..." >> "$LOGFILE"
git pull origin main
if [ $? -ne 0 ]; then
echo "!!!!! Error: Failed to pull latest changes !!!!!" >> "$LOGFILE"
exit 1
fi
echo "Running Hugo to build site..." >> "$LOGFILE"
/usr/local/bin/hugo --minify
if [ $? -ne 0 ]; then
echo "!!!!! Error: Hugo build failed !!!!!" >> "$LOGFILE"
exit 1
fi
echo "✓✓✓✓✓ Build completed: $(date) ✓✓✓✓✓" >> "$LOGFILE"
此处使用go
编写一个简单的Webhook
服务,监听/webhook
路径,接收POST
请求。
# 安装go
sudo apt update
sudo apt-get install golang-go
编写webhook.go
文件,示例如下:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os/exec"
)
type Payload struct {
Ref string `json:"ref"`
}
func handleWebhook(w http.ResponseWriter, r *http.Request) {
// 监听的分支,需要修改为实际分支,默认为main
var branch string = "refs/heads/main"
// 构建脚本路径,需要修改为实际路径
var build_sh = "/path/to/my-hugo-site/build.sh"
var payload Payload
json.NewDecoder(r.Body).Decode(&payload)
if payload.Ref == branch {
fmt.Fprintln(w, "Webhook received, processing...")
go func() {
log.Println("Starting build process...")
cmd := exec.Command("/bin/bash", build_sh)
err := cmd.Run()
if err != nil {
log.Printf("Build failed: %s\n", err.Error())
} else {
log.Println("Build completed successfully")
}
}()
} else {
fmt.Fprintln(w, "Not "+branch+" branch push, ignoring.")
}
}
func main() {
http.HandleFunc("/webhook", handleWebhook)
// 监听10085端口,需要修改为实际端口
log.Println("Starting server on :10085")
http.ListenAndServe(":10085", nil)
}
编译webhook.go
文件,生成可执行文件。
go build webhook.go -o webhook.x
将生成的webhook.x
文件注册为系统服务,使其在服务器启动时自动运行。
编写/etc/systemd/system/webhook-my-hugo-site.service
文件,示例如下:
[Unit]
Description=Webhook Service
After=network.target
[Service]
ExecStart=/path/to/my-hugo-site/webhook.x # 需要修改为实际Webhook.x可执行文件路径
Restart=always
User=root
Environment=GOPATH=/usr/local/go
Environment=PATH=/usr/local/go/bin:/usr/bin:/bin
[Install]
WantedBy=multi-user.target
在启动前,需要修改刚才的Nginx
配置文件/etc/nginx/sites-available/my-hugo-site
,添加一个server
块,用于接收Webhook
请求。
server {
listen 80;
listen 8080;
server_name example.com <Your-Server-IP>; # 需要修改为实际域名和服务器IP
location / {
root /path/to/my-hugo-site/public; # 需要修改为实际网站根目录
index index.html;
}
}
# 添加一个server块,用于接收Webhook请求
server {
listen 10086; # 添加Webhook监听端口,按需修改
server_name example.com <Your-Server-IP>; # 需要修改为实际域名和服务器IP
location / {
proxy_pass http://localhost:10085; # 需要修改为实际Webhook监听端口
}
}
启用服务并启动:
# 重载Daemon
sudo systemctl daemon-reload
# 启动服务
sudo systemctl enable webhook-my-hugo-site
sudo systemctl start webhook-my-hugo-site
# 查看服务状态
sudo systemctl status webhook-my-hugo-site
然后,在Github的my-hugo-site
仓库的Settings
中,找到Webhooks
,添加一个新的Webhook
,填写服务器的Webhook
地址,如http://<Your-Server-IP>:10086/webhook
,并选择application/json
格式。
现在,每次在Github上更新仓库后,服务器都会自动构建网站。
配置DNS和TLS
为了实现通过域名访问网站,需要在域名的DNS管理控制台中添加一条A
记录,将域名解析到服务器的公网IP。
如果你的服务器基于阿里云,可以参考阿里云DNS解析
为了实现HTTPS
访问,可以使用acme.sh
工具申请ZeroSSL
免费SSL证书。
# 安装acme.sh
curl https://get.acme.sh | sh
source ~/.bashrc
# 注册ZeroSSL申请邮箱
acme.sh --register-account -m <your_email@example.com>
# 申请并安装证书
sudo acme.sh --issue --standalone -d example.com -d www.example.com \
--key-file /etc/nginx/ssl/example.com.key \
--fullchain-file /etc/nginx/ssl/example.com.crt
编辑/etc/nginx/sites-available/my-hugo-site
文件,添加ssl_certificate
和ssl_certificate_key
字段。
server {
listen 80;
listen 8080;
server_name example.com <Your-Server-IP>; # 需要修改为实际域名和服务器IP
location / {
root /path/to/my-hugo-site/public; # 需要修改为实际网站根目录
index index.html;
}
listen 443 ssl; # 添加SSL监听端口
ssl_certificate /etc/nginx/ssl/example.com.crt; # 需要修改为实际证书路径
ssl_certificate_key /etc/nginx/ssl/example.com.key; # 需要修改为实际私钥路径
}
server {
listen 10086; # 添加Webhook监听端口,按需修改
server_name example.com <Your-Server-IP>; # 需要修改为实际域名和服务器IP
location / {
proxy_pass http://localhost:10085; # 需要修改为实际Webhook监听端口
}
}
重启Nginx
服务sudo systemctl restart nginx
。
现在,通过https://example.com
访问网站,应该可以看到已经使用HTTPS
加密。
若访问顺利,代表证书有效,可以继续配置Nginx
,添加HTTP
到HTTPS
的重定向。
编辑/etc/nginx/sites-available/my-hugo-site
文件,添加一个server
块,用于重定向。
server {
# 此处移除原先的80、8080端口监听
server_name example.com <Your-Server-IP>; # 需要修改为实际域名和服务器IP
location / {
root /path/to/my-hugo-site/public; # 需要修改为实际网站根目录
index index.html;
}
listen 443 ssl; # 添加SSL监听端口
ssl_certificate /etc/nginx/ssl/example.com.crt; # 需要修改为实际证书路径
ssl_certificate_key /etc/nginx/ssl/example.com.key; # 需要修改为实际私钥路径
}
# 添加一个server块,用于重定向
server {
listen 80;
server_name example.com <Your-Server-IP>; # 需要修改为实际域名和服务器IP
return 301 https://$host$request_uri;
}
server {
listen 10086; # 添加Webhook监听端口,按需修改
server_name example.com <Your-Server-IP>; # 需要修改为实际域名和服务器IP
location / {
proxy_pass http://localhost:10085; # 需要修改为实际Webhook监听端口
}
}
重启Nginx
服务sudo systemctl restart nginx
。
现在,通过http://example.com
访问网站,也会自动重定向到https://example.com
。
后续写作
在Windows上安装Hugo
的方法如下,打开PowerShell
,执行以下命令:
# 安装Scoop
iwr -useb get.scoop.sh | iex
# 安装Hugo
scoop install hugo-extended
# 验证安装
hugo version
在一切配置完成后,我们可以在自己的环境上也克隆my-hugo-site
仓库,安装Hugo
,用同样的方式新建文章然后通过Markdown
写作,之后用Git
提交到Github。
在编写中,可以通过hugo server
命令在本地环境预览网站效果。
结语
至此,我们已经完成了基于Hugo
的个人博客的搭建,实现了:
基于Markdown
在本地写作,通过Git
上传到远程仓库;远程仓库更新时触发Webhook
自动更新网站内容,通过Nginx
作为Web服务器,通过域名访问网站;通过acme.sh
申请ZeroSSl
免费SSL证书,实现HTTPS
访问。