【技术底稿】01:37岁老码农,用4台机器搭了套个人DevOps平台
37 岁老码农,用 4 台机器搭了套个人 Dev_Ops 平台,从 0 到 1 实战全过程
37 岁,14 年 Java 开发经验,我决定给自己搭一套「个人技术数据中心」。
从一台 32G 内存的开发机,到两台 16G Ubuntu 部署机,再加上一台阿里云轻量服务器,我要把分散在各处的服务、代码、数据,收拢成一个可一键启动、可自动部署、可公网访问的 DevOps 底座。
这不仅是为了软考「系统架构设计师」攒论文素材,更是为了给 40 岁之前的自己,留下一份可追溯、可复用、可展示的技术成长档案。
2026 年 3 月,我从 0 到 1 完成了第一步:把多机器环境,变成一键部署的公开服务。
一、我的硬件家底:4 台机器的个人数据中心
我用四台机器,搭起了一个小型分布式环境,覆盖开发、部署、备用、公网访问全场景:
| 机器 | 配置 | 核心角色 |
|---|---|---|
| 32G 开发机 | 32G 内存 + Windows | 开发环境、Ollama 模型、本地虚拟机调试 |
| 16G HP 服务器 | 16G 内存 + Ubuntu 22.04 | 核心部署节点:Jenkins、MySQL、Redis、Nginx、业务服务 |
| 16G 联想笔记本 | 16G 内存 + 待部署 | 备用节点,后续做双机高可用 |
| 阿里云 2 核 2G | 2 核 2G + Ubuntu | 公网业务节点:管理后台、小程序、数据库 |
整体架构如下:
- 开发机提交代码到 Gitee
- HP 服务器上的 Jenkins 自动拉取、编译、部署
- 通过 FRP 内网穿透,让公网可以访问本地 Jenkins
- 同时支持一键部署到阿里云,实现多环境发布
二、Docker Compose:用一个 YML 把所有服务跑起来
核心思路
把 MySQL、Redis、两个 Java 后端、Nginx 全部容器化,用docker-compose.yml统一管理,实现一条命令启动整个平台,再也不用手动启服务。
完整配置(已脱敏)
yaml
version: '3.8'
services:
# MySQL 8 数据库(持久化数据)
mysql:
image: mysql:8.0
container_name: mysql8
restart: always
environment:
MYSQL_ROOT_PASSWORD: mysql_123!
MYSQL_DATABASE: shangzhuhui
ports:
- "3306:3306"
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/conf:/etc/mysql/conf.d
command: --default-authentication-plugin=mysql_native_password
networks:
- app-network
# Redis 缓存(带密码保护)
redis:
image: redis:latest
container_name: redis
restart: always
ports:
- "6379:6379"
volumes:
- ./redis/data:/data
command: ["redis-server", "--requirepass", "111111"]
networks:
- app-network
# 小程序后端 API
shangzhuhui-api:
image: eclipse-temurin:17-jre-alpine
container_name: shangzhuhui-api
restart: always
depends_on:
- mysql
- redis
ports:
- "8081:8081"
volumes:
- ./shangzhuhui-api/shangzhuhui-api-1.0.0.jar:/app.jar
environment:
- SPRING_PROFILES_ACTIVE=dev
- SPRING_DATASOURCE_PASSWORD=mysql_123!
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/shangzhuhui?useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghai
- SPRING_DATA_REDIS_HOST=redis
- SPRING_DATA_REDIS_PORT=6379
- SPRING_DATA_REDIS_PASSWORD=111111
command: ["java", "-Xmx512m", "-jar", "/app.jar"]
networks:
- app-network
# 管理后台后端
shangzhuhui-admin:
image: eclipse-temurin:17-jre-alpine
container_name: shangzhuhui-admin
restart: always
depends_on:
- mysql
- redis
ports:
- "8080:8080"
volumes:
- ./shangzhuhui-admin/shangzhuhui-admin-1.0.0.jar:/app.jar
environment:
- SPRING_PROFILES_ACTIVE=dev
- SPRING_DATASOURCE_PASSWORD=mysql_123!
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/shangzhuhui?useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghai
- SPRING_DATA_REDIS_HOST=redis
- SPRING_DATA_REDIS_PORT=6379
- SPRING_DATA_REDIS_PASSWORD=111111
command: ["java", "-Xmx512m", "-jar", "/app.jar"]
networks:
- app-network
# Nginx:托管前端静态资源 + 反向代理API
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/html:/usr/share/nginx/html
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- shangzhuhui-api
- shangzhuhui-admin
networks:
- app-network
networks:
app-network:
driver: bridge
启动与验证
bash
运行
# 一键启动所有服务
docker-compose up -d
# 查看服务运行状态
docker-compose ps
# 浏览器访问验证
http://192.168.18.100/admin
验证成功截图:

- 登录页:
http://192.168.18.100/admin/login - 管理后台首页:
http://192.168.18.100/admin/dashboar
三、Jenkins Pipeline:从代码提交到自动部署,5 分钟搞定
核心目标
实现 代码推送 → 5 分钟自动更新服务,覆盖前后端编译、产物拷贝、Docker 重启全流程,彻底告别手动部署。
本地流水线核心逻辑
- 拉取代码:从 Gitee 拉取 develop 分支代码
- Maven 打包:编译两个 Spring Boot 服务,生成 jar 包
- 前端构建:分别构建 Admin 管理后台和 H5 / 小程序
- 拷贝产物:把 jar 包和前端 dist 目录拷贝到 Docker 部署目录
- 部署服务:通过 SSH 免密调用宿主机脚本,重启 Docker 服务
关键代码片段(本地部署)
groovy
// 阶段5:部署Docker服务(核心:SSH免密调用宿主机脚本)
stage('部署Docker服务') {
steps {
echo "开始通过SSH调用宿主机部署脚本..."
sh """
ssh -i ${SSH_KEY_PATH} \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
xyy@${HOST_IP} ${DEPLOY_SCRIPT}
"""
echo "✅ Docker服务部署完成!"
}
}
阿里云流水线扩展
在本地流水线基础上,我扩展出阿里云部署版本,解决公网部署权限问题:
- 先把产物上传到
/tmp目录(普通用户可写) - 用
sudo mv把文件移动到正式目录(规避权限不足) - 执行
sudo /home/xgk/start_backend.sh restart重启后端服务
构建成功截图:

- Jenkins 构建日志:显示所有服务部署成功
- 阿里云 Jenkins 首页:展示流水线运行状态
四、FRP 内网穿透:公网访问本地 Jenkins
核心目标
让外网用户可以访问内网 Jenkins 服务,实现跨机器自动化部署,随时随地触发构建。
核心配置(已脱敏)
frps.ini(阿里云服务端)
ini
[common]
bind_port = 7000
token = frp_token
dashboard_port = 7500
dashboard_user = admin
dashboard_pwd = 123456
log_file = ./frps.log
log_level = info
log_max_days = 3
frpc.toml(本地客户端)
toml
serverAddr = "你的阿里云IP"
serverPort = 7000
auth.token = "frp_token"
# 代理 Jenkins 8082 端口到阿里云 18082
[[proxies]]
name = "jenkins-local"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8082
remotePort = 18082
验证
访问 http://阿里云IP:18082,即可看到 Jenkins 界面,实现公网访问内网服务。

五、安全加固:让系统 7×24 小时稳定运行
1. UFW 防火墙(最小权限原则)
只开放必要端口,关闭所有非核心服务:
bash
运行
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw allow 8082/tcp # Jenkins
sudo ufw enable
2. MySQL 定时备份
每天凌晨 2 点自动备份,保留最近 7 天数据,防止数据丢失:
bash
运行
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/opt/backup/mysql"
docker exec mysql8 mysqldump -uroot -pmysql_123! shangzhuhui > $BACKUP_DIR/db_$DATE.sql
find $BACKUP_DIR -name "db_*.sql" -type f -mtime +7 -delete
3. 资源限制
- Java 服务限制
-Xmx512m,避免内存溢出 - MySQL/Redis 只允许内网访问,不暴露公网
- Jenkins 用非 root 用户运行,避免权限过大
六、踩坑实录:我遇到的那些坑
「实战中总会遇到各种坑,我把这些问题和解决方案整理如下」
- Docker 端口冲突:一开始把 8080 端口给了 Java 服务,后来发现 Jenkins 也要用,改成 8082 解决
- Jenkins 权限问题:容器内无法直接操作宿主机 Docker,改用 SSH 免密调用宿主机脚本绕开
- FRP Token 不一致:客户端和服务端 Token 写错,导致连接失败,反复核对后解决
- 前端路径嵌套:H5 打包后多了一层
build/h5目录,导致 Nginx 404,最后改成dist/build/h5/*直接上传 - 阿里云权限问题:普通用户无法直接写入
/usr/share/nginx/html,改用/tmp中转 +sudo mv规避
七、成果价值
- 效率提升:从手动部署到一键发布,发布时间从 30 分钟缩短到 5 分钟
- 安全可靠:非 root 用户 + 最小权限 sudo,符合企业级安全规范
- 资产沉淀:所有脚本和文档都归档到 Gitee,形成可复用的 DevOps 实战案例
- 软考素材:为「系统架构设计师」论文积累了完整的 DevOps 场景
- 业务支撑:支撑 AI 文案小助手 + 商家助手管理后台双业务公网部署,实现生产级服务迭代
-
业务支撑(阿里云管理后台):访问 http://59.110.31.76/admin 即可进入管理系统,默认首页为 AI 文案小助手,接口已通过 IP 白名单保护。

下一步计划
- 4 月:搭建第二台机器,实现 MySQL 主从复制
- 5 月:迁移到 k3s 集群,体验云原生编排
- 6 月:完善监控告警,实现故障自动感知
写在最后:技术从来不是目的,而是解决问题的工具。这套 DevOps 底座,是我 37 岁这年,给自己最好的一份技术答卷。
更多推荐

所有评论(0)