• 感觉这里堕落了 at 2020年04月01日

    额, 我感觉我还是很努力的在写文章了。 基本上平均 2 个礼拜更新一次

  • 用一段我写故障注入的注释来说明一下吧。

    基本上就是 jenkins 也是用 exec 这个方式来像容器发送命令的,基本上是读取不到你定义的环境变量的。 这个问题我至今还没有找到原因。 所以你还是用 jenkins pipeline 中的环境变量语法,来搞这些事把

  • 我是用 xml 哈哈哈, 这玩意随意了, 想用啥用啥

  • 恩 很强, 我们测接口都用这玩意

  • 你也可以用这个, 只不过你要测试什么版本就搭建什么虚拟机 + 浏览器。 然后注册到 grid hub 上就好了

  • 尽量不要拿 docker 做兼容性测试, 它用的毕竟是 linux 内核。 用户是不会用 linux 系统访问你们的产品的。

  • kubectl 的命令不是这么用的~~~ kubectl exec -it podname bash 这个命令是使用 tty 打开了容器的 bash shell 窗口。 脚本不是你在 shell 里做命令交互~~ exec 是让容器执行命令的, 所以你直接用 kubectl exec podname 你要执行的命令 就行了

  • 就 nfs 就行了吧

  • java 界 无可争议的 接口测试工具: rest-assured

  • 只做过 web 端的, 就是用 grid 做

  • 效果图如下:

  • 这个很简单啊, allure 在 jenkins 上的插件也是用 allure command line 启动的一个服务。 暴露了 http 接口。 你可以从中获取测试结果的。我是写了一段 groovy 脚本来抓取 allure report 的结果的。 可以使用 pipeline 来 执行 groovy 脚本,你也可以在其他 job 中添加 groovy script 这个步骤。 你可以参考我这里的脚本。代码如下:

    
    /**
     * Created by sungaofei on 20/2/8.
     */
    @Grab(group = 'org.codehaus.groovy.modules.http-builder', module = 'http-builder', version = '0.7')
    
    import groovyx.net.http.HTTPBuilder
    
    import static groovyx.net.http.ContentType.*
    
    
    import static groovyx.net.http.Method.*
    import groovy.transform.Field
    
    
    //global variable
    @Field jenkinsURL = "http://auto.4paradigm.com"
    @Field failed = "FAILED"
    @Field success = "SUCCESS"
    @Field inProgress = "IN_PROGRESS"
    @Field abort = "ABORTED"
    
    @NonCPS
    def String checkJobStatus() {
    
        def url = ""
    
        if (env.BRANCH_NAME!= "" && env.BRANCH_NAME != null){
            String jobName = "${JOB_NAME}".split("/")[0]
            url = "/view/API/job/${jobName}/job/${env.BRANCH_NAME}/${BUILD_NUMBER}/wfapi/describe"
        }else {
            url = "/view/API/job/${JOB_NAME}/${BUILD_NUMBER}/wfapi/describe"
        }
        HTTPBuilder http = new HTTPBuilder(jenkinsURL)
        String status = success
    
        println("1111111111")
        println("${JOB_NAME}")
        println(url)
        http.get(path: url) { resp, json ->
            if (resp.status != 200) {
                throw new RuntimeException("请求 ${url} 返回 ${resp.status} ")
            }
            List stages = json.stages
    
            for (int i = 0; i < stages.size(); i++) {
                def stageStatus = json.stages[i].status
                if (stageStatus == failed) {
                    status = failed
                    break
                }
                if (stageStatus == abort) {
                    status = abort
                    break
                }
            }
        }
    
        return status;
    
    }
    
    
    @NonCPS
    def call(String to) {
        println("邮件列表:${to}")
    
        def reportURL = ""
        String jobName = "${JOB_NAME}"
        String blueOCeanURL = ""
    
        if (env.BRANCH_NAME!= "" && env.BRANCH_NAME != null){
            jobName = "${JOB_NAME}".split("/")[0]
            reportURL = "/view/API/job/${jobName}/job/${env.BRANCH_NAME}/${BUILD_NUMBER}/allure/"
    //            http://auto.4paradigm.com/blue/organizations/jenkins/gitlabtest/detail/master/217/pipeline
            blueOCeanURL = "${jenkinsURL}/blue/organizations/jenkins/${jobName}/detail/${env.BRANCH_NAME}/${BUILD_NUMBER}/pipeline"
        }else{
            reportURL = "/view/API/job/${JOB_NAME}/${BUILD_NUMBER}/allure/"
            blueOCeanURL = "${jenkinsURL}/blue/organizations/jenkins/${JOB_NAME}/detail/${JOB_NAME}/${BUILD_NUMBER}/pipeline"
        }
    
        def sendSuccess = {
    //        blueOCeanURL = "${jenkinsURL}/blue/organizations/jenkins/${JOB_NAME}/detail/${JOB_NAME}/${BUILD_NUMBER}/pipeline"
    
            def fileContents = ""
            def passed = ""
            def failed = ""
            def skipped = ""
            def broken = ""
            def unknown = ""
            def total = ""
            HTTPBuilder http = new HTTPBuilder('http://auto.4paradigm.com')
            //根据responsedata中的Content-Type header,调用json解析器处理responsedata
            http.get(path: "${reportURL}widgets/summary.json") { resp, json ->
                println resp.status
                passed = json.statistic.passed
                failed = json.statistic.failed
                skipped = json.statistic.skipped
                broken = json.statistic.broken
                unknown = json.statistic.unknown
                total = json.statistic.total
    
            }
    
            println(passed)
    
            emailext body: """
    <html>
      <style type="text/css">
      <!--
      ${fileContents}
      -->
      </style>
      <body>
      <div id="content">
      <h1>Summary</h1>
      <div id="sum2">
          <h2>Jenkins Build</h2>
          <ul>
          <li>Job 地址 : <a href='${BUILD_URL}'>${BUILD_URL}</a></li>
           <li>测试报告地址 : <a href='${jenkinsURL}${reportURL}'>${jenkinsURL}${reportURL}</a></li>
           <li>Pipeline 流程地址 : <a href='${blueOCeanURL}'>${blueOCeanURL}</a></li>
          </ul>
    
          <h2>测试结果汇总</h2>
          <ul>
          <li>用例总数 : ${total}</li>
          <li>pass数量 : ${passed}</li>
           <li>failed数量 :${failed} </li>
           <li>skip数量 : ${skipped}</li>
           <li>broken数量 : ${broken}</li>
          </ul>
      </div>
      </div></body></html>
        """, mimeType: 'text/html', subject: "${JOB_NAME} 测试结束", to: to
    
        }
    
        def send = { String subject ->
            emailext body: """
    <html>
      <style type="text/css">
      <!--
      -->
      </style>
      <body>
      <div id="sum2">
          <h2>Jenkins Build</h2>
          <ul>
          <li>Job 地址 : <a href='${BUILD_URL}'>${BUILD_URL}</a></li>
            <li>测试报告地址 : <a href='${jenkinsURL}${reportURL}'>${jenkinsURL}${reportURL}</a></li>
           <li>Pipeline 流程地址 : <a href='${blueOCeanURL}'>${blueOCeanURL}</a></li>
          </ul>
      </div>
      </div></body></html>
        """, mimeType: 'text/html', subject: subject, to: to
        }
    
        String status = checkJobStatus()
    //    String status = $BUILD_STATUS
        println("当前job 的运行状态为: ${status}")
        switch (status) {
            case ["SUCCESS", "UNSTABLE"]:
                sendSuccess()
                break
            case "FAILED":
                send("Job运行失败")
                break
            case "ABORTED":
                send("Job在运行中被取消")
                break
            default:
                send("Job运行结束")
        }
    
    }
    
    
    
    
    
    
  • 不要在 jnlp 容器里用 cat 命令~~~ 加 cat 命令的目的是让其他容器能一直持续运行~~ jnlp 是 slave 容器, 不用加

  • 我们很无脑的用的 nfs 哈哈哈哈。 ceph 现在没在用了。 因为我们没那么高的性能要求

  • 吐槽下我那神奇的上司 at 2020年02月27日

    额, 6 楼是我, 楼主可以去翻我写的 spark 的帖子。 刚才忘了解除匿名了

  • 部署的时候配置域名跟 ingress 集成起来是这样的。 首先需要到你们的 dns 中添加一个泛域名解析。 解析地址填写你 ingress controller 的地址。 比如我们做的凡是以 testenv.4pd.io 为结尾的域名全部解析成我们 ingress controller 的 ip 地址。 然后为每一个环境创建一个 ingress 规则。 比如我们曾经做的:

    def create_ingress_yaml(config):
        document = """
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: %s
      annotations:
        nginx/client_max_body_size: 10240m
        nginx.org/client-max-body-size: "10240m"
        ingress.kubernetes.io/proxy-body-size: 10240m
    spec:
      rules:
      - host: %s.testenv.4pd.io
        http:
          paths:
          - path: /
            backend:
              serviceName: %s
              servicePort: 8888
      - host: %s.preditor.testenv.4pd.io
        http:
          paths:
          - path: /
            backend:
              serviceName: %s
              servicePort: 8090
      - host: %s.history.testenv.4pd.io
        http:
          paths:
          - path: /
            backend:
              serviceName: %s
              servicePort: 18080
        """ % (config.pht_pod_name, config.name_prefix, config.pht_pod_name, config.name_prefix, config.pht_pod_name,
               config.name_prefix, config.pht_pod_name)
        data = yaml.load_all(document)
        with open(config.ingress_conf_path, 'w') as stream:
            yaml.dump_all(data, stream)
    

    在部署环境的时候就把 ingress 创建好。 然后配合泛域名解析,ingress controller 会自动的帮我们转发请求到具体的环境上。 就可以达到每个环境都自动的有一个域名对应上了。

    部署其实很简单, 就像我上面做的那样就好。 维护一套 yaml 模板动态的去生成部署这些模块的 k8s 配置就可以了。

  • 😂 😂 😂 😂

  • go 就直接用 vendor 吧, 直接在项目里把依赖打成 vendor 就好了。 不要用 go mod 下载或去 worksapce 里找依赖。

  • 后续如何跟 ingress 集成起来是这样的。 首先需要到你们的 dns 中添加一个泛域名解析。 解析地址填写你 ingress controller 的地址。 比如我们做的凡是以 testenv.4pd.io 为结尾的域名全部解析成我们 ingress controller 的 ip 地址。 然后为每一个环境创建一个 ingress。 比如我们曾经做的:

    def create_ingress_yaml(config):
        document = """
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: %s
      annotations:
        nginx/client_max_body_size: 10240m
        nginx.org/client-max-body-size: "10240m"
        ingress.kubernetes.io/proxy-body-size: 10240m
    spec:
      rules:
      - host: %s.testenv.4pd.io
        http:
          paths:
          - path: /
            backend:
              serviceName: %s
              servicePort: 8888
      - host: %s.preditor.testenv.4pd.io
        http:
          paths:
          - path: /
            backend:
              serviceName: %s
              servicePort: 8090
      - host: %s.history.testenv.4pd.io
        http:
          paths:
          - path: /
            backend:
              serviceName: %s
              servicePort: 18080
        """ % (config.pht_pod_name, config.name_prefix, config.pht_pod_name, config.name_prefix, config.pht_pod_name,
               config.name_prefix, config.pht_pod_name)
        data = yaml.load_all(document)
        with open(config.ingress_conf_path, 'w') as stream:
            yaml.dump_all(data, stream)
    

    在部署环境的时候就把 ingress 创建好。 然后配合泛域名解析, 就可以达到每个环境都自动的有一个域名对应上了。

  • 不过我们之前使用搭建一个 ceph, 然后在 k8s 启动 pod 的时候, 把 maven 的本地缓存仓库挂载出来, 放到 ceph 里来解决动态依赖的问题。 但是我 python 很渣, 还不知道 pip 安装的依赖怎么挂载出来

  • 你这两个问题其实我也还没解决~~~ 好在我们对时间的要求不大

  • 单 slave 最大的缺陷就是没有高可用能力, 出现单点故障以后整个流程就会不可用的。

  • 我们测 IE 浏览器的时候也没办法, 老老实实在 windows 上部署~

  • 希望对你有帮助哈~

  • allure 这种这么好用的 report 框架~~~~ 大家用起来啊