大家好,又见面了,我是你们的朋友全栈君。
25.1 CI/CD介绍
互联网软件的开发和发布,已经形成了一套标准流程,假如把开发工作流程分为以下几个阶段:
编码 → 构建 → 集成 → 测试 → 交付 → 部署
正如你在上图中看到,持续集成(Continuous Integration)
、持续交付(Continuous Delivery)
和持续部署(Continuous Deployment)
有着不同的软件自动化交付周期。
持续集成(CI)
上面整个流程中最重要的组成部分就是持续集成(Continuous integration,简称CI)。
持续集成指的是,频繁地(一天多次)将代码集成到主干。将软件个人研发的部分向软件整体部分交付,频繁进行集成以便更快地发现其中的错误。
它的好处主要有两个:
代码语言:javascript复制1. 快速发现错误。每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易;
2. 防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。
持续集成并不能消除Bug,而是让它们非常容易发现和改正。持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。
持续交付
持续交付(Continuous delivery)指的是,频繁地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。
持续交付在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境的「类生产环境」(production-like environments)中。持续交付优先于整个产品生命周期的软件部署,建立在高水平自动化持续集成之上。
持续交付可以看作持续集成的下一步。它强调的是,不管怎么更新,软件是随时随地可以交付的。
持续部署(CD)
持续部署(continuous deployment)是持续交付的下一步,指的是代码通过评审以后,自动部署到生产环境。
持续部署的目标是,代码在任何时刻都是可部署的,可以进入生产阶段。
持续部署的前提是能自动化完成测试、构建、部署等步骤。
总的来说,持续集成、持续交付、持续部署提供了一个优秀的 DevOps 环境。对于整个开发团队来说,能很大地提升开发效率,好处与挑战并行。无论如何,频繁部署、快速交付以及开发测试流程自动化都将成为未来软件工程的重要组成部分。
25.2 Jenkins介绍
Jenkins概念
Jenkins是一个开源的、可扩展的持续集成、交付、部署(软件/代码的编译、打包、部署)的基于web界面的平台。允许持续集成和持续交付项目,无论用的是什么平台,可以处理任何类型的构建或持续集成。
官网:https://jenkins.io/ 官方文档:https://jenkins.io/doc/
Jenkins特性:
代码语言:javascript复制开源的java语言开发持续集成工具,支持CI,CD;
易于安装部署配置:可通过yum安装,或下载war包以及通过docker容器等快速实现安装部署,可方便web界面配置管理;
消息通知及测试报告:集成RSS/E-mail通过RSS发布构建结果或当构建完成时通过e-mail通知,生成JUnit/TestNG测试报告;
分布式构建:支持Jenkins能够让多台计算机一起构建/测试;
文件识别:Jenkins能够跟踪哪次构建生成哪些jar,哪次构建使用哪个版本的jar等;
丰富的插件支持:支持扩展插件,你可以开发适合自己团队使用的工具,如git,svn,maven,docker等。
Jenkins安装
安装最低配置:不少于256M内存,不低于1G磁盘,JDK版本>=8(openjdk也可以)。
- 环境:
hostname:lzx ip:192.168.33.150 role:jenkins服务器
hostname:lzx1 ip:192.168.33.160 role:部署机器
- 安装jenkins:
# yum install -y java-1.8.0-openjdk #安装openjdk,jenkins基于java开发
# wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat/jenkins.repo #wget -O 下载文件并以指定的文件名保存
# cat /etc/yum.repos.d/jenkins.repo
[jenkins]
name=Jenkins
baseurl=http://pkg.jenkins.io/redhat
gpgcheck=1 #为1时会检测key
# rpm --import https://pkg.jenkins.io/redhat/jenkins.io.key #安装jenkins key
# yum install -y jenkins
- 启动jenkins:
# systemctl start jenkins
# ps aux |grep jenkins
jenkins 1157 84.7 10.0 2320896 100884 ? Ssl 23:28 0:08 /etc/alternatives/java -Dcom.sun.akuma.Daemon=daemonized -Djava.awt.headless=true -DJENKINS_HOME=/var/lib/jenkins -jar /usr/lib/jenkins/jenkins.war --logfile=/var/log/jenkins/jenkins.log --webroot=/var/cache/jenkins/war --daemon --httpPort=8080 --debug=5 --handlerCountMax=100 --handlerCountMaxIdle=20
代码语言:javascript复制# less /var/log/jenkins/jenkins.log #查看jenkins日志,截取部分
Jenkins initial setup is required. An admin user has been created and a password generated
.
Please use the following password to proceed to installation:
77faa20f2ad544f7bcb6593b1cf1436b #admin密码,初始化安装时会用到
This may also be found at: /var/lib/jenkins/secrets/initialAdminPassword #admin密码也可以在这里查到
- 访问安装:
打开浏览器,访问ip:8080
进行安装,ip为服务器ip
提示需要输入管理员密码,输入77faa20f2ad544f7bcb6593b1cf1436b(上面查到的admin密码),点击继续
,会初始化一小段时间
这里我选择安装推荐的插件
这里就已经开始在安装插件,我们等它安装完就好了。
了解Jenkins
上面等插件安装完后,就进入到下面界面,提示要创建第一个admin用户,并设置用户名密码
这里我直接创建用户名为admin,密码自定义
点击保存并完成
提示配置jenkins URL,这里保持默认即可,继续点击保存并完成
提示jenkins已经就绪,开始使用jenkins
- 查看jenkins的配置文件:
# cat /etc/sysconfig/jenkins #查看jenkins的配置文件,定义了home、JAVA_CMD、user、port等基础配置,保持默认即可
代码语言:javascript复制## Path: Development/Jenkins
## Description: Jenkins Automation Server
## Type: string
## Default: "/var/lib/jenkins"
## ServiceRestart: jenkins
#
# Directory where Jenkins store its configuration and working
# files (checkouts, build reports, artifacts, ...).
#
JENKINS_HOME="/var/lib/jenkins"
## Type: string
## Default: ""
## ServiceRestart: jenkins
#
# Java executable to run Jenkins
# When left empty, we'll try to find the suitable Java.
#
JENKINS_JAVA_CMD=""
## Type: string
## Default: "jenkins"
## ServiceRestart: jenkins
#
# Unix user account that runs the Jenkins daemon
# Be careful when you change this, as you need to update
# permissions of $JENKINS_HOME and /var/log/jenkins.
#
JENKINS_USER="jenkins"
## Type: string
## Default: "false"
## ServiceRestart: jenkins
#
# Whether to skip potentially long-running chown at the
# $JENKINS_HOME location. Do not enable this, "true", unless
# you know what you're doing. See JENKINS-23273.
#
#JENKINS_INSTALL_SKIP_CHOWN="false"
## Type: string
## Default: "-Djava.awt.headless=true"
## ServiceRestart: jenkins
#
# Options to pass to java when running Jenkins.
#
JENKINS_JAVA_OPTIONS="-Djava.awt.headless=true"
## Type: integer(0:65535)
## Default: 8080
## ServiceRestart: jenkins
#
# Port Jenkins is listening on.
# Set to -1 to disable
#
JENKINS_PORT="8080"
## Type: string
## Default: ""
## ServiceRestart: jenkins
#
# IP address Jenkins listens on for HTTP requests.
# Default is all interfaces (0.0.0.0).
#
JENKINS_LISTEN_ADDRESS=""
## Type: integer(0:65535)
## Default: ""
## ServiceRestart: jenkins
#
# HTTPS port Jenkins is listening on.
# Default is disabled.
#
JENKINS_HTTPS_PORT=""
## Type: string
## Default: ""
## ServiceRestart: jenkins
#
# Path to the keystore in JKS format (as created by the JDK 'keytool').
# Default is disabled.
#
JENKINS_HTTPS_KEYSTORE=""
## Type: string
## Default: ""
## ServiceRestart: jenkins
#
# Password to access the keystore defined in JENKINS_HTTPS_KEYSTORE.
# Default is disabled.
#
JENKINS_HTTPS_KEYSTORE_PASSWORD=""
## Type: string
## Default: ""
## ServiceRestart: jenkins
#
# IP address Jenkins listens on for HTTPS requests.
# Default is disabled.
#
JENKINS_HTTPS_LISTEN_ADDRESS=""
## Type: integer(1:9)
## Default: 5
## ServiceRestart: jenkins
#
# Debug level for logs -- the higher the value, the more verbose.
# 5 is INFO.
#
JENKINS_DEBUG_LEVEL="5"
## Type: yesno
## Default: no
## ServiceRestart: jenkins
#
# Whether to enable access logging or not.
#
JENKINS_ENABLE_ACCESS_LOG="no"
## Type: integer
## Default: 100
## ServiceRestart: jenkins
#
# Maximum number of HTTP worker threads.
#
JENKINS_HANDLER_MAX="100"
## Type: integer
## Default: 20
## ServiceRestart: jenkins
#
# Maximum number of idle HTTP worker threads.
#
JENKINS_HANDLER_IDLE="20"
## Type: string
## Default: ""
## ServiceRestart: jenkins
#
# Pass arbitrary arguments to Jenkins.
# Full option list: java -jar jenkins.war --help
#
JENKINS_ARGS=""
- 查看程序主目录:
# ls /var/lib/jenkins/ #查看程序主目录
config.xml nodes
hudson.model.UpdateCenter.xml plugins
hudson.plugins.git.GitTool.xml queue.xml.bak
identity.key.enc secret.key
jenkins.CLI.xml secret.key.not-so-secret
jenkins.install.InstallUtil.lastExecVersion secrets
jenkins.install.UpgradeWizard.state updates
jenkins.model.JenkinsLocationConfiguration.xml userContent
jobs users
logs workflow-libs
nodeMonitors.xml
上面,
代码语言:javascript复制jobs 浏览器上面创建的任务都会存放在这里
logs 存放jenkins相关的日志
nodes 多节点时用到
plugins 插件所在目录
secrets 密码秘钥所在目录 #jobs和plugins目录比较重要
jenkins存放数据不依靠数据库,所以在移植时只需要拷贝整个程序主目录即可。
发布PHP代码
- 查看插件:
系统管理
–>插件管理
,“可选插件”是未安装的插件,“已安装”是已经安装的插件,其中勾选为灰色的是不可卸载的插件。
检查是否有“Git plugin”和“Publish over SSH”两个插件,如果没有,在“可选插件”中安装。发现“Publish over SSH”没有,安装它
安装完插件后,直接在linux机器上重启jenkins服务,而不是勾选空闲时重启
# systemctl restart jenkins
重新在浏览器上登录jenkins,并在linux机器上生成一对秘钥
代码语言:javascript复制# cd ~/.ssh/
# ls #如果之前有生成过密钥对,那可以直接用,不需要再生成
authorized_keys known_hosts
# ssh-keygen -f ~/.ssh/jenkins #生成密钥对
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): #直接回车,也可以设置密码
Enter same passphrase again: #直接回车
Your identification has been saved in /root/.ssh/jenkins.
Your public key has been saved in /root/.ssh/jenkins.pub.
The key fingerprint is:
SHA256:VF7nN5V6pPco5DDnm8PUeu8OEg3VMj/IkgfYkqd/Hx4 root@lzx
The key's randomart image is:
---[RSA 2048]----
| = . o.o|
| * = ooo|
| . = = *=.|
| . .oooO =o|
| S .B . .o|
| .=ooE.|
| oo= o|
| *.. |
| o |
----[SHA256]-----
# cat jenkins #查看私钥
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAu H7NOAvrPYWwNPur64cwZYJz80MZdmQP44KJy1Zcqsfmawt
axjbgzZSPyXSM9fDqo32dR4OR8PdqFdnSDWU8gXl2tgTcsUiy y 1TB7ZdgzmI8Z
LAAGKQsrWOFMBB9GCfBlAL6eGg4nnsnc4bDT2sJhZuzgfW/qK5AyWSCIm2wvfFw5
1m2WcaslRzNwGkg2cFK4tuY0ZkiF/UfA YC88RAhSbjFD5FQKLzSHmkZbxbSGC09
cYtsZb3g4VwmkZUEuU/jy5nBUpxv54B6NjkvMxCVx9M/zviiTSZt yRDaMySISxB
/3Y9FP7R/3Rcf1ZXYGK32hmQ55pDlbcM0gCEFQIDAQABAoIBAD0uSFweiMfc1unx
2W7R1sFfN1C4p8nev 9CNKIvTUpe2Qp0m9GdJZYjsUKo8Ts6jpfSZKgcom/SIzL3
S0WjVR4 t2u9IxAji8I2FKpdgEFY1uxd5MeENn1VCseFjfhlqqv/4RQN/nPjmhlG
CKxhpYs98vPOPQf70phe 1Y0ludtEztiJEM7viIsF8PLlCwf6P7S1JFIXXOAo xQ
JAHt3AlzUj HVfq97SbS cbhuR/MwvIMQs54AsyPOVtt1Q7AOniDw/bnX8JpE9mU
diww6KHn/qQIB0DSWgvfyGwC6qM6C3nUv EyybdJBcEsYAUH3SgZpBvu3smT2XN9
MCAFegECgYEA524LrSj57hPSKdGO3RT/jVReq0fHJscqSELHAl40lPSKQm vFK2L
Z20jnQhPg9ebJQjqdkZ/J8EHuwuZjtiqYAeWUERcp5cEEm/BQZ5fB vYlTZ QXNa
S6Z9ex2/ qBwY8bJeZR5LPEzVN2cENH8t6FiJO1/csgS3T KAxnGZoECgYEAz9Ri
CyGwoJs9IpEb3y9Q1AVeFvUQ47XQGJo4xD5nivJ3W/4XApajJl6hXoVp96Z0gXIR
Hhqp /d4MOlDSeAUPeHcW6gE0h8BYl25fXbvXSpA7rSv8QU BtQ4vMpsKF5bpRNt
vQjeJrPtHLuFJrOZ6yzov3s4EXlt0AbOBlWXW5UCgYEAvmn6zXRjfxFr3 B97UUz
pnL mWqrXyqLjXz0BOzB0rUJdPQzV44anvRbUv7dfZrOGcCmOL50LJ9T3wACTlfS
hsR3Vn IxJMqVIkCtxy5D lrAAogA3YHmyrXH48wv N11YJfjtssgF/MXk0f9W7w
wZ3VBJEsRaJ8cvGuejKg3QECgYEAzvoAun1O3mQ8OlSxoVAJ 11tGwftThofqKDO
sAJGNEbNQDyIxfcnfGr54pyTsCOCG/hxu71qVJlchbmJXeVfw1FQl6d5RZnDUyds
N9te/zW5fQFNx4clAsAP0VT26i0ur/PtcjmGpJzEI71NGgH75A7RrQEWEbERAAAG
5qAPIRECgYEAjpNyIsvDYcEtBj/oCQCa3FdPsGqRaYmBZLqJ4nCP5DolYgJkewKx
n5 AIYsXYYKB7W4a0uPKN6gMVVAkm0Icletpf9UpT51uMSR5JpqIwqOFNgI0 NkV
jlerYcD909WjV3OqrwZm7yPqwa2RPkIfCbeHnXbkAcv DuWn4L8RdI=
-----END RSA PRIVATE KEY-----
系统管理
–>系统设置
,找到之前安装的Publish over SSH
插件,Passphrase
填写之前生成秘钥时设置的密码,没有则留空,Path to key
留空,Key
粘贴/root/.ssh/jenkins
文件内容。
然后新增SSH Servers
,填入对应的hostname,这就是PHP代码要发布的机器
接下来还需要把公钥拷贝到对应的hostname机器(lzx1)上
代码语言:javascript复制# cat jenkins.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC74fs04C s9hbA0 6vrhzBlgnPzQxl2ZA/jgonLVlyqx ZrC1rGNuDNlI/JdIz18OqjfZ1Hg5Hw92oV2dINZTyBeXa2BNyxSLL7L7VMHtl2DOYjxksAAYpCytY4UwEH0YJ8GUAvp4aDieeydzhsNPawmFm7OB9b orkDJZIIibbC98XDnWbZZxqyVHM3AaSDZwUri25jRmSIX9R8D5gLzxECFJuMUPkVAovNIeaRlvFtIYLT1xi2xlveDhXCaRlQS5T PLmcFSnG/ngHo2OS8zEJXH0z/O KJNJm37JENozJIhLEH/dj0U/tH/dFx/VldgYrfaGZDnmkOVtwzSAIQV root@lzx
到lzx1上粘贴jenkins.pub
代码语言:javascript复制# cd ~/.ssh/
# ls
authorized_keys
# vim authorized_keys #写入下面内容
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC74fs04C s9hbA0 6vrhzBlgnPzQxl2ZA/jgonLVlyqx ZrC1rGNuDNlI/JdIz18OqjfZ1Hg5Hw92oV2dINZTyBeXa2BNyxSLL7L7VMHtl2DOYjxksAAYpCytY4UwEH0YJ8GUAvp4aDieeydzhsNPawmFm7OB9b orkDJZIIibbC98XDnWbZZxqyVHM3AaSDZwUri25jRmSIX9R8D5gLzxECFJuMUPkVAovNIeaRlvFtIYLT1xi2xlveDhXCaRlQS5T PLmcFSnG/ngHo2OS8zEJXH0z/O KJNJm37JENozJIhLEH/dj0U/tH/dFx/VldgYrfaGZDnmkOVtwzSAIQV root@lzx
lzx上登录lzx1测试
代码语言:javascript复制# ssh lzx1
root@lzx1's password:
Last login: Thu Sep 20 20:51:25 2018 from 192.168.33.1
#exit
logout
Connection to lzx1 closed.
[root@lzx .ssh]# #ssh登录没问题
浏览器上测试连接有没有问题,点击Test Configuration
,如果没问题,左侧会显示Success,如下图
这就说明我们前面的配置是没问题的。点击应用
,出现“已保存”
如果有多台web server ,可以继续新增,重复以上操作,最后点击保存
。
- 新建任务:
回到首页,点击创建一个新任务
,自定义任务名称,选择构建一个自由风格的软件项目
点击确定
描述
自定义;源码管理
选择Git
(如果使用SVN就选择Subversion
);Repository URL
选择自己公共仓库的地址,这里为了方便我使用别人的公共仓库;Branches to build
默认为*/master,意思是发布代码的分支为master分支;构建触发器
和构建环境
留空;构建
选择Send files or execute commands over SSH
,Name
选择对应的服务器,Source files
填入**/**
,表示要发布的代码为全部文件,Remote directory
填入/tmp/jenkins_test(自定义一个存放PHP代码的文件夹名),Remove prefix
和Exec command
可以留空
点击Add Server
可以增加更多台服务器,在生产环境下通常也是多台服务器一起发布代码。点击保存
点击左侧立即构建
,会出现构建历史
,其中#1
表示第一次构建
点击#1
,点击控制台输出
可以查看构建成功,通常构建完,不管是成功还是失败都会有邮件通知。这里因为没有配置邮件,所以没办法接收到邮件。
到lzx1上查看
代码语言:javascript复制# ll !$
ll /tmp/jenkins_test/
total 100
drwxr-xr-x 2 root root 66 Sep 21 02:42 D11Z
drwxr-xr-x 2 root root 260 Sep 21 02:42 D12Z
drwxr-xr-x 2 root root 24 Sep 21 02:42 D13Z
drwxr-xr-x 2 root root 4096 Sep 21 02:42 D14Z
drwxr-xr-x 2 root root 4096 Sep 21 02:42 D15Z
drwxr-xr-x 2 root root 25 Sep 21 02:42 D17Z
drwxr-xr-x 2 root root 76 Sep 21 02:42 D18Z
drwxr-xr-x 2 root root 155 Sep 21 02:42 D19Z
drwxr-xr-x 2 root root 83 Sep 21 02:42 D20Z
drwxr-xr-x 2 root root 200 Sep 21 02:42 D21Z
drwxr-xr-x 2 root root 128 Sep 21 02:42 D22Z
-rw-r--r-- 1 root root 18044 Sep 21 02:42 LICENSE
-rw-r--r-- 1 root root 419 Sep 21 02:42 README.md
-rw-r--r-- 1 root root 66243 Sep 21 02:42 习题答案.txt
和我们发布的源是一致的,如下图
如果在源上面做了变更,继续点击立即构建
,如果构建成功,那么在目标机器上也可以看到变更的内容。
Jenkins邮件配置
系统管理
→ 系统设置
,找到邮件通知
进行配置,这里我使用的是163邮箱
发邮件的地址必须与管理员地址一致,否则发邮件会报错。同时必须在163邮箱设置授权码,并且在上面设置密码的地方填入该授权码,不然也是发送不了邮件。
点击Test configuration
,如果显示为“Email was successfully sent”,说明邮箱配置成功
到163邮箱查看是否收到刚刚测试的邮件
收到了,邮件配置成功。点击保存
,进入到之前的任务中,点击配置
找到构建后操作
,选择E-mail Notification
,Recipients
这里填入接收邮件的邮箱地址
勾选保持默认即可,点击保存
现在来测试,故意让构建不成功,看是否能接收邮件。
- 首先lzx1上更改目标目录的权限,使之无法写入:
# cd /tmp/
# rm -rf jenkins_test/*
# chattr i jenkins_test/ #加上i权限,让它无法写入
- 回到浏览器,点击
立即构建
,查看控制台输出
:
- 到163邮箱进行查看:
邮件收到,邮箱配置成功。
但这样有一点不好,只有构建失败才会发邮件提示,我想让它不管构建成功还是失败都发邮件提示。
使用插件email-ext配置邮件
想让它不管构建成功还是失败都发邮件来提示,就必须要安装一个插件,该插件名字是Email Extension Plugin
。如果你是按照上面步骤来的,那么该插件就已经安装。系统管理
→ 插件管理
查看是否安装此插件,没有就安装。
系统管理
→ 系统设置
→ Extended E-mail Notification
,填写SMTP server
,勾选Use SMTP Authentication
,再填写User Name
和Password
,Password
这里填邮箱授权码,然后SMTP port
这里填25
往下拉,定位到Default Triggers
,选择always
,表示不管什么情况都会发邮件提示。之后再清空之前设置的邮箱通知,应用、保存即可
打开任务
(我这里是lzx_php)→ 配置
,去掉构建后操作
,点击增加构建后操作步骤
,选择Editable Email Notification
,在Project Recipient List
那里添加自己的邮箱,然后点击Advanced Setting
,定位到Triggers
,可以在Add Trigger
处增加发邮件的条件
现在来测试,看是否能接收邮件。
- 先在lzx1上改回目标目录原来的权限:
# chattr -i jenkins_test/ #让该目录可以写入
- 回到浏览器,点击
立即构建
,查看控制台输出
:
- 到163邮箱进行查看:
邮件收到,邮箱配置成功。
破解管理员密码
人难免会犯些错误,如果我们不小心忘记了管理员密码,我们如何去修改回来呢?
- lzx上编辑admin配置文件:
# cd /var/lib/jenkins/users/admin/
# ls
config.xml
# vim config.xml #找到<passwordHash>这行,用下面一行替换掉原来的
<passwordHash>#jbcrypt:$2a$10$eJAMBW3qb/ijrFsSxkJnDOB747e0mFWSR03UmLCn96E4N7vL5BYzC</passwordHash>
这样你的密码就会被重置为123456啦
- 重启服务,进入浏览器界面修改新的管理员密码:
# systemctl restart jenkins #需要重启服务,才能使用123456去登录
回到浏览器界面,使用123456去登录,系统管理
→ 管理用户
→ admin右侧设置齿轮图标,设置新的管理员密码,应用、保存即可