基于k8s、docker、jenkins构建springboot服务

Jenkins + github + docker + k8s + springboot

本文介绍基于k8s、docker、jenkins、springboot构建docker服务。

环境准备

server-1 k8s-master Centos7 ip地址10.12.5.110server-2 k8s-node Centos7 ip地址10.12.5.115

两台服务执行如下命令

$ setenforce 0$ systemctl stop firewalld$ systemctl disable firewalld

server-1 k8s-master 安装

k8s-master节点安装kubernets、docker、etcd、git、maven等软件。

安装docker

$ yum install docker// 修改docker配置文件 /etc/sysconfig/dockerOPTIONS='--registry-mirror=https://docker.mirrors.ustc.edu.cn --selinux-enabled --log-driver=journald --signature-verification=false'ADD_REGISTRY='--add-registry 10.12.5.110:5000'INSECURE_REGISTRY='--insecure-registry 10.12.5.110:5000'$ systemctl enable docker$ service docker start

安装registry

$ docker pull registry$ mkdir -p /data/docker/registry$ docker run -d -p 5000:5000 -v /data/docker/registry:/var/lib/registry registry

安装etcd

$ yum install etcd$ vi /etc/etcd/etcd.conf// 修改如下配置ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"$ systemctl enable etcd$ systemctl start etcd

安装kubernetes

$ yum install kubernetes// 修改配置文件apiserver$ vi /etc/kubernetes/apiserver// 修改如下配置KUBE_API_ADDRESS="--insecure-bind-address=0.0.0.0"KUBE_API_PORT="--port=8080"KUBELET_PORT="--kubelet-port=10250"KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ResourceQuota"// 启动服务$ systemctl enable kube-apiserver kube-controller-manager kube-scheduler$ systemctl start kube-apiserver kube-controller-manager kube-scheduler

安装git和mvn

$ yum install git$ yum install maven

server-2 k8s-node 安装
k8s-master节点安装kubernets、docker等软件。

安装docker

$ yum install docker// 修改docker配置文件 /etc/sysconfig/dockerADD_REGISTRY='--add-registry 10.12.5.110:5000'INSECURE_REGISTRY='--insecure-registry 10.12.5.110:5000'$ systemctl enable docker$ systemctl start docker

安装kubernetes

$ yum install kubernetes// 修改配置文件kubelet$ vi /etc/kubernetes/kubelet// 修改如下配置KUBELET_ADDRESS="--address=0.0.0.0"KUBELET_PORT="--port=10250"KUBELET_HOSTNAME="--hostname-override=10.12.5.115"KUBELET_API_SERVER="--api-servers=http://10.12.5.110:8080"// 修改配置文件config$ vi /etc/kubernetes/config// 修改如下配置KUBE_MASTER="--master=http://10.12.5.110:8080"// 启动服务$ systemctl enable kube-proxy kubelet$ systemctl start kube-proxy kubelet

检查k8s集群运行情况

// 在k8s-master节点执行$ kubectl get nodesNAME          STATUS    AGE10.12.5.115   Ready     3m

在k8s-master节点builder springcloudenv镜像

* 下载jdk8$ mdkir -p /home/docker/docker-jdk8$ wget http://javadl.oracle.com/webapps/download/AutoDL?BundleId=233162_512cd62ec5174c3487ac17c61aaa89e8 -O jre-8u161-linux-x64.tar.gz$ tar zxvf jre-8u161-linux-x64.tar.gz* 删除jdk中用不到的文件,尽量减少docker镜像文件大小$ cd jre1.8.0_171$ rm -rf lib/plugin.jar lib/ext/jfxrt.jar bin/javaws lib/javaws.jar lib/desktop plugin lib/deploy* lib/*javafx* lib/*jfx* lib/amd64/libdecora_sse.so lib/amd64/libprism_*.so lib/amd64/libfxplugins.so lib/amd64/libglass.so lib/amd64/libgstreamer-lite.so lib/amd64/libjavafx*.so lib/amd64/libjfx*.so* 编写Dockerfile文件$ cd /home/docker/docker-jdk8$ vi DockerfileFROM centosMAINTAINER by wangtwRUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&     echo 'Asia/Shanghai' >/etc/timezone &&     yum -y install kde-l10n-Chinese &&     localedef -c -f UTF-8 -i zh_CN zh_CN.utf8 &&     mkdir -p /usr/javaCOPY jre1.8.0_171 /usr/java/jre1.8.0_171ENV LANG zh_CN.UTF-8ENV JAVA_HOME /usr/java/jre1.8.0_171ENV PATH $JAVA_HOME/bin:$PATH* 创建镜像springcloudenv,并上传到registry,确保docker registry已经启动$ docker build -t springcloudenv .$ docker tag springcloudenv springcloudenv:v1$ docker push springcloudenv:v1// 查看registry中的镜像$ curl http://10.12.5.110:5000/v2/_catalog{"repositories":["springcloudenv"]}

安装nfs

在k8s-master节点安装nfs server

$ yum install -y nfs-utils rpcbind$ mkdir -p /data/mysql-pv$ chmod 777 /data/mysql-pv/$ mkdir -p /data/nfs$ chmod 666 /data/nfs/$ vi /etc/exports// 增加如下行/data/nfs 10.12.0.0/16(rw,no_root_squash,no_all_squash,sync)/data/mysql-pv 10.12.0.0/16(rw,no_root_squash,no_all_squash,sync)$ systemctl enable rpcbind nfs$ systemctl start rpcbind nfs$ mkdir -p /data/mysql$ mount -t nfs 10.12.5.110:/data/nfs /data/mysql/ -o proto=tcp -o nolock

在k8s-node节点安装nfs

$ yum install -y nfs-utils$ mkdir -p /data/mysql$ mount -t nfs 10.12.5.110:/data/nfs /data/mysql/ -o proto=tcp -o nolock

安装mysql docker

以持久化方式运行myql

在k8s-master节点创建相关文件

$ mkdir -p /home/k8s/yml/services/mysql$ cd /home/k8s/yml/services/mysql$ vi mysql-pv.yamlapiVersion: v1kind: PersistentVolumemetadata:    name: mysql-pvspec:    accessModes:    - ReadWriteOnce    capacity:    storage: 1Gi    persistentVolumeReclaimPolicy: Retain#storageClassName: nfs    nfs:    path: /data/mysql-pv    server: 10.12.5.110$ vi mysql-pvc.yamlapiVersion: v1kind: PersistentVolumeClaimmetadata:    name: mysql-pvcspec:  accessModes:    - ReadWriteOnce  resources:    requests:      storage: 1Gi  #storageClassName: nfs$ vi mysql.yamlapiVersion: v1kind: Servicemetadata:  name: mysqlspec:  ports:  - port: 3306  selector:    app: mysql---apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: mysqlspec:  replicas: 1  selector:    matchLabels:      app: mysql  template:    metadata:      labels:        app: mysql    spec:      containers:      - name: mysql        image: mysql:5.6        env:        - name: MYSQL_ROOT_PASSWORD          value: password        ports:        - containerPort: 3306          name: mysql        volumeMounts:         - name: mysql-persistent-storage          mountPath: /var/lib/mysql      volumes:      - name: mysql-persistent-storage        persistentVolumeClaim:          claimName: mysql-pvc

