<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>jenkins &#8211; 有意与无意之间</title>
	<atom:link href="https://zhangxihai.cn/archives/tag/jenkins/feed" rel="self" type="application/rss+xml" />
	<link>https://zhangxihai.cn</link>
	<description>千淘万漉虽辛苦 吹尽狂沙始到金 - 生命不息 编程不止</description>
	<lastBuildDate>Fri, 08 Jan 2021 03:05:15 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.0.11</generator>
	<item>
		<title>Jenkins增量部署 指定文件夹指定文件部署</title>
		<link>https://zhangxihai.cn/archives/81</link>
					<comments>https://zhangxihai.cn/archives/81#respond</comments>
		
		<dc:creator><![CDATA[胖爷]]></dc:creator>
		<pubDate>Sun, 08 Mar 2020 18:04:45 +0000</pubDate>
				<category><![CDATA[编码]]></category>
		<category><![CDATA[jenkins]]></category>
		<guid isPermaLink="false">https://zhangxihai.cn/?p=81</guid>

					<description><![CDATA[1 出发点 使用Jenkins部署代码的办法，一种是打包，一种是连接目标服务器拉取代码，虽然实际操作中会一些差别， 例如打包后上传与下载的区别，但大体就是这两种。 对于打包方法而言，通常是打全量包，或者是指定文件与文件拉取代码打包。但是有时候在非严格的版本控制下，需要增量部署， 或者动态地指定文件夹，动态地指定文件拉取部署；就像瓦力那样，但是又不想更换瓦力。...]]></description>
										<content:encoded><![CDATA[<h2>1 出发点</h2>
<p>使用Jenkins部署代码的办法，一种是打包，一种是连接目标服务器拉取代码，虽然实际操作中会一些差别， 例如打包后上传与下载的区别，但大体就是这两种。</p>
<p>对于打包方法而言，通常是打全量包，或者是指定文件与文件拉取代码打包。但是有时候在非严格的版本控制下，需要增量部署， 或者动态地指定文件夹，动态地指定文件拉取部署；就像瓦力那样，但是又不想更换瓦力。</p>
<h2>2 思路</h2>
<p>通过Git Parameters获取参数。</p>
<h2>3 步骤</h2>
<p><strong>安装扩展 ↓</strong><br />
安装 Pipeple、Git Parameter、Publisher Over SSH，省略过程。</p>
<p><strong>创建pipeline任务 ↓</strong><br />
省略</p>
<p><strong>添加参数 ↓</strong><br />
勾选参数话过程，添加以下参数<br />
<strong>BRANCHORTAG</strong><br />
GIT参数 默认：origin/master</p>
<p><strong>SEGMENT</strong><br />
选项参数 选项： 全量 增量</p>
<p><strong>FILES</strong><br />
文本参数 默认：</p>
<p><strong>添加pipe script 并修改配置 ↓</strong><br />
勾选groovy沙箱</p>
<pre><code class="language-groovy">node {

    def project = [
        &#039;repository&#039;: &#039;git@git.deepcase.cn:root/asinus.git&#039;,                  // 代码仓
        &#039;credentialsId&#039;: &#039;e6d3c193-91ba-429f-bd3a-06a0adf1fa69&#039;               // 凭证
    ]

    def incrementArr = []                                                     // 增量文件
    def isSparse = false                                                      // 是否是指定文件

    def sshServers = [                                                        // Publish Over SSH - SSH Server Config
        &#039;remoteDirectory&#039;: &#039;/home/sync&#039;,                                      // 上传目录 和配置一样 不要以/结尾
        &#039;prefix&#039;: &#039;&#039;,                                                         // 上传至远程服务器重命名前缀
        &#039;directory&#039;: &#039;&#039;,                                                      // 远程服务器上传目录 相对于上面的uploads /开头 不要以/结尾
        &#039;site&#039;: &#039;/home/wwwroot/asinus&#039;,                                       // 远程服务器解压目录 不要以/结尾
        &#039;lists&#039;: [
            [
                &#039;name&#039;: &#039;racknerd&#039;,                                           // 远程服务器配置名
                //&#039;remoteDirectory&#039;： &#039;&#039;，
                //&#039;prefix&#039;: &#039;&#039;,
                //&#039;directory&#039;: &#039;&#039;,
                //&#039;site&#039;: &#039;&#039;
            ]
        ]
    ]

    def tarInfo = [                                                           // 打包信息
        &#039;excludes&#039;: [                                                         // 例外的文件夹或路径
             &#039;.idea&#039;,
             &#039;.git&#039;,
             &#039;default&#039;,
             &#039;default@tmp&#039;,
             &#039;asinus.cn.*&#039;
        ],
        &#039;name&#039;: &quot;asinus.cn.${BUILD_ID}.${BUILD_NUMBER}.tar.gz&quot;,               // 打包文件名
        &#039;path&#039;: &quot;/home/jenkins/packages&quot;                                      // 打包备份路径 jenkins用户权限  不要以/结尾
    ]

    def tarFileName = &quot;${tarInfo.path}/${tarInfo.name}&quot;

    try {
        stage(&quot;检出 - ${project.repository}&quot;) {
            echo &quot;检出 - ${project.repository} 开始 -------------------------------------------------------------------------------&quot;
            // 定义检出参数
            def checkParameters = [                    //
                $class: &#039;GitSCM&#039;,
                branches: [[name: BRANCHORTAG]],                             // 分支
                extensions: [],
                submoduleCfg: [],
                userRemoteConfigs: [
                    [
                        url: project.repository,                             // 代码仓库
                        credentialsId: project.credentialsId                 // 凭证
                    ]
                ]
            ];

            // 处理增量检出
            if (SEGMENT == &#039;增量&#039;) {
                def files = FILES.trim().split(&quot;\n&quot;)
                int filesSize = files.size()

                if (filesSize &gt; 0 &amp;&amp; FILES != &#039;&#039;) {                          // 如果已经指定文件
                    isSparse = true
                    def sparseCheckoutPathArr = []
                    for (int i = 0; i &lt; filesSize; i++) {
                        incrementArr.add(files[i].trim())
                        sparseCheckoutPathArr.add([
                             $class:&#039;SparseCheckoutPath&#039;,
                             path: files[i].trim()                         // 指定仅检出指定文件
                        ])
                    }

                    def sparseCheckout = [
                        $class: &#039;SparseCheckoutPaths&#039;,
                        sparseCheckoutPaths: sparseCheckoutPathArr
                    ]
                    checkParameters.extensions.add(sparseCheckout)
                }
            }

            // 检出文件
            checkout(checkParameters)

            def diffFiles = sh(returnStdout: true, script: &quot;git diff --name-only ${BRANCHORTAG}^&quot;).trim();
            echo &quot;在本次检出 被更新的文件有：&quot;
            echo diffFiles;
             echo &quot;检出 - ${project.repository} 结束 -------------------------------------------------------------------------------&quot;
        }

        stage(&quot;打包文件 - ${SEGMENT}&quot;) {
            echo &quot;${SEGMENT}打包开始 -----------------------------------------------------------------------------------------------&quot;
            def excludes = tarInfo.excludes.join(&quot;  --exclude=&quot;).trim()
            def completeExcludes = &quot;--exclude=${excludes}&quot;
            echo completeExcludes

            if (SEGMENT == &#039;增量&#039;) {                                            // 仅打包增量文件
                // 设定git忽略的文件夹或文件
                def incrementExcludes = tarInfo.excludes.join(&quot;\n&quot;)
                sh(returnStdout: false, script: &#039;echo ${incrementExcludes} &gt;&gt; .git/info/exclude&#039;)

                if (isSparse) {                                                // 如果是指定文件
                    def tarFiles = incrementArr.join(&quot; &quot;)
                    def spareTarResult = sh(returnStdout: true, script: &quot;tar -zcvf ${tarFileName} ${tarFiles} ${completeExcludes}&quot;)
                    echo spareTarResult
                } else {                                                       // 非指定文件则打包git指定分支或tag最一次变更文件
                    def command = &quot;git archive -o ${tarFileName} ${BRANCHORTAG} \$(git diff --name-only ${BRANCHORTAG}^)&quot;;
                    echo command
                    def incrementTarResult = sh(returnStdout: true, script: command)
                    echo incrementTarResult
                }
            } else {                                                           // 打包全部文件
                def completeTarResult = sh(returnStdout: true, script: &quot;tar -zcvf ${tarFileName} . ${completeExcludes}&quot;)
                echo completeTarResult
            }

            sh(returnStdout: false, script: &quot;cp ${tarFileName} ./&quot;);

            echo &quot;${SEGMENT}打包结束 -----------------------------------------------------------------------------------------------&quot;
        }

        stage(&quot;部署至远程服务器&quot;) {
            echo &quot;部署开始 --------------------------------------------------------------------------------------------------------&quot;
            def remoteDirectory = sshServers.remoteDirectory.trim()
            def prefix = sshServers.prefix.trim()
            def directory = sshServers.directory.trim()
            def site = sshServers.site.trim()
            def publishers = []

            for(item in sshServers.lists) {
                def serverName = item.name

                // 如果服务器配置了remoteDirectory就覆盖通用配置
                if (item.remoteDirectory != null &amp;&amp; item.remoteDirectory.trim() != &#039;&#039;) {
                    remoteDirectory = item.remoteDirectory.trim()
                }

                // 如果服务器配置了prefix就覆盖通用配置
                if (item.prefix != null) {
                    prefix = item.prefix.trim()
                }

                // 如果服务器配置了directory就覆盖通用配置
                if (item.directory != null) {
                    directory= item.directory.trim()
                }

                // 如果服务器配置了site就覆盖通用配置
                if (item.site != null) {
                    site= item.site.trim()
                }

                def remoteFilePath = &quot;${remoteDirectory}${directory}/${tarInfo.name}&quot;
                echo remoteFilePath

                def publisher= [
                    &#039;configName&#039;: item.name,
                    &#039;verbose&#039;: true,
                    &#039;transfers&#039;: [
                         [&#039;execCommand&#039;: &quot;mkdir -p  ${directory} ${site}&quot;],
                         [
                             &#039;sourceFiles&#039;: tarInfo.name,
                             &#039;removePrefix&#039;: prefix,
                             &#039;remoteDirectory&#039;: directory,
                             &#039;execCommand&#039;: &quot;tar -zxvf ${remoteFilePath} -C ${site}&quot;
                         ],
                         //[&#039;execCommand&#039;: &quot;cd ${site} &amp;&amp; /usr/bin/php artisan config:clear &amp;&amp; /usr/bin/php artisan config:cache &amp;&amp; composer dump-autoload -o &amp;&amp; &quot;],
                    ]
                ]

                publishers.add(publisher)
            }

            sshPublisher([
                &#039;publishers&#039;: publishers
            ]);

            sh(returnStdout: false, script: &quot;rm -rf  ${tarInfo.name}&quot;);

            echo &quot;部署结束 --------------------------------------------------------------------------------------------------------&quot;
        }
    } finally {

    }
}</code></pre>
<h3>4 使用</h3>
<p>1.全量更新<br />
如果选择全量，FILES参数无效</p>
<p>2.增量不指定FILES更新<br />
拉取代码后，只打包当前分支/tag更新(相对于上一版本号^)的代码并发布到远程</p>
<p>3.增量指定FILES<br />
只会拉取指定分支/tag下的指定文件/文件夹并打包发布</p>
<h3>5 总结</h3>
<p>jenkins不是个啥好的部署工具，打包方式适合较少的应用服务器，优点是发版文件统一， 缺点是慢， 单服务器独立配置蛋疼。 透过SSH服务器在各个服务器上各自部署， 优点是快以及独立配置方便，缺点是版长期可能会不一致。</p>
<p>publish over ssh 本身只提供同步的操作，所以负载较多的应用还是慎用， 可以用ansile,  异步透过ssh， 在应用服务器上执行更新脚本， 在脚本中拉取指定分支所有变动， 再copy指定或变动文件到生产环境。</p>
<hr />
<h3>补充1 自由风格增量 指定</h3>
<p>在构建后执行shell</p>
<pre><code class="language-shell">#!/bin/bash
project=asinus-cn
fileName=$project.$BUILD_ID.$BUILD_NUMBER.tar.gz
backupPath=/home/jenkins/packages
backupFileName=&quot;$backupPath/$fileName&quot;
excludes=&quot;--exclude=&#039;.git&#039; --exclude=&#039;default&#039; --exclude=&#039;default@tmp&#039; --exclude=&#039;asinus.cn.*&#039;&quot;
gitClient=/usr/bin/git

if [ $SEGMENT == &quot;增量&quot; ];
then
    sparseFiles=`echo $FILES | tr &quot;\n&quot; &quot; &quot;`
    sparseFiles=`echo $sparseFiles |sed &quot;s/^[ \s]\{1,\}//g;s/[ \s]\{1,\}$//g&quot;`

    echo &quot;$fileName 将打包以下文件 -----------------------------------------------------------------------------------------------&quot;
    if [ -n &quot;$sparseFiles&quot; ];
    then
        tar -zcvf $backupFileName $sparseFiles $excludes
    else
        $gitClient archive -o $backupFileName $BRANCHORTAG $($gitClient diff --name-only ${BRANCHORTAG}^)
        echo `$gitClient diff --name-only ${BRANCHORTAG}^`
    fi
else
    tar -zcvf $backupFileName . $excludes
fi

cp -r $backupFileName .</code></pre>
<h3>补充2 要不要指定文件</h3>
<p>最好不要， 指定文件部署通常是因为版本控制及发版流程错误， 引发的破坏当前tag 或 分支引起的需要， 可以使用gitfllow流程，发版采用release 而不是master的tag, 这种在建立hotfix紧急修复后就可以合并到master 和 新的release版本， 而不是在master 的tag上折腾（这样会造成版本控制混乱），master的tag仅作为里程碑和修复坐标系存在，而不是用于发版</p>
<p>但是如果发版流程已经固定， 短期内很难改变， 又需要灵活的紧急修复功能（某一tag中一部分功能保留， 一部分修正， 一部分回滚， 但是无法打新tag）， 就可以指定文件部署</p>
]]></content:encoded>
					
					<wfw:commentRss>https://zhangxihai.cn/archives/81/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
