Aprenda sobre as práticas de teste de integração contínua baseadas em Jenkins e Kubernetes!

Autor | Liu Chunming, Editor-Chefe | Carol

Produzido | CSDN Cloud Computing (ID: CSDNcloud)

Imagem da capa | CSDN Download para Visual China

Atualmente, para reduzir o custo de uso da máquina, a empresa realizou um inventário de todas as máquinas virtuais da AWS e descobriu que algumas das máquinas com baixa utilização são usadas pela equipe de teste como máquinas Jenkins Slave. Não é o que esperávamos.Usando uma máquina virtual como Jenkins Slave, haverá muito desperdício, porque após a conclusão do trabalho de teste, quando o escravo está ocioso, os recursos da máquina virtual não são liberados.

Além da baixa utilização de recursos, a máquina virtual como Jenkins Slave tem outras desvantagens, como alocação desigual de recursos , alguns trabalhos Slave a serem colocados na fila e alguns Slaves podem estar ociosos. Além disso, a expansão da capacidade é inconveniente.Usando uma máquina virtual como escrava, se você quiser adicionar o Jenkins Slave, precisará montar manualmente a máquina virtual no Jenkins Master e configurar o ambiente para o escravo, o que torna o gerenciamento muito inconveniente e consome manutenção.

Em 2019, a equipe de operação e manutenção construiu uma plataforma de nuvem de contêineres Kubernetes. Para atingir o objetivo da empresa de reduzir o custo de uso da máquina, minha equipe de testes de carros conectados considerou a mudança total do Jenkins Slave para a plataforma de nuvem de contêineres Kubernetes. O principal objetivo é melhorar a utilização dos recursos do Jenkins Slave e fornecer uma expansão de capacidade mais flexível e flexível para atender às necessidades de mais e mais tarefas de teste para o Slave.

Este artigo é um resumo de nossa prática.

Arquitetura geral

Sabemos que Jenkins usa a arquitetura Master-Slave, responsável pelo gerenciamento do trabalho e o escravo pela execução do trabalho. Em nossa empresa, o Master é construído em uma máquina virtual, e o Slave vem da plataforma Kubernetes. Cada Slave é um Pod na plataforma Kubernetes. O Pod é a unidade de agendamento atômico do Kubernetes. O conhecimento mais básico do Kubernetes não é muito introduzido. Neste artigo, tudo o que você precisa lembrar é que o Pod é o Jenkins Slave.

O diagrama esquemático do cluster Jenkins Slave criado no Kubernetes é o seguinte.

Nesta arquitetura, o Jenkins Master é responsável por gerenciar e testar o trabalho.Para poder usar os recursos na plataforma Kubernetes, você precisa instalar o plug-in Kubernetes no Master.

A plataforma Kubernetes é responsável por gerar os Pods, que são usados ​​como Jenkins Slave para executar tarefas do trabalho. Quando Jó está prevista em Jenkins Mestre, Jenkins Mestre iniciada por Kubernetes-plugin com o pedido internet Kubernetes, Kubernetes gerar o correspondente Pod objetos de acordo com modelo de Pod, Pod objetos pedido iniciados JNLP para Jenkins Mestre, de modo a ligar o Jenkins Mestre, uma vez Depois que a conexão for bem-sucedida, você poderá executar o trabalho no Pod.

As imagens do contêiner usadas nos Pods vêm do Harbor. Aqui, três imagens são usadas em um Pod, que são imagens Java , imagens Python e imagens JNLP . O espelho Java fornece um ambiente Java que pode ser usado para compilar e executar o código de teste escrito em Java.O espelho Python fornece um ambiente Python usado para executar o código de teste escrito em Python.O espelho JNLP é um espelho escravo oficial fornecido por Jenkins.

Usando o Kubernetes como Jenkins Slave, como resolver os problemas acima mencionados de baixa utilização de recursos e alocação desigual de recursos ao usar máquinas virtuais, e realizar a expansão dinâmica e flexível do Slave?

Primeiro, somente quando o Jenkins Master tiver um trabalho agendado, ele será aplicado ao Kubernetes Pod para criar um Jenkins Slave. Após a execução do trabalho de teste, o escravo usado será reciclado pelo Kubernetes. Não é como quando uma máquina virtual é usada como escravo, há situações em que o escravo está ocioso, melhorando assim a taxa de utilização dos recursos de computação.

