编译工具
代码开发完毕后,我们需要将其编译为可运行的代码。编译工具与源代码使用语言有密切关系,常用的编译工具有:
- Java 常使用Maven, Ant 或 Gradle
- C或C++ 常使用make或各种make的衍生品。
- iOS开发使用xcode
- PHP、Python等脚本语言常无需编译,一般可将代码打包到一个单一文件,故编译可以使用zip压缩工具代替。
编译实践规范
编译工具是从源代码到目标程序的最重要一环。在实际工作过程中,常因为编译环境的不同,使得目标程序出现差异。因此有必要在团队内建立统一的编译规范:
- 统一编译工具。对开发者机器、测试环境和生产环境的编译器进行统一,包括编译器版本及配置
- 统一第三方库。第三方库的升级可能带来接口变更,导致程序发生不可预期的计算错误或崩溃。因此,建议显示地指定依赖的第三方库的版本号。
- 便捷的编译操作。当一个新的开发者签出代码后,应该根据编译指引文档,经过一两个操作就能编译目标代码。避免在编译过程中,需要开发者自行解决一些依赖或配置错误。
- 显示指明编译器的版本。不同的编译工具可能编译出来不同的内容,比如JDK7和JDK8,c++98和gnu++。
Jenkins
Jenkins是一个可扩展的开源编译工具。通过对插件、编译工作节点的设置,可以完成对所有开发语言的编译工作。同时,它更是一个强大的自动化集成、部署的工具,下一个章节我们即将介绍。
通过命令行安装Jenkins
以CentOS 7为例通过包管理工具进行安装,其他的版本可以参考jenkins官方安装文档:https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins
> sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
> sudo rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key
> sudo yum install -y java
> sudo yum install -y jenkins
> sudo yum install -y git subversion #安装必要的版本管理工具
服务启动后通过 http://127.0.0.1:8080/ 即可访问 Jenkins。根据提示,sudo cat /var/lib/jenkins/secrets/initialAdminPassword 获得登录密码,然后选择需要的组件,完成系统初始化安装。
通过Docker安装Jenkins
docker run -p 8080:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home jenkins
安装完毕后,通过 http://127.0.0.1:8080/ 即可访问 Jenkins
安装Jenkins组件
根据需要,我们可以安装组件,来扩充Jenkins的功能,从而满足项目的编译或自动化部署需求。常用组件的功能介绍如下:
| 插件名称 | 介绍 |
|---|---|
| Folders Plugin | 在Jenkins中建立目录 |
| OWASP Markup Formatter Plugin | 允许用户在描述信息中编写安全的HTML文本。 |
| build timeout plugin | 自动设置编译超时 |
| Timestamper | 在输出中,增加时间戳 |
| Ant Plugin | 支持使用Apache Ant编译项目 |
| Maven Project Plugin | 支持使用Maven编译项目。其他的编译工具还包括:CMake Plugin, |
| Xcode integration | 通过Jenkins调用xcode指令,完成编译和打包过程 |
| Gradle Plugin | Gradle项目编译支持。 Android开发必备。 |
| Pipeline | Jenkins官方支持的自动化套件。用于将自动化发布的过程、流程管理 |
| Pipeline: Stage View Plugin | Pipeline 视图。可更好的对Pipeline任务进行展示。 |
| GitHub Plugin | 集成Github访问 |
| Git Plugin | 支持通过GIT版本管理系统获取代码。Jenkins支持所有常用的版本管理工具,如Subversion Plug-in ,CVS Plug-in, Mercurial plugin等 |
| SSH Slaves plugin | 允许通过SSH建立Jenkins子节点。 |
| Matrix Authorization Strategy Plugin | 权限管理插件。支持按照用户/用户组,项目管理权限。其他的授权管理还包括基于角色授权的Role-based Authorization Strategy, |
| PAM Authentication plugin | linux可插入认证模块通过Linux系统的用户、密码管理Jenkins登录。其他的用户登录方式还包括LDAP Plugin,GitLab Authentication Plugin 联合授权,OpenID plugin,GitHub Authentication plugin等 |
| Email Extension Plugin | 更高级的Email设置。支持邮件自动触发,内容模板等 |
| Lockable Resources plugin | 锁插件。可建立锁,防止并发冲突。如可防止系统同时运行部署可用。当发生锁冲突时,后一个工作将等待锁结束后才运行。 |
| Copy Artifact Plugin | 允许项目之间进行编译结果复制 |
| Build Pipeline Plugin | 显示编译的上下游依赖 |
| Environment Injector Plugin | 在项目设置中,注入环境变量 |
同时推荐一些暂时还有没添加到 jenkins插件列表的插件。pubu 零信, 用于构建通知。
建立编译子节点
Jenkins使用节点来运行编译任务。默认的,系统将在Jenkins系统本机建立一个master节点,用于任务编译。但为了保证编译环境的稳定,我们推荐建立专用服务器,针对每一种编译环境分配独立的主机或虚拟机。推荐使用Vagrant进行虚拟机管理。
准备材料
- 已经准备好的Jenkins系统
- 一台新的机器,作为编译专属机器。该机器与Jenkins在同一个局域网内,如IP均为:192.168.1.X。
- 系统可采用CentOS或Ubuntu
- 安装VirtualBox。
- 安装Vagrant
建立Jenkins私钥
Jenkins与节点之间将使用SSH进行通讯,在建立节点之前,先在Jenkins系统中生成授权公私钥文件
# cd ~; mkdir -p jenkins/.ssh; cd jenkins/.ssh #准备.ssh目录
# ssh-keygen -t rsa -b 4096 -C "[email protected]" -f id_rsa #建立公私钥文件,提示Passphrase时不设置,直接敲回车。
# cp id_rsa.pub ~/ #复制公钥文件,以后备用。
# chmod 0700 .; chmod 0600 id_rsa*; #修改私钥文件夹及文件权限,禁止其他用户读取
# cd ../; sudo mv .ssh /var/lib/jenkins/ #将公私钥复制到Jenkins用户的家目录
# sudo chown -R jenkins:jenkins /var/lib/jenkins/.ssh #变更私钥文件夹及文件所有者为Jenkins运行用户
建立Jenkins私钥凭证
接下来,使用Jeknins私钥,建立授权凭证。
使用管理员账户,登录到jenkins,依次进入 Credentials =》 Jenkins Credentials Provider (global) =》 Add Credentials,进入授权新增页。
- 授权分类[Kinds]中选择 SSH Username with private key
- 授权作用域[Scope]保持默认,Global。
- 登录用户名[Username]填入上一步我们建立的用户名, jenkins
- 密钥[Private Key]选择 从Jenkins主机的~/.ssh目录中读取 [From the Jenkins master ~/.ssh]。系统将自动找到我们刚建立的id_dsa文件作为私钥
- 密码[Passphrase]留空
- ID填入 jenkins_master_key, 在以后我们写命令行的时候会使用到
- Description留空
- 点击OK, 保存本授权信息
后续即可在需要授权时,需选择此项,将显示为『jenkins』。
建立编译节点文件
mkdir /data/vm && cd /data/vm vi Vagrantfile
# -*- mode: ruby -*- # vi: set ft=ruby :` VM = "virtualbox" Vagrant.configure(2) do |config| config.vm.define :jdk8 do |jdk8| jdk8.vm.box = "centos/7" jdk8.vm.hostname = "jdk8" jdk8 "public_network", ip: “192.168.1.10" jdk8.vm.provider VM do |v| v.memory = 512 end end endvagrant up jdk8 --provision
上述指令声明并启动了一个名为jdk8的虚拟机,其使用centos7系统,IP为
192.168.1.10。注意IP需要设置为与当前机器统一局域网的网段的IP,否则将导致无法连接。
安装编译环境
接下来,登录到jdk8虚拟机上,安装编译环境,包括maven,oracle jdk8等。
$ vagrant ssh jdk8
# sudo yum -y install wget git
# sudo yum -y install maven
# wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u121-b13/e9e7ea248e2c4826b92b3f075a80e441/jdk-8u121-linux-x64.tar.gz"
# sudo mkdir -p /opt/ && cd /opt/
# sudo tar xzf ~/jdk-8u121-linux-x64.tar.gz
# sudo alternatives --install /usr/bin/java java /opt/jdk1.8.0_121/bin/java 2
# sudo alternatives --set java /opt/jdk1.8.0_121/bin/java
# sudo alternatives --install /usr/bin/jar jar /opt/jdk1.8.0_121/bin/jar 2
# sudo alternatives --install /usr/bin/javac javac /opt/jdk1.8.0_121/bin/javac 2
# sudo alternatives --set jar /opt/jdk1.8.0_121/bin/jar
# sudo alternatives --set javac /opt/jdk1.8.0_121/bin/javac
# java -version
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
# docker run -t -i airdock/oracle-jdk:jdk-8u112 java -version
修改maven配置,将maven仓库配置为国内地址,或者自己的私有仓库。
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
编译用户及关联授权
在jdk8系统中,新建用户,并允许jenkins系统进行登录。
# vagrant ssh jdk8 #登录到jdk8。
# sudo useradd jenkins #创建用户
# sudo su jenkins -
# cd ~
# mkdir -p ~/.ssh && chmod 0744 ~/.ssh/ #建立密钥目录及可登录授权文件
# touch ~/.ssh/authorized_keys && chmod 0600 ~/.ssh/authorized\_keys #设置auth keys权限。必须为0600
# echo "ssh-rsa ... [email protected]" >> ~/.ssh/authorized\_keys #将上一步在Jenkins机器上生成的公约文件内容,放入authorized_keys内。
关联节点
通过Jenkins WEB菜单,进入 系统管理 =》 管理节点 =》 新建节点, 进入节点新建指引流程
- 节点名称填 JDK8-192.168.1.10
- 节点类型选择:Permanent Agent(永久节点), 进入下一步
- "# of executors”为可并发运行的数量,由于Java项目间可能存在依赖编译或子项目编译,节点设置太少可能导致资源死锁。 推荐填4或2.
- 远程目录填 /home/jenkins ,即编译节点的登录用户家目录。
- 标签填JDK8
- 用法:尽可能的使用此节点
- 启动方法:Launch slave agent via SSH。 (依赖插件: SSH Slaves plugin)
- 主机[Host]: 192.168.1.10 , 填写jdk8的IP地址
- Credentials 选择”jenkins”
- Availability: "Keep this agent online as much as possible”, 保持默认值,即保持节点一直可用。
- 点击Save,完成。
- 保存后,节点将自动进行连接。 如发生错误,可点击左侧日志查看连接失败信息并修复
- 连接成功后的节点页面如下图:
更多编译节点的安装,如JDK7, 脚本语言编译,Gradle等可通过自动化部署脚本实现,在第N章,我们将进行详细介绍。
建立编译任务
我们以一个简单的Java项目为例,建立第一个编译项目,将源代码编译成需要的目标文件。 在第3章源代码管理中,我们已经搭建了gitlab服务器,用于存储我们的源代码,Jenkins可以从gitlab中获得代码,通过节点进行编译得到目标文件。 我们在gitlab中建立一个新的项目,登录gitlab,新建项目,从URL导入项目, URL填写一个简单的JAVA工程地址: https://github.com/gikoluo/DevOps_JavaProject_Sample 。完成后,我们得到了在私有仓库中的项目地址: http://10.100.18.106/sample/java-sample
创建gitlab部署私钥 进入Gitlab管理界面,点击右上角设置下拉菜单,找到 Deploy Keys并进入。 点击 New Deploy Key,将Jenkins服务器的公钥复制到Key输入框,命名为『Jenkins Deploy Key』 再进入java-sample项目,找到项目的 Deploy Keys , Enable 刚才加入的Deploy Key。
创建Jenkins编译任务 [需要安装 Maven Integration plugin ] 进入jenkins web页面,点击创建 先建立一个目录,项目名称填 sample, 下方选择目录[Folder],保存。 在目录下,再点击创建新任务,任务名称填: java-sample, 下方选择构建一个Maven项目, 保存。 在任务编译页面中,设置属性,重要属性包括:
- 源码管理,选择git,填入测试项目的的gitlab地址。git@gitlab:sample/java-sample.git,
- 授权选择jenkins 项目完成打包后,将生成一些jar文件。设置部署包文件,将包提出出来,以便下载和后续使用。在
设置编译工具, JDK8, 设置 Maven Home
放入Jenkins和JDK8节点。
$ cat ~/.ssh/config
Host gitlab
User git
Hostname 172.17.0.1
Port 7022
IdentityFile ~/.ssh/id_rsa
建立maven 填写git地址,选择私钥 设置部署包文件 运行,检查编译过程和编译结果。
如果一切正常,我们将在任务页面中看到编辑并存档的jar包。下载之后,本地测试运行一下:
java -jar multi1-6.12-SNAPSHOT.jar
编译工作就已经全部完成了。对目标文件的配置及发布管理我们在下一章介绍。
建立Sonar服务
Sonar能对代码进行静态检查,分析编码规范、代码重复率等,并估算代码债务时间。对于代码走查,帮助开发人员进行代码重构提供重要的参考。
建立Sonar服务器 Sonar服务我们使用docker进行安装:
docker run -d --name sonarqube-mysql \
-e MYSQL_ROOT_PASSWORD=sonar \
-e MYSQL_DATABASE=sonar -e MYSQL_USER=sonar \
-e MYSQL_PASSWORD=sonar \
-v /opt/docker/sonarqube/mysql:/var/lib/mysql mysql:latest
docker run -d --name sonar \
--link sonarqube-mysql:mysql \
-p 9000:9000 -p 9092:9092 \
-e SONARQUBE_JDBC_USERNAME=sonar \
-e SONARQUBE_JDBC_PASSWORD=sonar \
-e SONARQUBE_JDBC_URL="jdbc:mysql://mysql:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true" \
-v /data/docker/sonarqube/conf:/opt/sonarqube/conf \
-v /data/docker/sonarqube/data:/opt/sonarqube/data \
-v /data/docker/sonarqube/extensions:/opt/sonarqube/extensions \
-v /data/docker/sonarqube/logs:/opt/sonarqube/logs \
sonarqube
安装Jenkins插件
- 在Jenkins插件中心找到 SonarQube, 安装。
- 在Jenkins中,进入 管理Jenkins > 系统设置, 找到SonarQube配置。
- 勾选 Enable injection of SonarQube server configuration as build environment variables
- 填入正确的Sonar地址。
安装Sonar节点 我们公用jdk8,安装sonar。 使用Ansible代码 在Jenkins节点中,增加sonar tag 配置SonarQube扫描器
- 进入Jenkins,管理 > 全局工具管理
- 增加SonarQube扫描器,名字:
Sonar,SONAR_RUNNER_HOME 填/opt/sonar-scanner
配置任务
- 进入Jenkins,找到编译一个编译任务,进入修改页面
- 在编译后操作中,增加 Build Sonar。
- 填入Sonar参数,保存
- 重新编译任务,编译完成后,将在任务页面,出现sonaar标志及连接。点击后即可查看sonar代码扫描结果。
- 注:对Maven项目进行在编译后执行sonar官方已不再建议使用。我们将在下一张介绍如何在Pipeline流程中使用sonar。