持续集成和部署(Jenkins)
书接上文jenkins自动化部署go【docker+jenkins+go+gitlab+harbor+k8s】 我原计划是想把jenkins安装到docker,后来搞了一些时间也没有搞定所以才安装在ubuntu虚拟机上,这次尝试安装到k8s上,关于nfs的安装大家可以参考 ubuntu kubernetes中使用NFS创建pv_pvc
这里 jenkins 使用的存储为 NFS
安装 nfs 工具
#1安装nfs服务端
sudo apt install nfs-kernel-server -y
#2. 创建目录
sudo mkdir -p /nfs/jenkins
#3. 使任何客户端均可访问
sudo chown nobody:nogroup /nfs/jenkins
#sudo chmod 755 /nfs/jenkins
sudo chmod 777 /nfs/jenkins
#4. 配置/etc/exports文件, 使任何ip均可访问(加入以下语句)
vi /etc/exports
/nfs/jenkins *(rw,sync,no_subtree_check,no_root_squash)
#5. 检查nfs服务的目录
sudo exportfs -ra (重新加载配置)
sudo showmount -e (查看共享的目录和允许访问的ip段)
#6. 重启nfs服务使以上配置生效
sudo systemctl restart nfs-kernel-server
#sudo /etc/init.d/nfs-kernel-server restart
#查看nfs服务的状态是否为active状态:active(exited)或active(runing)
systemctl status nfs-kernel-server
#7. 测试nfs服务是否成功启动
#安装nfs 客户端
sudo apt-get install nfs-common
#创建挂载目录
sudo mkdir /nfs/jenkins/
#7.4 在主机上的Linux中测试是否正常
sudo mount -t nfs -o nolock -o tcp 192.168.100.11:/nfs/jenkins/ /nfs/jenkins/(挂载成功,说明nfs服务正常)
创建 nfs-client-provisioner deployment
kubectl apply -f nfs-client-provisioner.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-client-provisioner
namespace: kube-system
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: jenkinsnfs # 注意这里的值不能有下划线 _
- name: NFS_SERVER
value: 192.168.100.11
- name: NFS_PATH
value: /nfs/jenkins
volumes:
- name: nfs-client-root
nfs:
server: 192.168.100.11
path: /nfs/jenkins
## 创建 RBAC 授权
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: kube-system
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: kube-system
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: kube-system
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: kube-system
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
创建storageclass
名称为 jenkinsnfs,并且 provisioner 需要与 deployment 中的 PROVISIONER_NAME对应,注意这个变量不能有下划线 _
kubectl apply -f storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs
namespace: kube-ops
provisioner: jenkinsnfs
parameters:
archiveOnDelete: "true" # "false" 删除PVC时不会保留数据,"true"将保留PVC数据
创建 jenkins-deployment.yaml
kubectl apply -f jenkins-deployment.yaml
# 创建一个新的 namespace kube-ops
apiVersion: v1
kind: Namespace
metadata:
name: kube-ops
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: jenkins-claim
namespace: kube-ops
annotations:
volume.beta.kubernetes.io/storage-class: "nfs"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
# jenkins 对应的 RBAC
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-admin
namespace: kube-ops
labels:
name: jenkins
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jenkins-admin
labels:
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins-admin
namespace: kube-ops
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
# jenkins 对应的 svc
---
apiVersion: v1
kind: Service
metadata:
name: jenkins
namespace: kube-ops
labels:
app: jenkins
spec:
type: NodePort
ports:
- name: http
port: 8080 #服务端口
targetPort: 8080
nodePort: 32001 #NodePort方式暴露 Jenkins 端口
- name: jnlp
port: 50000 #代理端口
targetPort: 50000
nodePort: 32002
selector:
app: jenkins
# jenkins Deployment
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
namespace: kube-ops
labels:
app: jenkins
spec:
selector:
matchLabels:
app: jenkins
replicas: 1
template:
metadata:
labels:
app: jenkins
spec:
serviceAccountName: jenkins-admin
containers:
- name: jenkins
image: jenkins/jenkins:lts-alpine
securityContext:
runAsUser: 0 #设置以ROOT用户运行容器
privileged: true #拥有特权
ports:
- name: http
containerPort: 8080
- name: jnlp
containerPort: 50000
resources:
limits:
memory: 2Gi
cpu: "200m"
requests:
memory: 2Gi
cpu: "100m"
env:
- name: LIMITS_MEMORY
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Mi
- 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,设置该选项会影响 jenkins-slave 的启动
volumeMounts: #设置要挂在的目录
- name: data
mountPath: /var/jenkins_home
volumes:
- name: data
persistentVolumeClaim:
claimName: jenkins-claim #设置PVC
如果已经有nfs 以上可以省略【比如我后面用kubora创建nfs, k8s在2.0.5的版本修改以上yaml】
# jenkins 对应的 RBAC
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-admin
namespace: kube-system
labels:
name: jenkins
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jenkins-admin
labels:
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins-admin
namespace: kube-system
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Service
metadata:
name: jenkins
namespace: kube-system
labels:
app: jenkins
spec:
type: NodePort
ports:
- name: http
port: 8080 #服务端口
targetPort: 8080
nodePort: 32001 #NodePort方式暴露 Jenkins 端口
- name: jnlp
port: 50000 #代理端口
targetPort: 50000
nodePort: 32002
selector:
app: jenkins
# jenkins Deployment
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: jenkins
namespace: kube-system
labels:
app: jenkins
spec:
serviceName: jenkins
selector:
matchLabels:
app: jenkins
replicas: 1
template:
metadata:
labels:
app: jenkins
spec:
serviceAccountName: jenkins-admin
containers:
- name: jenkins
image: jenkins/jenkins:lts-alpine
securityContext:
runAsUser: 0 #设置以ROOT用户运行容器
privileged: true #拥有特权
ports:
- name: http
containerPort: 8080
- name: jnlp
containerPort: 50000
resources:
limits:
memory: 2Gi
cpu: "200m"
requests:
memory: 1Gi
cpu: "100m"
env:
- name: LIMITS_MEMORY
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Mi
- 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,设置该选项会影响 jenkins-slave 的启动
volumeMounts: #设置要挂在的目录
- name: data
mountPath: /var/jenkins_home
volumeClaimTemplates:
- metadata:
name: data
spec:
storageClassName: "nfs"
accessModes: [ "ReadWriteMany" ]
resources:
requests:
storage: 1Gi
获取 svc NodePort 的端口
root@k8s-master:/nfs# kubectl get pod -n kube-ops
NAME READY STATUS RESTARTS AGE
jenkins-6ccc9676d4-khpct 1/1 Running 0 6m57s
root@k8s-master:/nfs# kubectl get svc -n kube-ops
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
jenkins NodePort 10.99.157.233 <none> 8080:32001/TCP,50000:32002/TCP 23m
root@k8s-master:/nfs#
浏览器访问 http://192.168.100.11:32001/
进入 nfs server 查看密码
动态创建 jenkins-slave 构建任务
备注: 这个是我在网上找的:要不大家 就准备一张香港或者什么的卡,
把 Jenkins 插件源更改为国内
- 进入 Manage Jenkins -》 Manage Plugin -> Advanced 最下面有 Update Site 设置为:https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
- 修改服务器配置,进入 jenkins安装目录 , /updates/default.json ,将其中的 updates.jenkins-ci.org/download 替换为 mirrors.tuna.tsinghua.edu.cn/jenkins ,然后把www.google.com 修改为 www.baidu.com
cd /nfs/jenkins/kube-ops-jenkins-claim-pvc-f6ba2f57-6dd3-4309-889c-2cb383e09dfa/updates
cp default.json default.json.ori
sed -i "s#www.google.com#www.baidu.com#g" default.json
sed -i "s#updates.jenkins-ci.org/download#mirrors.tuna.tsinghua.edu.cn/jenkins#g" default.json
- 重启Jenkins服务
如果jenkins遇到 “There were errors checking the update sites: UnknownHostException: updates.jenkins.io” 错误, 可以考虑修改 jenkins 容器的dns 修改 vi /etc/resolv.conf文件中添加:
#nameserver 8.8.8.8
#nameserver 8.8.4.4
root@k8s-master:/nfs# kubectl get pod -n kube-ops
NAME READY STATUS RESTARTS AGE
jenkins-8447bcb8bc-c6kxk 1/1 Running 0 25m
root@k8s-master:/nfs# kubectl exec -it jenkins-8447bcb8bc-c6kxk -n kube-ops bash
bash-5.0# bash-5.0# cat /etc/resolv.conf
nameserver 10.96.0.10
search kube-ops.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
nameserver 8.8.8.8
nameserver 8.8.4.4
bash-5.0#
安装插件
Localization:Chinese
Git
Pipeline
Extended Choice Parameter
Kubernetes
实现Jenkins与Kubernetes整合
系统管理 -> 系统配置 -> 云 -> 新建云 -> Kubernetes
Kubernetes 地址: https://kubernetes.default.svc.cluster.local
Kubernetes 命名空间: kube-ops
然后点击"连接测试",如果出现 Connection test successful 的提示信息证明 Jenkins 已经可以和 Kubernetes 系统正常通信
Jenkins 地址: http://jenkins.kube-ops.svc.cluster.local:8080
测试
创建一个 流水线 任务,流水脚本如下
//创建一个Pod的模板,label为jenkins-slave
podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
containerTemplate(
name: 'jnlp',
image: "jenkins/jnlp-slave:latest"
)
]
)
{
//引用jenkins-slave的pod模块来构建Jenkins-Slave的pod
node("jenkins-slave"){
// 第一步
stage('测试'){
sh '''
echo "hello world"
'''
}
}
}
点击执行后,你可以在 Jenkins 的日志中看到有一个 jenkins-slave 节点生成来执行任务,任务执行完成后便自动销毁
如果遇到类似错误 “java.lang.NoSuchMethodError: No such DSL method 'stage' found ”, 就是缺少插件的安装。
配置 Jenkins Slave 运行的 Pod 模板
Jenkins 中除了使用 Pipeline 方式运行 Job 外,通常我们也会使用普通类型 Job,如果也要想使用kubernetes plugin 来构建任务,那么就需要点击 “系统管理” —> “系统设置” —> “云” —> “Kubernetes” —> “Add Pod Template” 进行配置 “Kubernetes Pod Template” 信息。
配置 Jenkins Slave 运行的 Pod 模板
以便在自由风格的软件项目中使用
Pod Templates 下面添加
名称 :jnlp
命名空间 :kube-ops
标签列表 :jenkins-slave
容器列表 添加
名称 : jnlp
Docker 镜像 :cnych/jenkins:jnlp6
运行的命令 和 命令参数 留空
挂载 /var/run/docker.sock 和 /root/.kube 和/usr/bin/docker 以便 cnych/jenkins:jnlp6 使用 kubectl 和 docker 命令
Service Account : jenkins-admin
测试
创建一个 自由风格的软件项目 mytest
标签表达式 :jenkins-slave # 以上面的模板一致
构建 -> 执行 shell
echo "测试 Kubernetes 动态生成 jenkins slave"
echo "==============docker in docker==========="
docker info
echo "=============kubectl============="
kubectl get pods
点击 构建执行 后,成功进行构建
GO的持续集成
由于我们的容器是没有go环境的 所以必须要有go plugin的支持【当然如有一个镜像有go,docker这些东西是更好的】【这里我尝试过很多方式都没有得到好的解决方案,就是能不能不从远程拉go的环境,比如从本地】
修改后的build.sh文件如下:
#!/bin/bash
#cd $WORKSPACE
export GOPROXY=https://goproxy.io
#根据 go.mod 文件来处理依赖关系。
go mod tidy
# linux环境编译
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main
# 构建docker镜像,项目中需要在当前目录下有dockerfile,否则构建失败
docker build -t gavintest .
docker tag gavintest 192.168.100.30:8080/go/gavintest:${BUILD_NUMBER}
docker login -u admin -p '123456' 192.168.100.30:8080
docker push 192.168.100.30:8080/go/gavintest
docker rmi gavintest
docker rmi 192.168.100.30:8080/go/gavintest:${BUILD_NUMBER}
kubectl set image deploy gavintest -n go gavintest=192.168.100.30:8080/go/gavintest:${BUILD_NUMBER}
kubectl rollout status deploy gavintest -n go
创建一个而自由风的项目:
构建结果【这里下载go比较耗时】
发现go 插件不好用,于是自己把 go的文件先下载下来, 然后放到内网环境【我这里是win10 下面】,脚本 内容如下:有些时候发现
go1.15.6.linux-amd64.tar.gz文件不需要重复下载,有时候提示 “wget: can't open 'go1.15.6.linux-amd64.tar.gz': File exists 实际就是文件已经存在
wget http://192.168.100.2/go1.15.6.linux-amd64.tar.gz
tar xfz go1.15.6.linux-amd64.tar.gz -C /usr/local
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
go version
-----------------------2021-04-15---------------------
我的jankin-slave 没有go,以前习惯于用docke commit 构建新的镜像, 后来发现比较困难, 还是用Dockerfile 内容如下:
FROM cnych/jenkins:jnlp6
ADD go /usr/local/
RUN export GOROOT=/usr/local/go
RUN export PATH=$PATH:$GOROOT/bin
WORKDIR /home/jenkins
ENTRYPOINT ["/usr/local/bin/jenkins-slave"]
制作镜像
docker build -t 192.168.100.30:8080/go/jenkins-agent:v1 .
docker push 192.168.100.30:8080/go/jenkins-agent
修改配置:
附上deploy的文件gv.yaml ,需要创建 kubectl create secret docker-registry regsecret --docker-server=192.168.100.30:8080 --docker-username=admin --docker-password=123456 -n=go
apiVersion: apps/v1
kind: Deployment
metadata:
name: gavintest
namespace: go
spec:
replicas: 3
minReadySeconds: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
selector:
matchLabels:
name: gavintest
template:
metadata:
labels:
name: gavintest
spec:
imagePullSecrets:
- name: regsecret
containers:
- name: gavintest
image: 192.168.100.30:8080/go/gavintest:20210301
ports:
- containerPort: 80
imagePullPolicy: Always
volumeMounts:
- mountPath: "/app/conf"
name: httpd-volume
volumes:
- name: httpd-volume
nfs:
server: 192.168.100.11
path: "/nfs/data"
---
apiVersion: v1
kind: Service
metadata:
name: gavintest
namespace: go
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
name: gavintest
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: gavintest
namespace: go
spec:
rules:
- host: go.k8s.com
http:
paths:
- path: /
backend:
serviceName: gavintest
servicePort: 80
参考:
https://www.cnblogs.com/djoker/p/12375881.html
https://www.cnblogs.com/sanduzxcvbnm/p/13821268.html
https://github.com/diodonfrost/docker-jenkins-slave/blob/master/ubuntu-18.04-jenkins-slave/Dockerfile.ubuntu-18.04
- 点赞
- 收藏
- 关注作者
评论(0)