今天把项目部署在了阿里云服务器上阿里云服务器300元优惠券,大体过程:

部署过程

1
2
3
主项目:Hexo + Butterfly
访问: https://fcmmm.xyz/

最终部署结构:

1
2
/www/wwwroot/fcmmm.xyz/
├── main/ # Hexo 主站 public/ 目录里的内容

Hexo 的静态构建产物默认生成在 public/,部署时应同步 public/ 里的内容,而不是整个 Hexo 源码目录。(Hexo) GitHub Actions 里使用 actions/checkout 是为了把仓库代码检出到工作区,后续步骤才能构建或同步文件。(GitHub) GitHub Secrets 用于保存 SSH 私钥、服务器 IP 等敏感信息。(GitHub Docs)


一、服务器目录准备

先用 root 登录服务器:

1
ssh root@114.215.125.28

进入服务器后执行:

1
mkdir -p /www/wwwroot/fcmmm.xyz/main

确认目录:

1
ls -la /www/wwwroot/fcmmm.xyz/

应该看到:

1
main/

二、创建 deploy 部署用户

仍然在服务器里执行:

1
id deploy || useradd -m -s /bin/bash deploy

创建 SSH 目录:

1
2
3
4
5
mkdir -p /home/deploy/.ssh
touch /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys

给网站目录权限:

1
2
chown -R deploy:deploy /www/wwwroot/fcmmm.xyz
chmod -R 755 /www/wwwroot/fcmmm.xyz

退出服务器:

1
exit

三、Windows 本地生成 SSH 密钥

在你的 Windows CMD 中执行:

1
ssh-keygen -t ed25519 -C "github-actions-deploy-xxx" -f github-actions-deploy-xxx

生成两个文件:

1
2
github-actions-deploy-xxx       # 私钥,放 GitHub Secrets
github-actions-deploy-xxx.pub # 公钥,放服务器

四、把公钥写入服务器

你是 Windows CMD,不能用 cat,要用 type

执行:

1
type github-actions-deploy-xxx.pub | ssh root@114.215.125.28 "cat >> /home/deploy/.ssh/authorized_keys && chown -R deploy:deploy /home/deploy/.ssh && chmod 700 /home/deploy/.ssh && chmod 600 /home/deploy/.ssh/authorized_keys"

然后测试:

1
ssh -i github-actions-deploy-xxx deploy@114.215.125.28

如果成功,会进入服务器,例如:

1
[deploy@xxx ~]$

如果还是:

1
Permission denied (publickey,gssapi-keyex,gssapi-with-mic)

说明密钥仍然没配置成功,需要检查:

1
2
cat /home/deploy/.ssh/authorized_keys
ls -la /home/deploy/.ssh

成功登录后退出:

1
exit

五、宝塔 Nginx 配置

进入宝塔面板:

1
网站 → fcmmm.xyz → 设置 → 配置文件