Segundo, o principal problema de alocação desigual de recursos é entre diferentes grupos de teste, devido aos diferentes ambientes e dependências de teste, o Jenkins Slave não pode ser compartilhado. A plataforma Kubernetes rompe a barreira do compartilhamento, desde que haja recursos de computação no cluster Kubernetes, você pode solicitar o Jenkins Slave adequado para o seu projeto, para que o fenômeno do Enfileiramento de trabalhos não ocorra mais.

Com a ajuda do Kubernetes, é ainda mais simples implementar a expansão elástica dinâmica do Slave. Porque o Kubernetes suporta inerentemente a expansão elástica. Quando é monitorado se os recursos do Kubernetes são insuficientes, você só precisa adicionar nós de nó através da plataforma de operação e manutenção. Para o teste, esta etapa é completamente transparente.

Configurar o Jenkins Master

Para usar o Kubernetes como Jenkins Slave, a primeira etapa é instalar o plug-in Kubernetes no Jenkins Master. O método de instalação é muito simples: faça login no Jenkins com uma conta de administrador do Jenkisn, procure o Kubernetes na página Gerenciar plug-in, verifique e instale.

A próxima etapa é configurar as informações de conexão do Kubernetes no Jenkins Master. conexão Jenkins Mestre necessidade Kubernetes nuvem para configurar as três mensagens fundamentais: o nome , o endereço e certificado . Todas as informações de configuração são mostradas na figura abaixo.

O nome será usado no Jenkins Pipeline. Ao configurar várias nuvens do Kubernetes, você precisa especificar um nome diferente para cada nuvem.

O endereço Kubernetes refere-se ao endereço do servidor da API Kubernetes. O Jenkins Master inicia uma solicitação para agendar Pods para esse endereço por meio do plug-in Kubernetes.

A chave do certificado de serviço Kubernetes é usada para estabelecer uma conexão com o servidor da API Kubernetes. O método é obter o conteúdo dos dados da autoridade de certificação em /root/.kube/config do arquivo /root/.kube/config do servidor da API Kubernetes E convertido em arquivos codificados em base64.

# echo certificate-authority-data的内容 | base64 -D > ~/ca.crt

O conteúdo de ca.crt é a chave do certificado de serviço Kubernetes.

A credencial na figura acima é um arquivo pxf gerado usando o certificado e a chave do cliente. Primeiro, converta o conteúdo dos dados do certificado do cliente e dos dados da chave do cliente em /root/.kube/config em arquivos codificados em base64.

# echo client-certificate-data的内容 | base64 -D > ~/client.crt
# echo client-key-data的内容 | base64 -D > ~/client.crt

Crie um arquivo pxf com base nesses dois arquivos:

# openssl pkcs12 -export -out ~/cert.pfx -inkey ~/client.key -in ~/client.crt -certfile ~/ca.crt
# Enter Export Password:
# Verifying - Enter Export Password:

Personalize uma senha e lembre-se.

Clique em Adicionar, selecione Certificado, clique em Carregar certificado, selecione o arquivo cert.pfx gerado anteriormente, digite a senha ao gerar o arquivo cert.pfx e o certificado será adicionado.

Em seguida, configure o URL do Jenkins e o número de pods que podem ser agendados ao mesmo tempo.

Após a conclusão da configuração, você pode clicar no botão "Testar Conexão" para testar se você pode se conectar ao Kubernetes. Se o Teste de conexão for bem-sucedido, a conexão será bem-sucedida e não haverá problema com a configuração.

Após configurar o plug-in Kubernetes, configure algumas ferramentas públicas no Jenkins Master, conforme necessário.Por exemplo, configurei o allure para gerar relatórios. Dessa maneira, quando essas ferramentas forem usadas no Jenkins Slave, elas serão instaladas automaticamente no Jenkins Slave.

Personalizar o pipeline do Jenkins

Após configurar as informações de conexão do Kubernetes, você pode usar o kubernetes como um agente no pipeline de tarefas de teste. A diferença de usar uma máquina virtual como Jenkins Slave está principalmente na parte pipeline.agent. O código a seguir é o conteúdo completo do Jenkinsfile.

