加餐1:搭建基于 K8 和 Docker 的 Jenkin 可伸缩持续集成系统

根据前面用户的反馈,这里补充一个完整的动手实践的案例——搭建“基于 K8s 和 Docker 的 Jenkins 可伸缩持续集成系统”,让模块 3 所介绍的内容落地。


这部分内容比较多且非常具体,包括 4 大部分:

  • Kubernetes (K8s)集群的部署,包括 kube-proxy、kubelet、docker 和 flanneld services 等安装;
  • 企业级容器注册管理平台 Harbor 的安装部署,包括 Docker、Docker Compose 等安装;
  • 采用 Jenkins pipeline 实现自动构建并部署至 K8s,包括建立 spring boot 示例工程、创建  Dockerfile 和 Jenkinsfile、配置 jenkins pipeline 任务和 K8s 的 kube.config 到最后测试 pipeline 任务等;
  • 遇到的问题(坑)及解决方法,比如启动 Jenkins,安装插件出现“无法连接服务器”错误,运行 pipeline,出现 command not found 错误等几个问题的解决。


这些具体的操作步骤经过了真实环境上的检验,最终整个基于 K8s 、Docker、Jenkins 的 CI 系统被成功部署起来。建议你按照下面介绍的详细步骤,自己亲自动手操作一回,功力会大增。

工作流程图

系统配置

Harbor 仓库 CentOS7、4 核 CPU、16G 内存、160G 硬盘

  • 192.168.10.160    harbor

集群 3 台机器,CentOS7、4 核 CPU、16G 内存、60G 硬盘

  • 192.168.10.161  k8s-master
  • 192.168.10.162  k8s-node1
  • 192.168.10.163  k8s-node2

Kubernetes 集群部署

安装前准备

(1)关闭 firewalld 改用 iptables。输入以下命令,关闭 firewalld:


[root@master ~]# systemctl stop firewalld.service #停止firewall 
[root@master ~]# systemctl disable firewalld.service #禁止firewall开机启动



(2)安装 ntp 服务:


[root@master ~]# yum install -y ntp wget net-tools 
[root@master ~]# systemctl start ntpd 
[root@master ~]# systemctl enable ntpd


安装配置

(1)安装 Kubernetes Master

 

使用以下命令安装 kubernetes 和 etcd:


# yum install -y kubernetes etcd



编辑 /etc/etcd/etcd.conf 使 etcd 监听所有的 IP 地址,确保下列行没有注释,并修改为下面的值:



[root@master ~]# cat /etc/etcd/etcd.conf 
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379" 
#[cluster] 
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.10.161:2379"



编辑 Kubernetes API server 的配置文件 /etc/kubernetes/apiserver,确保下列行没有被注释,并设置合适的值:



[root@master ~]# cat /etc/kubernetes/apiserver
###
# kubernetes system config
#
# The following values are used to configure the kube-apiserver
#

# The address on the local server to listen to.
KUBE_API_ADDRESS="--address=0.0.0.0"

# The port on the local server to listen on.
KUBE_API_PORT="--port=8080"

# Port minions listen on
KUBELET_PORT="--kubelet_port=10250"

# Comma separated list of nodes in the etcd cluster
KUBE_ETCD_SERVERS="--etcd_servers=http://192.168.10.161:2379"

# Address range to use for services
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=10.254.0.0/16"

# default admission control policies
KUBE_ADMISSION_CONTROL="--admission_control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota"

# Add your own!
KUBE_API_A



启动 etcd、kube-apiserver、kube-controller-manager and kube-scheduler 服务,并设置开机自启:



[root@master ~]# cat /script/kubenetes_service.sh

for SERVICES in etcd kube-apiserver kube-controller-manager kube-scheduler; do 
    systemctl restart $SERVICES
    systemctl enable $SERVICES
    systemctl status $SERVICES 
done
[root@master ~]# sh /script/kubenetes_service.sh



在 etcd 中定义 flannel network 的配置,这些配置会被 flannel service 下发到 nodes 中:



[root@master ~]# etcdctl mk /centos.com/network/config '{"Network":"172.17.0.0/16"}'



添加 iptables 规则,打开相应的端口:



[root@master ~]# iptables -I INPUT -p tcp --dport 2379 -j ACCEPT
[root@master ~]# iptables -I INPUT -p tcp --dport 10250 -j ACCEPT
[root@master ~]# iptables -I INPUT -p tcp --dport 8080 -j ACCEPT 
[root@master ~]# iptables-save



或者写入 iptables 配置文件 /etc/sysconfig/iptables。


查看节点信息(我们还没有配置节点信息,所以这里应该为空):



[root@master ~]# kubectl get nodes

NAME LABELS STATUS



(2)安装 Kubernetes Nodes

 

注:下面这些步骤应该在 node1 和 node2 上执行(也可以添加更多的 node)。


使用 yum 安装 kubernetes 和 flannel:



[root@slave1 ~]# yum install -y flannel kubernetes



为 flannel service 配置 etcd 服务器,编辑 /etc/sysconfig/flanneld 文件中的下列行以连接到 master:



[root@slave1 ~]# cat /etc/sysconfig/flanneld

FLANNEL_ETCD="http://192.168.10.161:2379" #改为etcd服务器的ip
FLANNEL_ETCD_PREFIX="/centos.com/network"



编辑 /etc/kubernetes/config 中 kubernetes 的默认配置,以确保 KUBE_MASTER 的值连接到 Kubernetes master API server:



[root@slave1 ~]# cat /etc/kubernetes/config

KUBE_MASTER="--master=http://192.168.10.161:8080"



编辑 /etc/kubernetes/kubelet 中五个参数的值:


node1:


[root@slave1 ~]# cat /etc/kubernetes/kubelet

KUBELET_ADDRESS="--address=0.0.0.0"
KUBELET_PORT="--port=10250"
KUBELET_HOSTNAME="--hostname_override=192.168.10.162"
KUBELET_API_SERVER="--api_servers=http://192.168.10.161:8080"
KUBELET_ARGS=""



node2:



[root@slave2 ~]# cat /etc/kubernetes/kubelet

KUBELET_ADDRESS="--address=0.0.0.0"
KUBELET_PORT="--port=10250"
KUBELET_HOSTNAME="--hostname_override=192.168.10.163"
KUBELET_API_SERVER="--api_servers=http://192.168.10.161:8080"
KUBELET_ARGS=""



启动 kube-proxy、kubelet、docker 和 flanneld services 服务,并设置开机自启:



[root@slave1 ~]# cat /script/kubernetes_node_service.sh

for SERVICES in kube-proxy kubelet docker flanneld; do 
systemctl restart $SERVICES
systemctl enable $SERVICES
systemctl status $SERVICES 
done



在每个 node 节点上,你应当注意到有两块新的网卡 docker0 和 flannel0,应该得到不同的 IP 地址范围在 flannel0 上,就像下面这样:


node1:



[root@slave1 ~]# ip a | grep docker | grep inet
inet 172.17.0.1/16 scope global docker0



node2:



[root@slave2 ~]# ip a | grep docker | grep inet
inet 172.17.60.0/16 scope global docker0



添加 iptables 规则:



[root@slave1 ~]# iptables -I INPUT -p tcp --dport 2379 -j ACCEPT
[root@slave1 ~]# iptables -I INPUT -p tcp --dport 10250 -j ACCEPT
[root@slave1 ~]# iptables -I INPUT -p tcp --dport 8080 -j ACCEPT



现在登录 kubernetes master 节点验证 minions 的节点状态:



[root@master ~]# kubectl get nodes
NAME           STATUS    AGE
192.168.10.162   Ready     2h
192.168.10.163   Ready     2h



至此,Kubernetes 集群已经配置并运行了,然后我们继续下面的步骤。

Harbor 安装部署

Harbor 是 VMWare 公司开源的企业级 Docker Registry 项目,项目地址是https://github.com/goharbor/harbor

下载离线安装包

下载地址 https://github.com/goharbor/harbor/releases


docker compose 持续集成 基于docker的持续集成系统_cicd


机器配置要求:


docker compose 持续集成 基于docker的持续集成系统_docker_02


将下载的安装包上传到服务器,运行以下命令解压:



tar zxvf harbor-offline-installer-v1.10.1.tgz


安装 Docker


