LOADING

加载过慢请开启缓存 浏览器默认开启

DevOps

2024/3/3 k8s

1. 基本理念

CODE -> BUILD -> INTEGRATE -> TEST -> DELIVER -> DEPLOY
|                                   |          |      |
<------Continuous Integration------->          |      |
|                                              |      |
<--------------Continuous Delivery------------->      |
|                                                     |
<---------------Continuous Deployment----------------->

什么是 CI/CD?

2. 安装

安装Jenkins

/var/jenkins_home为jenkins的所有配置和数据,需要注意备份。

启动:

docker run \
  -u root \
  -d \
  -p 8080:8080 \
  -p 50000:50000 \
  -v jenkins-data:/var/jenkins_home \
  -v /etc/localtime:/etc/localtime:ro \
  -v /var/run/docker.sock:/var/run/docker.sock \
  jenkinsci/blueocean

启动后需要升级到最新版本,点击右上角的通知,会提示你有最新版本可用,然后直接让它自动更新,重启就行了。

或者自己用 Dockerfile 重新打镜像,把最新版本的 war 包丢到/usr/share/jenkins/jenkins.war

测试使用

在项目根目录创建Jenkinsfile文件,添加如下内容:

pipeline {

    // 任何一个代理可用就可以执行
    agent any

    // 定义流水线的加工流程
    stages {
        // 流水线的所有阶段
        // 1. 编译
        stage('编译') {
            steps {
                echo "编译"
            }
        }

        stage('测试') {
            steps {
                echo "测试"
            }
        }

        stage('打包') {
            steps {
                echo "打包"
            }
        }

        stage('部署') {
            steps {
                echo "部署"
            }
        }

    }
}

之后直接在jenkins控制台进行打包,即可看到完整结果。

修改时区

虽然启动时修改了容器时区,但是 jenkins 默认还是会用其它的时区,这时候需要我们手动改一下。

打开系统管理 -> 脚本命令行 ,输入如下内容然后执行:

System.setProperty('org.apache.commons.jelly.tags.fmt.timeZone', 'Asia/Shanghai')

3. 插件推荐

自定义执行环境(Docker Pipeline)

在流水线中使用Docker

安装Docker Pipeline插件后,就可以使用 Docker 镜像作为某个 stage 的执行环境:

pipeline {
    // 如果 agent 为 none,则所有 stage 必须手动指定 agent
    agent none
    stages {
        stage('Back-end') {
            agent {
                docker { 
                    image 'maven:3-alpine'
                    // 挂载数据
                    args '-v $HOME/.m2:/root/.m2'
                }
            }
            steps {
                sh 'mvn --version'
            }
        }
        stage('Front-end') {
            agent {
                docker { image 'node:7-alpine' }
            }
            steps {
                sh 'node --version'
            }
        }
    }
}

挂载配置文件

由 jenkins 启动的 docker 容器,是可以读取到 jenkins-data 目录的。所以我们可以把所有配置文件放到 jenkins-data 目录:

[root@my ~]# docker volume inspect jenkins-data
[
    {
        "CreatedAt": "2024-03-03T21:15:07+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/jenkins-data/_data",
        "Name": "jenkins-data",
        "Options": null,
        "Scope": "local"
    }
]
[root@my ~]# cd /var/lib/docker/volumes/jenkins-data/_data
[root@my _data]# mkdir -r appconfig/maven
[root@my _data]# cd appconfig/maven
[root@my maven]# vi settings.xml 

之后在打包时手动指定配置文件位置:

stage('Maven打包') {

    agent {
        docker {
            image 'maven:3.8.8-amazoncorretto-17'
            // 注意这里是/var/jenkins_home,实际上在宿主机也能看到
            args '-v /var/jenkins_home/appconfig/maven/.m2:/root/.m2'
        }
    }

    steps {
        // 打包
        sh 'mvn clean package -DskipTests -s "/var/jenkins_home/appconfig/maven/settings.xml"'
    }
}

SSH执行远程命令

SSH Pipeline Steps

使用方式:

stage('Run remote command') {

    environment {
        K8S_MAIN_NODE = credentials('test-k8s-main')
    }

    steps {
        script {
            def remote = [
                    name: 'k8s-main',
                    host: '10.10.10.10',
                    user: K8S_MAIN_NODE_USR,
                    password: K8S_MAIN_NODE_PSW,
                    allowAnyHosts: true
            ]
            echo "Restarting server."
            sshCommand remote:remote, command: '~/restart.sh'
        }
    }
}

缓存node_modules

直接写了一个函数:

def resolveLastBuildVersion(String name) {
    dir("buildVersion") {
        result = sh encoding: 'UTF-8', returnStdout: true, script: "if [ -f \"${name}\" ];then cat ${name}; fi"
        return result
    }
}

def writeLastBuildVersion(String name, String hash) {
    writeFile encoding: 'UTF-8', file: "buildVersion/${name}", text: hash
}

def buildWebProject(String project) {
    checkoutRepo(project)
    dir(project) {
        String buildVersionKey = "${project}-build"
        String depVersionKey = "${project}-dep"
        String currentVersion = sh encoding: 'UTF-8', returnStdout: true, script: 'git rev-parse HEAD'
        String lastBuildVersion = resolveLastBuildVersion(buildVersionKey)
        echo "Current version is ${currentVersion}, last build version is ${lastBuildVersion}"
        if (currentVersion == lastBuildVersion) {
            int status = sh encoding: 'UTF-8', returnStatus: true, script: 'test -d dist'
            int status2 = sh encoding: 'UTF-8', returnStatus: true, script: 'test -f dist/index.html'
            if (status == 0 && status2 == 0) {
                echo "No changes found, skip build."
                return
            }
        } else {
            echo "Changes found, starting build web project."
        }

        String currentDepVersion = sh encoding: 'UTF-8', returnStdout: true, script: 'sha256sum package.json'
        String lastDepVersion = resolveLastBuildVersion(depVersionKey)
        echo "Current package.json sha256 is ${currentDepVersion}, last sha256 is ${lastDepVersion}"
        if (currentDepVersion != lastDepVersion) {
            echo "Changes found, reinstall node_modules."
            sh "npm i"
            writeLastBuildVersion(depVersionKey, currentDepVersion)
        } else {
            echo "No dependency changes found, skip install dependency."
        }
        sh "npm run build"
        writeLastBuildVersion(buildVersionKey, currentVersion)
    }
}