K8s中CI/CD持续集成
GIitlab介绍:
Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具,起源于Hudson(Hudson是商用的),主要用于持续、自动的构建/测试软件项目、监控外部任务的运行(这个比较抽象,暂且写上,不做解释)。Jenkins用Java语言编写,可在Tomcat等流行的servlet容器中运行,也可独立运行。通常与版本管理工具(SCM)、构建工具结合使用。常用的版本控制工具有SVN、GIT,构建工具有Maven、Ant、Gradle。
CI/CD
CI(Continuous integration,中文意思是持续集成)是一种软件开发时间。持续集成强调开发人员提交了新代码之后,立刻进行构建、(单元)测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。借用网络图片对CI加以理解。
CD(Continuous Delivery, 中文意思持续交付)是在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境(类生产环境)中。比如,我们完成单元测试后,可以把代码部署到连接数据库的Staging环境中更多的测试。如果代码没有问题,可以继续手动部署到生产环境。下图反应的是CI/CD 的大概工作模式。
一、容器交付流程
二、k8s部署项目流程:
环境:
三、部署(这里已经部署好k8s集群)
持续构建与发布是我们日常工作中必不可少的一个步骤,目前大多公司都采用 Jenkins 集群来搭建符合需求的 CI/CD 流程,然而传统的 Jenkins Slave 一主多从方式会存在一些痛点,比如:
- 主 Master 发生单点故障时,整个流程都不可用了
- 每个 Slave的配置环境不一样,来完成不同语言的编译打包等操作,但是这些差异化的配置导致管理起来非常不方便,维护起来也是比较费劲
- 资源分配不均衡,有的Slave 要运行的 job 出现排队等待,而有的 Slave 处于空闲状态 资源有浪费
- 每台 Slave 可能是物理机或者虚拟机,当Slave 处于空闲状态时,也不会完全释放掉资源。
正因为上面的这些种种痛点,我们渴望一种更高效更可靠的方式来完成这个 CI/CD 流程,而 Docker 虚拟化容器技术能很好的解决这个痛点,又特别是在 Kubernetes 集群环境下面能够更好来解决上面的问题,下图是基于 Kubernetes 搭建 Jenkins 集群的简单示意图:
从图上可以看到 Jenkins Master 和 Jenkins Slave 以 Pod 形式运行在 Kubernetes 集群的 Node 上,Master 运行在其中一个节点,并且将其配置数据存储到一个 Volume 上去,Slave 运行在各个节点上,并且它不是一直处于运行状态,它会按照需求动态的创建并自动删除。
这种方式的工作流程大致为:当 Jenkins Master 接受到 Build 请求时,会根据配置的 Label 动态创建一个运行在 Pod 中的 Jenkins Slave 并注册到 Master 上,当运行完 Job 后,这个 Slave 会被注销并且这个 Pod 也会自动删除,恢复到最初状态。
使用jenkins动态slave的优势:
- 服务高可用,当 Jenkins Master 出现故障时,Kubernetes 会自动创建一个新的 Jenkins Master容器,并且将 Volume 分配给新创建的容器,保证数据不丢失,从而达到集群服务高可用。
- 动态伸缩,合理使用资源,每次运行 Job时,会自动创建一个Jenkins Slave,Job 完成后,Slave 自动注销并删除容器,资源自动释放,而且 Kubernetes会根据每个资源的使用情况,动态分配 Slave 到空闲的节点上创建,降低出现因某节点资源利用率高,还排队等待在该节点的情况。
- 扩展性好,当Kubernetes 集群的资源严重不足而导致 Job 排队带时,可以很容易的添加一个 Kubernetes Node到集群中,从而实现扩展。
1、gitlab安装
[root@harbor ~]# docker pull twang2218/gitlab-ce-zh
我这里有harbor端口80、22、443被占用,gitlab端口改成81、23、444
[root@harbor ~]# docker run -d -p 81:80 -p 444:443 -p 23:22 --name gitlab --restart unless-stopped -v gitlab-config:/etc/gitlab -v gitlab-logs:/var/log/gitlab -v gitlab-data:/var/opt/gitlab twang2218/gitlab-ce-zh
用户名:root 密码:00000000
会发现http/ssh会出现容器id没有ip
外部访问提交代码url出现容器id
修改容器内部配置文件
vim /etc/gitlab/gitlat.rb #宿主机ip
docker restart gitlab
#容器里启动服务
docker exec id gitlab-ctl reconfigure
git仓库安装:
[root@harbor ~]# yum install git -y
初始化git init,会在当前目录下有一个.git文件
准备好提前的代码,解压
[root@harbor opt]# unzip tomcat-java-demo-master.zip
修改源码中配置文件
导入数据库
mysql> create database fp CHARACTER SET utf8 COLLATE utf8_general_ci;
Query OK, 1 row affected (0.01 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
gilab默认分支是master
添加gitlab地址
git remote add origin http://192.168.254.233:81/root/java-demo.git
git add . #添加到本地
git commit -m “说明”
这里会设置邮箱跟用户名
git config --global user.email "xxx"
git config --global user.name "xxx"
再次提交(git push -u origin +master强制上传)
git commit -m “说明”
git push origin master #推送到仓库
这里要输入密码不能做到后面自动化操作,我们这里配置免密
下次执行git push再次输入用户名之后,git就会记住用户名密码并在上述目录下创建.git-credentials文件,记录的就是输入的用户名密码。
2、使用Dockerflie制作Jenkins镜像
Jenkins war地址:https://www.jenkins.io/zh/download/
###画个重点这里我用tomcat中运行jenkins没问题后面jenkins cloud云部署websocket不能开启,导致不能正常动态扩展slaves。会从running->error一直不断重复创建。所以说这里使用java -jar 来就行了。经过大量测试发现,如果使用jenkins官网镜像就不会存在。
创建镜像:
[root@master jenkins]# docker build -t jenkins .
验证镜像是否可用:
[root@master jenkins]# docker run -itd --name jenkins -p 9999:8080 jenkins:latest
访问测试一哈:
推送到harbor仓库中:
先打标签在推送:
[root@master jenkins]# docker tag jenkins:latest hub.aiguigu.com/library/jenkins:v1
[root@master jenkins]# docker push hub.aiguigu.com/library/jenkins:v1
3、k8s部署jenkins使用yaml
-
A、创建命名空间便于管理
[root@master jenkins]# kubectl create ns devops
-
B、持久化存储PV/PVC
[root@master jenkins]# vim jenkins-pv-pvc.yaml apiVersion: v1 kind: PersistentVolume metadata: name: jenkins-pv namespace: devops spec: capacity: storage: 20Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain nfs: path: /data/nfs/jenkins server: 192.168.254.233 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: jenkins-pvc namespace: devops spec: accessModes: - ReadWriteMany resources: requests: storage: 20Gi
[root@master jenkins]# kubectl apply -f jenkins-pv-pvc.yaml
[root@master jenkins]# kubectl get pv -n devops [root@master jenkins]# kubectl get pvc -n devops
-
C、创建SA/RBAC
[root@master jenkins]# vim jenkins-sa.yaml apiVersion: v1 kind: ServiceAccount metadata: labels: app: jenkins name: jenkins-admin namespace: devops --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: jenkins-admin labels: app: jenkins namespace: devops roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io subjects: - kind: ServiceAccount name: jenkins-admin namespace: devops
-
D、创建deeployment
编写Deployment文件,我使用到了node标签apps.k8s.icjl/devops,打标签的命令如下:
这里给两个node打上标签kubectl label node your-node-name apps.k8s.icjl/devops= [root@master jenkins]# kubectl label node node1 apps.k8s.icjl/devops= node/node1 labeled [root@master jenkins]# kubectl label node node2 apps.k8s.icjl/devops= node/node2 labeled
查看一下:
[root@master jenkins]# vim jenkins-deploymeny.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: jenkins namespace: devops labels: app: jenkins spec: template: metadata: labels: app: jenkins spec: serviceAccountName: jenkins-admin imagePullSecrets: - name: ram-secret affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: apps.k8s.icjl/devops operator: Exists containers: - name: jenkins image: hub.aiguigu.com/library/jenkins:v4 imagePullPolicy: IfNotPresent volumeMounts: - name: jenkins-home mountPath: /root/.jenkins readOnly: false ports: - containerPort: 8080 name: web protocol: TCP - containerPort: 50000 name: agent protocol: TCP resources: limits: cpu: 1000m memory: 1Gi requests: cpu: 500m memory: 512Mi volumes: - name: jenkins-home persistentVolumeClaim: claimName: jenkins-pvc
[root@master jenkins]# kubectl apply -f jenkins-deploymeny.yaml
[root@master jenkins]# kubectl get pod -n devops
-
E、创建services服务
[root@master jenkins]# vim jenkins-service.yaml apiVersion: v1 kind: Service metadata: labels: app: jenkins name: jenkins namespace: devops spec: selector: app: jenkins type: NodePort ports: - name: web port: 8080 targetPort: web nodePort: 30002 - name: agent port: 50000 targetPort: agent nodePort: 30005 selector: app: jenkins
[root@master jenkins]# kubectl apply -f jenkins-service.yaml
网页验证:
[root@master jenkins]# kubectl get svc -n devops
-
F、创建ingress服务
[root@master jenkins]# vim jenkins-ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: name: jenkins labels: name: jenkins namespace: devops spec: rules: - host: www.myjenkins.com http: paths: - path: / backend: serviceName: jenkins servicePort: 8080
[root@master jenkins]# kubectl apply -f jenkins-ingress.yaml
本地配置hosts解析C:\Windows\System32\drivers\etc\hosts
4、jenkins使用
这个密码已经挂载出来了在192.168.254.233
cat /data/nfs/jenkins/secrets/initialAdminPassword
先默认,后面根据需要来装就行了
下载kubernetes/Git Parameter插件
配置java/maven
全局设置:
添加kubernetes信息:系统管理–>Cloud
Kubernetes地址:可以通过kubectl cluster-info命令获取
- Kubernetes 服务证书 key:本身所在的集群因为我们通过sa所以不需要 Kubernetes
- 命名空间:Jenkins的nodePod节点启动的namespace
- Jenkins地址:主节点8080端口通过nodeport暴露出来的,地址:端口
- Jenkins通道:主节点50000端口通过nodeport暴露出来的,地址:端口
接下来创建pipline
这里做一个测试看看是否能动态生成slave
项目测试:
直接在cloud设置k8s模板
pipline配置
构建触发器定时:这里写法和crontab一样
在gitlab仓库根目录下面编写jenkinsfile,现在192.168.254.233上面写,在提交到仓库。
[root@harbor tomcat-java-demo-master]# vim Jenkinsfile
def label = "jenkins-slave"
podTemplate(label: label, cloud: 'kubernetes',nodeSelector: 'apps.k8s.icjl/devops')
{
node(label) {
stage('pull code') {
git([branch: 'master', credentialsId: 'bbeb7d69-b162-482c-b720-7076170453ae', url: 'http://192.168.254.233:81/root/java-demo.git'])
}
stage('build') {
sh 'cd /home/jenkins/agent/workspace/java-demo/tomcat-java-demo-master && ls -al && mvn clean package -Dmaven.test.skip=ture'
}
stage('SonarQube') {
echo "质量扫描"
}
stage('制作镜像'){
echo "已经制作好镜像Dockerfile"
}
stage('构建镜像'){
sh '''
cd /home/jenkins/agent/workspace/java-demo/tomcat-java-demo-master
docker build -t tomcat-demon:v1 .
docker tag tomcat-demon:v1 hub.aiguigu.com/library/tomcat-demon:v2
'''
}
stage('上传到镜像仓库'){
sh '''
echo "192.168.254.233 hub.aiguigu.com">>/etc/hosts
touch /etc/password
echo "Harbor12345">/etc/password
cat /etc/password | docker login --username admin --password-stdin https://hub.aiguigu.com
docker push hub.aiguigu.com/library/tomcat-demon:v2
'''
}
stage('Deployment Services'){
sh """
cat > java-demo.yaml <<EOF
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: java-demo
name: java-demo
spec:
replicas: 1
selector:
matchLabels:
app: java-demo
template:
metadata:
labels:
app: java-demo
spec:
containers:
- image: hub.aiguigu.com/library/tomcat-demon:v2
name: java-demo
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
name: java-demo
spec:
type: NodePort
selector:
app: java-demo
ports:
- protocol: TCP
port: 8080
targetPort: 8080
EOF
"""
}
stage('deploy to k8s'){
sh 'kubectl apply -f java-demo.yaml'
}
}
后期出一个EFK/ELK以及Prometheus grafana企业级日志收集、监控平台。
关注博主不迷路,博主带你上高速!