美文网首页
jenkins插件开发(一)——h5发布到CDN

jenkins插件开发(一)——h5发布到CDN

作者: 天草二十六_简村人 | 来源:发表于2022-05-17 11:02 被阅读0次

    一、背景知识

    h5服务的部署有两种方式,一是自己搭建web服务,再由nginx等反向代理;二是本方案中的CDN。【使用CDN的好处这里就不赘述了】

    有些人可能会说,还有其他的好多容器可以做到,这里因为涉及到域名映射,对外访问的地址不能是ip,所以我们说大多是使用nginx反向代理来实现。
    (但它也不是本文要讲述的方式)

    本文主要讲述我们公司是如何实现h5发布到oss上的,因为我们打包构建使用的是jenkins,所以我写了一个Jenkins plugin。(源码已上传到github:https://github.com/zwp201301/jenkins-oss-plugin,希望能够帮助到类似需求的小伙伴!)

    二、问题描述

    • 前后端跨域问题

    云存储服务都有配置跨域的界面,包括Minio这样的内网文件系统。

    • 多环境部署

    通过子目录的区分,比如dev-开发环境,test-测试环境,staging-预发环境,prod-生产环境。

    • 发布及回滚

    因为不能做删除操作,发布的时候是每次进行覆盖全部,不做删除动作;遇到需要回滚,则对上一个版本的代码进行二次发布。

    • 多版本共存

    这里没有像多环境那样,新建版本号的子目录去区分不同版本号。
    那样可以做到同时有多个版本共存的情况,相对来说,占用的空间较大,维护的工作量也较大。
    暂时没这样去做,看各自的实际需求而定。

    三、jenkins plugin

    代码实现逻辑分为三大步

    • 将需要发布的文件统一拷贝至目标目录下
    • 逐个读取文件,以及文件的相对路径
    • 调用云存储或minio等上传文件的api

    详细的源码我将上传到github仓库,本文梳理出来几个关键实现:

    1、插件的入口类

    参考https://github.com/jenkinsci/ssh-steps-plugin的实现,入口类为UploadFileStep.java,它是一个Step。因为使用方式是pipeline,我们就没有实现由页面接收入参的方式。

    因为jenkins插件运行在master节点,它需要去读取slave节点上的h5/css/js等文件,所以不能是普通的Builder。

    • 一定要有构造函数,并在它上面加注解@DataBoundConstructor。
    • 作为入口,接收用户的参数是它的一个主要功能,另外它实现了Step的start()方法。
        @Override
        public StepExecution start(StepContext context) throws Exception {
            return new Execution(this, context);
        }
    
    // 线程CommandCallable的实现:
                public Object execute() {
                    return getService().upload(getListener(), getWorkspace());
                }
    
    // 核心语句,调用了类UploadFileService的上传文件方法。
    

    2、源码引用了minio的jar包,它依赖的guava版本比Jenkins应用原本依赖的guava要高很多,默认的加载机制是以jenkins应用为准的,也就是说不会使用插件指定的guava版本。所以,我们在pom.xml中必须设置插件优先。

               <plugin>
                    <groupId>org.jenkins-ci.tools</groupId>
                    <artifactId>maven-hpi-plugin</artifactId>
                    <configuration>
                        <minimumJavaVersion>8</minimumJavaVersion>
                        <pluginFirstClassLoader>true</pluginFirstClassLoader>
                    </configuration>
                </plugin>
    

    在调试的过程中,我遇到的报错有:

    java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;C)V
        at com.google.common.io.BaseEncoding$Alphabet.<init>(BaseEncoding.java:458)
        at com.google.common.io.BaseEncoding$Base64Encoding.<init>(BaseEncoding.java:919)
        at com.google.common.io.BaseEncoding.<clinit>(BaseEncoding.java:322)
        at io.minio.Digest.sha256Md5Hashes(Digest.java:89)
        at io.minio.MinioClient.createRequest(MinioClient.java:874)
        at io.minio.MinioClient.execute(MinioClient.java:981)
        at io.minio.MinioClient.execute(MinioClient.java:935)
        at io.minio.MinioClient.executeHead(MinioClient.java:1204)
        at io.minio.MinioClient.bucketExists(MinioClient.java:3592)
        at com.xhtech.tool.jenkins.service.MinioOssService.upload(MinioOssService.java:31)
        at com.xhtech.tool.jenkins.service.UploadFileService.iterateWorkspace(UploadFileService.java:80)
        at com.xhtech.tool.jenkins.service.UploadFileService.upload(UploadFileService.java:48)
        at com.xhtech.tool.jenkins.steps.UploadFileStep$Execution$CommandCallable.execute(UploadFileStep.java:104)
        at com.xhtech.tool.jenkins.util.SSHMasterToSlaveCallable.call(SSHMasterToSlaveCallable.java:38)
        at hudson.remoting.UserRequest.perform(UserRequest.java:212)
        at hudson.remoting.UserRequest.perform(UserRequest.java:54)
        at hudson.remoting.Request$2.run(Request.java:369)
        at hudson.remoting.InterceptingExecutorService$1.call(InterceptingExecutorService.java:72)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at hudson.remoting.Engine$1.lambda$newThread$0(Engine.java:93)
        at java.lang.Thread.run(Thread.java:748)
    
    java.lang.NoSuchMethodError: com.google.common.base.CharMatcher.ascii()Lcom/google/common/base/CharMatcher;
        at com.google.common.io.BaseEncoding$Alphabet.<init>(BaseEncoding.java:452)
        at com.google.common.io.BaseEncoding$Base64Encoding.<init>(BaseEncoding.java:891)
        at com.google.common.io.BaseEncoding.<clinit>(BaseEncoding.java:316)
        at io.minio.Digest.sha256Md5Hashes(Digest.java:89)
        at io.minio.MinioClient.createRequest(MinioClient.java:874)
        at io.minio.MinioClient.execute(MinioClient.java:981)
        at io.minio.MinioClient.execute(MinioClient.java:935)
        at io.minio.MinioClient.executeHead(MinioClient.java:1204)
        at 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)


    guava.png

    那么我们的自定义插件,是否已经是guava 25.1-jre?答案:是。


    插件.png
    guava版本.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
    private void iterateWorkspace(String workspacePath) {
            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("工程名称:" + appName);
    
            List<File> files = PathUtil.loopFiles(Paths.get(distPath), null);
    
            LogUtil.println("上传至OSS【" + ossType + "】的bucket:【" + bucketName + "】");
    
            for (File file : files) {
                String ossFileName = new StringBuilder(appName)
                        .append(File.separator)
                        .append(env)
                        .append(file.getPath().replaceAll(distPath, ""))
                        .toString();
    
                if (this.debug) {
                    LogUtil.println("上传到OSS的文件名是" + ossFileName + ",本地路径是" + file.getAbsolutePath());
                }
    
                OssFactory.getOssService(this.ossType).upload(this.bucketName,
                        ossFileName,
                        new File(file.getAbsolutePath()));
            }
        }
    

    四、pipeline使用示例

    我们使用的Jenkins集群是采用K8S部署的Master-Slave结构。

    自定义插件的pipeline写法.png

    注意:我们在jenkinsfile文件中,写法见下:

    -- ossType是由jenkins job的参数传入
    stage('Upload File To OSS') {
                when {
                    expression { "aliyun" == ossType || "minio" == ossType }
                }
                steps {
                    script {
                        tools.PrintMes("Upload File To OSS!!!", "green")
                        uploadFile debug: true, ossType: ossType
                    }
                }
                post {
                    failure {
                        //当此Pipeline失败时打印消息
                        script {
                            tools.PrintMes("Upload File To OSS failure!!!", "red")
                            http.imNotfiy(projectName, "FAIL", buildEnv, "Upload File To OSS failure", branch, buildUser)
                        }
                    }
                }
            }
    

    上面的pipeline语句"uploadFile debug: true, ossType: ossType"对应源码是:

        @Extension
        public static class DescriptorImpl extends SSHStepDescriptorImpl {
    
            @Override
            public String getFunctionName() {
                return "uploadFile";
            }
    
            @Override
            public String getDisplayName() {
                return getPrefix() + getFunctionName();
            }
        }
    

    相关文章

      网友评论

          本文标题:jenkins插件开发(一)——h5发布到CDN

          本文链接:https://www.haomeiwen.com/subject/qdepurtx.html