在完成前文的jenkins server 在k8s环境部署以后,本文咱们来测试在k8s集群环境中的jenkins pipeline构建项目和更新,具体环境要求以下:
一、jenkins pipeline插件安装成功
二、要更新的应用已提早部署
三、Jenkins slave中须要有kubectl、svn、mvn客户端且环境变量设置准确
四、Jenkis slave须要能和master的api-server进行正常通讯(这里为了简便,前文构建jenkins server的时候直接对defalut这个service account作rabc受权。其余可选方案是使用~/.kube/config文件实现客户端的受权认证)
五、须要配置共享卷(slave编译完成以后把war包copy到共享卷位置)java
一、点击“jenkins ”——“新建 ”——“pipeline”,输入项目的名称,点击“OK”node
二、下拉到“pipeline”配置,填写脚本
具体脚本内容以下:web
podTemplate(name: 'jenkins-slave', cloud: 'kubernetes', namespace: 'default', label: 'jenkins-slave', serviceAccount: 'default', containers: [ containerTemplate( name: 'jenkins-slave', image: 'harbor.59iedu.com/fjhb/jenkins-slave-toolkit:2018-08-10-v1', args: '${computer.jnlpmac} ${computer.name}', ttyEnabled: true, privileged: false, alwaysPullImage:true, ) ], volumes: [ persistentVolumeClaim(mountPath: '/tmp/', claimName: 'tomcat-jcsj-data') ]) { node('jenkins-slave') { stage('svn-checkout') { container('jnlp') { sh """ svn checkout --username=yanglw --password=mypassword http://192.168.1.81/svn/fjhbjsb/k8s-pipeline-test --non-interactive """ } } stage('mvn-package') { container('jnlp') { sh """ mkdir -p /var/jenkins_home && cd k8s-pipeline-test && mvn clean package && cp -rpf target/*.war /tmp/ """ } } stage('restart') { container('jnlp') { sh """ pod_name=`kubectl get pods -l name=jcsj-dev -o name | cut -d"/" -f2` kubectl delete pod \$pod_name """ } } } }
三、脚本释义
podTemplate节指定建立pod的模板和环境docker
Name:为pod的名称前缀
Cloud:为构建pod的云环境,须要和前面新建的云环境名称同样
Namespace:建立pod所在的namespace
Label: 建立pod对应的标签
serviceAccount:pod使用sa,这里使用default, 因此前文建立jenkins master deployment的时候多建立了一个default-role,这样自动建立出来的的jenkins-slave具备对应的api权限shell
containerTemplate节api
指定建立容器的模板,当云环境里面配置pod模板后,容器模板以云环境的配置为准。tomcat
Volumes节安全
配置jenkins-slave pod挂载的卷,当云环境里面配置pod模板后,挂载卷以云环境配置为准app
stage节为具体的pipeline步骤webapp
这里第一步进行svn代码迁出;
第二步进行编译,并把包传到共享卷上面;
第三步对pod进行删除,由于应用采用deployment方式部署,因此能够实现从新建立pod,达到更新的效果。
在开始构建以前,有必要介绍一下jcsj-dev的环境,这个环境是须要提早部署好的。部署文件以下
# cat deploy.yml --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: jcsj-dev namespace: default spec: replicas: 1 template: metadata: labels: name: jcsj-dev spec: containers: - name: jcsj-dev image: tomcat:latest imagePullPolicy: IfNotPresent ports: - name: web containerPort: 8080 volumeMounts: - mountPath: /usr/local/tomcat/webapps name: tomcat-jcsj-data volumes: - name: tomcat-jcsj-data persistentVolumeClaim: claimName: tomcat-jcsj-data --- apiVersion: v1 kind: Service metadata: name: jcsj-dev spec: type: NodePort ports: - port: 8080 targetPort: 8080 nodePort: 8453 selector: name: jcsj-dev
# cat pv.yaml --- apiVersion: v1 kind: PersistentVolume metadata: name: tomcat-jcsj-data spec: capacity: storage: 5Gi accessModes: - ReadWriteMany nfs: path: /home/tomcat_jcsj server: 192.168.115.6 persistentVolumeReclaimPolicy: Recycle --- kind: PersistentVolumeClaim apiVersion: v1 metadata: name: tomcat-jcsj-data spec: accessModes: - ReadWriteMany resources: requests: storage: 5Gi
更新index.jsp并提交
点击“当即构建”,构建过程当中能够经过控制台输出看具体的日志信息
构建过程当中观察jenkins master的日志输出:
# kubectl logs -f jenkins-master-588b89c75f-ztvgm INFO: Started provisioning Kubernetes Pod Template from kubernetes with 1 executors. Remaining excess workload: 0 Aug 13, 2018 10:14:22 AM hudson.slaves.NodeProvisioner$2 run INFO: Kubernetes Pod Template provisioning successfully completed. We have now 2 computer(s) Aug 13, 2018 10:14:22 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Created Pod: jenkins-slave-gjjbh in namespace default Aug 13, 2018 10:14:22 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for Pod to be scheduled (0/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:28 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Container is waiting jenkins-slave-gjjbh [jnlp]: ContainerStateWaiting(message=null, reason=ContainerCreating, additionalProperties={}) Aug 13, 2018 10:14:28 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for Pod to be scheduled (1/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:34 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (1/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:35 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (2/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:36 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (3/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:37 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (4/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:38 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (5/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:39 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (6/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:40 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (7/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:41 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (8/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:42 AM hudson.TcpSlaveAgentListener$ConnectionHandler run INFO: Accepted JNLP4-connect connection #1 from /172.30.66.4:47408 Aug 13, 2018 10:14:42 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (9/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:43 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (10/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:44 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (11/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:45 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (12/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:46 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (13/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:47 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (14/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:48 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (15/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:49 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (16/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:50 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (17/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:51 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (18/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:52 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (19/100): jenkins-slave-gjjbh Aug 13, 2018 10:14:53 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher launch INFO: Waiting for agent to connect (20/100): jenkins-slave-gjjbh Aug 13, 2018 10:15:12 AM org.csanchez.jenkins.plugins.kubernetes.pipeline.ContainerExecDecorator$1 doLaunch INFO: Created process inside pod: [jenkins-slave-gjjbh], container: [jnlp] with pid:[-1] Aug 13, 2018 10:15:21 AM org.csanchez.jenkins.plugins.kubernetes.pipeline.ContainerExecDecorator$1 doLaunch INFO: Created process inside pod: [jenkins-slave-gjjbh], container: [jnlp] with pid:[-1] Aug 13, 2018 10:16:04 AM org.csanchez.jenkins.plugins.kubernetes.pipeline.ContainerExecDecorator$1 doLaunch INFO: Created process inside pod: [jenkins-slave-gjjbh], container: [jnlp] with pid:[-1] Aug 13, 2018 10:16:06 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesSlave _terminate INFO: Terminating Kubernetes instance for agent jenkins-slave-gjjbh Aug 13, 2018 10:16:06 AM jenkins.slaves.DefaultJnlpSlaveReceiver channelClosed WARNING: Computer.threadPoolForRemoting [#8] for jenkins-slave-gjjbh terminated java.nio.channels.ClosedChannelException at org.jenkinsci.remoting.protocol.impl.ChannelApplicationLayer.onReadClosed(ChannelApplicationLayer.java:208) at org.jenkinsci.remoting.protocol.ApplicationLayer.onRecvClosed(ApplicationLayer.java:222) at org.jenkinsci.remoting.protocol.ProtocolStack$Ptr.onRecvClosed(ProtocolStack.java:832) at org.jenkinsci.remoting.protocol.FilterLayer.onRecvClosed(FilterLayer.java:287) at org.jenkinsci.remoting.protocol.impl.SSLEngineFilterLayer.onRecvClosed(SSLEngineFilterLayer.java:181) at org.jenkinsci.remoting.protocol.impl.SSLEngineFilterLayer.switchToNoSecure(SSLEngineFilterLayer.java:283) at org.jenkinsci.remoting.protocol.impl.SSLEngineFilterLayer.processWrite(SSLEngineFilterLayer.java:503) at org.jenkinsci.remoting.protocol.impl.SSLEngineFilterLayer.processQueuedWrites(SSLEngineFilterLayer.java:248) at org.jenkinsci.remoting.protocol.impl.SSLEngineFilterLayer.doSend(SSLEngineFilterLayer.java:200) at org.jenkinsci.remoting.protocol.impl.SSLEngineFilterLayer.doCloseSend(SSLEngineFilterLayer.java:213) at org.jenkinsci.remoting.protocol.ProtocolStack$Ptr.doCloseSend(ProtocolStack.java:800) at org.jenkinsci.remoting.protocol.ApplicationLayer.doCloseWrite(ApplicationLayer.java:173) at org.jenkinsci.remoting.protocol.impl.ChannelApplicationLayer$ByteBufferCommandTransport.closeWrite(ChannelApplicationLayer.java:311) at hudson.remoting.Channel.close(Channel.java:1295) at hudson.remoting.Channel.close(Channel.java:1263) at hudson.slaves.SlaveComputer.closeChannel(SlaveComputer.java:708) at hudson.slaves.SlaveComputer.access$800(SlaveComputer.java:96) at hudson.slaves.SlaveComputer$3.run(SlaveComputer.java:626) at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Aug 13, 2018 10:16:06 AM org.csanchez.jenkins.plugins.kubernetes.pipeline.PodTemplateStepExecution$PodTemplateCallback finished INFO: Removing pod template and deleting pod jenkins-slave-7gnwx from cloud kubernetes Aug 13, 2018 10:16:06 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesSlave _terminate INFO: Terminated Kubernetes instance for agent default/jenkins-slave-gjjbh Aug 13, 2018 10:16:06 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesSlave _terminate INFO: Disconnected computer jenkins-slave-gjjbh Terminated Kubernetes instance for agent default/jenkins-slave-gjjbh Aug 13, 2018 10:16:06 AM org.csanchez.jenkins.plugins.kubernetes.pipeline.PodTemplateStepExecution$PodTemplateCallback finished WARNING: Failed to delete pod for agent default/jenkins-slave-7gnwx: not found Aug 13, 2018 10:16:07 AM org.jenkinsci.plugins.workflow.job.WorkflowRun finish INFO: k8s-pipeline-test #1 completed: SUCCESS
本文仅介绍的pipeline方式构建项目的过程,实际应用中还须要解决若干问题,例如:
一、svn checkout步骤须要明文配置用户名和密码,存在安全性问题
二、 每次执行构建都须要全量迁出代码,效率不高
三、要更新的应用须要提早部署好,不能适配首次编译部署场景
四、对共享卷存在依赖,解耦问题须要解决
研究了pipeline的语法结合shell脚本,基本能够解决上述问题,如下是pipeline代码
podTemplate(name: 'jenkins-slave', cloud: 'kubernetes', namespace: 'default', label: 'jenkins-slave') { node('jenkins-slave') { stage('svn-checkout') { container('jnlp') { checkout([$class: 'SubversionSCM', additionalCredentials: [], excludedCommitMessages: '', excludedRegions: '', excludedRevprop: '', excludedUsers: '', filterChangelog: false, ignoreDirPropChanges: false, includedRegions: '', locations: [[cancelProcessOnExternalsFail: true, credentialsId: 'ef21f2d3-cb39-468a-aa19-9175d96a365f', depthOption: 'infinity', ignoreExternalsOption: true, local: '.', remote: 'http://192.168.1.81/svn/fjhbjsb/k8s-pipeline-test']], quietOperation: true, workspaceUpdater: [$class: 'UpdateUpdater']]) } } stage('mvn-package') { container('jnlp') { sh """ mvn clean package """ } } stage('build-docker-image') { container('jnlp') { sh ''' IMAGE_NAME="harbor.59iedu.com/fjhb/jcsj:v$BUILD_NUMBER" CONATINER_NAME="jcsj-dev" echo "192.168.1.227 harbor.59iedu.com" >> /etc/hosts sed -i "s/BUILD_NUMBER/$BUILD_NUMBER/g" tomcat.yaml sed -i "s/BUILD_NUMBER=1/BUILD_NUMBER=$BUILD_NUMBER/g" rolling-update.sh docker login harbor.59iedu.com -u admin -p Harbor12345 docker build -t $IMAGE_NAME . docker push $IMAGE_NAME ''' } } stage('apply update') { container('jnlp') { sh ''' NAMESPACE=default DEPLOYMENT=tomcat-jcsj REGISTRY=harbor.59iedu.com/fjhb/jcsj:v$BUILD_NUMBER FILE=tomcat.yaml STATUS=$(kubectl get deployment -l k8s-app=$DEPLOYMENT -o name -n $NAMESPACE) if [ ! $STATUS ];then kubectl create -f $FILE else kubectl set image deployment/$DEPLOYMENT $DEPLOYMENT=$REGISTRY --namespace=$NAMESPACE fi ''' } } } }
其中,svn-checkout节代码经过“流水线语法”工具生成,同时须要给default sa用户相应的权限,不然没法实现apply update节配置
# kubectl create clusterrolebinding k8s-admin --clusterrole=cluster-admin --user=system:serviceaccount:default:default
后续将介绍maven项目的构建和自动部署!