# 安装依赖包
yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加Docker软件包源
yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo# 安装Docker CE
yum install -y docker-ce
# 启动Docker服务并设置开机启动
systemctl start docker
systemctl enable docker


安装 docker-compose

Docker Compose 是 Docker 提供的一个命令行工具,用来定义和运行由多个容器组成的应用。使用 compose,我们可以通过 YAML 文件声明式的定义应用程序的各个服务,并由单个命令完成应用的创建和启动。


执行以下命令进行安装:



yum install epel-release 
yum install -y python-pip 
pip install docker-compose 
yum install git


Harbor 安装与配置

修改 harbor.yml:

  • hostname 这里设置本机的 IP
  • harbor_admin_password web 页面的密码

运行:



sh ./install.sh



访问页面 http://192.168.10.160/


docker compose 持续集成 基于docker的持续集成系统_cicd_03

Docker 主机访问 Harbor

在另外一个服务器(client)登录 Harbor:



# docker login 192.168.10.160
Username: admin
Password: 
Error response from daemon: Get https:// 192.168.10.160/v2/: dial tcp 192.168.10.160:443: connect: connection refused



这是因为 docker1.3.2 版本开始默认 docker registry 使用的是 https,我们设置 Harbor 默认为 http 方式,所以当执行用 docker login、pull、push 等命令操作而非 https 的 docker regsitry 的时就会报错。

 

解决 https

 

在 harbor 那台服务器上,其安装目录:



vi docker-compose.yml


docker compose 持续集成 基于docker的持续集成系统_cicd_04


修改 ports 信息


然后我们执行:



docker-compose stop
./install.sh



然后同时编辑 harbor 和 client 的 docker 配置文件:



# 1.
vim /etc/docker/daemon.json

{
 "insecure-registries": ["192.168.10.160"]
}

# 2.添加ExecStart=/usr/bin/dockerd |--insecure-registry=192.168.10.160
vim /usr/lib/systemd/system/docker.service

# 把这行注释掉,添加下面的配置 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecStart=/usr/bin/dockerd
  |--insecure-registry=192.168.10.160



1. 重启 docker



systemctl daemon-reload
systemctl restart docker



2.重启 harbor 的 docker-compose,命令如下:



docker-compose restart


 

client 登录仓库



# docker login 192.168.10.160
Username: admin
Password: 
Login Succeeded


采用 jenkins pipeline 实现自动构建并部署至 K8s

部署 jenkins

这里采用 yum install 的方式部署 jenkins。


(1)安装JDK:



yum install -y java



(2)安装 jenkins


添加 Jenkins 库到 yum 库,Jenkins 将从这里下载安装。



wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key
yum install -y Jenkins



(3)配置 jenkis 的端口:



vi /etc/sysconfig/jenkins



找到修改端口号:



JENKINS_PORT="8085"  此端口不冲突可以不修改



(4)启动 jenkins:



service jenkins start/stop/restart



(5)访问 http://localhost:8085 地址,等待出现下面解锁 jenkins 界面:


docker compose 持续集成 基于docker的持续集成系统_cicd_05


(6)在上图提示的密码文件中复制自动生成的密码。

(7)在解锁 jenkins 页面,粘贴密码并继续。

(8)解锁 jenkins 后,在界面中选择“安装建议的插件” 选项。

(9)最后,jenkins 要求创建管理员用户,创建新用户或使用 admin 用户,按照步骤完成后即可登录并使用 jenkis 了。

准备 java 示例工程

下面新建 spring boot 示例工程,示例工程的代码地址为:https://github.com/gemedia/docker-demo

创建 spring boot 示例工程

(1)生成 spring boot 基础工程,添加一个示例 Controller 类。



package com.docker.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("")
    public String hello() {
        return "Hello!";
    }



(2)修改 application 配置文件,设置端口。



spring.application.name=docker-demo
server.port=40080



(3)编译运行,访问 http://localhost:40080 地址可以看到示例运行结果。


docker compose 持续集成 基于docker的持续集成系统_cicd_06

添加 Dockerfile

在工程根目录创建 Dockerfile,用来构建 docker 镜像,其中 ${JAR_FILE} 参数在 pipeline 执行 docker build 时,通过 build-arg 参数传入。



FROM openjdk:8-jdk-alpine

