DevOps定义(来自维基百科): DevOps(Development和Operations的组合词)是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。

公司技术部目前几百人左右吧,但是整个技术栈还是比较落后的,尤其是DevOps、容器这一块,需要将全线打通,当时进来也主要是负责DevOps这一块的工作,应该说也是没怎么做好,其中也走了不少弯路,下面主要是自己踩过的坑吧。

一、自由风格的软件项目

主要还是基于jenkins里面构建一个自由风格的软件项目,当时参考的是阿里的codepipeline,就是对jenkins封装一层,包括创建job、立即构建、获取构建进度等都进行封装,并将需要的东西进行存库,没有想到码代码的时候,一堆的坑,比如: 1.连续点击立即构建,jenkins是不按顺序返回的,(分布式锁解决) 2.跨域调用,csrf,这个还好,不过容易把jenkins搞的无法登录(注意配置,具体可以点击这里) 3.创建job的时候只支持xml格式,还要转换一下,超级坑(xstream强行转换) 4.docker构建的时候,需要挂载宿主机的docker(想过用远程的,但效率不高) 5.数据库与jenkins的job一致性问题,任务创建失败,批量删除太慢(目前没想好怎么解决) 6.由于使用了数据库,需要检测job是否构建完成,为了自定义参数,我们自写了个通知插件,将构建状态返回到kafka,然后管理平台在进行消息处理。

完成了以上的东西,不过由于太过于简单,导致只能进行单条线的CICD,而且CI仅仅实现了打包,没有将CD的过程一同串行起来。简单来说就是,用户点击了构建只是能够打出一个镜像,但是如果要部署到kubernetes,还是需要在应用里手动更换一下镜像版本。总体而言,这个版本的jenkins我们使用的还是单点的,不足以支撑构建量比较大的情况,甚至如果当前服务挂了,断网了,整一块的构建功能都不能用。

<project>
<actions/>
<description>xxx</description>
<properties>
<hudson.model.ParametersDefinitionProperty>
<parameterDefinitions>
<hudson.model.TextParameterDefinition>
<name>buildParam</name>
<defaultValue>v1</defaultValue>
</hudson.model.TextParameterDefinition>
<hudson.model.TextParameterDefinition>
<name>codeBranch</name>
<defaultValue>master</defaultValue>
</hudson.model.TextParameterDefinition>
</parameterDefinitions>
</hudson.model.ParametersDefinitionProperty>
</properties>
<scm class="hudson.plugins.git.GitSCM">
<configVersion>2</configVersion>
<userRemoteConfigs>
<hudson.plugins.git.UserRemoteConfig>
<url>http://xxxxx.git</url>
<credentialsId>002367566a4eb4bb016a4eb723550054</credentialsId>
</hudson.plugins.git.UserRemoteConfig>
</userRemoteConfigs>
<branches>
<hudson.plugins.git.BranchSpec>
<name>${codeBranch}</name>
</hudson.plugins.git.BranchSpec>
</branches>
<doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
<extensions/>
</scm>
<builders>
<hudson.tasks.Shell>
<command>ls</command>
</hudson.tasks.Shell>
<hudson.tasks.Maven>
<targets>clean package install -Dmaven.test.skip=true</targets>
<mavenName>mvn3.5.4</mavenName>
</hudson.tasks.Maven>
<com.cloudbees.dockerpublish.DockerBuilder>
<server>
<uri>unix:///var/run/docker.sock</uri>
</server>
<registry>
<url>http://xxxx</url>
</registry>
<repoName>xxx/xx</repoName>
<forcePull>true</forcePull>
<dockerfilePath>Dockerfile</dockerfilePath>
<repoTag>${buildParam}</repoTag>
<skipTagLatest>true</skipTagLatest>
</com.cloudbees.dockerpublish.DockerBuilder>
</builders>
<publishers>
<com.xxxx.notifications.Notifier/>
</publishers>
</project>

二、优化之后的CICD

上面的过程也仍然没有没住DevOps的流程,人工干预的东西依旧很多,由于上级急需产出,所以只能将就着继续下去。我们将构建、部署每个当做一个小块,一个CICD的过程可以选择构建、部署,花了很大的精力,完成了串行化的别样的CICD。 以下图为例,整个流程的底层为:paas平台-jenkins-kakfa-管理平台(选择cicd的下一步)-kafka-cicd组件调用管理平台触发构建-jenkins-kafka-管理平台(选择cicd的下一步)-kafka-cicd组件调用管理平台触发部署。

