某外贸公司Ubuntu云服务器年费从8000元降到2000元的配置优化实录
去年接手这家外贸公司的服务器运维时,第一反应是”这配置是不是有点奢侈”。
客户跑的业务其实很简单:一个基于Laravel的外贸建站系统、一个ERP接口服务、一个定时任务同步海关数据,外加一套邮件推送系统。但他们的云服务器配置是——8核16G内存、100G SSD、带宽5Mbps,年费8000元,用的是阿里云ECS的通用型。
我第一眼就看出了问题:带宽和内存完全超标,CPU利用率长期在15%以下徘徊。
先说结论,优化后他们现在的配置是4核8G内存、30G SSD、带宽3Mbps,月付167元,年费2000出头。
不是夸张,下面我把整个过程完整拆解给你看。
第一次上机:看到的不是数据,是浪费
登录服务器后,我直接跑了一组基础监控命令。
先看CPU和内存的使用情况:
# 实时资源监控脚本(运行5分钟,采样一次)
for i in {1..300}; do
echo "=== $(date '+%H:%M:%S') ==="
uptime
free -h
echo "---"
sleep 1
done
结果很直观:
10:23:15 up 47 days, 3:12, 1 user, load average: 0.02, 0.05, 0.01
total used free shared buff/cache available
Mem: 15Gi 1.8Gi 12Gi 256Mi 1.5Gi 13Gi
Swap: 0B 0B 0B
15G内存,只用了1.8G。
再看带宽和磁盘IO:
# 网络带宽监控
sar -n DEV 1 300 | grep -E "IFACE|eth0|lo" | tail -20
IFACE rxpck/s txpck/s rxkB/s txkB/s
eth0 12.30 8.50 15.20 12.80
峰值也就15KB/s的带宽。 5Mbps的带宽对他们来说,相当于开着法拉利去菜市场买菜——完全够不着。
磁盘方面,100G的SSD里,系统盘占了30G,剩下的全是空余。他们的项目代码加数据库加起来,撑死了20G。
这个配置,说白了就是当初采购时”宁滥勿缺”的产物,没有经过任何实际负载分析。
第二步:摸清真实业务负载
光看静态数据不够,我得知道他们实际在跑什么。
我写了一个更系统的监控脚本,记录24小时的完整数据:
#!/bin/bash
# 24小时资源监控脚本
LOG_DIR="/var/log/server_monitor"
mkdir -p $LOG_DIR
while true; do
TIMESTAMP=$(date '+%Y%m%d_%H%M')
# CPU使用率
top -bn1 | grep "Cpu(s)" | awk '{print $2}' > $LOG_DIR/cpu_${TIMESTAMP}.log
# 内存使用
free -h | grep Mem | awk '{print $3}' > $LOG_DIR/mem_${TIMESTAMP}.log
# 磁盘IO
iostat -x 1 1 | grep -E "Device|vda" >> $LOG_DIR/disk_${TIMESTAMP}.log
# 网络流量
sar -n EDEV 1 1 | grep -E "eth0" >> $LOG_DIR/net_${TIMESTAMP}.log
# 进程TOP10
ps aux --sort=-%mem | head -11 > $LOG_DIR/processes_${TIMESTAMP}.log
sleep 300 # 每5分钟采样一次
done
跑了整整一天后,我把数据整理成这样的图表:
时间 CPU 内存 带宽(KB/s) 磁盘IO
00:00 3% 1.2G 2.1 0.01MB/s
06:00 8% 1.5G 5.3 0.02MB/s
09:00 22% 2.1G 12.8 0.05MB/s
12:00 18% 2.0G 10.5 0.04MB/s
15:00 25% 2.3G 15.2 0.06MB/s
21:00 5% 1.3G 3.0 0.01MB/s
峰值CPU才25%,峰值内存2.3G,峰值带宽15KB/s。
这时候我基本可以确定,4核8G的配置绰绰有余,甚至还可以再降一档。
第三步:服务优化——把能省的全省掉
资源够用只是第一步,真正的大头在软件层面的优化。
3.1 Nginx配置优化
他们之前的Nginx配置几乎是默认值,没有针对外贸网站的场景做调优:
# 优化后的Nginx配置片段
worker_processes auto;
worker_connections 1024; # 原值4096,根本用不了这么多
multi_accept on;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# 静态文件缓存,减少后端压力
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# Gzip压缩,节省带宽
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
这一套下来,带宽节省30%以上,静态资源直接缓存,根本不需要后端响应。
3.2 PHP-FPM调优
原来的PHP-FPM配置:
; 原始配置
pm = ondemand
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 0
外贸网站的PHP请求量其实很低,改成:
; 优化后配置
pm = dynamic
pm.max_children = 16
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 8
pm.max_requests = 500
pm.process_idle_timeout = 10s
内存占用从原来的300M+降到了120M左右。
3.3 MySQL优化
他们的MySQL配置也是默认值,表结构存在不少可以优化的地方:
-- 慢查询分析(运行一周后导出)
SELECT
DIGEST_TEXT AS query,
COUNT_STAR AS exec_count,
AVG_TIMER_WAIT/1000000000 AS avg_time_sec,
SUM_LOCK_TIME/1000000 AS lock_time_ms
FROM performance_schema.events_statements_summary_by_digest
ORDER BY avg_time_sec DESC
LIMIT 10;
结果发现几个问题:
- 有一个商品表没加索引,查询每次都全表扫描
- 日志表没有定期清理,半年前的数据还躺着
- innodb_buffer_pool_size默认只有128M
逐一解决:
-- 添加缺失的索引
ALTER TABLE products ADD INDEX idx_category_status (category_id, status);
ALTER TABLE orders ADD INDEX idx_user_create_time (user_id, created_at);
-- 清理6个月前的日志数据
DELETE FROM operation_logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 6 MONTH);
OPTIMIZE TABLE operation_logs;
-- 调整innodb_buffer_pool_size
SET GLOBAL innodb_buffer_pool_size = 512 * 1024 * 1024;
-- 在/etc/mysql/mysql.conf.d/mysqld.cnf中持久化
-- innodb_buffer_pool_size = 512M
优化后,数据库查询平均响应时间从120ms降到了15ms。
3.4 定时任务整合
原来他们有三个独立的定时任务,分布在不同的crontab里:
# 原来的定时任务
0 2 * * * /usr/bin/php /var/www/erp/artisan queue:work --timeout=300
30 2 * * * /usr/bin/php /var/www/site/artisan schedule:run
0 3 * * * curl https://api.customs.gov.cn/sync-data
整合成一个统一的调度脚本:
#!/bin/bash
# /opt/cron/jobs.sh
set -e
echo "[$(date)] 开始执行定时任务"
# 1. 海关数据同步(优先,必须在其他任务之前)
curl -s "https://api.customs.gov.cn/sync-data" > /dev/null
echo "[$(date)] 海关数据同步完成"
# 2. 队列处理
cd /var/www/erp
/usr/bin/php artisan queue:work --timeout=300 --tries=3 > /var/log/erp_queue.log 2>&1
echo "[$(date)] ERP队列处理完成"
# 3. Laravel调度
cd /var/www/site
/usr/bin/php artisan schedule:run >> /var/log/site_cron.log 2>&1
echo "[$(date)] 站点调度完成"
# 4. 清理临时文件
find /tmp -mtime +1 -delete
echo "[$(date)] 清理完成"
然后统一到一个crontab:
# 统一的crontab
0 2 * * * /opt/cron/jobs.sh
这样不仅方便管理,还避免了三个任务并发时产生的资源争抢。
第四步:监控和告警——便宜不代表不管
降级之后,怎么保证稳定性?我给他们配了一套轻量级的监控方案。
# 安装监控组件
apt install -y htop vnstat net-tools
# 编写监控告警脚本
cat > /opt/monitor/health_check.sh << 'EOF'
#!/bin/bash
# 检查CPU使用率
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
if [ $(echo "$CPU_USAGE > 80" | bc) -eq 1 ]; then
echo "[$(date)] CPU使用率过高: ${CPU_USAGE}%" | tee -a /var/log/monitor.log
# 发送告警通知
curl -s -X POST "https://hooks.example.com/alert" \
-H "Content-Type: application/json" \
-d "{\"text\":\"CPU告警: ${CPU_USAGE}%\"}"
fi
# 检查内存使用率
MEM_USAGE=$(free | grep Mem | awk '{printf("%.0f"), $3/$2 * 100}')
if [ "$MEM_USAGE" -gt 85 ]; then
echo "[$(date)] 内存使用率过高: ${MEM_USAGE}%" | tee -a /var/log/monitor.log
curl -s -X POST "https://hooks.example.com/alert" \
-H "Content-Type: application/json" \
-d "{\"text\":\"内存告警: ${MEM_USAGE}%\"}"
fi
# 检查磁盘使用率
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | cut -d'%' -f1)
if [ "$DISK_USAGE" -gt 90 ]; then
echo "[$(date)] 磁盘使用率过高: ${DISK_USAGE}%" | tee -a /var/log/monitor.log
fi
# 检查关键进程
for service in nginx mysql php-fpm; do
if ! pgrep -x "$service" > /dev/null; then
echo "[$(date)] 服务异常: ${service}" | tee -a /var/log/monitor.log
systemctl restart "$service"
fi
done
EOF
chmod +x /opt/monitor/health_check.sh
然后加入crontab,每分钟检查一次:
* * * * * /opt/monitor/health_check.sh
这套监控用的是他们现有的企业微信 webhook,告警直接推送到钉钉群,零额外成本。
第五步:迁移到新配置
监控和脚本都准备好了,接下来是迁移。
原来的8核16G机器,我们迁移到一台4核8G的新机器上:
# 数据备份(在原服务器上执行)
# 1. 数据库备份
mysqldump -u root -p --all-databases | gzip > /tmp/db_backup_$(date +%Y%m%d).sql.gz
# 2. 网站文件打包
tar -czf /tmp/site_backup_$(date +%Y%m%d).tar.gz \
--exclude='node_modules' \
--exclude='storage/logs/*' \
-C /var/www site/
# 3. 配置文件备份
tar -czf /tmp/config_backup.tar.gz \
/etc/nginx \
/etc/mysql \
/etc/php \
/etc/cron.d \
/opt/cron
新服务器配置:
# 新服务器初始化脚本
#!/bin/bash
# 系统更新
apt update && apt upgrade -y
# 安装基础软件
apt install -y nginx php8.1-fpm php8.1-mysql php8.1-curl \
php8.1-gd php8.1-mbstring php8.1-xml php8.1-zip \
mysql-server mysql-client redis-server \
git curl wget
# 配置nginx
cat > /etc/nginx/sites-available/default << 'NGINXEOF'
server {
listen 80;
server_name your-domain.com;
root /var/www/site/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
}
NGINXEOF
# 配置MySQL
sed -i 's/# innodb_buffer_pool_size = 128M/innodb_buffer_pool_size = 256M/' /etc/mysql/mysql.conf.d/mysqld.cnf
# 重启服务
systemctl restart nginx
systemctl restart mysql
systemctl restart php8.1-fpm
数据迁移:
# 在新服务器上恢复数据
# 1. 恢复数据库
gunzip < /tmp/db_backup_20240115.sql.gz | mysql -u root -p
# 2. 恢复网站文件
tar -xzf /tmp/site_backup_20240115.tar.gz -C /var/www/
chown -R www-data:www-data /var/www/site
# 3. 恢复配置
tar -xzf /tmp/config_backup.tar.gz -C /
迁移完成后,用压力测试验证新配置是否能扛住:
# Apache Bench 压力测试
ab -n 1000 -c 10 https://your-domain.com/
# 测试结果示例
Concurrency Level: 10
Time taken for tests: 12.345 seconds
Complete requests: 1000
Failed requests: 0
Transfer rate: 85.23 kb/s
Connection Times (ms)
min avg max connect
Processing: 45 120 350
Total: 50 125 355
1000个请求,平均响应125ms,零失败。 新配置完全够用。
优化成果
迁移完成后,他们的实际运行数据:
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 服务器配置 | 8核16G | 4核8G |
| 带宽 | 5Mbps | 3Mbps |
| 磁盘 | 100G SSD | 30G SSD |
| 年费用 | 8000元 | 2000元 |
| CPU平均使用率 | 15% | 35% |
| 内存平均使用率 | 12% | 45% |
| 平均响应时间 | 180ms | 85ms |
不仅省钱了,响应还更快了。
一些经验总结
这次优化让我深刻体会到,云服务器配置不是越贵越好,而是要匹配实际负载。
几个关键点:
- 先监控再决策。没有数据支撑的配置调整都是瞎猜。
- 软件优化往往比硬件升级更有效。一次好的代码或配置调整,可能比加1G内存带来的提升还大。
- 定期review配置。业务会增长,配置也要随之调整,不能一劳永逸。
- 监控告警不能省。省钱不代表放弃稳定性,一套廉价的监控方案就能做到心里有数。
现在他们的服务器跑得稳稳当当,每个月还给客户省了6000块。有段时间客户还问我是不是偷偷做了什么升级,因为网站打开速度明显变快了。
我笑了笑没解释——优化从来不是加法,而是做减法。 减掉浪费的,剩下的才是真正的性能。