CICD core concepts
CICD continuous integration (continuous integration, CI), continuous delivery (continuous delivery, CD), continuous deployment (continuous Deployment, CD) for short.
Refers to automatically perform a series of scripts in the development process to reduce the probability of the development of bug introduced, the process from development to deployment, minimize manual intervention in the new code.
1, continuous integration
continuous integration means that (several times a day) code frequently integrated into the trunk.
It has two benefits:
1) errors quickly: every little update is completed, it is integrated into the trunk, you can quickly find errors, positioning errors is relatively easy.
2) prevent a substantial deviation from the trunk branch: If it is not often integrated, trunk and is continually updated, will lead to the future integration of difficulty increases, even difficult to integrate.
Continuous integration purpose:
so that products can iterate quickly while maintaining high quality, its core measures are: before the code is integrated into the trunk, must pass the test automation, as long as there is a test case fails, it can not be integrated.
Martin Fowler said, "Continuous Integration does not eliminate the Bug, but to make them very easy to find and correct."
2, continued to deliver
sustained delivery refers frequently to the new version of the software delivered to the quality of the team or user, for review if the review by the code to enter the production stage.
Continuous delivery can be seen as the next step in continuous integration, emphasized that in any case the update, the software can be delivered anytime, anywhere.
3, continuous deployment
continued deployment is the next step continuous delivery, by referring to the code after the review, automated deployment to production.
Continuous deployment goal: the code can be deployed at all times, you can enter the production stage.
Continued deployment if you are able to complete the test automation, build, deploy, and other steps.
持续部署与持续交付的区别,可以参考下图:
Docker,kubernetes的CICD实现思路
jenkins是一个比较流行的持续集成工具。
GitLab是存储镜像的镜像仓库。
流程详解:
由客户端将代码push推送到git仓库,gitlab上配置了一个webHook的东西可以触发Jenkins的构建。进入到Jenkins虚线范围内,它所做的事情非常多,从mvn构建代码,对代码进行静态分析,做单元测试,测试通过之后就可以build镜像,镜像构建成功后就把镜像push推送到Harbor镜像仓库中,镜像push推送到镜像仓库后,我们就可以调用kubernetes集群的restAPI更新服务,而后kubernetes接收到了更新的指令,从Harbor镜像仓库pull拉取镜像,从而完成服务的更新与重启,最后我们从客户端来访问kubernetes集群的服务。
项目实践
项目环境:
主机 | 操作系统 | ip地址 |
---|---|---|
k8s01(master) | Centos 7.3 | 172.16.1.30 |
k8s02(node01) | Centos 7.3 | 172.16.1.31 |
k8s03 (node02) | Centos 7.3 | 172.16.1.32 |
jenkins+gitlab+docker | Centos 7.3 | 172.16.1.33 |
项目实施:
本文所用到的安装包和插件我已上传至百度网盘:链接:https://pan.baidu.com/s/17mDLgSZ218a-zYhjYddhiA
提取码:nyxh
1,部署kubernetes集群(前三台主机):
部署kubernetes集群可参考博文kubeadm部署kubernetes集群
#确保k8s集群正常运行:
[root@master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 112d v1.15.0
node01 Ready <none> 112d v1.15.0
node02 Ready <none> 112d v1.15.0
2,集群中部署harbor私有仓库**
可以选择任意一台服务器,这里选择master作为harboar私有仓库。详细配置可参考博文kubernetes部署Harbor私有仓库
[root@master ~]# yum -y install yum-utils device-mapper-persistent-data lvm2
[root@master ~]# curl -L https://github.com/docker/compose/releases/download/1.25.0-rc4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
[root@master ~]# chmod +x /usr/local/bin/docker-compose
[root@master harbor]# wget https://storage.googleapis.com/harbor-releases/release-1.7.0/harbor-offline-installer-v1.7.4.tgz
[root@master harbor]# tar xf harbor-offline-installer-v1.7.4.tgz
[root@master harbor]# cd harbor/
[root@master harbor]# vim harbor.cfg
[root@master harbor]# ./install.sh #执行安装脚本
[Step 4]: starting Harbor ...
Creating network "harbor_harbor" with the default driver
Creating harbor-log ... done
Creating redis ... done
Creating registryctl ... done
Creating harbor-adminserver ... done
Creating harbor-db ... done
Creating registry ... done
Creating harbor-core ... done
Creating harbor-portal ... done
Creating harbor-jobservice ... done
Creating nginx ... done
✔ ----Harbor has been installed and started successfully.----
Now you should be able to visit the admin portal at http://172.16.1.30.
For more details, please visit https://github.com/goharbor/harbor .
#登录web 界面新建仓库,URL:http://172.16.1.30
默认用户名:admin 密码:Harbor12345
#修改docker配置文件(且重启docker)并登录harbor:(集群中的三个节点做相同操作)[root@master harbor]# vim /usr/lib/systemd/system/docker.service
[root@master harbor]# systemctl daemon-reload
[root@master harbor]# systemctl restart docker
[root@master harbor]# docker login -uadmin -pHarbor12345 172.16.1.30:80
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
#node01和node02操作同上(都需登录harbor)
#在harbor服务器(master)上,将镜像上传到私有仓库:
[root@master harbor]# docker pull nginx:latest
[root@master harbor]# docker tag nginx:latest 172.16.1.30:80/cicd/nginx:v1.0
[root@master harbor]# docker push 172.16.1.30:80/cicd/nginx:v1.0
The push refers to repository [172.16.1.30:80/cicd/nginx]
12fdf55172df: Pushed
002a63507c1c: Pushed
1c95c77433e8: Pushed
v1.0: digest: sha256:099019968725f0fc12c4b69b289a347ae74cc56da0f0ef56e8eb8e0134fc7911 size: 948
#创建认证登录密钥,以保证kubernetes能够拉取harbor仓库中的私有镜像:
[root@master harbor]# kubectl create secret docker-registry login --docker-server=172.16.1.30:80 --docker-username=admin --docker-password=Harbor12345
secret/login created
[root@master harbor]# kubectl get secrets
NAME TYPE DATA AGE
default-token-wswg2 kubernetes.io/service-account-token 3 113d
login kubernetes.io/dockerconfigjson 1 33s
#选择集群中的某个节点测试是否能够从Harbor私有仓库中拉取镜像:
[root@node01 ~]# docker pull 172.16.1.30:80/cicd/nginx:v1.0
v1.0: Pulling from cicd/nginx
1ab2bdfe9778: Pull complete
a17e64cfe253: Pull complete
e1288088c7a8: Pull complete
7ee7d3fe92e1: Pull complete
Digest: sha256:763562fbc544806c1305304da23d36d0532f082325a1c427996474e304313801
Status: Downloaded newer image for 172.16.1.30:80/cicd/nginx:v1.0
[root@node01 ~]# docker images | grep "172.16.1.30"
172.16.1.30:80/cicd/nginx v1.0 c6ad7233ab97 10 minutes ago 126MB
ok,证明kubernetes和Harbor私有仓库部署完毕。接下来部署jenkins+gitlab,并配置联动。
3,部署jenkins+gitlab
#在最后一台主机172.16.1.33上进行操作。
1),基本环境准备
#关闭防火墙:
[root@docker-cicd ~]# systemctl stop firewalld
[root@docker-cicd ~]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Docs: man:firewalld(1)
#禁用selinux:
[root@docker-cicd ~]# vim /etc/sysconfig/selinux
2)部署jenkins服务
[root@docker-cicd ~]# mkdir CICD
[root@docker-cicd ~]# cd CICD/
[root@docker-cicd CICD]# tar zxf jdk-8u131-linux-x64.tar.gz
[root@docker-cicd CICD]# mv jdk1.8.0_131/ /usr/java
#设置全局变量:
[root@docker-cicd CICD]# vim /etc/profile
在最后添加以下配置项:
export JAVA_HOME=/usr/java
export JRE_HOME=/usr/java/jre
export PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH
export CLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar
[root@docker-cicd CICD]# source /etc/profile #使其环境变量生效
[root@docker-cicd CICD]# java -version #验证java环境
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
#安装tomcat
[root@docker-cicd CICD]# tar zxf apache-tomcat-7.0.54.ta
[root@docker-cicd CICD]# mv apache-tomcat-7.0.54 /usr/tomcat7
[root@docker-cicd CICD]# cd /usr/tomcat7/webapps/
[root@docker-cicd webapps]# ls
docs examples host-manager manager ROOT
[root@docker-cicd webapps]# rm -rf * #删除tomcat网页目录原有文件
#将jenkins war包上传至tomcat的网页根目录下
[root@docker-cicd webapps]# ls
jenkins.war
#修改tomcat的字符集:
[root@docker-cicd webapps]# vim /usr/tomcat7/conf/server.xml
#在tomcat中定义jenkins的家目录(/data/jenkins)
[root@docker-cicd webapps]# vim /usr/tomcat7/bin/catalina.sh
export CATALINA_OPTS="-DJENKINS_HOME=/data/jenkins"
export JENKINS_JAVA_OPTIONS="-Djava.awt.headless=true -Dhudson.ClassicPluginStrategy.noBytecodeTransformer=true"
#启动tomcat(即启动jenkins)
[root@docker-cicd webapps]# /usr/tomcat7/bin/catalina.sh start
Using CATALINA_BASE: /usr/tomcat7
Using CATALINA_HOME: /usr/tomcat7
Using CATALINA_TMPDIR: /usr/tomcat7/temp
Using JRE_HOME: /usr/java/jre
Using CLASSPATH: /usr/tomcat7/bin/bootstrap.jar:/usr/tomcat7/bin/tomcat-juli.jar
Tomcat started.
[root@docker-cicd webapps]# ss -anput | grep 8080
tcp LISTEN 0 100 :::8080 :::* users:(("java",pid=41682,fd=46))
#浏览器登录jenkins web界面,在web界面中配置安装jenkins
URL:http://172.16.1.33:8080/jenkins
[root@docker-cicd webapps]# cat /data/jenkins/secrets/initialAdminPassword
36ceb6eeb1bc46af8ce20935b81d8fe8
将管理员密码粘贴上后,点击“继续”。
#左边是自动安装,右边是自定义安装,我们选择安装推荐的插件。
选择离线安装,断掉网络失败后,点击继续。
登录界面如下:
#安装必要插件
1)首先安装中文插件:系统管理----->插件管理----->avalilable(可选)然后搜索localization-zh-cn
安装成功后,重新启动jenkins,可以发现界面已经支持中文了:
#还需安装3个插件,搜索gitlab安装:Gitlab Authentication、GitLab、Gitlab Hook
安装过程可能较慢,耐心等待,
插件下载成功后,勾选重启jenkins选项即可:
如果网络问题导致无法下载成功,还可以在服务器目录下进行上传插件:/var/lib/jenkins/plugins这个目录是存放所有安装的插件。(插件已上传至网盘)
3)部署gitlab服务
[root@docker-cicd CICD]# rpm -ivh gitlab-ce-11.9.8-ce.0.el6.x86_64.rpm
warning: gitlab-ce-11.9.8-ce.0.el6.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID f27eab47: NOKEY
Preparing... ################################# [100%]
Updating / installing...
1:gitlab-ce-11.9.8-ce.0.el6 ################################# [100%]
#为了解决端口冲突,修改gitlab配置文件:
[root@docker-cicd CICD]# vim /etc/gitlab/gitlab.rb
因没有部署dns,所以url修改为本机ip,80默认是http服务的端口,unicorn[‘port’]默认是8080,同样与上边的jenkins8080端口冲突,所以分别修改为81和8081。
#初始化并启动gitlab:
[root@docker-cicd CICD]# gitlab-ctl reconfigure
#确定81端口正常监听:
[root@docker-cicd CICD]# netstat -anput | grep -w 81
tcp 0 0 0.0.0.0:81 0.0.0.0:* LISTEN 47382/nginx: master
#浏览器登录gitlab,并在web界面中进行配置
URL:http://172.16.1.33:81/
登录gitlab后需要先设置一个密码再登录,这里设置1234.com(需要符合密码复杂性要求,最少8位),用户默认为root。
#配置ssh密钥认证:
部署jenkins和gitlab这两个服务的联动,需要经过ssh验证。
首先在gitlab上绑定jenkins服务器的ssh公钥(因为是部署在同一台服务器上,所以绑定自己的公钥)
[root@docker-cicd ~]# ssh-keygen -t rsa
[root@docker-cicd ~]# cat /root/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDW7s9qm08DaiShtnjBbUUwOpFYmvYaqUuqrIrXHrvcGGQDlte6Ug0R7/K5Igz4HKsjZIFCCblzgH2VLIk6FM0L+eccKnW8/uSywQcKclgK3gqiTRBDiqSt4NqYQz1R8hEW3KbXkXXUIU8kJ3svehxzG0Y8lUrMy7zEhkJ6vod7z5wcJeayzVfkro6Xd4TMiRrasXilApRiIr1Sr9Z9dWgWQkCCM85pQeZZirsZ/w4fT6gwRpP1kTtRBggDnXGi7lfWkMbKYljKMuG4jp92K8mRBLyjsg6Lkq0hkKAOToftwmpCx8CA/05LhMexvDnJXyFg/o+8yG+o0qmrlSvojl+N root@docker-cicd
复制这个公钥,然后登陆gitlab,点击右上角的设置:
#在gitlab上创建一个代码仓库:
输入一个仓库的名字 权限选择公共的(public)然后直接点击创建:
点击新建一个new.file:
创建完成后,在本地测试是否能够拉取仓库中的文件:
[root@docker-cicd ~]# git clone [email protected]:root/my-test-project.git
[root@docker-cicd ~]# cd my-test-project/
[root@docker-cicd my-test-project]# ls
index.html
[root@docker-cicd my-test-project]# cat index.html
print: "hello world!!"
4)配置jenkins和gitlab联动
该URL链接:
#执行构建操作:
#!/bin/bash
#定义变量(存放数据的目录)
backupcode="/data/backcode/$JOB_NAME/$BUILD_NUMBER"
#创建该目录
mkdir -p $backupcode
#对jenkins家目录(/data/jenkins)下的workspace/设置所需拥有的权限
chmod 644 "$JENKINS_HOME"/workspace/"$JOB_NAME"/*
#使用rsync工具拷贝并同步数据
rsync -acP "$JENKINS_HOME"/workspace/"$JOB_NAME"/* $backupcode
#以下操作是编写一个Dockerfile文件(将修改的内容拷贝到新镜像中)
echo FROM 172.16.1.30:80/cicd/nginx:v1.0 > "$JENKINS_HOME"/workspace/Dockerfile #源镜像就使用私有仓库中镜像
echo COPY ./"$JOB_NAME"/* /usr/share/nginx/html/ >> "$JENKINS_HOME"/workspace/Dockerfile
#删除旧版本的镜像(因为存在会持续不断的进行构建)
docker rmi 172.16.1.30:80/cicd/nginx:v2.0
#使用docker build 通过该dockerfile构建新版本镜像
docker build -t 172.16.1.30:80/cicd/nginx:v2.0 /"$JENKINS_HOME"/workspace/.
#将新生成的新镜像push到私有仓库上
docker push 172.16.1.30:80/cicd/nginx:v2.0
#删除之前版本的deployment资源(nginx应用)
ssh [email protected] kubectl delete deployment nginx
#重新创建nginx应用(这次从仓库中拉取的镜像是新生成的镜像)
ssh [email protected] kubectl apply -f /root/yaml/nginx.yaml
注意:/root/yaml/nginx.yaml此路径是自定义的,后边需要在该目录下创建nginx的YAML文件(路径保持一致,否则会导致构建失败)
这里面写的是jenkins构建时会执行的shell脚本,这个脚本才是实现持续集成核心,它实现了下端kubernetes自动更新容器的操作。
注意:脚本中所定义的内容,得根据你们自己当前的环境进行定义(比如主机地址,私有仓库镜像和存放文件路径都是不同的),脚本编写较易,可根据自己的需求进行修改。
#编写完脚本后不要保存,下面的这个插件很重要,就是他实现自动化更新的webhook插件,首先复制下图的jenkins URL地址,然后去gitlab上绑定webhook:
复制url后点击保存:
##登录github进行操作
点击添加后,可以看到有报错提示:gitlab默认设置不允许向自己发送webhook(因为我们是部署在一台服务器上),如下图所示:
##修改全局配置,勾选允许发送本地请求:
#修改成功后,重新添加web hook:
#添加完成后,会出现一个版块,测试连接:
证明jenkins与gitlab联动成功。
#如果添加webhook出现以下报错信息,则需要在jenkins上的“全局安全配置” 中开启匿名访问权限,然后重新添加。报错信息如下图:
5)配置jenkins免密登录kubernetes集群
配置免密登录的目的是为了在构建时,能够去执行master中的YAML文件。
#将jenkins主机上的密钥对拷贝给master节点,以实现能够免密登录集群中的master:
[root@docker-cicd ~]# ssh-copy-id [email protected]
到此,集成环境部署完毕,接下来进行测试。
5,测试部署环境
在gitlab上新建代码,删除代码,修改代码,都会触发webhook进行自动部署。最终会作用在所有的nginx容器中,也就是我们的web服务器。
#首先将jenkins和gitlab这台主机登录harbor私有仓库:
1)安装docker:
[root@docker-cicd ~]# cd /etc/yum.repos.d/
[root@docker-cicd yum.repos.d]# wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
[root@docker-cicd yum.repos.d]# yum repolist #查看可用的repo源
[root@docker-cicd yum.repos.d]# yum -y install docker-ce
[root@docker-cicd yum.repos.d]# systemctl start docker
[root@docker-cicd yum.repos.d]# systemctl enable docker
[root@docker-cicd yum.repos.d]# docker -v
Docker version 19.03.6, build 369ce74a3c
2)修改docker配置文件:
[root@docker-cicd yum.repos.d]# vim /usr/lib/systemd/system/docker.service
#重新加载docker服务:
[root@docker-cicd yum.repos.d]# systemctl daemon-reload
[root@docker-cicd yum.repos.d]# systemctl restart docker
3)登录harbor仓库:
[root@docker-cicd ~]# docker login -uadmin -pHarbor12345 172.16.1.30:80
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
4)在master上创建在shell脚本中定义的nginx应用的YAML文件(部署web服务):
[root@master ~]# mkdir yaml
[root@master ~]# cd yaml/
[root@master yaml]# vim nginx.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2 #定义两个副本
template:
metadata:
labels:
web: nginx
spec:
containers:
- name: nginx
image: 172.16.1.30:80/cicd/nginx:v2.0 #镜像指定私有仓库中的镜像(注意:版本为在jenkins上新构建的版本)
imagePullPolicy: Always #定义镜像策略(每次创建pod时,总是从仓库中重新拉取镜像)
ports:
- containerPort: 80
imagePullSecrets: #添加imagePullSecrets字段,指定刚才创建的登录密钥(login)
- name: login
---
apiVersion: v1 #创建service资源(映射web端口为31234)
kind: Service
metadata:
name: nginx-svc
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 31234
selector:
web: nginx
#在gitlab上修改或新建代码(例如修改之前创建的index.html文件)
#点击提交后,会触发webhook,进行构建:
控制台输出信息中会显示构建过程中的详细信息,如果在构建的过程中那个环节出现了问题,也能在信息中准确的查看到,如下图所示:
#浏览器访问web服务(nginx),URL:http://172.16.1.30:31234
#在gitlab上重新修改代码:
//修改内容:<h1>hello world, This is two build</h1>
再次触发webhook,重新构建:
#重新通过浏览器访问nginx服务:
#第三次在gitlab上修改代码:
//修改代码为:<h1>hello world, This is three build</h1>
#重新通过浏览器访问nginx:
#在服务器本地查看上面构建的项目和文件:
[root@docker-cicd ~]# cd /data/
[root@docker-cicd data]# ls
backcode jenkins #backcode为我们创建的存放构建项目的目录,一个为jenkins的家目录
[root@docker-cicd data]# cd backcode/test/
[root@docker-cicd test]# ls #在当前目录下存放了所构建的项目
10 11 12 3 4 5 6 7 8 9
[root@docker-cicd test]# cat 12/index.html #而目录中的内容正是我们所构建的代码
<h1>hello world, This is three build</h1>
[root@docker-cicd test]# cd ../../jenkins/workspace/
[root@docker-cicd workspace]# ls
Dockerfile test
[root@docker-cicd workspace]# cat test/index.html
<h1>hello world, This is three build</h1>
#在jenkins家目录下存放了Dcokerfile文件和最新构建的代码
So far, CICD project deployed, Kubernetes in web applications, will gitlab automatically constructed by jenkins +, without human intervention, automatically deploy and update web interface, reducing the probability of a bug in the production, waiting time and higher product quality.