jenkins初始wed配置和ci cd部署
学习笔记,仅供参考!
Jenkins Plugins
Kubernetes 安装


修改password密码点击保存!
安装插件

安装插件:blue Ocean
点击Manage Jenkins --》点击插件管理 --》Available plugins --》搜索blue Ocean ----》点击安装
Updates:更新插件Available plugins 下载并安装插件Installed plugins 已安装的插件Advanced settings 插件高级设置blue Ocean:pipaline可视化管理工具
对接kubernete



重启完成后进行k8s对接操作



如果jenkins是部署到k8s之上的那麽对接就不需要密钥对接和证书key(需要在jenkins设置里面配置特殊凭据(Certificate))把k8s的证书导出来进行制作,让后在导入到jenkins中
但是如果是部署到k8s之外的那麽对接就需要密钥对接

点击save保存
jenkins对接github
因为我们在初始安装的时候已经安装好了github的插件

点击构建新的流水线



对接gitee
需要下载对应的插件gitee

根据提示的gitee对接所需要的url 以及域名 和需要的凭证信息(在gitee设置里面配置,当然导入到jenkins中)可以实现对接
这里不再做操作
gitlab私有代码仓库部署操作(基础版)
ps:如果想要部署完整版的可以去网上自行查询
ps💛因为我们用的是云服务器所以因为没有域名的缘故直接写的ip地址,就不需要写host
常用的代码仓库只有gitlab提供私有代码托管仓库的搭建 免费开源的
提示:如果后期需要配置钉钉消息,那麽应该在pod应用容器中将本地时间进行挂载在容器中,因为钉钉消息需要时间一致
- name: time-localtimereadOnly: truemountPath: /etc/localtime
gitlab私有代码仓库的搭建:需要用到alertmanager nginx redis node-exporter 等等的一些组件搭建依赖postgres(部署harbor和sonarqube代码扫描 时有用到 ) redis
Docker
一部署postgres数据库
实现:部署postgres完成后数据库里自动的创建名为gitlab数据库
ps:(可以bash脚本的方式书写成config进行挂载到里面)
配置数据库用户和密码 以secret资源的方式创建 ps:加密
cat gitlab-secret.txtpostgres.user.root=rootpostgres.pwd.root=redhat123kubectl create -n jenkins secret generic gitlab-secret --from-env-file=gitlab-secret.txtgeneric:手动
部署postgres完成后数据库里自动的创建名为gitlab数据库
实现1:
spec:initContainers:- name: installimage: busyboxcommand:- sh- "-c"- |set -ecat > /docker-entrypoint-initdb.d/init-user-db.sh << EOFPOSTGRES_USER=rootpsql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSSQLCREATE DATABASE gitlab;EOSSQLEOFvolumeMounts:- name: installmountPath: /docker-entrypoint-initdb.d在容器下面volumeMounts:- name: installmountPath: /docker-entrypoint-initdb.dvolumes:- name: installemptyDir: {}\ psql \l实现2:cat init-user-db.sh#!/bin/bashset -epsql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQLCREATE DATABASE gitlab;EOSQLkubectl create -n jenkins configmap init-db --from-file init-user-db.shcat postgres-statefulset.yamlapiVersion: v1kind: Servicemetadata:name: postgres-headlessnamespace: jenkinslabels:app: postgesspec:type: ClusterIPclusterIP: Noneports:- port: 5432name: postgrestargetPort: 5432selector:app: postgres---apiVersion: v1kind: Servicemetadata:name: postgres-svclabels:app: postgresnamespace: jenkinsspec:type: ClusterIPports:- name: serverport: 5432targetPort: 5432#protocol: TCP# nodePort: 32088clusterIP: 10.97.235.67selector:app: postgres---apiVersion: apps/v1kind: StatefulSetmetadata:namespace: jenkinsname: postgres-statefulsetlabels:app: postgresspec:replicas: 1selector:matchLabels:app: postgresserviceName: postgres-headlesstemplate:metadata:labels:app: postgresspec:containers:- name: postgresimage: postgres:11.4 #若本地没有启动该仓库,换成postgres:11.4imagePullPolicy: "IfNotPresent"ports:- containerPort: 5432env:- name: POSTGRES_USER #PostgreSQL 用户名valueFrom:secretKeyRef:name: gitlab-secretkey: postgres.user.root- name: POSTGRES_PASSWORD #PostgreSQL 密码valueFrom:secretKeyRef:name: gitlab-secretkey: postgres.user.rootresources:limits:cpu: 1000mmemory: 2048Mirequests:cpu: 50mmemory: 100MivolumeMounts:- name: init-dbmountPath: /docker-entrypoint-initdb.d/init-user-db.shsubPath: init-user-db.sh- mountPath: /var/lib/postgresql/dataname: postgres-nfs- name: time-localtimereadOnly: truemountPath: /etc/localtimevolumes:- name: init-dbconfigMap:name: init-db# - name: postgre-nfs# mountPath: /var/lib/postgresql/data- name: time-localtimehostPath:path: /etc/localtimevolumeClaimTemplates:- metadata:name: postgres-nfsspec:accessModes: [ "ReadWriteMany" ]storageClassName: "nfs-storageclass"resources:requests:storage: 3Gi
二.部署redis
cat redis-statefults.yamlapiVersion: v1kind: Servicemetadata:name: redis-headlessnamespace: jenkinslabels:app: redisspec:type: ClusterIPclusterIP: None # 创建无头服务,如果需要对外暴露端口可自行创建serviceports:- name: redisport: 6379targetPort: redisselector:app: redis---apiVersion: v1kind: Servicemetadata:name: redis-svcnamespace: jenkinslabels:app: redisspec:type: ClusterIPports:- port: 6379targetPort: 6379clusterIP: 10.110.1.132selector:app: redis---apiVersion: v1kind: ConfigMapmetadata:name: redis-confignamespace: jenkinslabels:app: redisdata:redis.conf: |bind 0.0.0.0protected-mode yesport 6379---apiVersion: apps/v1kind: StatefulSetmetadata:name: redisnamespace: jenkinsspec:selector:matchLabels:app: redisserviceName: redis-headlessreplicas: 2updateStrategy:type: RollingUpdatetemplate:metadata:name: redislabels:app: redisspec:affinity:podAntiAffinity: #pod的反亲和性requiredDuringSchedulingIgnoredDuringExecution: #硬亲和性(硬策略)- labelSelector:matchExpressions:- key: appoperator: Invalues:- redistopologyKey: "kubernetes.io/hostname" containers:- name: redisimage: redis:5.0.13imagePullPolicy: "IfNotPresent"ports:- name: rediscontainerPort: 6379volumeMounts:- name: datamountPath: /etc/redis/- name: "redis-data"mountPath: "/data"volumes:- name: dataconfigMap:name: redis-config volumeClaimTemplates:- metadata:name: redis-dataspec:accessModes: [ "ReadWriteMany" ]storageClassName: "nfs-storageclass"resources:requests:storage: 200M
gitlab部署 (启动时间较长)
可以选择有状态或无状态部署策略
cat gitlab-statefulset.yaml---apiVersion: v1kind: Servicemetadata:name: gitlab-headlessnamespace: jenkinslabels:app: gitlabspec:clusterIP: Noneports:- port: 80name: postgrestargetPort: 80selector:app: gitlab---apiVersion: v1kind: Servicemetadata:name: gitlab-svclabels:app: gitlabnamespace: jenkinsspec:type: NodePortports:- name: serverport: 80targetPort: 80protocol: TCPnodePort: 32085clusterIP: 10.99.69.119selector:app: gitlab---apiVersion: apps/v1kind: StatefulSetmetadata:namespace: jenkinsname: gitlablabels:app: gitlabspec:replicas: 1selector:matchLabels:app: gitlabserviceName: gitlab-headlesstemplate:metadata:labels:app: gitlabspec:containers:- name: gitlabimage: sameersbn/gitlab:13.2.2imagePullPolicy: "IfNotPresent"env:- name: GITLAB_HOSTvalue: "gitlab-svc"- name: GITLAB_PORTvalue: "80"- name: GITLAB_SECRETS_DB_KEY_BASEvalue: "long-and-random-alpha-numeric-string"- name: GITLAB_SECRETS_DB_KEY_BASEvalue: "long-and-random-alpha-numeric-string"- name: GITLAB_SECRETS_SECRET_KEY_BASEvalue: "long-and-random-alpha-numeric-string"- name: GITLAB_SECRETS_OTP_KEY_BASEvalue: "long-and-random-alpha-numeric-string"- name: DB_HOSTvalue: "postgres-svc" #- name: DB_NAMEvalue: "gitlab"- name: DB_USERvalueFrom:secretKeyRef:name: gitlab-secret2key: username- name: DB_PASSvalueFrom:secretKeyRef:name: gitlab-secret2key: password- name: REDIS_HOSTvalue: "redis-svc"- name: REDIS_PORTvalue: "6379"ports:- containerPort: 80resources:limits:cpu: 2000mmemory: 5048Mirequests:cpu: 100mmemory: 500MivolumeMounts:- mountPath: /home/git/dataname: gitlab-nfs- name: time-localtimereadOnly: truemountPath: /etc/localtimevolumes:- name: time-localtimehostPath:path: /etc/localtimevolumeClaimTemplates:- metadata:name: gitlab-nfsspec:accessModes: [ "ReadWriteMany" ]storageClassName: "nfs-storageclass"resources:requests:storage: 3Gi

