Jenkins 持续集成

  1. 1   基础
    1. 1.1   安装
    2. 1.2   主目录
    3. 1.3   节点
    4. 1.4   凭证
    5. 1.5   Pipeline
  2. 2   声明式流水线
  3. 3   构建触发器
  4. 4   通知
  5. 7   分布式部署实战
    1. 7.1    Vagrantfile
    2. 7.2   scripts
    3. 7.3   运行 master 节点的 Jenkins
    4. 7.4   加入 node 从节点
  6. 8   Troubleshooting

今天看到《Jenkins2 权威指南》出来了,马上买了一本,DevOps 相关的书籍更新太快了,这包括 Docker、Jenkins,新特性快速推出,旧版本必然快速淘汰,可以看一下 Jenkins 的版本,Jenkins 1.x 相关书籍已经严重滞后了。

1   基础

1.1   安装

  1. 作为独立的应用程序安装:(1)二进制包安装,(2)brew、yum 安装,(3)直接下载 war 包,java -jar jenkins.war。
    更新:用 brew、yum 命令更新,或者用新版的 jenkins.war 替换旧版然后重启
  2. 应用服务器安装,比如 tomcat
    更新:注意要删除一些缓存,避免过期页面被引用

1.2   主目录

不管你把 Jenkins 的 war 文件存放在哪儿,Jenkins 都把其所有重要的数据存放在一个专用的、隔离的,称为 Jenkins 主目录的目录下。在这里,Jenkins 存储关于构建服务器的配置信息、构建作业、构建产物和其他有用的信息,当然也包括你安装了的任何插件。且 Jenkins 主目录格式是跨版本向后兼容的,所以你可以自由地更新或者重新安装 Jenkins 而不影响 Jenkins 主目录。

一般主目录是 ~/.jenkins 目录,一个好的做法是为 Jenkins 创建一个特殊的用户和用户组。sudo groupadd buildsudo 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 节点
    1. 需要安装 Docker
    2. 并运行 Docker Registry 容器,并连接到Docker Registry;
    3. 安装 JDK 环境,预备运行 jenkins.war
    4. 映射 8080 端口到宿主机,访问宿主机的 localhost:8080 即是访问 master 的8080
  • node 从节点
    1. 只需安装 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

  1. 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
  2. 设置主机名

    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
  3. 启动 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
  4. 安装 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




参考文献
[1] what-is-scrum. https://www.scrum.org/resources/what-is-scrum
[2] 如何从零开始搭建 CI/CD 流水线 https://www.infoq.cn/article/WHt0wFMDRrBU-dtkh1Xp