
工作流
目前我的工作流是:本地hexo d推送部署到github上,然后通过github action部署到github pages 和vercel上。同时通过脚本将public文件夹下的静态文件同步推送到我的ECS服务器上。
也就是说,可以通过三个域名访问到我的博客,一个是https://lloyd-kai.github.io/ 源站点,一个是https://blog.lloydkai.top/ 部署到vercel上的(我已解析为自己购买的域名),一个是https://lloydkai.cn/ 部署到阿里云的ECS上的(方便国内的用户访问)。
部署时参考的教程
部署到vercel上
零成本免备案部署博客——vercel与zeabur搭建hexo主题博客 , 第2期:Vercel部署并绑定自定义域名+安装Butterfly主题 。这两个视频互相补充,主要参考第二个视频即可部署成功
部署到gihub的pages上
请看hexo博客系列(二)博客创建指南 ,严格来说我不是将hexo博客项目中所有的内容推送到github的,而是用hexo-deployer-git插件在本地生成public下的html文件,再推送到github pages上的。所以你会看到其他的的教程中它是先将整个hexo项目先推送到github,然后再将分支推送到github pages上,而我只上传必要的文件到github pages上,所有的文件都在我的本地电脑里面。主要的参考教程:Fomalhaut的博客,核心就在于是否下载了hexo-deployer-git插件以及配置ssh密钥等。
官方hexo-deployer-git教程
部署到阿里云 ECS 上
主要参考的教程:链接
以下是根据我自己的实际使用情况修改,我使用的是阿里云 ECS,系统为 Ubuntu。
1. 连接阿里云服务器
没有上传 SSH 密钥之前,可以先用下面的命令登录,再输入密码:
1
| ssh -l root 你的ECS公网IP -p 22
|
如果后面已经配置好了 SSH 密钥,也可以直接使用:
2. 设置阿里云防火墙与安全组
这里最容易出问题的是 SSH 的来源 IP 白名单。我的电脑当前出口公网 IP 会变化,所以如果你开了代理或切换了网络,安全组里的放行地址也要一起更新。
如果你发现把 SSH 安全组临时放开为 0.0.0.0/0 就能连上,那基本可以确定问题出在“来源 IP 不匹配”,而不是 ECS 本身坏了。
3. 配置 Nginx 启用 HTTPS
Nginx 这里我只保留静态站点的基本配置,目录直接指向 /var/www/html/:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| server { listen 80 default_server; listen [::]:80 default_server; server_name _;
root /var/www/html; index index.html;
location / { } }
server { listen 443 ssl; listen [::]:443 ssl; server_name 你的域名 www.你的域名; ssl_certificate /etc/nginx/ssl/你的域名.pem; ssl_certificate_key /etc/nginx/ssl/你的域名.key; root /var/www/html; index index.html;
location / { } }
|
我这里不再使用 GitHub Actions 来同步 ECS,而是改成 Hexo 的 deployAfter 钩子执行脚本上传。
4. 自定义 ECS 部署脚本
我的实现思路是:
hexo d 完成 Git 部署后,再自动执行脚本上传 public/*
- 脚本先获取当前电脑的出口公网 IP,并在失败时提示去检查安全组来源地址
- 上传后自动修复目录与文件权限,避免 Nginx 读取静态资源异常
- Nginx 直接托管
/var/www/html/ 的静态文件
这样就不需要 GitHub Actions 参与 ECS 同步了,但 hexo d 会多执行一步上传,所以整体会稍慢一些。
首先,在 _config.yml 文件中添加配置:
1 2 3 4 5 6 7
| ecs_deploy: enable: true host: 你的ECS公网IP user: root target: /var/www/html/
|
然后在 Hexo 项目根目录下新建脚本 scripts/deploy-ecs.js。
这里我是使用https://cip.cc/ 网站查询本机真正的IP,因为我的IP是不定的,所以需要查找确认,如果你嫌麻烦可以直接在服务器的安全组里面的SSH中填写IP为0.0.0.0/0 也就是任何IP都可连接。
这个脚本的核心逻辑是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| 'use strict';
const { spawnSync } = require('child_process'); const https = require('https');
const PUBLIC_IP_CHECK_URL = 'https://cip.cc/';
function getPublicIp() { return new Promise((resolve) => { const request = https.get(PUBLIC_IP_CHECK_URL, (response) => { if (response.statusCode !== 200) { response.resume(); resolve(null); return; }
let body = ''; response.setEncoding('utf8'); response.on('data', (chunk) => { body += chunk; }); response.on('end', () => { const match = body.match(/(?:查询\s*IP|IP)\s*[::]\s*([0-9]{1,3}(?:\.[0-9]{1,3}){3})/i); resolve(match ? match[1] : null); }); });
request.setTimeout(5000, () => { request.destroy(); resolve(null); });
request.on('error', () => resolve(null)); }); }
function runOrThrow(command, errorMessage) { const result = spawnSync(command, { shell: true, stdio: 'inherit', cwd: process.cwd() });
if (result.status !== 0) { throw new Error(`${errorMessage} (exit code ${result.status})`); } }
function logConnectionHints(host, port) { hexo.log.warn(`[ecs-deploy] 连接失败:${host}:${port}`); hexo.log.warn('[ecs-deploy] 请打开 https://cip.cc/ 查询当前电脑出口公网 IP'); hexo.log.warn('[ecs-deploy] 请先确认 ECS 的公网 IP 是否发生变化'); hexo.log.warn('[ecs-deploy] 请确认安全组和服务器防火墙已经放行 SSH 端口'); hexo.log.warn('[ecs-deploy] 如果 SSH 端口不是 22,请在 _config.yml 中更新 ecs_deploy.port'); }
hexo.on('deployAfter', async function () { const cfg = hexo.config.ecs_deploy || {}; if (cfg.enable === false) { hexo.log.info('[ecs-deploy] disabled by config'); return; }
const host = cfg.host; const user = cfg.user || 'root'; const port = cfg.port || 22; const target = cfg.target || '/var/www/html/'; const sourceIp = cfg.source_ip || cfg.whitelist_ip || cfg.allowed_source_ip || '';
if (!host) { hexo.log.warn('[ecs-deploy] skip: missing ecs_deploy.host in _config.yml'); return; }
const currentPublicIp = await getPublicIp();
const source = 'public/*'; const destination = `${user}@${host}:${target}`; const scpCommand = `scp -P ${port} -r ${source} ${destination}`; const remoteFixPermsCommand = `ssh -p ${port} ${user}@${host} "find ${target} -type d -exec chmod 755 {} \\; ; find ${target} -type f -exec chmod 644 {} \\;"`;
if (currentPublicIp) { hexo.log.info(`[ecs-deploy] 正在通过 scp 上传:来源IP ${currentPublicIp} -> 目标服务器 ${destination}`); } else { hexo.log.info(`[ecs-deploy] 正在通过 scp 上传:来源IP 未获取到 -> 目标服务器 ${destination}`); }
try { runOrThrow(scpCommand, '[ecs-deploy] scp 上传失败'); } catch (error) { if (currentPublicIp && sourceIp) { hexo.log.warn(`[ecs-deploy] 当前电脑出口公网 IP: ${currentPublicIp}`); if (currentPublicIp !== sourceIp.replace(/\/\d+$/, '')) { hexo.log.warn('[ecs-deploy] 两者不一致,请到阿里云安全组中修改允许访问 SSH 的来源 IP'); } } else if (currentPublicIp) { hexo.log.warn(`[ecs-deploy] 当前电脑出口公网 IP: ${currentPublicIp}`); } else if (sourceIp) { hexo.log.warn(`[ecs-deploy] 安全组允许的来源 IP: ${sourceIp}`); hexo.log.warn('[ecs-deploy] 当前出口公网 IP 获取失败,请手动确认本机/VPN 的外网 IP 是否与安全组一致'); } logConnectionHints(host, port); throw error; }
hexo.log.info('[ecs-deploy] fixing file permissions for nginx'); try { runOrThrow(remoteFixPermsCommand, '[ecs-deploy] 远端权限修复失败'); } catch (error) { hexo.log.warn('[ecs-deploy] SSH 已连接,但远端权限修复失败'); hexo.log.warn('[ecs-deploy] 请检查远端目标路径是否存在、目录权限是否正确'); throw error; }
hexo.log.info('[ecs-deploy] upload finished'); });
|
为什么要做权限修复:
- 某些环境下,
scp 上传后目录权限可能变得过于严格
- 如果 Nginx 无法读取
css、js、font 等资源目录,就会出现样式缺失或者页面和本地预览不一致的问题
以后只需要正常在终端里输入下面的命令即可:
它会先完成 Hexo 的常规部署,再把生成的 public 目录同步到 ECS 上。
自动续签SSL证书
参考链接 ,使用了Let’s Encrypt自动续签,建议使用阿里云,每年免费额度有20个证书,有效时间为三个月,基本够用了。
SSL证书其实就是安全性的问题,如果没有签的话在访问网站的时候,浏览器就会显示
您与此网站之间建立的连接不安全,请勿在此网站上输入任何敏感信息(例如密码或信用卡信息),因为攻击者可能会盗取这些信息。 了解详情
自动续签时间是60天一次,一年大概就6个。如果需要就自己修改时间。
如何备案
官方文档
总体来说流程有五个——填表申请、阿里云初审、短信验证、管理审核、ICP出结果和公安备案,ICP出结果之后就要马上将备案信息添加到首页之下,包括公安的备案(出结果后30天内),如果不添加就会有罚款。
官方表述
ICP备案成功后,您需要在网站首页底部悬挂ICP备案号并生成链接指向工信部网站(https://beian.miit.gov.cn/),否则被相关部门核查出来,将会面临罚款。
参考资料
- 阿里云备案官方文档
- 链接