Jenkins 持续集成
今天看到《Jenkins2 权威指南》出来了,马上买了一本,DevOps 相关的书籍更新太快了,这包括 Docker、Jenkins,新特性快速推出,旧版本必然快速淘汰,可以看一下 Jenkins 的版本,Jenkins 1.x 相关书籍已经严重滞后了。
1 基础
1.1 安装
- 作为独立的应用程序安装:(1)二进制包安装,(2)brew、yum 安装,(3)直接下载 war 包,java -jar jenkins.war。 更新:用 brew、yum 命令更新,或者用新版的 jenkins.war 替换旧版然后重启
- 应用服务器安装,比如 tomcat 更新:注意要删除一些缓存,避免过期页面被引用
1.2 主目录
不管你把 Jenkins 的 war 文件存放在哪儿,Jenkins 都把其所有重要的数据存放在一个专用的、隔离的,称为 Jenkins 主目录的目录下。在这里,Jenkins 存储关于构建服务器的配置信息、构建作业、构建产物和其他有用的信息,当然也包括你安装了的任何插件。且 Jenkins 主目录格式是跨版本向后兼容的,所以你可以自由地更新或者重新安装 Jenkins 而不影响 Jenkins 主目录。
一般主目录是 ~/.jenkins 目录,一个好的做法是为 Jenkins 创建一个特殊的用户和用户组。sudo groupadd build,sudo useradd --create-home --shell /bin/bash --groups build jenkins,让主目录在用户空间下。
确保你的 Jenkins 主目录能定期备份是非常重要的,Jenkins 程序本身相对并不那么重要。
- workspace:是 Jenkins 对你的项目进行构建的地方
- builds:构建历史
1.3 节点
节点代表了任何可以执行 Jenkins 任务的系统,包括了主节点,代理节点和容器,前两都是机器,后者是需要在机器上运行的容器,但都可以称为节点,从这点可以看出,节点是承载任务的,而不是节点机器。
- 主节点:运行 Jenkins 实例的主要控制系统,不推荐在主节点上执行高负载任务,而应该在代理节点上运行
- 代理节点:也叫从节点,代表非主节点的系统,且由主系统管理,对资源访问有限,降低安全风险
- 容器:容器也应该选择在代理节点上运行
1.4 凭证
-
类型:除了默认类型,还可以安装插件扩展
- Username with password,用户名密码
- Docker Host Certificate Authentication
- SSH Username with private key,ssh 连接
- Secret file
- Secret text,比如token
- Certificate
-
域:尽量匹配对应的域名,不过不强制
- 全局
- 自定义域
-
提供者:
- jenkins
- 用户
1.5 Pipeline
Jenkins 2 的新特性,通过插件获得流水线这一新功能,通过 Jenkins DSL 实现流水线代码,代码提取到一个 Jenkinsfile 文件中。
- 脚本式流水线:node 用于指定节点,偏向程序语言
- 声明式流水线:agent 用于指定节点,偏向于自然语言
2 声明式流水线
创建一个流水线项目后,项目左侧列表会有“流水线语法”链接,可以借助这一脚手架直接生成 DSL 代码,比如与“构建触发”相关的可以选择 properties 步骤生成。当然想要理解声明式还得系统性地学习。
3 构建触发器
除了手动立即构建,还可以构建触发器自动的构建,这也是自动化部署的基础,常用的几种构建触发器如下:
- 其他工程构建后触发,如标题所述,允许在一个或者多个其它项目之后开发你的项目构建。依赖构建
- 定时构建,定时构建
- Build when a change is pushed to GitLab,需要设置 webhook URL
- GitHub hook trigger for GITScm polling,和 GitLab 一样需要在 GitHub 设置 webhook URL
- 轮询 SCM,Jenkins 主动询问
4 通知
7 分布式部署实战
下面通过 vagrant 进行分布式部署,主要还是用于开发和测试使用,本次实战只部署机器和环境,采用手动运行 jenkins.war。首先来看一下目录结构:
vagjenkins/
├── Vagrantfile
├── certs
│ ├── domain.crt
│ └── domain.key
├── jdk-8u211-linux-x64.tar.gz
├── jenkins.war
└── scripts
├── docker.sh
├── hosts.sh
├── java.sh
└── registry.sh
- certs 和 scripts/registry.sh 是安装 Docker registry 的脚本,具体可以查看 Docker Engine 一文。
- jdk-8u211-linux-x64.tar.gz 是运行 jenkins 的 jdk 环境工具
- jenkins.war,在环境创建好后启动,以 java -jar jenkins.war 手动运行
- Vagrantfile 和 scripts 在接下来重点介绍
7.1 Vagrantfile
- master 节点
- 需要安装 Docker
- 并运行 Docker Registry 容器,并连接到Docker Registry;
- 安装 JDK 环境,预备运行 jenkins.war
- 映射 8080 端口到宿主机,访问宿主机的 localhost:8080 即是访问 master 的8080
- node 从节点
- 只需安装 Docker,并连接到 master 虚拟机的 Docker Registry
Vagrantfile 就是基础设置即代码文件,可以将上述的过程代码化:
cat <<EOF >> /Users/ada/vagjenkins/Vagrantfile
# -*- mode: ruby -*-
# vi:set ft=ruby sw=2 ts=2 sts=2:
NODE_COUNT = 2
POD_NETWORK_CIDR = "10.244.0.0/16"
MASTER_IP = "192.168.56.113"
NODE_IP_PREFIX = "192.168.56."
Vagrant.configure("2") do |config|
config.vm.box = "centos/7"
config.vm.box_version = "1902.01"
config.vm.provider "virtualbox" do |v|
v.memory = 1536
v.cpus = 2
end
config.vm.provision "install-docker", type: "shell", :path => "scripts/docker.sh"
config.vm.provision "setup-hosts", type: "shell", :path => "scripts/hosts.sh" do |s|
s.args = ["eth1"]
end
config.vm.define "jenkins-master" do |node|
node.vm.hostname = "jenkins-master"
node.vm.network :private_network, ip: MASTER_IP
node.vm.network "forwarded_port", guest: 8080, host: 8080
node.vm.provision "install-registry", type: "shell", :path => "scripts/registry.sh"
node.vm.provision "install-java", type: "shell", :path => "scripts/java.sh"
end
(1..NODE_COUNT).each do |i|
config.vm.define "jenkins-node0#{i}" do |node|
node.vm.hostname = "jenkins-node0#{i}"
node.vm.network :private_network, ip: NODE_IP_PREFIX + "#{113 + i}"
end
end
# config all node to connect registry
config.vm.provision "shell", inline: <<-SHELL
mkdir -p /etc/docker/certs.d/192.168.56.113:5000
cp /vagrant/certs/domain.crt /etc/docker/certs.d/192.168.56.113:5000/ca.crt
SHELL
end
EOF
7.2 scripts
- Docker 安装
cat <<EOF >> /Users/ada/vagjenkins/scripts/docker.sh
#!/bin/bash
set -ex
# 安装 docker
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce docker-ce-cli containerd.io
# docker 官方镜像阿里云加速
mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://r11fpimm.mirror.aliyuncs.com"]
}
EOF
# 启动
usermod -aG docker vagrant
systemctl start docker
systemctl enable docker
EOF
- 设置主机名
cat <<EOF >> /Users/ada/vagjenkins/scripts/hosts.sh
#!/bin/bash
set -xe
IFNAME=$1
IP="$(ip -4 a s $IFNAME | grep "inet" | head -1 |awk '{print $2}' | cut -d'/' -f1)"
sed -e "s/^.*${HOSTNAME}.*/${IP} ${HOSTNAME} ${HOSTNAME}/" -i /etc/hosts
EOF
- 启动 docker registry 容器
cat <<EOF >> /Users/ada/vagjenkins/scripts/registry.sh
#!/bin/bash
set -ex
# run container
docker pull registry:2.7.1
docker run -d \
--restart=always \
--name registry \
-v `pwd`/registry:/var/lib/registry \
-v /vagrant/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-p 5000:5000 \
registry:2.7.1
# setup firewall, 因为 vagrant 镜像默认防火墙是关闭的,所以不用设置
#firewall-cmd --permanent --add-port=8080/tcp
#firewall-cmd --reload
EOF
- 安装 jdk 和 git,建议用 ssh 连接 git,一般有两种办法
- 如果是把 ssh 密钥放在服务器的话,必须得用
ssh -T git@github.com连接 github,把 git 服务器指纹写入 known_hosts,否则构建不会在指纹提示自动输入 yes - 只是安装 git,在 jenkins 创建新凭据,只需要填写 ssh 密钥,但每次使用时都需要指定用凭据,方便的是会自动采集指纹写入 known_hosts
我这里选择了第二种方式,注意设置环境变量的 $ 符号不要被解析掉:
cat <<EOF >> /Users/ada/vagjenkins/scripts/java.sh
#!/bin/bash
set -ex
# untar jdk
mkdir -p /usr/local/java
tar -zxf /vagrant/jdk-8u211-linux-x64.tar.gz -C /usr/local/java/
# set env
cat <<EOF >> /home/vagrant/.bash_profile
export JAVA_HOME=/usr/local/java/jdk1.8.0_211
export JRE_HOME=\${JAVA_HOME}/jre
export CLASSPATH=.:\${JAVA_HOME}/lib:\${JRE_HOME}/lib
export PATH=\${JAVA_HOME}/bin:\$PATH
EOF
yum install -y git
EOF
7.3 运行 master 节点的 Jenkins
完成以上步骤,启动虚拟机,用 java -jar /vagrant/jenkins.war 运行 Jenkins,因为 8080 已经映射到宿主机,所以可以在宿主机访问 Jenkins:localhost:8080。
7.4 加入 node 从节点
这里主要就把从节点的 ssh 私钥(服务器鉴权放在 /home/vagrant/.ssh/authorized_keys)生成 jenkins 的凭据,节点的 ssh 私钥可以通过 vagrant ssh [name] –debug 的输出日志中获取,一般放在 /Users/ada/vagjenkins/.vagrant/machines/[name]/virtualbox/private_key,通过 ssh、scp 去连接都需要该 private_key,例子如下:
scp -i /Users/ada/vagjenkins/.vagrant/machines/jenkins-node01/virtualbox/private_key /Users/ada/.ssh/github/* vagrant@192.168.56.114:/home/vagrant/.ssh、- NAT 网络通过端口转发连接,
ssh -i /Users/ada/vagjenkins/.vagrant/machines/jenkins-master/virtualbox/private_key vagrant@127.0.0.1 -p 2222 - 因为我创建了主机网络,所以还可以直接通过主机网络的 IP 地址连接,
ssh -i /Users/ada/vagjenkins/.vagrant/machines/jenkins-master/virtualbox/private_key vagrant@192.168.56.113
主节点通过 ssh 连接到从节点,同样需要 known_hosts 验证,在可以在服务器手动执行 ssh vagrant@192.168.56.115 验证再到 jenkins 启动节点。
8 Troubleshooting
- Jenkins 出现更新出错的问题,一般就是 /etc/resolv.conf 解析出问题,可以用以下的方式查看: curl -I –connect-timeout 10 -m 20 https://updates.jenkins.io/update-center.json
参考文献 [1] what-is-scrum. https://www.scrum.org/resources/what-is-scrum [2] 如何从零开始搭建 CI/CD 流水线 https://www.infoq.cn/article/WHt0wFMDRrBU-dtkh1Xp