部署和注册gitlab完成后点击Create a project创建一个仓库

点击Create Project创建
将gitee上面的代码导入到部署的gitlab私有仓库中
(因为往我们部署的私有仓库里面进行导入代码需要git命令 所有需要在控制或工作节点都下载上git命令 yum install git -y)
去要下载的平台上 在要下载的代码列表里面点击克隆或者下载按钮,复制下载连接地址


直接在master上下载 让后解压 : unzip +压缩包 (yum search unzip)
把解压出来的代码上传到私密仓库中
在解压出来的代码目录中执行我们部署的gitlab提供的git命令(一定要在本目录执行!!!)


Create a new repository创建新的存储库Push an existing folder推送现有文件夹Push an existing Git repository推送现有的Git存储库根据我们的部署我们是要推送gitee下载的代码进行推送,所以使用第二个哐框里面的命令
git init 代码初始化git remote add origin http://gitlab-svc/root/myblog.git 生成一个git地址 (在执行这个命令时如果出现fatal: unable to access 'http://gitlab-svc/root/myblog.git/': Could not resolve host: gitlab-svc; Unknown error需要将此命令的http://gitlab-svc/root/myblog.git修改为内部ip来使用)git remote -v 查看git地址git add .git commit -m "Initial commit" 初始化 git commit -m "updata Dockerfile(指定上传的包)"git push -u origin master #出现以下error 在host里面写指定


jenkins和gitlab对接
在jenkins下载gitlab插件(不需要重启)

安装完成后点击系统管理---》系统配置中

这里的URL:我写的是gitlab-svc的地址,如果配置了ingress的话写域名

在gitlab创建token




复制创建好的token到jenkins上面

点击Test Connection进行连接测试
ps:如果失败就可能是需要在host主机列表里面书写映射ip或者kubectl edit -n kube-system cm coredns #里面添加静态映射
hosts {192.168.10.30 jenkins.smile.com gitlab.smile.comfallthrough}想要快速生效就删除
kubectl delete pods -n kube-system coredns-7f89b7bc75-
至此jenkins和gitlab成功打通
在jenkins配置当gitlab代码仓库发生更新后直接下载到本地并推送消息给钉钉
点击新建任务


源码管理选择git
添加凭证


这里的URL:有ingress就写域名 没有就写无头或者svc地址 gitlab的
选择构建触发器
点击 Build when a change is pushed to GitLab. GitLab webhook URL: http://123.60.162.77:32080/project/freeeee? #当代码发生变化的时候向gitlab进行推送 #因为地址的原因不能直接进行推送,需要配置wedhooks


在wedhooks里面配置上个图片中的url地址进行创建



ps:Url is blocked:Requests to the local network are not allowed如果出现这个错误:是因为本地网络连接不允许,他是从一个安全方面的进行一个考量解决方法: 点击此页面左上角的小扳手 点击左边的状态栏 Settings设置 点击里面的network


jenkins点击save
测试:看到200 成功



ps:构建过后的代码存放在jenkins动态存储类中/workspace/freeeee目录中
至此这边只是实现了简单的构建任务,要实现向钉钉推送还需要重新配置一个


钉钉创建自定义机器人!!!

win 查询公网ip命令:curl ifconfig.me
自定义机器人接入 - 钉钉开放平台 (dingtalk.com)
curl 'https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxx' \-H 'Content-Type: application/json' \-d '{"msgtype": "text","text": {"content":"我就是我, 是不一样的烟火"}}'
ps⛵️创建钉钉机器人的ip段要尤其注意 !!!!!!正常情况下在本地搭建的需要写的win公网ip


jenkins添加备节点
因为jenkins目前只有一个master节点 ,所以的工作都来自于master节点,master处于单点状态!!很危险

创建一个名为k8s-slave1的节点 并勾选固定节点

jenkins执行任务时候严重依赖标签
Jenkins 可以在此节点上执行并发构建的最大数目 #同时可以跑多个任务
点击save保存 ![]()

点击创建好的节点上面进行配置

在选订好的节点上进行配置,将其配置成 从节点
Or run from agent command line, with the secret stored in a file:echo a763cb84fc791b919eb64532910af0e09515c395684ec66b1a231dbed5824091 > secret-file
curl -sO http://123.60.162.77:32080/jnlpJars/agent.jar #因为我部署在云服务器 所以123.60.162.77:32080尽量用内网,如果是本地部署的需要在host里面写ingres地址做映射java -jar agent.jar -jnlpUrl http://123.60.162.77:32080/manage/computer/k8s%2Dslave1/jenkins-agent.jnlp -secret @secret-file -workDir "/opt/jenkins_jobs"
在执行此命令同时需要下载java环境yum install java-11-openjdk -y java --version

java -jar agent.jar -jnlpUrl http://123.60.162.77:32080/manage/computer/k8s%2Dslave1/jenkins-agent.jnlp -secret @secret-file -workDir "/opt/jenkins_jobs"
当在本地或者云服务器部署执行此命令的时候会出现连接不上(50000端口)
解决办法:


(记得写完成加端口号50000 10.99.93.125:50000)点击保存就好


成功(可以写多个从节点)
但是因为java -jar agent.jar -jnlpUrl http://123.60.162.77:32080/manage/computer/k8s%2Dslave1/jenkins-agent.jnlp -secret @secret-file -workDir "/opt/jenkins_jobs"
给定的命令是前台运行不符合工作使用,很不方便 所以要将其修改为后台运行!
nohup java -jar agent.jar -jnlpUrl http://123.60.162.77:32080/manage/computer/k8s%2Dslave1/jenkins-agent.jnlp -secret @secret-file -workDir "/opt/jenkins_jobs" &

让后修改我们直接构建的任务 让他能够使用从节点


点击保存
在开始构建之前要在从节点上安装git命令 因为我们需要从仓库克隆下面,需要用到git
yum install git -y


以上都是我们手动触发的任务,我们要的是实现自动触发
测试:往gitlab仓库里面上传代码
将准备好的代码或者dockerfile上传到master节点上面的仓库目录里面

git status #查看是否有新的文件需要上传
git add .
git status
git commit -am 'add Dockerfile'
git push #输入gitlab密码和账户进行上传



呈上启下 :以上的操作仅仅只是将代码从仓库里面下载到了本地 没有其他的操作
但是我们希望把代码下载到本地后的通过制作镜像,上传到镜像仓库 ,并部署到k8s上面!
流水线:
Scripted Pipeline 脚本式流水线 最初的形态
Declarative Pipeline: 声明式流水线 (类似于Dockerfile)
pipeline脚本:的实现方式是通过Groovy DSL (领域专用语言)写的,所有的发布流程都可以有一段Groovy脚本,通过wed.ui的方式表述出来
构建并部署单分支流水线(pipeline)
ps:一个程序员在自己的node boot上面对代码进行了更新,把代码推送到gitlab上面,由我们部署的jenkins自动的获取到我们更新的代码, gitlab和jenkins要产生关联/一个认证的关系
jenkins所有的功能基本上都是以插件的形势来实现的触发器(jenkins):当gitlab仓库代码发生更新的时候会自动的触发jenkins进行代码拉取,jenkins之所以能够拉取gitlab仓库实时更新的代码是因为构建了一个任务类型的触发器gitlab_wed_hooks: jenkins触发器是通过gitlab的wed-hooks来进行勾连,或对接
只要代码一发生改变就会触发流水线
jenkins ---》往gitlab拉取更新的代码 ---》让后制作成镜像 ---》将镜像上传到镜像仓库中(私有公有)---》 部署到k8s 实现对外的访问
pipeline { agent {label 'k8s-slave1'} #any 可以写多个从节点,在任意多个从节点上运行environment { PROJECT = 'myblog'}stages { stage('Checkout') { #第一步 获取代码steps { checkout scm #检测代码 scm是一个特殊代码 由pipeline特殊执行 }}stage('Build') { #第二步 通过代码构建镜像steps { sh 'make' }}stage('Test'){ #第三步 上传镜像至镜像仓库(harbor)steps {sh 'make check'junit 'reports/**/*.xml' }} stage('Deploy') { #第四步 把镜像部署在K8S集群中steps {sh 'make publish'}}}post { #声明 定义一个steps或多个steps 根据流水线的阶段来进行判断 成功发什么,失败了又发什么success { echo 'Congratulations!'}failure { echo 'Oh no!'}always { echo 'I will always say Hello again!'}}
}
声明式流水线范例:
pipeline {agent {label 'k8s-slave1'} #agent标签代理,声明是由那个agent(从节点)来执行,label标签跟从节点配置的标签一致 #通过标签来选择 属于全局定义 可以在每个stages阶段下面写agent来单独声明由那个节点执行 ! environment { #环境变量PROJECT = 'myblog'}stages { #阶段 分段执行 一个stages里面可以包含多个stagestage('printenv') { #将本地的所有环境变量都输出出来steps {echo 'Hello World'sh 'printenv'}}stage('check') { #当gitlab代码仓库内容发生改变的话,jenkins自动获取拉取代码steps {checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: 'gitlab-user', url: 'http://gitlab.smile.com/root/myblog.git']]) #可以通过jenkins自动生成}}stage('build-image') { steps {sh 'docker build . -t myblog:latest -f Dockerfile'}}stage('send-msg') {steps {sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=' \-H 'Content-Type: application/json' \-d '{"msgtype": "text", "text": {"content": "您有新的代码更新"}}'"""}}}
}
options :允许从流水线内部配置特定于流水线的选项 {timeout: 设置流水线的运行超时时间,工作阶段一般会在30分钟 }
书写格式: options { timent(time: 1, unit: 'HOURS')} 设置允许超时时间为一个小时
restry: 可以失败的次数 书写方式: options { retry(3) }s
在构建单分支流水线之前,选择禁用之前创建的任务





