之前寫過一篇《Jenkins On Mesos—Jenkins上Mesos Plugin的使用》的博客,說的是Jenkins通過Mesos Plugin來實現slave節點的動態擴展和收縮。如果使用docker的人,不知道kubernetes的話,總是顯得有些尷尬,所以最近自己也開始在測試環境使用目前火熱的Kubernetes 1.8版(之前是在用Marathon+Mesos那一套)。Marathon、Mesos有的功能,Kubernetes當然也都有。這裏就記錄一下Kubernetes上實現slave節點動態增減的Kubernetes Plugin配置的一點實踐。
配置Jenkins Server
首先,需要有一套k8s集羣環境。之前已經部好了,這裏就不說k8s的搭建,只說Jenkins插件。我的k8s集羣是配置了SSL加密證書的,所以Jenkins Server要想能與k8s集羣的apiserver通信,需要先通過權限認證。k8s裏面有個Service Account的概念,配置使用Service Account來實現給Jenkins Server的授權。
Service Account:
相對於kubectl訪問apiserver時用的user account,service account方案是爲了給Pod中的process訪問k8s API提供的一種身份標識。簡單的說就是,通過service account可以實現給Pod中的進程授權訪問k8s API。
下面是配置Jenkins Server用到的yaml配置文件:
jenkins-rbac.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: jenkins
name: jenkins-admin
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: jenkins-admin
labels:
k8s-app: jenkins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: jenkins-admin
namespace: kube-system
說明:這裏是創建了一個名爲jenkins-admin的ServiceAccount,直接繼承了cluster-admin的權限。還可以根據自己實際情況,創建指定權限的ClusterRole,比如(僅供參考):
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
labels:
k8s-app: jenkins
name: jenkins
rules:
- apiGroups: ["", "extensions", "apps"]
resources:
- nodes
- nodes/proxy
- endpoints
- secrets
- pods
- deployments
- services
verbs: ["get", "list", "watch"]
jenkins.yaml
apiVersion: apps/v1beta2 # for versions before 1.8.0 use apps/v1beta1
kind: Deployment
metadata:
name: jenkins
namespace: kube-system
labels:
k8s-app: jenkins
spec:
replicas: 1
selector:
matchLabels:
k8s-app: jenkins
template:
metadata:
labels:
k8s-app: jenkins
spec:
containers:
- name: jenkins
image: my.example.com/library/centos7.4-ssh-maven-jenkins:2.19
imagePullPolicy: Always
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home
ports:
- containerPort: 8080
- containerPort: 50000
volumes:
- name: jenkins-home
hostPath:
path: /var/www/webapps/jenkins
nodeSelector:
jenkins: "true"
serviceAccount: "jenkins-admin"
注: 這裏的Jenkins鏡像我是用的自己定製的,可以替換成自己需要的鏡像。serviceAccount指定上面創建的jenkins-admin賬號。
jenkins-service.yaml
jenkins-service.yaml
---
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: jenkins
name: jenkins
namespace: kube-system
annotations:
prometheus.io/scrape: 'true'
spec:
ports:
- port: 8080
name: jenkins
nodePort: 31888
targetPort: 8080
- port: 50000
name: jenkins-agent
nodePort: 50000
targetPort: 50000
type: NodePort
selector:
k8s-app: jenkins
說明:Service配置暴露兩個端口,一個是Jenkins Server的訪問端口,這裏nodePort方式指定的是31888;另一個是Jenkins Agent通信用的端口,默認是50000,如果不暴露的話,Jenkins slave節點是無法和Jenkins Server建了連接的。注:nodePort方式默認的端口範圍是30000~32767,不包含50000端口的,可以參考這裏進行修改。
配置Kubernetes Plugin
用上面的yaml配置在k8s上將Jenkins Server運行之後,在Jenkins的【插件管理】裏面搜索Kubernetes plugin並安裝。
接下來,在【系統管理】-【系統設置】-【新增一個雲】-【Kubernetes】配置k8s的插件。
參考上圖的說明,進行配置。三個標記到的地方,是需要配置的必須信息,可以根據自己的情況進行配置。要注意的是,這裏的Name字段配的名字,後面在配置pipeline的Jenkins任務時,是需要用到的(假設這裏使用的名字叫Kubernetes)。然後點【Test Connection】,如果前面的Service Account配置的沒問題的話,就會提示“Connection successful”,否則,會有訪問apiserver的403權限報錯。
到這裏,最基本的配置其實就可以了。比如,用pipeline方式創建一個如下的Jenkins構建任務:
pipeline scripts的內容如下:
/* cloud字段指定系統設置裏配置的Kubernetes雲的名字,本例用的是:Kubernetes */
podTemplate(label: 'mypod', cloud: 'Kubernetes') {
node('mypod') {
stage('Run shell') {
sh 'echo hello world'
}
}
}
這裏是一個簡單的示例,此插件的更多pipeline寫法可以參考: https://github.com/jenkinsci/kubernetes-plugin
點擊Jenkins任務構建,觀察會自動生成作爲slave節點的Pod:
運行成功的Jenkins任務輸出日誌裏,可以看到運行的slave節點名:
等任務構建完成之後,會發現slave節點又動態的消失了:
以上,就完成了我們希望通過Kubernetes Plugin來完成Jenkins Slave節點動態創建和回收的目的。
製作自己的slave節點鏡像
上面雖然已經可以實現Jenkins Slave節點的動態創建和回收了,但是使用的是默認的slave鏡像:jenkinsci/jnlp-slave。在Jenkins觸發任務構建的時候,kubernetest plugin會先去公共的docker倉庫獲取這個鏡像,然後運行容器作爲slave節點。實際使用中,我們往往希望用自己預裝了一些軟件的鏡像來做slave節點,來完成我們需要的構建任務。這裏就可以參考jenkinsci/jnlp-slave(https://github.com/jenkinsci/docker-jnlp-slave)的製作,來做自己的slave節點鏡像。
Jenkins slave連接Server的方法
Jenkins Server和slave節點直接有幾種連接方式:ssh連接和jnlp連接。Kubernetes plugin插件用的是jnlp方式。這種方式是通過運行slave.jar,指定Jenkins Server的url參數和secret token參數,來建立連接。
jenkinsci/jnlp-slave鏡像是以jenkinsci/slave (https://github.com/jenkinsci/docker-slave)爲基礎鏡像製作的。參考這兩個鏡像的Dockerfile,做一個自己的:
FROM my.example.com/library/centos7.4-ssh-docker-maven:latest
ENV HOME /home/jenkins
ARG AGENT_WORKDIR=/home/jenkins/agent
COPY slave.jar /usr/share/jenkins/slave.jar
COPY jenkins-slave /usr/local/bin/jenkins-slave
RUN groupadd -g 1000 jenkins \
&& useradd -c "Jenkins user" -d $HOME -u 1000 -g 1000 -m jenkins \
&& chmod 755 /usr/share/jenkins \
&& chmod 644 /usr/share/jenkins/slave.jar
USER jenkins
ENV AGENT_WORKDIR=${AGENT_WORKDIR}
RUN mkdir /home/jenkins/.jenkins && mkdir -p ${AGENT_WORKDIR}
VOLUME /home/jenkins/.jenkins
VOLUME ${AGENT_WORKDIR}
WORKDIR /home/jenkins
ENTRYPOINT ["jenkins-slave"]
說明:這裏的基礎鏡像是用的自己之前製作的基於centos的鏡像(默認的jnlp-slave鏡像是基於ubuntu系做的)。slave.jar這個包,可以在自己Jenkins Server的訪問地址後添加/jnlpJars/slave.jar路徑來獲取到(如:http://your-jenkins-server/jnlpJars/slave.jar)。jenkins-slave這個腳本,可以在 https://github.com/jenkinsci/docker-jnlp-slave/blob/master/jenkins-slave 下載到。
使用docker build命令將上面的Dockerfile製作成鏡像之後,上傳到自己的docker私有倉庫來供k8s獲取就可以了。
指定使用自制的slave節點鏡像
pipeline類型的方式
要想使用自己的slave節點鏡像,在配置pipeline腳本的時候,就需要參數指明瞭。
podTemplate(label: 'mypod-1', cloud: 'Kubernetes', containers: [
containerTemplate(
name: 'jnlp',
image: 'my.example.com/library/centos7.4-ssh-docker-maven-jenkins-slave:2.19',
alwaysPullImage: true,
args: '${computer.jnlpmac} ${computer.name}'),
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
],)
{
node('mypod-1') {
stage('Task-1') {
stage('show release version') {
sh 'cat /etc/redhat-release '
}
}
}
}
說明: containerTemplate的name屬性必須叫jnlp,才能用images指定的鏡像替換默認的jenkinsci/jnlp-slave鏡像。此外,還要args參數傳遞兩個jenkins-slave運行需要的參數。
非pipeline類型的方式
如果不使用pipeline類型任務的話,要想使用kubernetes plugin的雲構建任務,還需要回到【系統設置】-【雲】-【Kubernetes】-【Add Pod Template】裏面繼續配置
這裏有兩點需要注意:Labels和Containers下的Name字段的名字配置。Labels名在配置非pipeline任務時,用來指定任務運行的節點。
Name名則必須叫jnlp,才能用指定的Docker image代替默認的jenkinsci/jnlp-slave鏡像,否則,你配的不叫jnlp的容器會被運行,但是Kubernetes plugin還是會用默認的jenkinsci/jnlp-slave鏡像與Jenkins Server建立連接。