创建mysql service和pod

$ cd /home/k8s/yml/services/mysql$ kubectl create -f mysq-pv.yaml$ kubectl get pvNAME       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM               REASON    AGEmysql-pv   1Gi        RWO           Retain          Bound     default/mysql-pvc             2d$ kubectl create -f mysq-pvc.yaml$ kubectl get pvcNAME        STATUS    VOLUME     CAPACITY   ACCESSMODES   AGEmysql-pvc   Bound     mysql-pv   1Gi        RWO           2d$ kubectl create -f mysq.yaml$ kubectl get serviceNAME         CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGEmysql        10.254.151.113   <none>        3306/TCP   2d$ kubectl get podNAME                           READY     STATUS             RESTARTS   AGEmysql-3827607452-hd5ct         1/1       Running            80         19h

k8s-master节点安装和配置jenkins

$ mkdir /home/jenkins$ cd$ vi .bash_profileexport JENKINS_HOME=/home/jenkins$ cd /home/jenkins$ wget http://mirrors.jenkins.io/war-stable/latest/jenkins.war$ java -jar jenkins.war --httpPort=9090&浏览器打开地址:http://10.12.5.110:9090把文件/home/jenkins/secrets/initialAdminPassword的内容复制到安装页面解决插件安装离线问题打开地址:http://10.12.5.110:9090/pluginManager/advanced把https://updates.jenkins.io/update-center.json修改为http://updates.jenkins.io/update-center.json安装插件:Publish Over SSH

创建jenkins任务,源代码在github上

* 任务名称:springboottest* 任务类型:自由风格* 创建github访问CredentialCredential类型:username with password* 源码管理 选择gitRepository URL 值为https://github.com/wangtiewu/springboottest.gitCredentials 值为上一步创建的 Credential* 创建构建脚本mvn clean packagetag=$( date +'%Y%m%d%H%M' )app=springcloudtestecho $tag > version_$app.txtdocker_name=$appdocker build -t $docker_name:$tag .docker push $docker_name:$tag* 创建ssh server系统管理-〉系统设置菜单-〉publish over ssh* 增加构建后操作,Send build artifacts over SSHSSH Server:name 值为k8s masterTransfer Set:Source files 值为version_springcloudtest.txtTransfer Set:Remote directory   值为springcloudtestTransfer Set:Exec command 值为cd /home/k8s/yml/services/springcloudtestapp=springcloudtestdeploy_file=$app.yamlversion_file=version_$app.txtpatch_file=$app.patchnew_version=$( cat $version_file )old_version=$( cat $deploy_file | grep image | awk -F ":" '{print $3}' )sed -i "s/$old_version/$new_version/" $deploy_filedeployment_num=$( /usr/bin/kubectl get deployment | grep springcloudtest | wc -l )if [ $deployment_num -eq 1 ];then    p_old_version=$( cat $patch_file | jq '.spec.template.spec.containers[0].image' )    p_new_version="$app:$new_version"    sed -i "s/$p_old_version/$p_new_version/" $patch_file    patch=$( cat $patch_file )    /usr/bin/kubectl patch deployment $app -p $patchelse    /usr/bin/kubectl create -f $deploy_filefi

创建springclouttest service和pod

$ mkdir -p /home/k8s/yml/services/springcloudtest$ cd /home/k8s/yml/services/springcloudtest$ vi springcloudtest-svr.yamlapiVersion: v1kind: Servicemetadata:  name: springcloudtestspec:  ports:  - name: springcloudtest-svr    port: 9091    nodePort: 30000    targetPort: 9091  selector:    app: springcloudtest  type: NodePort$ vi springcloudtest.yamlapiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: springcloudtestspec:  replicas: 1  template:    metadata:      labels:        app: springcloudtest    spec:      containers:      - name: springclouttest        image: springcloudtest:201805272108        ports:         - containerPort: 9091          protocol: TCP$ vi springcloudtest.patch{"spec":{"template":{"spec":{"containers":[{"name":"springclouttest","image":"springcloudtest:201805052039"}]}}}}

构建jenkins任务:springclouttest

构建完成后,kubectl get pod 查看springcloudtest-1053936621-w19sh   1/1       Running   1          1d

创建springclouttest service

$ cd /home/k8s/yml/services/springcloudtest$ kubectl create -f springcloudtest-svr.yaml查看服务状态$ kubectl get servicespringcloudtest   10.254.126.13   <nodes>       9091:30000/TCP   45m在k8s-node查看容器情况$ docker psCONTAINER ID        IMAGE                                                        COMMAND                  CREATED             STATUS              PORTS               NAMES269366fa1b49        mysql:5.6                                                    "docker-entrypoint..."   About an hour ago   Up About an hour                        k8s_mysql.affc4af4_mysql-3827607452-lp1cp_default_15ace557-60ed-11e8-aa6f-0800276c68b7_404d6f6a2ff21fb61984        springcloudtest:201805272108                                 "/bin/sh -c 'java ..."   About an hour ago   Up About an hour                        k8s_springclouttest.c0ee0b5d_springcloudtest-1053936621-w19sh_default_a320f883-61b2-11e8-aa6f-0800276c68b7_a99e1b3788e9ba47faf1        registry.access.redhat.com/rhel7/pod-infrastructure:latest   "/usr/bin/pod"           About an hour ago   Up About an hour                        k8s_POD.389b0ddb_mysql-3827607452-lp1cp_default_15ace557-60ed-11e8-aa6f-0800276c68b7_7387fdc6130a60acd94d        registry.access.redhat.com/rhel7/pod-infrastructure:latest   "/usr/bin/pod"           About an hour ago   Up About an hour                        k8s_POD.2aaa0bac_springcloudtest-1053936621-w19sh_default_a320f883-61b2-11e8-aa6f-0800276c68b7_fa8aba88解决 NodePort方式下,Node所在节点可以访问服务,节点外客户端请求服务都会失败问题$ iptables -P FORWARD ACCEPT

创建数据库和表