#构建参数
ARG JAR_FILE
ARG WORK_PATH="/opt/demo"
# 环境变量
ENV JAVA_OPTS="" \
    JAR_FILE=${JAR_FILE}

#设置时区
RUN apk update && apk add ca-certificates && \
    apk add tzdata && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo "Asia/Shanghai" > /etc/timezone

COPY target/$JAR_FILE $WORK_PATH/

WORKDIR $WORK_PATH

ENTRYPOINT exec java $JAVA_OPTS -jar $JAR_


添加 K8s 的 Deployment 配置

在工程根目录创建 k8s-deployment.tpl 文件,此文件用来作为 K8s 的 yaml 文件模板。在 jenkens pipeline 执行时,会先将 tpl 文件中 {} 括起来的自定义参数用 sed 命令替换为实际的内容。



apiVersion: apps/v1
kind: Deployment
metadata:
  name: {APP_NAME}-deployment
  labels:
    app: {APP_NAME}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: {APP_NAME}
  template:
    metadata:
      labels:
        app: {APP_NAME}
    spec:
      containers:
      - name: {APP_NAME}
        image: {IMAGE_URL}:{IMAGE_TAG}
        ports:
        - containerPort: 40080
        env:
          - name: SPRING_PROFILES_ACTIVE
            value: {SPRING_PROFILE}


添加 Jenkinsfile

在工程根目录创建 Jenkinsfile,用来执行 jenkins pipeline 任务。Jenkinsfile 文件的大概内容描述如下。


environment 中变量说明:

  • HARBOR_CREDS 为 harbor 镜像仓库的用户密码,数据保存为 jenkins 的“username and password”类型的凭据,用 credentials 方法从凭据中获取,使用时通过 HARBOR_CREDS_USR 获取用户名,HARBOR_CREDS_PSW 获取密码;
  • K8S_CONFIG 为 K8s 中 kubectl 命令的 yaml 配置文件内容,数据保存为 jenkins 的“Secret Text”类型的凭据,用 credentials 方法从凭据中获取,这里保存的 yaml 配置文件内容以 base64 编码格式保存,在设置凭据时先要进行 base64 编码(此 base64 编码是非必须的,如果直接保存原文,下面 Jenkinsfile 中需要去掉 base64 -d 解码) ;
  • GIT_TAG 变量通过执行 sh 命令获取当前 git 的 tag 值,由于后面构建 docker 镜像时使用 git 的 tag 作为镜像的标签,所以这个变量也不能为空。


parameters 中变量说明:

  • HARBOR_HOST,harbor 镜像仓库地址;
  • DOCKER_IMAGE,docker 镜像名,包含 harbor 项目名称;
  • APP_NAME,K8s 中的标签名称,对应 K8s 的 yaml 模板中的 {APP_NAME};
  • K8S_NAMESPACE,K8s 中的 namespace 名称,执行 kubectl 命令会部署至此命名空间。


stages 说明:

  • Maven Build,使用 docker 的方式执行 maven 命令,args 参数中将 .m2 目录映射出来,避免执行时重复从远端获取依赖;stash 步骤中将 jar 文件保存下来,供后面的 stage 使用;
  • Docker Build,unstash 获取 jar 文件,通过 sh 依次执行 docker 命令登录 harbor、构建镜像、上传镜像、移除本地镜像,构建镜像时,会获取 jar 文件名传入 JAR_FILE 参数;
  • Deploy,使用 docker 的方式执行 kubectl 命令,在执行前先将 K8S_CONFIG 中的内容进行 base64 解密并存为 ~/.kube/config 配置文件,然后执行 sed 命令将 k8s-deployment.tpl 文件中“{参数名}”形式参数替换为实际的参数值,最后执行 kubectl 命令部署至 K8s。



