GitLab CI 示例:利用 CI 同步生产环境数据库到开发环境

问题

我们通常需要频繁的将生产环境(production)的数据库同步到 develop 和 staging 环境。

之前我们的做法是写了一个 shell 在服务器上手动运行,这个需要测试人员和开发人员拥有服务器权限,甚至是生产环境的权限,风险较大且 “不够 CI/CD 化”。

后来是想写一个 HTTP 服务来触发这样的数据库同步操作,增加了额外的工作开销,还要为这个服务配相应的 CI/CD,不如直接使用 GitLab CI 来完成这个工作。

GitLab CI Pipeline 不仅可以在用户 push 代码的时候被触发,还可以通过 HTTP 调用的形式主动触发。

目标

开发 & 测试人员将 production 环境的数据库同步到 develop / staging 环境:

- 无需接触服务器环境
- 任意时间可主动进行同步(相对于计划任务而言)
- 可指定同步某个项目相关的数据库
- 可指定同步到某个服务器环境 ( develop / staging )

预设场景:

- 有三个数据库项目:project-a  project-b  project-c
- 有三个服务器环境:production  develop  staging
- 通过 GitLab CI Pipeline Triggers 来触发执行(HTTP方式)
- 我们可任意指定同步某一个项目的 production 数据库到 develop 或 staging

首先,我们建了一个新的 GitLab Repo,可以叫作 db-sync,并开始配置其 CI Pipeline。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# .gitlab-ci.yml
stages:
- dump
- upload
- restore

before_script:
- if [ $TO = dev ];then export SERVER_HOST=$DEV_SERVER_HOST;fi
- if [ $TO = stg ];then export SERVER_HOST=$STG_SERVER_HOST;fi
- if [ $PROJECT = project-a ];then export MONGO_DB=project-a-db;fi
- if [ $PROJECT = project-b ];then export MONGO_DB=project-b-db;fi
- if [ $PROJECT = project-c ];then export MONGO_DB=project-c-db;fi

cache:
paths:
- tmp_data

dump_db:
image: mongo:latest
stage: dump
script:
- rm -rf tmp_data || true
- mkdir tmp_data || true
- >
mongodump -h $MONGO_HOST --port=$MONGO_PORT -u $MONGO_USER
-p $MONGO_PASSWORD -d $MONGO_DB --out=tmp_data
- cd tmp_data/$MONGO_DB && tar -zcvf ./../data.tar.gz ./*
only:
variables:
- $PROJECT
- $TO

upload_db:
image: laogen/openssh-client:latest
stage: upload
script:
- eval $(ssh-agent -s)
- echo "$SERVER_SSH_PRIV_KEY" > deploy.key
- mkdir -p ~/.ssh
- chmod 0600 deploy.key
- ssh-add deploy.key
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- ssh root@$SERVER_HOST "rm -rf /srv/db-data/$PROJECT/_data/ || true"
- ssh root@$SERVER_HOST "rm -rf /srv/db-data/$PROJECT/data.tar.gz || true"
- scp tmp_data/data.tar.gz root@$SERVER_HOST:/srv/db-data/$PROJECT/data.tar.gz
- ssh root@$SERVER_HOST "mkdir -p /srv/db-data/$PROJECT/_data || true"
- ssh root@$SERVER_HOST "tar zxvf /srv/db-data/$PROJECT/data.tar.gz -C /srv/db-data/$PROJECT/_data"
only:
variables:
- $PROJECT

restore:
image: laogen/openssh-client:latest
stage: restore
script:
- eval $(ssh-agent -s)
- echo "$SERVER_SSH_PRIV_KEY" > deploy.key
- mkdir -p ~/.ssh
- chmod 0600 deploy.key
- ssh-add deploy.key
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- scp restore.sh root@$SERVER_HOST:/srv/db-bak/$PROJECT/restore.sh
- export CONTAINER_ID='$(docker ps -f name='$PROJECT' -f ancestor=bitnami/mongodb -q)'
- ssh root@$SERVER_HOST "docker exec $CONTAINER_ID sh /bitnami/restore.sh"
only:
variables:
- $TO
- $PROJECT

这个 Pipeline 有三个 stages:

- dump 从 prod 数据库中把数据导出为文件
- upload 将数据文件上传到 dev/stg 目标服务器
- restore 通过 ssh 远程连接到目标服务器,将数据文件导入(restore)到 mongo 中

其中涉及的 ssh 等操作前文具体讲过,不了解的话可移步 GitLab CI 示例:Docker 镜像打包发布 & SSH 部署

这段 CI 脚本是我在实际工作中使用的,其中 mongodb 是使用 bitnami/mongodb 镜像启动的,所以会有 bitnami 相关的字眼,需要了解的话请查看文末的相关链接。

如果使用其它 mongo 镜像或者其它数据库( mysql 等)需要做相应的调整,这里只是提供一个思路作为参考。

restore.sh 这个文件在 git repo中,它被挂载到数据库容器中执行的:

1
2
# restore.sh
mongorestore --drop -u $MONGODB_USERNAME -p $MONGODB_PASSWORD -d $MONGODB_DATABASE /bitnami/_data

触发同步

我们通过 $PROJECT 来控制要同步哪个项目的数据库;
通过 $TO 控制要同步到哪个服务器环境(develop / staging)。

1
2
3
4
5
# 同步 project-a 项目的数据库到 dev 环境
curl -X POST -F token=XXXXXXX \
-F ref=master -F "variables[TO]=dev" \
-F "variables[PROJECT]=project-a" \
https://gitlab.com/api/v4/projects/xxxxxx/trigger/pipeline
1
2
3
4
5
# 同步 project-b 项目的数据库到 stg 环境
curl -X POST -F token=XXXXXXX \
-F ref=master -F "variables[TO]=stg" \
-F "variables[PROJECT]=project-b" \
https://gitlab.com/api/v4/projects/xxxxxx/trigger/pipeline

我们通过 GitLab CI 的 HTTP 触发机制触发 Pipeline 的执行。
其中 tokenurl 在 GitLab 仓库的设置页面获取 Settings > CI/CD > Pipeline triggers > Add trigger,该页面中有详情的使用说明。

小结

上面触发的例子是通过 curl 发送请求完成的,也可以使用 Webhook 的方式,这样可以把请求链接加入到浏览器的书签中,需要同步数据库的时候,直接打开相应的书签即可快捷完成。

这种方式不仅可以用来同步数据库,还可以用来同步其它生产环境的数据,比如文件资源等。

同时,除去用来同步数据,也可用于备份数据等工作。

相关链接