pipeline {
    agent {
      kubernetes{
          cloud 'kubernetes-bj' //Jenkins Master上配置的Kubernetes名称
          label 'SEQ-AUTOTEST-PYTHON36' //Jenkins slave的前缀
          defaultContainer 'python36' // stages和post步骤中默认用到的container。如需指定其他container,可用语法 container("jnlp"){...}
          idleMinutes 10 //所创建的pod在job结束后的空闲生存时间
          yamlFile "jenkins/jenkins_pod_template.yaml" // pod的yaml文件
      }
    }
    environment {
        git_url = '[email protected]:liuchunming033/seq_jenkins_template.git'
        git_key = 'c8615bc3-c995-40ed-92ba-d5b66'
        git_branch = 'master'
        email_list = '[email protected]'
    }
    options {
        buildDiscarder(logRotator(numToKeepStr: '30'))  //保存的job构建记录总数
        timeout(time: 30, unit: 'MINUTES')  //job超时时间
        disableConcurrentBuilds() //不允许同时执行流水线
    }
    stages {
        stage('拉取测试代码') {
            steps {
                git branch: "${git_branch}", credentialsId: "${git_key}", url: "${git_url}"
            }
        }
        stage('安装测试依赖') {
            steps {
                sh "pipenv install"
            }
        }
        stage('执行测试用例') {
            steps {
                sh "pipenv run py.test"
            }
        }
    }
    post {
        always{
            container("jnlp"){ //在jnlp container中生成测试报告
                allure includeProperties: false, jdk: '', report: 'jenkins-allure-report', results: [[path: 'allure-results']]
            }   
        }
    }
}

No Pipeline acima, a parte principal relacionada a este artigo é a seção agent.kubernetes.Esta seção descreve como gerar o Jenkins Slave na plataforma kubernetes.

cloud é o nome do Kubernetes configurado no Jenkins Master, usado para identificar qual nuvem do Kubernetes é usada pelo pipeline atual.

O rótulo é o prefixo do nome Jenkins Slave. É usado para distinguir diferentes Jenkins Slaves. Quando ocorre uma exceção, você pode usar esse nome para depurar na nuvem do Kubernetes.

defaultContainer, no Jenkins Slave, defini três contêineres, que foram introduzidos anteriormente. O defaultContainer representa o contêiner padrão no qual o código é executado em estágios e estágios pós no pipeline. Em outras palavras, se você não especificar um contêiner nos estágios e estágios pós, o código será executado no defaultContainer por padrão. Se você quiser usar outros contêineres para executar o código, precisará especificá-lo de maneira semelhante ao contêiner ("jnlp") {...}.

idleMinutes especifica por quanto tempo o Jenkins Slave pode ser retido após o término do trabalho de teste em execução no Jenkins Slave. Durante esse período, o Jenkins Slave não será reciclado pelo Kubernetes.Se houver um trabalho de teste com a mesma etiqueta agendada durante esse período, você poderá continuar usando esse Jenkins Slave ocioso. O objetivo disso é melhorar a utilização do Jenkins Slave e evitar o agendamento frequente do Kubernetes, porque é demorado gerar com êxito um Jenkins Slave.

yamlFile, esse arquivo é um arquivo de modelo padrão do Kubernetes Pod. O Kubernetes gera objetos de Pod com base nesse arquivo, que são usados ​​como Jenkins Slave. Este arquivo define três contêineres (Container) e regras de agendamento e armazenamento externo. Este arquivo usa o Kubernetes como o arquivo principal do cluster Jenkins Slave.O conteúdo deste arquivo será descrito em detalhes abaixo.

Neste ponto, o Pipeline do trabalho de teste é estabelecido.

Modelo personalizado de Jenkins Slave

Ao usar uma máquina virtual como Jenkins Slave, se uma nova máquina virtual for adicionada, precisamos inicializar a máquina virtual, principalmente para instalar software de ferramenta, pacotes dependentes e conectar-se ao Jenkins Master. O mesmo ocorre ao usar a nuvem Kubernetes como um cluster Jenkins Slave.Você precisa definir o sistema operacional, o software dependente e os discos externos usados ​​pelo Jenkins Slave. As informações são gravadas em um arquivo Yaml, que é um arquivo de modelo padrão para os objetos de pod do Kubernetes. O Kubernetes irá gerar um Pod com base nesse arquivo Yaml e conectá-lo ao Jenkins Master.

O conteúdo deste arquivo Yaml é o seguinte:

apiVersion: v1
kind: Pod
metadata:
  # ① 指定 Pod 将产生在Kubernetes的哪个namespace下,需要有这个namespace的权限
  namespace: sqe-test  
