结论先行:独立开发者最该自动化的就是一件事:把「本地 Docker Compose 写代码 → git push → GitHub Actions 自动测试、构建镜像、部署到 VPS → Telegram 通知」这条路打通。我的实测是单次部署从 18 分钟压到 4 分钟,凌晨困到手滑也不会把线上搞挂。一台 VPS、一个中小项目,照这套搭两天能跑通。但如果你是纯前端或 Serverless 项目,先用 Vercel / Cloudflare Pages 这类零运维托管,需要长驻后端进程时再上 VPS。

昨天改了8次代码,每次都要SSH登录服务器、git pull、重启服务。凌晨1点最后一次部署时,脑子已经糊了,漏看了报错日志,导致线上服务挂了半小时。这事儿不能这么下去了。

全景图解导览:我的完整开发部署工作流:从本地到VPS的自动化实战指南 STAGE 01 · 问题起爆 写在前面 • 前置知识检查:开始前,问 自己这几个问题: • 环节02:改代码 定位核心风险卡点 STAGE 02 · 机制拆解 期望目标 • ✅本地开发:Docker Compose一键启动开发环境 • ✅代码管理:Git规范化提… 理清底层因果逻辑 STAGE 03 · 方案对照 计划思考 • 为什么需要标准化工作 流?:独立开发者的时间很… • 工具选型:|类别|工具|为… 对齐量化实测标尺 STAGE 04 · 工程落地 操作步骤 • 看git log就知道项目做了 什么:git log--oneline • 自动生成Changelog:可以… 锁死生产确定性

0、写在前面

之前我的开发流程是这样的:

  1. 改代码
  2. 本地测试
  3. git push
  4. SSH 登录服务器
  5. git pull
  6. 重启服务
  7. 手动验证

看似简单,但一天改个十几次代码,光 SSH 登录服务器、敲命令就要花掉不少时间。更糟的是,容易出错,忘记 pull、重启错了服务、漏看了报错日志。

后来我花了两天时间,把这套流程自动化了。现在只需要:

1
2
3
git add .
git commit -m "feat: 新功能"
git push

三分钟后,生产环境自动更新完成,Telegram 还会收到部署通知。

这篇文章就把这套工作流完整分享给你,从零开始搭建,照做就能用。

0.1 前置知识检查

开始前,问自己这几个问题:

  • ✅ 我用过Git吗?(基本的add/commit/push)
  • ✅ 我有VPS吗?(如果没有,推荐Vultr注册一个)
  • ✅ 我了解Docker基本概念吗?(镜像、容器、Dockerfile)
  • ✅ 我的代码能本地跑起来吗?

如果上面有❌,不要着急!先花1-2小时补一下对应知识。

问题现场解剖:写在前面 ⚠️ 从问题出发:表面症状是如何一步步演变为生产故障或降权的? 独立开发者工具箱 · CH-59 · 核心卡点传导链 1. 前置知识检查 开始前,问自己这几个问题: 需通过真实环境实测核验 症结:认知偏差与配置盲区 2. 环节02 改代码 需通过真实环境实测核验 传导:隐性劣化与边界失守 3. 环节03 本地测试 需通过真实环境实测核验 破局:量化指标与工程验证 ✅ 破局核心原则(结论先行) 独立开发者最该自动化的就是一件事:把「本地Docker Compose写代码→git push→GitHub Actions自动测试、构建镜像、部署到VPS→Telegram通知」这条…

1、期望目标

搭建一套「从代码到生产」的完整自动化流程:

写完代码,git push,3分钟后自动上线。这套自动化流程包含:

  • ✅ 本地开发:Docker Compose 一键启动开发环境
  • ✅ 代码管理:Git 规范化提交,自动检查代码质量
  • ✅ 自动化测试:push 时自动跑测试
  • ✅ 自动构建:构建 Docker 镜像并推送到镜像仓库
  • ✅ 自动部署:rsync 同步文件到 VPS,重启服务
  • ✅ 部署通知:Telegram / Discord / Slack 通知部署结果
  • ✅ 回滚能力:出问题时快速回滚到上一个版本

我的实测:单次部署从18分钟降到4分钟(后面有对比表),两个月零部署事故。

底层机制与架构拆解:期望目标 四阶段递进分析模型 · 从前置识别到最终工程定级 S1 ✅本地开发 Docker Compose一键启 动开发环境 S2 ✅代码管理 Git规范化提交,自动检 查代码质量 S3 ✅自动化测试 push时自动跑测试 底层规则强约束 S4 ✅自动构建 构建Docker镜像并推送到 镜像仓库 💡 工程启示:任何一个前置环节失守,都会通过传导链放大为下游的系统性偏差

2、计划思考

为什么需要标准化工作流?

独立开发者的时间很贵,不应该浪费在重复劳动上。但很多人(包括以前的我)觉得:

「我的项目很小,手动部署也很快啊,搞自动化是不是浪费时间?」

这是个陷阱。项目小的时候确实手动部署不麻烦,但问题是:

场景 手动部署的问题 自动化部署的优势
频繁迭代 每次都要重复操作,容易出错 push 就完事,零心智负担
多环境(开发/测试/生产) 容易混淆环境,把测试代码上到生产 自动区分环境,绝不可能搞错
团队协作 其他人不知道怎么部署 文档即代码,workflow 就是文档
凌晨 2 点修 bug 困倦时容易手滑,删库跑路 自动化流程固定,不会犯错
多个项目 每个项目部署方式不同,记忆负担大 统一模板,新项目 5 分钟配好
出问题要快速回滚 手动回滚容易漏步骤 一个命令回滚,从容应对

自动化工作流这把效率武器,独立开发者比大公司更需要。

CI/CD 全链路工作流 开发者 本地开发 git push GitHub 仓库 + 代码评审 PR / Tag GitHub Actions CI/CD 流水线 测试 → 构建 → 推送镜像 VPS 服务器 Docker 部署 pull → restart Docker Hub / GHCR 镜像仓库(中转) Telegram 通知 部署成功 / 失败 本地 git push → 自动跑测试 → 构建镜像 → 推送 → VPS 拉取 → 重启 → 通知 全链路 5 分钟 · 凌晨 3 点也不怕

工具选型

类别 工具 为什么独立开发者需要它 新手可以跳过吗
代码仓库 GitHub 免费私有仓库 + Actions CI/CD 太好用 ❌ 必备
容器化 Docker 环境一致性,部署不依赖本地环境 ❌ 必备
CI/CD GitHub Actions 和代码仓库无缝集成,免费额度够用 ❌ 必备
镜像仓库 Docker Hub 官方仓库,免费私有仓库 ❌ 必备
VPS 任意云厂商 我常用 Vultr 和 DigitalOcean ❌ 必备
反向代理 Nginx 稳定、功能强大、社区活跃 ✅ 可选(Caddy更简单)
进程管理 Docker Compose 管理多容器很方便 ❌ 必备

💡 出海开发者福利

Vultr 和 DigitalOcean 都有全球机房,离用户越近延迟越低。我用这两家多年,稳定、性价比高,不用实名备案(国内开发者都懂这里的繁琐与卡点)。注册时通过我的 aff 链接,还能拿新用户优惠。


3、操作步骤

3.1 本地开发环境配置

安装必要工具

确保本地装了这些工具(Mac 用 Homebrew,Linux 用 apt/yum,Windows 用 WSL2):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Mac(新版Docker Desktop)
brew install --cask docker
# 启动Docker Desktop后验证
docker compose version

# 或兼容旧版(明确说明)
brew install docker docker-compose

# Ubuntu/Debian
sudo apt update
sudo apt install -y docker.io docker-compose git

# 验证安装
docker --version
docker-compose --version
git --version

⚠️ Windows 用户

强烈建议用 WSL2 跑 Docker Desktop,不要用原生 Windows Docker。性能、兼容性都好太多。

项目结构示例

一个典型的项目结构:

1
2
3
4
5
6
7
8
9
10
11
my-project/
├── .github/
│ └── workflows/
│ └── deploy.yml # CI/CD workflow
├── src/ # 源代码
├── tests/ # 测试
├── Dockerfile # Docker 镜像定义
├── docker-compose.yml # 本地开发环境
├── docker-compose.prod.yml # 生产环境配置
├── .gitignore # 忽略文件
└── README.md

创建 Dockerfile

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
# 多阶段构建:先构建,再打包成运行时镜像
FROM node:22-alpine AS builder

WORKDIR /app

# 复制依赖文件
COPY package*.json ./

# 安装依赖(利用 Docker 缓存)
RUN npm ci --only=production

# 复制源代码
COPY . .

# 构建(如果你的项目需要构建)
# RUN npm run build

# 运行时镜像
FROM node:22-alpine

WORKDIR /app

# 非root用户运行(安全最佳实践)
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001

# 复制构建产物
COPY --from=builder /app ./

# 切换用户
USER nodejs

# 暴露端口
EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"

# 启动命令
CMD ["node", "src/index.js"]

💡 多阶段构建的好处

  • 最终镜像只包含运行时文件,体积小(从 500MB 降到 50MB 很常见)
  • 不包含构建工具(gcc、make 等),安全性更高
  • 拉取镜像更快,部署更快

本地开发用 docker-compose.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
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DB_HOST=db
volumes:
- ./src:/app/src # 热重载
- /app/node_modules # 避免覆盖 node_modules
depends_on:
- db

db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"

volumes:
pgdata:

启动开发环境:

1
2
3
4
5
6
7
8
9
10
11
# 一键启动
docker compose up

# 后台运行
docker compose up -d

# 查看日志
docker compose logs -f app

# 进入容器调试
docker compose exec app sh

3.2 Git 工作流规范

.gitignore 配置

不要把敏感文件、依赖文件提交到仓库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# .gitignore
node_modules/
.env
.env.local
*.log
coverage/
.DS_Store
.idea/
.vscode/
dist/
build/

# 生产环境配置
docker-compose.prod.yml
deploy/

💡 永远不要提交 .env!

.env 里通常有数据库密码、API Key 等敏感信息。一旦泄露到 GitHub,就是灾难。

正确做法:提交 .env.example(示例文件),生产环境变量在 VPS 上配置。

规范化提交信息(可选,团队协作时推荐)

如果你是独立开发者,基本的 git commit 就够了。如果是团队协作,可以安装 commitizen 工具:

1
2
3
4
5
6
7
8
# 全局安装
npm install -g commitizen cz-conventional-changelog

# 配置(在 ~/.czrc)
echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc

# 以后提交用这个命令
git cz

会引导你选择提交类型:

1
2
3
4
5
6
7
8
9
? Select the type of change that you're committing:
feat: A new feature
fix: A bug fix
docs: Documentation only changes
style: Changes that do not affect the meaning of the code
refactor: A code change that neither fixes a bug nor adds a feature
perf: A code change that improves performance
test: Adding missing tests
chore: Changes to the build process or auxiliary tools

然后让你输入描述,自动生成规范的提交信息:

1
2
3
4
5
feat: add user authentication feature

- add JWT token validation
- add login/logout endpoints
- add password hashing

这样做的价值:

  1. 看 git log 就知道项目做了什么:git log --oneline
  2. 自动生成 Changelog:可以用 standard-version 自动生成版本日志
  3. 自动化版本管理:语义化版本号(Semantic Versioning)

Pre-commit 钩子(可选,团队协作时推荐)

独立开发时手动跑 lint 也够了。团队协作时,用 husky 在提交前自动检查代码:

1
2
3
4
5
6
7
8
# 安装
npm install -D husky lint-staged

# 初始化
npx husky-init

# 配置 .husky/pre-commit
echo "npx lint-staged" > .husky/pre-commit

配置 package.json:

1
2
3
4
5
6
7
8
9
10
11
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,yml,yaml}": [
"prettier --write"
]
}
}

现在每次提交会自动:

  • 跑 ESLint 检查代码质量
  • 跑 Prettier 格式化代码
  • 只检查暂存的文件(lint-staged)

3.3 GitHub Actions CI/CD

创建 workflow 文件

创建 .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
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
name: Deploy to VPS

on:
push:
branches:
- main # 主分支 push 时触发
pull_request:
branches:
- main # PR 时跑测试,但不部署

env:
DOCKER_IMAGE: my-username/my-app # Docker 镜像名
DOCKER_TAG: ${{ github.sha }} # 用 commit sha 作为 tag

jobs:
# Job 1: 测试
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run tests
run: npm test

- name: Run linter
run: npm run lint

# Job 2: 构建并推送镜像
build:
needs: test # 测试通过后才执行
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.DOCKER_IMAGE }}:${{ env.DOCKER_TAG }}
${{ env.DOCKER_IMAGE }}:latest
cache-from: type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache,mode=max

# Job 3: 部署到 VPS
deploy:
needs: build # 构建成功后才部署
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Deploy to VPS
uses: burnett01/[email protected]
with:
switches: -avzr --delete --exclude=node_modules --exclude=.git
path: ./
remote_path: /var/www/my-app
remote_host: ${{ secrets.VPS_HOST }}
remote_user: ${{ secrets.VPS_USER }}
remote_key: ${{ secrets.SSH_PRIVATE_KEY }}

- name: Restart services on VPS
uses: appleboy/[email protected]
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/my-app
IMAGE_TAG=${{ github.sha }}
docker-compose -f docker-compose.prod.yml pull
docker-compose -f docker-compose.prod.yml up -d
docker image prune -f

- name: Notify on success
if: success()
uses: slackapi/[email protected]
with:
webhook-url: ${{ secrets.SLACK_WEBHOOK }}
payload: |
{
"text": "✅ 部署成功!Commit: ${{ github.sha }}"
}

配置 GitHub Secrets

在 GitHub 仓库设置里添加这些 Secrets(Settings → Secrets and variables → Actions):