// 需要在jenkins的Credentials设置中配置jenkins-harbor-creds、jenkins-k8s-config参数
pipeline {
    agent any
    environment {
        HARBOR_CREDS = credentials('jenkins-harbor-creds')
        K8S_CONFIG = credentials('jenkins-k8s-config')
        GIT_TAG = sh(returnStdout: true,script: 'git describe --tags --always').trim()
    }
    parameters {
        string(name: 'HARBOR_HOST', defaultValue: '192.168.10.160', description: 'harbor仓库地址')
        string(name: 'DOCKER_IMAGE', defaultValue: 'tssp/pipeline-demo', description: 'docker镜像名')
        string(name: 'APP_NAME', defaultValue: 'pipeline-demo', description: 'k8s中标签名')
        string(name: 'K8S_NAMESPACE', defaultValue: 'demo', description: 'k8s的namespace名称')
    }
    stages {
        stage('Maven Build') {
            when { expression { env.GIT_TAG != null } }
            agent {
                docker {
                    image 'maven:3-jdk-8-alpine'
                    args '-v $HOME/.m2:/root/.m2'
                }
            }
            steps {
                sh 'mvn clean package -Dfile.encoding=UTF-8 -DskipTests=true'
                stash includes: 'target/*.jar', name: 'app'
            }

        }
        stage('Docker Build') {
            when { 
                allOf {
                    expression { env.GIT_TAG != null }
                }
            }
            agent any
            steps {
                unstash 'app'
                sh "docker login -u ${HARBOR_CREDS_USR} -p ${HARBOR_CREDS_PSW} ${params.HARBOR_HOST}"
                sh "docker build --build-arg JAR_FILE=`ls target/*.jar |cut -d '/' -f2` -t ${params.HARBOR_HOST}/${params.DOCKER_IMAGE}:${GIT_TAG} ."
                sh "docker push ${params.HARBOR_HOST}/${params.DOCKER_IMAGE}:${GIT_TAG}"
                sh "docker rmi ${params.HARBOR_HOST}/${params.DOCKER_IMAGE}:${GIT_TAG}"
            }
            
        }
        stage('Deploy') {
            when { 
                allOf {
                    expression { env.GIT_TAG != null }
                }
            }
            agent {
                docker {
                    image 'lwolf/helm-kubectl-docker'
                }
            }
            steps {
                sh "mkdir -p ~/.kube"
                sh "echo ${K8S_CONFIG} | base64 -d > ~/.kube/config"
                sh "sed -e 's#{IMAGE_URL}#${params.HARBOR_HOST}/${params.DOCKER_IMAGE}#g;s#{IMAGE_TAG}#${GIT_TAG}#g;s#{APP_NAME}#${params.APP_NAME}#g;s#{SPRING_PROFILE}#k8s-test#g' k8s-deployment.tpl > k8s-deployment.yml"
                sh "kubectl apply -f k8s-deployment.yml --namespace=${params.K8S_NAMESPACE}"
            }
            
        }
        
    }
}


配置 jenkins pipeline 任务

创建 jenkins pipeline 任务,并设置需要的参数。

新建 pipeline 任务

单击“新建任务”按钮,输入名称并选择“流水线”(pipeline),然后单击“确定”按钮。


docker compose 持续集成 基于docker的持续集成系统_k8s_07

配置 pipeline 任务

进入任务的配置界面,在流水线(pipeline)设置部分,选择“Pipeline script from SCM”选项。SCM 选项选为“Git”,配置好工程的 git 地址以及获取代码的凭证信息;然后在“Additional Behaviours”中添加“Clean before checkout”。


docker compose 持续集成 基于docker的持续集成系统_k8s_08

配置 harbor 账号与密码

选择“凭据”,然后在下图所示位置单击“添加凭据”按钮。在新凭据设置界面,类型选择为“Username with password”,ID 设置为“jenkins-harbor-creds”(此处的 ID 必须与 Jenkinsfile 中的保持一致)。Username 与 Password 分别设置为 harbor 镜像私库的用户名和密码。

     

docker compose 持续集成 基于docker的持续集成系统_jenkins_09


docker compose 持续集成 基于docker的持续集成系统_docker_10

配置 K8s 的 kube.config 信息

在 K8s 中使用 kubectl 命令时需要 yaml 格式的服务器以及授权信息配置文件,这里将 kubectl 的 yaml 配置文件的内容以 base64 编码后保存在 jenkins 的凭据中。pipeline 任务执行时,先从 jenkins 凭据中获取内容,进行 base64 解码后将配置保存为 ~/.kube/config 文件。kubectl 的配置文件的内容如下:



apiVersion: v1
kind: Config
clusters:
- name: "test"
  cluster:
    server: "https://xxxxx"
    api-version: v1
    certificate-authority-data: "xxxxxx"