spec:
  containers:
    # ② 必选,负责连接Jenkins Master,注意name一定要是jnlp
    - name: jnlp
      image: swc-harbor.nioint.com/sqe/jnlp-slave:root_user
      imagePullPolicy: Always
      # 将Jenkins的WORKSPACE(/home/jenkins/agent)挂载到jenkins-slave
      volumeMounts:
        - mountPath: /home/jenkins/agent
          name: jenkins-slave

    # ③ 可选,python36环境,已安装pipenv,负责执行python编写的测试代码
    - name: python36
      image: swc-harbor.nioint.com/sqe/automation_python36:v1
      imagePullPolicy: Always
      # 通过cat命令,让这个container保持持续运行
      command:
        - cat
      tty: true
      env:
        # 设置pipenv的虚拟环境路径变量 WORKON_HOME
        - name: WORKON_HOME 
          value: /home/jenkins/agent/.local/share/virtualenvs/
      # 创建/home/jenkins/agent目录并挂载到jenkins-slave Volume上
      volumeMounts: 
        - mountPath: /home/jenkins/agent
          name: jenkins-slave
      # 可以对Pod使用的资源进行限定,可调。尽量不要用太多,够用即可。
      resources: 
        limits:
          cpu: 300m
          memory: 500Mi

    # ④ 可选,Java8环境,已安装maven,负责执行Java编写的测试代码
    - name: java8
      image: swc-harbor.nioint.com/sqe/automation_java8:v2
      imagePullPolicy: Always
      command:
        - cat
      tty: true
      volumeMounts:
        - mountPath: /home/jenkins/agent
          name: jenkins-slave

  # ⑤ 声明一个名称为 jenkins-slave 的 NFS Volume,多个container共享
  volumes:
    - name: jenkins-slave
      nfs:
        path: /data/jenkins-slave-nfs/
        server: 10.125.234.64
  # ⑥ 指定在Kubernetes的哪些Node节点上产生Pod
  nodeSelector:
    node-app: normal
    node-dept: sqe

Por meio do arquivo Yaml acima, é possível ver que três contêineres são definidos no Pod por meio de spec.containers, a saber, jnlp responsável pela conexão com o Jenkins Master, python36 responsável pela execução do código Python e java8 responsável pela execução do código Java. Podemos comparar o Jenkins Slave a um pod, e o recipiente dentro dele é como um feijão no pod. Cada bean tem uma função diferente.

Ao mesmo tempo, também é declarado um volume chamado jenkins-slave.O contêiner jnlp monta o diretório Jenkins WORKSPACE (/ home / jenkins / agent) para jenkins-slave. Ao mesmo tempo, os dois contêineres python36 e java8 também montam o diretório / home / jenkins / agent no jenkins-slave. Portanto, a modificação do diretório / home / jenkins / agent em qualquer contêiner pode ler o conteúdo modificado nos outros dois contêineres. O principal benefício da montagem do armazenamento externo é que os resultados do teste e o ambiente virtual podem ser persistentes, especialmente após a persistência do ambiente virtual, em vez de executar um teste a cada vez para criar um novo ambiente virtual, mas reutilizar o ambiente virtual existente, Acelere todo o processo de execução do teste.

Além disso, também especifica qual namespace de namespace do kubernetes é usado e em quais nós do nó Jenkins Slave são gerados. Em relação a outros detalhes deste arquivo Yaml, escrevi nos comentários do arquivo, você pode consultá-lo para entender.

Imagem de contêiner personalizado

Os três contêineres usados ​​no Jenkins Slave foram introduzidos anteriormente.Vamos dar uma olhada nas imagens desses três contêineres separadamente.

