SSH 命令行 (Jenkins 原生)
持续集成中的 SSH 命令行插件通常用于授权构建机登录云服务器执行相应的命令与任务。通过 SSH 命令行插件,构建机可以针对云服务器执行以下操作:
- 将构建机上的文件推送到云主机;
- 将云主机上的文件拉取至构建机上;
- 远程登录云主机上执行命令;
- 将构建机上的脚本传输至云主机并执行;
- 远程登录云主机后删除文件;
参考构建计划模板
账号密码凭据 SSH Jenkinsfile 示例
pipeline {
agent any
stages {
stage('阶段-1') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: env.GIT_BUILD_REF]],
userRemoteConfigs: [[
url: env.GIT_REPO_URL,
credentialsId: env.CREDENTIALS_ID
]]])
}
}
stage('阶段名称') {
steps {
// usernamePassword 表示需要传入一个是用户名密码类型的凭据 ID
withCredentials([usernamePassword(
// REMOTE_CRED 是一个环境变量,环境变量的值就是凭据的 ID
// 当然也可以直接写固定值,credentialsId: "xxxx-xxxx-xxxxx"
credentialsId: "${REMOTE_CRED}",
passwordVariable: 'password',
usernameVariable: 'userName'
)]) {
script {
remoteConfig = [:]
remoteConfig.name = "my-remote-server"
remoteConfig.host = "${REMOTE_HOST}"
remoteConfig.allowAnyHosts = true
remoteConfig.user = userName
// SSH 登录密码
remoteConfig.password = password
writeFile(file: 'test.sh', text: 'ls')
sshCommand(remote: remoteConfig, command: 'for i in {1..5}; do echo -n \"Loop \$i \"; date ; sleep 1; done')
sshScript(remote: remoteConfig, script: 'test.sh')
sshPut(remote: remoteConfig, from: 'test.sh', into: '.')
sshGet(remote: remoteConfig, from: 'test.sh', into: 'test_new.sh', override: true)
sshRemove(remote: remoteConfig, path: 'test.sh')
}
}
}
}
stage('不使用凭据方便验证是否能成功 ssh 连接') {
steps {
script {
remoteConfig = [:]
remoteConfig.name = "my-remote-server"
remoteConfig.host = "1.1.1.1"
remoteConfig.allowAnyHosts = true
remoteConfig.port = 22
remoteConfig.user = "yourusername"
// SSH 登录密码
remoteConfig.password = "yourpassword"
writeFile(file: 'test.sh', text: 'ls')
sshCommand(remote: remoteConfig, command: 'for i in {1..5}; do echo -n \"Loop \$i \"; date ; sleep 1; done')
sshScript(remote: remoteConfig, script: 'test.sh')
sshPut(remote: remoteConfig, from: 'test.sh', into: '.')
sshGet(remote: remoteConfig, from: 'test.sh', into: 'test_new.sh', override: true)
sshRemove(remote: remoteConfig, path: 'test.sh')
}
}
}
}
}
私钥凭据 SSH Jenkinsfile 示例
如何配置免密登录
- 创建公私钥对
ssh-keygen -m PEM -t rsa -b 4096
, 会生成公钥id_rsa.pub
和私钥id_rsa
。 - 将公钥在录入到被登录机器的 ~/.ssh/authorized_keys 中,这样就可以通过私钥访问该机器了。
- 将私钥录入到 CODING 的凭据管理中。
SSH 私钥 Jenkinsfile 示例
pipeline {
agent any
stages {
stage('阶段-1') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: env.GIT_BUILD_REF]],
userRemoteConfigs: [[
url: env.GIT_REPO_URL,
credentialsId: env.CREDENTIALS_ID
]]])
}
}
stage("阶段名称") {
steps {
// sshUserPrivateKey 表示需要传入一个是 SSH 私钥类型的凭据 ID
withCredentials([sshUserPrivateKey(
// REMOTE_CRED 是一个环境变量,环境变量的值就是凭据的 ID
// 当然也可以直接写固定值,credentialsId: "xxxx-xxxx-xxxxx",
credentialsId: "${REMOTE_CRED}",
keyFileVariable: "privateKeyFilePath"
)]) {
script {
remoteConfig = [:]
remoteConfig.name = "my-remote-server"
remoteConfig.host = "${REMOTE_HOST}"
remoteConfig.allowAnyHosts = true
remoteConfig.user = userName
remoteConfig.port = 22
// SSH 私钥文件地址
remoteConfig.identityFile = privateKeyFilePath
writeFile(file: 'test.sh', text: 'ls')
sshCommand(remote: remoteConfig, command: 'for i in {1..5}; do echo -n \"Loop \$i \"; date ; sleep 1; done')
sshScript(remote: remoteConfig, script: 'test.sh')
sshPut(remote: remoteConfig, from: 'test.sh', into: '.')
sshGet(remote: remoteConfig, from: 'test.sh', into: 'test_new.sh', override: true)
sshRemove(remote: remoteConfig, path: 'test.sh')
}
}
}
}
}
}
SSH + nohup 避坑
pipeline {
agent any
stages {
stage("编译构建"){
steps {
sh "CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go"
}
}
stage("阶段名称") {
steps {
script {
remoteConfig = [:]
remoteConfig.name = "my-remote-server"
remoteConfig.host = "1.1.1.1"
remoteConfig.allowAnyHosts = true
remoteConfig.user = "root"
remoteConfig.port = 22
// SSH 登录密码
remoteConfig.password = "xxxxx"
sshPut(remote: remoteConfig, from: 'main', into: '/root')
sshCommand(remote: remoteConfig, command: 'ls main')
sshCommand(remote: remoteConfig, command: 'chmod +x /root/main && nohup /root/main > nohup.log 2>&1 &')
}
}
}
}
}
如果直接使用 nohup /root/main &
, 构建任务是不会退出的。需要改成 nohup /root/main > nohup.log 2>&1 &
。
除了使用 nohup, 更加推荐您使用 systemd 来管理您的服务。这样在 sshCommand 中的 shell 可以改成 systemctl restart xxxx
。
SSH + nohup 部署服务
通过一个简单的案例使用 SSH + nohup 停止服务、部署服务
pipeline {
agent any
stages {
stage('编译构建') {
steps {
sh '''
cat >main.go<<EOF
package main
import (
"fmt"
"log"
"net/http"
)
func index(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello golang asdfasdfasdfasdfasdfasdfasdf!")
}
func main() {
http.HandleFunc("/", index)
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
EOF
cat main.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
'''
}
}
stage('阶段名称') {
steps {
script {
remoteConfig = [:]
remoteConfig.name = "my-remote-server"
remoteConfig.host = "1.1.1.1"
remoteConfig.allowAnyHosts = true
remoteConfig.user = "root"
remoteConfig.port = 22
remoteConfig.password = "123456"
remoteConfig.logLevel="INFO"
sshRemove(remote: remoteConfig, path: "/root/main")
sshCommand(remote: remoteConfig, command: 'ls')
sshPut(remote: remoteConfig, from: 'main', into: '/root')
sshCommand(remote: remoteConfig, command: '/root/startapp.sh /root/main')
sshCommand(remote: remoteConfig, command: 'curl http://localhost:9090')
}
}
}
}
}
startapp.sh
#!/bin/bash
if [ $# -eq 0 ]; then
echo "请提供要启动的程序的路径"
exit 1
fi
ps aux | grep "$1" | grep -v grep | grep -v "startapp"
APP_PID=`ps aux | grep "$1" | grep -v grep | grep -v "startapp" | awk '{print $2}'`
echo "找到了符合要求 $1 的PID进程: $APP_PID"
if [ ! -z $APP_PID ]
then
echo "$1的进程PID为:$APP_PID"
echo "准备Kill掉PID为 $APP_PID 的进程"
kill -9 $APP_PID
sleep 3s
fi
chmod +x $1
nohup $1 > $1.log 2>&1 &
echo "启动程序成功"
拓展阅读
如需了解更多 Jenkinsfile 中关于 SSH 命令的内容,你可以查看 Jenkins 官方帮助文档。
如需了解更多 Jenkins 的 SSH 插件相关内容,你可以查看该插件的官方主页。
常见问题
问题反馈 >
2024-10-23最近更新
感谢反馈有用
感谢反馈没用
在阅读中是否遇到以下问题?*
您希望我们如何改进?*
如果您希望得到回复,请留下您的邮箱地址。