Secret 名称 说明 获取方式
Secret 名称 说明 获取方式
---------------- ---------------------------- --------------------------------
DOCKER_USERNAME Docker Hub 用户名 你的 Docker Hub 账号
DOCKER_PASSWORD Docker Hub 密码/Access Token Docker Hub 设置
VPS_HOST VPS 的 IP 地址或域名 你的 VPS IP
VPS_USER VPS 登录用户名(非root) 之前创建的部署用户(如 deploy)
SSH_PRIVATE_KEY SSH 私钥 在本地生成,见下文
TELEGRAM_BOT_TOKEN Telegram Bot Token 找 @BotFather 获取
TELEGRAM_CHAT_ID 接收通知的 Chat ID 见下文

生成部署用 SSH 密钥

🛠️ 一键 SSH 密钥与免密配置:
如果觉得手动配置 ED25519 密钥、修改 authorized_keys 权限容易出错,推荐使用 ittinker-scripts 开源的 ssh-key-setup 自动化脚本快速完成基线配置:

1
curl -sSL https://raw.githubusercontent.com/ittinker/ittinker-scripts/main/ssh-key-setup/setup.sh | bash

手动生成步骤如下:

1
2
3
4
5
# 在本地生成部署专用密钥对
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/github-deploy

# 复制公钥内容
cat ~/.ssh/github-deploy.pub

把公钥添加到 VPS:

1
2
3
4
5
# 在 VPS 上,把公钥添加到部署用户的 authorized_keys
echo "你的公钥内容" >> ~/.ssh/authorized_keys

# 测试登录
ssh -i ~/.ssh/github-deploy your-user@your-vps-ip

把私钥内容复制到 GitHub Secrets SSH_PRIVATE_KEY:

1
2
# 复制私钥(包含 BEGIN 和 END 行)
cat ~/.ssh/github-deploy

配置部署通知

Telegram Bot:

  1. 找 @BotFather 创建 Bot,获取 Token
  2. 把 Bot 拉入你的频道/群组
  3. 获取Chat ID(注意顺序!)
    • 先给你的Bot发任意消息(如"/start")
    • 然后访问这个URL:
      1
      curl https://api.telegram.org/bot<TOKEN>/getUpdates
    • 返回JSON里找到 message.chat.id(或 result[0].message.chat.id)
      • 个人聊天:正数ID
      • 群组/频道:负数ID(通常是-100xxxxxx)
    • ⚠️ 常见错误:直接访问URL没给Bot发消息,返回空结果
  4. 在 workflow 里用 curl 发通知:
1
2
3
4
5
6
7
- name: Notify on Telegram
if: always()
run: |
curl -X POST \
"https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
-H "Content-Type: application/json" \
-d '{"chat_id": "${{ secrets.TELEGRAM_CHAT_ID }}", "text": "部署 ${{ job.status }}: ${{ github.sha }}"}'

3.4 VPS 部署配置

💡 还没VPS?Vultr/DigitalOcean注册时通过我的aff链接,还能拿新用户优惠

创建部署用户

不要用 root 部署,创建专用用户:

1
2
3
4
5
6
7
# 在 VPS 上
sudo adduser deploy
sudo usermod -aG docker deploy # 加入 docker 组

# 配置 sudoers(如果需要)
sudo visudo
# 添加:deploy ALL=(ALL) NOPASSWD: /usr/bin/docker

生产环境 docker-compose.prod.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
services:
app:
image: my-username/my-app:latest # 从镜像仓库拉取
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=${DB_HOST}
- DB_PASSWORD=${DB_PASSWORD}
restart: always # 自动重启
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"

nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
restart: always

Nginx 反向代理

编写 nginx.conf:

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
events {
worker_connections 1024;
}

http {
upstream app {
server app:3000;
}

# 限流配置
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

server {
listen 80;
server_name example.com;

# 强制 HTTPS
return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl http2;
server_name example.com;

ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;

# SSL 配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

# 日志
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

location / {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_cache_bypass $http_upgrade;
}
}
}

环境变量管理

创建 .env 文件(不要提交到 git):

1
2
3
4
5
6
7
8
9
10
11
12
# 1. SSH登录VPS并进入项目目录
ssh your-user@your-vps-ip
cd /var/www/my-app

# 2. 创建.env文件
nano .env

# 3. 复制以下内容并修改
NODE_ENV=production
DB_HOST=db # 如果用docker-compose的db服务
DB_PASSWORD=your_strong_password
JWT_SECRET=your_jwt_secret_here

SSL 证书(Let’s Encrypt)

用 certbot 获取免费 SSL 证书:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 安装 certbot
sudo apt install certbot

# 获取证书(standalone 模式)
sudo certbot certonly --standalone -d example.com -d www.example.com

# 证书会保存在 /etc/letsencrypt/live/example.com/
# 复制到项目目录
sudo cp /etc/letsencrypt/live/example.com/fullchain.pem ./ssl/
sudo cp /etc/letsencrypt/live/example.com/privkey.pem ./ssl/

# 设置自动续期
echo "0 0 * * 0 certbot renew --quiet" | sudo crontab -

3.5 监控与日志