Primeiro, o DockerHub (https://hub.docker.com/r/jenkinsci/jnlp-slave) fornece a imagem oficial do Jenkins Slave.Nós alternamos o usuário padrão na imagem oficial para o usuário raiz, caso contrário, ao executar casos de teste Podem ocorrer problemas de permissão. O Dockerfile da imagem do contêiner JNLP é o seguinte:

FROM jenkinsci/jnlp-slave:latest
LABEL maintainer="[email protected]"
USER root

A imagem do Python é pipenv instalada na imagem oficial do Python 3.6.4. Como os atuais projetos Python de nossa equipe dependem da pipenv para gerenciar os projetos. Deixe-me falar sobre isso aqui, o pipenv é uma versão atualizada do pip, que pode não apenas criar um ambiente virtual independente para o seu projeto, mas também manter e gerenciar automaticamente os pacotes dependentes do projeto. Ao contrário do pip, que usa requirements.txt para gerenciar dependências, o pipenv usa o Pipefile para gerenciar dependências. Os benefícios aqui não serão introduzidos. Os amigos interessados ​​podem verificar a documentação oficial do pipenv https://github.com/pypa/pipenv. O arquivo de encaixe da imagem do Python é o seguinte:

FROM python:3.6.4
LABEL maintainer="[email protected]"
USER root
RUN pip install --upgrade pip
RUN pip3 install pipenv

A imagem Java é baseada na extensão da imagem maven no DockerHub. A principal mudança é colocar o arquivo de configuração maven settings.xml usado dentro da empresa no espelho. O Dockerfile completo é o seguinte:

FROM maven:3.6.3-jdk-8
LABEL maintainer="[email protected]"
USER root

# 设置系统时区为北京时间
RUN mv /etc/localtime /etc/localtime.bak && \
    ln -s /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime && \
    echo "Asia/Shanghai" > /etc/timezone # 解决JVM与linux系统时间不一致问题
# 支持中文
RUN apt-get update && \
    apt-get install locales -y && \
    echo "zh_CN.UTF-8 UTF-8" > /etc/locale.gen && \
    locale-gen
# 更新资源地址
ADD settings.xml /root/.m2/

# 安装jacococli
COPY jacoco-plugin/jacococli.jar  /usr/bin
RUN  chmod +x /usr/bin/jacococli.jar

Depois de criar a imagem do contêiner, nós a enviaremos para o porto interno da empresa, para que os kubernetes possam puxar a imagem rapidamente. Você pode criar sua própria imagem de contêiner de acordo com a situação real e as necessidades do projeto.

Executar testes automatizados

Nas etapas anteriores, concluímos a preparação do uso do Kubernetes como Jenkins Slave. O próximo passo é executar o trabalho de teste. Comparado ao uso de uma máquina virtual para executar um trabalho de teste, esta etapa é realmente a mesma.

Crie um trabalho no estilo de pipeline e configure-o da seguinte maneira:

Após a conclusão da configuração, clique em Compilar para iniciar o teste.

Otimização de desempenho

Ao contrário da máquina virtual como Jenkins Salve, o Kubernetes que gera o Jenkins Slave é um processo dinâmico de criação, porque é criado dinamicamente e envolve problemas de eficiência. Há duas maneiras de resolver o problema de eficiência: por um lado, tente usar o Jenkins Slave existente para executar o trabalho de teste. Por outro lado, é acelerar a eficiência da geração do Jenkins Slave. Abaixo, veremos as medidas de otimização específicas desses dois aspectos.

7.1 Faça pleno uso do Jenkins Slave existente

Faça pleno uso do Jenkins Slave existente, você pode começar de dois aspectos.

Por um lado, a configuração de idleMinutes permite que o Jenkins Slave não seja destruído imediatamente após a execução do trabalho de teste, mas pode ficar ocioso por um período de tempo.Se um trabalho de teste for iniciado durante esse período, ele poderá ser designado para ser executado nele, o que melhora A taxa de utilização existente do Jenkins Slave também evita a criação demorada do Jenkins Slave.

Por outro lado, em mais pipelines de trabalhos de teste, a mesma etiqueta é usada, para que, após o término do trabalho de teste atual, o Jenkins Slave também possa ser usado pelo trabalho de teste que será iniciado e use a mesma etiqueta. Por exemplo, o Jenkins Slave usado pelo job1 é

DD-SEQ-AUTOTEST-PYTHON, quando o trabalho de teste1 terminar, use o mesmo trabalho de teste2 para iniciar, você poderá usar diretamente o Jenkins Slave usado pelo trabalho de teste1.

7.2 Acelere a eficiência da programação do Jenkins Slave

O processo completo para gerar o Jenkins Slave no Kubernetes e adicioná-lo ao Jenkins Master é:

  • O Jenkins Master calcula a situação atual de carga;

  • De acordo com a carga, o Jenkins Master inicia solicitações ao servidor da API do Kubernetes por meio do plug-in do Kubernetes, conforme necessário;

  • O servidor da API Kubernetes agenda Pods para o cluster Kubernetes;

  • Depois que o Pod é gerado, ele é automaticamente conectado ao Jenkins Master através do protocolo JNLP.

Os últimos três passos são muito rápidos, principalmente afetados pela rede. Na primeira etapa, após uma série de cálculos de algoritmos, o Jenkins Master descobriu que nenhum Jenkins Slave estava disponível antes de decidir iniciar uma solicitação ao servidor da API do Kubernetes. Esse processo não é eficiente na configuração de inicialização padrão do Jenkins Master. Geralmente, faz com que um novo trabalho de teste aguarde um pouco antes de começar a gerar Pods no Kubernetes.

Portanto, é necessário modificar os itens de inicialização do Jenkins Master, que envolvem principalmente os seguintes parâmetros:

-Dhudson.model.LoadStatistics.clock=2000 
-Dhudson.slaves.NodeProvisioner.recurrencePeriod=5000 
-Dhudson.slaves.NodeProvisioner.initialDelay=0 
-Dhudson.model.LoadStatistics.decay=0.5 
-Dhudson.slaves.NodeProvisioner.MARGIN=50 
-Dhudson.slaves.NodeProvisioner.MARGIN0=0.85

O Jenkins Master calculará a carga do cluster em intervalos. O intervalo de tempo é determinado por hudson.model.LoadStatistics.clock. O padrão é 10 segundos. Nós o ajustaremos em 2 segundos para acelerar a frequência do Master para calcular a carga do cluster, para saber mais rapidamente Mudanças na carga. Por exemplo, costumava levar até 10 segundos para saber quantos trabalhos precisam ser agendados para execução, agora são necessários apenas 2 segundos.

Quando o Jenkins Master calculou a carga do cluster, descobriu-se que não havia Jenkins Slave disponível. O mestre Jenkins notificará o NodeProvisioner do plug-in Kubernetes para produzir Pods em intervalos de período de recorrência. Portanto, o valor recurrencePeriod não pode ser menor que hudson.model.LoadStatistics.clock, caso contrário, vários escravos Jenkins serão gerados.

initialDelay é um tempo de atraso, usado originalmente para garantir que o Jenkins Slave e o Master estáticos estejam conectados, porque estamos usando o plug-in Kubernetes para gerar dinamicamente o slave Jenkins, não há Jenkins Slave estático, portanto, definimos o parâmetro como 0.

O significado original do parâmetro hudson.model.LoadStatistics.decay é suprimir a instabilidade da carga principal, que tem uma grande influência no valor da carga obtido pela avaliação. A deterioração padrão é 0,9. Definimos decaimento para 0,5 para permitir flutuações relativamente grandes na carga.A carga avaliada pelo Jenkins Master é a carga real, tanto quanto possível, e o número de escravos necessários para a avaliação.

hudson.slaves.NodeProvisioner.MARGIN e hudson.slaves.NodeProvisioner.MARGIN0, esses dois parâmetros alinham a carga calculada até um número inteiro, o que pode gerar um escravo adicional para melhorar a eficiência.

Adicione os parâmetros acima ao processo de inicialização do Jenkins Mater e reinicie o Jenkins Master para entrar em vigor.

java -Dhudson.model.LoadStatistics.clock=2000 -Dxxx -jar jenkins.war

Sumário

Este artigo apresenta as vantagens de usar o Kubernetes como um ambiente de teste de integração contínuo, detalha o método de uso e otimiza seu desempenho. Dessa forma, as deficiências da máquina virtual como Jenkins Slave são perfeitamente resolvidas.

Além dos benefícios que o teste automatizado pode se beneficiar do Kubernetes, no processo de configuração de um ambiente de teste de desempenho, com a ajuda do mecanismo de expansão elástica dinâmica do Kubernetes, a criação de clusters de teste de pressão em larga escala tem vantagens óbvias em termos de eficiência e conveniência.

Introdução do autor: Liu Chunming, evangelista em tecnologia de teste de software, veterano em testes de dez anos, especialista em blogs da CSDN, conferencista do MSTC, conferencista do ArchSummit, executando a conta pública "Ming Shuo software testing". Bom no desenvolvimento da estrutura de teste, desenvolvimento da plataforma de teste, integração contínua, governança do ambiente de teste, etc.

【FIM】

Recomendações mais interessantes

PlatformUma plataforma de desenvolvimento de IA assassina está aqui! Diga adeus à troca de ferramentas de modelagem dispersa

IdeaA ideia de transporte inteligente causada pelo engarrafamento da quarta via circular de Pequim

Por favor, não me pergunte qual é a pilha!

IdeaA ideia de transporte inteligente causada pelo engarrafamento da quarta via circular de Pequim

MachineA máquina virtual da sua empresa ainda está ociosa? Aprenda sobre as práticas de teste de integração contínua baseadas em Jenkins e Kubernetes!

WebDe Web1.0 a Web3.0: análise detalhada do desenvolvimento e direção futura da Internet nesses anos

Todo "assistindo" você pede, eu levo isso a sério

Artigos publicados em 1958 · mais de 40.000 elogios · 18,17 milhões de visualizações

Acho que você gosta

Origin blog.csdn.net/csdnnews/article/details/105548964
Recomendado
Clasificación