users:
- name: "user1"
  user:
    token: "xxxx"

contexts:
- name: "test"
  context:
    user: "user1"
    cluster: "test"

current-context: "tes



可以在 Linux 中采用下面命令将 kubectl 的 yaml 配置文件进行 base64 编码。



base64 kube-config.yml > kube-config.txt



然后类似上一步,在 jenkins 凭据中增加配置文件内容。在凭据设置界面,类型选择为“Secret text”,ID 设置为“jenkins-k8s-config”(此处的 ID 必须与 Jenkinsfile 中的保持一致),Secret 设置为上面经过 base64 编码后的配置文件内容。


docker compose 持续集成 基于docker的持续集成系统_docker_11

测试 pipeline 任务

在创建的 pipeline 任务中,单击“Build With Parameters”按钮,即可立即执行 pipeline 任务。


docker compose 持续集成 基于docker的持续集成系统_docker_12


在当前界面中查看任务的执行结果,可以看到每个阶段的运行状态。

docker compose 持续集成 基于docker的持续集成系统_k8s_13


执行成功后,查看 harbor 镜像仓库,docker 镜像成功上传至 harbor。


docker compose 持续集成 基于docker的持续集成系统_cicd_14


在 Linux 服务器查看 deployment,运行以下命令:



kubectl get deployment



docker compose 持续集成 基于docker的持续集成系统_docker compose 持续集成_15


查看 pod:



kubectl get pod



docker compose 持续集成 基于docker的持续集成系统_cicd_16

遇到的问题及解决方法

(1)启动 Jenkins,安装插件出现“无法连接服务器”错误


安装插件那个页面,就是提示你 offline 的那个页面,不要动,然后打开一个新的 tab,输入网址 http://localhost:port/pluginManager/advanced。


这里面最底下有个“Update Site”,把其中的链接改成http://mirror.esuni.jp/jenkins/updates/update-center.json。


单击 submit 按钮:


docker compose 持续集成 基于docker的持续集成系统_docker compose 持续集成_17


然后重新启动 jenkins,这样就能正常安装插件。


(2)运行 pipeline,出现 command not found 错误


yum 安装的 Jenkins 配置文件默认位置 /etc/sysconfig/jenkins,默认 jenkins 服务以 jenkins 用户运行,这时在 jenkins 执行 ant 脚本时可能会发生没有权限删除目录、覆盖文件等情况。可以让 jenkins 以 root 用户运行来解决这个问题。


a.将 jenkins 账号分别加入到 root 组中:



gpasswd -a jenkins root



b.修改 /etc/sysconfig/jenkins 文件:



#user id to be invoked as (otherwise will run as root; not wise!)
JENKINS_USER=root
JENKINS_GROUP=root



可以修改为 root 权限运行,重启 jenkins 服务。


(3)在 docker build 阶段出现 exec: "docker-proxy": executable file not found in $PATH 错误,解决方法是,需要启动 docker-proxy:



cd /usr/libexec/docker/
 ln -s docker-proxy-current docker-proxy



(4)出现 Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? 错误,运行如下命令:



systemctl daemon-reload
service docker restart



(5)出现 shim error: docker-runc not installed on system. 错误,经过一番排查,如下解决方案有用:



cd /usr/libexec/docker/
 ln -s docker-runc-current docker-runc



加餐2:敏捷测试分析与计划的案例

这一讲是第 5 部分“敏捷测试分析与计划”加餐的内容,利用前面学到的测试分析和测试计划的知识,从信息收集到测试分析、测试策略制定,一步步制定出测试计划。案例仍然沿用了在线教育 App 第二次迭代的软件开发项目,也就是 App 2.0,项目背景请参考第 34 讲中的介绍。

这一讲的目标是为 App 2.0 制定一个测试计划,在这之前需要做的工作包括以下几个步骤:

  • 基于启发式测试策略的分析模型进行测试分析,收集项目的上下文信息;
  • 根据项目目标和质量要求制定测试目标;
  • 根据上下文信息,完成测试需求分析,明确测试范围,包括功能性和非功能性的测试;
  • 根据收集的项目上下文信息进行风险分析,识别出 Top 5 的测试风险并制定预防/处理措施;
  • 制定基于风险的测试策略;
  • 基于测试目标和测试策略等内容制定出测试计划。
