「Jenkins Pipeline」- SSH

问题描述

在 Jenkins Pipeline 中,需要在远程主机上执行命令。当然可以直接执行 ssh(1)命令,但是该方法可移植性较差,并非最佳实践;

该笔记将记录:在 Jenkins Pipeline 中,如何使用 SSH 命令的方法,及相关问题的解决办法;

解决方案

安装 SSH Pipeline Steps 插件:
1)插件主页:https://plugins.jenkins.io/ssh-steps/#plugin-content-sshput
2)使用文档:https://www.jenkins.io/doc/pipeline/steps/ssh-steps/
3)项目主页:https://github.com/jenkinsci/ssh-steps-plugin#sshput

远程执行命令

然后,在 Jenkins Pipeline 中使用如下代码:

def remote = [:]
remote.name = 'test'
remote.host = 'test.domain.com'
remote.user = 'root'
remote.password = 'password'
remote.allowAnyHosts = true

sshCommand remote: remote, command: "ls -lrt"

但安全性较低:因为密码不应该直接进行编码,而是应该是创建 Credentials 后,再使用代码获取。如下示例:

// 注意,我们这里跳过了创建 Credentials 的方法

// 在 Jenkinsfile 中
withCredentials([usernamePassword(credentialsId: "Your-Credentials-Id", usernameVariable: "username", passwordVariable: "password")]) {

	def remote = [:]
	remote.name = 'test'
	remote.host = 'test.domain.com'
	remote.user = username
	remote.password = password
	remote.allowAnyHosts = true

	sshCommand remote: remote, command: "ls -lrt"
}

// 在共享库中有点不同
//「共享库」与「Jenkinsfile」二者是有差别的。主要在于对变量的理解上。 由 withCredentials 生成
// 的变量是位于环境中的,所以要到 env 中获取;
class Foo {

	Script pipeline // 在 Jenkinsfile 中,以 new Foo(this)的形式实例化

	Foo(Script pipeline) {
		this.pipeline = pipeline
	}

	public void bar() {
		this.pipeline.withCredentials([usernamePassword(credentialsId: "Your-Credentials-Id", usernameVariable: "username", passwordVariable: "password")]) {

			def remote = [:]
			remote.name = 'test'
			remote.host = 'test.domain.com'
			remote.user = this.pipeline.env.username // 变量是注入到环境变量中的
			remote.password = this.pipeline.env.password // 变量是注入到环境变量中的
			remote.allowAnyHosts = true

			sshCommand remote: remote, command: "ls -lrt"
		}
	}
}

当然支持 SSH 私钥访问:

def remote = [:]
remote.name = "node-1"
remote.host = "10.000.000.153"
remote.allowAnyHosts = true

node {
    withCredentials([sshUserPrivateKey(credentialsId: 'sshUser', keyFileVariable: 'identity', passphraseVariable: '', usernameVariable: 'userName')]) {
        remote.user = userName
        remote.identityFile = identity

        stage("SSH Steps Rocks!") {
            writeFile file: 'abc.sh', text: 'ls'
            sshCommand remote: remote, command: 'for i in {1..5}; do echo -n \"Loop \$i \"; date ; sleep 1; done'
        }
    }
}

(注意,上一个示例中「Jenkinsfile」与「共享库」的差别,这里不再展开)

远程复制文件

pipeline {
    agent any
    stages {
        stage('Publish to Remote Server') {
            steps {
                withCredentials([file(credentialsId: "HOST-SSH-PRIVATE-KEY", variable: 'privateKeyFile')]) {
                    sshPut(
                        remote: [
                            name : "server name",
                            host : "192.168.1.52",
                            user : "root",
                            identityFile : privateKeyFile,
                            allowAnyHosts : true
                        ],
                        from : "./",
                        into : "/opt/"
                    )
                }
            }
        }
    }
}

相关链接

我们需要了解「Credentials Binding」插件,及对应的「Pipeline Steps Reference/Credentials Binding Plugin」手册,该插件负责获取 Jenkins 中配置的各种 Credentials 信息,以提高 Pipeline 的安全性及可移植性;

已知问题

Failed SFTP MKDIR: …

put method generates “Failed SFTP MKDIR” · Issue #218 · int128/groovy-ssh · GitHub

该错误并不会影响文件复制操作。

参考文献

Pipeline Steps Reference/SSH Pipeline Steps
SSH Steps for Jenkins Pipeline
Jenkins Pipeline ssh steps sshPut all of a file type – Stack Overflow