Wagtail 项目多环境 migrations 管理方案
一、项目框架背景介绍
Wagtail 是一款基于 Django 开发的 CMS 应用框架,其数据交互依赖于 Django ORM 方法,该方法要求开发者在每次发生数据库改动时,需要依次通过 makemigrations 和 migrate 指令使数据库改动生效。而 makemigrations 指令,会依据项目各 app 模块中的 models 和 migrations 文件夹中数据库迁移脚本的变化,生成初始化或变更的数据库迁移脚本(如:0001_initial.py)。这些数据库迁移脚本,是 Wagtail 项目初始化和改动当前数据库所依赖的必要指导文件。最后,migrate 指令会比对 django_migrations 数据表中已执行数据库迁移脚本和 migrations 文件夹中脚本的差异,将未执行的脚本进行执行,并将执行成功的文件信息存入数据表。
二、问题产生背景
因此,在进行团队项目开发,同时涉及多环境部署的情况下,这种数据库初始化和变更方式会对代码仓的管理有如下的要求:
-
团队中开发者,在进行开发时,数据库必须使用本地数据库。
-
线上不同环境(dev、sit、uat、prod),存在数据库不同的步进情况(一般dev的更新是最频繁的,sit、uat 和 prod 依次减缓),同时,线上发布环境通常在集群发布,容器都是一次性的,所以本地存储不是持久存储,这就需要将服务每次启动初始化产生的 migrations 文件分别进行远程仓库的存储。
为了使代码仓库的管理满足以上要求,则需要:
-
开发者不能将自己本地代码中的 migrations 文件上传到远程仓库,这个可以通过向 .gitignore 中添加目录正则来忽略所有 migrations 文件,不同步到远程仓库。
-
线上的四个不同环境的 migrations 文件需要通过远程仓库进行隔离储存和管理,这意味着,线上发布的代码中,需要包含 migrations 文件,要同步到远程仓库。
三、问题产生
此时,如果简单地通过一个仓库多分支来实施上述需求操作,就会出现一个矛盾,开发者的开发分支(如 dev)不可以上传 migrations 文件,但是向线上 dev 环境发布时又需要上传 migrations 文件到仓库进行存储。
四、问题解决思路
既然 migrations 的存在给项目代码仓库带来了如此不便,那消除它的方式,就是将它抽离出来,单独建仓进行管理。这样项目代码仓永不包含 migrations 文件,只作为程序本身的代码载体,就不会影响到开发人员代码到线上环境代码的合并流程,消除了由于项目代码仓是否包含 migrations 文件产生的矛盾。
五、方案设计
(一)条件准备
-
拥有可以被线上环境访问到的远程仓库。
-
拥有可以执行 git 指令和 bash 脚本的容器环境。
-
容器的 entrypoint 依据环境分别执行代码仓中的 bash 脚本(如 start-dev.sh 等)。
(二)流程设计
(三)实施流程
1. 搭建 migrations 仓库
在 gitlab 上创建 yourproject-wagtail-migrations 仓库,并用 Readme 自述文件进行初始化,再为仓库创建 4 个环境的分支,分别命名为:dev、sit、uat、prod。
migrations 仓库的目录结构,应该保持和项目代码仓一致,类似如下:
/yourproject-wagtail-migrations (690.73KB)
├── app1 (1.91KB)
│ └── migrations (1.91KB)
│ ├── 0001_initial.py (901b)
│ ├── __init__.py (0b)
│ └── __pycache__ (1.03KB)
│ ├── 0001_initial.cpython-38.pyc (905b)
│ └── __init__.cpython-38.pyc (154b)
├── app2 (3.98KB)
│ └── migrations (3.98KB)
│ ├── 0001_initial.py (2.01KB)
│ ├── __init__.py (0b)
│ └── __pycache__ (1.98KB)
│ ├── 0001_initial.cpython-38.pyc (1.83KB)
│ └── __init__.cpython-38.pyc (154b)
└── app3 (13.21KB)
└── migrations (13.21KB)
├── 0003_alter_xxxxx.py (973b)
├── 0001_initial.py (3.94KB)
├── __init__.py (1.42KB)
├── __pycache__ (5.8KB)
│ ├── 0003_alter_xxxxx.cpython-38.pyc (1.11KB)
│ ├── 0002_alter_xxxxx.cpython-38.pyc (801b)
│ ├── 0001_initial.cpython-38.pyc (3.09KB)
│ ├── __init__.cpython-38.pyc (151b)
│ └── 0004_alter_xxxxx.cpython-38.pyc (684b)
├── 0004_alter_xxxxx.py (538b)
└── 0002_alter_xxxxx.py (585b)
2. 编写项目启动脚本 start.sh
编写 start.sh 脚本并放在项目代码仓中,并在 CI/CD 构建镜像时,将该脚本作为 entrypoint 的执行脚本,并在运行容器时执行。
2.1 start.sh 脚本
# 脚本中的 [对应环境] 可由以下关键字替换:dev、sit、uat、prod
#!/bin/bash
service nginx start
# 配置 git
git config --global user.email "公司邮箱"
git config --global user.name "公司域账号"
# 拉取 migrations
git clone -b [对应环境] https://公司域账号:密码@gitlab.yoursite.com/yourgroup/yourproject-wagtail-migrations.git /home/yourproject-wagtail-migrations
# 拷贝 migrations
cp -r /home/yourproject-wagtail-migrations/* /home/yourproject-wagtail-server/
# 执行 migrations
python3.8 manage.py makemigrations --settings zopwagtail.settings.[对应环境]
python3.8 manage.py migrate --settings zopwagtail.settings.[对应环境]
# 备份 migrations
find -name migrations | while read line; do
cp -r $line/* /home/yourproject-wagtail-migrations/$line
done
cd /home/yourproject-wagtail-migrations
git add .
git commit -m "Synchronize migrations: $(date +%Y)-$(date +%m%d)-$(date +%H%M)"
git push
# 初始化服务
cd /home/yourproject-wagtail-server
python3.8 manage.py collectstatic --noinput --settings zopwagtail.settings.[对应环境]
python3.8 manage.py runserver --settings zopwagtail.settings.[对应环境]
(三)补充说明
1. 关于 prod 环境的处理
一般情况,prod 环境出于安全考虑是无法直接访问公司 GitLab 的,所以无法在容器启动时做 migrations 仓库的同步。因此,在CI/CD 构建镜像时,就需要同时拉取 migrations 仓库合并至项目仓库中,执行 makemigrations 指令,并将产生的 migrations 文件变化同步到 migrations 仓库中。
这种情况下,需要对上述流程进行优化,优化结果如下:
在这种流程设计下,CI/CD构建时的脚本可以编写如下:
# 脚本中的 [对应环境] 可由以下关键字替换:dev、sit、uat、prod
git clone -b [对应环境] https://gitlab.yoursite.com/yourgroup/yourproject-wagtail-migrations.git
cd /workspace
mkdir yourproject-wagtail-migrations-prepare
cp -r yourproject-wagtail-migrations/* zop-wagtail-migrations-prepare
# 拷贝 migrations
cd /workspace
cd yourproject-wagtail-migrations-prepare && ls
# 只拷贝项目仓库存在的 migrations 目录
cd /workspace/yourproject-wagtail-server
find -name migrations | while read line; do
if [ -d '/workspace/yourproject-wagtail-migrations-prepare/'$line ]; then
cp -r /workspace/yourproject-wagtail-migrations-prepare/$line/* /workspace/yourproject-wagtail-server/$line
fi
done
cd ..
# 执行 migrations
cd yourproject-wagtail-server && pwd && ls
python3.8 manage.py makemigrations --settings yourprojectwagtail.settings.makemigrations_helper
# 这里的 makemigrations_helper 配置专门用于在构建环境中执行 makemigration 用,内容可以和对应环境的配置文件保持一致
# 删除 migrations 仓库的文件
mkdir -p /workspace/gitbak
mv /workspace/yourproject-wagtail-migrations/.git /workspace/gitbak
rm -r /workspace/yourproject-wagtail-migrations/*
mv /workspace/gitbak/.git /workspace/yourproject-wagtail-migrations
# 备份 migrations
find -name migrations | while read line; do
mkdir -p /workspace/yourproject-wagtail-migrations/$line
cp -r $line/* /workspace/yourproject-wagtail-migrations/$line
done
# 提交代码
cd /workspace/yourproject-wagtail-migrations
git add .
git commit -m "Synchronize migrations: $(date +%Y)-$(date +%m%d)-$(date +%H%M)"
git push
此时,所有环境的 start.sh 脚本可改为如下:
# 脚本中的 [对应环境] 可由以下关键字替换:dev、sit、uat、prod
#!/bin/bash
service nginx start
# 初始化数据库
python3.8 manage.py migrate --settings zopwagtail.settings.[对应环境]
# 初始化服务
python3.8 manage.py collectstatic --noinput --settings zopwagtail.settings.[对应环境]
python3.8 manage.py runserver --settings zopwagtail.settings.[对应环境]