$ docker exec -it 269366fa1b49 /bin/bash$ mysql -uroot -p输入密码mysql>create database test1 default charset utf8 COLLATE utf8_general_ci;mysql>use test1mysql>create table HELLO(ID bigint, NAME varchar(128), primary key (ID));

测试服务

浏览器打开 http://10.12.5.115:30000/浏览器显示 Hello World!$ curl --data '{"id":1,"name":"Hello World"}' -H "Content-type: Application/json" http://10.12.5.115:30000/create/hello

使用 Jenkins 执行持续集成的几个实用经验分享

众所周知,持续构建与发布是我们日常工作中要面对的的一个重要环节,目前很多公司都采用 Jenkins 来搭建符合需求的 CI/CD 流程,作为一个持续集成的开源工具,它以安装启动方便,配置简单,上手容易的特点,深受广大用户的欢迎,通过笔者这几年在公司使用 Jenkins 集群,来完成公司日常各项目组持续集成和发布流程的经验,给大家分享几个实用的经验。

1、 修改 JVM 的内存配置

Jenkins 启动方式有两种方式,一种是以 Jdk Jar 方式运行,一种是将 War 包放在 Tomcat 容器下运行。不管何种方式运行,都会存在一个问题就是,默认 JVM 内存分配太少,导致启动或者运行一段时间后内存溢出报错 java.lang.OutOfMemoryError: PermGen space。所以,需要在启动前修改 JVM 内存配置。以 Tomcat 容器方式启动 Jenkins 为例配置如下:

# 进入到 Jenkins 运行所在 Tomcat conf 目录
$ vim catalina.sh
# 在 #JAVA_OPTS="$JAVA_OPTS -Dorg.apache.catalina.security.SecurityListener.UMASK=`umask`" 行下增加修改配置 JVM 内存配置大小,例如下边配置:
JAVA_OPTS="$JAVA_OPTS -server -Xms512m -Xmx1024m -XX:PermSize=256M -XX:MaxPermSize=512m"
12345

注意:这里的几个 JVM 参数含义如下:

  • -Xms: 使用的最小堆内存大小
  • -Xmx: 使用的最大堆内存大小
  • -XX:PermSize: 内存的永久保存区域大小
  • -XX:MaxPermSize: 最大内存的永久保存区域大小

这几个参数也不是配置越大越好,具体要根据所在机器实际内存和使用大小配置。

2、修改 Jenkins 主目录

Linux 下 Jenkins 默认安装目录为 /var/lib/jenkins/,这个目录磁盘空间有限,长时间使用会导致磁盘空间不够,建议修改为其他大磁盘空间目录。这里修改安装目录有两种方式,一种是配置为系统环境变量中,一种是配置到 Tomcat 容器环境变量中。

配置 JENKINS_HOME 到系统环境变量里面

# 注意:如果一台机器只安装一个 Jenkins 时,可以配置如下。
$vim /etc/profile
...
export JENKINS_HOME=/data0/jenkins
export PATH=$PATH:$JENKINS_HOME
# 使配置生效
$ source /etc/profile1234567

配置 JENKINS_HOME 到该 Jenkins 启动的 Tomcat 容器环境变量中

# 注意:如果一台机器上边安装多个 Jenkins 时,不能配置 JENKINS_HOME 到系统环境变量里面,
# 需要配置 JENKINS_HOME 到该 Jenkins 启动的 Tomcat 容器配置里面,这样可以区分不同的 Jenkins 目录。
$ vim /data0/scm/apache-tomcat-7.0.85/conf/context.xml
<Context>
    ...
    # 增加以下配置,优先获取该配置路径。
    <Environment name="JENKINS_HOME" value="/data0/jenkins" type="java.lang.String"/>
</Context>12345678

这里要说明一下,如果一台机器上只安装了一个 Jenkins 服务时,可以配置 JENKINS_HOME 到系统环境变量里面,如果安装了多个 Jenkins 服务时,不能这么配置,因为 Jenkins 会读取系统环境变量中 JENKINS_HOME 作为主目录安装,那样会存在配置覆盖的问题。此时应该采用第二种方式,各自配置 JENKINS_HOME 到自己启动的 Tomcat 容器环境变量中,Jenkins 会优先读取该容器环境变量作为各自的主目录安装。

附 Jenkins 寻找 JENKINS_HOME 环境变量的顺序为:首先读取容器环境变量,如果没有,则读取系统环境变量,如果还没有,则使用默认路径安装。

3、配置优化减少磁盘空间占用

Jenkins 运行 Job 构建比较多时,如果没有配置好清理策略的话,会导致占用磁盘空间比较大,最终由于磁盘空间不够导致构建失败的问题。

3.1 丢弃旧的构建配置

我们可以在 Job 中配置丢弃旧的构建,通过设置 “保持构建的天数” 和 “保持构建的最大个数” 两个参数,控制该 Job 最大保存构建数量。

未分类

未分类

如上图所示,我配置了最大保持 3 天之内的构建,如果超过 3 天的构建,则会在Job 执行前被清理掉。同时配置了最大保持构建数量为 10 个,意思就是如果 3 天内构建次数如果超过 10 次,则最多保留最近执行的 10 个构建。这样配置的好处,除了能够自动清理一些 Build 之外,还能够为我们代码执行远程停止 Job Build 时,缩短停止时间,下边会讲到。

3.2 修改工作空间和构建记录根目录

Jenkins 工作主要分为安装主目录,工作空间目录以及构建记录目录,默认配置路径如下图所示:

未分类

如果我们修改了 Jenkins 安装主目录之后,因为工作空间目录是在安装主目录下的 workspace 目录,构建目录在安装主目录下的 builds 目录,这样运行 Job 执行构建比较多时,还会存在磁盘空间不够的问题,那么此时可以在 “系统管理” —> “系统配置” —> “高级” 分别修改工作空间根目录和构建记录根目录,指向其他磁盘空间即可。

4、设置全局属性

适当设置全局属性,可以避免在 Job 中重复写一些固定值,例如输出日志地址、接口请求地址等等,而且当固定值需要修改时,只需要修改一次即可,不用去每个 Job 里面修改,方便维护。我们可以去 “系统管理” —> “系统配置” —> “全局属性” 下增加 Environment variables 键值对,例如如下图:

未分类

那么,在 Job 构建时执行 “ Execute Shell” 使用时,可以直接应用即可,例如如下代码:

未分类

5、JDK/Maven/Gradle 等软件多版本安装

对于一些常用的软件,比如 Jdk、Maven、Gradle等,可能每个项目对软件依赖版本不一样,有的项目依赖 Jdk7,有的依赖 Jdk8,所以为了更好的适配各个项目,可以指定安装多个版本软件,然后 Job 创建时选择其中一个版本使用。这里以 Jdk 为例,去 “系统管理” —> “Global Tool Configuration” —> “JDK” 分别安装 jdk6、Jdk7、Jdk8。

