Devops简介
k8s助力Devops在企业落地实践
传统方式部署项目为什么发布慢,效率低?
上线一个功能,有多少时间被浪费了?
如何解决发布慢,效率低的问题呢?
什么是Devops?
敏捷开发
提高开发效率,及时跟进用户需求,缩短开发周期。
敏捷开发包括编写代码和构建代码两个阶段,可以使用git或者svn来管理代码,用maven对代码进行构建。
持续集成(CI)
持续集成强调开发人员提交了新代码之后,立刻自动的进行构建、(单元)测试。根据测试结果,可以确定新代码和原有代码能否正确地集成在一起。持续集成过程中很重视自动化测试验证结果,对可能出现的一些问题进行预警,以保障最终合并的代码没有问题。
常见的持续集成工具:
Jenkins Jenkins是用Java语言编写的,是目前使用最多和最受欢迎的持续集成工具,使用Jenkins,可以自动监测到git或者svn存储库代码的更新,基于最新的代码进行构建,把构建好的源码或者镜像发布到生产环境。Jenkins还有个非常好的功能:它可以在多台机器上进行分布式地构建和负载测试 TeamCityTravis CIGo CDBambooGitLab CICodeship它的好处主要有以下几点:
较早的发现错误:每次集成都通过自动化的构建(包括编译、发布、自动化测试)来验证,哪个环节出现问题都可以较早的发现快速的发现错误:每完成一部分代码的更新,就会把代码集成到主干中,这样就可以快速的发现错误,比较容易的定位错误提升团队绩效:持续集成中代码更新速度快,能及时发现小问题并进行修改,使团队能创造出更好的产品防止分支过多的偏离主干:经常持续集成,会使分支代码经常向主干更新,当单元测试失败或者出现bug,如果开发者需要在没有调试的情况下恢复仓库的代码到没有bug的状态,只有很小部分的代码会丢失持续集成的目的是提高代码质量,让产品快速的更新迭代。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。
Martin Fowler说过:“持续集成并不能消除bug,而是让它们非常容易发现和纠正。”
持续交付
持续交付在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境的“类生产环境”中。交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。
如果所有的代码完成之后一起交付,会导致很多问题爆发出来,解决起来很麻烦,所以持续集成,也就是每更新一次代码,都向下交付一次,这样可以及时发现问题,及时解决,防止问题大量堆积。
持续部署
持续部署是指当交付的代码通过评审之后,自动部署到生产环境中。持续部署是持续交付的最高阶段。
k8s在Devops中的核心作用
docker和k8s的出现使devops变得更加普及,更加容易实现。在传统运维中我们服务时需要针对不同的环境去安装不同的版本,部署方式也杂、多。那么有了docker之后,一次构建、到处运行,只需要构建一次镜像,那么只要有docker的主机,就可以基于镜像把应用跑起来。
在至多微服务中,每天可能需要去处理各种服务的崩溃,而服务间的依赖调用关系也极其复杂,这队解决问题带来了很大的复杂度。要很好的解决这个问题,就需要用到容器编排工具。
kubernetes的出现主宰了容器编排的市场,也进化了过去的运维方式,将开发与运维联系的更加紧密。而且让devops这一角色变得更加清晰,它是目前可用的很流行的容器解决方案之一。
自动化
敏捷开发---->持续集成---->持续交付---->持续部署
多集群管理
可以根据客户需求对开发,测试,生产环境部署多套kubernetes集群,每个环境使用独立的物理资源,相互之间避免影响。
多环境一致性
kubernetes是基于docker的容器编排工具,因为容器的镜像是不可变的,所以镜像把OS、业务代码、运行环境、程序库、目录结构都包含在内,镜像保存在我们的私有仓库,只要用户从我们提供的私有仓库拉取镜像,就能保证环境的一致性。
实时反馈和智能化报表
每次集成或交付,都会第一时间将结果通过多途径的方式反馈给你,也可以定制适合企业专用的报表平台。
前情提要:
我的节点以及k8s版本 主从的ip:ip | |
---|---|
wentaomaster1 | 192.168.184.10 |
wentaonode1 | 192.168.184.20 |
安装nfs存储
#安装nfs并设置开机自启动,主从都需要安装[root@wentaomaster1 ~]#yum install -y nfs-utils[root@wentaomaster1 ~]#systemctl enable nfs --now[root@wentaonode1 ~]#yum install -y nfs-utils[root@wentaonode1 ~]#systemctl enable nfs --now
#配置nfs存储目录,仅在master节点配置[root@wentaomaster1 ~]# vim /etc/exports/data/v2 *(rw,no_root_squash)#使nfs配置生效[root@wentaomaster1 ~]# exportfs -arv
创建数据存储挂载
创建项目所用的名称空间[root@wentaomaster1 jenkins-k8s]# kubectl create namespace jenkins-k8snamespace/jenkins-k8s created
创建存储用的pv [root@wentaomaster1 jenkins-k8s]# cat pv.yamlapiVersion: v1kind: PersistentVolumemetadata: name: jenkins-k8s-pvspec: capacity: storage: 10Gi accessModes: - ReadWriteMany nfs: server: 192.168.184.10 path: /data/v2
[root@wentaomaster1 ~]# kubectl apply -f pv.yaml
创建pvc [root@wentaomaster1 jenkins-k8s]# cat pvc.yamlkind: PersistentVolumeClaimapiVersion: v1metadata: name: jenkins-k8s-pvc namespace: jenkins-k8sspec: resources: requests: storage: 10Gi accessModes: - ReadWriteMany
[root@wentaomaster1 ~]# kubectl apply -f pvc.yaml
创建serviceaccount并RBAC授权
创建一个服务账号sa并对其授权[root@wentaomaster1 ~]# kubectl create sa jenkins-k8s-sa -n jenkins-k8s#对sa服务账号授予系统角色cluster-admin的权限【RBAC】[root@wentaomaster1 ~]# kubectl create clusterrolebinding jenkins-k8s-sa-cluster -n jenkins-k8s --clusterrole=cluster-admin --serviceaccount=jenkins-k8s:jenkins-k8s-sa
准备项目需要的镜像
在工作节点上,从dockerhub上拉取jenkins镜像(我用的2.418)#用于安装jenkins用[root@wentaonode1 ~]# docker pull jenkins/jenkins:2.418
创建一个dockerfile构建镜像jenkins-slave-latest:v1 [root@wentaomaster1 slave]# cat dockerfileFROM jenkins/jnlp-slave:4.13.3-1-jdk11USER root# 安装DockerRUN apt-get update && apt-get install -y \ docker.io# 将当前用户加入docker用户组RUN usermod -aG docker jenkinsRUN curl -LO https://dl.k8s.io/release/stable.txtRUN curl -LO https://dl.k8s.io/release/$(cat stable.txt)/bin/linux/amd64/kubectlRUN chmod +x kubectlRUN mv kubectl /usr/local/bin/ENV DOCKER_HOST unix:///var/run/docker.sock#k8s中装好jenkins后,是有主从的,构建jenkins时候会在集群里再创建一个jenkins从,我用jenkins-slave-latest这个镜像来构建jenkins从[root@wentaomaster1 slave]# docker build -t=jenkins-slave-latest:v1 .
安装jenkins
给nfs的数据目录/data/v2做下权限[root@wentaomaster1 ~]# chown -R 1000.1000 /data/v2
使用deployment控制器创建jenkins pod
[root@wentaomaster1 ~]# cat jenkins-deploy.yamlkind: DeploymentapiVersion: apps/v1metadata: name: jenkins namespace: jenkins-k8sspec: replicas: 1 selector: matchLabels: app: jenkins template: metadata: labels: app: jenkins spec: serviceAccount: jenkins-k8s-sa containers: - name: jenkins image: docker.io/jenkins/jenkins:2.418 imagePullPolicy: IfNotPresent ports: - containerPort: 8080 name: web protocol: TCP - containerPort: 50000 name: agent protocol: TCP resources: limits: cpu: 2000m memory: 2Gi requests: cpu: 500m memory: 512Mi livenessProbe: httpGet: path: /login port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5 failureThreshold: 12 readinessProbe: httpGet: path: /login port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5 failureThreshold: 12 volumeMounts: - name: jenkins-volume subPath: jenkins-home mountPath: /var/jenkins_home volumes: - name: jenkins-volume persistentVolumeClaim: claimName: jenkins-k8s-pvc
[root@wentaomaster1 ~]# kubectl apply -f jenkins-deploy.yaml
给jenkins pod前端加上service,对外部提供网络访问
[root@wentaomaster1 ~]# cat jenkins-service.yamlapiVersion: v1kind: Servicemetadata: name: jenkins-service namespace: jenkins-k8s labels: app: jenkinsspec: selector: app: jenkins type: NodePort ports: - name: web port: 8080 targetPort: web nodePort: 30002 - name: agent port: 50000 targetPort: agent
[root@wentaomaster1 ~]# kubectl apply -f jenkins-service.yaml#通过kubectl get svc -n 看到对外提供的端口是30002
登录jenkins web页面
浏览器网址栏输入192.168.184.10:30002 可以看到提示为:初始密码在跑jenkins的pod里面的/var/jenkins_home/secrets/initialAdminPassword
文件中 将密码复制后,点击继续 选择“安装推荐的插件” 安装完成之后,创建管理员用户 实例配置不改,用默认url 更新kubernetes插件 点击安装后,下滑到最下面,勾选安装后重启 安装完成后,再安装jenkins自动化部署的ui界面Blue Ocean 输入之前设置的用户、密码,选择保持登录状态 jenkins对接kubernetes自动生成jenkins从节点
新增一个云kubernetes 填写云kubernetes配置内容 名称:kuberneteskubernetes地址:https://192.168.184.10:6443 6443是kubernetes apiserver的端口 kubernetes命名空间:jenkins-k8sJenkins UI界面配置访问Dockerhub凭据
添加dockerhub凭据jenkins+k8s自动化发布项目
准备好各个环境的命名空间#创建开发、生产、测试的命名空间[root@wentaomaster1 ~]# kubectl create ns devlopment[root@wentaomaster1 ~]# kubectl create ns production[root@wentaomaster1 ~]# kubectl create ns qatest
创建流水线自动化发布任务,左侧栏点击新建任务 node('testtan') { stage('Clone') { echo "1.Clone Stage" git url: "https://github.com/twttwttwt/jenkins-sample/" script { build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() } } stage('Test') { echo "2.Test Stage" } stage('Build') { echo "3.Build Docker Image Stage" sh "docker build -t twttwt/jenkins-demo:${build_tag} ." } stage('Push') { echo "4.Push Docker Image Stage" withCredentials([usernamePassword(credentialsId: 'dockerhub', passwordVariable: 'dockerHubPassword', usernameVariable: 'dockerHubUser')]) { sh "docker login -u ${dockerHubUser} -p ${dockerHubPassword}" sh "docker push twttwt/jenkins-demo:${build_tag}" } } stage('Deploy to dev') { echo "5. Deploy DEV"sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-dev.yaml" sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-dev.yaml"// sh "bash running-devlopment.sh" sh "kubectl apply -f k8s-dev.yaml --validate=false"}stage('Promote to qa') {def userInput = input( id: 'userInput', message: 'Promote to qa?', parameters: [ [ $class: 'ChoiceParameterDefinition', choices: "YES\nNO", name: 'Env' ] ] ) echo "This is a deploy step to ${userInput}" if (userInput == "YES") { sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-qa.yaml" sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-qa.yaml"// sh "bash running-qa.sh" sh "kubectl apply -f k8s-qa.yaml --validate=false" sh "sleep 6" sh "kubectl get pods -n qatest" } else { //exit } }stage('Promote to pro') {def userInput = input( id: 'userInput', message: 'Promote to pro?', parameters: [ [ $class: 'ChoiceParameterDefinition', choices: "YES\nNO", name: 'Env' ] ] ) echo "This is a deploy step to ${userInput}" if (userInput == "YES") { sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-prod.yaml" sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-prod.yaml"// sh "bash running-production.sh" sh "cat k8s-prod.yaml" sh "kubectl apply -f k8s-prod.yaml --record --validate=false" } }}
点击保存后回到dashboard,可以看到已经多出来了一条任务,点击构建 [root@wentaomaster1 ~]# kubectl get pod -n production[root@wentaomaster1 ~]# kubectl get pod -n devlopment[root@wentaomaster1 ~]# kubectl get pod -n qatest
#通过上面可以看到jenkins对接k8s,可以把应用发布到k8s集群的开发、测试、生产环境了。
访问github中index配置的web页面devops自动化版本更新
修改上图web页面的index文件 此时访问web页面,暂时没有改动 回到dashboard,重新执行一次pipeline文件 可以看到,已成功更新Devops自动化版本回滚
新建任务node('testtan') { stage('git clone') { git url: "https://github.com/twttwttwt/jenkins-rollout" sh "ls -al" sh "pwd"} stage('select env') { def envInput = input( id: 'envInput', message: 'Choose a deploy environment', parameters: [ [ $class: 'ChoiceParameterDefinition', choices: "devlopment\nqatest\nproduction", name: 'Env' ] ])echo "This is a deploy step to ${envInput}"sh "sed -i 's/<namespace>/${envInput}/' getVersion.sh"sh "sed -i 's/<namespace>/${envInput}/' rollout.sh"sh "bash getVersion.sh"// env.WORKSPACE = pwd()// def version = readFile "${env.WORKSPACE}/version.csv"// println version} stage('select version') { env.WORKSPACE = pwd() def version = readFile "${env.WORKSPACE}/version.csv" println version def userInput = input(id: 'userInput', message: '选择回滚版本', parameters: [ [ $class: 'ChoiceParameterDefinition', choices: "$version\n", name: 'Version' ] ]) sh "sed -i 's/<version>/${userInput}/' rollout.sh"} stage('rollout deploy') { sh "bash rollout.sh"}}
保存后回到首页