本帖最后由 adminlily 于 2018-10-9 11:25 编辑
一、DevOps实战-基于Docker的CI/CD
在本篇博客,我们将使用如下技术一步步实现全部的持续部署流程。[Spring Boot, GitLab, Jenkins,Docker and Slack. ] 首先,我们会创建一个带有单元测试和集成测试的spring boot样品应用,然后将它push到GitLab.在push代码到gitlab上之后,Jenkins pipeline 将通过web hook自动获取到代码,然后运行测试用例。如果这些测试全部通过,没有任何错误信息,Jenkins将去编译代码,并且通过docker将代码部署到服务器上。最后,它将发送docker image到本地的docker私有仓库。如果整个测试流水线有任何一步失败,它将通过Slack去通知用户。
在开始持续部署操作之前,让我们先看下整个架构图: 图1:基于docker的CI/CD流水线
二、准备工作
注意:在该方案中,我们需要Spring Boot应用。你可以去github上面克隆样本应用:([ /onedaywillcome1/ContinuousIntegrationAndContinuousDeliveryApp.git]onedaywillcom ... uousDeliveryApp.git[/url])这是一个Hello World Spring Boot-Maven 应用,并且附带了一个单元测试和集成测试。
首先,我们需要在gitlab的集成菜单上设置一下去触发Jenkins.
当所有的设置都在Jenkins上设置完之后,GitLab将准备好去随时触发Jenkins。
接下来,我们需要安装Jenkins,Docker以及一些其他相关的依赖。
下图是在AWS EC2上运行的一个实例,实例的外网ip为:52.11.94.229 图3:AWS实例
现在我们就可以ssh到实例上去安装下列依赖:
# ssh to instance # Update packages sudo yum update -y # Install Git sudo yum install git -y # Download Jekins repo sudo wget -O /etc/yum.repos.d/jenkins.repo [ /redhat/jenkins.repo]redhat/jenkins.repo[/url] # Import jenkins key sudo rpm — import [ /redhat/jenkins-ci.org.key]redhat/jenkins-ci.org.key[/url] # Install Jenkins sudo yum install jenkins -y # Start Jenkins sudo service jenkins start # Enter to home directory cd ~ # Download Java 8 wget — no-cookies — no-check-certificate — header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept securebackup-cookie" "otn pub/java/jdk/8u60-b27/jdk-8u60-linux-x64.rpm" # Install Java 8 sudo yum localinstall jdk-8u60-linux-x64.rpm # Check the java version java -version # Copy JAVA_HOME to bash_profile echo "export JAVA_HOME=/usr/java/jdk1.8.0_60/jre" >> ~/.bash_profile # Run the bash_profile source ~/.bash_profile # Check JAVA_HOME is successfully installed as environment variable env # Download Maven wget [ /pub/apache/maven/maven-3/3.0.5/binaries/apache-maven-3.0.5-bin.tar.gz]pub/ ... en-3.0.5-bin.tar.gz[/url] # Untar maven gz file tar xvf apache-maven-3.0.5-bin.tar.gz #Move apache file to /usr/local dir sudo mv apache-maven-3.0.5 /usr/local/apache-maven # Copy below Maven commands to bash_profile echo "export M2_HOME=/usr/local/apache-maven" >> ~/.bash_profile echo "export M2=$M2_HOME/bin" >> ~/.bash_profile echo "export PATH=$M2:$PATH" >> ~/.bash_profile # Run the bash_profile source ~/.bash_profile # Check Maven commands successfully installed as env. variable env # Install Docker sudo yum install -y docker # Start docker sudo service docker start # Add docker user to ec2-user and jenkins sudo usermod -a -G docker ec2-usersudo usermod -a -G docker jenkins # Check docker info sudo docker info # Exit from instance exit # Ssh again # Now we are able to check info without sudo docker info # Finally restart jenkins & docker sudo service docker restart sudo service jenkins restart
Jenkins现在启动并且正常运行起来了,你可以访问[ :8080]h :8080[/url],不过需要拷贝jenkins admin的密码(jenkins启动日志中会有)去解锁jenkins并且安装一些推荐的plugins。 图4:jenkins安装
在jenkins中安装gitlab和slack插件:
跳转到([ :8080/pluginManager/available]h :8080/pluginManager/available[/url]),选择gitlab和slack plugin使用不重启安装。
在安装了slack和gitlab插件之后,我们可以接下来以下的操作(可选)。Slack插件需要有webhook配置,如果我们不去配置这个webhook,jenkins将会出现异常。因此,为了解决这个问题,我们将做如下操作(使用groovy脚本去管控): sudo mkdir /var/lib/jenkins/init.groovy.dsudo vi /var/lib/jenkins/init.groovy.d/disable-slack-webhooks.groovy
groovy脚本disable-slack-webhooks.groovy内容如下:
import jenkins.model.Jenkinsimport hudson.model.RootActiondef j = Jenkins.instance;def removal = { lst -> lst.each { x -> if(x.getClass().name.contains("slack.webhook")) { lst.remove(x) } }}removal(j.getExtensionList(RootAction.class))removal(j.actions)
给init.groovy.d目录添加权限,并重启jenkins:
sudo chown jenkins:jenkins -R /var/lib/jenkins/init.groovy.dsudo service jenkins restart
现在,让我们开始去配置jenkins:
访问 ([ :8080/configure]h :8080/configure[/url]) 去配置java_home和m2_home作为环境变量并且填写slack的通知设置。
三、Demo演示
现在我们已经全部配置完毕,并且运行在 jenkins上,现在我们需要创建一个pipeline job并且创建pipeline script。选择"Build when a change is pushed to GitLab"按钮,因为我们将要push代码到gitlab上,然后gitlab将会触发jenkins。
图9:jenkins触发构建
拷贝以下pipeline script到pipeline job,在"准备(preparation)"阶段,Jenkins将从Gitlab上克隆样品spring boot应用,并在"Test"阶段进行测试,如果测试通过,它将切换到"编译(Build)"阶段,生成编译的结果并进入到最终的"部署(deployment)"阶段.
node { def mvnHome stage(‘Preparation’) { // for display purposes git ‘git@gitlab.com:<myRepo>/ContinuousIntegrationAndContinuousDeliveryApp.git' mvnHome = tool 'M2' } stage('Test') { try { sh "'${mvnHome}/bin/mvn' test" } catch (e) { notifyStarted("Tests Failed in Jenkins!") throw e } } stage('Build') { try { sh "'${mvnHome}/bin/mvn' clean package -DskipTests" }catch (e) { notifyStarted("Build Failed in Jenkins!") throw e } } stage('Results') { try{ archive 'target/*.jar' }catch (e) { notifyStarted("Packaging Failed in Jenkins!") throw e } } } stage('Deployment') { try{ sh '/var/lib/jenkins/workspace/Pipeline/runDeployment.sh' }catch (e) { notifyStarted("Deployment Failed in Jenkins!") throw e } } notifyStarted("All is well! Your code is tested,built,and deployed.")}def notifyStarted(String message) { slackSend (color: '#FFFF00', message: "${message}: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")}
现在,切换到/var/lib/jenkins/workspace/Pipeline目录,创建一个runDeployment.sh脚本,部署目录并且在部署目录放置一个Dockerfile.
cd /var/lib/jenkins/workspace/Pipelinemkdir deployment#Copy below Dockerfilesudo vi deployment/DockerfileFROM centosRUN yum install -y wgetRUN wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "otn-pub/java/jdk/8u60-b27/jdk-8u60-linux-x64.rpm"RUN yum localinstall jdk-8u60-linux-x64.rpm -yEXPOSE 8090ADD app.jar myApp.jarENTRYPOINT ["java","-jar","myApp.jar"] # Create runDeployment.sh and copy below script with your docker username and password.sudo vi runDeployment.sh#!/bin/bash -execho "Deploying app.jar to docker folder"packageName=`ls target/continuousintegrationandcontinuousdeliveryapp*.jar`versionid=`echo $packageName | awk -F "-" '{ print $2}'`versionname=`echo $packageName | awk -F "-" '{ print $3}' | awk -F "." '{ print $1}'`version=`echo $versionid-$versionname`echo "version: $version"cp -r $packageName deployment/app.jardockerImageName=onedaywillcome/myappdockerpid=`docker ps -a | grep $dockerImageName | grep "Up" | awk -F " " '{ print $1 }'`if [[ $dockerpid != "" ]];then docker kill $dockerpid docker rm $dockerpidfidocker build -t $dockerImageName deployment/.docker run -d -p 8090:8090 $dockerImageNamedockerImageId=`docker images | grep $dockerImageName | grep latest | awk -F " " '{print $3}'` docker tag $dockerImageId $dockerImageName:$versiondocker login -u <DOCKER_USERNAME> -p <DOCKER_PASSWORD>docker push $dockerImageName:$version#Give jenkins ownershipt to deployment directory and runDeployment.shsudo chown jenkins:jenkins runDeployment.shsudo chmod 775 runDeployment.shsudo chown jenkins:jenkins -R deployment
到此为止,所有的工作都已经完成。
当你现在去push你的代码到gitlab的时候,Gitlab将触发Jenkins,然后jenkins将首先进行测试,编译然后再服务器上部署,同时将编译生成的image镜像上传到Docker Registry,而在整个部署流水线中如果发生什么异常问题,它将通过Slack去通知你相关的异常点。 [img][/img]图10:部署流水线
我们的Spring boot应用现在就会运行起来了。 [img][/img]图11:spring-app
应用相关的docker image文件也已经上传到Docker hub registry: [img][/img]图12:docker hub镜像仓库
这样,我们就完成了一个持续集成持续部署(CI-CD)的Demo.
四、总结:
当然了,本篇博客只是一个很小的Demo,在实际企业内部使用过程中,这些环节需要更多的功能点以及安全方面的考虑。但是,站在企业研发者的角度去考虑这个问题,整个持续集成到持续测试很大程度上减少了时间损耗,这样开发人员在每次提交代码到主干分支,进行编译测试后就能很快的反馈测试结果,更佳方便了研发的效率;同时还有更重要的一点是,应用最终的产出物品是一个docker image,倘若在集成测试阶段可以正常的运行业务应用,那么最终使用该image镜像文件部署在预发布环境或者生产环境部署也依然会一切正常,因为业务代码以及业务应用运行时环境都被统一封装到了docker image中去了,这样也在很大程度上减少了业务应用多环境异构的情况,从此妈妈再也不用担心研发大人们的代码在本地和测试环境可以运行,而在生产环境不能正常运行了。
原创: 彪哥 译
|