如何优雅地部署自己的个人博客主页 🚀
作为一个沉迷代码无法自拔审美在线的理科生,假期在 GitHub 闲逛时,一眼就被优美的 Shiro 主题 俘获了芳心,于是立马拍桌决定:我也要搞一个!
为了 装X 为了便于展示自己的项目和介绍之类的~
- 个人配置:MacBook Pro + 1Panel 服务器(阿里云买的轻量级 2vCPU/1GiB)
- 部署路线:后端采用 Docker 部署,前端则是利用 Github Action 构建 Shiro 然后推送到服务器上。
💳 准备工作(氪金环节)
万丈高楼平地起,首先你得拥有:
- 一台自己的服务器:如果只是为了搞一个网页 ,可以像我一样搞一个很小规模的那种。
- 一个自己喜欢的域名:这可是你的门面!
苯人的上述工作都是在阿里云购买完成的。
🛠️ 安装 1Panel
如果你并非购买的应用镜像(也就是服务器是“毛坯房”),则需要进行这一步骤。
参考官网的 1Panel官方安装文档 步骤即可,一步一步慢慢设置即可~
⚙️ 后端 Core 的部署(给博客安个“发动机”)
这里推荐用 Docker 去进行后端的部署,一般来讲如果是 1Panel 面板会自动安装下载好,干净又卫生。
1. 检查并安装 Docker
可以先在 1Panel 的 「容器」 菜单中检查下 Docker 是否安装好再进行下一步。
2. 拉取 mx-space 文件并修改配置
可使用命令行直接拉取文件,也可以使用 1Panel 面板终端进行拉取,存放路径可以自行修改(但要记住在哪!)。
# 回到根目录并创建 mx-space/core 的文件夹,并跳转至 core 文件夹下
cd && mkdir -p mx-space/core && cd $_
# 拉取 docker-compose.yml 文件
wget https://fastly.jsdelivr.net/gh/mx-space/core@master/docker-compose.yml
用 vim 或者自己习惯的方式修改 docker-compose.yml 内的部分内容(见注释):
services:
app:
container_name: mx-server
image: innei/mx-server:latest
environment:
- TZ=Asia/Shanghai
- NODE_ENV=production
- DB_HOST=mongo
- REDIS_HOST=redis
# 填写自己博客的域名,多个用 , 相连,这里可以额外加上本机地址允许他们自己访问自己
- ALLOWED_ORIGINS=blog.lolita.best,localhost:*,127.0.0.1:*
# 填写你自己生成的JWT密钥,16≤密钥长度≤32 个字符(务必保存好自己的密钥不要泄露)
- JWT_SECRET=xxxxx
# 是否开启加密的相关配置,这边默认为false与空即可,如需加密可以参考官方配置文档
- ENCRYPT_ENABLE=false
- ENCRYPT_KEY=
volumes:
- ./data/mx-space:/root/.mx-space
ports:
# -'2333:2333' 修改了docker的端口映射,不开放端口,只对本机监听
- '127.0.0.1:2333:2333'
depends_on:
- mongo
- redis
networks:
- mx-space
restart: unless-stopped
healthcheck:
test: ['CMD', 'curl', '-f', 'http://127.0.0.1:2333/api/v2/ping']
interval: 1m30s
timeout: 30s
retries: 5
start_period: 30s
mongo:
container_name: mongo
image: mongo
volumes:
- ./data/db:/data/db
networks:
- mx-space
restart: unless-stopped
redis:
image: redis:alpine
container_name: redis
volumes:
- ./data/redis:/data
healthcheck:
test: ['CMD-SHELL', 'redis-cli ping | grep PONG']
start_period: 20s
interval: 30s
retries: 5
timeout: 3s
networks:
- mx-space
restart: unless-stopped
networks:
mx-space:
driver: bridge
3. 启动 Core
在上述操作都没问题后,直接在当前的终端目录下启动启动!
# 后续如果需要更新后端或者yaml配置修改更新容器也是一样的指令
docker compose pull && docker compose up -d
启动成功后你可以在 1Panel 的侧栏容器内看到 mx-server,redis,mongo 这三个容器的运行情况(都是绿色 已启动)。至此你已经成功布置好了后端。
4. 反向代理(打通“任督二脉”)
后端是支棱起来了,但现在它还只是个“局域网巨星”,外面的人访问不到。我们需要设置反向代理,打通它和外界的“任督二脉”。
可以参考官方安装设置反向代理的操作!
(根据自身购买的域名的特点,结合 AI 老师搞一下即可)
设置完毕后,这时候你就可以通过 https://你的域名/qaqdmin 访问后台管理地址了,根据指示填写对应内容即可。在设定的网站设置内填写对应的地址(看准,是你自己的!!!)。
✨ 前端部署(给博客搞个“精装修”)
苯人是用的是 Shiro 的开源版本,并通过 Github Action 的方式自动部署。
1. Fork 仓库并修改 Workflows 文件
仓库地址: GitHub - innei-dev/shiroi-deploy-action
Fork 到本地后打开,修改 workflows 下的 deploy.yml 文件 (要改的地方有中文注释)。
# deploy.yml 的完整内容
name: Build and Deploy
on:
workflow_dispatch: # 增加了工作流的手动运行
push:
branches:
- main
# schedule:
# - cron: '0 3 * * *'
repository_dispatch:
types: [trigger-workflow]
permissions: write-all
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
PNPM_VERSION: 9.x.x
HASH_FILE: build_hash
jobs:
prepare:
name: Prepare
runs-on: ubuntu-latest
if: ${{ github.event.head_commit.message != 'Update hash file' }}
outputs:
hash_content: ${{ steps.read_hash.outputs.hash_content }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Read HASH_FILE content
id: read_hash
run: |
content=$(cat ${{ env.HASH_FILE }}) || true
echo "hash_content=$content" >> "$GITHUB_OUTPUT"
check:
name: Check Should Rebuild
runs-on: ubuntu-latest
needs: prepare
outputs:
canceled: ${{ steps.use_content.outputs.canceled }}
steps:
- uses: actions/checkout@v4
with:
repository: innei-dev/shiroi # 填写你需要访问的仓库,如开源版为innei/shiro
token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
fetch-depth: 0
lfs: true
- name: Use content from prev job and compare
id: use_content
env:
FILE_HASH: ${{ needs.prepare.outputs.hash_content }}
run: |
file_hash=$FILE_HASH
current_hash=$(git rev-parse --short HEAD)
echo "File Hash: $file_hash"
echo "Current Git Hash: $current_hash"
if [ "$file_hash" == "$current_hash" ]; then
echo "Hashes match. Stopping workflow."
echo "canceled=true" >> $GITHUB_OUTPUT
else
echo "Hashes do not match. Continuing workflow."
fi
build:
name: Build artifact
runs-on: ubuntu-latest
needs: check
if: ${{needs.check.outputs.canceled != 'true'}}
strategy:
matrix:
node-version: [20.x]
outputs:
sha_short: ${{ steps.store.outputs.sha_short }}
branch: ${{ steps.store.outputs.branch }}
steps:
- uses: actions/checkout@v4
with:
repository: innei-dev/shiroi # 填写你需要访问的仓库,如开源版为innei/shiro
token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
fetch-depth: 0
lfs: true
- name: Checkout LFS objects
run: git lfs checkout
- uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- uses: jongwooo/next-cache@v1
- name: Install dependencies
run: pnpm install
- name: Build project
run: |
sh ./ci-release-build.sh
- name: Cache Build Artifacts
id: cache-primes
uses: actions/cache/save@v4
with:
path: assets
key: ${{ github.run_number }}-release
- name: Store artifact commit version
shell: bash
id: store
run: |
sha_short=$(git rev-parse --short HEAD)
branch_name=$(git rev-parse --abbrev-ref HEAD)
echo "sha_short=$sha_short" >> "$GITHUB_OUTPUT"
echo "branch=$branch_name" >> "$GITHUB_OUTPUT"
deploy:
name: Deploy artifact
runs-on: ubuntu-latest
needs: build
steps:
- name: Restore cached Build Artifacts
id: cache-primes-restore
uses: actions/cache/restore@v4
with:
path: |
assets
key: ${{ github.run_number }}-release
- name: Move assets to root
run: mv assets/release.zip release.zip
- name: copy file via ssh password
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USER }}
password: ${{ secrets.PASSWORD }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
source: 'release.zip'
target: '/tmp/shiro'
- name: Exec deploy script with SSH
uses: appleboy/ssh-action@master
with:
command_timeout: 5m
host: ${{ secrets.HOST }}
username: ${{ secrets.USER }}
password: ${{ secrets.PASSWORD }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
# script_stop: true 移除了改行指令,因为该指令已经停止使用了会提示但不影响,不改也行
script: |
set -e
export NVM_DIR=~/.nvm # 定义.nvm的安装目录,否则终端运行时明明安装了但找不到命令
source ~/.nvm/nvm.sh # 识别nvm命令,否则可能会报command not found
source $HOME/.bashrc
basedir=$HOME/mx-space/shiro # 填写你自己希望的路径,因为我的后端文件在mx-space下,所以我希望我的shiro也在mx-space文件夹下,方便管理
workdir=$basedir/${{ github.run_number }}
mkdir -p $workdir
mkdir -p $basedir/.cache
mv /tmp/shiro/release.zip $workdir/release.zip
rm -r /tmp/shiro
cd $workdir
unzip -qq -o $workdir/release.zip
rm -rf $workdir/standalone/.env
ln -s $HOME/mx-space/shiro/.env $workdir/standalone/.env # 你的.env文件路径,我存放在mx-space/shiro文件夹内,方便管理
export NEXT_SHARP_PATH=$(npm root -g)/sharp
# copy workdir ecosystem.config.js to basedir if not exists
if [ ! -f $basedir/ecosystem.config.js ]; then
cp $workdir/standalone/ecosystem.config.js $basedir/ecosystem.config.js
fi
# https://github.com/Unitech/pm2/issues/3054
# symlink workdir node entry file to basedir
ln -sf $workdir/standalone/server.js $basedir/server.js
mkdir -p $workdir/standalone/.next
rm -rf $workdir/standalone/.next/cache
ln -sf $basedir/.cache $workdir/standalone/.next/cache
cd $basedir
pm2 reload ecosystem.config.js --update-env
rm $workdir/release.zip
pm2 save
echo "Deployed successfully"
- name: After deploy script
run: |
hash=${{ needs.build.outputs.sha_short }}
# curl -X "POST" "https://mx.innei.in/api/v2/fn/shiro/new-version-hook" -H 'Content-Type: application/json' -d "{\"hash\": \"$hash\", \"key\": \"\"}"
${{ secrets.AFTER_DEPLOY_SCRIPT }}
store:
name: Store artifact commit version
runs-on: ubuntu-latest
needs: [deploy, build]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0
- name: Use outputs from build
env:
SHA_SHORT: ${{ needs.build.outputs.sha_short }}
BRANCH: ${{ needs.build.outputs.branch }}
run: |
echo "SHA Short from build: $SHA_SHORT"
echo "Branch from build: $BRANCH"
- name: Write hash to file
env:
SHA_SHORT: ${{ needs.build.outputs.sha_short }}
run: echo $SHA_SHORT > ${{ env.HASH_FILE }}
- name: Commit files
run: |
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add ${{ env.HASH_FILE }}
git commit -a -m "Update hash file"
- name: Push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.ref }}
2. 设置工作流的 Secrets
为了让 GitHub Actions 能够安全连接到你的服务器,需要在仓库的 Settings -> Secrets and variables -> Actions 中设置以下密钥:
HOST: 服务器地址USER: 服务器用户名PASSWORD: 服务器密码PORT: 服务器 SSH 端口KEY: 服务器 SSH Key(可选,密码 key 二选一)GH_PAT: 可访问之前repository内填写仓库的 Github Token
至于如何获取
KEY和GH_PAT可以自行 google 或者 AI(一步步操作),这是基础~
3. 配置 .env 文件
返回到 1Panel,在你的工作流内指定好的地方(就是找到 /shiro 所在的地方)创建 .env 文件并将以下内容复制修改成自己的内容。如上方参考中是 root/mx-space/shiro。
(在终端中熟练掌握
cd和ls两个指令有助于你迅速找到相应内容)
# 后端API地址
NEXT_PUBLIC_API_URL=https://blog.lolita.best/api/v2
# 后端网关地址
NEXT_PUBLIC_GATEWAY_URL=https://blog.lolita.best
# TMDB信息获取
TMDB_API_KEY=填写你的TMDB-API
# GitHub token,用来获取前端版本哈希
GH_TOKEN=填你自己的token
4. 服务器安装必要环境
要在服务器的终端处下载以下一系列帅气的助手:Node.js, npm, pnpm, pm2, sharp。
可以在 1Panel 的终端内输入以下指令进行直接安装。
(温馨提示:如果内存不够,可以尝试 swap
白嫖一点空间)
#!/bin/bash
echo "🚀 开始安装 Node.js、npm、pnpm、pm2、sharp..."
# 确认 Homebrew 是否已安装
if ! command -v brew &> /dev/null
then
echo "📦 未检测到 Homebrew,开始安装..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
fi
# 更新 brew
brew update
# 安装 Node.js(自带 npm)
echo "📦 安装 Node.js 和 npm..."
brew install node
# 安装 pnpm
echo "📦 安装 pnpm..."
npm install -g pnpm
# 安装 pm2
echo "📦 安装 pm2..."
npm install -g pm2
# 安装 sharp 所需依赖
echo "📦 安装 sharp 依赖..."
brew install pkg-config cairo pango libpng jpeg giflib librsvg
# 安装 sharp
echo "📦 安装 sharp..."
npm install -g sharp
# 输出版本信息
echo "✅ 安装完成!版本信息如下:"
node -v
npm -v
pnpm -v
pm2 -v
node -e "console.log('sharp version:', require('sharp').version)"
5. 启动 Workflow
在 Github 内找 Actions,然后手动 Run workflow 并等待工作流跑完,不报错、显示绿色就成功了。
6. 管理后台配置 Shiro 主题云函数
提示: 云函数的配置修改更新并非立刻刷新,需要时间请耐心等待或者多次刷新!!!
Shiro 主题还要配合云函数才可以部署,进入管理后台 /附加功能/配置与云函数 内,先导入相关文件。
右上角第二个下载 —— 弹出弹窗选择下边的导入shiro
再点击右上角的新增按钮,在编辑页面中,填入以下设置:
- 名称:
shiro - 引用:
theme - 数据类型:
JSON - 数据:右侧复制以下示例,更改为自己的信息。数据内容参考:配置 - Mix Space 文档
{
"footer": {
"otherInfo": {
"date": "2019-{{now}}",
"icp": {
"text": "萌ICP备20248012号",
"link": "https://icp.gov.moe/?keyword=20248012"
}
},
"linkSections": [
{
"name": "关于",
"links": [
{ "name": "关于本站", "href": "/about-site" },
{ "name": "关于我", "href": "/about", "external": true }
]
},
{
"name": "更多",
"links": [
{ "name": "时间线", "href": "/timeline" },
{ "name": "友链", "href": "/friends", "external": true }
]
},
{
"name": "联系",
"links": [
{ "name": "写留言", "href": "/message", "external": true },
{ "name": "发邮件", "href": "mailto:mine.sumika@gmail.com", "external": true },
{ "name": "GitHub", "href": "https://github.com/Sumikanya", "external": true }
]
}
]
},
"config": {
"color": {
"light": ["#33A6B8", "#FF6666", "#26A69A", "#fb7287", "#69a6cc", "#F11A7B", "#78C1F3", "#FF6666", "#7ACDF6"],
"dark": ["#F596AA", "#A0A7D4", "#ff7b7b", "#99D8CF", "#838BC6", "#FFE5AD", "#9BE8D8", "#A1CCD1", "#EAAEBA"]
},
"bg": [
"https://github.com/Innei/static/blob/master/images/F0q8mwwaIAEtird.jpeg?raw=true",
"https://github.com/Innei/static/blob/master/images/IMG_2111.jpeg.webp.jpg?raw=true"
],
"custom": { "css": [], "styles": [], "js": [], "scripts": [] },
"site": {
"favicon": "https://cn.cravatar.com/avatar/08d99bea604c306b51ebe162e93412f1?s=36",
"faviconDark": "https://cn.cravatar.com/avatar/08d99bea604c306b51ebe162e93412f1?s=36"
},
"hero": {
"title": {
"template": [
{ "type": "h1", "text": "Hi, I'm ", "class": "font-light text-4xl" },
{ "type": "h1", "text": "星野纯夏", "class": "font-medium mx-2 text-4xl" },
{ "type": "h1", "text": "💕。", "class": "font-light text-4xl" },
{ "type": "br" },
{ "type": "h1", "text": "一个喜欢科技数码的Lo娘 ", "class": "font-light text-4xl" },
{ "type": "code", "text": "🦋<INFP>🦋", "class": "font-medium mx-2 text-3xl rounded p-1 bg-gray-200 dark:bg-gray-800/0 hover:dark:bg-gray-800/100 bg-opacity-0 hover:bg-opacity-100 transition-background duration-200" },
{ "type": "span", "class": "inline-block w-[1px] h-8 -bottom-2 relative bg-gray-800/80 dark:bg-gray-200/80 opacity-0 group-hover:opacity-100 transition-opacity duration-200 group-hover:animation-blink" }
]
},
"description": "希望在这个世界上可以留下属于我的痕迹",
"Hitokoto": { "random": true, "custom": "自定义一言" }
},
"module": {
"activity": { "enable": true, "endpoint": "/fn/ps/update" },
"donate": {
"enable": false, "link": "https://afdian.net/@Innei",
"qrcode": ["https://cdn.jsdelivr.net/gh/Innei/img-bed@master/20191211132347.png", "https://cdn.innei.ren/bed/2023/0424213144.png"]
},
"bilibili": { "liveId": 19781 }
}
}
}
7. 设置 PM2 服务在重启之后还原进程
pm2 startup
pm2 save
如何手动启动 Shiro?可以进入 shiro 的前端路径并运行以下代码(默认 github 的工作流配置内已经启动过了):
pm2 start ecosystem.config.js
可以使用以下指令查看正在运行的进程:
pm2 list
status 显示 绿色的 online 即可~
🎉 部署完毕
前后端都部署成功后你就可以直接访问你的网页查看到当前效果了。
📚 参考链接(感谢各位大佬带飞)
十分感谢官方以及其他搭建的相关教程: