第 3 章第 2 节:迁移计划书
迁移数据库的决定已经做出。接下来就要付诸行动了。此刻感觉就像是站在悬崖边,但不得不往下跳。现在的问题是能不能在跳下去之前弄个降落伞。
我们花了数个小时详细以文字形式记录下迁移的每一个步骤,就如同对飞机起飞前做的航前检查。对于类似这种情况的高风险操作,最好不要临场发挥。提前做好计划,然后严格按计划行事最合适。否则一个小失误就可能是致命的。
架构设计:迁移前和迁移后的对比
目标是从单台不堪重负的服务器扩展成两台各司其职的组合。
迁移前:单台服务器(例如公网 IP:104.248.62.77 )搞定全部:Nginx ,Gunicorn ,Django 以及 PostegreSQL 。
迁移后:
应用服务器 (公网 IP 同迁移前:104.248.62.77 ): 负责运行 Nginx ,Gunicorn 以及 Django 。
数据库服务器(新公网 IP:142.93.218.155 ):只运行 PostgreSQL 数据库。
因此应用服务器不再同处于 localhost (意为在此同一台机器上)的数据库进行通信。现在它需要通过网络连接与新的专用数据库服务器对话。
以下便是我们制定的迁移计划书。如果以后你也需要完成类似的在线数据库迁移,那以下步骤即使看上去很吓人,那也得硬着头皮上。
步骤 1:提前准备好新的数据库服务器
如同搬家之前得先等新房造好,我们迁移数据库的第一步就是弄一台专属的服务器。
所以我们回到 DigitalOcean 的页面准备新建一个水滴( Droplet )服务器。不过这次我们选择的付费套餐跟之前的通用类型有所区别,这次我们特意挑了一个为存储优化过的类型。这种服务器类型拥有更快的 SSD 存储(称之为 NVMe )以及更多的内存空间,显然这是为了数据库的数据输入输出任务量身定制的。这将是我们全新进阶版本的图书馆。
服务器创建好之后,我通过 SSH 连接上去安装唯一需要的软件:PostgreSQL 数据库。其它比如 Nginx ,Python 或者任何其它应用层代码都不需要。这台服务器的职责很明确:好好保管我们的数据就行了。我还加上了防火墙配置,只允许从我们的应用服务器连接到这台数据库。互联网上的其它人就别妄想读取我们的数据了。这如同给予了我们的应用服务器一把特殊私密的进入图书馆的钥匙。
步骤 2:备份数据( pg_dump )
这是最关键的一步。如何复制一个时刻可能产生数据变化的数据库呢?简单地复制数据库文件没用,因为数据库文件在被复制时可能正被修改,移动文件可能导致数据永久损坏。
有一种办法是给数据库状态来一张完美的快照。对于 PostgreSQL 而言,pg_dump 就是这个魔法工具。
pg_dump 是一个可以完整读取整个数据库(包括所有的数据库表,所有数据,所有表之间的关系等等)并生成一个 sql 后缀超大单文件的命令行工具。这个文件包含了从零开始重建整个数据库所需的全部 SQL 指令。
可以设想一下:pg_dump 就是一个神奇的抄写员,他走进图书馆然后读完了每一本书,最后写下一本全新的大部头书籍:“重建图书馆馆藏的指南”。
所以,在我们之前那台不堪重负的服务器上,我运行了以下命令:
pg_dump -U postgres dukaan_prod > dukaan_backup.sql
我紧张地盯着服务器的 CPU 开始飙升。它正在很努力地创建数据库快照。几分钟之后,任务完成了。我们成功地生成了包含整个公司灵魂的备份文件:dukaan_backup.sql 。
步骤 3:传输并恢复备份
现在我们的备份数据已就绪,但是所处位置不对。我们需要把备份文件安全地从旧服务器转移到新的数据库专属服务器。为了完成这个任务,我们需要另外一个命令行工具 scp ( Secure Copy )。
scp dukaan_backup.sql
[email protected]:/root/
这条指令将会安全地把我们的备份文件通过网络传输到新服务器。传输完毕后,新图书馆就拥有了完整的重建手册。
现在可以开始重建了。我 SSH 登录到新的数据库服务器,创建了一个新的(空空如也)名为 dukaan_prod 数据库,然后开始执行重建指令:
psql -U postgres -d dukaan_prod < dukaan_backup.sql
这条指令执行的内容与 pg_dump 正好相反。它从大部头的备份文件中一条一条读取指令然后执行。它会创建数据库表,插入数据并重建表之间的约束关系。我盯着屏幕,祈祷不要出错。几分钟之后,重建完成了。
步骤 4:切换
这一步是验证迁移是否正确完成的决定时刻,也是最危险的一步。我们将把 Dukaan 应用的连接从旧数据库切换到新数据库。这不可避免地导致网站会有几分钟不可访问。
启动维护模式:第一步是禁止任何新数据的写入。我们在网站上发布了一个“正在维护”的页面,任何此时访问
mydukaan.io 的用户都将被告知:“Dukaan 网站正在升级,请 5 分钟后再回来。”
增量数据的同步:在我们创建备份文件到此时做切换之间,网站产生了一些新的数据。比如一些新店铺,一些新产品的更新。所以我们需要再次重复上述步骤 2 (备份数据)和步骤 3 (传输并恢复备份)。因为这次网站已经处于维护模式,备份和恢复的速度会更快一些。这样可以确保我们的新数据库是跟原库完全一致的。
更新应用的数据库连接配置:这一步正是实施心脏外科手术的那一刀。在我们 Django 应用的配置文件中有一条关于数据库连接的配置项。类似 HOST:'localhost'这样。现在我们把它指向新的数据库专属服务器的 IP 地址:HOST:'142.93.218.155'。
重启并祷告:数据库新连接的配置保存完毕后,我敲下了重启应用服务器的指令:sudo systemctl restart gunicorn 。那之后的几秒里,我的心紧张地提到了嗓子眼。Dukaan 应用正在重启,然后将试图通过网络第一次向新数据库发起通话。
疯狂测试:Gunicorn 重启之后,我和苏米特开始尝试网页上的一切按钮。能不能登录?没问题。店铺信息能不能正确显示?可以。能不能添加一个新产品?也可以!成功了。数据库连接正确无误。
关闭维护模式:测试完成之后,我们长舒一口气,解除了网站的维护模式。
整个迁移过程中,我们的网站只有 3 分钟不能访问。
分离应用和数据库的升级成功完成。我们的应用现在拥有了自己独立的厨房,我们的数据也安全的保存在专属的图书馆中。用户们几乎立刻就高兴地告诉我们“网站感觉变快了。”我们顺利熬过了第一次重大架构升级。厨房变得更整洁,图书馆也变得井井有条,大厨和管理员现在可以尽情施展,完全不用担心会互相影响。