目前实现了串行化的CICD构建部署,之后考虑实现多个CICD并行,并且一个CICD能够调用另一个CICD,实际运行中,出现了一大堆问题。由于经过的组件太多,一次cicd的运行报错,却很难排查到问题出现的原因,业务方的投诉也开始慢慢多了起来,只能说劝导他们不要用这个功能。

没有CICD,就无法帮助公司上容器云,无法合理的利用容器云的特性,更无法走上云原生的道路。于是,我们决定另谋出路。

三、调研期

由于之前的CICD问题太多,特别是经过的组件太多了,导致出现问题的时候无法正常排查,为了能够更加稳定可靠,还是决定了要更换一下底层。 我们重新审视了下pipeline,觉得这才是正确的做法,可惜不知道如果做成一个产品样子的东西,用户方Dockerfile都不怎么会写,你让他写一个Jenkinsfile?不合理!在此之外,我们看到了serverless jenkins、谷歌的tekton。 GitLab-CICD Gitlab中自带了cicd的工具,需要配置一下runner,然后配置一下.gitlab-ci.yml写一下程序的cicd过程即可,构建镜像的时候我们使用的是kaniko,整个gitlab的cicd在我们公司小项目中大范围使用,但是学习成本过高,尤其是引入了kaniko之后,还是寻找一个产品化的CICD方案。

分布式构建jenkins x 首先要解决的是多个构建同时运行的问题,很久之前就调研过jenkins x,它必须要使用在kubernetes上,由于当时官方文档不全,而且我们的DevOps项目处于初始期,所有没有使用。jenkins的master slave结构就不多说了。jenkins x应该说是个全家桶,包含了helm仓库、nexus仓库、docker registry等,代码是jenkins-x-image。

serverless jenkins 好像跟谷歌的tekton相关,用了下,没调通,只能用于GitHub。感觉还不如直接使用tekton。

阿里云云效 提供了图形化配置DevOps流程,支持定时触发,可惜没有跟gitlab触发结合,如果需要个公司级的DevOps,需要将公司的jira、gitlab、jenkins等集合起来,但是图形化jenkins pipeline是个特别好的参考方向,可以结合阿里云云效来做一个自己的DevOps产品。

微软Pipeline 微软也是提供了DevOps解决方案的,也是提供了yaml格式的写法,即:在右边填写完之后会转化成yaml。如果想把DevOps打造成一款产品,这样的设计显然不是最好的。

谷歌tekton kubernetes的官方cicd,目前已用于kubernetes的release发版过程,目前也仅仅是与GitHub相结合,gitlab无法使用,全过程可使用yaml文件来创建,跑起来就是类似kubernetes的job一样,用完即销毁,可惜目前比较新,依旧处于alpha版本,无法用于生产。有兴趣可以参考下:Knative 初体验:CICD 极速入门

四、产品化后的DevOps平台

在调研DockOne以及各个产商的DevOps产品时,发现,真的只有阿里云的云效才是真正比较完美的DevOps产品,用户不需要知道pipeline的语法,也不需要掌握kubernetes的相关知识,甚至不用写yaml文件,对于开发、测试来说简直就是神一样的存在了。云效对小公司(创业公司)免费,但是有一定的量之后,就要开始收费了。在调研了一番云效的东西之后,发现云效也是基于jenkins x改造的,不过阿里毕竟人多,虽然能约莫看出是pipeline的语法,但是阿里彻底改造成了能够使用yaml来与后台交互。 下面是以阿里云的云效界面以及配合jenkins的pipeline语法来讲解:

4.1 Java代码扫描

PMD是一款可拓展的静态代码分析器它不仅可以对代码分析器,它不仅可以对代码风格进行检查,还可以检查设计、多线程、性能等方面的问题。阿里云的是简单的集成了一下而已,对于我们来说,底层使用了sonar来接入,所有的代码扫描结果都接入了sonar。

stage('Clone') {
steps{
git branch: 'master', credentialsId: 'xxxx', url: "xxx"
}
}
stage('check') {
steps{
container('maven') {
echo "mvn pmd:pmd"
}
}
}

4.2 Java单元测试

Java的单元测试一般用的是Junit,在阿里云中,使用了surefire插件,用来在maven构建生命周期的test phase执行一个应用的单元测试。它会产生两种不同形式的测试结果报告。我这里就简单的过一下,使用"mvn test"命令来代替。