日志管理

查看容器日志:

1
2
3
4
5
6
7
8
9
10
11
# 查看所有日志
docker compose logs

# 实时追踪
docker compose logs -f app

# 查看最近 100 行
docker compose logs --tail=100 app

# 查看特定时间段的日志
docker compose logs --since 2025-07-02T10:00:00 app

日志轮转(在 docker-compose.yml 里配置):

1
2
3
4
5
logging:
driver: "json-file"
options:
max-size: "10m" # 单个日志文件最大 10MB
max-file: "3" # 保留最近 3 个日志文件

健康检查

在 Dockerfile 里定义健康检查(前面已有),检查状态:

1
2
3
4
5
# 查看容器健康状态
docker ps

# 查看健康检查日志
docker inspect --format='{{json .State.Health}}' my-app | jq

监控告警

用 Uptime Kuma 监控服务可用性:

1
2
3
4
5
docker run -d --restart=always \
-p 3001:3001 \
-v uptime-kuma:/app/data \
--name uptime-kuma \
louislam/uptime-kuma:1

访问 http://<你的VPS-IP>:3001,配置监控:

  • 添加监控端点(如 https://example.com/api/health)
  • 设置告警(Telegram/邮件/Discord)
  • 配置检查间隔(1-5 分钟)

🛠️ VPS 宿主机资源水位监控:
除了用 Uptime Kuma 探测 HTTP 业务可用性,VPS 本身的 CPU、内存和磁盘水位也需要守护。推荐在服务器部署 ittinker-scripts 开源的 vps-health-monitor,当磁盘或内存占用超 85% 时自动通过 Telegram 推送报警:

1
curl -sSL https://raw.githubusercontent.com/ittinker/ittinker-scripts/main/vps-health-monitor/install.sh | bash

3.6 回滚策略

当新版本出问题,快速回滚:

1
2
3
4
5
6
7
8
9
# 方法1:回滚到上一个镜像
docker compose pull my-app:previous-tag
docker compose up -d

# 方法2:直接在 VPS 上回退 git
cd /var/www/my-app
git log --oneline -10 # 查看最近 10 次提交
git reset --hard abc123 # 回退到某个 commit
docker compose up -d --build

更好的做法:保留最近几个版本的镜像:

1
2
# 在 workflow 里,不要删除旧镜像
# docker image prune -f <-- 删除这行
落地执行与验证流水线:操作步骤 按序执行以下四步流程,把定性经验转化为可自动巡检的工程防线 STEP 01 看git log就知道项目… git log--oneline 可复现验收标准 STEP 02 自动生成Changelog 可以用 standard-version自动… STEP 03 自动化版本管理 语义化版本号(Semantic Versioning) STEP 04 本地开发环境配置 本地开发环境配置 可复现验收标准

4、总结

检查清单

上线前检查这些:

检查项 命令/方法 预期结果
本地测试通过 docker compose up -d && 测试 所有功能正常
测试通过 npm test 无失败用例
代码检查通过 npm run lint 无报错
Docker 镜像构建成功 docker build -t test . 构建成功
GitHub Actions 通过 查看 Actions 标签页 绿色 ✅
VPS 能拉取镜像 SSH 到 VPS: docker pull <image> 拉取成功
环境变量已配置 cat .env 所有必需变量都有
SSL 证书有效 curl -I https://example.com 返回 200/301
健康检查通过 docker ps 状态为 healthy
监控已配置 Uptime Kuma/其他工具 监控端点已添加

关键文件位置

1
2
3
4
5
6
7
8
9
10
11
GitHub 仓库:
├── .github/workflows/deploy.yml # CI/CD 定义
├── Dockerfile # 镜像定义
├── docker-compose.yml # 开发环境
└── docker-compose.prod.yml # 生产环境

VPS 上:
├── /var/www/my-app/ # 项目目录
├── /var/www/my-app/.env # 环境变量(不进 git)
├── /var/www/my-app/nginx.conf # Nginx 配置
└── /var/www/my-app/ssl/ # SSL 证书

效率提升

操作 手动部署耗时 自动化部署耗时 节省时间
提交代码 1 分钟 1 分钟 0
登录服务器、拉取代码 2 分钟 0 2 分钟
重启服务、验证 5 分钟 3 分钟 2 分钟
修复遗漏的问题 10 分钟 0 10 分钟
合计 18 分钟 4 分钟 77%

最佳实践

  1. 小步快跑:频繁提交小改动,而不是攒一大堆再发
  2. 自动化一切:手动做的事超过 3 次,就该自动化
  3. 监控先行:先配监控,再上线,出问题才能第一时间知道
  4. 保留回滚能力:永远保持能快速回退到上一个稳定版本
  5. 文档即代码:workflow 文件就是最好的部署文档
多维实测与判断标尺矩阵:总结 检查项 命令/方法 预期结果 本地测试通过 docker compose up-d&&测试 所有功能正常 测试通过 npm test 无失败用例 代码检查通过 npm run lint 无报错 Docker镜像构建成功 docker build-t test. 构建成功

5、异常记录

Q1:GitHub Actions 拉取私有子模块失败?

现象:

1
fatal: clone of '[email protected]:private/repo.git' failed

解决方案:

在 workflow 里添加 SSH key 配置:

1
2
3
4
5
6
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts

Q2:Docker 构建太慢?

优化方法:

  1. 利用层缓存:把变化少的指令放前面
1
2
3
4
5
6
7
8
# ✅ 好:利用缓存
COPY package*.json ./
RUN npm ci
COPY . .

# ❌ 差:每次都重新安装依赖
COPY . .
RUN npm install
  1. 用 BuildKit 缓存挂载:
1
DOCKER_BUILDKIT=1 docker build --cache-from=my-app:latest .
  1. GitHub Actions 缓存:
1
2
3
4
5
6
7
- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-

Q3:部署后网站 502 Bad Gateway?

排查步骤:

  1. 容器是否在运行:
1
docker ps
  1. 查看日志:
1
docker compose logs app
  1. 检查环境变量:
1
docker compose exec app env | grep DB
  1. 手动测试容器:
1
docker compose exec app curl http://localhost:3000

常见原因:

  • 环境变量没配置
  • 数据库连接失败
  • 端口冲突
  • 启动超时(增加 healthcheck 的 start_period)

Q4:rsync 同步太慢或卡住?

优化方法:

  1. 排除不必要的文件:
1
2
3
4
5
6
switches: -avzr --delete \
--exclude=node_modules \
--exclude=.git \
--exclude=coverage \
--exclude=*.log \
--exclude=.env
  1. 用压缩(-z 已启用):网络慢时特别有用

  2. 检查 SSH 密钥性能:ED25519 比 RSA 快

  3. 换 rsync 镜像:用国内镜像加速 GitHub Actions 的 rsync

Q5:Docker 镜像太大,部署慢?

优化方法:

  1. 用 alpine 基础镜像:
1
2
3
FROM node:22-alpine    # 100MB
# 而不是
FROM node:22 # 900MB
  1. 多阶段构建:前面已讲

  2. 合并 RUN 指令:

1
2
3
4
5
6
7
8
9
# ✅ 好:一层
RUN apk add --no-cache git curl && \
npm install -g pm2 && \
npm ci --production

# ❌ 差:三层
RUN apk add --no-cache git curl
RUN npm install -g pm2
RUN npm ci --production
  1. 清理缓存:
1
2
RUN npm ci && \
npm cache clean --force

Q6:Let’s Encrypt 证书申请失败?

常见原因:

  1. 域名还没解析到 VPS:先等 DNS 生效(用 dig example.com 检查)

  2. 80 端口被占用:确保 Nginx 没在运行,或者用 standalone 模式

  3. 申请次数超限:Let’s Encrypt 有速率限制,不要频繁申请

解决方案:用 DNS 验证(不受端口限制)

1
sudo certbot certonly --manual --preferred-challenges dns -d example.com

Q7:内存不够用,容器被杀?

现象:

1
2
docker ps    # 容器消失
dmesg | grep oom-killer # 看到内存不足日志

解决方案:

  1. 限制容器内存:
1
2
3
4
5
6
services:
app:
deploy:
resources:
limits:
memory: 512M
  1. 加 Swap:
1
2
3
4
5
6
7
8
# 创建 2GB swap
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# 永久生效
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
  1. 升级 VPS:如果是长期项目,建议至少 1GB 内存(可以用 Vultr 的高内存实例)
问题现场解剖:异常记录 ⚠️ 从问题出发:表面症状是如何一步步演变为生产故障或降权的? 独立开发者工具箱 · CH-59 · 核心卡点传导链 1. 利用层缓存 把变化少的指令放前面 需通过真实环境实测核验 症结:认知偏差与配置盲区 2. 用BuildKit缓存挂载 bash 需通过真实环境实测核验 传导:隐性劣化与边界失守 3. GitHub Actions缓存 yaml 需通过真实环境实测核验 破局:量化指标与工程验证 ✅ 破局核心原则(结论先行) 独立开发者最该自动化的就是一件事:把「本地Docker Compose写代码→git push→GitHub Actions自动测试、构建镜像、部署到VPS→Telegram通知」这条…

6、FAQ(独立开发者最常问的 5 个问题)

Q1:项目还在早期,一定要上全套 CI/CD 吗?什么时候开始自动化?

不用一上来就全上。MVP 阶段一天改不了几次代码,手动部署完全够。但只要出现下面任一信号,就该至少把「push → 自动构建 → 部署」这条主干自动化:

  • 同一天部署超过 3 次
  • 已经有真实用户在用,凌晨翻车会丢用户
  • 部署步骤多到要照着备忘录敲

监控(Uptime Kuma)要早于花哨的 CI 配齐,上线第一天就该有,否则出问题你最后一个知道。

Q2:GitHub Actions 免费额度够用吗?私有仓库要不要花钱?

够。免费账户私有仓库每月 2,000 分钟,公共仓库无限免费。一个中小项目单次 CI 大概 5-10 分钟,一个月几十次部署远用不完。要留意两点:ubuntu runner 按 1 倍计费,macOS runner 按 10 倍计费(独立开发者基本只用 ubuntu);以及从 2026 年 3 月 1 日起,私有仓库跑自托管 runner 收 $0.002/分钟的"平台费",GitHub 把它算进你的免费分钟额度里抵扣(按 list price 计)。GitHub 托管 runner 不收平台费(仍走免费 2,000 分钟/月);公共仓库完全免费,包括自托管 runner。

Q3:镜像仓库选 Docker Hub 还是 GitHub Container Registry?

独立开发者建议直接用 GitHub Container Registry(ghcr.io)。它目前存储和流量都免费,公共、私有镜像拉取都没有限速;而 Docker Hub 免费账户每 6 小时只给 200 次匿名拉取,生产环境多拉几次就撞限。GHCR 还有个隐形好处:在 Actions 里用内置的 GITHUB_TOKEN 就能登录推送,不用再去 Docker Hub 建账号、往 Secrets 里存一份密码。

Q4:docker-compose 和 docker compose,到底用哪个?

用 docker compose(中间是空格)。这是 V2,用 Go 写的,随 Docker Desktop 自带,是当前主推版本。docker-compose(连字符)是 V1,Python 写的,2023 年 6 月已停止维护。多数机器上两个命令都能跑,因为 V2 提供了兼容别名,但新项目一律写 V2。另外,新写的 docker-compose.yml 也不需要再写 version: '3.8' 这一行,Compose 规范已把它标为废弃,写了也会被忽略并告警。

Q5:没有 VPS,能直接用 Vercel / Cloudflare Pages 这类托管吗?

能,而且建议先试。纯前端或 Serverless 全栈(Next.js、Remix 等)直接上 Vercel / Cloudflare Pages,零运维、免费额度够 MVP,git push 就上线,连这套 CI/CD 都不用自己搭。只有当你需要长驻进程时才该上 VPS:定时任务、WebSocket、爬虫、数据库长连接、自定义系统服务,或者后端跑得多、PaaS 按量计费比 VPS 还贵的时候。

问题整改前后解剖:FAQ(独立开发者最常问的5个问题) ⚠️ 整改前:常见踩坑与失分现场 • 盲区 1:忽视「Q1:项目还在早期,一定…」边界约束 • 盲区 2:缺乏「Q2:GitHub Actions免费…」实测核验 • 盲区 3:遗漏「Q3:镜像仓库选Docker…」兜底监控 后果:线上隐性故障频发 / 评估等级下调 ✅ 整改后:高确定性工程实践 • 动作 1:Q1:项目还在早期,一定要上全套CI/CD吗?什… • 动作 2:Q2:GitHub Actions免费额度够用吗?私有仓… • 动作 3:Q3:镜像仓库选Docker Hub还是GitHub… 收益:指标可观测、配置可复现、风险可阻断

下一篇预告

下一篇我们来讲 容器化最佳实践,会更深入地介绍:

  • 如何设计高效的 Dockerfile
  • Docker Compose 高级配置
  • 容器安全加固
  • 多容器编排技巧
  • 多环境部署(适合团队协作项目)

这套工作流我已经用了两年多,支撑了多个项目从 0 到 1 再到稳定盈利。独立开发者的时间很贵,把重复劳动自动化,才能把精力用在真正重要的事情上,打磨产品、服务用户、探索新可能。

独立开发者工具箱系列,欢迎关注!



💡 透明声明:本文包含 5 处推广链接(2 个独立服务,如 Vultr / Cloudflare / Tailscale 等)。每次点击我会收到一笔佣金,不影响你的购买价格,也不影响我的内容判断,工具好不好用我自己用了才知道。完整推广政策见 ittinker.com/about。

数据与引用

  • GitHub Actions 2026 年免费政策(2026 年 3 月起):私有仓库免费档仍是 2,000 Linux 分钟/月 + 500 MB 制品存储;2026 年 3 月 1 日起,新增 $0.002/分钟的"平台费",并把自托管 runner 也纳入免费分钟池,意味着公共仓库仍然完全免费(hosted runner 不收费),私有仓库存量自托管 runner 也开始按 list price 抵扣免费额度。
    来源:GitHub Actions 2026 定价更新,GitHub Changelog 2025-12-16
    含义:独立开发者用公共仓库存代码、迁移到 GHCR 的玩法不受影响;如果你的项目是私有仓 + 大量自托管 runner 做构建,从 2026 年 3 月起可能要付一点平台费(多数个人项目仍吃不完免费额度)。

  • GitHub Actions 2026 年 1 月降价:hosted runner 单价在 1 月 1 日已下调最高 39%,Linux 2 核从 $0.008/分钟降到 $0.006,Windows 从 $0.016 降到 $0.010,macOS 从 $0.080 降到 $0.062。
    来源:Is GitHub Actions Free in 2026,CICDCalculator.com
    含义:即使你不小心跑超免费额度,超额成本比 2025 年便宜约 1/3;多数小型项目跑不超免费层不用 care。

  • GitHub Octoverse 2025 全球开发者大盘:平台 2025 年新增 3,600 万开发者,总用户 1.8 亿+;merge 了 5.187 亿 PR(+29% YoY),9.86 亿次 commit(+25% YoY)。
    来源:GitHub Octoverse 2025,Forbes 摘要
    含义:CI/CD 工作流从"加分项"变成"基础设施",你搭这套自动化不是过度工程,是在跟进主流玩家都在做的事。

  • GitHub Actions Marketplace 规模:2025 年时收录的官方/社区 Actions 已超 20,000 个,每月跑约 2.63 亿 jobs,单日算力 4,100 万+ build hours,容器化 workflow 同比 +25%。
    来源:GitHub statistics 2025,TMS Outsource
    含义:绝大多数需要的步骤(缓存、Lint、推送镜像、SSH 部署、发 Telegram 通知)都有现成 Action,文章里那几行 uses: actions/... 是踩了全社区验证过的轮子。

  • Docker Compose V1 → V2 迁移:docker-compose(连字符)是 Python 写的 V1,2023 年 6 月已停止维护;docker compose(空格)是 V2,Go 写的,随 Docker Desktop 内置分发,是当前主推。多数机器 V2 提供兼容别名所以两个命令都能跑。
    来源:Docker Compose V2 GA,Docker 官方博客
    含义:文章里写的 docker compose up -d 还能跑,但你手写新脚本、装新机器时一律用 V2,避免装老镜像踩维护终止的坑。新版 docker-compose.yml 顶部那行 version: '3.8' 也已经不需要写了(Compose 规范已标废弃)。

  • 本机 Docker 镜像变小技巧的实际收益:多阶段构建 + node:22-alpine 基础镜像,能把 Node.js 镜像从约 900 MB 压到约 100 MB;拉镜像时间、磁盘占用、构建缓存体积同比例下降。
    来源:Docker Hub,node 镜像标签页
    含义:这是 FAQ Q4 之外另一个"日常肉眼可见"的优化,COLD 部署从 5 分钟降到 30 秒,长任务挂着不被网络抖动拖垮。

  • VPS 商家定价(2026 年 4 月):Hetzner 4 月 1 日起 CX/CAX 系列涨 33-38%(DRAM/NVMe 成本,对老客户也生效)但 20 TB/月带宽仍是三家最宽松;DigitalOcean、Vultr 同等配置 $12-24/月。
    来源:DigitalOcean vs Vultr vs Hetzner 2026,BitDoze
    含义:CI/CD 流水线把 VPS 部署从"手工运维"变成"自动恢复",同等 $12/月 的内存/带宽/IO 差距就是从"勉强支撑"到"从容应对并发"的差距。

  • VPS 商家定价(2026 年 4 月):Hetzner 4 月 1 日起 CX/CAX 系列涨 33-38%(DRAM/NVMe 成本,对老客户也生效)但 20 TB/月带宽仍是三家最宽松;DigitalOcean、Vultr 同等配置 $12-24/月。
    来源:DigitalOcean vs Vultr vs Hetzner 2026,BitDoze
    含义:CI/CD 流水线把 VPS 部署从"手工运维"变成"自动恢复",同等 $12/月 的内存/带宽/IO 差距就是从"勉强支撑"到"从容应对并发"的差距。

问题整改前后解剖:数据与引用 ⚠️ 整改前:常见踩坑与失分现场 • 盲区 1:忽视「GitHub Actions 2026年…」边界约束 • 盲区 2:缺乏「GitHub Actions 2026年1…」实测核验 • 盲区 3:遗漏「GitHub Octoverse 2025…」兜底监控 后果:线上隐性故障频发 / 评估等级下调 ✅ 整改后:高确定性工程实践 • 动作 1:GitHub Actions 2026年免费政策(2026年3月… • 动作 2:GitHub Actions 2026年1月降价:hosted… • 动作 3:GitHub Octoverse 2025全球开发者大盘:平… 收益:指标可观测、配置可复现、风险可阻断