是时候展示真正的贫穷了
一般来说呢,如果是一个没啥用户的服务其实系统资源开销是比较小的。而相比之下,项目构建和测试阶段花费的系统资源会高于服务运行需要的开销。
举个极端例子,一个前端的react项目,构建大概会花费2-3核的cpu外加1-2G的内存。而最终的runtime其实就是一个nginx,用户不多的话,cpu基本就没有开销,内存也就10M左右。
对于贫穷的我来说,就只能想一些奇妙的操作来省钱。
基本方向
贫穷的直接影响就是资源短缺,这里短缺的说到底就是cpu和内存资源的短缺。因此搜罗各种边边角角的资源就成了非常必要的事情。不论是各个环境的盈余资源,还是作为监控的树莓派,以及本地的资源,统统都是可以塞进一个agent的地方。
总而言之,需要随时随地,在各异的环境与条件下启动一个agent来给Jenkins使用。
网络环境各异是必然的。以测试环境为例,其网络是在家里面的子网。Jenkins master是无法直接连接到测试环境,而家里的网络却可以访问到Jenkins master。
所以,简而言之,除了利用docker抹平除了网络以外的各种环境差异,还采取主动连接master的方式解决网络问题。
实现原理
3件事情:
- 主动把Jenkins里已经下线的agent清理掉。
- 创建一个agent
- 获得agent的secret,并将agent连接到master
清理已经下线的agent
清理的脚本是由python写成。使用的是python-jenkins这个库。任务就是把所有当前的agent扫一遍,然后看看“同类”的agent下线了的,就干掉。
虽然按照正常操作,agent在收到关闭命令的时候应该会主动把自己删除掉。但我这里没有这么做。理由是,agent下线的原因可能是本地开发用笔记本合上了盖子。同时又懒得做一个独立的管理服务来做这个事情。
创建agent
这里的创建agent主要指的是,在Jenkins master上创建一个待连接的agent。创建过程通过jenkins-cli.jar
完成,具体命令是:
cat agent.xml | java -jar jenkins-cli.jar -s https://$JENKINS_HOST/ -auth $USERNAME:$TOKEN create-node $HOSTNAME
就是通过用户名以及相应的token
,以agent.xml
为模板,创建一个以机器名
为名字的agent。
这里的关键是agent.xml
如何获得。
个人的做法是,首先在Jenkins的页面上手动创建出一个agent,然后再用get-node
来获取我当前手动创建出来的agent的xml配置。
具体可以参考:https://stackoverflow.com/a/30731429/6274938
连接到master
要连接到master,首先要一个secret。也就是一个命令的事情:
curl -L -s -u $USERNAME:$TOKEN -X GET https://$JENKINS_HOST/computer/$HOSTNAME/slave-agent.jnlp | sed "s/.*<application-desc main-class=\"hudson.remoting.jnlp.Main\"><argument>\([a-z0-9]*\).*/\1/" > secret-file
大致来说,我也是抄过来的,具体来源忘了。但按说应该能够随手搜到很多类似操作。
最后连接也只是一个命令:
java -jar agent.jar -jnlpUrl https://$JENKINS_HOST/computer/$HOSTNAME/slave-agent.jnlp -secret @secret-file
部分代码
Dockerfile:
FROM openjdk:8
RUN apt-get update
RUN apt-get -y upgrade
RUN apt-get install -y apt-transport-https ca-certificates curl
RUN curl -fsSL "https://download.docker.com/linux/debian/gpg" | apt-key add
RUN echo "deb [arch=amd64] https://download.docker.com/linux/debian stretch stable" > /etc/apt/sources.list.d/docker.list
RUN apt-get update
RUN apt-get install -y docker-ce
RUN apt-get install -y python-pip
RUN pip install python-jenkins
ADD register.sh ./
RUN chmod u+x ./register.sh
ADD cleanup.py ./
ADD agent.xml ./
CMD service docker start && ./register.sh
其实可以看到这里在镜像里安装了docker和python-jenkins,而且采用了jdk8作为base image。就是一脸要docker in docker跑构建,或者使用docker.sock的样子。
register.sh:
python ./cleanup.py
wget -c https://$JENKINS_HOST/jnlpJars/jenkins-cli.jar
wget -c https://$JENKINS_HOST/jnlpJars/agent.jar
cat slave.xml | java -jar jenkins-cli.jar -s https://$JENKINS_HOST/ -auth $USERNAME:$TOKEN create-node $HOSTNAME
curl -L -s -u $USERNAME:$TOKEN -X GET https://$JENKINS_HOST/computer/$HOSTNAME/slave-agent.jnlp | sed "s/.*<application-desc main-class=\"hudson.remoting.jnlp.Main\"><argument>\([a-z0-9]*\).*/\1/" > secret-file
java -jar agent.jar -jnlpUrl https://$JENKINS_HOST/computer/$HOSTNAME/slave-agent.jnlp -secret @secret-file
这里其实没什么,就是如原理中所说的实施。
使用场景
我这里大概有3类使用场景:1. prod环境上使用;2. test环境上使用;3. 本地环境使用。三个场景其实没啥太大的差别,主要区别是:docker in docker与否,系统资源限制,网络资源限制。
Prod环境上使用
prod环境选择了privileged而不是docker.sock。主要是怕我pipeline脚本写高兴了,万一写了一个docker rm -f $(docker ps -aq)
。那我那台机器就没有了。不知道是不是因为prod的系统资源紧张,反正感觉这样要慢很多。
prod环境的系统资源相对是比较紧张的。因此agent相应的资源就会少很多。
prod环境的网络却是非常好的。访问什么都很快。
Test环境上使用
test环境选择了privileged而不是docker.sock。理由和prod环境一样,不想重新setup node。
test环境的系统资源相对宽松一些。但测试环境的机器毕竟是J1900的cpu,性能也就那样。
test环境的网络是糟糕的。由于test环境物理位置是我家,所以你懂的,干啥都得走我那套网络。延迟爆炸。
本地环境使用
本地环境使用是指:当我开启我的开发机的时候(开发机可以是一台虚拟机),将这台开发机作为Jenkins的agent来跑build。
其实原理也很简单,就是启动时脚本跑一下dockers命令就可以了。而且好处也明显,当我在开发的时候,能至少保证一个agent可用。
本地环境果断选择了docker.sock。又快又好。大不了直接docker system prune
。
本地环境的系统资源非常充沛。再怎么也是9代i7下的8核24g虚拟机。
本地环境的网络就随缘。毕竟和机器所处的网络和位置有很大关系。
我们穷人是这样的……人穷了会因为生活的压力而多多少少在生活实践上变得有点变态……