场景
jenkins master运行在物理机A的容器a中,jenkins agent运行在物理机B的容器b上,A和B是同一个局域网
当使用JNLP协议时,agent启动,主动连接master时闪退,报错port not reachable。(可能的表现:不用websocket连接则必然闪退;如果给节点连接启用websocket,则可能能连上,但很容易断开)
报错信息类似这样
baozhenchen@SC02CW70JMD6T Documents % java -jar agent.jar -jnlpUrl http://10.12.78.42:9001/computer/ccc/slave-agent.jnlp -secret a199192028ed581db11ca1a2a435e5c8fedb88ed948c7eb37edb4ad302908f39 -auth jagent:jagent -workDir "~/workspace"
十一月 22, 2020 3:28:09 下午 org.jenkinsci.remoting.engine.WorkDirManager initializeWorkDir
信息: Using ~/workspace/remoting as a remoting work directory
十一月 22, 2020 3:28:09 下午 org.jenkinsci.remoting.engine.WorkDirManager setupLogging
信息: Both error and output logs will be printed to ~/workspace/remoting
十一月 22, 2020 3:28:10 下午 hudson.remoting.jnlp.Main createEngine
信息: Setting up agent: ccc
十一月 22, 2020 3:28:10 下午 hudson.remoting.jnlp.Main$CuiListener <init>
信息: Jenkins agent is running in headless mode.
十一月 22, 2020 3:28:10 下午 hudson.remoting.Engine startEngine
信息: Using Remoting version: 4.2
十一月 22, 2020 3:28:10 下午 org.jenkinsci.remoting.engine.WorkDirManager initializeWorkDir
信息: Using /Users/baozhenchen/Documents/~/workspace/remoting as a remoting work directory
十一月 22, 2020 3:28:10 下午 hudson.remoting.jnlp.Main$CuiListener status
信息: Locating server among [http://10.12.78.42:9001/]
十一月 22, 2020 3:28:10 下午 org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver resolve
信息: Remoting server accepts the following protocols: [JNLP4-connect, Ping]
十一月 22, 2020 3:28:10 下午 org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver isPortVisible
警告: Connection refused (Connection refused)
十一月 22, 2020 3:28:10 下午 hudson.remoting.jnlp.Main$CuiListener error
严重: http://10.12.78.42:9001/ provided port:9101 is not reachable
java.io.IOException: http://10.12.78.42:9001/ provided port:9101 is not reachable
at org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver.resolve(JnlpAgentEndpointResolver.java:314)
at hudson.remoting.Engine.innerRun(Engine.java:690)
at hudson.remoting.Engine.run(Engine.java:518)
如果agent在物理机A中的容器启动(和master同一台物理机),则可以成功连接
根本原因
jenkins master容器使用的是bridge的网络模式,导致不能在主机以外连接jnlp。
agent会先尝试连接jenkins master的HTTP端口,这个端口因为做了映射,可以正确访问。通过HTTP端口来得知jenkins master的jnlp端口和ip地址。
而master不知道自己在容器里运行,他告知agent客户端的主机ip地址在bridge网络模式下是内网地址,只有物理机A可见,对于物理机B来讲不可访问,因而连接失败(报错有误导性)。
这个错误,和redis集群、couchbase集群不能使用bridge,要求使用host模式连接的道理是一样的。
jenkins master容器改为host模式连接就可以解决问题。
验证猜想
当我改为host模式,且不映射端口号时,agent通过访问物理机的8080端口,就可以正确的连上jenkins master,因而验证了jenkins master必须采用host网络模式部署的猜想。
但是,这里有个困难,host模式不支持端口映射,在物理机A上如果启动多个jenkins master容器,则端口号会冲突,不能满足我的定制端口号的需求。因而若要在主机A上面启动两个jenkins master,就只能通过注入环境变量的方式修改启动命令。
搜索过jenkins修改端口号,发现要更改jenkins配置文件。但要在容器启动前修改配置文件(还要持久化,要么就重新打一个容器–但又不通用),这样复杂的操作并不理想。
谷歌之,发现解法不好找,主要是提出这类问题的人很多,但是各种解决方案并不可用,或者是使用场景不能满足我的要求。
探索解决
此问题臭名昭著,在stackoverflow很多(大部分不需要映射端口,或者是使用多端口启动),但为我所用者少。故在拼凑了很多信息,翻看了jenkins/jenkins镜像中的启动命令的源码后,想到方法并解决了这个问题,特别记录之。
docker exec进入容器
root@sg2-shopee-cloud-devvm-test-10-129-140-26:/home/ld-sgdev/baozhen_chen# docker exec -it ed34114ca8c1 /bin/bash
[root@sg2-shopee-cloud-devvm-test-10-129-140-26 /]#
在容器里ps -ef观察启动脚本(不使用cat管道,则不能显示换行的文本)
[root@sg2-shopee-cloud-devvm-test-10-129-140-26 jenkins]# ps -ef | cat
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Nov18 ? 00:00:07 /sbin/tini -- /usr/local/bin/jenkins.sh
root 6 1 0 Nov18 ? 00:28:20 java -Duser.home=/var/jenkins_home -Djenkins.model.Jenkins.slaveAgentPort=50000 -jar /usr/share/jenkins/jenkins.war
root 7628 0 0 14:13 pts/0 00:00:00 /bin/bash
root 7733 7628 0 14:55 pts/0 00:00:00 ps -ef
root 7734 7628 0 14:55 pts/0 00:00:00 cat
发现是由/usr/local/bin/jenkins.sh脚本作为入口,拉起了java [参数] -jar /usr/share/jenkins/jenkins.war,后者才是真正的启动命令,其中50000刚好是JNLP的端口号,看下是怎么样从/usr/local/bin/jenkins.sh里传进去的
查看启动脚本的源码部分
[root@sg2-shopee-cloud-devvm-test-10-129-140-26 /]# cat /usr/local/bin/jenkins.sh
#! /bin/bash -e
: "${JENKINS_WAR:="/usr/share/jenkins/jenkins.war"}"
: "${JENKINS_HOME:="/var/jenkins_home"}"
: "${COPY_REFERENCE_FILE_LOG:="${JENKINS_HOME}/copy_reference_file.log"}"
: "${REF:="/usr/share/jenkins/ref"}"
touch "${COPY_REFERENCE_FILE_LOG}" || {
echo "Can not write to ${COPY_REFERENCE_FILE_LOG}. Wrong volume permissions?"; exit 1; }
echo