使用 S3作为 Maven 制品库配合 CI/CD 流水线实现自动化构建

0
0
{"value":"\n\n#### **背景介绍**\n\n\n很多项目使用 Jenkins 作为持续集成和持续交付的 CICD pipeline,来 build 基于 Maven 作为自动化构建工具的 Java 工程。 在有的场景中,我们的 Java 工程依赖于我们自己团队或者项目的其他包,然而根据环境限制的不同,或者安全策略的考虑,我们无法将所依赖的包上传至 Maven Central Repository,也不具备搭建私有 Maven Repository 的条件或者便利。\n\n这时候,我们需要一种机制,能够管理并共享 dependency,使得 Jenkins CICD pipeline 可以无缝的使用这种共享机制,来完成 Java 工程的自动化构建。\n\n本文阐述了一种方案来实现这一目的,一切环境都采用 Amazon 服务设计和搭建。\n\n\n#### **方案设计**\n\n\n![image.png](https://dev-media.amazoncloud.cn/d4fddac8a01b4d11a0d3aaf3b4bf7f5f_image.png)\n\n在 Account A 中存在一个 CodeCommit Repository, 名为 Repo1, Repo1 用来构建 Application A,同时,Repo 1 所构建生成的 Package, 也将作为其他工程的依赖,参与其他工程的构建。在 Account A 中,使用 Amazon Codebuild 进行代码的构建,并使用 CodeDeploy 进行 Application 的部署, 部署环境由 CloudFormation 定义并自动生成。这些组件构成了 Account A 中的 CICD Pipeline。\n\n通常情况,Repo1 所构建的 Package 可以上传至 Maven Central,供其他工程引用,下载并构建自己的 Application 或者 Artifact。\n\n但因为安全策略,这个场景不允许我们的 Package 上传至 Maven Central Repository。我们也不具备搭建私有 Maven Repository 的环境和网络条件。\n\n所以我们采用 S3作为 Artifact 仓库。在 Repo1 的构建过程中,由 codebuild 执行脚本,并配合 Maven 构建出 Package,脚本将 Package 上传至 S3的 Artifact Bucket.\n\n在 Account A 的 IAM 中已经为 Account B 设定好权限,Account B 的用户可以访问 Account A 的 S3 并下载相关的 Artifact。\n\nAccount B 要 clone 并且构建自己的工程,工程代码存储于 CodeCommit 的 Repo2, Repo2 同样使用 Maven 进行构建。但是在 Account B 中,选择了使用 Jenkins 进行自动化构建和部署。\n\n我们使用 Jenkins Shared library 来定义持续集成和持续部署的步骤,这样的好处是不需要在每个构建的工程中重复定义构建和部署逻辑,将标准的构建流程抽象出来进行统一定义,在各个工程构建的时候, 只需要传入一些参数即可。\n\n在 Jenkins 将 Code 从 Repo2 拉取下来后,开始进行 Build, Build 之前需要将依赖项从 S3同步至本地,接下来使用 Maven 进行构建,因为 Account B 的网络环境限制,无法访问 Maven Central Repository,所以必须使用本地路径加载 dependencies. 从 S3下载下来的 package 保证了本地 build 的成功。\n\n构建成功后,Jenkins 将 Application B 通过 CloudFormation 进行部署。\n\n\n#### **实现落地**\n\n\n在 Repo1 的构建中,首先使用 Maven 将代码构建打包,然后使用 Maven 的 exec-maven-plugin 插件,调用事先准备好的脚本,将 pacakge 上传至 S3 的 bucket。\n\n具体代码如下:\n\n```\n<plugin>\n <artifactId>exec-maven-plugin</artifactId>\n <groupId>org.codehaus.mojo</groupId>\n <executions>\n <execution><!-- Get com.amazonaws.proserve libs -->\n <id>deploy-libs-s3</id>\n <phase>deploy</phase>\n <goals>\n <goal>exec</goal>\n </goals>\n </execution>\n </executions>\n <configuration>\n <workingDirectory>${basedir}/scripts/</workingDirectory>\n <executable>bash</executable>\n <commandlineArgs>m2_repo_helper.sh -u -b ${cicd.bucket}</commandlineArgs>\n </configuration>\n </plugin>\n```\n\n以上 maven 插件调用了 m2_repo_helper.sh 脚本,进行包上传。\n\n脚本代码如下:\n\n```\n# Upload libs to S3 bucket (passed as arg)\nupload_libs() {\n echo Uploading Java libs to s3://${1}\n CMD=\" aws s3 sync ${HOME}/.m2/repository/com/amazonaws/proserve s3://${1}/libs\"\n echo Executing ${CMD}\n ${CMD}\n}\n\n#############\n# Main Script\n#############\n\nACTION=\"\"\nBUCKET=\"\"\n\nwhile getopts 'udb:' c\ndo\n case $c in\n u) ACTION=UPLOAD ;;\n d) ACTION=DOWNLOAD ;;\n b) BUCKET=${OPTARG} ;;\n esac\ndone\n\n[ -z \"${ACTION}\" ] && usage\n[ -z \"${BUCKET}\" ] && usage\n\ncase $ACTION in\n UPLOAD) upload_libs $BUCKET ;;\n DOWNLOAD) download_libs $BUCKET ;;\nesac\n```\n\n在 Repo2 构建时,需要首先从 S3下载相关的 dependency, 我们在 Jenkins 的 share library 中实现包的下载。在下载之前,需要 Account B 中运行 Jenkins Pipeline 的 User assume Account A 中的权限,获得 token 来 download 相关的 dependency。\n\nAssume Role 的命令如下:\n\n```\nsh('aws sts assume-role-with-web-identity --role-arn $' + Role + ' --role-session-name x-account --web-identity-token file://$AWS_WEB_IDENTITY_TOKEN_FILE --duration 14400 > ./temp_creds.json')\n```\n\nDownload dependency 的命令如下:\n\n```\nsh('aws s3 sync s3://XXX-XXX-XXX-cicd-deployment/libs $HOME/.m2/repository/com/amazonaws/proserve')\n```\n\n到此,可以在 Repo2的 POM 文件中,直接引用该 dependency 进行构建,dependency 的声明如下:\n\n```\n <dependency>\n <groupId>com.amazonaws.proserve.common</groupId>\n <artifactId>MagnaCommon</artifactId>\n <version>2.0-SNAPSHOT</version>\n </dependency>\n```\n\n\n#### **总结**\n\n\n以上是该方案的全部内容,改方案适用于不能使用 Maven Central Repository 以及私有 Repository 的场景,S3 作为一个存储 artifact 的仓库,可以跨账号,跨区域实现 package 的共享,并且通过 Jenkins 的 share library 定义的构建逻辑,在每次代码构建之前将 package download 到构建环境的本地。该方案对工程的 Maven 构建透明,即 Maven 无需感知 Dependency Repository 的位置,只需要使用既有的步骤进行构建即可。\n\n\n#### **本篇作者**\n\n\n![image.png](https://dev-media.amazoncloud.cn/ca21e13c75b84969bfdd013a6ba67609_image.png)\n\n\n#### **师帅**\n\n\nAmazon 云应用架构师,擅长云原生,容器化以及微服务相关解决方案的设计和实践,擅长 DDD 领域驱动建模的理论和落地。致力于向客户提供相关领域咨询和赋能工作。","render":"<h4><a id=\"_2\"></a><strong>背景介绍</strong></h4>\n<p>很多项目使用 Jenkins 作为持续集成和持续交付的 CICD pipeline,来 build 基于 Maven 作为自动化构建工具的 Java 工程。 在有的场景中,我们的 Java 工程依赖于我们自己团队或者项目的其他包,然而根据环境限制的不同,或者安全策略的考虑,我们无法将所依赖的包上传至 Maven Central Repository,也不具备搭建私有 Maven Repository 的条件或者便利。</p>\n<p>这时候,我们需要一种机制,能够管理并共享 dependency,使得 Jenkins CICD pipeline 可以无缝的使用这种共享机制,来完成 Java 工程的自动化构建。</p>\n<p>本文阐述了一种方案来实现这一目的,一切环境都采用 Amazon 服务设计和搭建。</p>\n<h4><a id=\"_12\"></a><strong>方案设计</strong></h4>\n<p><img src=\"https://dev-media.amazoncloud.cn/d4fddac8a01b4d11a0d3aaf3b4bf7f5f_image.png\" alt=\"image.png\" /></p>\n<p>在 Account A 中存在一个 CodeCommit Repository, 名为 Repo1, Repo1 用来构建 Application A,同时,Repo 1 所构建生成的 Package, 也将作为其他工程的依赖,参与其他工程的构建。在 Account A 中,使用 Amazon Codebuild 进行代码的构建,并使用 CodeDeploy 进行 Application 的部署, 部署环境由 CloudFormation 定义并自动生成。这些组件构成了 Account A 中的 CICD Pipeline。</p>\n<p>通常情况,Repo1 所构建的 Package 可以上传至 Maven Central,供其他工程引用,下载并构建自己的 Application 或者 Artifact。</p>\n<p>但因为安全策略,这个场景不允许我们的 Package 上传至 Maven Central Repository。我们也不具备搭建私有 Maven Repository 的环境和网络条件。</p>\n<p>所以我们采用 S3作为 Artifact 仓库。在 Repo1 的构建过程中,由 codebuild 执行脚本,并配合 Maven 构建出 Package,脚本将 Package 上传至 S3的 Artifact Bucket.</p>\n<p>在 Account A 的 IAM 中已经为 Account B 设定好权限,Account B 的用户可以访问 Account A 的 S3 并下载相关的 Artifact。</p>\n<p>Account B 要 clone 并且构建自己的工程,工程代码存储于 CodeCommit 的 Repo2, Repo2 同样使用 Maven 进行构建。但是在 Account B 中,选择了使用 Jenkins 进行自动化构建和部署。</p>\n<p>我们使用 Jenkins Shared library 来定义持续集成和持续部署的步骤,这样的好处是不需要在每个构建的工程中重复定义构建和部署逻辑,将标准的构建流程抽象出来进行统一定义,在各个工程构建的时候, 只需要传入一些参数即可。</p>\n<p>在 Jenkins 将 Code 从 Repo2 拉取下来后,开始进行 Build, Build 之前需要将依赖项从 S3同步至本地,接下来使用 Maven 进行构建,因为 Account B 的网络环境限制,无法访问 Maven Central Repository,所以必须使用本地路径加载 dependencies. 从 S3下载下来的 package 保证了本地 build 的成功。</p>\n<p>构建成功后,Jenkins 将 Application B 通过 CloudFormation 进行部署。</p>\n<h4><a id=\"_36\"></a><strong>实现落地</strong></h4>\n<p>在 Repo1 的构建中,首先使用 Maven 将代码构建打包,然后使用 Maven 的 exec-maven-plugin 插件,调用事先准备好的脚本,将 pacakge 上传至 S3 的 bucket。</p>\n<p>具体代码如下:</p>\n<pre><code class=\"lang-\">&lt;plugin&gt;\n &lt;artifactId&gt;exec-maven-plugin&lt;/artifactId&gt;\n &lt;groupId&gt;org.codehaus.mojo&lt;/groupId&gt;\n &lt;executions&gt;\n &lt;execution&gt;&lt;!-- Get com.amazonaws.proserve libs --&gt;\n &lt;id&gt;deploy-libs-s3&lt;/id&gt;\n &lt;phase&gt;deploy&lt;/phase&gt;\n &lt;goals&gt;\n &lt;goal&gt;exec&lt;/goal&gt;\n &lt;/goals&gt;\n &lt;/execution&gt;\n &lt;/executions&gt;\n &lt;configuration&gt;\n &lt;workingDirectory&gt;${basedir}/scripts/&lt;/workingDirectory&gt;\n &lt;executable&gt;bash&lt;/executable&gt;\n &lt;commandlineArgs&gt;m2_repo_helper.sh -u -b ${cicd.bucket}&lt;/commandlineArgs&gt;\n &lt;/configuration&gt;\n &lt;/plugin&gt;\n</code></pre>\n<p>以上 maven 插件调用了 m2_repo_helper.sh 脚本,进行包上传。</p>\n<p>脚本代码如下:</p>\n<pre><code class=\"lang-\"># Upload libs to S3 bucket (passed as arg)\nupload_libs() {\n echo Uploading Java libs to s3://${1}\n CMD=&quot; aws s3 sync ${HOME}/.m2/repository/com/amazonaws/proserve s3://${1}/libs&quot;\n echo Executing ${CMD}\n ${CMD}\n}\n\n#############\n# Main Script\n#############\n\nACTION=&quot;&quot;\nBUCKET=&quot;&quot;\n\nwhile getopts 'udb:' c\ndo\n case $c in\n u) ACTION=UPLOAD ;;\n d) ACTION=DOWNLOAD ;;\n b) BUCKET=${OPTARG} ;;\n esac\ndone\n\n[ -z &quot;${ACTION}&quot; ] &amp;&amp; usage\n[ -z &quot;${BUCKET}&quot; ] &amp;&amp; usage\n\ncase $ACTION in\n UPLOAD) upload_libs $BUCKET ;;\n DOWNLOAD) download_libs $BUCKET ;;\nesac\n</code></pre>\n<p>在 Repo2 构建时,需要首先从 S3下载相关的 dependency, 我们在 Jenkins 的 share library 中实现包的下载。在下载之前,需要 Account B 中运行 Jenkins Pipeline 的 User assume Account A 中的权限,获得 token 来 download 相关的 dependency。</p>\n<p>Assume Role 的命令如下:</p>\n<pre><code class=\"lang-\">sh('aws sts assume-role-with-web-identity --role-arn $' + Role + ' --role-session-name x-account --web-identity-token file://$AWS_WEB_IDENTITY_TOKEN_FILE --duration 14400 &gt; ./temp_creds.json')\n</code></pre>\n<p>Download dependency 的命令如下:</p>\n<pre><code class=\"lang-\">sh('aws s3 sync s3://XXX-XXX-XXX-cicd-deployment/libs $HOME/.m2/repository/com/amazonaws/proserve')\n</code></pre>\n<p>到此,可以在 Repo2的 POM 文件中,直接引用该 dependency 进行构建,dependency 的声明如下:</p>\n<pre><code class=\"lang-\"> &lt;dependency&gt;\n &lt;groupId&gt;com.amazonaws.proserve.common&lt;/groupId&gt;\n &lt;artifactId&gt;MagnaCommon&lt;/artifactId&gt;\n &lt;version&gt;2.0-SNAPSHOT&lt;/version&gt;\n &lt;/dependency&gt;\n</code></pre>\n<h4><a id=\"_127\"></a><strong>总结</strong></h4>\n<p>以上是该方案的全部内容,改方案适用于不能使用 Maven Central Repository 以及私有 Repository 的场景,S3 作为一个存储 artifact 的仓库,可以跨账号,跨区域实现 package 的共享,并且通过 Jenkins 的 share library 定义的构建逻辑,在每次代码构建之前将 package download 到构建环境的本地。该方案对工程的 Maven 构建透明,即 Maven 无需感知 Dependency Repository 的位置,只需要使用既有的步骤进行构建即可。</p>\n<h4><a id=\"_133\"></a><strong>本篇作者</strong></h4>\n<p><img src=\"https://dev-media.amazoncloud.cn/ca21e13c75b84969bfdd013a6ba67609_image.png\" alt=\"image.png\" /></p>\n<h4><a id=\"_139\"></a><strong>师帅</strong></h4>\n<p>Amazon 云应用架构师,擅长云原生,容器化以及微服务相关解决方案的设计和实践,擅长 DDD 领域驱动建模的理论和落地。致力于向客户提供相关领域咨询和赋能工作。</p>\n"}
目录
亚马逊云科技解决方案 基于行业客户应用场景及技术领域的解决方案
联系亚马逊云科技专家
亚马逊云科技解决方案
基于行业客户应用场景及技术领域的解决方案
联系专家
0
目录
关闭
contact-us