CentOS 安装部署Jenkins

起源

最近一直在忙Bearcat 的网站, 每次更新一部分内容, 都需要手动提交到 GitHub 和 Gitee, 以及打包成 Html 网站, 发布到 Coding.net, 很是麻烦. 所以研究一下自动构建看看能不能减少一些重复的工作.

安装 Jenkins

看网上有人使用 Docker 和 Nginx 直接就跑起来了, 由于个人手中服务器配置很低, 又跑了不少服务在上面, 已经入不敷出, 所以放弃 Docker 的方案.

准备实施的方案是官网直接下载包, 使用 Java 启动.

CentOS 上安装 JDK

下载

进入 Oracle 官网 下载 JDK 包. 由于需要点同意按钮后才能下载, 所以直接复制的连接可能在 CentOS 上直接 Wget 不能用, 需要先点下载, 开始下载后到下载列表去拷贝连接即可.

安装及排错

我下载的是 Linux 的 Rpm 包, 下载好后, 我直接执行了安装: rpm -ivh jdk-8u161-linux-x64.rpm.

谁料到由于服务器上面确实某个用于解压缩的底层插件链接库, 解压缩全部失败, 安装过程中大量出现:

.... /lib/ld-linux.so.2: bad ELF interpreter ....

网上找的解决方案

安装好依赖之后, 执行 java/javac -version 仍一直报错:

Error occurred during initialization of VM
java/lang/NoClassDefFoundError: java/lang/Object

网上查了一下, 说是内存不足, 也有说是缺少环境变量, 但是经排查应该都不是. 忽然觉得安装 jdk 的过程中报错, 那么应该重新安装才是.

于是重新执行 rpm -ivh jdk-8u161-linux-x64.rpm. 提示 package jdk1.8-2000:1.8.0_161-fcs.i586 is already installed 已经安装过, 那么应该是安装记录中显示安装过, 但是实际安装失败, 需要先卸载再安装:

rpm -e jdk1.8-2000:1.8.0_161-fcs.i586
rpm -ivh jdk-8u161-linux-x64.rpm

执行成功.

最后配置环境变量 ~/.bash_profile

export JAVA_HOME=/usr/java/jdk1.8.0_161
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$JAVA_HOME/bin:$PATH

执行查看版本:

java -version
javac -version

JDK 自动化安装脚本

安装完写了一个自动化脚本, 放在一个开源项目里面了, 参考此链接 里面的 JDKInstaller 目录.

官方地址下载 Jenkins

Jenkins 官网 下载 Jenkins.

我用的机器是 CentOS7, 所以选择的是”Red Hat/Fedora/CentOS”版本.

看到 CentOS 版本支持直接通过 yum 源安装, 所以直接导入 yum 源. 安装成功.

通过 Yum 安装 Jenkins 的自动化安装脚本

安装完写了一个自动化脚本, 放在一个开源项目里面了, 参考此链接 里面的 JenkinsInstaller 目录.

阅读 Jenkins 文档

官方文档W3Cschool 翻译的中文文档

启动

1
java -jar jenkins.war --httpPort=8080 >>/var/log/jenkins.log 2>&1 &

使用 Nginx 代理

upstream jenkins {
    server 127.0.0.1:49001;
}
server {
    listen 80;
    server_name jenkins.your-domain.com;

    location / {
        proxy_pass http://jenkins;
    }
}

