Kubernetes Jenkins动态创建Slave
0、前言
首先,我们考虑个问题,为何需要jenkins slave?其实在生产环境中,如果用单master,除非你单机器的配置特别高并且构建次数不多情况下,可以不考虑使用slave,但是,在构建次数上百次并且jenkins master运行在kubernetes环境中,借助kubernetes的灵活性,强烈推荐使用slave,master负责自动创建Slave Pod,然后将任务推送给Slave Pod,任务执行完毕后,Slave Pod会自动被回收/销毁。
创建slave流程图:
1、Jenkins部署
jenkins交付进kubernetes
1.准备镜像文件
$ docker pull jenkins/jenkins:2.204.1
$ docker tag a3f949e5ebfd harbor.od.com/infra/jenkins:v2.204.1
$ docker push harbor.od.com/infra/jenkins:v2.204.1
2.资源配置文件
- rbac
$ vi /data/k8s-yaml/jenkins_slave/rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins
namespace: infra
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: jenkins
rules:
- apiGroups: ["extensions", "apps"]
resources: ["deployments"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
resources: ["services"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get","list","watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: jenkins
namespace: infra
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins
namespace: infra
Note:Jenkins会自动创建slave pod,所以需要给jenkins绑定Kubernetes的RBAC权限
- Deployment
$ vi /data/k8s-yaml/jenkins_slave/dp.yaml
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: jenkins
namespace: infra
labels:
name: jenkins
spec:
replicas: 1
selector:
matchLabels:
name: jenkins
template:
metadata:
labels:
app: jenkins
name: jenkins
spec:
serviceAccount: jenkins
volumes:
- name: data
nfs:
server: hdss7-200.host.com
path: /data/nfs-volume/jenkins_home
containers:
- name: jenkins
image: harbor.od.com/infra/jenkins:v2.204.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: web
protocol: TCP
- containerPort: 50000
name: agent
protocol: TCP
env:
- name: JAVA_OPTS
value: "-Xms1G -Xmx1G -XX:PermSize=512m -XX:MaxPermSize=1024m -Duser.timezone=Asia/Shanghai"
- name: TRY_UPGRADE_IF_NO_MARKER
value: "true"
volumeMounts:
- name: data
mountPath: /var/jenkins_home
imagePullSecrets:
- name: harbor
securityContext:
runAsUser: 0
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
revisionHistoryLimit: 7
progressDeadlineSeconds: 600
- Service
$ vi /data/k8s-yaml/jenkins_slave/svc.yaml
kind: Service
apiVersion: v1
metadata:
name: jenkins
namespace: infra
spec:
ports:
- name: web
port: 80
targetPort: 8080
protocol: TCP
- name: agent
port: 50000
targetPort: 50000
protocol: TCP
selector:
app: jenkins
- Ingress
$ vi /data/k8s-yaml/jenkins_slave/ingress.yaml
kind: Ingress
apiVersion: extensions/v1beta1
metadata:
name: jenkins
namespace: infra
spec:
rules:
- host: jenkins.od.com
http:
paths:
- path: /
backend:
serviceName: jenkins
servicePort: 80
Note:这里的Service对外暴露了80以及50000端口,80作为Jenkins Server Web端口,50000位创建的Jenkins Salve和Master建立通信连接的默认端口,如果该端口不暴露的话,Slave就无法和Master建立连接。
3.应用资源配置清单
$ kubectl apply -f http://k8s-yaml.od.com/jenkins_slave/rbac.yaml
$ kubectl apply -f http://k8s-yaml.od.com/jenkins_slave/dp.yaml
$ kubectl apply -f http://k8s-yaml.od.com/jenkins_slave/svc.yaml
$ kubectl apply -f http://k8s-yaml.od.com/jenkins_slave/ingress.yaml
2、配置jenkins动态slave
初始化jenkins后,需要安装kubernetes 插件。
1.安装插件完成之后,点击 Manage Jenkins —> Configure System —> (拖到最下方)Add a new cloud —> 选择 Kubernetes,然后填写 Kubernetes 和 Jenkins 配置信息
填写kubernetes集群内部访问地址:https://kubernetes.default.svc.cluster.local,点击Test Connection,如果出现 Connection test successful 的提示信息证明 Jenkins 已经可以和 Kubernetes 系统正常通信了,然后下方的 Jenkins URL 地址:http://jenkins.infra.svc.cluster.local:80
为何我这里使用k8s svc的名称使用方式,为了方便通过DNS记录能够解析成该Service的Cluster IP。
2.创建Pipeline动态构建测试
创建一个类型为Pipeline类型Job命名为test-slave,然后在Pipeline脚本填写下面一个简单的测试脚本
def label = "jenkins-slave-${UUID.randomUUID().toString()}"
podTemplate(label: label, cloud: 'kubernetes') {
node(label) {
stage('Run shell') {
sh 'sleep 10s'
sh 'echo hello world.'
}
}
}
3.点击构建流水线
可以看到在jenkins的namespaces下自动创建了对应的agent pod 相当于就是一个jenkins 的node 当任务执行完成这个pod会自动退出这个pod默认会去pull一个jenkins/jnlp-slave:x.xx-xx-alpine
的镜像
[root@hdss7-21 ~]# kubectl get pods -n infra
NAME READY STATUS RESTARTS AGE
jenkins-77b9c47874-qjgfd 1/1 Running 1 13h
jenkins-slave-c07daa7b-31ef-41ea-825e-05c9c721edad-sb7h6-lpgwv 1/1 Running 0 18s
3、dubbo服务构建
我们在构建dubbo服务的时候,需要编译dubbo后制作镜像并推送到harbor,这时候就要用到maven和docker命令,所以需要自己去构建基础镜像,然后在pipeline里调用
注意:这里并不实现如何将dubbo服务交付到k8s,而是演示实现动态jenkins创建slave去构建dubbo服务
3.1、制作dubbo镜像底包
底包需要有通用性,所有的dubbo微服务制作镜像时都会引用这个底包来制作,该底包主要实现一些功能,例如jmx监控(jmx_javaagent),和dubbo微服务的启动脚本,以及依赖的jdk环境
1.准备镜像(jdk环境)
$ docker pull stanleyws/jre8:8u112
$ docker tag stanleyws/jre8:8u112 harbor.od.com/public/jre:8u112
$ docker push harbor.od.com/public/jre:8u112
2.自定义Dockerfile
- /data/dockerfile/jre8/Dockerfile
FROM harbor.od.com/public/jre:8u112
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\
echo 'Asia/Shanghai' >/etc/timezone
ADD config.yml /opt/prom/config.yml
ADD jmx_javaagent-0.3.1.jar /opt/prom/
WORKDIR /opt/project_dir
ADD entrypoint.sh /entrypoint.sh
CMD ["/entrypoint.sh"]
- config.yml(这是jmx_agent读取的配置文件)
---
rules:
- pattern: '.*'
- jmx_javaagent-0.3.1.jar
# 采集jvm监控数据的jar包
$ wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.3.1/jmx_prometheus_javaagent-0.3.1.jar
- entrypoint.sh(不要忘了给该文件执行权限)
#!/bin/sh
M_OPTS="-Duser.timezone=Asia/Shanghai -javaagent:/opt/prom/jmx_javaagent-0.3.1.jar=$(hostname -i):${M_PORT:-"12346"}:/opt/prom/config.yml"
C_OPTS=${C_OPTS}
JAR_BALL=${JAR_BALL}
exec java -jar ${M_OPTS} ${C_OPTS} ${JAR_BALL}
按照上面的java启动选项解释
-agentpath:<pathname>[=<options>] // 加载java代理
-Duser.timezone:<timezone> // 指定时区
C_OPTS=${C_OPTS} // 额外的启动参数,默认为空,可在k8s资源配置清单中添加额外参数
JAR_BALL=${JAR_BALL} // 启动的jar包名字,在k8s资源配置清单中指定
3.制作dubbo服务docker底包
$ ls -l
total 372
-rw-r--r-- 1 root root 405 Jan 16 15:26 Dockerfile
-rw-r--r-- 1 root root 41 Jan 16 15:28 config.yaml
-rwxr-xr-x 1 root root 234 Jan 16 15:37 entrypoint.sh
-rw-r--r-- 1 root root 367417 May 10 2018 jmx_prometheus_javaagent-0.3.1.jar
$ docker build . -t harbor.od.com/base/jre8:8u112
$ docker push harbor.od.com/base/jre8:8u112
3.2、制作slave基础镜像
我们现在有个dubbo项目,需要用到maven构建项目,然后通过Docker打包成镜像并推送到Harbor,所以需要用到两个镜像,Maven以及Docker镜像。
3.2.1、Maven镜像
该镜像主要用于构建java应用,这里就选择:maven:v3.3.9-jdk8
准备镜像文件(推送到本地仓库)
$ docker pull maven:3.3.9-jdk-8-alpine
$ docker tag dd9d4e1cd9db harbor.od.com/public/maven:v3.3.9-jdk8
$ docker push harbor.od.com/public/maven:v3.3.9-jdk8
3.2.2、Docker镜像
该镜像主要用于将dubbo项目打包成镜像并推送到harbor,但需要定制化一下镜像,需要将一台已经实现docker login 登录到harbor仓库所生成的配置文件,路径为:/root/.docker/config.json
,与原始Docker镜像一起打包生成新的Docker镜像并推送到本地仓库。
1.准备镜像文件
$ docker pull docker:19.03
$ docker tag e036013d6d10 harbor.od.com/public/docker:v19.03
$ docker push harbor.od.com/public/docker:v19.03
2.Dockerfile
- vim /data/dockerfile/docker/Dockerfile
FROM harbor.od.com/public/docker:v19.03
USER root
ADD config.json /root/.docker/config.json
3.将/root/.docker/config.json
文件拷贝到Dockerfile目录下
{
"auths": {
"harbor.od.com": {
"auth": "YWRtaW46SGFyYm9yMTIzNDU="
}
},
"HttpHeaders": {
"User-Agent": "Docker-Client/19.03.6 (linux)"
}
}
需要通过该文件才能访问到harbor仓库
4.制作并推送镜像(推送到本地仓库)
$ docker build ./ -t harbor.od.com/public/docker:v19.03
$ docker push harbor.od.com/public/docker:v19.03
3.3、添加git key
我们这里使用到了Jenkins的Git插件来拉取代码,所以需要先创建一堆密钥,然后将公钥添加到git仓库,再将私钥添加到jenkins凭证,如下:
3.4、创建dubbo流水线
1.添加参数化构建
The String Parameter:app_name
Describe:项目名称,例如dubbo-service
String Parameter:image_name
Describe:docker镜像名称,格式:<仓库名>/<镜像名> 例如:app/dubbo-demo-service
String Parameter:git_repo
Describe:项目所在的git中央仓库的地址:如https://gitee.com/jasonminghao/dubbo-demo-service.git
String Parameter:git_ver
Describe:项目所在git中央仓库对应项目的分支或者版本号,例如master分支:*/master,commit ID:903b4e6
String Parameter:image_ver
Describe:镜像版本和git_ver一致即可,但是不要有任何的特殊符号
String Parameter:add_tag
Describe:docker镜像标签的一部分,日期时间戳,例如:20200121_1734
String Parameter:target_dir
Default:./target
Describe:编译项目的目录,产生jar/war包所在的目录
String Parameter:mvn_cmd
Default:mvn clean package -Dmaven.test.skip=true
Describe:执行mvn编译所用的命令
Choice Parameter:base_image
Choice Value: base/jre8:8u112
Describe:项目使用的docker底包镜像
2.pipeline
podTemplate(cloud:'kubernetes',containers: [
containerTemplate(
name: 'maven',
image: 'harbor.od.com/public/maven:v3.3.9-jdk8',
ttyEnabled: true,
command: 'cat'),
containerTemplate(
name: 'docker',
ttyEnabled: true,
image: 'harbor.od.com/public/docker:v19.03'),
],
volumes: [
nfsVolume(mountPath: '/root/.m2', readOnly: false, serverAddress: 'hdss7-200.host.com', serverPath: '/data/nfs-volume/maven_repo/'),
hostPathVolume(hostPath: '/run/docker.sock', mountPath: '/run/docker.sock')
]
){
node(POD_LABEL) {
stage('Get a Maven project') {
// 从git仓库拉取代码
checkout([$class: 'GitSCM', branches: [[name: "${params.git_ver}"]], browser: [$class: 'GitLab', repoUrl: ''], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'git', name: 'git', refspec: 'refs/changes/*:refs/changes/*', url: "${params.git_repo}"]]])
container('maven') {
stage('Build a Maven project') {
// 执行maven构建
sh "${params.mvn_cmd}"
}
}
}
stage('Docker build') {
container('docker') {
stage('create dir') {
// /tmp目录创建一个临时用于构建镜像的工作目录,将jar包移动到该目录
sh "mkdir /tmp/${params.app_name}"
sh "cd ${params.target_dir} && mkdir /tmp/${params.app_name}/project_dir && mv *.jar /tmp/${params.app_name}/project_dir"
}
stage('docker build image') {
// 动态生成Dockerfile,构建镜像并推送到harbor
sh "cd /tmp/${params.app_name}/ && ls -lha"
sh """
echo "FROM harbor.od.com/${params.base_image}" >/tmp/${params.app_name}/Dockerfile
echo "ADD ./project_dir /opt/project_dir" >>/tmp/${params.app_name}/Dockerfile
"""
sh "cd /tmp/${params.app_name}/ && pwd && docker build ./ -t harbor.od.com/${params.image_name}:${params.git_ver}_${params.add_tag} && docker push harbor.od.com/${params.image_name}:${params.git_ver}_${params.add_tag} "
}
}
}
}
}
podTemplate只适用于pipeline
3.5、执行流水线构建
1.填写对应参数
2.查看infra
名称空间下的Pod
[root@hdss7-200 harbor]# kubectl get pods -n infra
NAME READY STATUS RESTARTS AGE
apollo-portal-57bc86966d-4tr6w 1/1 Running 8 37h
dubbo-demo-slave-16-trktm-8x2d7-bw5dr 3/3 Running 0 53s
dubbo-monitor-555c94f4b7-85plg 1/1 Running 32 7d14h
jenkins-75fbb46546-f5ltc 1/1 Running 6 18h
可以看到jenkins slave会以在jenkins创建的项目名为命名来创建pod
3.构建结果
Kubernetes Jenkins动态创建Slave的更多相关文章
- k8s-jenkins x CI/CD 动态创建slave---01
jenkins CI/CD(动态创建slave)简述: 由于之前管理kubernetes集群应用发布,用的是Gitlab-CI,用作开发环境管理还可以,生产环境管理发布,缺点太多,打包速度很慢.研究新 ...
- Kubernetes之动态Jenkins slave
一.前提 本次实践前,需已完成以下过程: 1.搭建好一个Kubernetes集群(本实践为单节点集群),网上参考较多,不赘述. 2.选取kubernetes集群外的一台服务器安装 NFS服务端,并在集 ...
- kubernetes跑jenkins动态slave
使用jenkins动态slave的优势: 服务高可用,当 Jenkins Master 出现故障时,Kubernetes 会自动创建一个新的 Jenkins Master 容器,并且将 Volume ...
- TopoLVM: 基于LVM的Kubernetes本地持久化方案,容量感知,动态创建PV,轻松使用本地磁盘
正文 研发测试场景下,一般追求的是一键快速起环境,横向动态复制,一人一套,随起随用,用完即走.作为使用方,其不用关心实际的物理资源是怎样的,环境起在哪里,只要声明自己的使用需求即可.但作为方案构建者以 ...
- Kubernetes+Jenkins+Nexus+Gitlab进行CI/CD集成
前面已经完成了 二进制部署Kubernetes集群,下面进行CI/CD集成. 一.流程说明 应用构建和发布流程说明: 1.用户向Gitlab提交代码,代码中必须包含Dockerfile: 2.将代码提 ...
- 在linux上创建slave节点
在slave机器上创建一登录用户,步骤如下: 切换至/usr/sbin目录,执行useradd -m test007 -d /home/test007,test007就是我们所创建的用户 执行su+ ...
- JavaScript dom 动态创建标记
此前的大多数DOM都是用来查找元素,getElementById和getElementsByTagName都可以方便快捷的找到文档中的某个或者某些特定的元素节点,这些元素随后可以用诸如setAttri ...
- ios动态创建类Class
[Objective-C Runtime动态加载]---动态创建类Class 动态创建类Class,动态添加Class成员变量与成员函数,动态变量赋值与取值,动态函数调用等方法 a.使用objc_al ...
- winform 用户控件、 动态创建添加控件、timer控件、控件联动
用户控件: 相当于自定义的一个panel 里面可以放各种其他控件,并可以在后台一下调用整个此自定义控件. 使用方法:在项目上右键.添加.用户控件,之后用户控件的编辑与普通容器控件类似.如果要在后台往窗 ...
随机推荐
- 洛谷-P3369-普通平衡树(Treap)
题目传送门 标题说平衡树,那么应该AVL,红黑树都能过,但是这次做这题主要是学习Treap,所以花了几天搞出了这题.其他方法以后再说吧 Treap(带旋转) #include <bits/std ...
- 树形dp(最小支配集)
http://poj.org/problem?id=3659 #include<iostream> #include<cstring> #include<algorith ...
- zabbix报错
zabbix报错 原文:https://www.cnblogs.com/dbslinux/p/9465803.html https://www.cnblogs.com/losbyday/catego ...
- CF-544:部分题目总结
-------------------昨天打的重现赛,感觉是我打的发挥的最好的一场比赛了,六题都一次AC.那么就来总结一下吧 题目链接:http://codeforces.com/contest/11 ...
- jQuery选择器的效率问题
jQuery提供了功能强大,并兼容多种css版本的选择器,不过发现很多同学在使用选择器时并未注重效率的问题. a) 尽量使用Id选择器,jQuery选择器使用的API都是基于getElementByI ...
- python-倒序循环
有时候循环需要用到倒序,所以整理一下倒序循环的方法 方法1: 如果要倒序遍历访问序列中的元素,可以对该序列使用reversed() 函数,reversed函数会生成一份倒序列表的拷贝,但是不会改变原列 ...
- 4K时代,你不能不知道的HEVC
最近追的美剧更新啦!但手机没连wifi,看视频心疼流量:画面不清晰,老是卡机:真是令人苦恼不已.别着急,或许在HEVC大范围普及之后,这一切烦恼都将不复存在了. HEVC是什么?它是High Effi ...
- fiddler导出请求返回的响应数据
或者右键 选择response导出
- spring+mybatis+mysql5.7实现读写分离,主从复制
申明:请尽量与我本博文所有的软件版本保持一致,避免不必要的错误. 所用软件版本列表:MySQL 5.7spring5mybaties3.4.6 首先搭建一个完整的spring5+springMVC5+ ...
- SQL语言基础及数据库的创建
一.数据类型:1.二进制数据二进制数据以十六进制形式存储.二进制数据最多能存8000个英文字符,4000个汉字字符. 2.字符数据char:存100,不足100补足.varcha:存多少占多少. 3. ...