我们有多个 Jenkins ssh 代理、多个具有不同 jdk 版本的构建容器,并且容器使用 java 将内容(声纳结果等)发布到其他服务器。
容器需要信任与主机相同的服务器(Jenkins ssh 代理)。因此容器的jdk内的java cacerts需要与主机的cacerts匹配。为此,我添加了参数:
agent {
docker {
registryUrl "https://private.registry"
registryCredentialsId "private-registry-creds"
image "build_container_jdk17"
args "-v /host/cacerts:/usr/share/jdk-17.0.6+10/lib/security/cacerts:ro"
}
}
从 Jenkinsfile 启动容器时。
问题是容器之间的jdk路径发生变化。容器内的jdk路径也存储在环境变量$JAVA_HOME中。
从容器内部获取 JAVA_HOME 环境变量以在卷命令行参数上使用的最简单方法是什么?
agent {
docker {
registryUrl "https://private.registry"
registryCredentialsId "private-registry-creds"
image "build_container_jdk17"
args "-v /host/cacerts:${CONTAINER_JAVA_HOME}/lib/security/cacerts:ro"
}
}
我尝试使用:
args '-v /host/cacerts:${JAVA_HOME}/lib/security/cacerts:ro'
希望冒号右侧的环境变量能够从容器内部进行计算。这导致“无效的卷规格”。
我认为有一种方法可以在一个阶段启动容器,设置变量并使用该变量在下一阶段重新启动。我想在尝试这个之前我会问一下,因为它看起来过于复杂,而且我可能缺少一些明显的解决方案。
您有两个选择:
docker inspect jenkins/jenkins:lts | jq -r '.[].Config.Env[] | select(contains("JAVA_HOME")) | split("=")[1]'
docker run --rm --entrypoint='printenv' jenkins/jenkins:lts JAVA_HOME
两个选项都会输出
/opt/java/openjdk
,这又可以传递到体积定义的 local 评估中
# opt 1
docker run --rm \
-v "/mnt/mypath/myfile:`docker inspect jenkins/jenkins:lts | jq -r '.[].Config.Env[] | select(contains(\"JAVA_HOME\")) | split("=")[1]'`/mypath/myfile" \
jenkins/jenkins:lts
# or opt 2
docker run --rm \
-v "/mnt/mypath/myfile:`docker run --rm --entrypoint='printenv' jenkins/jenkins:lts JAVA_HOME`/mypath/myfile" \
jenkins/jenkins:lts
选项 1 应该更快,但需要
jq
解析检查的 json 输出。
尊重引号和反引号。
在他的回答中@mbwmd 给出了两个选项来从 docker 容器内部获取环境变量。这些都是有效的,这取决于您的用例,您会选择哪一个。
我想扩展这个答案,并从 Jenkins 管道的角度提供详细信息,因为这是我问题的一部分。
当您使用管道时,管道代理也是容器的主机。 “docker检查”选项可能更可取:
pipeline {
agent { label 'jenkins_ssh_agent' }
environment {
CONTAINER_JAVA_HOME = get_container_java_home()
DOCKER_ARGS = "-v /host/cacerts:${CONTAINER_JAVA_HOME}/lib/security/cacerts:ro"
}
stages {
stage ('Build') {
agent {
docker {
label 'jenkins_ssh_agent'
registryUrl 'https://private.registry'
registryCredentialsId 'private-registry-creds'
image 'build_container_jdk17:1'
args "${DOCKER_ARGS}"
reuseNode true
}
}
steps {
script {
// java should now trusts trusted.private.server like the host does.
sh(script: 'java -jar connect.jar https://trusted.private.server/config.xml')
}
}
}
}
}
get_container_java_home() {
return sh(returnStdout: true, script: 'docker inspect --format \'' {{ range .Config.Env }}{{println .}}{{end}}\' private.registry/build_container_jdk17:1 | grep JAVA_HOME |cut -d \'=\' -f 2').trim();
}
docker检查命令源自@Vonc的答案:https://stackoverflow.com/a/30343218/6502579。如果您愿意,您也可以像 @mbwmd 在他的回答中建议的那样使用 jq 。我没有 jq 可用,想避免依赖。
此选项的优点是您不需要启动容器来获取环境变量。
缺点可能是您必须从管道运行 docker 命令。根据管道设置,这在某些情况下可能会导致问题。
选项 #2 通过使用阶段来保存环境变量来避免使用 docker 命令:
pipeline {
agent none
stages {
stage ('Checkout') {
agent {
docker {
registryUrl 'https://private.registry'
registryCredentialsId 'private-registry-creds'
image 'build_container_jdk17:1'
}
}
steps {
script {
// this command does not need java to trust any servers.
checkout(/*configure checkout*/)
env.CONTAINER_JAVA_HOME=sh(script: 'echo $JAVA_HOME', returnStdout: true).trim()
}
}
}
stage('Build') {
agent {
docker {
registryUrl 'https://private.registry'
registryCredentialsId 'private-registry-creds'
image 'build_container_jdk17:1'
args "-v /host/cacerts:${CONTAINER_JAVA_HOME}/lib/security/cacerts:ro"
}
}
steps {
script {
// java should now trusts trusted.private.server like the host does.
sh(script: 'java -jar connect.jar https://trusted.private.server/config.xml')
}
}
}
}
}
如果您正在运行多个阶段并且不需要在第一次运行容器时设置参数,那么这是一个不错的选择。