一、背景知识
部署h5服务有两种方式。一种方法是自己构建Web 服务并使用反向代理(例如nginx)。另一个是该解决方案的CDN。 【使用CDN的好处我就不详细说了】
有人可能会说,还有很多其他容器可以做到这一点,但大多数容器都使用nginx,因为外部访问的地址不能是IP,因为需要使用反向代理来实现。
(不过,本文不讨论这一点)
在本文中,我们创建了一个Jenkins 插件,因为我们使用Jenkins 进行打包和构建。 (源码已上传至github:https://github.com/zwp201301/jenkins-oss-plugin,希望对有类似需求的朋友有所帮助。)
二、问题描述
前后端跨域问题
云存储服务具有跨域配置接口,包括Minio等内网文件系统。
部署多个环境
它们通过子目录来区分,例如Development – 开发环境、Test – 测试环境、Staging – 预发布环境、Production – 生产环境。
发布与回滚
无法进行删除操作,因此每次发布而不删除时,都会针对整个版本,如果需要回滚,则会再次发布之前版本的代码。
多个版本并存
没有新的版本子目录来区分不同的版本号,就像在多个环境中一样。
这种方式允许多个版本同时共存,占用空间相对较多,维护工作量较大。
我们目前不这样做。这取决于每个人的实际需求。
三、jenkins plugin
代码实现逻辑主要分为三个步骤
将需要发布的文件复制到目标目录,逐一读取文件,并使用文件的相对路径调用云存储、minio等上传文件的接口。
在这篇文章中,我们将整理一些关键的实现,并将详细的源码上传到github仓库。
1、插件的入口类
请参阅https://github.com/jenkinsci/ssh-steps-plugin 的实现。入口类是UploadFileStep.java,是一个步骤。使用方式是pipeline,所以没有办法从页面接收参数。
由于jenkins插件运行在主节点上,因此需要读取从节点上的h5/css/js等文件,而常规构建器无法做到这一点。
构造函数必须存在并用@DataBoundConstructor 注解。作为入口点,接收用户参数是其主要功能之一。另外,实现Step 的start() 方法。
@覆盖
公共StepExecution start(StepContext context) 抛出异常{
返回新的执行(this,上下文);
}
//线程CommandCallable的实现:
公共对象执行() {
返回getService().upload(getListener(), getWorkspace())。
}
//核心语句调用类UploadFileService的文件上传方法。
2、源码引用了minio的jar包,它依赖的guava版本比Jenkins应用原本依赖的guava要高很多,默认的加载机制是以jenkins应用为准的,也就是说不会使用插件指定的guava版本。所以,我们在pom.xml中必须设置插件优先。
插入
groupIdorg.jenkins-ci.tools/groupId
artifactIdmaven-hpi-插件/artifactId
作品
最小Java版本8/最小Java版本
pluginFirstClassLoadertrue/pluginFirstClassLoader
/作品
/插入
在调试过程中,出现了类似如下的错误:
java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;C)V
在com.google.common.io.BaseEncoding$Alphabet.init(BaseEncoding.java:458)
在com.google.common.io.BaseEncoding$Base64Encoding.init(BaseEncoding.java:919)
在com.google.common.io.BaseEncoding.clinit(BaseEncoding.java:322)
在io.minio.Digest.sha256Md5Hashes(Digest.java:89)
在io.minio.MinioClient.createRequest(MinioClient.java:874)
在io.minio.MinioClient.execute(MinioClient.java:981)
在io.minio.MinioClient.execute(MinioClient.java:935)
在io.minio.MinioClient.executeHead(MinioClient.java:1204)
io.minio.MinioClient.bucketExists(MinioClient.java:3592)
在com.xhtech.tool.jenkins.service.MinioOssService.upload(MinioOssService.java:31)
在com.xhtech.tool.jenkins.service.UploadFileService.iterateWorkspace(UploadFileService.java:80)
在com.xhtech.tool.jenkins.service.UploadFileService.upload(UploadFileService.java:48)
在com.xhtech.tool.jenkins.steps.UploadFileStep$Execution$CommandCallable.execute(UploadFileStep.java:104)
在com.xhtech.tool.jenkins.util.SSHMasterToSlaveCallable.call(SSHMasterToSlaveCallable.java:38)
在hudson.remoting.UserRequest.perform(UserRequest.java:212)
在hudson.remoting.UserRequest.perform(UserRequest.java:54)
在hudson.remoting.Request$2.run(Request.java:369)
在hudson.remoting.InterceptingExecutorService$1.call(InterceptingExecutorService.java:72)
在java.util.concurrent.FutureTask.run(FutureTask.java:266)
在java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
在java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
在hudson.remoting.Engine$1.lambda$newThread$0(Engine.java:93)
在java.lang.Thread.run(Thread.java:748)
java.lang.NoSuchMethodError: com.google.common.base.CharMatcher.ascii()Lcom/google/common/base/CharMatcher;
在com.google.common.io.BaseEncoding$Alphabet.init(BaseEncoding.java:452)
在com.google.common.io.BaseEncoding$Base64Encoding.init(BaseEncoding.java:891)
在com.google.common.io.BaseEncoding.clinit(BaseEncoding.java:316)
在io.minio.Digest.sha256Md5Hashes(Digest.java:89)
在io.minio.MinioClient.createRequest(MinioClient.java:874)
在io.minio.MinioClient.execute(MinioClient.java:981)
在io.minio.MinioClient.execute(MinioClient.java:935)
在io.minio.MinioClient.executeHead(MinioClient.java:1204)
在io.minio.MinioClient.bucketExists(MinioClient.java:3592)
错误原因是没有找到guava类,升级了其他插件依赖的guava版本。
显示jenkins jar包.png
jenkins应用程序依赖的jar.png
如下图所示,Jenkins应用程序依赖的guava版本比较低,为11.0.1,远低于minio jar 7.1.4依赖的版本(25.1-jre)。
番石榴.png
那么,guava 25.1-jre 上已经有自定义插件了吗?答案是。
插件.png
番石榴版.png
总结:报错的根由是Jenkins应用的guava版本替换了插件所引用的guava版本。
3、OSS工厂,根据用户的选择,采用不同的策略发布到不同的OSS。
MinioOssService.java(实现类) AliyunOssService.java(实现类) OssService.java(接口) OssFactory.java(工程类)
4、核心实现类UploadFileService.java
核心实现扫描工作空间中的目标文件,解析环境名称和项目名称,然后调用具体的oss实现上传文件。
请注意,oss远程地址必须处于不同的路径级别。我们约定的规则是:项目名称+环境名称+目标目录相对路径(oms/test/img/logo.jpg、oms/test/index.html等)。
项目名称为oms,环境名称为test,目标目录内的相对路径为img/logo.jpg、index.html。
私人无效iterateWorkspace(字符串工作区路径){
String distPath=workspacePath + File.separator + this.targetPath;
LogUtil.println(\’上传文件目录来源:\’ + distPath);
String env=this.getEvnByWorkspace(workspacePath);
LogUtil.println(\’发布到环境:\’ + env);
String appName=this.getAppNameByWorkspace(workspacePath);
LogUtil.println(\’项目名称:\’ + 应用程序名称);
ListFile 文件=PathUtil.loopFiles(Paths.get(distPath), null);
LogUtil.println(\’Bucket已上传到OSS [\’ + ossType + \’]: [\’ + BucketName + \’]\’);
对于(文件文件:文件){
String ossFileName=new StringBuilder(应用程序名称)
.append(文件.分隔符)
.append(env)
.append(file.getPath().replaceAll(distPath, \’\’))
.toString();
如果(这个。调试){
LogUtil.println(\’上传到OSS的文件名为\’ + ossFileName + \’,本地路径为\’ + file.getAbsolutePath());
}
OssFactory.getOssService(this.ossType).upload(this.bucketName,
oss文件名,
新文件(file.getAbsolutePath()));
}
}
四、pipeline使用示例
我们使用的Jenkins集群是采用K8S部署的主/从结构。
如何编写自定义plugin.png
注意:位于jenkinsfile 文件中。写法如下:
— ossType由jenkins作业参数传递
stage(\’上传文件到OSS\’) {
什么时候{
表达式{ \’aliyun\’==ossType || \’minio\’==ossType }
}
{步
剧本{
tools.PrintMes(\’上传文件到OSS!\’, \’绿色\’)
UploadFile debug: true,ossType: ossType
}
}
邮政{
失败{
//当管道失败时打印一条消息
剧本{
tools.PrintMes(\’上传文件到OSS失败!\’, \’red\’)
http.imNotfiy(projectName, \’FAIL\’, buildEnv, \’上传文件到OSS失败\’,branch, buildUser)
}
}
}
}
上述管道语句“uploadFile debug: true, ossType: ossType”对应的源码如下:
@扩张
公共静态类DescriptorImpl 扩展SSHStepDescriptorImpl {
@覆盖
公共字符串getFunctionName() {
返回“上传文件”。
}
@覆盖
公共字符串getDisplayName() {
返回getPrefix() + getFunctionName()。
}
}
以上#CDN上发布的关于jenkins插件开发的内容来源网络(1)——h5,仅供参考。相关信息请参见官方公告。
原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/92058.html