信息收集

首先我采用在第 26、29 讲中的启发式测试策略的分析方法,从产品元素、质量标准、项目因素、可用的测试技术四个方面收集并分别进行归纳整理,如图 1 所示。在归纳整理过程中,聚焦 2.0 版本会发生哪些变化(和 1.0 版本比较),了解到变化主要发生在质量标准项目因素这两个部分,比如质量标准中要考虑用户大幅提升之后对性能提出的要求,而项目因素引入了更多的测试风险,比如单元测试覆盖率只有 40%,还有开发和测试人员都有人员变动等。

docker compose 持续集成 基于docker的持续集成系统_jenkins_18

从测试分析中得到项目的目标是在 4 周内如期交付 App 2.0,包括 7 个新的用户故事,对于安全性、稳定性、兼容性、耗电量、流量等方面的质量要求和 App 1.0 相同。结合其他的因素,比如目前的测试自动化率、时间和人员的情况,可以制定如图 2 所示的测试目标。在这个图里,测试目标按照四项测试子目标分别给出具体的目标,这四项子目标分别是:单元测试、功能测试、性能测试和其他的专项回归测试。

docker compose 持续集成 基于docker的持续集成系统_docker compose 持续集成_19

测试分析——测试范围

通过测试分析过程中收集的信息,可以得出如下测试范围:

  • 新功能特性,包括 7 个用户故事的功能以及功能之间的交互;
  • 新版本的专项测试,新的软件包的升级/安装/启动/卸载;
  • 已有功能的回归测试,验证已有功能是否受到新的改动带来的影响;
  • 功能性的专项回归测试,交叉事件测试、弱网络测试、边界测试;
  • 非功能性的回归测试,包括兼容性、性能、安全性、易用性、耗电量、网络流量等。

如图 3 所示,测试范围按照测试项进行了划分。

docker compose 持续集成 基于docker的持续集成系统_docker compose 持续集成_20

测试分析——风险分析和控制

根据测试分析的结果应用第 28 讲的知识,分析测试风险并制定预防/处理措施,如图 4 所示。这里识别出 Top 5 的测试风险分别为:

  • 单元测试的代码覆盖率低导致代码质量低;
  • 用户界面需求变更导致测试自动化率降低;
  • 需要兼容的手机/型号/分辨率种类太多;
  • 手工专项回归测试任务重;
  • 新加入的测试人员探索式测试经验不多。

docker compose 持续集成 基于docker的持续集成系统_docker_21

基于风险的测试策略

根据测试范围分析和风险分析的结果,接下来就可以为 App2.0 制定测试策略了,如图 5 所示。

docker compose 持续集成 基于docker的持续集成系统_docker compose 持续集成_22

因为是在 App1.0 的基础上制定测试策略,所以重点是针对 App 2.0 的测试目标以及存在的测试风险在测什么、怎么测方面提出应对措施。比如,针对单元测试代码覆盖率低的问题,应该确保高优先级的用户故事的单元测试覆盖率达标,并且加强接口自动化测试。

测试计划

现在我们就可以制定出如图 4 所示的测试计划了。前面已经制定出了测试目标、测试项、测试风险和测试策略,几乎已经覆盖率测试计划中的所有关键内容。在此基础上,又添加了关于人员安排、测试交付物,以及测试依赖等内容,最后的测试计划如图 6 所示。

docker compose 持续集成 基于docker的持续集成系统_k8s_23

docker compose 持续集成 基于docker的持续集成系统_cicd_24

从上面测试计划制定过程中,首先要明确测试目标,后续测试分析、测试策略都是为了更好地实现测试目标。其次,在这过程中,我们采用思维导图的方式,很好地支持发散性思维和系统性思维的运用,一气呵成,快速完成测试计划中几个关键环节:测试范围的确定、测试风险分析、测试策略制定直至最后测试计划的制定。而且,测试计划中的每一项内容都标注了优先级,体现了基于风险的测试策略在敏捷测试中是不可缺少的,有助于我们更高效地完成测试。

这里受篇幅所限,但希望这些内容有着良好的参考价值,对你未来做好敏捷测试计划有所帮助。