未分类

然后,在创建 Job 时,选择项目需要的一个版本即可。

未分类

6、设置构建超时时间

有些 Job 在执行构建时,由于某些原因导致构建挂起,耗时比较长,而这些长时间挂起的 Job 会导致 Jenkins 内存占用比较大,性能下降,严重的会直接导致 Jenkins 挂掉。所以,我们需要设置构建超时时间来预防这种事情发生,一旦超过一定的时间,要让 Job 自动停止掉。例如,这里我设置构建超过 30 分钟则将本次 Build 置为失败。

未分类

7、配置视图分类管理 Job

Jenkins 默认视图为 ALL 显示所有 Job 列表,如果 Job 比较多的话,找某个 Job 会不太方便,虽然有 Search 搜索功能,毕竟还是不太方便。这时候,我们可以通过新建视图方式,对 Job 进行分门别类,这样管理和查找起来就方便多啦!例如这里我新建 “List View” 类型视图 “wy”,然后选择该视图所关联的 Job 就可以了。

未分类

这样通过视图切换,查找相关 Job 就方便多了。

未分类

8、配置多节点管理

一般我们会使用 Jenkins Slave 集群管理来完成日常持续集成操作,使用 Jenkins Slave 一主多从方式,可以将 Job 调度到对应的 Slave 机器上执行,能够大大提高系统并发执行效率。我们可以从 “系统管理” —> “管理节点” —> “新建节点”,设置节点类型为 “Permanent Agent” 名称 “wy_slave0” 的一个从节点,当然有多个节点时,可以创建多个。创建完毕之后,此时插件还属于不可用状态,因为还没有执行关联,具体关联方式可以参照 Jenkins 上节点关联说明(如下图,这里忽略操作),关联完毕之后,我们就可以在新建 Job 中配置指定那个 Slave 节点运行了。

未分类

未分类

未分类

这里,有两种方式指定 Job 在那个 Slave 节点运行,一种是对于自由风格类型的 Job,我们可以通过在 “Restrict where this project can be run” 选项下指定 “Label Expression” 标签指定节点标签即可。

未分类

另一种是对于多配置项目类型的 Job,我们可以通过在 “Configuration Matrix” 先配置 “Slave” 选择 Node/Label 勾选指定一个或多个 Slave 执行。

未分类

9、一些实用插件

Jenkins 的基础配置就能够满足我们日常的基本工作,但是为了提高构建效率和方便维护,Jenkins 上提供了很多实用的插件,使用这些插件,我们可以更加轻松、更加简便、更加高效的执行持续集成和发布流程。下边,我就简单介绍几个我使用的插件。

9.1 Managed Script 插件管理脚本文件

该插件是为了在管理文件时创建 Script 脚本文件,然后在 Job 中配置直接使用,方便脚本的统一管理和维护。首先我们需要去 “系统管理” —> “管理插件” —> “可选插件” 中选择 “Managed script” 插件,安装重启即可。

未分类

安装完毕后,可以从 “系统管理” —> “Managed files” —> “Add a new Config” 选择 “Managed script file” 类型,创建一个新的 shell 脚本文件,然后输入我们要执行的脚本代码。这里我创建了两个脚本,分别为 before-build-step-shell 和 after-build-step-shell,意思很明确了,前者在构建前执行的一些操作,后者在构建后执行的一些操作。

未分类

未分类

注意: 这里的脚本可以使用一些 Jenkins 系统的环境变量参数、参数化构建时传递的参数以及系统命令哦。

创建完毕后,我们在 Job 中构建处选择 “Execute managed script” 就可以使用这些脚本了。

未分类

9.2 PostBuildScript 插件根据 Build 状态执行脚本

推荐安装 PostBuildScript 插件,该插件可以在构建后操作中,根据构建结果状态,执行对应的脚本操作,很实用的一个插件。同上安装该插件,重启 Jenkins 完毕插件生效后,Job 中构建后操作处选择 “Execute Scripts” ,然后在 “Add build step” 中选择 “Execute shell” 等选项(当然也可以配合上一个插件,那么这里就选择 “Execute managed script”),下边选择一个 build 状态条件值,如果选择 SUCCESS 状态,那么该脚本只有在 Build 成功时才会执行,其他状态依次类推,状态可以多选哦,多选代表多种状态都能下触发。

未分类

9.3 Jenkins2.0 Pipeline 插件执行持续集成发布流程

Jenkins 2.0 的精髓是 Pipeline as Code,是帮助 Jenkins 实现 CI 到 CD 转变的重要角色。Pipeline是一套运行于 Jenkins 上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂发布流程。Pipeline 中任何发布流程都可以表述为一段 Groovy 脚本,并且 Jenkins 支持从代码库直接读取脚本。使用 Pipeline 执行持续集成发布流程好处是显而易见的,它可以把以前需要多个节点上多个 Job 使用一段脚本来替代,而且脚本语言功能强大,可以很好的完成一些复杂的流程操作,推荐大家尝试使用。这里就不多说了,详细可以参考之前文章 初试Jenkins2.0 Pipeline持续集成。

9.4 Kubernetes Plugin 插件动态创建 Jenkins Slave

我们知道传统的 Jenkins Slave 一主多从方式会存在一些痛点,比如 Master 单点故障,Slave 配置环境差异,资源分配不均衡等导致可靠性和可维护性比较差,而使用 Kubernetes Plugin 插件可以动态的创建和删除 Jenkins Slave 节点,使用它可以很好的保证服务高可用,动态伸缩合理使用资源,以及良好的扩展性。使用该插件后,它的工作流程大致为:当 Jenkins Master 接受到 Build 请求时,会根据配置的 Label 动态创建一个运行在 Docker Container 中的 Jenkins Slave 并注册到 Master 上,当运行完 Job 后,这个 Slave 会被注销并且 Docker Container 也会自动删除,恢复到最初状态。这里也不多说了,详细可以参考之前文章 初试 Jenkins 使用 Kubernetes Plugin 完成持续构建与发布。

10、JAVA 代码触发 Jenkins Job 创建、删除、停止等操作。

Jenkins Job 创建、删除、构建等操作,除了在页面手动操作外,我们还可以通过 Jenkins API 接口执行对应操作,详细接口可参考 Jenkins
REST API 文档地址:http:///api。这里我要演示的是使用 Jenkins-client.jar 包,使用 JAVA 代码操作如何创建、删除、停止、触发构建等,使用代码触发 jenkins 相关操作,好处就是自己可控,这样可以配合自己的业务需要,随时启动或者新建 Job 啦。

public class JenkinsUtils {

