部署Jenkins
系统环境:
• kubernetes 版本:1.23.3
• jenkins 版本:2.172
• jenkins 部署示例文件 Github 地址:https://github.com/my-dlq/blog-example/tree/master/jenkins-deploy
一、设置存储目录
在 Kubenetes 环境下所起的应用都是一个个 Docker 镜像,为了保证应用重启的情况下数据安全,所以需要将 Jenkins 持久化到存储中。这里用的是 NFS 网路存储,方便在 Kubernetes 环境下应用启动节点转义数据一致。当然也可以选择存储到本地,但是为了保证应用数据一致,需要设置 Jenkins 固定到某一 Kubernetes 节点。
1、安装 NFS 服务端
详情请看 CentOS7 搭建 nfs 服务器 ,这里不过多叙述。
2、挂载 NFS 并设置存储文件夹
如果不能直接操作 NFS 服务端创建文件夹,需要知道 NFS 服务器地址,然后将其挂在到本地目录,进入其中创建 Jenkins 目录空间。
(1)、挂载 NFS
$ mount -o vers=4.1 192.168.2.11:/nfs/ /nfs
(2)、在 NFS 共享存储 文件夹下创建存储 Jenkins 数据的文件夹
$ mkdir -p /nfs/data/jenkins
二、创建 PV & PVC
创建 PV 绑定 NFS 创建的 Jenkins 目录,然后创建 PVC 绑定这个 PV,将此 PVC 用于后面创建 Jenkins 服务时挂载的存储。
1、准备 PV & PVC 部署文件
一定要确保 PV 的空间大于 PVC,否则无法关联
jenkins-pv-pvc.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins
labels:
app: jenkins
spec:
capacity:
storage: Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
mountOptions: #NFS挂在选项
- hard
- nfsvers=4.1
nfs: #NFS设置
path: /nfs/data/jenkins
server: 192.168.2.11
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: jenkins
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: Gi #生产环境空间一定要设置比较大点
selector:
matchLabels:
app: jenkins
2、创建 PV & PVC
提前将 namespace 修改成你自己的 namespace
• -n:指定 namespace
$ kubectl apply -f jenkins-pv-pvc.yaml -n public
三、创建 ServiceAccount & ClusterRoleBinding
此 kubernetes 集群用的是 RBAC 安全插件,必须创建权限给一个 ServiceAccount,然后将此 ServiceAccount 绑定到 Jenkins 服务,这样赋予 Jenkins 服务一定权限执行一些操作,为了方便,这里将 cluster-admin 绑定到 ServiceAccount 以保证 Jenkins 能拥有一定的权限。
注意:请提前修改 yaml 中的 namespace
(1)、jenkins-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-admin #ServiceAccount名
namespace: mydlqcloud #指定namespace,一定要修改成你自己的namespace
labels:
name: jenkins
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: jenkins-admin
labels:
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins-admin
namespace: mydlqcloud
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
(2)、创建 RBAC 命令
$ kubectl create -f jenkins-rbac.yaml
四、创建 Service & Deployment
这里开始部署 Jenkins 服务,创建 Service 与 Deployment,其中 Service 暴露两个接口 80880 与 50000。而 Deployment 里面要注意的是要设置上面创建的 ServiceAccount ,并且设置容器安全策略为"runAsUser: 0"以 Root 权限运行容器,而且暴露8080、50000两个端口。
1、创建 Service & Deployment 部署文件
jenkins-deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: jenkins
labels:
app: jenkins
spec:
type: NodePort
ports:
- name: http
port: #服务端口
targetPort:
nodePort: #NodePort方式暴露 Jenkins 端口
- name: jnlp
port: #代理端口
targetPort:
nodePort:
selector:
app: jenkins
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
labels:
app: jenkins
spec:
selector:
matchLabels:
app: jenkins
replicas:
template:
metadata:
labels:
app: jenkins
spec:
serviceAccountName: jenkins-admin
containers:
- name: jenkins
image: registry.cn-shanghai.aliyuncs.com/mydlq/jenkins:2.172
securityContext:
runAsUser: #设置以ROOT用户运行容器
privileged: true #拥有特权
ports:
- name: http
containerPort:
- name: jnlp
containerPort:
resources:
limits:
memory: Gi
cpu: "1000m"
requests:
memory: Gi
cpu: "500m"
env:
- name: LIMITS_MEMORY
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: Mi
- name: "JAVA_OPTS" #设置变量,指定时区和 jenkins slave 执行者设置
value: "
-Xmx$(LIMITS_MEMORY)m
-XshowSettings:vm
-Dhudson.slaves.NodeProvisioner.initialDelay=0
-Dhudson.slaves.NodeProvisioner.MARGIN=50
-Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
-Duser.timezone=Asia/Shanghai
"
- name: "JENKINS_OPTS"
value: "--prefix=/jenkins" #设置路径前缀加上 Jenkins
volumeMounts: #设置要挂在的目录
- name: data
mountPath: /var/jenkins_home
volumes:
- name: data
persistentVolumeClaim:
claimName: jenkins #设置PVC
参数说明:
• JAVA_OPTS: JVM 参数设置
• JENKINS_OPTS: Jenkins 参数设置
• 设置执行任务时候不等待:
默认情况下,Jenkins生成代理是保守的。例如,如果队列中有两个构建,它不会立即生成两个执行器。它将生成一个执行器,并等待某个时间释放第一个执行器,然后再决定生成第二个执行器。Jenkins确保它生成的每个执行器都得到了最大限度的利用。如果你想覆盖这个行为,并生成一个执行器为每个构建队列立即不等待,所以在Jenkins启动时候添加这些参数:
• -Dhudson.slaves.NodeProvisioner.initialDelay=0
• -Dhudson.slaves.NodeProvisioner.MARGIN=50
• -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
2、部署 Jenkins
执行 Kuberctl 命令将 Jenkins 部署到 Kubernetes 集群。
注意:将"-n"后面的 namespace 换成你自己的 namespace
• -n:指定应用启动的 namespace
$ kubectl create -f jenkins-deployment.yaml -n mydlqcloud
五、获取 Jenkins 生成的 Token
在安装 Jenkins 时候,它默认生成一段随机字符串,用于安装验证。这里访问它的安装日志,获取它生成的 Token 字符串。
(1)、查看 Jenkins Pod 启动日志
注意:这里"-n"指的是要 namespace,后面跟的 namespace 请替换成你jenkins 启动的 namespace
$ kubectl log $(kubectl get pods -n mydlqcloud | awk '{print $1}' | grep jenkins) -n mydlqcloud
(2)、查看日志中生成的 Token 字符串
查看日志,默认给的token为:
*************************************************************
*************************************************************
*************************************************************
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:
3b3e0dda9d6746358ade987775f924ef
This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
*************************************************************
*************************************************************
*************************************************************
六、启动 Jenkins 进行安装
输入集群地址和 Jenkins Service 提供的 NodePort 端口,访问 Jenkins 进行安装步骤,可以按下一步步执行:
1、进入Jenkins
输入集群地址和上面设置的Nodeport方式的端口 32001,然后输入上面获取的 Token 字符串。例如,本人的集群IP为 192.168.2.11 ,所以就可以访问 http://192.168.2.11:32001/jenkins ,进入后可以看到下面的界面。
2、安装插件
选择自定义插件来进行安装
安装一些常用的插件,这里可以选择一下,推荐安装下面插件。
确定后可以看到正在安装插件界面
3、设置用户名、密码
然后进入 Jenkins 界面
七、其他
1、插件因网络问题安装失败
如果 Jenkins 安装插件失败,则可以按一下设置
• (1)、进入地址 http://192.168.2.11:32001/jenkins/pluginManager/advanced
• (2)、将最下面的 Update Site 的 URL 地址替换成:http://mirror.esuni.jp/jenkins/updates/update-center.json
• (3)、点"submit"按钮,然后点右下角角 "check now"
• (4)、然后输入地址 http://192.168.2.11:32001/jenkins/restart 重启 jenkins 后再重新安装插件
PS:上面地址替换成你们的集群地址及端口。
部署SonarQube
SonarQube 是一款用于代码质量管理的开源工具,它主要用于管理源代码的质量。 通过插件形式,可以支持众多计算机语言,比如 java, C#, go,C/C++, PL/SQL, Cobol, JavaScrip, Groovy 等。sonar可以通过PMD,CheckStyle,Findbugs等等代码规则检测工具来检测你的代码,帮助你发现代码的漏洞,Bug,异味等信息。 Sonar 不仅提供了对 IDE 的支持,可以在 Eclipse和 IntelliJ IDEA 这些工具里联机查看结果;同时 Sonar 还对大量的持续集成工具提供了接口支持,可以很方便地在持续集成中使用 Sonar。
Sonar可以从以下七个维度检测代码质量,而作为开发人员至少需要处理前5种代码质量问题。
-
不遵循代码标准 sonar可以通过PMD,CheckStyle,Findbugs等等代码规则检测工具规范代码编写。
-
潜在的缺陷 sonar可以通过PMD,CheckStyle,Findbugs等等代码规则检测工具检 测出潜在的缺陷。
-
糟糕的复杂度分布 文件、类、方法等,如果复杂度过高将难以改变,这会使得开发人员 难以理解它们, 且如果没有自动化的单元测试,对于程序中的任何组件的改变都将可能导致需要全面的回归测试。
-
重复 显然程序中包含大量复制粘贴的代码是质量低下的,sonar可以展示 源码中重复严重的地方。
-
注释不足或者过多 没有注释将使代码可读性变差,特别是当不可避免地出现人员变动 时,程序的可读性将大幅下降 而过多的注释又会使得开发人员将精力过多地花费在阅读注释上,亦违背初衷。
-
缺乏单元测试 sonar可以很方便地统计并展示单元测试覆盖率。
-
糟糕的设计 通过sonar可以找出循环,展示包与包、类与类之间的相互依赖关系,可以检测自定义的架构规则 通过sonar可以管理第三方的jar包,可以利用LCOM4检测单个任务规则的应用情况, 检测耦合。
1、环境介绍
1.1、kubernetes集群环境
[root@k8s-master-01 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master-01 Ready master 5d5h v1.23.3
k8s-master-02 Ready master 5d4h v1.23.3
k8s-master-03 Ready master 5d4h v1.23.3
k8s-node-01 Ready <none> 5d4h v1.23.3
k8s-node-02 Ready <none> 5d4h v1.23.3
k8s-node-03 Ready <none> 5d4h v1.23.3
1.2、存储环境
本集群中kubernetes底层存储使用的是glusterfs,并且以glusterfs作为存储创建了storageclass便于动态创建pv
[root@k8s-master-01 ~]# kubectl get sc
NAME PROVISIONER AGE
gluster-heketi kubernetes.io/glusterfs 4d22h
1.3、sonarqube版本
SonarQube版本:7.9.1
2、部署sonarqube
SonarQube
需要依赖数据库存储数据,且SonarQube7.9
及其以后版本将不再支持Mysql
,所以这里推荐设置PostgreSQL
作为SonarQube
的数据库。
2.1、部署[PostgreSQL]
在k8s
集群部署PostgreSQL
,需要将数据库的数据文件持久化,因此需要创建对应的pv
,本次安装通过storageclass
创建pv
。由于postgre
只需要集群内部连接,因此采用Headless service
来创建数据库对应的svc
,数据库的端口是5432
,最终的yaml如下
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-sonar
labels:
app: postgres-sonar
spec:
replicas: 1
selector:
matchLabels:
app: postgres-sonar
template:
metadata:
labels:
app: postgres-sonar
spec:
containers:
- name: postgres-sonar
image: postgres:11.4
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5432
env:
- name: POSTGRES\_DB
value: "sonarDB"
- name: POSTGRES\_USER
value: "sonarUser"
- name: POSTGRES\_PASSWORD
value: "123456"
resources:
limits:
cpu: 1000m
memory: 2048Mi
requests:
cpu: 500m
memory: 1024Mi
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: postgres-data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
spec:
accessModes:
- ReadWriteMany
storageClassName: "gluster-heketi"
resources:
requests:
storage: 1Gi
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
spec:
accessModes:
- ReadWriteMany
storageClassName: "gluster-heketi"
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: postgres-sonar
labels:
app: postgres-sonar
spec:
clusterIP: None
ports:
- port: 5432
protocol: TCP
targetPort: 5432
selector:
app: postgres-sonar
• 执行kubectl apply
创建资源,并检查对应的pv,pvc以及日志
[root@k8s-master-01 ~\]# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-f0157e05-427b-45af-8c09-9803b11f7036 1Gi RWX Retain Bound default/postgres-data gluster-heketi 133m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/postgres-data Bound pvc-f0157e05-427b-45af-8c09-9803b11f7036 1Gi RWX gluster-heketi 133m
[root@k8s-master-01 ~\]# kubectl get pods
NAME READY STATUS RESTARTS AGE
[root@k8s-master-01 ~\]# kubectl get pods|grep postgres
postgres-sonar-75d7f8d99-sxdnn 1/1 Running 0 136m
2.2、部署SonarQube
先看看yaml,后面附上说明:
apiVersion: apps/v1
kind: Deployment
metadata:
name: sonarqube
labels:
app: sonarqube
spec:
replicas: 1
selector:
matchLabels:
app: sonarqube
template:
metadata:
labels:
app: sonarqube
spec:
initContainers:
- name: init-sysctl
image: busybox
imagePullPolicy: IfNotPresent
command: \["sysctl", "-w", "vm.max\_map\_count=262144"\]
securityContext:
privileged: true
containers:
- name: sonarqube
image: sonarqube:lts
ports:
- containerPort: 9000
env:
- name: SONARQUBE\_JDBC\_USERNAME
value: "sonarUser"
- name: SONARQUBE\_JDBC\_PASSWORD
value: "123456"
- name: SONARQUBE\_JDBC\_URL
value: "jdbc:postgresql://postgres-sonar:5432/sonarDB"
livenessProbe:
httpGet:
path: /sessions/new
port: 9000
initialDelaySeconds: 60
periodSeconds: 30
readinessProbe:
httpGet:
path: /sessions/new
port: 9000
initialDelaySeconds: 60
periodSeconds: 30
failureThreshold: 6
resources:
limits:
cpu: 2000m
memory: 2048Mi
requests:
cpu: 1000m
memory: 1024Mi
volumeMounts:
- mountPath: /opt/sonarqube/conf
name: data
subPath: conf
- mountPath: /opt/sonarqube/data
name: data
subPath: data
- mountPath: /opt/sonarqube/extensions
name: data
subPath: extensions
volumes:
- name: data
persistentVolumeClaim:
claimName: sonarqube-data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sonarqube-data
spec:
accessModes:
- ReadWriteMany
storageClassName: "gluster-heketi"
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
name: sonarqube
labels:
app: sonarqube
spec:
type: NodePort
ports:
- name: sonarqube
port: 9000
targetPort: 9000
nodePort: 30003
protocol: TCP
selector:
app: sonarqube
• 实践中value: "jdbc:postgresql://postgres-sonar:5432/sonarDB"中的postgres-sonar直接写入集群IP也可以的
• 通过官方的sonar
镜像部署,通过环境变量指定连接数据库的地址信息,同样通过storageclass
来提供存储卷,通过NodePort
方式暴露服务。
• 与常规部署不同的是,这里对sonar
通过init container
进行了初始化,执行修改了容器的vm.max_map_count
大小。修改这里的原因可以参考官方文档
修改此权限需要授权能执行系统命令
securityContext: privileged: true
2.3、访问检查
上述部署完成后,检查控制器创建的pod
是否正常,并通过nodeport方式访问即可,默认登录的用户名和密码是admin/admin 中文插件名称:Chinese Pack,安装过程在界面操作,这里省略
StorageClass部署流程
为什么需要StorageClass
在一个大规模的Kubernetes集群里,可能有成千上万个PVC,这就意味着运维人员必须实现创建出这个多个PV,此外,随着项目的需要,会有新的PVC不断被提交,那么运维人员就需要不断的添加新的,满足要求的PV,否则新的Pod就会因为PVC绑定不到PV而导致创建失败.而且通过 PVC 请求到一定的存储空间也很有可能不足以满足应用对于存储设备的各种需求,而且不同的应用程序对于存储性能的要求可能也不尽相同,比如读写速度、并发性能等,为了解决这一问题,Kubernetes 又为我们引入了一个新的资源对象:StorageClass,通过 StorageClass 的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了
StorageClass部署流程
- • 要使用 StorageClass,我们就得安装对应的自动配置程序,比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的自动配置程序,我们也叫它 Provisioner,这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。
搭建StorageClass+NFS,大致有以下几个步骤:
1.创建一个可用的NFS Serve 2.创建Service Account.这是用来管控NFS provisioner在k8s集群中运行的权限 3.创建StorageClass.负责建立PVC并调用NFS provisioner进行预定的工作,并让PV与PVC建立管理 4.创建NFS provisioner.有两个功能,一个是在NFS共享目录下创建挂载点(volume),另一个则是建了PV并将PV与NFS的挂载点建立关联
创建StorageClass
部署NFS
• 在所有节点中执行
yum -y install nfs-utils rpcbind
systemctl enable rpcbind && systemctl start nfs
systemctl enable nfs-server && systemctl start rpcbind
• 创建共享目录
# 创建共享目录
mkdir /home/data
# 设置环境变量
vi /etc/exports
# 添加以下内容
/home/data *(insecure,rw,sync,no_subtree_check,no_root_squash)
# 重启服务
systemctl restart nfs
创建目录
添加nfs-subdir-external-provisioner仓库
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
查看nfs-subdir-external-provisioner所有版本
helm search repo nfs-subdir-external-provisioner --versions
拉取文件
helm pull nfs-subdir-external-provisioner/nfs-subdir-external-provisioner --version 4.0.14
Error: Get "https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/releases/download/nfs-subdir-external-provisioner-4.0.14/nfs-subdir-external-provisioner-4.0.14.tgz": unexpected EOF
根据报错去下载这个包
解压chart包
tar -xzvf nfs-subdir-external-provisioner-4.0.14.tgz
打开values配置文件
vi nfs-subdir-external-provisioner/values.yaml
修改如下参数
replicaCount: 3 # 第1行,副本个数,根据自身需求设置,建议3个
image: registry.cn-beijing.aliyuncs.com/xngczl/nfs-subdir-external-provisione
repository: # 第5行,设置镜像仓库
tag: v4.0.0 # 第6行,镜像版本
nfs:
server: 192.168.1.101 # 第11行,nfs server端地址
path: /home/data # 第12行,nfs目录
storageClass:
defaultClass: true # 第27行,设置为默认存储类,如果不设置,使用存储类时需要指定
name: nfs-storage # 第31行,设置存储类资源名称
创建命名空间
kubectl create namespace nfs-storage
安装nfs-subdir-external-provisioner
helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner -n nfs-storage
查看pod
kubectl get pod -n nfs-storage
测试
创建PVC的yaml文件
cat > test-pvc.yaml << EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
spec:
storageClassName: nfs-storage # 指定存储类,如果设置了默认,可以去掉
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
EOF
创建pvc,会自动相应的pv
kubectl apply -f test-pvc.yaml -n nfs-storage
查看pvc和pv是否创建
kubectl get pvc,pv -n nfs-storage
关于StorageClass回收策略对数据的影响
第一种
# 配置
archiveOnDelete: "false"
reclaimPolicy: Delete #默认没有配置,默认值为Delete
#结果
1.pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
2.sc删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
3.删除PVC后,PV被删除且NFS Server对应数据被删除
第二种
#配置
archiveOnDelete: "false"
reclaimPolicy: Retain
#结果
1.pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
2.sc删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
3.删除PVC后,PV不会别删除,且状态由Bound变为Released,NFS Server对应数据被保留
4.重建sc后,新建PVC会绑定新的pv,旧数据可以通过拷贝到新的PV中
第三种
#配置
archiveOnDelete: "ture"
reclaimPolicy: Retain
#结果
1.pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
2.sc删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
3.删除PVC后,PV不会别删除,且状态由Bound变为Released,NFS Server对应数据被保留
4.重建sc后,新建PVC会绑定新的pv,旧数据可以通过拷贝到新的PV中
第四种
#配置
archiveOnDelete: "ture"
reclaimPolicy: Delete
#结果
1.pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
2.sc删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
3.删除PVC后,PV不会别删除,且状态由Bound变为Released,NFS Server对应数据被保留
4.重建sc后,新建PVC会绑定新的pv,旧数据可以通过拷贝到新的PV中
总结:除以第一种配置外,其他三种配置在PV/PVC被删除后数据依然保留