也可以使用[官方Wiki论坛](https://wiki.jenkins.io/display/JENKINS/Running+Jenkins+behind+nginx)给出的配置, 个人感觉会更靠谱一点.

server {
  listen          80;       # Listen on port 80 for IPv4 requests

  server_name     jenkins.example.com;

  #this is the jenkins web root directory (mentioned in the /etc/default/jenkins file)
  root            /var/run/jenkins/war/;

  access_log      /var/log/nginx/jenkins/access.log;
  error_log       /var/log/nginx/jenkins/error.log;
  ignore_invalid_headers off; #pass through headers from Jenkins which are considered invalid by nginx server.

  location ~ "^/static/[0-9a-fA-F]{8}\/(.*)$" {
    #rewrite all static files into requests to the root
    #E.g /static/12345678/css/something.css will become /css/something.css
    rewrite "^/static/[0-9a-fA-F]{8}\/(.*)" /$1 last;
  }

  location /userContent {
    #have nginx handle all the static requests to the userContent folder files
    #note : This is the $JENKINS_HOME dir
    root /var/lib/jenkins/;
    if (!-f $request_filename){
      #this file does not exist, might be a directory or a /**view** url
      rewrite (.*) /$1 last;
      break;
    }
    sendfile on;
  }

  location @jenkins {
      sendfile off;
      proxy_pass         http://127.0.0.1:8080;
      proxy_redirect     default;
      proxy_http_version 1.1;

      proxy_set_header   Host              $host;
      proxy_set_header   X-Real-IP         $remote_addr;
      proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
      proxy_set_header   X-Forwarded-Proto $scheme;
      proxy_max_temp_file_size 0;

      #this is the maximum upload size
      client_max_body_size       10m;
      client_body_buffer_size    128k;

      proxy_connect_timeout      90;
      proxy_send_timeout         90;
      proxy_read_timeout         90;
      proxy_request_buffering    off; # Required for HTTP CLI commands in Jenkins > 2.54
  }

  location / {
    # Optional configuration to detect and redirect iPhones
    if ($http_user_agent ~* '(iPhone|iPod)') {
      rewrite ^/$ /view/iphone/ redirect;
    }

    try_files $uri @jenkins;
  }
}

打开浏览器访问 jenkins.your-domain.com 进一步配置

  1. 查看初始化的管理员密码

在/var/log/jenkins.log 查找类似如下内容

*************************************************************
*************************************************************
*************************************************************

Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:

1234567890qwertyuiopasdfghjklzxc     <-- 这里就是默认的初始化密码

This may also be found at: /root/.jenkins/secrets/initialAdminPassword

*************************************************************
*************************************************************
*************************************************************
  1. 为 Jenkins 创建管理员用户和密码

创建管理员账号密码, 以及邮件等信息.

  1. 使用新创建的账号密码登录 Jenkins, 开始 CI 之旅.

提示安装插件, 建议使用推荐设置可以省去一些麻烦.

推荐安装 blue ocean 插件, 用于图形化管理 pipeline, 很方便.

pipeline 的语法 - 注释说明版不能直接复制运行

Jenkinsfile (Declarative Pipeline) 声明语法的Pipeline, 还有一种node语法的, 个人感觉没有这个看着舒服...

// 整个文件, 以 `pipeline {}` 包裹
pipeline {

    // 执行环境, any任何环境一般是本机, 可以是远程节点服务器, 可以是docker服务
    agent any
    agent {
        docker { image 'node:7-alpine' }
    }

    // 全局环境变量
    environment {
        DISABLE_AUTH = 'true'
        DB_ENGINE    = 'sqlite'
    }

    // 构建步骤: Build, Test, Deploy, 可以随意添加
    stages {
        // 顺序化步骤
        stage('Build') {
            steps {
                sh 'echo "Hello World"'
                sh '''
                    echo "Multiline shell steps works too"
                    ls -lah
                '''
            }
        }

        // 并行化步骤
        stage('Install Deps') {
            parallel {
                stage('Install Deps') {
                    steps {
                        sh 'yarn install --offline'
                    }
                }
                stage('Remove Spams') {
                    steps {
                        sh '''rm -rf website website.tar.gz
                    rm -rf platform platform.tar.gz
                    rm -rf hall hall.tar.gz
                    '''
                    }
                }
            }
        }

        // 重复执行与超时时间
        stage('Test') {
            steps {
                // 重复3次
                retry(3) {
                    sh './flakey-test.sh'
                }

                // 3s 超时
                timeout(time: 3, unit: 'MINUTES') {
                    sh './health-check.sh'
                }

                sh 'echo "Fail!"; exit 1'

                // 检测测试文档
                junit 'reports/**/*.xml'
            }
        }

        stage('Deploy - Staging(适用于部署)') {
            steps {
                sh './deploy staging'
                sh './run-smoke-tests'
            }
        }
        stage('Sanity check 神志正常检查, 点击继续开始执行下面内容, 否则卡死此步骤') {
            steps {
                input "Does the staging environment look ok?"
            }
        }
        stage('Deploy - Production(产品化,正式部署)') {
            steps {
                sh './deploy production'
            }
        }
    }

    // 构建结束调用
    post {
        always {
             // 一定会执行的代码
            echo 'One way or another, I have finished'
            /* do sth cleanup */
        }
        success {
            // 成功时候执行的代码
            echo 'I succeeeded!'
        }
        unstable {
            // 不稳定的时候执行的代码 - 不稳定就是各个语句没问题, 但是测试有没通过的测试脚本
            echo 'I am unstable :/'
        }
        failure {
            // 构建过程就报错失败了
            echo 'I failed :('

            // 发送邮件给指定用户/组
            mail to: 'team@example.com',
                 subject: "Failed Pipeline: ${currentBuild.fullDisplayName}",
                 body: "Something is wrong with ${env.BUILD_URL}"

            hipchatSend message: "Attention @here ${env.JOB_NAME} #${env.BUILD_NUMBER} has failed.",
                        color: 'RED'

            slackSend channel: '#ops-room',
                      color: 'good',
                      message: "The pipeline ${currentBuild.fullDisplayName} completed successfully."
        }
        changed {
            // pipeline流程有更改后, 再次执行时会调用
            echo 'This will run only if the state of the Pipeline has changed'
            echo 'For example, if the Pipeline was previously failing but is now successful'
            echo 'Things were different before...'
        }
    }
}

Jenkins 词汇表

通用规则

  • Agent: Agent 通常是一个机器或容器,它连接到 Jenkins 主机,并在主控器指导时执行任务。
  • Artifact: 在 Build 或 Pipeline 运行期间生成的不可变文件,该文件归档到 Jenkins Master 上以供用户随后检索。
  • Build: 项目 单次执行的结果
  • Cloud: 提供动态代理 配置和分配的系统配置,例如由 Azure VM Agents 或 Amazon EC2 插件提供的配置和分配 。
  • Core: 主要的 Jenkins 应用程序(jenkins.war)提供了 可以构建 Plugins 的基本 Web UI,配置和基础。
  • Downstream: 配置 Pipeline 或项目时被触发作为一个单独的 Pipeline 或项目的执行的一部分。
  • Executor: 用于执行由节点上的 Pipeline 或 项目定义的工作的插槽。节点可以具有零个或多个配置的执行器,其对应于在该节点上能够执行多少并发项目或 Pipeline。
  • Fingerprint: 考虑全局唯一性的哈希追踪跨多个 Pipeline 或项目的工件或其他实体 的使用 。
  • Folder: 类似于文件系统上的文件夹的 Pipeline 和/或 项目 的组织容器。
  • Item: Web UI 中的实体对应于:Folder, Pipeline, or Project.
  • Job: 一个不推荐的术语,与项目同义。
  • Label: 用于分组代理的用户定义的文本,通常具有类似的功能或功能。例如 linux 对于基于 Linux 的代理或 docker 适用于支持 Docker 的代理。
  • Master: 存储配置,加载插件以及为 Jenkins 呈现各种用户界面的中央协调过程。
  • Node: 作为 Jenkins 环境的一部分并能够执行 Pipeline 或项目的机器。无论是 the Master 还是 Agents 都被认为是 Nodes。
  • Project: 用户配置的 Jenkins 应该执行的工作描述,如构建软件等。
  • Pipeline: 用户定义的连续输送 Pipeline 模型,以便更多阅读本手册中的“ Pipeline”一章。
  • Plugin: 与 Jenkins Core 分开提供的 Jenkins 功能扩展。
  • Publisher: 完成发布报告,发送通知等的所有配置步骤后的 构建的 一部分。
  • Stage: stage 是 Pipeline 的一部分,用于定义整个 Pipeline 的概念上不同的子集,例如:“构建”,“测试”和“部署”,许多插件用于可视化或呈现 Jenkins Pipeline 状态/进度。
  • Step: 单一任务从根本上讲,指的是 Jenkins 在 Pipeline 或项目中做了什么。
  • Trigger: 触发新 Pipeline 运行或构建的标准。
  • Update Center: 托管插件和插件元数据的库存,以便在 Jenkins 内部进行插件安装。
  • Upstream: 配置的 Pipeline 或项目,其触发单独的 Pipeline 或项目作为其执行的一部分。
  • Workspace: Noede 文件系统上的一次性目录, 可以由 Pipeline 或项目完成工作。在 Build 或 Pipeline 运行完成后,工作区通常会保留原样,除非在 Jenkins Master 上已经设置了特定的 Workspace 清理策略。

推荐阅读:

  1. Jenkins 官方文档
  2. W3C 翻译版文档 - 个人感觉像是 google/百度机器人翻译的, 不怎么样
  3. Jenkins+Node.js 持续集成
  4. 使用 Jenkins 自动部署 nodejs 应用
  5. 使用 Nginx 给 Jenkins 做代理
  6. 使用 Jenkins 配置 Git+Maven 的自动化构建

使用 Jenkins 做持续集成

Jenkins Github Webhook
配置 Bitbucket 到 Jenkins 的 WebHook
Gitee(Oschina)码云实现 Jenkins WebHooks 持续集成 - 使用 Generic Webhook Trigger 插件
Gitlab 利用 Webhook 实现 Push 代码后的 jenkins 自动构建

Donate - Support to make this site better.
捐助 - 支持我让我做得更好.