stage('Clone') {
steps{
echo "1.Clone Stage"
git branch: 'master', credentialsId: 'xxxxx', url: "xxxxxx"
}
}
stage('test') {
steps{
container('maven') {
sh "mvn test"
}
}
}

4.3 Java构建并上传镜像

镜像的构建比较想使用kaniko,尝试找了不少方法,到最后还是只能使用dind(docker in docker),挂载宿主机的docker来进行构建,如果能有其他方案,希望能提醒下。目前jenkins x使用的是dind,挂载的时候需要配置一下config.json,然后挂载到容器的/root/.docker目录,才能在容器中使用docker。

为什么不推荐dind:挂载了宿主机的docker,就可以使用docker ps查看正在运行的容器,也就意味着可以使用docker stop、docker rm来控制宿主机的容器,虽然kubernetes会重新调度起来,但是这一段的重启时间极大的影响业务。


stage('下载代码') {
steps{
echo "1.Clone Stage"
git branch: 'master', credentialsId: 'xxxxx', url: "xxxxxx"
script {
build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
}
}
}
stage('打包并构建镜像') {
steps{
container('maven') {
echo "3.Build Docker Image Stage"
sh "mvn clean install -Dmaven.test.skip=true"
sh "docker build -f xxx/Dockerfile -t xxxxxx:${build_tag} ."
sh "docker push xxxxxx:${build_tag}"
}
}
}

4.4 部署到阿里云k8s

CD过程有点困难,由于我们的kubernetes平台是图形化的,类似于阿里云,用户根本不需要自己写deployment,只需要在图形化界面做一下操作即可部署。对于CD过程来说,如果应用存在的话,就可以直接替换掉镜像版本即可,如果没有应用,就提供个简单的界面让用户新建应用。当然,在容器最初推行的时候,对于用户来说,一下子需要接受docker、kubernetes、helm等概念是十分困难的,不能一个一个帮他们写deployment这些yaml文件,只能用helm创建一个通用的spring boot或者其他的模板,然后让业务方修改自己的配置,每次构建的时候只需要替换镜像即可。

def tmp = sh (
returnStdout: true,
script: "kubectl get deployment -n ${namespace} | grep ${JOB_NAME} | awk '{print \$1}'"
)
//如果是第一次,则使用helm模板创建,创建完后需要去epaas修改pod的配置
if(tmp.equals('')){
sh "helm init --client-only"
sh """helm repo add mychartmuseum http://xxxxxx \
--username myuser \
--password=mypass"""
sh """helm install --set name=${JOB_NAME} \
--set namespace=${namespace} \
--set deployment.image=${image} \
--set deployment.imagePullSecrets=${harborProject} \
--name ${namespace}-${JOB_NAME} \
mychartmuseum/soa-template"""
}else{
println "已经存在,替换镜像"
//epaas中一个pod的容器名称需要带上"-0"来区分
sh "kubectl set image deployment/${JOB_NAME} ${JOB_NAME}-0=${image} -n ${namespace}"
}

4.5 整体流程

代码扫描,单元测试,构建镜像三个并行运行,等三个完成之后,在进行部署

pipeline:

pipeline {
agent {
label "jenkins-maven"
}
stages{
stage('代码扫描,单元测试,镜像构建'){
parallel {
stage('并行任务一') {
agent {
label "jenkins-maven"
}
stages('Java代码扫描') {
stage('Clone') {
steps{
git branch: 'master', credentialsId: 'xxxxxxx', url: "xxxxxxx"
}
}
stage('check') {
steps{
container('maven') {
echo "$BUILD_NUMBER"
}
}
}
}
}
stage('并行任务二') {
agent {
label "jenkins-maven"
}
stages('Java单元测试') {
stage('Clone') {
steps{
echo "1.Clone Stage"
git branch: 'master', credentialsId: 'xxxxxxx', url: "xxxxxxx"
}
}
stage('test') {
steps{
container('maven') {
echo "3.Build Docker Image Stage"
sh "mvn -v"
}
}
}
}
}
stage('并行任务三') {
agent {
label "jenkins-maven"
}
stages('java构建镜像') {
stage('Clone') {
steps{
echo "1.Clone Stage"
git branch: 'master', credentialsId: 'xxxxxxx', url: "xxxxxxx"
script {
build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
}
}
}
stage('Build') {
steps{
container('maven') {
echo "3.Build Docker Image Stage"
sh "mvn clean install -Dmaven.test.skip=true"
sh "docker build -f epaas-portal/Dockerfile -t hub.gcloud.lab/rongqiyun/epaas:${build_tag} ."
sh "docker push hub.gcloud.lab/rongqiyun/epaas:${build_tag}"
}
}
}
}
}
}
}
stage('部署'){
stages('部署到容器云') {
stage('check') {
steps{
container('maven') {
script{
if (deploy_app == "true"){
def tmp = sh (
returnStdout: true,
script: "kubectl get deployment -n ${namespace} | grep ${JOB_NAME} | awk '{print \$1}'"
)
//如果是第一次,则使用helm模板创建,创建完后需要去epaas修改pod的配置
if(tmp.equals('')){
sh "helm init --client-only"
sh """helm repo add mychartmuseum http://xxxxxx \
--username myuser \
--password=mypass"""
sh """helm install --set name=${JOB_NAME} \
--set namespace=${namespace} \
--set deployment.image=${image} \
--set deployment.imagePullSecrets=${harborProject} \
--name ${namespace}-${JOB_NAME} \
mychartmuseum/soa-template"""
}else{
println "已经存在,替换镜像"
//epaas中一个pod的容器名称需要带上"-0"来区分
sh "kubectl set image deployment/${JOB_NAME} ${JOB_NAME}-0=${image} -n ${namespace}"
}
}else{
println "用户选择不部署代码"
}
}
}
}
}
}
}
}
}

在jenkins x中查看:

4.4 日志

jenkins blue ocean步骤日志:

云效中的日志:

4.5 定时触发

    triggers {
cron('H H * * *') //每天
}

五、其他

5.1 Gitlab触发

pipeline中除了有对于时间的trigger,还支持了gitlab的触发,需要各种配置,不过如果真的对于gitlab的cicd有要求,直接使用gitlab-ci会更好,我们同时也对gitlab进行了runner的配置来支持gitlab的cicd。gitlab的cicd也提供了构建完后即销毁的过程。

六、总结

功能最强大的过程莫过于自己使用pipeline脚本实现,选取最适合自己的,但是对于一个公司来说,如果要求业务方来掌握这些,特别是IT流动性大的时候,既需要重新培训,同个问题又会被问多遍,所以,只能将DevOps实现成一个图形化的东西,方便,简单,相对来说功能还算强大。

DevOps最难的可能都不是以上这些,关键是让用户接受,容器云最初推行时,公司原本传统的很多发版方式都需要进行改变,有些业务方不愿意改,或者有些代码把持久化的东西存到了代码中而不是分布式存储里,甚至有些用户方都不愿意维护老代码,看都不想看然后想上容器,一个公司在做技术架构的时候,过于混乱到最后填坑要么需要耗费太多精力甚至大换血。

最后,DevOps是云原生的必经之路!!!

文章同步:

博客园:https://www.cnblogs.com/w1570631036/p/11524673.html

个人网站:http://www.wenzhihuai.com/getblogdetail.html?blogid=663

gitbook:https://gitbook.wenzhihuai.com/devops/devops-ping-tai

