Hexo-自定义部署
Hexo-自定义部署
1. 多Page仓库部署
在 Hexo-初始化静态网站 中提到,GitHub 允许一个账号拥有 1 个 账号 Page 和多个 项目 Page,因此可以利用多个项目 Page 达到分内容、分域名、冗余部署等。
开启项目 Page 的前提是已经开启了账号 Page,确保可用默认域名
[username].github.io
访问账号 Page。
1.1 开启项目Page
新建一个 GitHub 仓库用作项目 Page,GitHub 对项目 Page 的仓库名没有限制。
新建完成后需要初始化仓库以及提交一个初始 Commit,以一个简单的 HTML 源文件为例,在本地创建如下代码并上传到项目 Page 仓库的 main
分支中:
1 | <html> |
在项目 Page 仓库的 Settings - Pages
中的部署源选项 Source
选择 main
分支,则项目 Page 就已经建立好了,默认情况下这个项目 Page 的访问域名为:[username].github.io/DemoPage
。
1.2 自定义域名
GitHub Page 设置域名的方式是在 Page 仓库根目录下创建一个没有后缀的文件 CNAME
,并在文件中写入一行(且只能有一行)需要自定义的域名,例如:
1 | xxx.com |
然后在 DNS 添加一条 CName记录,让 xxx.com
指向 [username].github.io
即可。需要注意的是,CNAME
文件中指定的是纯域名,不携带任何协议网络(例如 http
、https
等)。
假设账号 Page 设置了自定义域名
xxx.com
、项目 Page 的仓库名为DemoPage
。
默认情况下,项目 Page 的访问路径是作为账号 Page 的子路径访问的,则项目 Page 的访问路径会自动设置为 xxx.com/DemoPage
。
如果想要用自定义域名访问,只需要在 DemoPage 仓库根目录下创建 CNAME
文件,并在其中写入对应的域名。
- 作为子域名访问时,写入例如:
demo.xxx.com
- 作为独立的顶级域名访问时,写入例如:
yyy.com
然后在对应域名的 DNS 中添加一条 CName 记录,同样指向账号 Page 的默认域名 [username].github.io
,GitHub 会自动判断请求的域名是否指向了某个 Page,如果是则自动转发。
2. 云服务器部署
考虑到 GitHub Page 在国内访问不太稳定,受不同运营商、不同地域的影响差别很大,因此也希望能部署到云服务器上。
2.1 搭建云服务器
云服务器按照个人需求选择即可。
以腾讯云轻量应用服务器 + Ubuntu Server 18 LTS 为例。
2.1.1 准备部署环境
- 确保服务器已经可用,且具有公网 IP,允许公网访问。
- 确保本地已有可用 SSH 密钥。
- 服务器绑定 SSH 公钥,用于远端部署。
2.1.2 配置Ubuntu默认账号
腾讯云轻量应用服务器的默认账号为 ubuntu
,如果创建服务器实例时已经设置了该账号的密码则可以跳过。否则需要通过「重置密码」设置,注意设置时选择用户名 系统默认 ubuntu
。
2.1.3 登录服务器
腾讯云轻量应用服务器在选择 Ubuntu 镜像时,默认会创建一个用户 lighthouse
,并且腾讯云默认禁用了 root 用户,尽管可以通过配置开启,但远端登录时仍有许多问题。因此建议切换为标准的默认用户 ubuntu
,否则后续需要多次调整权限。
网页端控制台可以在登录后通过 su ubuntu
切换至 ubuntu
账号,远端登录时可以通过 SSH 连接直接指定登录用户:
1 | // [username] 即为需要登录的用户,前提是该用户已创建。 |
(1)如果首次从本地远端登录,可能会提示密钥交换信息:
1 | The authenticity of host 'XXX.XXX.XXX.XXX (XXX.XXX.XXX.XXX)' can't be established. |
输入 yes
继续即可。
(2)如果曾经连接过,但是服务器重置了或者公网 IP 有变更,则远端登录时可能提示错误:
1 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
根据提示编辑 ~/.ssh/known_hosts
文件,删除曾经保存的服务器 IP 对应的 Host 信息:
1 | // 删除公网 IP 对应的这条 Host 记录并保存 |
然后再重新登录即可。
2.2 配置云服务器
配置云服务器有两种方式:
- 使用默认 ubuntu 用户 +
sudo
操作,但某些脚本可能需要单独设置 root 权限。 - 使用 root 用户配置,但远端使用 SSH 鉴权 Push 时也需要指定为 root 用户,可能会有安全性问题,不推荐。
结合上述问题,本文以第 2 种方式为例,使用 root 用户配置服务器,但禁用远端登录 root 用户,配置完后再对远端 Push 涉及的目录设置为 ubuntu 用户权限。
2.2.1 开启Root用户
腾讯云轻量应用服务器默认禁用了 root 用户,需要先以普通用户(例如 ubuntu)登录后再手动开启:
1 | // 1. 设置 root 用户密码,可以与其他用户的相同: |
断开远端连接重新登录;如果允许直接通过密码远端登录 root 用户,则可以直接通过 ssh root@[server ip]
登录。本文为了安全性保持禁用 root 用户直接密码登录,因此仍需先以 ubuntu 用户登录,然后再切换至 root 用户:
1 | // 从本地登录至远端 ubuntu 用户: |
2.2.2 安装环境
在云服务器部署静态网站,主要需要用到两个开发环境:
- Nginx: 用于自动把公网请求映射到具体的静态网页资源。
- Git: 用于从本地推送静态网站资源。
腾讯云轻量应用服务器的 Ubuntu 系统默认自带了 Git 环境,因此只需要安装 Nginx:
1 | // 更新 apt: |
Nginx 常用命令:
- 启动服务:
service nginx start
- 停止服务:
service nginx stop
- 重启服务:
service nginx restart
- 刷新配置:
nginx -s reload
(需要先启动 Nginx 服务)注意:启动服务并不会自动读取最新配置,每次修改 Nginx 配置后都需要刷新配置才能生效。
2.2.3 配置网站目录
采用 Nginx + Git 托管静态网站需要用到两个目录:
- Nginx 最终代理和转发的网站资源文件目录。
- Git 接收远端 Push 网站资源文件的 Git 仓库目录。通过配置 Git Hook 将收到的文件自动关联到 Ngnix 的资源文件目录。
(1)创建 Nginx 网站资源目录并授权;
1 | 实际目录可自定义: |
(2)创建 Git 接收裸仓库的目录并授权;
1 | 注意创建的是裸仓库,添加 --bare 参数 |
Git 裸仓库是指该仓库将只保存仓库的提交历史(
.git
文件),而不会保存实际文件,但同样支持 Push 和 Pull,通常作为服务器存储仓库。
(3)配置 Nginx 代理和转发目录(注意备份);
1 | sudo vim /etc/nginx/sites-available/default |
修改 server 部分
1 | server { |
然后重启 Nginx 服务:
1 | sudo service nginx restart |
2.2.4 验证网站访问性
(1)在 WebServer
目录下创建一个测试网页:
1 | vim /xxx/WebServer/index.html |
随便填充一个元素:
1 | <html> |
然后访问公网 IP,如果可以正常访问说明配置正确;如果网页报 404 错误,则需要查看 Nginx 错误日志。
(2)Nginx 日志存放目录配置在 Nginx 配置文件中:
1 | sudo vim /etc/nginx/nginx.conf |
找到 http 部分的 Logging 设置:
1 | http { |
然后查看错误日志:
1 | sudo vim /var/log/nginx/error.log |
错误日志提示发生了 403 权限拒绝问题。
(3)查看 Nginx 的运行和启动用户:
1 | sudo ps aux | grep "nginx: worker process" | awk '{print $1}' |
发现 Nginx 启动用户是 root,但运行用户是 www-data,所以出现了用户权限被拒绝的情况。
(4)将 Nginx 的运行用户也修改为 root 用户:
1 | sudo vim /etc/nginx/nginx.conf |
将 user 修改为 root 用户:
1 | user root; |
然后重启 Nginx 服务:
1 | sudo service nginx restart |
再次访问云服务器的公网 IP,可以正确访问了。
2.2.5 创建Git-Hook
由于 Nginx 只能将网络请求代理和转发到 WebServer
目录下的网站资源文件,而本地的网站资源文件是通过 Git Push 到 Web.git
仓库下的,因此需要创建一个 Git-Hook,自动将 Web.git
下的资源文件关联到 WebServer
目录中:
(1)创建一个 Hook 文件 post-receive
:
1 | vim /xxx/Web.git/hooks/post-receive |
(2)在 post-receive
中写入一行:
1 | git --work-tree=/xxx/WebServer --git-dir=/xxx/Web.git checkout -f |
(3)保存退出后,给 post-receive
文件添加执行权限:
1 | chmod +x /xxx/Web.git/hooks/post-receive |
2.3 本地Push至云服务器
经过上述配置,服务器中的 Git 接收和 Nginx 代理转发已经配置好了,即可从本地将 Hexo 生成的静态网站 Push 至服务器。
(1)在 Hexo 站点配置文件中添加云服务器 Git 部署配置:
1 | deploy: |
(2)本地执行 Hexo 命令部署:
1 | hexo clean && hexo g -d |
有可能报错 unable to create temporary object directory
:
1 | error: remote unpack failed: unable to create temporary object directory |
也有可能报错 Permission denied
:
1 | error: unable to create file xxx: Permission denied |
向云服务器 Push 时报错,如果原因为 unable to create
、unable to access
、permission denied
等,绝大多数是因为 Git Push 的用户没有目标路径的权限导致的,在本例中,WebServer
或 Web.git
所在目录、以及其内所有子文件都是 root 用户权限,而云服务器为了安全禁用了远端登录 root 用户,所以 Git 在以 ubuntu 用户 Push 时就会导致权限不足。
(3)将云服务器中 WebServer
和 Web.git
所在目录、以及内部递归所有子文件都改为 ubuntu 用户权限:
需要修改 WebServer、Web.git、以及各自所在的所有父级目录。
1 | 递归修改该目录及其内部所有子文件的权限, |
(4)再次查看文件权限,确保上述各个目录均可被 ubuntu 用户访问:
1 | ll |
(5)再次从本地执行 Hexo 命令,即可成功部署。
2.4 GitHub-Page开启HTTPS
GitHub Page 只需要确保域名已绑定 SSL 证书,然后在 Page 仓库的 Settings - Pages
中勾选 Enforce HTTPS
即可。
2.5 云服务器开启HTTPS
云服务器开启 HTTPS 需要自行申请 SSL 证书:
- 使用域名提供商颁发的证书:
- 通常有免费证书(一般有效期最大 90 天),不支持原证书续签,每次都要重新申请后手动替换证书公私钥;
- 购买付费证书;
- 使用 LetsEncrypt 颁发的免费证书(有效期 90 天),可以直接在服务器使用脚本配置:
- 由于地区限制,
.cn
顶域只能通过 DNS 的方式验证,因此域名过期后只能重新申请证书并手动更新 DNS 验证,但无需替换公私钥文件(Certbot 会用新证书覆盖); - 其他国际域名可通过 HTTP + WebRoot 的方式自动验证域名,可以配合
crontab
实现自动续签;
- 由于地区限制,
注意:
- 根域名(顶级域名,如
example.com
)和 子域名/泛域名(如xxx.example.com
)需要分别申请各自的证书。 - 使用 LetsEncrypt 颁发的证书时,如果服务器在大陆境内则需要先为服务器备案。
- Nginx 配置 SSL 需要证书的公钥和私钥。
2.5.1 从域名提供商获取SSL证书
如果使用域名提供商的证书(不论 免费/付费),优先选择标明了 Nginx 场景的证书,或者下载包含 pem 文件
、crt 文件
、key 文件
的证书:
- 公钥:
domain.crt
或domain.pem
文件; - 私钥:
domain.key
文件;
然后将证书从本地上传到远端:
1 | 在本地终端执行以下代码: |
注意,这一步命令可能会遇到报错 Permission denied (publickey)
,则检查以下配置:
检查目标路径的用户权限,例如服务器当前用户是 ubuntu,但
/DemoDir
这个目录可能是 root 用户权限,解决办法为:- scp 命令改为指定 root 用户登录服务器,但通常服务器为了安全会禁用 root 用户的远端登录,因此不推荐。
- scp 目标路径临时选择一个 ubuntu 用户有权限的目录,然后登录到服务器后再移动到其他目录,此时可以自由切换 root 用户了。
检查服务器用户的 SSH 配置中是否绑定了本地公钥:
- 查看服务器的
~/.ssh/authorized_keys
文件,是否包含本地~/.ssh/id_rsa.pub
公钥内容。 - 如果没有,则将本地
id_rsa.pub
公钥内容追加到服务器authorized_keys
文件后(该文件可以包含多个公钥的内容)。
- 查看服务器的
2.5.2 LetsEncrypt证书+DNS校验
使用 LetsEncrypt 颁发的证书本质也是在服务器内下载证书,不过 LetsEncrypt 提供了 certbot
脚本,可以在服务器内完成:
(1)安装 Certbot:
参考:
- 在Ubuntu上使用Let's Encrypt配置Nginx SSL证书并自动更新
- 在Ubuntu上使用Certbot申请Let’s Encrypt SSL证书
- Ubuntu通过certbot手动配置Let's Encrypt SSL泛型域名证书
- certbot 常用操作
Ubuntu 下默认没有 yum 源:
Apache 服务器可以参考:
其他 OS 可以使用 yum 安装,参考:
- CertBot自动更新Nginx证书
- centos 7 nginx 配置Let's Encrypt证书,并自动更新
- 使用CertBot自动更新Nginx的ssl证书
- Linux 下 Nginx 安装部署 Let’s Encrypt 证书实现 HTTPS
使用 Docker 可以参考:
1 | sudo apt-get install certbot |
(2)为根域名 example.com
和 泛域名 *.example.com
同时申请证书:
1 | sudo certbot certonly -d example.com -d *.example.com --manual --preferred-challenges dns |
LetsEncrypt 会询问是否接受服务器 IP 被记录,必须接受才能继续申请:
1 | Saving debug log to /var/log/letsencrypt/letsencrypt.log |
输入 Y
后 LetsEncrypt 会分别为根域名和泛域名生成 DNS 的 TXT 记录用于校验,需要手动将其添加到域名 DNS 解析中:
1 | (Y)es/(N)o: Y |
(3)在下一步之前,确保已经在 DNS 解析中为根域名和泛域名分别添加了一条 TXT 记录
(4)输入回车继续,LetsEncrypt 会通过 DNS 校验域名的合法性,稍等片刻校验通过后将会自动下载证书到本地:
1 | Waiting for verification... |
其中:
/etc/letsencrypt/live/example.com/fullchain.pem
为公钥/etc/letsencrypt/live/example.com/privkey.pem
为私钥
(5)使用 DNS 校验的证书续签时需要重新执行一遍申请流程,但由于证书文件路径不变(新证书将覆盖旧证书),只需要重新使用 Certbot 生成新的校验 Key 并更新到 DNS 即可。
国内域名只能使用 DNS 校验,其他校验方式会报错(参考:Let's encrypt的TLS-SNI安全问题):
1 Client with the currently selected authenticator does not support any combination of challenges that will satisfy the CA. You may need to use an authenticator plugin that can do challenges over DNS.但 Github 上有不少工具可以通过 DNS 服务商提供的 API 接口实现续签时自动化更新 DNS 记录,例如腾讯云:
查看所有 certbot
申请的证书的有效期:
1 | sudo certbot certificates |
2.5.3 LetsEncrypt证书+HTTP校验
(1)指定验证方式为 http:
1 | sudo certbot certonly --preferred-challenges http -d example.com |
(2)三种方式任选一项即可:
1 | Saving debug log to /var/log/letsencrypt/letsencrypt.log |
(3)其中,方式 1(nginx)和方式 2(standalone)均为全自动完成,而方式 3(webroot)则需要指定一个目录;在证书续期(renew)时,LetsEncrypt 会在该目录下存放一个临时文件,并告知服务器通过访问域名的一个特定路径来验证是否可以读取到临时文件,因此这种方式还需要配置 Nginx,确保 LetsEncrypt 通过 HTTP 请求验证域名时可以被正确路由到该目录。
(4)以方式 3(WebRoot)为例,按要求指定 WebRoot 的路径:
1 | Plugins selected: Authenticator webroot, Installer None |
其中:
/etc/letsencrypt/live/example.com/fullchain.pem
为公钥/etc/letsencrypt/live/example.com/privkey.pem
为私钥
(5)使用 certbot
手动更新证书
1 | 更新系统上所有 certbot 申请的证书: |
如果 renew
命令没有报错,就可以使用 crontab
周期性执行 renew
进行自动续签了。
(6)使用 crontab
定期自动更新证书
查看已配置的定期任务:
1 | sudo cat /etc/crontab |
配置定期任务:
1 | sudo crontab -e |
在打开的编辑器中修改定期时间,并保存退出(以下配置表示每月 1 日执行 certbot
更新证书):
可以使用
sudo certbot renew --dry-run
测试证书更新流程是否能正确执行。
1 | 0 0 1 * * /usr/bin/certbot renew --quiet |
2.5.4 将SSL公私钥配置到Nginx
由于云服务器采用 Nginx 转发请求,因此需要将 SSL 证书的公私钥配置到 Nginx 中。
如果使用 root 用户运行 Nginx,则需要切换至 root 用户再配置。
(1)去除(注释掉)Nginx 默认 Server 配置 /etc/nginx/sites-available/default
(注意备份):
1 | server { |
(2)修改 Nginx 转发配置 /etc/nginx/nginx.conf
(注意备份),参考以下部分按实际情况修改:
1 | user root; |
注意以上 ssl_certificate
和 ssl_certificate_key
需要分别替换为之前获取到的 SSL 证书的公钥和私钥。
(3)验证 Nginx 配置并重启服务:
1 | sudo nginx -t |
2.6 验证网站访问
- 以 HTTP 协议访问云服务器公网 IP:报错 444.
- 以 HTTPS 协议访问云服务器公网 IP:报错 444。
- 以 HTTP 协议访问自定义域名:自动转发至 HTTPS。
- 以 HTTPS 协议访问自定义域名:正常访问。
如果以上均验证成功,则网站已经正常部署。
2.7 常见异常
如果访问服务器时报错 403,先按上文所述查看 Nginx 的报错日志:
1 | vim /var/log/nginx/error.log |
(1)directory index of "..." is forbidden
1 | yyyy/MM/dd hh:mm:ss [error] 28043#28043: *6 directory index of "/home/ubuntu/Projects/WebServer/" is forbidden, client: XXX.XXX.XXX.XXX, server: XXX.YYY, request: "GET / HTTP/1.1", host: "XXX.YYY" |
首先检查 Git Push 是否成功,Hexo 在 Deploy 时,一旦某个文件 Push 失败则 Git 会中断 Push 任务,但只要 Push 任务结束 Hexo 就会提示 INFO Deploy done: git
,所以看到这个提示并不一定代表 Push 是成功的,需要检查 Deploy 日志,确保没有提示任何文件写入失败。如果有,大概率是权限问题导致的,可参照上文配置;如果没有,则需要检查 Nginx 配置:
1 | vim /etc/nginx/nginx.conf |