如何仅使用主git分支使我的项目从开发人员自动部署到阶段,并在Jenkins中手动部署到生产

时间:2019-05-03 20:47:59

标签: git jenkins jenkins-pipeline

我大约有30个Wordpress网站,因此Jenkins的配置方式是每个网站都有一份工作。开发过程如下(我不知道它是否最佳,但这就是我们的方法):

  1. 由于我们有外包开发人员,因此他们将自己的存储库托管在自己的存储库托管提供程序中。只要代码准备好进行质量检查,他们就会将所有更改提交到master分支中的存储库中。
  2. 然后我使用jenkinsfile手动运行Jenkins作业,如下所示。
  3. 工作流必须为:部署到开发环境,并且仅当先前的部署成功时才部署到阶段。在这里,我们必须停下来,让质量检查人员检查网站上是否有任何损坏的链接,错误等。
  4. 如果一切看起来都与我们预期的一样,并且未发现任何错误,则最终将其部署到生产环境中。

注意:有人建议我只进行分期和制作。之所以没有该配置,是因为无法在线访问开发环境,原因是因为我使用此环境来测试后端配置(例如apache conf等)。

另外,还有其他一些人建议为每个环境都创建一个分支,这在理论上是有意义的,但是我认为这将改变我们的外包开发人员将代码提交到存储库的方式,我的意思是,他们总是必须将代码提交到dev分支,然后合并到stage分支以部署到stage,我认为这不是很好。

现在,步骤2-4如下所示: 为了给您一个有关该过程外观的示例,我们将提供一个名为“ Bearitos”的示例网站和工作:

enter image description here

在名为“ Bearitos”的工作之外,还有一个名为“任何人的贝雷托斯”的项目。

enter image description here

这基本上意味着在该项目中,我有一个配置为三个阶段的管道:dev,staging和prod,它们使用以下参数进行参数化:DEPLOY_TO:Dev / staging / prod和DEPLOY_DB:是/否。因此,根据用户的选择,Jenkins将部署到该特定环境,我认为甚至没有必要使用这些选项,因为应该开发正确的部署流程->登台->产品,所以不应该出现这种情况。在哪里跳过开发或暂存,然后直接在生产环境附近部署,所以我认为应该对此进行更好的更新

enter image description here

在Jenkinsfile里面,我定义了三个阶段Dev,Staging或Prod,还定义了选择是否构建数据库的选项,以下是我的Jenkinsfile外观的示例:

// Deployment template for CMS-based websites (Drupal or Wordpress)
// 
//
pipeline {
agent any

parameters {
choice(choices: "Dev\nStaging\nProduction", description: "Choose which environment to push changes to.", name: "DEPLOY_TO")
choice choices: "No\nYes", description: "Choose whether to deploy the database as well.", name: "DEPLOY_DB"
}

environment {
SITEID = "ge"
NOFLAGS = "0"
DBNAME = "wpress_website"
DBSERVER = "dbserver"
DBUSER = "geWordpress"
DBPASS = "akjh23kas"
EXCLUDE = "comp_commentmeta,comp_comments" // separate multiple tables with commas
DEPLOY_TO = "${params.DEPLOY_TO}"
DEPLOY_DB = "${params.DEPLOY_DB}"
}

stages {
stage("deploy-db-dev") {
when {
allOf { 
environment ignoreCase: true, name: "DEPLOY_TO", value: "dev"; 
environment ignoreCase: true, name: "DEPLOY_DB", value: "yes"; 
}
}
steps {
// this stage only required until we make our dev the master DB
// copy full dev database from appserv1
// import latest database dump to dev server
script {
FILENM = sh(script: 'ls -t goewp-s-dump* | head -1', returnStdout: true)
}
//Fixing the problem with the collation existing in the sql dump file, refer to: https://stackoverflow.com/questions/42385099/1273-unknown-collation-utf8mb4-unicode-520-ci 
//apparently, this is due to a version of mysql issue. Once the problem is fixed from the server side we can then remove the following lines.

sh "sed -i s/utf8mb4_unicode_520_ci/utf8mb4_unicode_ci/g ${FILENM}"
//The following line was added because the site is pointing to a staging server which we don't have control over, again, once this is fixed we can delete the following line of code. 
sh "sed -i s/edit.staging.websites.3pth.com/stage.goewpfoods.hcgweb.net/g ${FILENM}"

sh "mysql -h appserver -u ${env.DBUSER} --password='${env.DBPASS}' ${env.DBNAME}_dev < ${WORKSPACE}/${FILENM}"
}
}
stage("deploy-dev") {
when {
environment ignoreCase: true, name: "DEPLOY_TO", value: "dev"
}
steps {
// copy files to appserv2
// NOTE: if we move the repo to SVN, we should change httpdocs/ to ${env.SITEID}docs/
sh "sudo chown jenkins:jenkins *"

//Replace the wp-config.php file with our comp file with our information. 
sh "/bin/cp httpdocs/wp-config-comp.php httpdocs/wp-config.php"

// prepare the dev server to receive files by changing the owner
sh "ssh webadmin@appserv2 \"sudo chown -R webadmin:webadmin /var/opt/httpd/${env.SITEID}docs/\""
// copy files from control server to dev
sh "rsync --exclude=Jenkinsfile -rav -e ssh --delete ${WORKSPACE}/httpdocs/ webadmin@appserv2:/var/opt/httpd/${env.SITEID}docs/"
// fix the owner/permissions on the dev server
sh "ssh webadmin@appserv2 \"sudo chown -R apache:${env.SITEID}-web /var/opt/httpd/${env.SITEID}docs/\""
sh "ssh webadmin@appserv2 \"sudo chmod -R g+w /var/opt/httpd/${env.SITEID}docs/\""
sh "ssh webadmin@appserv2 \"sudo find /var/opt/httpd/${env.SITEID}docs/ -type d -exec chmod g+s {} \\;\""
}
}
stage("deploy-db-staging") {
when {
allOf { 
environment ignoreCase: true, name: "DEPLOY_TO", value: "staging"; 
environment ignoreCase: true, name: "DEPLOY_DB", value: "yes"; 
}
}
steps {
script {
def myexcludes = env.EXCLUDE.split(',').toList()
MYFLAGS = "-Q -K -c -e --default-character-set=utf8 "
if (env.NOFLAGS == "0") {
myexcludes.each {
MYFLAGS = "${MYFLAGS} --ignore-table=${env.DBNAME}_dev.${it}"
}
}
}
sh "cd ${WORKSPACE}"
// pull a backup of the current dev database (may exclude some tables)
sh "mysqldump -h appserv2 -u ${env.DBUSER} --password='${env.DBPASS}' ${env.DBNAME}_dev ${MYFLAGS} > ${env.DBNAME}_dev.sql"
// create a backup copy of the current staging database (full backup)
sh "mysqldump -h ${env.DBSERVER} -u ${env.DBUSER} --password='${env.DBPASS}' ${env.DBNAME}_stage > ${env.DBNAME}_stage_bak.sql"
// upload the dev database dump to the staging database
sh "mysql -h ${env.DBSERVER} -u ${env.DBUSER} --password='${env.DBPASS}' ${env.DBNAME}_stage < ${WORKSPACE}/${env.DBNAME}_dev.sql"
}
}
stage("deploy-staging") {
when {
environment ignoreCase: true, name: "DEPLOY_TO", value: "staging"
}
steps {
// copy files from dev to control server
sh "rsync --exclude=.svn --exclude=.git -rav -e ssh webadmin@appserv2:/var/opt/httpd/${env.SITEID}docs/ /tmp/${env.SITEID}docs/"

//Replace the wp-config.php file with our comp file with our information. 
sh "/bin/cp httpdocs/wp-config-comp.php httpdocs/wp-config.php"

// prepare the staging server to receive files by changing the owner
sh "ssh webadmin@stageserv \"sudo chown -R webadmin:webadmin /var/opt/httpd/${env.SITEID}docs/\""
// copy files from control server to staging
sh "rsync --exclude=.svn --exclude=.git -rav -e ssh --delete /tmp/${env.SITEID}docs/ webadmin@stageserv:/var/opt/httpd/${env.SITEID}docs/"
// fix the owner/permissions on the staging server
sh "ssh webadmin@stageserv \"sudo chown -R apache:${env.SITEID}-web /var/opt/httpd/${env.SITEID}docs/\""
sh "ssh webadmin@stageserv \"sudo chmod -R g+w /var/opt/httpd/${env.SITEID}docs/\""
sh "ssh webadmin@stageserv \"sudo find /var/opt/httpd/${env.SITEID}docs/ -type d -exec chmod g+s {} \\;\""

// delete the temporary files on the control server
sh "rm -Rf /tmp/${env.SITEID}docs/"
// clear the caches
sh "wget -O - \"http://www.web.net/incacache.php?api_key=yoiVbjgtL&site_id=088\""
}
}
stage("deploy-db-production") {
when {
allOf { 
environment ignoreCase: true, name: "DEPLOY_TO", value: "production"; 
environment ignoreCase: true, name: "DEPLOY_DB", value: "yes"; 
}
}
steps {
script {
def myexcludes = env.EXCLUDE.split(',').toList()
MYFLAGS = "-Q -K -c -e --default-character-set=utf8 "
if (env.NOFLAGS == "0") {
myexcludes.each {
MYFLAGS = "${MYFLAGS} --ignore-table=${env.DBNAME}_stage.${it}"
}
}
}
sh "cd ${WORKSPACE}"
// pull a backup of the current staging database (may exclude some tables)
sh "mysqldump -h ${env.DBSERVER} -u ${env.DBUSER} --password='${env.DBPASS}' ${env.DBNAME}_stage ${MYFLAGS} > ${env.DBNAME}_stage.sql"
// create a backup copy of the current production database (full backup)
sh "mysqldump -h ${env.DBSERVER} -u ${env.DBUSER} --password='${env.DBPASS}' ${env.DBNAME}_prod > ${env.DBNAME}_prod_bak.sql"
// upload the staging database dump to the production database
sh "mysql -h ${env.DBSERVER} -u ${env.DBUSER} --password='${env.DBPASS}' ${env.DBNAME}_prod < ${WORKSPACE}/${env.DBNAME}_stage.sql"
}
}
stage("deploy-production") {
when {
environment ignoreCase: true, name: "DEPLOY_TO", value: "production"
}
steps {
// copy files from staging to control server
sh "rsync --exclude=.svn --exclude=.git -rav -e ssh webadmin@stageserv:/var/opt/httpd/${env.SITEID}docs/ /tmp/${env.SITEID}docs/"

// prepare the production server to receive files by changing the owner
sh "ssh webadmin@appserv3 \"sudo chown -R webadmin:webadmin /var/opt/httpd/${env.SITEID}docs/\""
sh "ssh webadmin@appserv4 \"sudo chown -R webadmin:webadmin /var/opt/httpd/${env.SITEID}docs/\""
// copy files from control server to production
sh "rsync --exclude=.svn --exclude=.git -rav -e ssh --delete /tmp/${env.SITEID}docs/ webadmin@appserv3:/var/opt/httpd/${env.SITEID}docs/"
sh "rsync --exclude=.svn --exclude=.git -rav -e ssh --delete /tmp/${env.SITEID}docs/ webadmin@appserv4:/var/opt/httpd/${env.SITEID}docs/"
// fix the owner/permissions on the production server
sh "ssh webadmin@appserv3 \"sudo chown -R apache:${env.SITEID}-web /var/opt/httpd/${env.SITEID}docs/\""
sh "ssh webadmin@appserv4 \"sudo chown -R apache:${env.SITEID}-web /var/opt/httpd/${env.SITEID}docs/\""
sh "ssh webadmin@appserv3 \"sudo chmod -R g+w /var/opt/httpd/${env.SITEID}docs/\""
sh "ssh webadmin@appserv4 \"sudo chmod -R g+w /var/opt/httpd/${env.SITEID}docs/\""
sh "ssh webadmin@appserv3 \"sudo find /var/opt/httpd/${env.SITEID}docs/ -type d -exec chmod g+s {} \\;\""
sh "ssh webadmin@appserv4 \"sudo find /var/opt/httpd/${env.SITEID}docs/ -type d -exec chmod g+s {} \\;\""

// delete the temporary files on the control server
sh "rm -Rf /tmp/${env.SITEID}docs/"
// clear the caches
sh "wget -O - \"http://www.web.net/incacache.php?api_key=yoiVbjgtL&site_id=088\""
}
}
}

这种方法目前面临的问题是:

  1. 我不知道该如何自动化部署 参数化的管道,所以我不确定如何使其自动化。的 所需的过程将是使部署一次自动化 Jenkins每隔X分钟在git存储库中轮询一次, 自动部署到“ Dev> Stage”(仅在Dev部署成功的情况下),然后停止 在那里,直到我们对暂存进行质量检查后手动部署到Prod。

  2. 当前的Git配置仅配置了一个分支 (主),这是开发人员在更改完成后立即进行推送的位置 想要部署到开发->阶段->产品。但我认为 理想的情况是为开发部署提供一个开发分支 用于部署到Stage环境的舞台分支,然后针对 一旦我们将这些开发人员和暂存分支合并到 主分支。我不确定这是否是最佳选择,所以我会 感谢对此的任何建议或想法。

期望的方法将是解决提到的问题,并且一旦开发->阶段部署成功,还具有自动的方式来部署和通知。就像我们现在正在做的那样,除了可以选择手动执行所提到的工作流外(这并不重要,但拥有功能会很不错)。

提前感谢您的帮助!

4 个答案:

答案 0 :(得分:2)

实现一个能够部署到所有环境并被参数化的单独的部署管道,然后实现另一个管道,该管道被隔离并触发将管道部署到dev(阶段dev),并且当这项工作成功时,就会触发管道再次部署到阶段(阶段Qa)。然后可以手动完成产品的部署。

https://jenkins.io/doc/pipeline/steps/pipeline-build-step/#-build-%20build%20a%20job

答案 1 :(得分:2)

  1. 摆脱掉您的参数,它们是不需要的(会阻止您自动化)。
  2. deploy to prod阶段有一个手动输入
pipeline {
    agent any
    stages {
        stage('Deploy to prod') {
            input {
                message "Should we continue?"
                ok "Yes, we should."
            }
            steps {
                echo "Deploying."
            }
        }
    }
}
  1. 这应该是Jenkins中的多分支管道项目(因为您想在所有分支上都使用它)
  2. 如果您想对不同的分支使用不同的阶段,请使用when
pipeline {
    agent any
    stages {
        stage('Example Build') {
            steps {
                echo 'Hello World'
            }
        }
        stage('Example Deploy') {
            when {
                branch 'production'
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}

关于建议-我要说的是,您需要将git流与CI / CD流进行匹配。给定git分支类型的生命周期是什么?给定阶段的结果是什么?您是否要对所有分支执行阶段,对deploy to prod仅对一个分支执行阶段?

答案 2 :(得分:1)

pipeline 
{
        stages 
        {
               stage('Build')
               {
                     steps
                     {
                        echo 'building the codes from the git'
                      }
                }
                stage('developer-branch-stuff')
                {
                   when
                   {
                       branch 'developer'
                   }
                   steps
                   {
                      echo 'run this stage - only if the branch = developer branch'
                   }
                }
        stage('Deliver for development') 
        {
            when 
            {
                branch 'developer'
            }
            steps 
            {
                sh 'your_filename_along_with_your_filepath'
                input message: 'shall we deploy it? (Click "Proceed" to continue)'
            }
        }
        stage('Deploy for production') 
        {
            when
            {
                branch 'developer'
            }
            steps
            {
                sh 'your_filename_along_with_your_filepath'
                input message: 'shall we proceed to production? (Click "Proceed" to continue)'
            }
        }
    }
}

答案 3 :(得分:0)

我会在这里使用git tags

首先,我将进行 Dev 构建工作,该工作配置为侦听我的master分支中的所有提交,然后开始构建代码,运行单元测试,将应用程序部署到开发环境等。如果构建成功,当构建成功时,它将使用特定的版本号标记git存储库。

现在, Staging 构建作业将被配置为Dev构建作业的下游作业,并且仅在Dev构建作业成功时才执行。运行时,它将版本号作为上游Dev构建作业的参数,并检出该特定版本的代码。如果失败,它将从存储库中删除该特定的版本标记。

如果您想在以上两个阶段中的任何一个阶段中进行手动测试,并据此将构建标记为“通过/失败”,则有一个Fail the build Jenkins插件。如果在进行手动测试后将构建标记为“失败”,则还应删除与该构建对应的特定版本标记。

此后,您可以手动进行发布作业,该作业会列出git存储库中的所有版本标记,这些标记标记了所有成功的构建。您可以选择任何一个版本并启动Release作业,该作业会将与该版本有关的代码部署到生产中。