DevOps平台的更多相关文章

  1. DevOps平台的“精益创业”之路

    本文内容节选自第六届全球软件案例研究峰会,时任中国移动通信集团浙江有限公司罗琼老师,申健老师分享的<DevOps平台的“精益创业”之路>实录,重点分享:DevOps产品研发过程,对外实施敏 ...

  2. 在Kubernetes集群里安装微服务DevOps平台fabric8

    转载于https://blog.csdn.net/wzp1986/article/details/72128063?utm_source=itdadao&utm_medium=referral ...

  3. JFrog推出全球首个支持混合云架构,端到端的通用DevOps平台 ——JFrog Platform

            JFrog Platform,基于屡获殊荣的JFrog Artifactory制品仓库的独特能力,通过多合一的体验提供DevSecOps.CI / CD和软件分发的解决方案. 2020 ...

  4. 研讨会回放视频:如何提升Jenkins能力,使其成为真正的DevOps平台

    "如何实现集中管理.灵活高效的CI/CD"在线研讨会精彩分享 演讲嘉宾:杨海涛 在2022年3月29日举办的"如何实现集中管理.灵活高效的CI/CD"在线研讨会 ...

  5. 中国DevOps平台市场,华为云再次位居领导者位置

    摘要:华为云软件开发生产线DevCloud在市场份额和发展战略两大维度均排名第一,再次位居领导者位置. 9月21日 ,国际权威分析师机构IDC发布<IDC MarketScape: 中国 Dev ...

  6. 开源推荐 - CoDo开源一站式DevOps平台

    一群有梦想的年轻人开源了一个云管理平台,他们的口号是:让天下没有996的运维 有幸参与到CoDo项目的开发,这是一个非常棒的一站式开源运维平台,分享给大家 平台介绍 CODO是一款为用户提供企业多混合 ...

  7. 「产品运营」研发效能之DevOps平台如何运营?

    有人常说「酒香不怕巷子深」.不是的,如果这个巷子是酒吧街,那最深的那家酒吧肯定是租金最便宜的.酒吧的地段好坏已经在租金价格上体现出来了.现在已经不是那个工具缺乏.有个工具就拍手称快.欣然去试用的时代了 ...

  8. 云原生时代的DevOps平台设计之道

    开发人员与运维人员是 IT 领域很重要的两大人群,他们都会参与到各种业务系统的建设过程中去.DevOps 是近年间火爆起来的一种新理念,这种理念被很多人错误的解读为"由开发人员(Dev)学习 ...

  9. 基于jenkins,tekton等工具打造kubernetes devops平台

    本贴为目录贴,将不断更新 目录 1.Docker在centos下安装以及常见错误解决 2.使用kubernetes 官网工具kubeadm部署kubernetes(使用阿里云镜像) 3.无法访问gcr ...

随机推荐

  1. Go中的日志及第三方日志包logrus

    有别的语言使用基础的同学工作中都会接触到日志的使用,Go中自然也有log相关的实现.Go log模块主要提供了3类接口,分别是 "Print .Panic .Fatal ",对每一 ...

  2. Streaming+Sparksql使用sql实时分析 rabbitmq+mongodb+hive

    SparkConf sparkConf = new SparkConf()//此处使用一个链接切记使用一个链接否则汇报有多个sparkcontext错误 .setAppName("Spark ...

  3. 100天搞定机器学习|Day16 通过内核技巧实现SVM

    前情回顾 机器学习100天|Day1数据预处理100天搞定机器学习|Day2简单线性回归分析100天搞定机器学习|Day3多元线性回归100天搞定机器学习|Day4-6 逻辑回归100天搞定机器学习| ...

  4. cmd命令行带参启动程序

    cmd命令行带参启动程序 有一些程序不支持被直接启动,编写代码时,我们可以通过Process类来启动某个进程(某个软件),在不用代码调从而启动某个软件时,windows系统下,通常我们会用到cmd命令 ...

  5. 调用链系列(3):如何从零开始捕获body和header

    拓展阅读:调用链系列(1):解读UAVStack中的贪吃蛇 调用链系列(2):轻调用链实现 在Java中,HTTP协议的请求/响应模型是由Servlet规范+Servlet容器(如Tomcat)实现的 ...

  6. 不可错过的几款GitHub开源项目

    工作之余或者周末感觉无聊?不知道干什么?想继续提高技术,但是不知道做什么的同学,看过来,不妨利用闲暇时间来撸几个 GitHub 上还不错的开源项目,本文推荐的开源项目比较适合新手.及对MVP设计模式不 ...

  7. C/C++ 数据类型

    C/C++ 数据类型 C语言包含5个基本数据类型: void, integer, float, double, 和 char. 类型 描述 字节数 取值范围 void 空类型 1 int 整型 4 - ...

  8. Redis缓存,持久化,高可用

    一,Redis作缓存服务器 ​ 本篇博客是接着上一篇博客未分享完的技术点. ​ redis作为缓存服务器是众多企业中的选择之一,虽然该技术很成熟但也是存在一定的问题.就是缓存带来的缓存穿透,缓存击穿, ...

  9. 单元测试之NUnit一

    NUnit 分三篇文章介绍,入门者可阅读文章,有基础者直接参考官方文档.初次写博客,望大家指点. 导航: 单元测试之NUnit一 单元测试之NUnit二 单元测试之NUnit三 NUnit是什么? N ...

  10. java短信验证和注册

    最近公司需要用到短信验证注册,所以申请了阿里云的短信服务.我的项目是分布式的spring boot 原理: 利用第三方发送短信 获取回执消息,然后存入缓存里面 将用户填写的验证码与缓存里面的验证码对比 ...