最近运维的一台服务器,突然收到了磁盘告警。上去一看,df -h 显示根目录 /dev/vda1 的使用率已经超过 95%。
这台服务器是 CentOS 7.2 老系统,上面跑着几个网站,网站文件都放在 /www/wwwroot 目录下。很明显,是网站数据把硬盘塞满了。
业务不能停,直接加一块硬盘是最快的办法。服务商给加了一块盘,系统里识别为 /dev/vdb。
现在的目标很明确:
- 把 
/www/wwwroot里的所有数据,都挪到新硬盘上。 - 整个过程不能停掉 Nginx,网站要一直能访问。
 - 不能修改任何 Nginx 或网站代码里的路径配置。
 - 数据不能丢。
 
一番琢磨,最终的方案是利用软链接来"骗"过系统和程序。思路清晰,操作起来也放心。
准备新家:处理新硬盘
活儿要一步步干。先把新硬盘准备好,让它在系统里能用起来。这个过程分为三小步:分区、格式化、挂载。
1. 给新硬盘分区
一块全新的硬盘,就像一个没划分房间的毛坯房,得先给它分区。我用的是 fdisk 命令。
            
            
              bash
              
              
            
          
          fdisk /dev/vdb
        进去之后,操作很简单,就是用 n 新建分区,然后一路回车,使用默认的设置,让这个分区占满整块硬盘。最后用 w 保存退出。这之后,系统里就多了一个叫 /dev/vdb1 的分区了。
可以用 lsblk 命令看看,确认一下分区是不是建好了。
2. 格式化分区
分区好了,就要格式化,相当于给房间铺上地砖。CentOS 7 默认推荐用 XFS 文件系统,性能不错。
            
            
              bash
              
              
            
          
          mkfs.xfs /dev/vdb1
        这个命令跑完,分区就有了自己的文件系统。
3. 挂载硬盘
现在,要把这个准备好的分区,挂到系统的一个目录上,我们才能访问它。我打算把它挂载到 /data 目录。
先创建这个目录:
            
            
              bash
              
              
            
          
          mkdir /data
        然后挂载上去:
            
            
              bash
              
              
            
          
           mount /dev/vdb1 /data
        用 df -h 看一下,如果列表里出现了 /data 目录,并且对应的是 /dev/vdb1,那就说明成功了。
但这样只是临时挂载,服务器重启后就没了。所以要把它写进开机启动项里。
先用 blkid 命令查一下新分区的 UUID,这是一串唯一的识别码,比设备名 /dev/vdb1 更可靠。
            
            
              bash
              
              
            
          
          blkid /dev/vdb1
        复制下那串 UUID 的值,然后编辑 /etc/fstab 文件。
            
            
              bash
              
              
            
          
          vim /etc/fstab
        在文件最后加上一行:
            
            
              ini
              
              
            
          
          UUID=刚才复制的那串字符   /data   xfs   defaults   0   0
        保存退出。这样,新硬盘的"新家"就彻底安顿好了。
无感迁移数据
硬盘准备好了,接下来就是最关键的一步:在不影响网站的情况下,把数据挪移
这里的核心工具是 rsync 和 ln -s。
rsync用来同步文件,它非常强大,能保持文件所有属性不变。ln -s用来创建软链接,也就是我们用来"欺骗"程序的快捷方式。
1. 第一次数据同步
首先,要把 /www/wwwroot 的数据完整地复制一份到 /data。因为网站文件可能很多,这个过程会比较慢,但它不影响线上运行。
在开始前,先给新目录设置好和老目录一样的权限。
            
            
              bash
              
              
            
          
          # 查看老目录的所有者,比如是 www
ls -ld /www/wwwroot
# 创建新目录
mkdir /data/wwwroot
# 设置和老目录一样的所有者和权限
chown www:www /data/wwwroot
chmod --reference=/www/wwwroot /data/wwwroot
        然后开始同步:
            
            
              bash
              
              
            
          
          rsync -avz --delete /www/wwwroot/ /data/wwwroot/
        这个命令会把 /www/wwwroot/ 目录下的所有内容,原封不动地同步到 /data/wwwroot/。-a 参数保证了权限、时间戳这些信息都不变。--delete 确保目标目录和源目录完全一致。
这个过程耗时十几分钟,耐心等着就行。网站依然在老地方正常服务。
2. 闪电切换
第一次同步完成后,新旧目录的数据基本一样了。但同步期间,网站可能又产生了新数据,比如用户上传了图片。所以,我们需要在切换前,再进行一次极快的增量同步。
操作非常快,需要连续敲两个命令:
            
            
              bash
              
              
            
          
          # 1. 再次快速同步,只同步差异文件,几秒钟就完事
rsync -avz --delete /www/wwwroot/ /data/wwwroot/
# 2. 把老目录改个名,这个操作是瞬间完成的, 然后立刻创建软链接,指向新目录
mv /www/wwwroot /www/wwwroot_old && ln -s /data/wwwroot /www/wwwroot
        这两步操作的间隔是毫秒级的。对于 Nginx 来说,它访问 /www/wwwroot 时,系统会告诉它:"哦,这个地方指向 /data/wwwroot",然后 Nginx 就自动跑去新硬盘上读数据了。整个过程对它来说是透明的,所以服务不会中断。
检查和收尾
切换完成了,但工作还没结束。
首先,马上去点点网站,到处访问一下,看看有没有任何问题。
然后,检查一下链接是否正确:
            
            
              bash
              
              
            
          
          ls -l /www/
        应该能看到 wwwroot -> /data/wwwroot 这样的箭头。
再用 df -h 确认一下,新硬盘 /data 的使用量上去了,老硬盘的空间被释放出来了。
最后,那个被改名的 /www/wwwroot_old 目录,先不要删! 这是你的后悔药。让网站在新位置上稳定运行一两天,确认万无一失之后,再执行删除。
            
            
              bash
              
              
            
          
          # 确认一切正常后,再删除备份
rm -rf /www/wwwroot_old
        至此,整个无缝扩容就完成了。网站没停,配置没改,数据也安全。