    private static String jenkins_url = "http://127.0.0.1/jenkins/";
    private static String jenkins_user = "admin";
    private static String jenkins_token = "1b356d175432ed0d34c440d68d00fe49";

    /**
     * 通过模板创建 Job
     * @param jobName
     * @param jobTemplate
     * @return
     */
    public static boolean createJobFromTemplate(String jobName, String jobTemplate) {
        try {
            URI uri = new URI(jenkins_url);            
            JenkinsServer jenkins = new JenkinsServer(uri, jenkins_user, jenkins_token);
            String template = jenkins.getJobXml(jobTemplate);
            Document doc = DocumentHelper.parseText(template);
            String newConfigXml = doc.asXML();
            jenkins.createJob(jobName, newConfigXml, false);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除/禁用 Job
     * @param jobName
     * @return
     */
    public static boolean deleteJob(String jobName){
        try {
            URI uri = new URI(jenkins_url);            
            JenkinsServer jenkins = new JenkinsServer(uri, jenkins_user, jenkins_token);
            jenkins.deleteJob(jobName, false);
            jenkins.disableJob(jobName, false);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 启动 Job 构建
     * @param jobName
     * @param params
     * @return
     */
    public static boolean startJob(String jobName, String params){
        try {
            String buildUrl = jenkins_url + jobName + "/buildWithParameters?" + params;
            HttpUtils.HttpGet(buildUrl);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 停止正在构建中的 Job,先清除等待队列中的 build,在停止运行中的 build
     * @param jobName
     * @return
     */
    public static boolean stopJob(String jobName){
        try {
            URI uri = new URI(jenkins_url);
            JenkinsServer jenkins = new JenkinsServer(uri, jenkins_user, jenkins_token);
            // 先 kill 掉 queue 里面的 build
            QueueItem qi = jenkins.getJob(jobName).getQueueItem();
            if(qi != null){
                HttpUtils.HttpPost(jenkinsUrl + "queue/cancelItem?id=" + qi.getId());
            }
            // 在 kill 掉正在运行中的 build
            List<Build> bulidsList = jenkins.getJob(jobName).getAllBuilds();
            for(Build b:bulidsList){
                if(b.details().isBuilding()){
                    try{
                        b.Stop();
                    }catch(Exception ee){
                        ee.printStackTrace();
                        return false;
                    }
                }
            }
        } catch(Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

这里有一个地方要注意,在停止构建中的 Job 时,这里是遍历所有 Build,然后在 Kill 掉运行中的 Build,如果 Build 历史比较多的时候,会耗时比较久,这将会导致立马重新执行该 Job Build 时, Build 会被异常 Abort 掉。。。 也尝试过获取最后一次 Build 执行 Stop 操作,好像也不太好使。所以这里大家可以通过上边 3.1、丢弃旧的构建配置 中的操作,减少构建历史记录,这样就可以很快执行完毕,就不会出现上述问题了。

jenkins使用开始踩坑(1)

一.前戏

话说上一篇文章安装完 JDK 和 jenkins 之后 , 在使用的时候遇到了很多问题,现在一一和大家分享一下

二.问题1

1.目的:因为第一次使用jenkins,不太会用,给自己的一个小目标就是执行一个python的脚本

2.过程

前期也没有配置jenkins 直接打开就开始新建任务(过程省略,啥都没配置,只是执行了这样一个命令 python *.py),刚开始学或者已经用过的同学应该能看懂我说的啥。

好,继续,接下来就是【立即构建】,问题来了,执行这么简单的一个脚本既然给我报错。没错了,报错信息意思找不到selenium包

ft1.1: ImportError: Failed to import test module: LoginTc
Traceback (most recent call last):
  File "C:Python36libunittestloader.py", line 428, in _find_test_path
    module = self._get_module_from_name(name)
  File "C:Python36libunittestloader.py", line 369, in _get_module_from_name
    __import__(name)
  File "D:Petrochina_Retail_Test_Projectretailtest_caseLoginTc.py", line 11, in <module>
    from retail.test_case.models.myunit import MyunitTest
  File "D:Petrochina_Retail_Test_Projectretailtest_casemodelsmyunit.py", line 7, in <module>
    from retail.test_case.models.driver import WDriver
  File "D:Petrochina_Retail_Test_Projectretailtest_casemodelsdriver.py", line 7, in <module>
 from selenium import webdriver
ModuleNotFoundError: No module named 'selenium'

  
3.解决:再IDE中运行这个脚本没有问题,在cmd下运行也没有问题,很伤, 查看各种环境变量 发现没有问题,于是卸载jenkins重新安装试试,发现还是不行,经过几个小时的折腾,发现确实在python安装目录的C:Python36Libsite-packages 下面没有selenium包。之前可能被cmd和IDE给忽悠了,我去找了一下IDE中在哪里引入seleniium包,关键来了,看图:

未分类

你现在看到的目录是我改过后的,原来的目录是:c:usersv-xugappdataroamingpythonpython36site-packages 很奇怪为什么会在这,原来是安装的时候我自己设置到了这个目录,导致jenkins在找selenium包找不到,于是把selenium 复制到python的目录里面,再次构建

三.问题2

1.过程:发现新问题了,脚本是执行了而且jenkins控制台也没有报错,但是我的脚本里面有执行打开浏览器的操作,可以实际我并没有看见浏览器被打开,就执行结束了

2.解决:伤心呀!百度。找到这样一篇文章 Jenkins调度Selenium脚本不打开浏览器解决办法

于是按照上面步骤开始操作,问题来了 我发现我运行不了java命令,提示信息是说 我的JDK是11 而jenkins 需要运行JDK8 于是卸载JDK 重新安装8的版本。一步一步,都做完之后惊喜的发现真的解决了,很开心! 这篇文章写的很乱,因为好多东西我没有记录,没法看到一些错误信息,但是大体的问题就是这样,希望遇到一样的情况的同学能帮到你一点点小忙。

Jenkins调度Selenium脚本不打开浏览器解决办法

The web browser doesn’t show while run selenium test via jenkins
原因:因为jenkins是用windows installer 安装成 windows的服务了,那么jenkins是一个后台服务,所以跑selenium cases 的时候不显示浏览器
  
解决办法:

Step 1. Control Panel(控制面板) -> Administrative Tools(管理工具) -> Services(服务)
找到Jenkins,右键菜单,执行stop; 右键菜单 “属性” start type 选择“Disabled” 点击 应用,点击确定。(这一步目的:让jenkins不在以windows 服务的方式在后台运行)
  
Step2. 通过命令行启动Jenkins
java -jar “D:Program Files (x86)Jenkinsjenkins.war”
当然,大家也可以做个bat文件,以后每次启动jenkins,双击运行这个bat文件就OK了,bat文件内容如下:
  

echo "Jenkins CI automation testting"
java -jar "D:Program Files (x86)Jenkinsjenkins.war"
pause

Step3. 重新创建Jenkins的Job,重新配置,在执行测试
问题解决了。
  
注意问题:

(1)通过这种方式启动jenkins,之前以windows service 启动jenkins的数据都丢失了,需要重新创建job。
(2)之前以windows service 启动jenkins中安装的插件也没有了,需要重新安装所需插件。

Jenkins和Git稀疏结账

您可以使用Git的稀疏结账功能。 请注意,Git仍将整个存储库克隆到本地磁盘。 然而,这并不是太糟糕,因为它是压缩的。

1.在Jenkins中创建一个新作业,在源代码管理部分设置Git存储库。
2.建立项目。 这会将整个存储库克隆到本地磁盘。
3.打开项目的工作区文件夹,删除除.git文件夹之外的所有内容。
4.打开项目工作区文件夹的Git shell。 启用稀疏检查:

git config core.sparsecheckout true

5.更新工作树:

git read-tree -mu HEAD

6.在.git / info文件夹中创建sparse-checkout文件。 添加要签出到该文件的子文件夹的路径,如下所示(注意尾部斜杠):

folder/to/include/

7.再次构建项目。 这次只有一个子文件夹应出现在工作区文件夹中。

curl 调用jenkins的api

jenkins提供了rest api,通过调用接口,可以执行一些job的操作,如构建job ,新建job,启用禁用等操作骑车

其次curl是什么?

cURL是一个利用URL语法在命令行下工作的文件传输工具,1997年首次发行。它支持文件上传和下载,所以是综合传输工具,但按传统,习惯称cURL为下载工具。 cURL还包含了用于程序开发的libcurl。

cURL支持的通信协议有FTP、FTPS、HTTP、HTTPS、TFTP、SFTP、Gopher、SCP、Telnet、DICT、FILE、LDAP、LDAPS、IMAP、POP3、SMTP和RTSP。

curl还支持SSL认证、HTTP POST、HTTP PUT、FTP上传, HTTP form based upload、proxies、HTTP/2、cookies、用户名+密码认证(Basic, Plain, Digest, CRAM-MD5, NTLM, Negotiate and Kerberos)、file transfer resume、proxy tunneling。

可以看出curl支持http协议,所以调用jenkins的rest api是没有问题的。

接下来示例演示操作cur 对jenkins的job的启用和禁用及构建,示例中用到的jenkins url为:http://localhost:8080/jenkins/,job名称为:login,jenkins管理员账号:root, 密码:root

启用job login

curl --user root:root --data enable http://localhost:8080/jenkins/job/login/enable

禁用job login

curl --user root:root --data disable http://localhost:8080/jenkins/job/login/disable

构建job login

curl --user root:root --data build http://localhost:8080/jenkins/job/login/build

命令中–user 表示jenkins账号密码认证

执行结果如下(如果在windows下,是没有办法直接在cmd下执行curl命令,可以下git的客户端,在git bash中执行命令)

Administrator@OXPLAJ8T7PEPCDB MINGW64 ~
$ curl --user root:root  --data disable  http://localhost:8080/jenkins/job/login/disable
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100     7    0     0  100     7      0      7  0:00:01 --:--:--  0:00:01    56

Administrator@OXPLAJ8T7PEPCDB MINGW64 ~
$ curl --user root:root  --data enable  http://localhost:8080/jenkins/job/login/enable
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100     6    0     0  100     6      0      6  0:00:01 --:--:--  0:00:01    42

Administrator@OXPLAJ8T7PEPCDB MINGW64 ~
$ curl --user root:root  --data build http://localhost:8080/jenkins/job/login/build
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100     5    0     0  100     5      0      5  0:00:01 --:--:--  0:00:01    35

Administrator@OXPLAJ8T7PEPCDB MINGW64 ~
$

执行后在jenkins页面查看,可以看到禁用、停用、构建job

Jenkins pipeline 并行执行任务流

笔者在 https://www.cnblogs.com/sparkdev/p/7617765.html 一文中介绍了如何在声明式 pipeline 中执行并行的任务。前一段时间,Jenkins 发布了 1.3 版的声明式 pipeline(declarative pipeline),这个版本继续增强了并行执行任务的能力:并行执行的任务可以是个任务流。官方称这一功能为 “sequential stages”,本文将解释 “sequential stages”,并通过 demo 演示其用法。

之前的任务并行方式

就是笔者在 https://www.cnblogs.com/sparkdev/p/7617765.html 一文中介绍的方式,我们在一个 stage 中设置多个子 stage 并行执行:

stages {
    stage('Stage1') {
        ...
    }
    stage('并行执行的 Stage') {
        parallel {
            stage('Stage2.1') {
                agent { label "test1" }
                steps {
                    echo "在 agent test1 上执行的并行任务 1."
                }
            }
            stage('Stage2.2') {
                agent { label "test2" }
                steps {
                    echo "在 agent test2 上执行的并行任务 2."
                }
            }
        }
    }
    stage('Stage3') {
        ...
    }
}

上面代码中任务的执行过程如下图所示:

未分类

任务 2.1 和任务 2.2 并行执行。

并行执行任务流

过去并行执行的任务都是单个的,但实际情况中我们还需要任务流级别的并行能力,如下图所示:

未分类

上图中显示有两条任务流在并行的执行,我们可以通过下面的代码来实现:

pipeline {
    agent none

    stages {
        stage('Stage1') {
            agent { label "master" }
            steps {
                timestamps {
                    echo '这是第一个被执行的 stage.'
                    sleep 5
                }
            }
        }
        stage("build, deploy and test on Windows and Linux") {
            parallel {
                stage("windows") {
                    agent {
                        label "master"
                    }
                    stages {
                        stage("build") {
                            steps {
                                timestamps {
                                    echo "build on windows."
                                }
                            }
                        }
                        stage("deploy") {
                            steps {
                                timestamps {
                                    echo "deploy on windows."
                                }
                            }
                        }
                        stage("test") {
                            steps {
                                timestamps {
                                    echo "test on windows."
                                    sleep 5
                                }
                            }
                        }
                    }
                }

                stage("linux") {
                    agent {
                        label "worker1"
                    }
                    stages {
                        stage("build") {
                            steps {
                                timestamps {
                                    echo "build on linux."
                                }
                            }
                        }
                        stage("deploy") {
                             steps {
                                 timestamps {
                                     echo "deploy on linux."
                                 }
                            }
                        }
                        stage("test") {
                            steps {
                                timestamps {
                                    echo "test on linux."
                                    sleep 5
                                }
                            }
                        }
                    }
                }
            }
        }
        stage('Stage3') {
            agent { label "worker1" }
            steps {
                timestamps {
                    echo '这是最后一个被执行的 stage.'
                }
            }
        }
    }
}

为了显示任务的执行时间,笔者使用了 timestamper 插件。下图显示了笔者精简后的运行日志:

未分类

红框中的内容说明我们的两个任务流是完全并行执行的。这就是 1.3 版的声明式 pipeline 中增加的 “sequential stages” 功能。

总结

如今 jenkins 对声明式 pipeline 中并行任务的执行支持的非常给力(虽然经历了一个稍显漫长的过程)。笔者在 2017 年初调研时发现声明式 pipeline 无法支持并行的任务,后来开始支持比较初级的并行任务,笔者在 https://www.cnblogs.com/sparkdev/p/7617765.html 一文中进行了介绍。到今年(2018) 7 月份声明式 pipeline 发布了版本 1.3,这个版本中开始支持本文介绍的任务流级别的并行。至此笔者认为 jenkins 声明式 pipeline 中任务的并行执行功能已经比较完善了。

jenkins使用git+gradle+webhook实现自动部署

1、下载jenkins

网址:http://mirrors.jenkins-ci.org/,可以按照操作系统来选择相应的安装方式。RC是候选发布版,我们选择LTS长期稳定版即可。

我采用的war包的部署方式,选择war-stable,找到最新的jenkins为2.121.2,进入目录下载,或者可以直接wget链接。

wget mirrors.jenkins-ci.org/war-stable/2.121.2/jenkins.war

war包的安装方式非常简单,直接扔进tomcat目录下,只要tomcat处于运行状态,就会自动解压。

2、配置jenkins

访问jenkins目录 http://你的域名或者ip:8080/jenkins,jenkins就会启动。

未分类

稍等片刻,等jenkins启动完毕后,为了验证身份,jenkins会要求你去本地目录下读取初始密码并填入。

未分类

然后就是jenkins的配置页面的首页,会让你选择安装插件的方式,这里我选则右边自定义插件的安装方式。

未分类

选择上方的无,然后选择git,gradle,由于这里未显示完整的插件列表,webhook需要在插件管理器中配置

未分类

未分类

点击安装,等待安装成功,创建管理员账户后,设置jenkins的访问路径,开始使用jenkins

未分类

进入插件管理器,点击左侧系统管理,选择管理插件

未分类

未分类

点击可选插件,由于我使用的仓库是coding,搜索coding webhook,直接安装,其他仓库同理,若没有集成好的插件,可以搜索webhook,使用Generic Webhook Trigger(webhook通用触发器)

未分类

3、新建任务

单击开始创建一个新任务,输入任务名称test,选择构建一个自由风格的软件项目

未分类

选择丢弃旧的构建,点击右下方高级,四个参数按照自己机器的磁盘空间来填写,因为jenkins每次构建结束后(无论成功还是失败)都会记录构建,长期会占用大量的空间

$JENKINS_HOME/jobs/[JOB_NAME]/builds 目录存储了该Jenkins job的全部构建记录(目录名为构建序号)

$JENKINS_HOME/jobs/[JOB_NAME]/builds/BUILD_NUMBER/artifacts 目录存储了该次构建的artifact

未分类

之后设置源码管理,设置仓库地址,选择用户凭据

未分类

设置webhook触发器,图中的都必须选中,webhook令牌可以不填

未分类

接着去仓库中配置webhook

未分类

若触发成功就会在$JENKINS_HOME的workspace目录下生成项目文件,包括项目文件夹test,和临时文件夹test@tmp

未分类

设置gradle自动打包,使用gradle wrapper,要求项目里必须有gradle wrapper的相关文件,否则要设置wrapper的路径。

未分类

若使用gradle,可以去系统管理的全局工具配置安装指定版本的gradle

未分类

若构建成功,就会在项目的build目录下生成war包,在我实际上线的项目中,由于用到了tomcat,还需要再增加构建步骤,将生成的war包拷贝到tomcat的webapps目录下

未分类

至此,项目自动部署配置完毕,只要我在本地修改完代码push到仓库中,服务器便会自动pull代码然后打成war包部署到tomcat下。

如何在Docker里正确集成Jenkins和Phabricator

|用Docker安装Jenkins非常简单,但要把一个运行在Docker里的Jenkins和Phabricator相集成,事情就变得不那么容易。

未分类

单独安装Jenkins并不复杂,用Docker安装Jenkins更加简单,甚至将Jenkins与Phabricator集成也不难,但要把一个运行在Docker里的Jenkins和Phabricator相集成,事情就变得不那么容易。

我把所有走过的坑全部隐藏,直接告诉你最正确的步骤。

这一步似乎很简单,但不要按照官方教程上所说的来,而按照以下命令执行:

docker run --name jenki
ns -p 8088:8080 -p 50000:50000 -v /var/jenkins_home:/var/jenkins_home -e PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/src/arcanist/bin' jenkins/jenkins

-p 8088:8080是为了避免8080端口冲突(毕竟只要是个Java程序就想占用8080端口),-e设置里特意增加了/src/arcanist/bin是为下一步集成Phabricator里的Arcanist做准备,因为如果不在这里设置好这个环境变量的话,后面会带来很大麻烦。

因为官方提供的Docker里根本就没有Arcanist,所以我们必须进入Docker的容器,手工安装arc。

docker exec -it -u root jenkins bash

好在这个Docker并不复杂,只是一个Debian,所以我们以root用户进入,然后:

apt-get update
apt-get install php
apt-get install php-curl
apt-get install rsync
apt-get install vim
mkdir /src
cd /src
git clone git://github.com/facebook/libphutil.git
git clone git://github.com/facebook/arcanist.git

把你的ssh公钥私钥文件拷到/var/jenkins_home/.ssh里,因为后面不论是git还是rsync你都需要它们。然后再以jenkins用户身份进入Docker:

docker exec -it -u jenkins jenkins bash

然后:

arc set-config default http://your.phabricator.com/
cd /var/jenkins_home/.ssh
chmod 600 id_rsa
chmod 600 id_rsa.pub

至此,你已经把官方提供的Docker改得面目全非,才算正确地在Docker里安装好了arc和Jenkins。

接下来的步骤,你就可以参照官方教程一步一步执行,我就不再重复了。

如果你也遇到了类似的头疼问题,希望这篇文章能够对你有所启发。

使用Jenkins的任务自动跑脚本后发现,tomcat服务刚启动就被杀死

在Jenkins的使用中,遇到过的一个场景是:使用python自动执行tomcat bin目录下的startup.bat开启批处理,服务开启成功后就随着python脚本执行结束该服务就被杀死,开始以为是python脚本的问题,但是直接执行是没有问题的;之后一直查运行环境差异,发现也不是这个原因;到后来才怀疑到Jenkins任务结束时候自动关掉了所有的子进程。通过以下shell脚本片段解决了问题:

1.第一种方案: #临时改变BUILD_ID值,使得Jenkins不会找到并结束掉python脚本启动的后台进程
OLD_BUILD_ID=$BUILD_ID
echo $OLD_BUILD_ID
BUILD_ID=dontKillMe
./run.sh restart
#改回原来的BUILD_ID值
BUILD_ID=$OLD_BUILD_ID
echo $BUILD_ID

问题的根本在于是Jenkins使用processTreeKiller杀掉了所有子进程,而且这是Jenkins的默认行为。其实回头来看这个问题,就发现Jenkins的做法非常合理。当一次build异常结束,或被人终止时,必然需要结束所有这次build启动的子进程。下面的link提供了更多细节,以及解决方法。https://wiki.jenkins-ci.org/display/JENKINS/ProcessTreeKiller

2.第二种方案:

使用jenkins的批处理或者ant 启动tomcat失败。查了一下原因说是 jenkins在脚本执行结束后,就认为任务结束了,但是脚本启动的相关子程序仍然在运行。由于jenkins认为任务已经结束了,就结束了该构建相关的子进程。

解决办法:增加一个环境变量。

BUILD_ID=pleaseDontKillMe and it works like a charm。

可以添加在执行任务的节点设置中添加该变量。

解决详细步骤:

前置说明:我是通过slave节点来启动tomcat 的,所以再slave节点配置上述环境变量。

未分类

未分类

Android使用Jenkins自动化构建测试打包apk

Jenkins这东西搭建起来真是一点也不省心啊,看着别人的教程摸着石头过河,配置的东西有点多啊,稍有不慎,就构建不成功啦!即使步骤跟别人一样也会报各种乱七八糟的错误啊哈哈~~这东西只能佛系搭建~~在经历几十次的随缘构建之后,终于构建成功啦~~好感人!眼泪掉下来。。。。。。在此做下记录,造福广大群众!阿弥陀佛~~废话少说,且看如下操作:

下载Jenkins并安装

官网下载地址:https://jenkins.io/download

未分类

我们下载如图所示Window版本,下载下来是个.zip的压缩包,我们解压一下得到如下图所示文件:

未分类

双击即可开始安装,一直Next,即可安装完成。

环境变量配置

  • Java环境配置
  • Android SDK环境配置

右键此电脑-属性-高级系统设置-环境变量

在系统变量中添加 :

变量名 JAVA_HOME

变量值 E:Javajdk8 (路径根据自己实际的java安装目录选择)

变量名 ANDROID_HOME

变量值 E:AndroidSDK (路径根据自己实际的java安装目录选择)

未分类

然后在系统的Path变量中添加 :%Java_Home%bin

未分类

以上是Java环境的基本配置,作为一名Android开发攻城狮应该都知道基本操作。接下来就是重头戏啦!!!

Jenkins配置

安装完jenkins一般会自动在浏览器中打开,没有的话在浏览器输入 localhost:8080 或者 127.0.0.1:8080 页面出来后会提示选择安装插件,第一个是默认推荐的插件,第二个是自己选择要安装的插件

未分类

我们直接选默认的就好,点击第一个坐等jenkins自动安装插件

未分类

插件安装后需要创建你的账号

未分类

创建完就可以登录Jenkins控制台啦!

未分类

点击左侧 系统管理-系统设置

未分类

来到如下设置页面:

未分类

点开主目录右边的高级设置,修改一下工作空间根目录(路径任意,自己知道就行),若不改,aapt编译时会报错,因为windows版本默认的根目录在C盘底下路径过长

未分类

往下拉配置全局属性,勾选环境变量,添加如下键值对

未分类

为什么说GRADLE_USER_HOME字段要固定写呢,因为要和Gradle的配置字段一致,否则编译会报错,如果这里不进行配置Gradle会使用默认的目录,会因为默认目录路径太长导致编译报错,是不是很坑!!!

未分类

配置完点击左下角保存按钮保存配置。

回到主页面点击左侧 新建任务

未分类

输入一个任务名称,选择构建一个自由风格的软件项目,点击确定

未分类

这样在 我的视图 里就能看到创建的任务啦,点击任务,进入任务详细页面

未分类

点击配置,进行构建前的详细配置,先配置普通项General,勾选Github prooject,设置远程仓库地址,以Github为例:

未分类

然后设置 源码管理 勾选Git,添加Github项目地址,你的Github账号密码

未分类

然后设置 构建触发器 (非必需设置)设置了只是多了一种构建方式,可以勾选 触发远程构建 ,然后设置一个身份验证令牌,即可通过浏览器访问链接触发构建

未分类

如红色方框所示,有两种拼接链接的方式触发构建,你可以在浏览器中输入 localhost:8080/me/my-views/view/all/job/Test/build?token=123456 或者 /buildWithParameters?token=123456 来触发构建。

也可以勾选Poll SCM,设置每隔一段时间自动构建,我设置了每小时构建一次,如下图所示

未分类

语法参考如下:

未分类

接下来设置 构建 点击增加构建步骤,选择Invoke Gradle script

未分类

勾选Invoke Gradle,选择gradle

未分类

Tasks 填写 clean build 命令

未分类

最后设置 构建后操作 点击增加构建后操作步骤,选择Archive the artifacts

未分类

填入正则表达式路径,匹配用于存档的apk

未分类

app/build/outputs/release/*.apk的意思是匹配app/build/outputs/release路径下所有拓展名为.apk的安装包,看这路径是不是很熟悉,其实就是项目路径下apk的输出路径

未分类

存档后Jenkins-release-v1.0.0.apk就会显示在Jenkins项目最后一次成功的构建结果

未分类

到此配置基本完成,是时候构建一波了,回到任务主页,点击立即构建

未分类

左下角会出现构建进度,蓝色圆点表示成功,红色圆点表示失败,点击构建序号,下拉选择控制台输出可以查看构建日志

未分类

未分类

至此,构建就大功告成啦!

在本地的JenkinsWork工作空间目录下可以看到从Github上拉取的项目源码

未分类

构建成功打包的apk在此目录下

未分类

接下来我们可以设置让构建打包好的apk自动发布到fir,详细教程请移步至http://blog.fir.im/jenkins文章写得很详细,我就不多说了。发布到fir后,平常测试人员找你要测试包,你可以对他说:“自己去fir下载。”一脸帅气.jpg

好了,今天就分享到这,祝大家Jenkins构建之路愉快!