使用下面这份配置。它适配:

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
/www/wwwroot/fcmmm.xyz/main      → 主站
server
{
listen 80;
listen 443 ssl;
listen 443 quic;
http2 on;
http3 on;

server_name fcmmm.xyz www.fcmmm.xyz;

# 主站根目录:Hexo + Butterfly 构建后的 public/ 内容
root /www/wwwroot/fcmmm.xyz/main;
index index.html index.htm default.htm default.html;

# 宝塔扩展配置
include /www/server/panel/vhost/nginx/extension/fcmmm.xyz/*.conf;

# CERT-APPLY-CHECK--START
include /www/server/panel/vhost/nginx/well-known/fcmmm.xyz.conf;
# CERT-APPLY-CHECK--END

# SSL-START
# error_page 404/404.html;
ssl_certificate /www/server/panel/vhost/cert/fcmmm.xyz/fullchain.pem;
ssl_certificate_key /www/server/panel/vhost/cert/fcmmm.xyz/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_tickets on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000";
add_header Alt-Svc 'quic=":443"; h3=":443"; h3-29=":443"; h3-27=":443"; h3-25=":443"; h3-T050=":443"; h3-Q050=":443"; h3-Q049=":443"; h3-Q048=":443"; h3-Q046=":443"; h3-Q043=":443"';
quic_retry on;
quic_gso on;
ssl_early_data on;
error_page 497 https://$host$request_uri;
# SSL-END

# ERROR-PAGE-START
# error_page 404 /404.html;
# error_page 502 /502.html;
# ERROR-PAGE-END

# REWRITE-START
include /www/server/panel/vhost/rewrite/html_fcmmm.xyz.conf;
# REWRITE-END


# Hexo 主站
location / {
try_files $uri $uri/ /index.html;
}

# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|webp|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public";
try_files $uri =404;
}

# 禁止访问敏感文件
location ~* (\.user.ini|\.htaccess|\.htpasswd|\.env.*|\.project|\.bashrc|\.bash_profile|\.bash_logout|\.DS_Store|\.gitignore|\.gitattributes|LICENSE|README\.md|CLAUDE\.md|CHANGELOG\.md|CHANGELOG|CONTRIBUTING\.md|TODO\.md|FAQ\.md|composer\.json|composer\.lock|package(-lock)?\.json|yarn\.lock|pnpm-lock\.yaml|\.\w+~|\.swp|\.swo|\.bak(up)?|\.old|\.tmp|\.temp|\.log|\.sql(\.gz)?|docker-compose\.yml|docker\.env|Dockerfile|\.csproj|\.sln|Cargo\.toml|Cargo\.lock|go\.mod|go\.sum|phpunit\.xml|pom\.xml|build\.gradl|pyproject\.toml|requirements\.txt|application(-\w+)?\.(ya?ml|properties))$
{
return 404;
}

# 禁止访问敏感目录
location ~* /(\.git|\.svn|\.bzr|\.vscode|\.claude|\.idea|\.ssh|\.github|\.npm|\.yarn|\.pnpm|\.cache|\.husky|\.turbo|\.next|\.nuxt|node_modules|runtime)/ {
return 404;
}

# SSL 验证目录
location ~ \.well-known {
allow all;
}

if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
return 403;
}

access_log /www/wwwlogs/fcmmm.xyz.log;
error_log /www/wwwlogs/fcmmm.xyz.error.log;
}

保存后 SSH 执行:

1
2
nginx -t
systemctl reload nginx

六、主项目 Hexo + Butterfly 配置

xxx.github.io 仓库的 _config.yml 中确认:

1
2
url: https://fcmmm.xyz
root: /

root: /fcmlove/

本地测试构建:

1
2
3
npm install
npx hexo clean
npx hexo generate

构建后会生成:

1
public/

服务器最终应该是:

1
2
3
4
/www/wwwroot/fcmmm.xyz/main/index.html
/www/wwwroot/fcmmm.xyz/main/archives/
/www/wwwroot/fcmmm.xyz/main/css/
/www/wwwroot/fcmmm.xyz/main/js/

七、GitHub Secrets 配置

仓库添加 Secrets:

1
xxx.github.io

进入仓库:

1
Settings → Secrets and variables → Actions → New repository secret

添加:

1
2
3
4
ALIYUN_HOST       114.215.125.28
ALIYUN_USER deploy
ALIYUN_PORT 22
ALIYUN_SSH_KEY github-actions-deploy-xxx 私钥完整内容

注意:

ALIYUN_SSH_KEY 填的是私钥,不是 .pub 公钥。

在 Windows CMD 查看私钥内容:

1
type github-actions-deploy-xxx

复制完整内容,包含:

1
2
3
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----

八、主项目 xxx.github.io 的 GitHub Actions

xxx.github.io 仓库中新建:

1
.github/workflows/deploy.yml

内容:

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
name: Deploy Hexo Main Site to Aliyun

on:
push:
branches: [main]

jobs:
build-and-deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout source
uses: actions/checkout@v4
with:
submodules: recursive

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install dependencies
run: npm install

- name: Build Hexo
run: |
npx hexo clean
npx hexo generate

- name: Configure SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.ALIYUN_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H -p "${{ secrets.ALIYUN_PORT }}" "${{ secrets.ALIYUN_HOST }}" >> ~/.ssh/known_hosts

- name: Deploy to Aliyun
run: |
rsync -az --delete \
-e "ssh -p ${{ secrets.ALIYUN_PORT }}" \
public/ \
${{ secrets.ALIYUN_USER }}@${{ secrets.ALIYUN_HOST }}:/www/wwwroot/fcmmm.xyz/main/

如果你的主分支是 master,把:

1
branches: [main]

改成:

1
branches: [master]

九、手动测试部署是否成功

仓库 push 后,去 GitHub 仓库查看:

1
Actions → Deploy ...

如果显示绿色成功,再登录服务器检查:

1
ls -la /www/wwwroot/fcmmm.xyz/main/

必须看到:

1
/www/wwwroot/fcmmm.xyz/main/index.html

然后测试 Nginx:

1
2
nginx -t
systemctl reload nginx

浏览器访问:

1
https://fcmmm.xyz/

十、完整操作顺序

按这个顺序做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1. 宝塔创建 fcmmm.xyz 网站
2. 创建 /www/wwwroot/fcmmm.xyz/main/
3. 配置宝塔 Nginx
4. nginx -t
5. systemctl reload nginx
6. 创建 deploy 用户
7. 配置 deploy 的 SSH 公钥
8. 本地确认 ssh -i github-actions-deploy-xxx deploy@114.215.125.28 能登录
9. 给 /www/wwwroot/fcmmm.xyz 授权给 deploy
10. 两个 GitHub 仓库分别添加 Secrets
11. xxx.github.io 添加 Hexo 部署 workflow
12. push 仓库
13. 检查 GitHub Actions 是否成功
14. 访问 fcmmm.xyz

十三、常见问题

1. deploy 密钥登录失败

如果:

1
ssh -i github-actions-deploy-xxx deploy@114.215.125.28

报:

1
Permission denied

重新在服务器执行:

1
2
3
4
5
mkdir -p /home/deploy/.ssh
touch /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys

然后 Windows CMD 再执行:

1
type github-actions-deploy-xxx.pub | ssh root@114.215.125.28 "cat >> /home/deploy/.ssh/authorized_keys && chown -R deploy:deploy /home/deploy/.ssh && chmod 700 /home/deploy/.ssh && chmod 600 /home/deploy/.ssh/authorized_keys"

2. GitHub Actions 报 Permission denied

服务器执行:

1
2
chown -R deploy:deploy /www/wwwroot/fcmmm.xyz
chmod -R 755 /www/wwwroot/fcmmm.xyz


最终结果

1
2
3
4
5
→ GitHub Actions 构建 Hexo
→ public/
→ /www/wwwroot/fcmmm.xyz/main/
→ https://fcmmm.xyz/

只要 deploy 用户能用密钥登录,并且两个仓库的 Secrets 配置正确,之后每次 push 到 GitHub,都会自动部署到阿里云服务器。