创建流水线的方式有两种:1.在wed UI上面直接书写脚本Pipeline script 2.不需要在jenkins wed-ui里面书写脚本,通过设置Pipeline script from SCM在节点上面书写脚本上传到gitlab仓库
使用wed ui的方式实现Pipeline流水线:
checkout scmGit:{checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: 'gitlab-user', url: 'http://gitlab.smile.com/root/myblog.git']])} #此代码可以使用jenkins自行生成
点击Pipeline流水线下面的流水线语法----》 找到并点击checkout




ps⛵️:请自行将Dockerfile文件上传致要执行的节点上面并上传到gitlab仓库中
master(源码存放节点):git commit -am 'updata Dockerfile'
cat Dockerfile
# Base images 基础镜像
FROM centos:centos7.5.1804#MAINTAINER 维护者信息
LABEL maintainer="55691759@qq.com"#ENV 设置环境变量
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8#RUN 执行以下命令
RUN curl -so /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo && rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
RUN yum install -y python36 python3-devel gcc pcre-devel zlib-devel make net-tools nginx#工作目录
WORKDIR /opt/myblog#拷贝文件至工作目录
COPY . . #把当前node1里面的内容直接copy到/opt/jen_jobs# 拷贝nginx配置文件
COPY myblog.conf /etc/nginx#安装依赖的插件
RUN pip3 install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txtRUN chmod +x run.sh && rm -rf ~/.cache/pip#EXPOSE 映射端口
EXPOSE 8002#容器启动时执行命令
CMD ["./run.sh"]
点击save保存 并点击立即构建


以上操作完成任务的描述: 从代码仓库下载代码到本地 然后通过dockerfile构建镜像
流水线的图形界面形势来查看整体
ps:还可以通过Pipeline流水线的图形界面形势来查看整体的结果
#缺点:对于阶段执行的描述不是特别完整


在Pipeline流水线的图形界面时,当个别阶段执行出错/失败的时候可以单独的重启此阶段让他重新跑完
2.通过设置Pipeline script from SCM在节点上面书写Pipeline脚本上传到gitlab仓库
Pipeline script from SCM:

将jenkinsfile 上传到源码节点的存放目录里面 ,并上传到gitlab里面
cat Jenkinsfile
pipeline {agent {label 'k8s-slave1'}environment { PROJECT = 'myblog'}stages {stage('printenv') {steps {echo 'Hello World'sh 'printenv'}}stage('check') {steps {checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: 'gitlab-user', url: 'http://gitlab.smile.com/root/myblog.git']])}}stage('build-image') {steps {sh 'docker build . -t myblog:latest -f Dockerfile'}}stage('send-msg') {steps {sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=' \-H 'Content-Type: application/json' \-d '{"msgtype": "text", "text": {"content": "您有新的代码更新"}}'"""}}}
}
git add .
git remote -v
git status
git push

优化:Pipeline流水线优化
从jenkinsfile: 问题1.:sh 'docker build . -t myblog:latest -f Dockerfile' #tag latest以上的build构建的镜像因为每次构建的tag都是latest容易被覆盖 造成悬空镜像 永远只有一个镜像,造成版本无法后滚操作,当新版本出现问题之后无法回退解决方法:使用环境变量myblog:${GIT_COMMIT} 是gitlab自带的我们可以以{commit}id来作为镜像的版本号来使用 #tagsh 'docker build . -t myblog:${GIT_COMMIT} -f Dockerfile'


在源码存放节点 jenkinsfile文件中进行修改
将jenkinsfile 上传到源码节点的存放目录里面 ,并上传到gitlab里面
cat Jenkinsfile
pipeline {agent {label 'k8s-slave1'}environment { PROJECT = 'myblog'}stages {stage('printenv') {steps {echo 'Hello World'sh 'printenv'}}stage('check') {steps {checkout scm}}stage('build-image') {steps {sh 'docker build . -t myblog:${GIT_COMMIT} -f Dockerfile'}}stage('send-msg') { steps {sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=' \-H 'Content-Type: application/json' \-d '{"msgtype": "text", "text": {"content": "您有新的代码更新"}}'"""}}}
}
git add .
git remote -v
git commit -am 'updata Jenkinsfile'
git status
git push

问题2.stage('send-msg') { #美化
向钉钉发送消息,使始终是以stage的形势来发送 ,如果错了就不会继续执行下面的消息 但是我们希望正确的发送就发送正确,错误就发送失败了,然后不会影响下面的执行
这个时候就用到了post:(根据流水线或者阶段的完成情况而运行而运行post)
cat Jenkinsfile #在源代码存放节点
pipeline {agent { label 'k8s-slave1'}stages {stage('printenv') {steps {echo 'Hello World'sh 'printenv'}}stage('check') {steps {checkout scm}}stage('build-image') {steps {retry(2) { sh 'docker build . -t myblog:${GIT_COMMIT}'} #可以失败两次}}}post {success { echo 'Congratulations!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=' \-H 'Content-Type: application/json' \-d '{"msgtype": "text", "text": {"content": "😄👍构建成功👍😄\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"}}'"""}failure {echo 'Oh no!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=' \-H 'Content-Type: application/json' \-d '{"msgtype": "text", "text": {"content": "😖❌构建失败❌😖\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"}}'"""}always { echo 'I will always say Hello again!'}}
}
git commit -am 'updata Jenkinsfile'
git status
git push

将构建的镜像上传到镜像仓库中(私有/公有)
公有镜像仓库 dockerhub
想要上传镜像首先要给镜像修改名字docker tag myblog:4ba71b845bc07a86e7ad9101bf1a2227be43c896 1008611zmx/myblog:v1.0docker login -u 1008611zmx -pdocker push 1008611zmx/myblog:v1.0
cat Jenkinsfile
pipeline {agent { label 'k8s-slave1'}stages {stage('printenv') {steps {echo 'Hello World'sh 'printenv'}}stage('check') {steps {checkout scm}}stage('build-image') {steps {retry(2) { sh 'docker build . -t 1008611zmx/myblog:${GIT_COMMIT}'}}}stage('builid-image') {steps {retry(2) { sh 'docker login -u 1008611zmx -p '}retry(2) { sh 'docker push 1008611zmx/myblog:${GIT_COMMIT}'}}}}post {success { echo 'Congratulations!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=' \-H 'Content-Type: application/json' \-d '{"msgtype": "text", "text": {"content": "😄👍构建成功👍😄\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"}}'"""}failure {echo 'Oh no!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=' \-H 'Content-Type: application/json' \-d '{"msgtype": "text", "text": {"content": "😖❌构建失败❌😖\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"}}'"""}always { echo 'I will always say Hello again!'}}
}
git commit -am 'updata Jenkinsfile'
git status
git push

持续优化: 账户和密码 'docker login -u -p ' 和钉钉发送消息的token值 暴漏,存在安全隐患创建两个包含docker账号密码和钉钉的token值的凭证 #通过环境变量的方式进行调用





environment {BITBUCKET_COMMON_CREDS = credentials('jenkins-bitbucket-common-creds')
}
实际设置中包含了三个变量:BITBUCKET_COMMON_CREDS -包含一个以冒号分隔的用户名和密码,格式为username:passwordBITBUCKET_COMMON_CREDS_USR -附加的一个仅包含用户名部分的变量BITBUCKET_COMMON_CREDS_PSW -附加的一个仅包含密码部分的变量
cat Jenkinsfile
pipeline {agent { label 'k8s-slave1'}environment {DOCKERHUB_CREDS = credentials('dockerhub')DINGTALK_CREDS = credentials('dingtalk')}stages {stage('printenv') {steps {echo 'Hello World'sh 'printenv'}}stage('check') {steps {checkout scm}}stage('build-image') {steps {retry(2) { sh 'docker build . -t 1008611zmx/myblog:${GIT_COMMIT}'}}}stage('push-image') {steps {retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}retry(2) { sh 'docker push 1008611zmx/myblog:${GIT_COMMIT}'}}}}post {success { echo 'Congratulations!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "text", "text": {"content": "????构建成功????\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"}}'"""}failure {echo 'Oh no!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "text", "text": {"content": "???构建失败???\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"}}'"""}always { echo 'I will always say Hello again!'}}
}

私有镜像仓库 harbor
curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
yum install docker-compose -y
tar -zxvf harbor-offline-installer-v2.5.3.tgz
配置 :harbor.yml
安装./install.sh
通过流水线构建的镜像上传到镜像仓库中部署到k8s 实现对外的访问
导入准备好的yaml文件 ;deploy-myblog.yaml deploy-mysql.yaml
先执行deploy-mysql.yaml
stage('deploy') {steps {sh "sed -i 's#{{IMAGE}}#1008611zmx/myblog:${GIT_COMMIT}#g' deploy/*" #deploy/*是指在deploy目录下面存放的yaml文件都修改并执行 deploy/*存放yaml的目录 /*匹配所有
cat Jenkinsfile
pipeline {agent { label 'k8s-slave1'} #######environment {DOCKERHUB_CREDS = credentials('dockerhub')DINGTALK_CREDS = credentials('dingtalk')}stages {stage('printenv') {steps {echo 'Hello World'sh 'printenv'}}stage('check') {steps {checkout scm}}stage('build-image') {steps {retry(2) { sh 'docker build . -t 1008611zmx/myblog:${GIT_COMMIT}'}}}stage('push-image') {steps {retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}retry(2) { sh 'docker push 1008611zmx/myblog:${GIT_COMMIT}'}}}stage('deploy') {steps {sh "sed -i 's#{{IMAGE_URL}}#1008611zmx/myblog:${GIT_COMMIT}#g' deploy/deploy-myblog.yaml"timeout(time: 1, unit: 'MINUTES') {sh "kubectl apply -f deploy/deploy-myblog.yaml"}}}}post {success { echo 'Congratulations!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "text", "text": {"content": "😄👍构建成功👍😄\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"}}'"""}failure {echo 'Oh no!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "text", "text": {"content": "😖❌构建失败❌😖\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"}}'"""}always { echo 'I will always say Hello again!'}}
}
git add . #因为加入的有新的目录
git status
git commit -am 'add depoly'
git push
ps🈂️:因为我们是写的流水线脚本是从节点上执行的,但是从节点上面没有kubectl命令,所以会报错
解决办法: 在从节点上面创建/root/.kube 把master节点上面/root/.kube目录里面的config文件copy到从节点上面 从节点就可以使用k8s命令有了k8s权限 #但是极其不推荐,相当于从节点有了master权限
cat deploy-myblog.yaml
apiVersion: v1
kind: Namespace
metadata:name: develop
---
apiVersion: apps/v1
kind: Deployment
metadata:name: myblognamespace: develop
spec:replicas: 1 #指定Pod副本数selector: #指定Pod的选择器matchLabels:app: myblogtemplate:metadata:labels: #给Pod打labelapp: myblogspec:containers:- name: myblogimage: {{IMAGE_URL}} ## 修改imagePullPolicy: IfNotPresentenv:- name: MYSQL_HOSTvalue: "10.104.238.30"- name: MYSQL_PORTvalue: "3306"#- name: MYSQL_USER#value: "root"- name: MYSQL_PASSWDvalue: "123456"ports:- containerPort: 8002resources:requests:memory: 100Micpu: 50mlimits:memory: 500Micpu: 100mlivenessProbe:httpGet:path: /blog/index/port: 8002scheme: HTTPinitialDelaySeconds: 10 # 容器启动后第一次执行探测是需要等待多少秒periodSeconds: 15 # 执行探测的频率timeoutSeconds: 2 # 探测超时时间readinessProbe: httpGet: path: /blog/index/port: 8002scheme: HTTPinitialDelaySeconds: 10 timeoutSeconds: 2periodSeconds: 15
git commit -am 'updata Jenkinsfile'
git push

至此完成!
多分支流水线的构建
测试环境 生产环境
测试/生产环境通过命名空间namespace进行区分
ps:当部署的gpmall的项目出现了问题,要在测试环境进行调整/测试,完成后与生产环境的代码相互集成校验
代码提交的之后代码应该先往测试环境部署进行测试 (代码严格禁止在源代码上进行修改,修改源代码的复制品),如果测试环境的代码没有问题的话,在将其通过生产流水线,部署到生产环境中去,如果生产环境下的代码出现问题后,要修改测试环境的源代码的复制品进行更新,进行循环测试,当更新的代码没有问题后,把源代码复制器的更新,真正的源代码进行更新,进行合并,推送给生产环境的流水线进行部署!
根据以上定义:我们至少需要两条流水线
我们可以构建两条流水线,但是这样的方式很不好,不利于我们后面的持续集成,从而引出多分支!

先将之前构建的单分支流水线进行禁用操作~

然后在源代码存放的节点上面创建代码仓库分支
git log 查看操作步骤
git branch 查看分支 * 指到/处于那个分支就表示正在那个分支上面git checkout -b develop 创建名为develop的分支
:Switched to a new branch 'develop' #产生了一个新的分支git checkout master 切换分支 到master上面
:Switched to branch 'master'git branch develop
* mastergit push --set-upstream origin develop 上传/更新分支到代码仓库上面
在develop分支上面修改的内容不会影响到master分支的代码仓库git merge 代码合并


配置多分支流水线
发现分支 add 选择正则表达式 因为只有几个分支所有就写.* 匹配所有 当分支多的时候可以写成 master|develop这种格式


点击保存会自动扫描

效果实现: 我们在源代码节点上面修改master分支的代码 会触发jenkins 多分支流水线中master任务
我们在源代码节点上面修改develog分支的代码 会触发jenkins 多分支流水线中develog任务
在原来单分支流水线上面的的流水线脚本进行美化
将Jenkinsfile写好的脚本上传到develop分支上面 #把原来的Jenkinsfile转移到其他的目录里面保证当我们上传的新的流水线脚本发现问题会后能够回滚
pipeline {agent { label 'k8s-slave1'}environment {IMAGE_REPO = "1008611zmx/myblog"DINGTALK_CREDS = credentials('dingtalk')DOCKERHUB_CREDS = credentials('dockerhub')TAB_STR = "\n \n " #换行}stages {stage('printenv') {steps {script{sh "git log --oneline -n 1 > gitlog.file" #显示第一行到gitlog里面env.GIT_LOG = readFile("gitlog.file").trim() #env.GIT_LOG 读取gitlog.file trim的意思是修剪 美化 }sh 'printenv'}}stage('checkout') {steps {checkout scmscript{env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR }}}stage('build-image') {steps {retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR #(a=沙
a += 淼 +=是在原来的基础上对变量的新一轮赋值
a=沙淼)}}}stage('push-image') {steps {retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('deploy') {steps {sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"timeout(time: 1, unit: 'MINUTES') {sh "kubectl apply -f deploy/"}script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}}post {success { echo 'Congratulations!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😄👍 构建成功 👍😄 \n**项目名称**:smile \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'""" }failure {echo 'Oh no!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😖❌ 构建失败 ❌😖 \n**项目名称**:smile \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'"""}always { echo 'I will always say Hello again!'}}
}
git log 将源代码存放目录里面的log目录上传到代码仓库中 git commit -am 'add log' git push例子:(可copy)
cat Jenkinsfile
pipeline {agent { label 'k8s-slave1'}environment {IMAGE_REPO = "1008611zmx/myblog"DINGTALK_CREDS = credentials('dingtalk')DOCKERHUB_CREDS = credentials('dockerhub')TAB_STR = "\n \n "}stages {stage('printenv') {steps {script{sh "git log --oneline -n 1 > gitlog.file"env.GIT_LOG = readFile("gitlog.file").trim()}sh 'printenv'}}stage('checkout') {steps {checkout scmscript{env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('build-image') {steps {retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('push-image') {steps {retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('deploy') {steps {sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"timeout(time: 1, unit: 'MINUTES') {sh "kubectl apply -f deploy/"}script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}}post {success { echo 'Congratulations!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😄👍 构建成功 👍😄 \n**项目名称**:smile 33 \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'""" }failure {echo 'Oh no!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😖❌ 构建失败 ❌😖 \n**项目名称**:smile \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'"""}always { echo 'I will always say Hello again!'}}
}


gitlab做代码合并 通知gitlab构建状态 Merge 代码合并

将下面的代码部分替换到上面写好的Jenkinsfile里面 57dd行
cat Jenkinsfile
pipeline {agent { label 'k8s-slave1'}options {buildDiscarder(logRotator(numToKeepStr: '10'))disableConcurrentBuilds()timeout(time: 20, unit: 'MINUTES')gitLabConnection('10.99.69.119') #定义三个参数 多分支为代码合并做准备 连接gitlab让我们在gitlab中看到jenkins流水线执行的状态}environment {IMAGE_REPO = "1008611zmx/myblog"DINGTALK_CREDS = credentials('dingtalk')DOCKERHUB_CREDS = credentials('dockerhub')TAB_STR = "\n \n "}stages {stage('printenv') {steps {script{sh "git log --oneline -n 1 > gitlog.file"env.GIT_LOG = readFile("gitlog.file").trim()}sh 'printenv'}}stage('checkout') {steps {checkout scmupdateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success') #向gitlab发送流水线执行状态script{env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR #钉钉的消息通知 如果checkout执行成功就写一个这个,向钉钉发送消息}}}stage('build-image') {steps {retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('push-image') {steps {retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('deploy') {steps {sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"timeout(time: 1, unit: 'MINUTES') {sh "kubectl apply -f deploy/"}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}}post {success { echo 'Congratulations!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😄👍 构建成功 👍😄 \n**项目名称**:smile 33 \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'""" }failure {echo 'Oh no!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😖❌ 构建失败 ❌😖 \n**项目名称**:smile \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'"""}always { echo 'I will always say Hello again!'}}
}
由于我们配置了gitlab的互联 通知gitlab的构建状态 可以在gitlab commit里面看到完整的gitlab构建是否成功 每个构建步骤都可以从这里跳转到jenkins的图形界面 确定运行中没有问题 ,但是这个仅仅至少依据,不能确保代码是否真的有问题 可以作为代码合并的依据


把deploy yaml文件添加命名空间 把所有的服务和代码在命名空间下运行 让后隔离测试 ,让后把代码合并到master就可以更新了
多分支流水线的进阶
现在基与整个jenkins不管是单/多流水线,还是自由构建的项目,首先要依赖于agent (标签代理)
我们在jenkins上配置了从节点, 将任务都尽量的在从节点上部署,那是因为master节点只要一出问题那麽就会导致整个jenkins服务的崩坏,但是要想在从节点上部署工作上的服务进行测试或者生产 ,就要给从节点master权限,但是在这样在k8s集群中是一种高度风险的操作(高危),就算我们可以单独在集群中拿出两道三台的从节点来运行任务 但是还是会带来一个新的问题,流水线不是24小时都会有流水的,只有当代码发生了更新之后才会有触发任务,当没有流水的时候机器也不可能一直的开着会造成资源的浪费 (造成平时没有代码更新的时候没有任何的流水线任务,但是一旦发送了代码更新之后就会连带这一大批的代码任务,就会可能造成节点不够用)
实现的目的:当我们需要流水线的时候我们就基与流水线的pod就创建出来,当不需要的时候就把pod资源删除
或者说:有代码更新触发流水线的时候,创建基与流水线的jenkins的pods,这个pod负责真个流水线的运行,当整个流水线任务完成后把jenkins的pod自动的删除 (前提条件是必须要和k8s集群对接)
好处:1.资源不会被浪费 2.就算很多时候一瞬间需要构建的任务或者一个节点承受不住的时候,也可以自动的创建出多个pods来抵挡 ,jenkins和k8s结合的很紧密
jenkins对接k8s



创建对接k8s下的pod模板




ps:如果工作空间卷选择的是host Path 本地存储的时候 就需要让pod设置成只运行在host path本地存储的节点上面才能读取数据
我们使用的是本地存储 在工作空间卷上面配置节点选择器 通过给所选的从节点上面打标签来实现
在master节点上面执行:kubectl label nodes master-node1 agent=true同时在jenkins wed界面上面设置主机路径为绝对路径 并修改从节点的主机路径目录的权限chown -R 1000:1000 /opt/jenkins_jobs/
#是因为我们在创建jenkins pod的时候yaml将权限修改为了1000,如果不修改权限的话进来的时候直接就会挂root,那和我们的pod和jenkins的pod对连的时候直接就会报错!!


如果工作空间卷选择pvc的话,就需要创建一个pvc将pvc的值写在声明值里面
点击save #注意保存完成后将jenkinsfile脚本中的agent 修改为jnlp-slave 才能识别
![]()
修改完agent后 在源代码节点的目录中
git commit -am 'change slave to k8s pod'
git push
https://hub.docker.com/r/jenkins/inbound-agent
# git merge 代码合并

但是这样下载的jenkins-inbound-agent的镜像中没有docker命令,会报错!
解决方法: 1.直接在jenkins-inbound-agent镜像中安装docker命令
2.直接做两个pod
(在一个pod中搞两个容器 jenkins-inbound 作用:和jenkins来进行对接
tools 工具容器 作用:在这个容器中安装各种各样的工具
比如:tools-pyhon的容器环境 java-tools的容器环境等等 我们也可以直接把环境和docker命令等等的东西直接安装在jenkins-inbound但是这样的话就不够灵活 )
这里选择第二种:制作tools工具容器
在master节点 创建tools
mkdir tools
制作的tools工作容器中要有git命令 .kube/config 加入master权限 docker命令 和kubectlwhich kubectl
/usr/bin/kubectl
[root@master-jenkins tools]# cp `which kubectl` .
[root@master-jenkins tools]# ls
kubectl
[root@master-jenkins tools]# cp /root/.kube/config .
[root@master-jenkins tools]# ls
config kubectl
[root@master-jenkins tools]# touch Dockerfile
# FROM alpine alpine的特点是小 run=apkcat Dockerfile
FROM alpine
LABEL maintainer="1234567890.com"
USER rootRUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \apk update && \apk add --no-cache openrc docker git curl tar gcc g++ make \bash shadow openjdk8 python2 python2-dev py-pip python3-dev openssl-dev libffi-dev \libstdc++ harfbuzz nss freetype ttf-freefont chromium chromium-chromedriver && \mkdir -p /root/.kube && \usermod -a -G docker rootCOPY config /root/.kube/RUN rm -rf /var/cache/apk/*#-----------------安装 kubectl--------------------#
COPY kubectl /usr/local/bin/
RUN chmod +x /usr/local/bin/kubectl
# ------------------------------------------------#
docker build -t 1008611zmx/tools:v1.0 . #docker push 1008611zmx/tools:v1.0 可以直接上传到工有镜像仓库中
在pod-template中添加 容器 Container Template


docker inspect jenkins/inbound-agent:3107.v665000b_51092-4 查看容器的详细信息

添加卷: 是因为tools这个容器想要运行必须调用docker.sock 安全套接字ll /var/run/docker.sock,要在添加卷的主机路径和挂载路径中挂载他

点击save
然后修改源代码仓库中的jenkinsfile,进行调用tools
cat Jenkinsfile
pipeline {agent { label 'jnlp-slave'}options {buildDiscarder(logRotator(numToKeepStr: '10'))disableConcurrentBuilds()timeout(time: 20, unit: 'MINUTES')gitLabConnection('10.99.69.119')}environment {IMAGE_REPO = "1008611zmx/myblog"DINGTALK_CREDS = credentials('dingtalk')DOCKERHUB_CREDS = credentials('dockerhub')TAB_STR = "\n \n "}stages {stage('printenv') {steps {script{sh "git log --oneline -n 1 > gitlog.file"env.GIT_LOG = readFile("gitlog.file").trim()}sh 'printenv'}}stage('checkout') {steps {container('tools') { #调用这个容器checkout scm}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('build-image') {steps {container('tools') {retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('push-image') {steps {container('tools') {retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('deploy') {steps {container('tools') {sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"timeout(time: 1, unit: 'MINUTES') {sh "kubectl apply -f deploy/"}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}}post {success { echo 'Congratulations!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😄👍 构建成功 👍😄 \n**项目名称**:smile 33 \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'""" }failure {echo 'Oh no!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😖❌ 构建失败 ❌😖 \n**项目名称**:smile \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'"""}always { echo 'I will always say Hello again!'}}
}
git commit -am 'change slave to k8s pod'

至此具备小型工作的jenkins雏形已经具备
集成sonarQube实现 代码扫描
SonarQube 10.0 #merge 代码合并
jenkins: 在ci/cd中起到管理调度的作用
ps:为什么需要多分支流水线 :是因为在工作环境中不可能只有一个环境,而是至少有两个
测试环境和生产环境
代码提交之后的代码应该先往测试环境部署进行测试 (代码严格禁止在源代码上进行修改,修改源代码的复制品),如果测试环境的代码没有问题的话,在将其通过生产流水线,部署到生产环境中去,如果生产环境下的代码出现问题后,要修改测试环境的源代码的复制品进行更新,进行循环测试,当更新的代码没有问题后,把源代码复制器的更新,真正的源代码进行更新,进行合并,推送给生产环境的流水线进行部署!
ingress --金丝雀发布 --灰度发布 会对新版和旧版的流量进行一个分流 新版本抢先体验
Sonar可以从以下七个维度检测代码质量,而作为开发人员至少需要处理前5种代码质量问题。1. 不遵循代码标准sonar可以通过PMD,CheckStyle,Findbugs等等代码规则检测工具规范代码编写。
2. 潜在的缺陷sonar可以通过PMD,CheckStyle,Findbugs等等代码规则检测工具检 测出潜在的缺陷。
3. 糟糕的复杂度分布文件、类、方法等,如果复杂度过高将难以改变,这会使得开发人员 难以理解它们, 且如果没有自动化的单元测试,对于程序中的任何组件的改变都将可能导致需要全面的回归测试。
4. 重复显然程序中包含大量复制粘贴的代码是质量低下的,sonar可以展示 源码中重复严重的地方。
5. 注释不足或者过多没有注释将使代码可读性变差,特别是当不可避免地出现人员变动 时,程序的可读性将大幅下降 而过多的注释又会使得开发人员将精力过多地花费在阅读注释上,亦违背初衷。
6. 缺乏单元测试sonar可以很方便地统计并展示单元测试覆盖率。
7. 糟糕的设计通过sonar可以找出循环,展示包与包、类与类之间的相互依赖关系,可以检测自定义的架构规则 通过sonar可以管理第三方的jar包,可以利用LCOM4检测单个任务规则的应用情况, 检测耦合。
sonarqube架构简介

-
CS架构
-
sonarqube scanner
-
sonarqube server
-
-
SonarQube Scanner 扫描仪在本地执行代码扫描任务
-
执行完后,将分析报告被发送到SonarQube服务器进行处理
-
SonarQube服务器处理和存储分析报告导致SonarQube数据库,并显示结果在UI中
-
能够检测很多的代码 java python等等的
sonarqube on kubernetes环境搭建
-
资源文件准备
sonar/sonar.yaml
-
和gitlab共享postgres数据库
-
使用ingress地址
sonar.smile.com进行访问 -
使用initContainers进行系统参数调整
安装sonarqube服务端
apiVersion: v1
kind: Service
metadata:name: sonarqube-svcnamespace: jenkinslabels:app: sonarqube
spec:type: NodePortports:- name: sonarqubeport: 9000targetPort: 9000protocol: TCPnodePort: 32086clusterIP: 10.107.89.235selector:app: sonarqube
---
apiVersion: apps/v1
kind: Deployment
metadata:namespace: jenkinsname: sonarqubelabels:app: sonarqube
spec:replicas: 1selector:matchLabels:app: sonarqubetemplate:metadata:labels:app: sonarqubespec:#nodeSelector:# sonar: "true"initContainers:- command:- /sbin/sysctl- -w- vm.max_map_count=262144 # 调整虚拟内存image: alpine:3.6imagePullPolicy: IfNotPresentname: elasticsearch-logging-initresources: {}securityContext: #开启特权级 root权限 调整虚拟内存需要开启root模式privileged: truecontainers:- name: sonarqubeimage: sonarqube:7.9-communityports:- containerPort: 9000env:- name: SONARQUBE_JDBC_USERNAMEvalueFrom:secretKeyRef:name: gitlab-secret2key: username- name: SONARQUBE_JDBC_PASSWORDvalueFrom:secretKeyRef:name: gitlab-secret2key: password- name: SONARQUBE_JDBC_URLvalue: "jdbc:postgresql://10.97.235.67:5432/sonar" #5432 写的的postgresal的svc 的地址 livenessProbe:httpGet:path: /sessions/newport: 9000initialDelaySeconds: 60periodSeconds: 30readinessProbe:httpGet:path: /sessions/newport: 9000initialDelaySeconds: 60periodSeconds: 30failureThreshold: 6resources:limits:cpu: 2000mmemory: 4096Mirequests:cpu: 100mmemory: 512MivolumeMounts:- name: time-localtimereadOnly: truemountPath: /etc/localtimevolumes:- name: time-localtimehostPath:path: /etc/localtime
把代码质量检测写在Jenkins 流水线脚本里面的build-image前面,因为前面的checkout代码检测之后才能触发流水线的工作,让后才能扫描我们的代码,构建镜像,上传镜像 ,但是代码扫描解决不了所有问题只能解决一些基础问题比如:性能过多,注释不足,更多的是检测基础有没有问题,如果基础没有问题就上线进行测试,测试性能
🈂️:因为用的云服务器所以如果服务连接不上或者报错的时候就检查以下host主机文件和k8s集群coreDns里面配置的静态路由!
在k8s集群中部署sonarqube服务端在jenkins命名空间下
因为我使用的是云服务器所以要修改yaml文件在使用
🍶🎉在执行此yaml文件的时候需要在postgres数据库中创建一个sonar数据库 !!!
kubectl exec -it -n jenkins postgres-statefulset-0 -- bash
create database sonar;
kubectl apply -f sonar.yaml

登录密码: 默认都是admin !
开发 专业 数据中心的插件需要付费 插件不是由sonar官方提供的 都是由社区或者国外提供的

我们安装部署了sonarqube-server服务端 同样还需要安装客户端 才能使用
通过客户端将scm检测之后触发jenkins触发器通过gitlab的wedhooks收取到更新的代码后通过sonarqube-client进行代码检测 ,之后将扫描的结果在发送给服务端 ,让后进行镜像构建 上传镜像等等
安装sonarqube-client客户端 (客户端和服务端都安装在master节点)

wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.2.0.1873-linux.zip
unzip sonar-scanner-cli-4.2.0.1873-linux.zip修改配置文件 !
[root@master-jenkins sonar-scanner-4.2.0.1873-linux]# lsbin#执行命令 conf#配置文件 jre#java包内置了java环境 支撑扫描脚本的java运行 lib#二进制文件
vim conf
#Configure here general information about the environment, such as SonarQube server connection details for example
#No information about specific project should appear here#----- Default SonarQube server
#sonar.host.url=http://localhost:9000
sonar.host.url=http://10.107.89.235:9000 # 书写 svc的 地址 #如果是本地部署的话是应该写ingres域名 #注意如果本地部署就需要写host 和coredns里面配置静态路由#----- Default source code encoding
#sonar.sourceEncoding=UTF-8sonar bin下的执行脚本
[root@master-jenkins conf]# cd ../bin/
[root@master-jenkins bin]# ls
sonar-scanner sonar-scanner-debug #执行脚本 扫描代码!如果想要执行代码扫描那麽应该在jenkins源代码的仓库目录里面执行脚本
../sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -X
执行../sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -X
报错: Caused by: You must define the following mandatory properties for 'Unknown': sonar.projectKey
原因: 缺乏sonar.projectKey 可以在官网中找到 https://docs.sonarqube.org/latest/analyzing-source-code/scanners/sonarscanner/解决方法: 将**vim sonar-project.properties** 写到源代码仓库目录中 来定义扫描的内容
sonar.projectKey=myblog
sonar.projectName=myblog
# if you want disabled the DTD verification for a proxy problem for example, true by default
sonar.coverage.dtdVerification=false
# JUnit like test report, default value is test.xml
sonar.sources=blog,myblog #扫描的内容 blog,myblog代码在源代码放开目录中
让后在源代码仓库执行../sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -X

源代码仓库 ../sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -X

Total time: 5.740s #耗时 15:14:35.074 INFO: Final Memory: 12M/47M #使用了多少内存

到这,客户端扫描了之后就会把结果上传到服务端


🍶:因为CI/CD的缘故我们希望将代码扫描检测sonar 集成到jenkins 代码仓库中!
🍶:实现方法: 因为tools的镜像涵盖了很多的工具,如果我们想做一个具有代码检测功能的流水线,客户我们就需要把sonar-scanner-4.2.0.1873-linux端集成到我们jnlp的pod中,在我们的jnlp中pod中有两个容器,一个是jnlp容器 和jenkins来进行对接,一个是tools 工具容器 作用,根据两个容器的作用我们应该把sonar-scanner-4.2.0.1873-linux客户端集成放在tools容器中
因为我们sonar-scanner-4.2.0.1873-linux客户端中在jre中集成内置了java环境 而我们要把客户端集成到tools容器中而tools容器中已经有了java环境 为了减少我们的环境在集成过程中就不需要jre目录了 还有一点是因为在sonar-scanner-4.2.0.1873-linux客户端jre目录特别占存储
[root@master-jenkins tools]# cp -r ../sonar-scanner-4.2.0.1873-linux/ sonar-scanner[root@master-jenkins tools]# ls
config Dockerfile kubectl sonar-scanner[root@master-jenkins tools]# cd sonar-scanner/
[root@master-jenkins sonar-scanner]# ls
bin conf jre lib[root@master-jenkins sonar-scanner]# rm -rf jre/[root@master-jenkins sonar-scanner]# cd ..[root@master-jenkins tools]# cd sonar-scanner/[root@master-jenkins sonar-scanner]# cd bin/[root@master-jenkins bin]# vim sonar-scanner
sonar-scanner sonar-scanner-debug #use_embedded_jre=true 改为 use_embedded_jre=false embedded内置 因为我们我们tools应该有java环境我们不希望他使用自己内置的java环境所以要修改为false然后将tools的Dockerfile重新书写
cat Dockerfile
FROM alpine
LABEL maintainer="2769718603@qq.com"
USER rootRUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \apk update && \apk add --no-cache openrc docker git curl tar gcc g++ make \bash shadow openjdk8 python2 python2-dev py-pip python3-dev openssl-dev libffi-dev \libstdc++ harfbuzz nss freetype ttf-freefont chromium chromium-chromedriver && \mkdir -p /root/.kube && \usermod -a -G docker rootCOPY config /root/.kube/RUN rm -rf /var/cache/apk/*#-----------------安装 kubectl--------------------#
COPY kubectl /usr/local/bin/
RUN chmod +x /usr/local/bin/kubectl
# ------------------------------------------------##---------------安装 sonar-scanner-----------------#
COPY sonar-scanner /usr/lib/sonar-scanner
RUN ln -s /usr/lib/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner && chmod +x /usr/local/bin/sonar-scanner
ENV SONAR_RUNNER_HOME=/usr/lib/sonar-scanner
# ------------------------------------------------#
docker build -t 1008611zmx/tools:v2 .
让后docker login 登录公有镜像仓库
docker push 1008611zmx/tools:v2
让后把jenkins系统管理--节点管理---配置集群中的pod template的jnlp容器中的tools容器进行修改!修改为我们最新制作的tools容器的名称

然后将jenkins 连接sonar
在jenkins wed页面下载sonar的插件

当gitlab发生了代码更新之后 代码拉到我们jenkins-slave中jenkins-slave-pod(inbound tools两个容器)我们tools容器中集成了sonar-client 将代码的扫描结果上传给了sonar-server服务端,当扫描结果通过了之后,让后用代码制作镜像,把镜像推送给镜像仓库,因为我们jenkins对接了k8s,k8s向我们的镜像仓库进行访问拉取镜像 部署服务 其中jenkins扮演了一个指挥官 调度官的角色

下载安装完sonar插件之后我们需要创建一个属于自己的对接的工具,这个工具需要有一个自己的token凭证
#在jenkins wed页面 系统管理--创建凭证
而jenkins凭证里面需要的token值需要到sonar中创建


让后在系统管理中 SonarQube servers配置 进行对接

Server URL:如果是本地部署的那麽我们应该写ingress 因为ingress的域名是集群的内部
需要在kubectl edit -n kube-system cm coredns中配置静态路由 # 着急的话可以强制更新
sonar对接完成!
sonar距拣门 (什么样的标准 代码才会允许通过) #相当于操作系统的防火墙!!!

什么样的标准 代码运行通过 (如果开发写的代码符合我们的标准就通过,如果没符合就不通过)
Metric Operator ErrorCoverage on New Code is less than 80.0% #代码 修改/改变超过代码80%才能通过 小于百分之80报错Duplicated Lines on New Code is greater than 3.0% #新代码的重复行大于百分之三 才通过Maintainability Rating on New Code is worse than AReliability Rating on New Code is worse than ASecurity Rating on New Code is worse than A
在sonar中创建规则 (因为默认的规则可能会不符合需要创建)

创建出新的规则后如何将项目调进来:通过以下方式就可以

让后在规则中创建策略来对All选中的项目进行使用

让后将实例apply上:#将测试yaml文件先执行上 让后通过流水线进行更新 更新的时候只需要更新myblog!!!
cat deploy-mysql.yaml
apiVersion: v1
kind: Service
metadata:name: mysql-svcnamespace: develop
spec:ports:- port: 3306protocol: TCPtargetPort: 3306selector:app: mysql#type: ClusterIPclusterIP: 10.104.238.30
---
apiVersion: apps/v1
kind: Deployment
metadata:name: mysqlnamespace: develop
spec:replicas: 1 #指定Pod副本数selector: #指定Pod的选择器matchLabels:app: mysqltemplate:metadata:labels: #给Pod打labelapp: mysqlspec:containers:- name: mysqlimage: mysql:5.7ports:- containerPort: 3306env:#- name: MYSQL_USER# #value: root- name: MYSQL_ROOT_PASSWORDvalue: "123456"- name: MYSQL_DATABASEvalue: "myblog"resources:requests:memory: 100Micpu: 50mlimits:memory: 500Micpu: 100mcat /myblog/deploy-myblog.yaml
apiVersion: v1
kind: Service
metadata:name: myblog-svclabels:app: myblognamespace: develop
spec:type: NodePortports:- name: myblogport: 8002targetPort: 8002protocol: TCPnodePort: 32087clusterIP: 10.104.222.190selector:app: myblog
---
apiVersion: apps/v1
kind: Deployment
metadata:name: myblognamespace: develop
spec:replicas: 1 #指定Pod副本数selector: #指定Pod的选择器matchLabels:app: myblogtemplate:metadata:labels: #给Pod打labelapp: myblogspec:containers:- name: myblogimage: smsmcopy/myblog:627c60d6727d617f78b4ca7cc1d1191f91def301imagePullPolicy: IfNotPresentenv:- name: MYSQL_HOSTvalue: "10.104.238.30"- name: MYSQL_PORTvalue: "3306"- name: MYSQL_USERvalue: "root"- name: MYSQL_PASSWDvalue: "123456"ports:- containerPort: 8002resources:requests:memory: 100Micpu: 50mlimits:memory: 500Micpu: 100m
kubectl exec -it -n develop myblog-67976548d7-k58ln -- bash
myblog有一个典型的python文件需要执行 manage.py
python3 manage.py makemigrations #如果失败请先检测pod的与svc直接是否正常连接 lable
python3 manage.py migrate
python3 manage.py createsuperuser #生成一个管理员密码 也是myblog的登录密码



至此一个简略的博客完成
http://123.60.162.77:32087/blog/article/edit/0

将源代码仓库中的deploy目录里面的myblog的yaml文件进行修改 也可以直接替换
cp /myblog/deploy-myblog.yaml .
部署完sonar代码扫描之后和myblog的博客之后对源代码目录中的Jenkins流水线脚本进行修改
因为在源代码仓库执行../sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -X 所以要在Jenkins中进行调用
在jenkins中介绍了如何在流水线脚本中写pipeline SonarQube Scanner for Jenkins
把sonar的脚本写并放在Jenkinsfile流水线脚本scm后面

cat Jenkinsfile
pipeline {agent { label 'jnlp-slave'}options {buildDiscarder(logRotator(numToKeepStr: '10'))disableConcurrentBuilds()timeout(time: 20, unit: 'MINUTES')gitLabConnection('10.99.69.119')}environment {IMAGE_REPO = "1008611zmx/myblog"DINGTALK_CREDS = credentials('dingtalk')DOCKERHUB_CREDS = credentials('dockerhub')TAB_STR = "\n \n "}stages {stage('printenv') {steps {script{sh "git log --oneline -n 1 > gitlog.file"env.GIT_LOG = readFile("gitlog.file").trim()}sh 'printenv'}}stage('checkout') {steps {container('tools') {checkout scm}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('CI'){failFast true #只要失败立刻终止parallel { #并行参数 (因为我们的steps是一步一步的进行的 但是加了paraller参数之后 stage可以同时运行)stage('Unit Test(单元测试)') {steps {echo "Unit Test Stage Skip..."}}stage('Code Scan') { #代码扫描steps {container('tools(调用tools容器)') {withSonarQubeEnv('sonarqube(是我们的sonarqube的svc地址#如果写svc地址错误之后 替换成sonarqube的全局唯一凭证)') {sh 'sonar-scanner -X' #调用我们代码扫描的程序在源代码目录里面进行扫描 (之前客户端的执行脚本命令)sleep 10}script {timeout(1) {def qg = waitForQualityGate('sonarqube(是我们的sonarqube的svc地址#如果写svc地址错误之后 替换成sonarqube的全局唯一凭证)') #def 函数 调用 sonarqube变量 如果qg.status不ok的话就报一个错 配合failFast true 立刻停止if (qg.status != 'OK') {error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"}}}}}}}}stage('build-image') {steps {container('tools') {retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('push-image') {steps {container('tools') {retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('deploy') {steps {container('tools') {sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"timeout(time: 1, unit: 'MINUTES') {sh "kubectl apply -f deploy/"}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}}post {success { echo 'Congratulations!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😄👍 构建成功 👍😄 \n**项目名称**:smile 33 \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'""" }failure {echo 'Oh no!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😖❌ 构建失败 ❌😖 \n**项目名称**:smile \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'"""}always { echo 'I will always say Hello again!'}}
}[root@master-jenkins python-demo-master]# git branch
* developmaster
因为内我们deploy改变了 sonar-project.properties 又新增了一个目录(是我们执行sonar客户端执行出来的)
[root@master-jenkins python-demo-master]# git add .
[root@master-jenkins python-demo-master]# git commit -am 'add sonarqube'
[root@master-jenkins python-demo-master]# git pushgit add --all
git status
git comtios -am "updata Jenkins"
git push

如果报错就将我们之前手动执行的代码检测生成的.sonarqube删除就可以了
如果报错的话,将当时我们用命令执行出来的.scannerwork 进行删除 !!!
那麽做到这里请严格注意凭证的全局唯一id名字!

集成RobotFramework实现验收测试
一个基于Python语言,用于验收测试和验收测试驱动开发(ATDD)的通用测试自动化框架,提供了一套特定的语法,并且有非常丰富的测试库 。
robot用例简介 #在下面启动tools容器做验收测试的时候写入进去 vi的方式
robot/robot.txt
*** Settings ***
Library RequestsLibrary
Library SeleniumLibrary*** Variables ***
${demo_url} http://myblog.smile.com/admin*** Test Cases ***
api[Tags] criticalCreate Session api ${demo_url}${alarm_system_info} RequestsLibrary.Get Request api /log ${alarm_system_info.status_code}log ${alarm_system_info.content}should be true ${alarm_system_info.status_code} == 200
# 使用tools镜像启动容器,来验证手动使用robotframework来做验收测试 #在v2容器中
$ docker run --rm -ti smsmcopy/tools:v2 bash
bash-5.0# apk add chromium chromium-chromedriver
$ vi requirements.txt
robotframework
robotframework-seleniumlibrary
robotframework-databaselibrary
robotframework-requests#pip安装必要的软件包
$ pip install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txt #使用robot命令做测试
$ robot -d artifacts/ robot.txt

与tools工具镜像集成
FROM alpine
LABEL maintainer="55691759@qq.com"
USER rootRUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \apk update && \apk add --no-cache openrc docker git curl tar gcc g++ make \bash shadow openjdk8 python2 python2-dev py-pip python3-dev openssl-dev libffi-dev \libstdc++ harfbuzz nss freetype ttf-freefont chromium chromium-chromedriver && \mkdir -p /root/.kube && \usermod -a -G docker rootCOPY config /root/.kube/COPY requirements.txt /RUN pip install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txt RUN rm -rf /var/cache/apk/* && \rm -rf ~/.cache/pip#-----------------安装 kubectl--------------------#
COPY kubectl /usr/local/bin/
RUN chmod +x /usr/local/bin/kubectl
# ------------------------------------------------##---------------安装 sonar-scanner-----------------#
COPY sonar-scanner /usr/lib/sonar-scanner
RUN ln -s /usr/lib/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner && chmod +x /usr/local/bin/sonar-scanner
ENV SONAR_RUNNER_HOME=/usr/lib/sonar-scanner
# ------------------------------------------------#在构建镜像之前将requirements.txt 写入到tools目录中
vi requirements.txt
robotframework
robotframework-seleniumlibrary
robotframework-databaselibrary
robotframework-requestsdocker build -t 1008611zmx/tools:v3 .docker push 1008611zmx/tools:v3

让后 更新Jenkins wed界面中kubernetes中的containers template tools版本

安装插件:
安装robotFramework
-
插件中心搜索robotframework,直接安装

在源代码仓库添加 robbot.txt
cat robbot.txt
*** Settings ***
Library RequestsLibrary
Library SeleniumLibrary*** Variables ***
${demo_url} http://10.98.118.196/admin #修改为myblog的svc*** Test Cases ***
api[Tags] criticalCreate Session api ${demo_url}${alarm_system_info} RequestsLibrary.Get Request api /log ${alarm_system_info.status_code}log ${alarm_system_info.content}should be true ${alarm_system_info.status_code} == 200
python-demo项目添加robot.txt文件:
jenkins/pipelines/p10.yaml
pipeline {agent { label 'jnlp-slave'}options {buildDiscarder(logRotator(numToKeepStr: '10'))disableConcurrentBuilds()timeout(time: 20, unit: 'MINUTES')gitLabConnection('gitlab')}environment {IMAGE_REPO = "smsmcopay/myblog"DINGTALK_CREDS = credentials('dingtalk')DOCKERHUB_CREDS = credentials('dockerhub')TAB_STR = "\n \n "}stages {stage('printenv') {steps {script{sh "git log --oneline -n 1 > gitlog.file"env.GIT_LOG = readFile("gitlog.file").trim()}sh 'printenv'}}stage('checkout') {steps {container('tools') {checkout scm}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('build-image') {steps {container('tools') {retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('push-image') {steps {container('tools') {retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('deploy') {steps {container('tools') {sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"timeout(time: 1, unit: 'MINUTES') {sh "kubectl apply -f deploy/"}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('Accept Test') {steps {container('tools') {sh 'robot -i critical -d artifacts/ robot.txt|| echo ok'echo "R ${currentBuild.result}"step([$class : 'RobotPublisher',outputPath: 'artifacts/',outputFileName : "output.xml",disableArchiveOutput : false,passThreshold : 80,unstableThreshold: 20.0,onlyCritical : true,otherFiles : "*.png"])echo "R ${currentBuild.result}"archiveArtifacts artifacts: 'artifacts/*', fingerprint: true}}}}post {success { echo 'Congratulations!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😄👍 构建成功 👍😄 \n**项目名称**:smile \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'""" }failure {echo 'Oh no!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😖❌ 构建失败 ❌😖 \n**项目名称**:smile \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'"""}always { echo 'I will always say Hello again!'}}
}
在Jenkins中查看robot的构建结果。
cat Jenkinsfile
pipeline {agent { label 'jnlp-slave'}options {buildDiscarder(logRotator(numToKeepStr: '10'))disableConcurrentBuilds()timeout(time: 20, unit: 'MINUTES')gitLabConnection('10.99.69.119')}environment {IMAGE_REPO = "1008611zmx/myblog"DINGTALK_CREDS = credentials('dingtalk')DOCKERHUB_CREDS = credentials('dockerhub')TAB_STR = "\n \n "}stages {stage('printenv') {steps {script{sh "git log --oneline -n 1 > gitlog.file"env.GIT_LOG = readFile("gitlog.file").trim()}sh 'printenv'}}stage('checkout') {steps {container('tools') {checkout scm}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('CI'){failFast trueparallel {stage('Unit Test') {steps {echo "Unit Test Stage Skip..."}}stage('Code Scan') {steps {container('tools') {withSonarQubeEnv('sonarqube') {sh 'sonar-scanner -X'sleep 10}script {timeout(1) {def qg = waitForQualityGate('sonarqube')if (qg.status != 'OK') {error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"}}}}}}}}stage('build-image') {steps {container('tools') {retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('push-image') {steps {container('tools') {retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('deploy') {steps {container('tools') {sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"timeout(time: 1, unit: 'MINUTES') {sh "kubectl apply -f deploy/"}}updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')script{env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR}}}stage('Accept Test') {steps {container('tools') {sh 'robot -i critical -d artifacts/ robot.txt|| echo ok'echo "R ${currentBuild.result}"step([$class : 'RobotPublisher',outputPath: 'artifacts/',outputFileName : "output.xml",disableArchiveOutput : false,passThreshold : 80,unstableThreshold: 20.0,onlyCritical : true,otherFiles : "*.png"])echo "R ${currentBuild.result}"archiveArtifacts artifacts: 'artifacts/*', fingerprint: true}}}}post {success { echo 'Congratulations!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😄👍 构建成功 👍😄 \n**项目名称**:smile \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'""" }failure {echo 'Oh no!'sh """curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \-H 'Content-Type: application/json' \-d '{"msgtype": "markdown","markdown": {"title":"myblog","text": "😖❌ 构建失败 ❌😖 \n**项目名称**:smile \n**Git log**: ${GIT_LOG} \n**构建分支**: ${GIT_BRANCH} \n**构建地址**:${RUN_DISPLAY_URL} \n**构建任务**:${BUILD_TASKS}"}}'"""}always { echo 'I will always say Hello again!'}}
}
小结
思路:
-
讲解最基础的Jenkins的使用
-
Pipeline流水线的使用
-
Jenkinsfile的使用
-
多分支流水线的使用
-
与Kubernetes集成,动态jnlp slave pod的使用
-
与sonarqube集成,实现代码扫描
-
与Robotframework集成,实现验收测试
问题:
-
Jenkinsfile过于冗长
-
多个项目配置Jenkinsfile,存在很多重复内容
-
没有实现根据不同分支来部署到不同的环境
-
Java项目的构建
-
k8s部署后,采用等待的方式执行后续步骤,不合理
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
