我正在尝试从Jenkins Pipeline中的Docker容器内部执行SSH命令。我正在使用CloudBees Docker Pipeline插件来旋转容器并执行命令,以及SSH Agent插件来管理我的SSH键。这是我的jenkinsfile的基本版本:
node {
step([$class: 'WsCleanup'])
docker.image('node').inside {
stage('SSH') {
sshagent (credentials: [ 'MY_KEY_UUID' ]) {
sh "ssh -vvv -o StrictHostKeyChecking=no ubuntu@example.org uname -a"
}
}
}
}
当SSH命令运行时,我会收到此错误:
+ ssh -vvv -o StrictHostKeyChecking=no ubuntu@example.org uname -a
No user exists for uid 1005
我通过日志梳理,并意识到Docker Pipeline插件会自动告诉容器通过将UID传递给uid作为命令行参数的同一用户运行:
$ docker run -t -d -u 1005:1005 [...]
我决定通过在每个环境中运行cat /etc/passwd
来检查主机和容器中存在的用户。果然,每个用户列表的列表都不同。1005是主机上的Jenkins用户,但是容器中不存在UID。为了解决该问题,我将/etc/passwd
从主机插入容器时将其旋转到容器:
node {
step([$class: 'WsCleanup'])
docker.image('node').inside('-v /etc/passwd:/etc/passwd') {
stage('SSH') {
sshagent (credentials: [ 'MY_KEY_UUID' ]) {
sh "ssh -vvv -o StrictHostKeyChecking=no ubuntu@example.org uname -a"
}
}
}
}
@nathan-Thompson提供的解决方案很棒,但是就我而言,即使在主机机器的/etc/passwd
中,我也找不到用户!这意味着安装passwd
文件没有解决问题。这个问题https://superuser.com/questions/580148/580148/users-not-found-in-in-etc-passwd建议某些用户使用诸如LDAP这样的身份提供商在主机中登录。
解决方案是找到一种将适当的线添加到容器上的passwd
文件中的方法。在主机上调用getent passwd $USER
将为运行容器的Jenkins用户提供passwd
行。
我在节点上添加了一个步骤(而不是Docker代理)以获取行并将其保存在文件中。然后在下一步中,我将生成的passwd
安装到容器中:
stages {
stage('Create passwd') {
steps {
sh """echo $(getent passwd $USER) > /tmp/tmp_passwd
"""
}
}
stage('Test') {
agent {
docker {
image '*******'
args '***** -v /tmp/tmp_passwd:/etc/passwd'
reuseNode true
registryUrl '*****'
registryCredentialsId '*****'
}
}
steps {
sh """ssh -i ********
"""
}
}
}
我刚刚找到了我想分享的另一个解决方案。它与现有解决方案区分开来,因为它允许以一个代理而不是每个阶段运行完整的管道。
诀窍是,而不是直接使用图像,而是请参考Dockerfile(可能是构建FROM
原件),然后添加用户:
# Dockerfile
FROM node
ARG jenkinsUserId=
RUN if ! id $jenkinsUserId; then
usermod -u ${jenkinsUserId} jenkins;
groupmod -g ${nodeId} jenkins;
fi
// Jenkinsfile
pipeline {
agent {
dockerfile {
additionalBuildArgs "--build-arg jenkinsUserId=$(id -u jenkins)"
}
}
}
agent {
docker {
image 'node:14.10.1-buster-slim'
args '-u root:root'
}
}
environment {
SSH_deploy = credentials('e99988ea-6bdc-45fc-b9e1-536b875bcac7')
}
stage('build') {
steps {
sh '''#!/bin/bash
eval $(ssh-agent -s)
cat $SSH_deploy | tr -d 'r' | ssh-add -
touch .env
echo 'REACT_APP_BASE_API = "//172.22.132.115:8080"' >> .env
echo 'REACT_APP_ADMIN_PANEL_URL = "//172.22.132.115"' >> .env
yarn install
CI=false npm run build
ssh -t -o StrictHostKeyChecking=no root@172.22.132.115 'rm -rf /usr/local/src/build'
scp -r -o StrictHostKeyChecking=no build root@172.22.132.115:/usr/local/src/
ssh -t -o StrictHostKeyChecking=no root@172.22.132.115 'systemctl restart nginx'
'''
}
来自内森·汤普森(Nathan Thompson)提供的解决方案,我将其修改为Jenkins Docker构建容器,该容器在Jenkins Docker-Slave内运行。#docker中的docker
if (validated_parameters.custom_gradle_image){
docker.image(validated_parameters.custom_gradle_image).inside(" -v /etc/passwd:/etc/passwd -v /var/lib/jenkins/.ssh/:/var/lib/jenkins/.ssh/ "){
sshagent(['jenkins-git-io']){
sh "${gradleCommand}"
}