集群数据库自动更新

实现功能提要:我们在某服务器上搭建了测试环境进行项目功能开发,同时在其他多个集群上部署了多套相同的系统供公司内部人员使用,由于系统还在持续功能添加和完善中,每隔一段时间都需要对其他集群的系统进行手动更新,而更新的第一步就是数据库更新,为保证数据能完整同步到其他系统,我们测试过程中涉及到的DDL和DML操作都会记录在一个表更新sql文件中,便于其他系统数据库执行;但手动更新一段时间后发现每个集群系统更新进度不一样,导致每次数据库手动更新十分麻烦,为次,我准备写一个数据库自动更新脚本,在进行系统更新前,只需要一个启动脚本命令便可实现数据库更新。

脚本文件由三部分组成:start.sh,ansible.yaml,sql(sql目录下有new.sql,old.sql,diff.sql三个文件)

1、启动脚本start.sh 

#!/bin/sh

#进入当前脚本目录
cd `dirname $0`;

# 旧文件清理
sql_path="./sql"
if [ -d "${sql_path}" ]; then
   rm -rf ${sql_path}/*
else
   mkdir ${sql_path}
fi
touch ${sql_path}/old.sql  ${sql_path}/new.sql  ${sql_path}/diff.sql

newsql="${sql_path}/new.sql"
oldsql="${sql_path}/old.sql"
diffsql="${sql_path}/diff.sql"

# 在bm-api-server pod中执行解压命令解压bm-server-biz.jar文件
bm_api_server=$(kubectl get pods -n bm-system | grep -e "^bm-api-server" | awk '{print $1}' | uniq)
kubectl -n bm-system exec -it $bm_api_server -- /bin/bash -c "jar -xvf bm-server-biz.jar BOOT-INF/classes/db/migration/V1.0.4__update_20221228.sql"

#复制old.sql到node01上
kubectl cp -n bm-system $bm_api_server:BOOT-INF/classes/db/migration/V1.0.4__update_20221228.sql ${oldsql}

#判断old.sql是否复制下来
if [ -s "${oldsql}" ]; then
   echo "Then ols.sql is ok"
else 
   echo "Then old.sql is empty."
   exit 1
fi

#从gitlab仓库获取最新版本的表更新sql文件
ansible-playbook ./ansible.yaml

#判断new.sql是否复制下来
if [ -s "$newsql" ]; then
   echo "Then new.sql is ok."
   kubectl -n bm-system exec -it $bm_api_server -- /bin/bash -c "rm -rf BOOT-INF/"
else 
   echo "Then new.sql is empty."
   exit 1
fi

# 找出new.sql中不同的行
diff_lines=$(diff "$newsql" "$oldsql" | grep "^<" | sed -e 's/^< //')

# 输出不同行
if [[ -n "$diff_lines" ]]; then
  echo "Different lines in $newsql:"
  echo "$diff_lines" >> "${diffsql}"
else
  echo "There is no difference between $newsql and $oldsql."
  exit 1
fi

# 在数据库中执行需要更新的sql
database=$(kubectl get pods -n bm-system | grep -e "^metadata" | awk '{print $1}' | uniq)
kubectl -n dmcp-system exec -it ${database} -- /bin/bash -c "[ -f ./diff.sql ] && rm diff.sql && touch diff.sql;[ ! -f ./diff.sql ] && touch diff.sql"

#复制diff.sql复制到metadata pod中
kubectl cp -n bm-system ./sql/diff.sql $database:diff.sql

#执行sql
kubectl -n bm-system exec -it $database -- /bin/bash -c "cd /opt/dmdbms/bin ; ./disql BM/Baomi <<EOF
start /home/dmdba/diff.sql
EOF"

2、ansible.yaml

- name: Checkout new.sql file from the Git repository
  hosts: localhost
  become: true
  vars:
    git_repository: https://username:password@gitlab.com/group/repository.git
    git_branch: master
  tasks:
    - name: Clone the Git repository
      git:
        repo: "{{ git_repository }}"
        dest: /tmp/repo
        update: yes
        version: "{{ git_branch }}"
        depth: 1
        recursive: yes
    - name: Copy file to destination
      copy:
        src: /tmp/repo/bm-server-biz/docs/update.sql
        dest: /root/bm-sql-update/sql/new.sql

实现思路是,首先需要获取当前集群运行系统的表更新sql文件,其中一个办法就是到包含该文件的子系统中获取,由于该子系统的pod中是有maven构建的jar包的,只需要将jar包中表更新文件解压至指定目录,然后复制到集群本地,称该获取的文件为old.sql文件;然后通过ansible 的git获取到最新文件,称为new.sql;然后对比两个文件的差异(主要是要获取到从上次更新到目前新增的sql语句),将差异写进diff.sql中;最后将diff.sql复制到数据库pod中,并连接数据库实例,批量执行sql,于是就实现了该集群数据库的更新。

整个过程不难理解,只是在测试过程中出现了一些小的问题,下面将记录这些问题和解决方案。

1)如何实现在pod外面执行pod内部命令

使用kubectl exec命令进入到pod,再使用-c参数执行命令,-c 选项用于指定要执行命令的容器名称,指定该选项后,kubectl exec 命令将在指定的容器内执行命令。如果不指定该选项,则默认使用第一个容器。
如果要执行多条命令,可以使用分号 ; 或双竖线 || 连接多条命令。例如,要在指定的容器内执行两个命令,可以这样写:

kubectl exec -it podname -c containername sh -c "command1; command2"

在上面的命令中,command1 和 command2 是要执行的两个命令。sh -c 表示要使用 Bash shell 来解释命令。
如果需要在多个容器中执行命令,可以使用 -c 选项指定多个容器名称,并使用 && 连接多个命令。例如:

kubectl exec -it podname -c container1 sh -c "command1" && \
kubectl exec -it podname -c container2 sh -c "command2"

在上面的命令中,&amp;&amp; 表示只有前一个命令成功执行后,才会继续执行后续命令。这样可以确保命令执行的顺序和成功率。

2)实现k8s本地和pod间文件传输

"kubectl cp "命令用于将文件从pods复制到本地路径,反之亦然。

  1. 将文件从pod复制到本地

    kubectl cp <pod_name>:<file_path> <destination_path>

  2. 将文件从pod的特定容器复制到本地

    kubectl cp <pod_name>:<file_path> <destination_path> -c specific_container

  3. 将文件从本地复制到pod

    kubectl cp <local_source_path> <pod_name>:<destination_path>

 但是要注意的是,pod冒号后要直接加根目录,不能加“/”,否则报错。如以下命令会报错,需要将home前面的/去掉,另外本地路劲必须指向具体文件而不是一个目录,如将./test.cap改成./就会报错。

kubectl cp aks-ssh2-6cd4948f6f-fp9tl:/home/azureuser/test.cap ./test.cap

3)执行脚本的时候报错:syntax error: unexpected end of file!

用 Linux 系统编写的 shell 脚本,在 vim 的命令模式下,执行 :set ff 就能看到 fileformat 类型是 unix ,那么你的 shell 脚本里存在语法错误,可以使用二分法逐段检查错误,具体就是注释一部分,留一部分,然后执行 sh -n 脚本名字 来检查错误,知道排查处错误为止。

4)jar包解压某个文件到目录命令  

jar -xvf bm-server-biz.jar BOOT-INF/classes/db/migration/update_20221228.sql

 5)shell脚本细节

1、判断文件是否为空。使用文件路径作为参数,并使用 -s 测试操作符检查文件是否为空,r若为空则用exit 1 停止整个脚本,如:

file_path=$file
if [ -s "$file_path" ]; then
  echo "file is not empty."
else 
  echo "file is empty."
  exit 1
fi

2、进入当前脚本目录

cd `dirname $0`  或者 cd $(dirname $0)

3、判断文件或者目录是否存在

# -f判断文件
FILE=/etc/resolv.conf
if [ -f "$FILE" ]; then
    echo "$FILE exist"
fi
# -d判断目录
FILE=/etc/docker
if [ -d "$FILE" ]; then
    echo "$FILE is a directory"
fi
# 或者
[ -d /etc/docker ] && echo "$FILE is a directory"

https://www.cnblogs.com/tongye/p/10646211.html

6)将拉到本地/tmp/repo/目录下的内容删除后,再执行ansible.yaml,出现报错:fatal: [localhost]: FAILED! => {"before": "19529cb0506ce62ea5609b3e97cc958618e5f226", "changed": false, "msg": "Local modifications exist in repository (force=no)."}

该错误提示说明Git仓库中已经存在本地未提交的修改,并且在执行git clone或git pull命令时指定了force=no参数,因此Git不允许覆盖本地的修改。解决方案是:强制覆盖本地修改;通过强制拉取Git仓库并覆盖本地修改。

cd /tmp/repo
git reset --hard origin/master
git clean -f -d
git pull origin master

上述命令将会重置Git仓库的状态,并将本地工作目录中的所有文件还原为与远程仓库相同状态。


优化:

经测试,该数据库更新脚本能够实现数据库的更新,但是缺点也很明显:如脚本中涉及到ansible剧本,并不是所有的集群环境里都有ansible,所以需要想办法把这部分功能换成shell命令;另外就是实现过程还是有些复杂且不够自动化,经过导师的建议,我决定对其进行改进,利用Jenkins实现一个全自动化集群数据库更新方案,如下是实现的jenkinsfile脚本:

def checkoutRepo(repoUrl, path) {
    checkout([
        $class: 'GitSCM',
        branches: [[name: 'origin/master']],
        doGenerateSubmoduleConfigurations: false,
        extensions: [[$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [[path: path]]]],
        submoduleCfg: [],
        userRemoteConfigs: [[credentialsId: 'lsn', url: "git@git.damengbm.com:bm/${repoUrl}"]]
    ])
}
pipeline {
    agent {
        node {
            label 'bm_115.160'
        }
    }
    stages {
        stage('change cluster') {
            steps {
                script {
                    def config_cluster = "${params.CONFIG_CLUSTER}"
                    withCredentials([file(credentialsId: config_cluster, variable: 'CONFIG_FILE')]) {
                    sh '''
                      cat $CONFIG_FILE > /root/.kube/config
                      export KUBECONFIG=/root/.kube/config
                      kubectl -n bm-system get pods
                    '''
                    }
                }
            }
        }
        stage('get old sql') {
            steps {
                sh '''
                   mkdir -p sql-update
                   touch sql-update/server_old.sql sql-update/ticket_old.sql
                   touch sql-update/server_new.sql sql-update/ticket_new.sql
                   cp /home/bm/workspace/bm-api-server/bm-server-biz/docs/表变更update.sql sql-update/server_old.sql
                   cp /home/bm/workspace/bm-api-ticket/bm-server-ticket/doc/表变更-update.sql sql-update/ticket_old.sql
                 '''
                }
        }
        stage('git Checkout get new sql') {
             steps {
                script {
                    checkoutRepo("bm-api-server.git", "bm-server-biz/docs/表变更update.sql")
                    sh '''
                      cp bm-server-biz/docs/表变更update.sql sql-update/server_new.sql
                      rm -rf bm-server-biz/
                    '''
                    checkoutRepo("bm-api-ticket.git", "bm-server-ticket/doc/表变更-update.sql")
                    sh '''
                      cp bm-server-ticket/doc/表变更-update.sql sql-update/ticket_new.sql
                      rm -rf bm-server-ticket/
                    '''
                }
            }
        }
        stage('compare SQL Files') {
            steps {
                sh '''
                    diff --unchanged-line-format= --old-line-format= --new-line-format='%L' sql-update/server_old.sql sql-update/server_new.sql > sql-update/diff.sql || :
                    diff --unchanged-line-format= --old-line-format= --new-line-format='%L' sql-update/ticket_old.sql sql-update/ticket_new.sql >> sql-update/diff.sql || :
                '''   
            }
        }
        stage('copy diff.sql File') {
            steps {
                sh '''
                    database=$(kubectl get pods -n bm-system | grep -e "^metadata" | awk '{print $1}' | uniq)
                    echo ${database}
                    kubectl cp -n bm-system sql-update/diff.sql ${database}:diff.sql
                '''
            }
        }
        stage('execute diff.sql File') {
            steps {
                sh '''
                    database=$(kubectl get pods -n bm-system | grep -e "^metadata" | awk '{print $1}' | uniq)
                    kubectl -n bm-system exec -i $database -- /bin/bash -c "cd /opt/dmdbms/bin ; ./disql DM_BM/Dbm8888 <<EOF
                    start /home/dmdba/diff.sql
                    EOF"
                '''
            }
        }
        
    }
}

最新版本:之前的版本获取old.sql是通过到api-server工程工作区下获取,也就意味着sql-update和api-server两个必须有一个先后执行顺序,即先执行sql-update再执行api-server,否则将不能获取到旧的sql文件,因为jenkins工程构建时会先检查源码管理部分是否开启,开启的话就根据url拉取代码,代码更新后再执行构建步骤中的shell脚本,我有想过改动api-server使其在拉取代码前先把旧的sql文件备份,查询到如下资料:

在Jenkins中,工程的执行顺序是可以根据你的配置进行调整的。
默认情况下,在构建过程中,Jenkins会先进行源代码拉取(从配置的Git仓库URL中拉取代码),然后执行构建步骤中定义的操作(如执行Shell脚本)。
这是因为源代码获取是构建过程中的一项关键步骤,通常需要先获取代码才能进行后续的构建和测试操作。
如果你的构建过程需要在代码拉取之前执行某些操作,你可以使用Jenkins的插件或命令来自定义构建流程。例如:

1、Pre-SCM Build Step插件:该插件允许在源代码管理之前执行某些操作。你可以在构建步骤中添加一个"Pre-SCM Build Step"来定义在代码拉取之前需要执行的脚本或命令。
2、在构建步骤中添加额外的步骤:你可以在构建步骤中添加多个步骤,并根据需要进行排序。通过调整步骤的顺序,你可以控制在代码拉取之前或之后执行特定的操作。

请注意,这些配置选项和插件的可用性可能取决于你所使用的Jenkins版本和安装的插件。建议参考Jenkins的文档和插件文档,以了解更多关于自定义构建流程的选项和最佳实践。

因为安装新的插件又要重启服务,而jenkins重启给我带来了很多麻烦,于是没有查到有这个插件后就放弃了这个办法;下面是尝试第二种办法:

#!/bin/bash
# 备份旧的sql文件

# 清理旧的代码目录(可选)
rm -rf api-server

# 克隆 Git 仓库到本地,只拉取api-server目录
git clone --depth 1 --branch <branch-name> <Git repository URL> api-server

# 切换到api-server目录
cd api-server

# 可以执行其他构建步骤,例如编译、测试等

# 在这之后需要执行的其他命令或脚本

 第二个办法在测试过程中也发现了新的问题,也就是将旧的sql文件备份到old.sql后,如果先构建api-server,那么old.sql中是他上一次的旧文件,但到了构建sql-update时,他如何判断old.sql是新的还是旧的呢?要想在sql-update中顾全所有构建情况,这将是个很麻烦的事情,于是不得不想其他办法,比如将sql-update独立起来,不让他依赖api-server获取旧文件,这时我才意识到早该这样做了,下面是最新的jenkinsfile(也可以用shell脚本实现,因为在jenkinsfiile中执行shell命令太多要注意的了,之后会再尝试一下):

def checkoutRepo(repoUrl, path) {
    checkout([
        $class: 'GitSCM',
        branches: [[name: 'origin/master']],
        doGenerateSubmoduleConfigurations: false,
        extensions: [[$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [[path: path]]]],
        submoduleCfg: [],
        userRemoteConfigs: [[credentialsId: 'lsn', url: "git@git.dameng.com:mcp/${repoUrl}"]]
    ])
}
pipeline {
    agent {
        node {
            label 'mcp_113.160'
        }
    }
    stages {
        stage('change cluster') {
            steps {
                script {
                    def config_cluster = "${params.CONFIG_CLUSTER}"
                    withCredentials([file(credentialsId: config_cluster, variable: 'CONFIG_FILE')]) {
                    sh '''
                      cat $CONFIG_FILE > /root/.kube/config
                      export KUBECONFIG=/root/.kube/config
                      kubectl -n dmcp-system get pods
                    '''
                    sh "echo '正在连接${params.CONFIG_CLUSTER}集群'"
                    }
                }
            }
        }
        stage('get diff sql') {
            steps {
              script {
                //sh 'mkdir sql-update'
                checkoutRepo("mcp-api-server.git", "mcp-server-biz/docs/表变更update.sql")
                sh """
                  if [ ! -f 'sql-update/server${params.CONFIG_CLUSTER}' ]; then
                    touch sql-update/server${params.CONFIG_CLUSTER}
                    cp mcp-server-biz/docs/表变更update.sql sql-update/diff.sql
                  else
                    diff --unchanged-line-format= --old-line-format= --new-line-format='%L' sql-update/server${params.CONFIG_CLUSTER} mcp-server-biz/docs/表变更update.sql > sql-update/diff.sql || :
                  fi
                  rm -rf mcp-server-biz/
                """
               /* checkoutRepo("mcp-api-ticket.git", "mcp-server-ticket/doc/表变更-update.sql")
                sh """
                  if [ ! -f 'sql-update/ticket${params.CONFIG_CLUSTER}' ]; then
                    cp mcp-server-ticket/doc/表变更-update.sql sql-update/ticket${params.CONFIG_CLUSTER}
                    cat mcp-server-ticket/doc/表变更-update.sql >> sql-update/diff.sql
                  else
                    diff --unchanged-line-format= --old-line-format= --new-line-format='%L' sql-update/ticket${params.CONFIG_CLUSTER} mcp-server-ticket/doc/表变更-update.sql >> sql-update/diff.sql || :
                  fi
                  rm -rf mcp-server-ticket/
                """*/
              }
            }
        }
        stage('copy diff.sql File') {
            steps {
                sh '''
                    database=$(kubectl get pods -n dmcp-system | grep -e "^metadata" | awk '{print $1}' | uniq)
                    echo ${database}
                    kubectl -n dmcp-system exec -i $database -- /bin/bash -c \'if [ ! -f "./diff.sql" ]; then touch diff.sql; fi\'
                    kubectl cp -n dmcp-system sql-update/diff.sql ${database}:diff.sql
                '''
            }
        }
        stage('execute diff.sql File') {
            steps {
                sh '''
                    database=$(kubectl get pods -n dmcp-system | grep -e "^metadata" | awk '{print $1}' | uniq)
                    kubectl -n dmcp-system exec -i $database -- /bin/bash -c "cd /opt/dmdbms/bin ; ./disql DM_MCP/Dameng8888 <<EOF
                    start /home/dmdba/diff.sql
                    EOF"
                '''
            }
        }
        stage('update sql file') {
            steps {
                sh """
                    cat sql-update/diff.sql >> sql-update/server${params.CONFIG_CLUSTER}
                """
            }
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,我无法找到直接回答您问题的信息。引用\[1\]提到了Raft协议中的数据提交和同步过程,但没有提到自动更新数据库过期数据的具体方法。引用\[2\]提到了使用canal解决数据异构问题和生成结构化数据的应用场景,但没有提到自动更新数据库过期数据。引用\[3\]提到了WAL的作用,但没有提到自动更新数据库过期数据的实现方式。 根据我的专业知识,要实现每过30秒自动更新数据库过期数据,可以考虑使用定时任务或调度器来定期触发数据库更新操作。具体实现方式可能因数据库类型和应用程序架构而异。您可以在应用程序中设置一个定时器,每过30秒触发一次数据库更新操作,或者使用专门的调度器工具来执行定时任务。在数据库更新操作中,您可以根据数据的过期时间进行筛选,并更新相应的数据。 请注意,具体的实现方式可能因您使用的数据库和应用程序框架而有所不同。建议您参考相关文档或咨询相关领域的专家以获取更准确的解决方案。 #### 引用[.reference_title] - *1* *3* [云计算与云原生 — ETCD 数据库完全解析](https://blog.csdn.net/Jmilk/article/details/124262406)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [解决redis缓存和数据库的数据一致性问题](https://blog.csdn.net/LC_Liangchao/article/details/122078186)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值