前言:
本文仅代表笔者的个人观点。
点击播放音乐,伴随着西班牙斗牛版“难念的经”,阅读本文效果更佳。
本文参照了github上的文档和源代码:https://github.com/lbroudoux/openshift-tasks
一、古龙的七种武器与应用部署的六种武器
在武侠圈,古龙小说的七种武器非一般江湖兵器,件件精妙绝伦。有:长生剑、孔雀翎、碧玉刀、多情环、霸王枪、离别钩、一口箱子。
在IT圈,随着PaaS的兴起,应用的种类暴增。如果我们在Openshift上部署1000种应用,怎么办?
二、应用部署的困境
从开发者角度,在Openshift上部署大量的应用时,最难的事情,不是如何部署这些应用,而是如何找到最好的方法来部署这些应用。
在选择部署方式时,需要考虑如下几个因素:
应用的开发流程,比如是否已经使用Jenkins, Nexus
开发人员对于容器和CI/CD的熟悉程度;是否想将容器的dockerfile和pipeline作为代码来管理?
运维部门是否需要开发人员提供应用的基础镜像或基础模板?
开发过程中,是否需要多部门和多组织参与?
三、部署1000中应用的六种武器
在Openshift上部署应用时,我们有六种武器、也是件件杀伤力极大,分别是:默认S2I template部署、自定义S2I template部署、自定义B2I部署、在源码外构建pipeline部署、在源码中构建pipeline部署、从IDE部署。
由于篇幅有限,我们本文先介绍前两种武器:1.在源码外构建pipeline部署和 2.在源码中构建pipeline部署。
这两种部署应用的方法,都会用到Jenkis。不同点在于:
在源码外构建pipeline的方式,是jenkins的pipeline调用Openshift的S2I、BC、DC等。代码构建是在Openshift中完成;
而在源码中构建pipeline:代码构建和应用部署都在Jenkins中完成:Jenkins的Slave pod负责构建应用并部署应用。但需要注意的是,Jenkins file的触发(bc),是在Openshift中完成(通过Openshift中的Jenkins file模板)。
四、第一种武器:在源码内构建pipeline
实验中,我们部署的是一个基于JBoss EAP base image的应用,应用代码位于git代码库。
在上图中,jenkins贯穿整个CI/CD,其中包括:
获取源码----->编译----->生成应用(war包)---->拉取base image---->将应用(war包)与base image合并---->生成App Image---->部署App image。
在本实验中,涉及两个重要的配置文件:openshift-tasks-jenkinsfile和Jenkinsfile。
openshift-tasks-jenkinsfile是创建Jenkins master(执行openshift-tasks-jenkinsfile的模板时,如果项目中没有jenkins的master,会自动触发部署)。
而部署openshift-tasks-jenkins file模板的时候,会提示输APPLICATION_NAME、DEV_PROJECT、SOURCE_URL、SOURCE_REF这几个变量,这些变量会被注入到模板中的BuildConfig部分,并进行覆盖。
openshift-tasks-jenkinsfile的BuildConfig部分定义了Jenkins file的地址。
openshift-tasks-jenkinsfile带着这几个参数,继续触发Jenkin file并注入参数。Jenkins file第一行注明了调用maven,因此会触发部署jenkins maven slave pod。
接下来,在jenkins slave pod中,根据Jenkins file定义的应用的'build'、test、deployInDev三个阶段进行执行,应用的bc和dc也在Jenkins File中生成,最终完成一应用的构建。
接下来,我们先看:openshift-tasks-jenkinsfile完整的文件内容:
apiVersion: v1
kind: Template
labels:
template: openshift-tasks-jenkinsfile
metadata:
name: openshift-tasks-jenkinsfile
模板的名称
objects:
- apiVersion: v1
kind: BuildConfig
BC阶段的定义
metadata:
annotations:
labels:
application: $-jenkinsfile
name: $-jenkinsfile
spec:
source:
git:
ref: $
uri: $
type: Git
strategy:
jenkinsPipelineStrategy:
jenkinsfilePath: Jenkinsfile
type: JenkinsPipeline
type: Generic
截至到目前,Jenkinsfile的bc定义完成。jenkinsPipelineStrategy和jenkinsfilePath指定了这个bc阶段会调用的jenkins file的路径。
triggers:
- github:
secret: kJZLvfQr3hZg
type: GitHub
- generic:
secret: kJZLvfQr3hZg
type: Generic
parameters:
- description: The name for the application.
name: APPLICATION_NAME
required: true
value: jkf-tasks
通过模板部署应用的时候,提示输入应用的名称,默认名称是jkf-tasks
- description: The name of Dev project
name: DEV_PROJECT
required: true
value: ocp-tasks
通过模板部署应用的时候,提示输入Dev project的名称,默认名称是ocp-tasks
- description: Git source URI for application
name: SOURCE_URL
required: true
value: https://github.com/lbroudoux/openshift-tasks
通过模板部署应用的时候,提示输入SOURCE_URL的名称,默认名称是https://github.com/lbroudoux/openshift-tasks
- description: Git branch/tag reference
name: SOURCE_REF
value: master
通过模板部署应用的时候,提示输入SOURCE_REF的名称,默认名称是master。
接来下,查看Jenkins file完整的文件内容:
node('maven') {
代码构建调用maven
// define commands
def mvnCmd = "mvn"
// injection of environment variables is not done so set them here...
在此阶段注入参数变量对以下默认参数数值进行覆盖(从openshift-tasks-jenkinsfile template部署的时候,输入的参数变量带过来):
def sourceRef = "master"
def sourceUrl = "https://github.com/lbroudoux/openshift-tasks"
def devProject = "ocp-tasks"
def applicationName = "jkf-tasks"
以上代码定义了编译方式,使用maven、定义了源码的地址、在Openshift上的项目(构建在哪发生)、生成应用的名称。
stage 'build'
git branch: sourceRef, url: sourceUrl
sh "$ clean install -DskipTests=true"
以上代码定义了pipeline的构建阶段。调用mvn clean先清理编译环境,然后用mvn install进行构建。
stage 'test'
sh "$ test"
以上代码定义了代码测试阶段
stage 'deployInDev'
sh "rm -rf oc-build && mkdir -p oc-build/deployments"
sh "cp target/openshift-tasks.war oc-build/deployments/ROOT.war"
// clean up. keep the image stream
以上代码定义了在dev阶段部署操作:将编译好的war包,拷贝到 oc-build/deployments目录下
sh "oc project $"
以上代码调用oc client,切换项目。
sh "oc delete bc,dc,svc,route -l application=$ -n $"
// create build. override the exit code since it complains about existing imagestream
以上代码清空项目中的内容。
sh "oc new-build --name=$ --image-stream=jboss-eap70-openshift --binary=true --labels=application=$ -n $ || true"
// build image
以上代码根据jboss-eap70-openshift的base image,创建bc
sh "oc start-build $ --from-dir=oc-build --wait=true -n $"
以上代码执行构建,即根据上一步指定的base image,加上本地oc-build目录下的内容(ROOT.war),生成应用的镜像。
// deploy image
sh "oc new-app $:latest -n $"
sh "oc expose svc/$ -n $"
}
以上代码根据上一步生成的镜像,部署应用,并为应用创建root。
当然,在做maven编译的时候,需要用到pom文件,由于内容较多,不再贴出来,地址:https://github.com/stonezyg/openshift-tasks/blob/master/pom.xml。
五、第一种武器的威力验证
为了方便理解,将所有操作步骤贴出:
首先,根据yaml文件创建openshift-tasks-jenkins file模板。
#oc create -f https://raw.githubusercontent.com/lbroudoux/openshift-tasks/master/app-template-jenkinsfile.yaml -n ocp-tasks
接下来,通过模板部署jenkins master:
提示输入参数变量,这些参数,就是最终会传到Jenkinsfile的jenkins slave pod中的。这里,我们使用默认参数值。
接下来,在项目中,会部署一个Jenkins的 master pod:
我们可以设置Jenkins Master所指向的slave pod的地址:registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7
而Pipeline也被创建成功(根据jenkins file中的定义)
接下来,手工触发Pipeline:
接下来,我们关注Jenkins上的日志输出,由于信息较多,我只列出关键内容:
获取代码:
下载maven相关的pom文件:
下载构建需要的jar包:
下载完所需内容以后,进行Build,我们可以看一下build主任务:
Build成功:
接下来进入test阶段,下面内容可以看出,test阶段是调用mvn test的命令:
test成功:
接下来是的devInDev阶段:
在这个阶段,Jenkins会调用openshift的命令,创建bc和dc:
部署应用并为应用创建routes:
截至到目前,pipeline执行完毕,应用也部署成功。
我们将视角切换到Openshift的界面,pipeline已经执行成功。
接下来,我们通过浏览器访问应用的routes:
可以看到应用部署已经成功:
六、第一种武器的功效总结
此种武器主要利用Jenkins进行代码的构建、应用的部署。对于较为复杂的应用编译,使用此种方法较为合适。另外,很多IT程度较高的客户,在docker大火之前,就已经基于Jenkins实现CI/CD了。这种情况下,如果新引入Openshift平台,使用此方法较可以延续以前的IT运维习惯,学习成本也相对较低(不需要大量修改现有的Jenkins)。
此这种方法的劣势在于对于Slave Pod有一定要求,不同于开发语言,需要使用不同的slave pod。此外,很多时候,我们也需要对slave pod的镜像做一定的定制,如增加一些rpm包等。
魏新宇
"大卫分享"运营者、红帽资深解决方案架构师
专注开源云计算、容器及自动化运维在金融行业的推广
拥有红帽RHCE/RHCA、VMware VCP-DCV、VCP-DT、VCP-Network、VCP-Cloud、ITIL V3、Cobit5、C-STAR、AIX、HPUX等相关认证。
领取专属 10元无门槛券
私享最新 技术干货