在K8S实现CICD
- 我们要做什么?
- 整体规划
- 一 安装k8s
- 二 在工具集群安装jenkins
-
- 2.1 创建jenkins.yaml文件
- 2.2 创建本地目录
- 2.3 部署jenkins-master
- 2.4 关于jenkins-slave储备知识
- 2.5 安装并配置k8s插件(用于动态创建slave pod)
-
- 步骤1
- 步骤2
- 步骤3
- 步骤4:手动创建一个Pod Template
- 步骤5:测试pod Template
- 三 部署gitlab
-
- 3.1 在工具集群部署gitlab
- 3.2 添加解析
- 3.3 登录创建项目
- 四 开发机配置登录gitlab
-
- 4.1 为开发配置访问gitlab域名的解析
- 4.2 在开发机制作秘钥对
- 4.3 复制粘贴公钥到gitlab里
- 4.4 开发机器模拟更新代码
- 五 部署harbor
-
- 1、创建harbor命名空间
- 2、创建NFS外部供应商
- 3、安装helm
- 4、添加仓库地址
- 5、下载Chart包到本地
- 6、修改values.yaml
- 7、登录Harbor UI界面
- 六 打通jenkins与gitlab
-
- 6.1 储备知识
-
- 6.1.1 Pipeline
- 6.1.2 jenkins的Pipeline有几个核心概念
- 6.1.3 如何创建jenkins的Pipeline
- 6.2 牛刀小试
- 6.3 在指定的slave中构建任务
- 6.4 对接k8s
-
- 6.4.1、在gitab里创建一个项目,提供该项目的链接信息(略,之前已经创建过了详见3.3小节)
- 6.4.3、在gitlab里配置webhook
- 6.4.4、创建Jenkinsfile文件
-
- 6.4.4.1 思路概述
- 6.4.4.2 准备工作之创建三个凭证
- 6.4.4.3 准备工作之登录harbor创建一个存放go镜像的仓库
- 6.4.4.4 准备工作之先在测试集群与生产集群都事先创建好test.yaml
- 6.4.4.5 准备工作之开发机push初始代码
- 6.4.4.6 为该项目master分支创建Jenkinsfile文件并测试
- 6.4.4.7 为该项目的develop分支创建Jenkinsfile文件并测试
- 6.5 优化之为发布到生产集群添加交互式确认
- 6.6 总结Jenkinsfile的大致模板
前言
我们要做什么?
开发人员在本地merge完毕代码,push到代码仓库之后,如果是develop开发分支的代码则发布到k8s测试环境,如果是master分支的代码则发布到k8s生产环境
整体规划
注意:
1、但凡部署一个k8s集群,对cpu的要求是至少有2個核,实验环境自己玩的话,建议2G,如果自己的实验机不够,可以考虑1.5G
2、k8s工具集群需要运行jenkins、gitlab、harbor,需要消耗大量内存,建議8核16G,
3、工具集群与实际运行应用的测试或生产集群隔离开始有好处的,防止不必要的资源争抢
4、本套架构实际在公司里是用了3套k8s集群,我们想在实验环境里复现这套公司的完整架构,考虑到资源问题,k8s集群均采用单节点部署,多节点部署见
k8s部署应该是一个基本功。
5、有两套方案
(1)gitlab(配置Auto DevOPS)-》K8s
(2)gitlab-》jenkins->k8s(本文采用该方案)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZVY4GLJ-1689070560529)(/media/202306/2023-06-26_145957_4284170.8485550136061444.png)]
一 安装k8s
(1)基本配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QVWWFpeM-1689070560529)(/media/202306/2023-06-26_150018_3274490.12122526753387619.png)]
1、配置静态ip地址
略
2、 关闭NetworkManager
systemctl stop NetworkManager
systemctl disable NetworkManager
3、关闭selinux与防火墙
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/sysconfig/selinux
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce 0
systemctl stop firewalld.service
systemctl disable firewalld.service
4、关闭swap分区
# Kubernetes 1.8开始要求关闭系统的Swap,如果不关闭,默认配置下kubelet将无法启动,所以我们有两种处理方式,采用一种即可
方式一:关闭swap分区
swapoff -a # 先临时关闭,立即生效
sed -i 's/.*swap.*/#&/' /etc/fstab # 注释掉swap,永久关闭,保证即便重启主机也会生效
方式二: kubelet忽略swap
echo 'KUBELET_EXTRA_ARGS="--fail-swap-on=false"' > /etc/sysconfig/kubelet
5、配置主机名
hostnamectl set-hostname master
6、更新系统软件(排除内核)
yum install epel-release -y && yum update -y --exclud=kernel*
7、安装基础常用软件
yum install wget expect vim net-tools ntp bash-completion ipvsadm ipset jq iptables conntrack sysstat libseccomp -y
# 其他(选做)
yum -y install python-setuptools python-pip gcc gcc-c++ autoconf libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel
zlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel zip unzip ncurses ncurses-devel curl curl-devel e2fsprogs
e2fsprogs-devel krb5-devel libidn libidn-devel openssl openssh openssl-devel nss_ldap openldap openldap-devel openldap-clients
openldap-servers libxslt-devel libevent-devel ntp libtool-ltdl bison libtool vim-enhanced python wget lsof iptraf strace lrzsz
kernel-devel kernel-headers pam-devel tcl tk cmake ncurses-devel bison setuptool popt-devel net-snmp screen perl-devel
pcre-devel net-snmp screen tcpdump rsync sysstat man iptables sudo libconfig git bind-utils
tmux elinks numactl iftop bwm-ng net-tools expect
8、更新系统内核(docker 对系统内核要求比较高,最好使用4.4+),非必须操作,推荐做(注意:3.x内核与5.x内核加载的ipvs模块名是不同的,后续加载时需要注意一下)
一般来说,只有从https://www.kernel.org/ 下载并编译安装的内核才是官方内核,可以看出目前的稳定版版本为5.18.10
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3GhwdJtb-1689070560530)(/media/202306/2023-06-26_150114_2316170.43159386305829217.png)]
不过,大多数 Linux 发行版提供自行维护的内核,可以通过 yum 或 rpm 等包管理系统升级。
ELRepo是一个为Linux提供驱动程序和内核映像的存储库,这里的升级方案就是采用ELRepo提供的内核通道。
ELRepo官网:http://elrepo.org/tiki/tiki-index.php
# 1、升级系统内核
#查看 yum 中可升级的内核版本
yum list kernel --showduplicates
#如果list中有需要的版本可以直接执行 update 升级,多数是没有的,所以要按以下步骤操作
#导入ELRepo软件仓库的公共秘钥
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
#Centos7系统安装ELRepo
yum -y install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
#Centos8系统安装ELRepo
#yum -y install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm
#查看ELRepo提供的内核版本
yum --disablerepo="*" --enablerepo="elrepo-kernel" list available
#(1) kernel-lt:表示longterm,即长期支持的内核,会不断修复一些错误;当前为5.4.208。建议安装lt版
yum --enablerepo=elrepo-kernel install kernel-lt.x86_64 -y
#(2) kernel-ml:表示mainline,即当前主线的内核;会加入一些新功能。
# 若想安装主线内核则执行: yum --enablerepo=elrepo-kernel install kernel-ml.x86_64 -y
#查看系统可用内核,并设置启动项
sudo awk -F' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg
#0 : CentOS Linux (5.17.1-1.el7.elrepo.x86_64) 7 (Core)
#1 : CentOS Linux (3.10.0-1160.53.1.el7.x86_64) 7 (Core)
#2 : CentOS Linux (3.10.0-1160.el7.x86_64) 7 (Core)
#3 : CentOS Linux (0-rescue-20220208145000711038896885545492) 7 (Core)
#指定开机启动内核版本
grub2-set-default 0 # 或者 grub2-set-default 'CentOS Linux (5.17.1-1.el7.elrepo.x86_64) 7 (Core)'
#生成 grub 配置文件
grub2-mkconfig -o /boot/grub2/grub.cfg
#查看当前默认启动的内核
grubby --default-kernel
#重启系统,验证
uname -r
(2)安装k8s
1、安装docker
# 1、选做,卸载之前的docker
yum -y remove docker
docker-client
docker-client-latest
docker-common
docker-latest
docker-latest-logrotate
docker-logrotate
docker-selinux
docker-engine-selinux
docker-engine
# 2、安装docker所需安装包
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum install docker-ce -y
# 3、启动并设置开机启动
systemctl start docker && systemctl enable docker && systemctl status docker
# 4、基本配置
cat > /etc/docker/daemon.json
2、拉取镜像到本地
kubeadm部署时会去指定的地址拉取镜像,该地址在墙外无法访问,所以我们从阿里云拉取,并tag为指定的地址即可
#1、=====>编写脚本
cat > dockpullImages1.18.1.sh
3、安装kubelet、kubeadm 和 kubectl
kubelet 运行在 Cluster 所有节点上,负责启动 Pod 和容器。
kubeadm 用于初始化 Cluster。
kubectl 是 Kubernetes 命令行工具。通过 kubectl 可以部署和管理应用,查看各种资源,创建、删除和更新各种组件。
cat /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
sed -ri 's/gpgcheck=1/gpgcheck=0/g' /etc/yum.repos.d/kubernetes.repo
安装
1.安装
yum makecache fast
# 注意,如果直接执行 yum install -y kubelet kubeadm kubectl ipvsadm 默认是下载最新版本v1.22.2
======================================================================
[root@master ~]# yum install -y kubelet-1.18.1-0.x86_64 kubeadm-1.18.1-0.x86_64 kubectl-1.18.1-0.x86_64 ipvsadm
2.加载ipvs相关内核模块
yum install -y conntrack-tools ipvsadm ipvsadmin ipset conntrack libseccomp
如果重新开机,需要重新加载(可以写在 /etc/rc.local 中开机自动加载)
modprobe ip_vs
modprobe ip_vs_rr
modprobe ip_vs_wrr
modprobe ip_vs_sh
#modprobe nf_conntrack_ipv4 # 如果是3.x内核,那么应该加载这一行
modprobe nf_conntrack # 如果是高版本内核比如5.x,那么应该加载这个。在高版本内核已经把nf_conntrack_ipv4替换为nf_conntrack了。
3.编辑文件添加开机启动
cat >> /etc/rc.local /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
vm.swappiness=0
EOF
5.使配置生效
sysctl --system
6.如果net.bridge.bridge-nf-call-iptables报错,加载br_netfilter模块
# modprobe br_netfilter
# sysctl -p /etc/sysctl.d/k8s.conf
7.查看是否加载成功
[root@master ~]# lsmod | grep ip_vs
ip_vs_sh 16384 0
ip_vs_wrr 16384 0
ip_vs_rr 16384 0
ip_vs 159744 6 ip_vs_rr,ip_vs_sh,ip_vs_wrr
nf_conntrack 151552 5 xt_conntrack,nf_nat,nf_conntrack_netlink,xt_MASQUERADE,ip_vs
nf_defrag_ipv6 24576 2 nf_conntrack,ip_vs
libcrc32c 16384 4 nf_conntrack,nf_nat,xfs,ip_vs
4、启动kubelet
#1.配置kubelet使用pause镜像
#配置变量:
systemctl start docker && systemctl enable docker
DOCKER_CGROUPS=$(docker info | grep 'Cgroup Driver' | cut -d' ' -f4)
echo $DOCKER_CGROUPS
#这个是使用国内的源。-###注意我们使用谷歌的镜像--操作下面的第3标题
#2.配置kubelet的cgroups
cat >/etc/sysconfig/kubelet/etc/sysconfig/kubelet
启动
systemctl daemon-reload
systemctl enable kubelet && systemctl restart kubelet
# 注意在这里使用 # systemctl status kubelet,你会发现报错误信息;
# 7月 10 23:28:36 master systemd[1]: Unit kubelet.service entered failed state.
# 7月 10 23:28:36 master systemd[1]: kubelet.service failed.
#运行 # journalctl -xefu kubelet 命令查看systemd日志会发现提示缺少一些问题件
#这个错误在运行kubeadm init 生成CA证书后会被自动解决,此处可先忽略。
#简单地说就是在kubeadm init 之前kubelet会不断重启。
5、初始化master,注意修改apiserver-advertise-address为master节点ip
kubeadm init
--kubernetes-version=v1.18.1
--service-cidr=10.96.0.0/12
--pod-network-cidr=10.244.0.0/16
--apiserver-advertise-address=172.16.10.14
--ignore-preflight-errors=Swap
参数解释:
–kubernetes-version: 用于指定k8s版本;
–apiserver-advertise-address:用于指定kube-apiserver监听的ip地址,就是 master本机IP地址。
–pod-network-cidr:用于指定Pod的网络范围; 10.244.0.0/16
–service-cidr:用于指定SVC的网络范围;
–image-repository: 指定阿里云镜像仓库地址
看到以下信息表示安装成功
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 172.16.10.14:6443 --token n3mvgw.56ul27rjtox7fr3n
--discovery-token-ca-cert-hash sha256:b93e84284d278e4056ee5a1b0370a20f49a1878df8a3492f5f855e2d5141e6e7
成功后注意最后一个命令,这个join命令可以用来添加节点,不添加的话默认当前master节点同时会被当做一个node节点加到k8s集群中。
注意保持好kubeadm join,后面会用到的。
如果初始化失败,请使用如下代码清除后重新初始化
kubeadm reset
6、按照提示配置kubectl
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 查看
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
7、kubernetes出于安全考虑默认情况下无法在master节点上部署pod,于是用下面方法去掉master节点的污点:
# 1、kubeadm init创建完集群后,当你部署pod时,查看kubectl describe pod会发现问题
3 node(s) had taints that the pod didn't tolerate.
# 2、解决方法
kubectl taint nodes --all node-role.kubernetes.io/master-
8、配置使用网络插件
要让 Kubernetes Cluster 能够工作,必须安装 Pod 网络,否则 Pod 之间无法通信。
Kubernetes 支持多种网络方案,这里我们先使用 flannel,后面还会讨论 Canal。
flannel.yaml
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: psp.flannel.unprivileged
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
spec:
privileged: false
volumes:
- configMap
- secret
- emptyDir
- hostPath
allowedHostPaths:
- pathPrefix: "/etc/cni/net.d"
- pathPrefix: "/etc/kube-flannel"
- pathPrefix: "/run/flannel"
readOnlyRootFilesystem: false
# Users and groups
runAsUser:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
fsGroup:
rule: RunAsAny
# Privilege Escalation
allowPrivilegeEscalation: false
defaultAllowPrivilegeEscalation: false
# Capabilities
allowedCapabilities: ['NET_ADMIN', 'NET_RAW']
defaultAddCapabilities: []
requiredDropCapabilities: []
# Host namespaces
hostPID: false
hostIPC: false
hostNetwork: true
hostPorts:
- min: 0
max: 65535
# SELinux
seLinux:
# SELinux is unused in CaaSP
rule: 'RunAsAny'
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: flannel
rules:
- apiGroups: ['extensions']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['psp.flannel.unprivileged']
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- apiGroups:
- ""
resources:
- nodes
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes/status
verbs:
- patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: flannel
namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
name: kube-flannel-cfg
namespace: kube-system
labels:
tier: node
app: flannel
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-flannel-ds
namespace: kube-system
labels:
tier: node
app: flannel
spec:
selector:
matchLabels:
app: flannel
template:
metadata:
labels:
tier: node
app: flannel
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
hostNetwork: true
priorityClassName: system-node-critical
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: flannel
initContainers:
- name: install-cni
image: registry.cn-hangzhou.aliyuncs.com/alvinos/flanned:v0.13.1-rc1
command:
- cp
args:
- -f
- /etc/kube-flannel/cni-conf.json
- /etc/cni/net.d/10-flannel.conflist
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d
- name: flannel-cfg
mountPath: /etc/kube-flannel/
containers:
- name: kube-flannel
image: registry.cn-hangzhou.aliyuncs.com/alvinos/flanned:v0.13.1-rc1
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
resources:
requests:
cpu: "100m"
memory: "50Mi"
limits:
cpu: "100m"
memory: "50Mi"
securityContext:
privileged: false
capabilities:
add: ["NET_ADMIN", "NET_RAW"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
- name: run
mountPath: /run/flannel
- name: flannel-cfg
mountPath: /etc/kube-flannel/
volumes:
- name: run
hostPath:
path: /run/flannel
- name: cni
hostPath:
path: /etc/cni/net.d
- name: flannel-cfg
configMap:
name: kube-flannel-cfg
部署flannel
kubectl apply -f flannel.yaml
查看
[root@master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 6m49s v1.18.1
[root@master ~]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-66bff467f8-mtrkv 1/1 Running 0 3m57s
coredns-66bff467f8-qhwv4 1/1 Running 0 3m57s
etcd-master 1/1 Running 0 4m9s
kube-apiserver-master 1/1 Running 0 4m9s
kube-controller-manager-master 1/1 Running 0 4m9s
kube-flannel-ds-b25g6 1/1 Running 0 32s
kube-proxy-gkqjv 1/1 Running 0 3m57s
kube-scheduler-master 1/1 Running 0 4m9s
二 在工具集群安装jenkins
持续的构建与发布在日常工作中必不可少,而基于k8s的CI/CD有很多工具可用,比如jenkins、Gitlab CI以及新兴的drone之类的,目前大多数公司仍然是部署jenkins集群来定制符合自己要求的CI/CD流程,所以本文采用的是jenkins
在工具集群安装jenkins,所以下述操作均在test06上执行
2.1 创建jenkins.yaml文件
采用的镜像jenkins/jenkins:lts是jenkins官网的默认镜像,若相定制自己的jenkins镜像,比如将一些插件直接打包到镜像里,可以参考:https://github.com/jenkinsci/docker
下面有两个版本的jenkins.yaml,推荐使用版本二,两者唯一的不同就是,版本二将jenkins的插件源mirrors.jenkins-ci.org解析到了127.0.0.1:80(安装完后可以进入pod内查看/etc/hosts文件,会多一条解析),然基于sidecar设计模式启动了一个监听127.0.0.1:80的nginx,该nginx将收到的请求都转发到了清华源,以此提升插件下载速度
版本一:
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins-pv
labels:
type: local
spec:
capacity:
storage: 5Gi
accessModes:
- "ReadWriteOnce"
hostPath:
path: /data/jenkins
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pvc
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
spec:
replicas: 1
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
containers:
- name: jenkins
image: jenkins/jenkins:lts-jdk11
ports:
- containerPort: 8080
name: web
protocol: TCP
- containerPort: 50000
name: agent
protocol: TCP
resources:
limits:
cpu: 1500m
memory: "6Gi"
requests:
cpu: 1500m
memory: "2048Mi"
readinessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home
volumes:
- name: jenkins-home
persistentVolumeClaim:
claimName: jenkins-pvc
---
apiVersion: v1
kind: Service
metadata:
name: jenkins-init-service
spec:
type: NodePort
ports:
- port: 7096
name: web1
nodePort: 7096
targetPort: 8080
- port: 50000
name: web2
nodePort: 50000
targetPort: 50000
selector:
app: jenkins
版本二:
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins-pv
labels:
type: local
spec:
capacity:
storage: 5Gi
accessModes:
- "ReadWriteOnce"
hostPath:
path: /data/jenkins
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pvc
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: ConfigMap
metadata:
name: jenkins-mirror-conf
data:
nginx.conf: |
user nginx;
worker_processes 3;
error_log /dev/stderr;
events {
worker_connections 10240;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" $request_time';
access_log /dev/stdout main;
server {
listen 80;
server_name mirrors.jenkins-ci.org;
location / {
proxy_redirect off;
proxy_pass https://mirrors.tuna.tsinghua.edu.cn/jenkins/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Accept-Encoding "";
proxy_set_header Accept-Language "zh-CN";
}
index index.html index.htm index.php;
location ~ /. {
deny all;
}
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
spec:
replicas: 1
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
hostAliases:
- ip: "127.0.0.1"
hostnames:
- "mirrors.jenkins-ci.org"
containers:
- name: mirror
image: nginx:1.7.9
ports:
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx
readOnly: true
name: nginx-conf
- name: jenkins
image: jenkins/jenkins:lts-jdk11
ports:
- containerPort: 8080
name: web
protocol: TCP
- containerPort: 50000
name: agent
protocol: TCP
resources:
limits:
cpu: 1500m
memory: "6Gi"
requests:
cpu: 1500m
memory: 2048Mi
readinessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home
volumes:
- name: jenkins-home
persistentVolumeClaim:
claimName: jenkins-pvc
- name: nginx-conf
configMap:
name: jenkins-mirror-conf
items:
- key: nginx.conf
path: nginx.conf
---
apiVersion: v1
kind: Service
metadata:
name: jenkins-init-service
spec:
type: NodePort
ports:
- port: 7096
name: web1
nodePort: 7096
targetPort: 8080
- port: 50000
name: web2
nodePort: 50000
targetPort: 50000
selector:
app: jenkins
2.2 创建本地目录
本例中pv用的是本地路径,所以需要在本机创建目录,该目录会作为jenkins master的pv挂载到/var/jenkins_home下
mkdir -p /data/jenkins
chmod o+w /data/jenkins
2.3 部署jenkins-master
如果直接部署会报错
The Service "jenkins-init-service" is invalid: spec.ports[0].nodePort: Invalid value: 7096: provided port is not in the valid range. The range of valid ports is 30000-32767
意思为k8s运行的svc端口范围只能在30000-32767,解决方法为
kubernetes默认端口号范围是 30000-32767 ,如果期望值不是这个区间则需要更改。
1、找到配置文件里,一般的在这个文件夹下: /etc/kubernetes/manifests/
2、找到文件名为kube-apiserver.yaml 的文件,也可能是json格式
3、编辑添加配置 service-node-port-range=1024-65535,如下图所示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H8IkZzvX-1689070560530)(/media/202306/2023-06-26_150606_3472190.5160812013443139.png)]
需要等待一会,k8s会自动加载配置,加载完毕后执行安装
kubectl apply -f jenkins.yaml
ps:会自动生成root的密码,用于登录jenkins的web界面,可以查看密码
# 如果你部署的是版本1那么查看
kubectl exec -ti jenkins-548bfffcb-mtc4t cat /var/jenkins_home/secrets/initialAdminPassword
# 如果你部署的是版本2那么查看
kubectl exec -ti jenkins-7c7c67bf9c-hvqms -c jenkins cat /var/jenkins_home/secrets/initialAdminPassword
2.4 关于jenkins-slave储备知识
jenkins主从模式介绍:
英文简称为 Master-Slave,基于分而治之、解耦合的核心思想,将一庞大的工作拆分,master主要负责基本管理、提供操作入口,例如流水线的创建与修改等,至于流水线的具体执行则交给slave去做。
为何要有主从模式呢?
因为日常构建 Jenkins 任务中,会经常出现下面的情况:
1、自动化测试需要消耗大量的 CPU 和内存资源,如果服务器上还有其他的服务,可能会造成卡顿或者宕机;
2、Jenkins 平台项目众多,如果同一时间构建大量的任务,会出现多个任务抢占资源的情况。
Jenkins 提供了主从模式(Master-Slave) 解决这个问题。我们可以为 Jenkins 配置多台 slave 从机,当 slave 从机和 Jenkins 服务建立连接之后,由 Jenkins 发指令给指定的 slave 从机运行任务,消耗的资源由 slave 从机去承担。
传统的jenkins一主多从架构是有缺点的
1、主 Master 发生单点故障时,整个流程都不可用了
2、每个 Slave 的配置环境不一样,来完成不同语言的编译打包等操作,但是这些差异化的配置导致管理起来非常不方便,维护起来也是比较费劲,
3、资源有浪费,每台 Slave 可能是物理机或者虚拟机,当 Slave 处于空闲状态时,也不会完全释放掉资源。或者有的 Slave 要运行的 job 出现排队等待,而有的 Slave 处于空闲状态,
如下图,我们将master跑在k8s里提供故障恢复能力,并且配置动态创建pod slave来解决上述2、3问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CSftCOXv-1689070560531)(/media/202306/2023-06-26_150639_1711310.4818021934277654.png)]
那什么是动态pod slave呢?
需要知道:传统的主从,是静态的,会造成镜像冗余,单个镜像很大
例如针对不同的程序比如java、go
你需要定义一个java的slave镜像,用于构建java环境,为了完成流水线,该镜像里需要有以下工具
1、拉代码:安装git工具
2、测试:代码检测工具
3、编译:java环境
4、打镜像:docker工具
5、推送到k8s:kubectl工具
你还需要定义一个go的slave镜像,用于构建go环境,为了完成流水线,该镜像里需要有以下工具
1、拉代码:安装git工具
2、测试:代码检测工具
3、编译:go环境
4、打镜像:docker工具
5、推送到k8s:kubectl工具
你会发现go的这个slave的镜像与java这个slave镜像需要安装的冗余部分太多
会造成镜像很大没必要
2.5小节里安装k8s插件,就是为了动态创建pod,每次构建都会动态产生一个pod在里面运行slave,该slave
pod里会启动多个容器用于流水线的不同阶段
此时针对工具,我们可以
1、制作一个安装git的小镜像
2、制作一个安装有代码检测工具的小镜像
3、制作一个镜像里面安装docker与kubectl工具
针对编译环境
1、为了构建java程序我们做一个安装有java环境的小镜像
2、为了构建go程序则做一个有go环境的小镜像
然后在动态的pod Template里组合使用上面的容器,构建java的流水线与构建go的流水线可以共用上面做的1、2、3小镜像
具体怎么实现呢?请看2.5小节
2.5 安装并配置k8s插件(用于动态创建slave pod)
接下来我们就需要来配置 Jenkins,让他能够动态的生成 Slave 的 Pod。
步骤1
我们需要安装 kubernetes 插件, 点击 Manage Jenkins -> Manage Plugins -> Available -> Kubernetes 勾选安装即可。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wzn9ZlLH-1689070560531)(/media/202306/2023-06-26_150743_1979780.5473396912438083.png)]
步骤2
先制作一个用于jenkins链接k8s工具集群的凭据(注意是工具集群,jenkins的pod slave肯定是要运行在工具集群里的嘛)
此功能是用于在k8s中启动一个jenkins节点pod,在cloud中已添加的集群名称下添加一个pod模板,例如有maven或者是其他打包工具的镜像,然后在新建项目中勾选限制项目的运行节点,选择云中配置的某个模板,执行此任务时就会在对应的k8s集群中启动一个用于运行该任务的pod,运行完成后自动销毁。
注意:这玩意不是让你用来使用kubectl命令执行上线部署的
首先需要生成jenkins所需的k8s密钥
在kubectl命令服务器上找到当初配置连接集群时的 config 文件,位置在 ~/.kube/config
# 命令
certificate_authority_data=$(awk -F': ' '/certificate-authority-data/{print $2}' ~/.kube/config)
client_certificate_data=$(awk -F': ' '/client-certificate-data/{print $2}' ~/.kube/config)
client_key_data=$(awk -F': ' '/client-key-data/{print $2}' ~/.kube/config)
echo "$certificate_authority_data" | base64 -d > ca.crt
echo "$client_certificate_data" | base64 -d > client.crt
echo "$client_key_data" | base64 -d > client.key
# 再生成jenkins使用的PKCS12格式的cert.pfx文件,需要设置密码,注意密码后期jenkins需要
# openssl pkcs12 -export -out cert.pfx -inkey client.key -in client.crt -certfile ca.crt
Enter Export Password: 在此输入密码egon123
Verifying - Enter Export Password: 在此输入密码egon123
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FANkPnqd-1689070560531)(/media/202306/2023-06-26_150804_8828180.11062114778501575.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZSaM5lPI-1689070560531)(/media/202306/2023-06-26_150810_2752720.11046539275512102.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9zMSTwWC-1689070560532)(/media/202306/2023-06-26_150816_6860670.5829576289468812.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cgNIePrZ-1689070560532)(/media/202306/2023-06-26_150821_8210270.4279185615441333.png)]
步骤3
安装好了kubernetes 插件,又有了凭据,接下来我可以配置jenkins链接k8s集群,需要注意的是配置方式新版与旧版不同
目前使用版本为Jenkins (2.346.1)为新版本
旧版找到配置按钮:
点击 Manage Jenkins —> Configure System —> (拖到最下方),如果有 Add a new cloud —> 选择 Kubernetes,然后填写 Kubernetes 和 Jenkins 配置信息,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LTdunEbE-1689070560532)(/media/202306/2023-06-26_150830_0900270.06379379171203048.png)]
新版本的 Kubernetes 插件将配置单独放置到了一个页面中链接地址为:http://172.16.10.16:7096/configureClouds/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NxiIhJyi-1689070560532)(/media/202306/2023-06-26_150838_6261570.357716979880205.png)]
链接测试通过后会显示:Connected to Kubernetes v1.18.1
接着上一步继续配置(如果上一步没有点击保存,则直接继续配置即可)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KXmG0EVK-1689070560533)(/media/202306/2023-06-26_150846_8311110.20530096363141626.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R07klAX7-1689070560533)(/media/202306/2023-06-26_150853_8004310.5054887369834744.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7QkCvxlN-1689070560533)(/media/202306/2023-06-26_150858_5835750.670910813468798.png)]
查看jenkins的svc地址,填入,然后保存,下图为何可以填入jenkins的svc地址,是因为工具相关都部署在k8s集群中,例如后面部署的gitlab就可以用svc地址访问jenkins,因为都是在一个k8s集群里嘛!
[root@test06 ~]# kubectl -n default get svc |grep jenkins
jenkins-init-service NodePort 10.100.229.121 7096:7096/TCP,50000:50000/TCP 88m
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YQzNEVgN-1689070560533)(/media/202306/2023-06-26_150914_6684630.37682620103061937.png)]
步骤4:手动创建一个Pod Template
配置 Pod Template,本质就是配置 Jenkins Slave 运行的 Pod 模板,为了能够让大家快速看到jenkins slave以一个pod的方式动态启动与销毁的效果,此处我们就先手动创建一个Pod Template,你需要事先知道的时候,这个Pod Template在后面我们是可以通过流水线代码自定义的,不同的流水线可以定义自己单独的Pod Template,完全不需要你在jenkins的web页面里手动创建一个固定死了的Pod Template,我们此处手动创建只是会提前让你体验一下动态床pod slave的效果而已,后期这个手动创建的固定死了的pod template都是可以删掉的。
手动创建一个Pod Template如下图操作,这里尤其注意Labels/标签列表 ,它非常重要,后面执行Job会通过该值选中,然后我们这里使用的是 cnych/jenkins:jnlp6 这个镜像,这个镜像是在官方的 jnlp 镜像基础上定制的,加入了 docker、kubectl 等一些实用的工具,
再次强调:此处我们添加一个pod Template只是为了用于案例演示(它的配置包括镜像啥的对我们来说都没啥用),后期我们会删掉该pod Template,然后用pipeline脚本定制pod Template、自己选择镜像,所以本小节手动创建一个Pod Template这一小节的所有步骤都只是为了演示,对后续的实际应用均没啥用。
系统配置->节点管理->Configure Clouds->Pod Templates->添加Pod模板->Pod Templates details
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B2inrGw0-1689070560533)(/media/202306/2023-06-26_150949_4274910.7918261464228786.png)]
然后我们这里需要在下面挂载两个主机目录,一个是 /var/run/docker.sock,该文件是用于 Pod 中的容器能够共享宿主机的 Docker,这就是大家说的 docker in docker 的方式,Docker 二进制文件已经打包到上面的镜像中了,另外一个目录下 /root/.kube 目录,我们将这个目录挂载到容器的 /root/.kube 目录下面这是为了让我们能够在 Pod 的容器中能够使用 kubectl 工具来访问我们的 Kubernetes 集群,方便我们后面在 Slave Pod 部署 Kubernetes 应用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jxVP7by-1689070560534)(/media/202306/2023-06-26_151000_7752850.5135524642252596.png)]
了解docker in docker
1、docker in docker 是什么?
docker in docker即在docker容器内运行docker指令
通常不建议在docker容器内运行docker,但在有些场景和业务上,比如我们的CICD,如果agent运行在容器里,我们就是需要在该容器内使用docker命令,比如执行docker pull 拉取镜像,以及docker build构建镜像等操作,docker命令都是提交给了docker的守护进程,所以agent里是需要能够访问到docker守护进程的,如何访问呢?通过套接字文件即可,具体做法就是把宿主机中运行的docker服务的套接字文件映射到jenkins agent容器中即可
2、如何实现docker in docker
把宿主机中运行的docker服务的套接字文件docker.sock挂载jenkins agent容器中,实现共享宿主机的docker.socket,这就使得在容器中可以使用宿主机上的docker daemon。
如此,我们便可以在容器内部使用docker pullpushbuild imagerun等命令了(这些命令的执行都是在与宿主机上面的docker daemon通信)
3、示例
docker run -it --name docker-daemon --hostname daemon-test --network=host -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -e DOCKER_HOST="unix:///var/run/docker.sock" centos:7 /bin/bash
–network: 指定容器的网络, 启动容器默认使用bridge网络,这里直接使用主机的网络
-e:设置环境变量,这里直接指定使用docker.sock访问docker daemon
-v: 挂载文件,直接将主机的docker.sock挂载至容器内,共享docker daemon;挂载docker命令脚本至容器内,共享docker服务
另外,我们在步骤5,启动 Slave Pod 执行测试命令时,有一条kubectl get pods查看pod信息,需要k8s权限才行,所以我们需要创建一个ServiceAccount
jenkinsAcount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins
namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: jenkins
rules:
- apiGroups: ["extensions", "apps"]
resources: ["deployments", "ingresses"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
resources: ["services"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
resources: ["pods/log", "events"]
verbs: ["get","list","watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: jenkins
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins
namespace: default
然后在 Slave Pod 配置的地方点击下面的高级,添加上对应的 ServiceAccount 即可:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fhA0b6Eo-1689070560534)(/media/202306/2023-06-26_151105_5020470.4464676589683707.png)]
步骤5:测试pod Template
创建一个job任务,测试能否启动一个pod来运行slave,并观察job运行完毕后pod是否自动销毁
jenkins首页->新建任务->输入一个任务名称、选择Freestyle project 类型的任务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Guuwvx6C-1689070560534)(/media/202306/2023-06-26_151120_7424970.5800700835375897.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q1sWNvmo-1689070560534)(/media/202306/2023-06-26_151146_1267550.04236676573396536.png)]
然后往下拉,在 Build 区域选择Execute shell
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y6DWzkZv-1689070560534)(/media/202306/2023-06-26_151152_9223120.0426708215454078.png)]
填入测试命令,点击保存
echo "测试 Kubernetes 动态生成 jenkins slave"
echo "==============docker in docker==========="
docker info
echo "=============kubectl============="
kubectl get pods
sleep 120
然后点击构建
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6yw32MIq-1689070560535)(/media/202306/2023-06-26_151205_7766850.1346267654795531.png)]
查看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kUolBrbo-1689070560535)(/media/202306/2023-06-26_151212_9275340.7096742963069186.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ri7kmkXQ-1689070560535)(/media/202306/2023-06-26_151217_7278710.8059480678642418.png)]
三 部署gitlab
注意,可以把gitlab也安装到工具集群中,但是会gitlab占用资源较多,需要注意一下
3.1 在工具集群部署gitlab
Gitlab 主要涉及到3个应用:Redis、Postgresql、Gitlab 核心程序,
我们这里选择使用的镜像不是官方的,而是 Gitlab 容器化中使用非常多的一个第三方镜像:sameersbn/gitlab,基本上和官方保持同步更新,地址:http://www.damagehead.com/docker-gitlab/
如果我们已经有可使用的 Redis 或 Postgresql 服务的话,那么直接配置在 Gitlab 环境变量中即可,如果没有的话就单独部署,我们这里为了展示 gitlab 部署的完整性,还是分开部署。
首先部署需要的 Redis 服务,对应的资源清单文件如下:(gitlab-redis.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: default
labels:
name: redis
spec:
selector:
matchLabels:
name: redis
template:
metadata:
name: redis
labels:
name: redis
spec:
containers:
- name: redis
resources:
limits:
cpu: 1
memory: "2Gi"
requests:
cpu: 1
memory: "2048Mi"
image: sameersbn/redis:4.0.9-2
imagePullPolicy: IfNotPresent
ports:
- name: redis
containerPort: 6379
volumeMounts:
- mountPath: /var/lib/redis
name: data
livenessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 30
timeoutSeconds: 5
readinessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 30
timeoutSeconds: 1
volumes:
- name: data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: default
labels:
name: redis
spec:
ports:
- name: redis
port: 6379
targetPort: redis
selector:
name: redis
然后是数据库 Postgresql,对应的资源清单文件如下:(gitlab-postgresql.yaml),存储pv、pvc根据自己的需求设置,
本例采用hostPath,先在主机创建目录
mkdir -p /data/postgresql
gitlab-postgresql.yaml文件内容如下,存储空间推进20Gi,但是因为是测试环境设置为10G
apiVersion: v1
kind: PersistentVolume
metadata:
name: postgresql-pv
namespace: default
labels:
type: local
spec:
capacity:
storage: 10Gi
accessModes:
- "ReadWriteOnce"
hostPath:
path: /data/postgresql
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgresql-pvc
namespace: default
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgresql
namespace: default
labels:
name: postgresql
spec:
selector:
matchLabels:
name: postgresql
template:
metadata:
name: postgresql
labels:
name: postgresql
spec:
containers:
- name: postgresql
resources:
limits:
cpu: 4
memory: "4Gi"
requests:
cpu: 2
memory: "2048Mi"
image: sameersbn/postgresql:10-2
imagePullPolicy: IfNotPresent
env:
- name: DB_USER
value: gitlab
- name: DB_PASS
value: passw0rd
- name: DB_NAME
value: gitlab_production
- name: DB_EXTENSION
value: pg_trgm
- name: USERMAP_UID
value: "999"
- name: USERMAP_GID
value: "999"
ports:
- name: postgres
containerPort: 5432
volumeMounts:
- mountPath: /var/lib/postgresql
name: data
readinessProbe:
exec:
command:
- pg_isready
- -h
- localhost
- -U
- postgres
initialDelaySeconds: 30
timeoutSeconds: 1
volumes:
- name: data
persistentVolumeClaim:
claimName: postgresql-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgresql
namespace: default
labels:
name: postgresql
spec:
ports:
- name: postgres
port: 5432
targetPort: postgres
selector:
name: postgresql
然后就是我们最核心的 Gitlab 的应用,对应的资源清单文件如下:(gitlab.yaml),存储也使用hostPath,请依据自己的情况填写存储,但是考虑到性能,还是建议用本地存储,可以考虑用localpath的存储类
创建目录
mkdir -p /data/gitlab
yaml文件内容如下
apiVersion: v1
kind: PersistentVolume
metadata:
name: gitlab-pv
namespace: default
labels:
type: local
spec:
capacity:
storage: 10Gi
accessModes:
- "ReadWriteOnce"
hostPath:
path: /data/gitlab
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitlab-pvc
namespace: default
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitlab
namespace: default
labels:
name: gitlab
spec:
selector:
matchLabels:
name: gitlab
template:
metadata:
name: gitlab
labels:
name: gitlab
spec:
initContainers:
- name: fix-permissions
image: busybox
command: ["sh", "-c", "chown -R 1000:1000 /home/git/data"]
securityContext:
privileged: true
volumeMounts:
- name: data
mountPath: /home/git/data
containers:
- name: gitlab
resources:
requests:
cpu: 2
memory: "2Gi"
limits:
cpu: 4
memory: "4Gi"
image: sameersbn/gitlab:12.9.5
imagePullPolicy: IfNotPresent
env:
- name: TZ
value: Asia/Shanghai
- name: GITLAB_TIMEZONE
value: Beijing
- name: GITLAB_SECRETS_DB_KEY_BASE
value: long-and-random-alpha-numeric-string
- name: GITLAB_SECRETS_SECRET_KEY_BASE
value: long-and-random-alpha-numeric-string
- name: GITLAB_SECRETS_OTP_KEY_BASE
value: long-and-random-alpha-numeric-string
- name: GITLAB_ROOT_PASSWORD
value: 'egon@666'
- name: GITLAB_ROOT_EMAIL
value: 18611453110@163.com
- name: GITLAB_HOST
value: git.k8s.local # 该域名会是你后面从gitlab里拉取项目的地址,需要添加解析才行
- name: GITLAB_PORT # 这个端口很重要,与svc对应好
value: "1180"
- name: GITLAB_SSH_PORT # 这个端口很重要,与svc对应好
value: "30022"
- name: GITLAB_NOTIFY_ON_BROKEN_BUILDS
value: "true"
- name: GITLAB_NOTIFY_PUSHER
value: "false"
- name: GITLAB_BACKUP_SCHEDULE
value: daily
- name: GITLAB_BACKUP_TIME
value: 01:00
- name: DB_TYPE
value: postgres
- name: DB_HOST
value: postgresql
- name: DB_PORT
value: "5432"
- name: DB_USER
value: gitlab
- name: DB_PASS
value: passw0rd
- name: DB_NAME
value: gitlab_production
- name: REDIS_HOST
value: redis
- name: REDIS_PORT
value: "6379"
ports:
- name: http
containerPort: 80
- name: ssh
containerPort: 22
volumeMounts:
- mountPath: /home/git/data
name: data
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 60
timeoutSeconds: 1
volumes:
- name: data
persistentVolumeClaim:
claimName: gitlab-pvc
---
apiVersion: v1
kind: Service
metadata:
name: gitlab
namespace: default
labels:
name: gitlab
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: http
nodePort: 1180
- name: ssh
port: 22
targetPort: ssh
nodePort: 30022
selector:
name: gitlab
查看
[root@test06 ~]# kubectl get pods |grep -v jenkins
NAME READY STATUS RESTARTS AGE
gitlab-5954f566bf-lcl7j 1/1 Running 0 99s
postgresql-8b5cfff54-5qfdl 1/1 Running 0 108s
redis-bb67747bf-bnkzd 1/1 Running 0 103s
[root@test06 ~]# kubectl get svc |grep gitlab
gitlab NodePort 10.97.217.189 80:1180/TCP,22:30022/TCP 110s
强调:gitlab相关信息
关于gitlab.yaml中的一些环境变量的设置
- name: GITLAB_ROOT_PASSWORD
value: 'egon@666'
- name: GITLAB_ROOT_EMAIL
value: 18611453110@163.com
- name: GITLAB_HOST
value: git.k8s.local
- name: GITLAB_PORT
value: "1180"
- name: GITLAB_SSH_PORT
value: "30022"
# 1、账号
账号名:root
密码为:egon@666
# 2、svc采用nodePort
web访问端口固定为80
SSH端口固定为30022
# 3、结合GITLAB_HOST:git.k8s.local
gitlab的web访问地址为:
http://git.k8s.local:1180
gitlab的ssh访问地址为
git.k8s.local:30022
# 4、GITLAB_HOST:git.k8s.local用来访问gitlab的主机,我们设置为域名,需要为访问者添加指向该域名的地址解析
因为我们的svc采用的是nodeport,并且端口固定,所以搭配一个k8s节点的ip地址就可以访问到
所以我们选取gitlab所在k8s集群中的任意一个节点的ip地址,我们就一个节点,地址为172.16.10.16
所以我们让git.k8s.local解析到该地址就可以
需要访问gitlab的有哪些,就在哪里添加解析
1、一个是k8s的工具集群,里面安装有jenkins需要访问gitlab
2、另外一个是开发机
3、需要访问gitlab的web界面的用户
3.2 添加解析
在工具集群添加自定义解析
# 1、添加自定义解析
[root@test06 ~]# kubectl -n kube-system edit cm coredns
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
hosts { # 添加自定义解析
172.16.10.16 git.k8s.local
fallthrough
}
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
creationTimestamp: "2022-07-12T02:37:57Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data: {}
manager: kubeadm
operation: Update
time: "2022-07-12T02:37:57Z"
# 2、重启coredns
kubectl -n kube-system scale deployment coredns --replicas=0
kubectl -n kube-system scale deployment coredns --replicas=2
# 3、测试
[root@test06 ~]# cat test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: test
name: test
spec:
replicas: 1
selector:
matchLabels:
app: test
strategy: {}
template:
metadata:
labels:
app: test
spec:
containers:
- image: centos:7
imagePullPolicy: IfNotPresent
name: test
command: ["sh","-c","tail -f /dev/null"]
[root@test06 ~]# kubectl apply -f test.yaml
[root@test06 ~]# kubectl exec -ti test-75bf4b886f-xkht7 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.
sh-4.2#
sh-4.2# ping git.k8s.local
PING git.k8s.local (172.16.10.16) 56(84) bytes of data.
64 bytes from git.k8s.local (172.16.10.16): icmp_seq=1 ttl=64 time=0.024 ms
64 bytes from git.k8s.local (172.16.10.16): icmp_seq=2 ttl=64 time=0.024 ms
在开发机172.16.10.17添加解析
echo "172.16.10.16 git.k8s.local" >> /etc/hosts
在要访问gitlab的web界面进行操作的主机添加hosts文件解析,或者干脆用ip地址172.16.10.16访问也行
比如我们要在windows主机以域名的方式访问gitlab的webui,那么配置HOSTS解析
# 编辑文件,路径如下
C:WindowsSystem32driversetcHOSTS
#
添加解析
172.16.10.16 git.k8s.local
3.3 登录创建项目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6zKcflSC-1689070560535)(/media/202306/2023-06-26_151415_3104840.6355752390387.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ctbqq8C0-1689070560536)(/media/202306/2023-06-26_151420_0812190.6948612040059644.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Zcn4M0B-1689070560536)(/media/202306/2023-06-26_151428_9426900.5514679799639219.png)]
点击Clone可以获取到gitlab中项目
1、SSH方式链接地址
ssh://git@git.k8s.local:30022/root/greenhat.git
2、http方式链接地址
http://git.k8s.local:1180/root/greenhat.git
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OQM2eQ2Z-1689070560536)(/media/202306/2023-06-26_151440_3165750.2820118142695026.png)]
四 开发机配置登录gitlab
按照事先的规划 172.16.10.17充当开发机的角色
4.1 为开发配置访问gitlab域名的解析
该步骤之前已经做过了,知道就行
echo "172.16.10.16 git.k8s.local" >> /etc/hosts
4.2 在开发机制作秘钥对
[root@test07 ~]# ssh-keygen # 一路回车
[root@test07 ~]# cd /root/.ssh/
[root@test07 .ssh]# ls
id_rsa id_rsa.pub
[root@test07 .ssh]# cat id_rsa.pub # 查看公钥
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDRRndlXF4tKTxp1ukEzdXwuj58Com1alMgfQSGbSM6GiDk8Xupx5gnpIjg8S0e6HsMtvwM723lv2R0IEgD0HO+FdZ9KzjN++YU2EtloBAHr18C46e9ASViB/IqoE3r4TzW33EfmKtFwx/EHNl0tJg5RhEirz5ZcGp2SF+ApTpOv+AWfim1kZqt8KgIuBfAd7ezDM5+8j750JVQKfn/xL7TMA6TlHA7qSlXML/UDZPaWIkkWq2nshosVAcZvW8IN/JBlK3K9A9TCdl0kVD7MzMGYtDmlhLxhNQj1vy52kioMFlxNJlIJ7JyxkiTJsIKJO9kkIgmEPndlJ+3pYVSZg/B root@test07
4.3 复制粘贴公钥到gitlab里
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4u3lBFRL-1689070560536)(/media/202306/2023-06-26_151511_2642510.5586424845744464.png)]
4.4 开发机器模拟更新代码
因为我们贴到gitlab里的是ssh的公钥,所以此处我们用git的项目的ssh地址:ssh://git@git.k8s.local:30022/root/greenhat.git
yum install git -y
git clone ssh://git@git.k8s.local:30022/root/greenhat.git
git config --global user.name "root"
git config --global user.email "email@example.com"
git remote add origin ssh://git@git.k8s.local:30022/root/greenhat.git
cat > test.sh /tmp/a.log
sleep 100000
EOF
git add .
git commit -m "第一次提交"
git push origin
可以到gitlab里看到更新的内容
五 部署harbor
1、创建harbor命名空间
后续将Harbor相关的服务都部署在该命名空间中。
kubectl create namespace harbor
2、创建NFS外部供应商
本处使用NFS为存储,需要提供外部供应商,如果你有该供应商,可跳过本步骤。
步骤一:部署NFS服务端,在工具集群,本例中为172.16.10.16
yum install -y nfs-utils
systemctl start nfs && systemctl enable nfs
systemctl status nfs
chkconfig nfs on #设置为开机自启
mkdir -p /data/nfs/harbor #创建共享目录
cat > /etc/exports
步骤二:安装客户端
# 本处客户端即为kubernets集群的每一个节点,若Pod调度到的节点没有该服务,则无法使用对应的存储卷。
yum -y install nfs-utils
systemctl start nfs-utils && systemctl enable nfs-utils
systemctl status nfs-utils
步骤三:创建NFS provisioner,nfs-provisioner.yaml****
cat > nfs-provisioner.yaml
步骤四:部署
kubectl apply -f nfs-provisioner.yaml
kubectl -n harbor get pod
# 显示
NAME READY STATUS RESTARTS AGE
nfs-proversitioner-5c6f96d484-bvb7d 1/1 Running 0 6s
步骤五:创建存储类
Harbor的database和redis组件是为有状态服务,需要对Harbor数据做持久化存储。
本处基于NFS创建StorageClass存储类,NFS服务器和共享目录为:
NFS服务器地址:172.16.10.16
NFS共享目录:/data/nfs/harbor
cat > harbor-storageclass.yaml
部署
kubectl apply -f harbor-storageclass.yaml
kubectl -n harbor get storageclass
# 显示
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
harbor-storageclass example.com/nfs Delete Immediate false 0s
3、安装helm
官网:https://github.com/helm/helm/releases
wget https://get.helm.sh/helm-v3.9.0-linux-amd64.tar.gz
tar xf helm-v3.9.0-linux-amd64.tar.gz -C /data/
cat >> /etc/profile
4、添加仓库地址
helm repo add harbor https://helm.goharbor.io
helm repo list # 查看添加的Chart
5、下载Chart包到本地
因为需要修改的参数比较多,在命令行直接helm install比较复杂,我就将Chart包下载到本地,再修改一些配置,这样比较直观,也比较符合实际工作中的业务环境。
helm pull harbor/harbor # 下载Chart包
tar zxvf harbor-1.8.2.tgz # 解压包
6、修改values.yaml
$ cd harbor
$ ls
cert Chart.yaml conf LICENSE README.md templates values.yaml
$ vim values.yaml
expose:
type: nodePort # 我这没有Ingress环境,使用NodePort的服务访问方式。
tls:
enabled: false # 关闭tls安全加密认证(如果开启需要配置证书)
...
externalURL: http://172.16.10.16:30002 # 使用nodePort且关闭tls认证,则此处需要修改为http协议和expose.nodePort.ports.http.nodePort指定的端口号,IP即为kubernetes的节点IP地址
# 持久化存储配置部分
persistence:
enabled: true # 开启持久化存储
resourcePolicy: "keep"
persistentVolumeClaim: # 定义Harbor各个组件的PVC持久卷部分
registry: # registry组件(持久卷)配置部分
existingClaim: ""
storageClass: "harbor-storageclass" # 前面创建的StorageClass,其它组件同样配置
subPath: ""
accessMode: ReadWriteMany # 卷的访问模式,需要修改为ReadWriteMany,允许多个组件读写,否则有的组件无法读取其它组件的数据
size: 5Gi
chartmuseum: # chartmuseum组件(持久卷)配置部分
existingClaim: ""
storageClass: "harbor-storageclass"
subPath: ""
accessMode: ReadWriteMany
size: 5Gi
jobservice: # 异步任务组件(持久卷)配置部分
existingClaim: ""
storageClass: "harbor-storageclass" #修改,同上
subPath: ""
accessMode: ReadWriteMany
size: 1Gi
database: # PostgreSQl数据库组件(持久卷)配置部分
existingClaim: ""
storageClass: "harbor-storageclass"
subPath: ""
accessMode: ReadWriteMany
size: 1Gi
redis: # Redis缓存组件(持久卷)配置部分
existingClaim: ""
storageClass: "harbor-storageclass"
subPath: ""
accessMode: ReadWriteMany
size: 1Gi
trivy: # Trity漏洞扫描插件(持久卷)配置部分
existingClaim: ""
storageClass: "harbor-storageclass"
subPath: ""
accessMode: ReadWriteMany
size: 5Gi
...
harborAdminPassword: "Harbor12345" # admin初始密码,不需要修改
...
metrics:
enabled: true # 是否启用监控组件(可以使用Prometheus监控Harbor指标),非必须操作
core:
path: /metrics
port: 8001
registry:
path: /metrics
port: 8001
jobservice:
path: /metrics
port: 8001
exporter:
path: /metrics
port: 8001
###以下的trace为2.4版本的功能,不需要修改
扩展:
如果不希望安装最新的版本,可以通过以下命令修改镜像版本号来安装指定的版本。
sed -i /tag/s/v2.4.2/v2.3.5/g values.yaml
执行helm install安装Harbor
helm install harbor . -n harbor # 将安装资源部署到harbor命名空间
服务验证
[root@test06 harbor]# kubectl -n harbor get pods -w
NAME READY STATUS RESTARTS AGE
harbor-chartmuseum-7fd694bb8-pmllp 1/1 Running 0 55s
harbor-core-7c6944f9cf-shclb 1/1 Running 0 55s
harbor-database-0 1/1 Running 0 55s
harbor-jobservice-bc848f555-qbd5s 1/1 Running 0 55s
harbor-nginx-f546d84f5-lslcj 1/1 Running 0 55s
harbor-notary-server-788754ccf4-xzslr 1/1 Running 0 55s
harbor-notary-signer-88496c75b-5ctj6 1/1 Running 0 55s
harbor-portal-bdc75c86b-fgn2t 1/1 Running 0 55s
harbor-redis-0 1/1 Running 0 55s
harbor-registry-7b745f4f85-zs7lj 2/2 Running 0 55s
harbor-trivy-0 1/1 Running 0 55s
nfs-proversitioner-5c6f96d484-bvb7d 1/1 Running 0 27m
7、登录Harbor UI界面
[root@test06 harbor]# kubectl -n harbor get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
harbor NodePort 10.104.8.173 80:30002/TCP,4443:30004/TCP 68s
harbor-chartmuseum ClusterIP 10.98.123.149 80/TCP 68s
harbor-core ClusterIP 10.109.155.6 80/TCP 68s
harbor-database ClusterIP 10.100.99.187 5432/TCP 68s
harbor-jobservice ClusterIP 10.96.107.73 80/TCP 68s
harbor-notary-server ClusterIP 10.98.148.174 4443/TCP 68s
harbor-notary-signer ClusterIP 10.106.38.85 7899/TCP 68s
harbor-portal ClusterIP 10.105.2.1 80/TCP 68s
harbor-redis ClusterIP 10.109.79.173 6379/TCP 68s
harbor-registry ClusterIP 10.100.58.241 5000/TCP,8080/TCP 68s
harbor-trivy ClusterIP 10.105.61.120 8080/TCP 68s
[root@test06 harbor]#
使用kubernetes任一节点主机IP和30002端口即可访问UI管理界面。
http://172.16.10.16:30002
admin
Harbor12345
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ZvrQSSQ-1689070560536)(/media/202306/2023-06-26_151832_9489650.5284370538788816.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-22f43sag-1689070560537)(/media/202306/2023-06-26_151838_3026410.8754095710520974.png)]
8、推送镜像
配置docker添加對harbor的信任(强调:在所有k8s集群的节点都添加,包括测试、生产、工具集群)
# 如果直接登录我们的harbor仓库会报错http: server gave HTTP response to HTTPS client,需要配置insecure-registries
[root@test06 ~]# cat /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"insecure-registries":["172.16.10.16:30002"],
"registry-mirrors":["https://reg-mirror.qiniu.com/"],
"live-restore":true
}
[root@test06 ~]# systemctl restart docker
登录(如果是push操作,或者是拉取私有仓库的镜像都需要登录,后面我们需要在测试、生产拉取私有仓库镜像,在工具集群push镜像,所以三套集群都执行登录操作)
[root@test06 ~]# docker login -u admin -p Harbor12345 http://172.16.10.16:30002/
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
从docker.io里拉取镜像
docker pull centos:8
推送到我们自己的harbor里
docker tag centos:8 172.16.10.16:30002/online/centos:8
docker push 172.16.10.16:30002/online/centos:8
然后以后都可以从我们自己的harbor里拉取镜像了
docker pull 172.16.10.16:30002/online/centos:8
六 打通jenkins与gitlab
6.1 储备知识
在jenkins中完成构建有很多方式,我们采用常用的Pipeline/流水线
6.1.1 Pipeline
开发人员往gitlab里提交更新的代码之后,想要发布,运维人员需要执行下述步骤
1、拉取代码
2、单元测试
3、构建(配置好软件运行环境、下载依赖包、编译等过程,得到可执行的程序)
4、制作镜像
5、编写或更新yaml,使用最新的镜像,完成上线
传统的上线方式,上述步骤需要运维人员在单台或多台主机依次执行,流程步骤繁杂、无法可视化,极容易出错,回滚麻烦。
我们引入jenkins中的Pipeline,就是为了把运维人员手动在单个或多个节点的任务(例如代码拉取、单元测试、构建、部署等)连接到一起,相当于建立了一条流水线,每次上线时,只需要点击构建,即执行这条Pipeline流水线,这些任务就会安装提前设定好的样子依次在单台或多台主机执行,我们可以在jenkins界面看到整个过程,实现可视化。
6.1.2 jenkins的Pipeline有几个核心概念
-
Node:节点,一个 Node 就是一个 Jenkins 节点,Master 或者 Agent,是执行 Step 的具体运行环境,比如我们之前动态运行的 Jenkins Slave 就是一个 Node 节点
-
Stage:阶段,一个 Pipeline 可以定义多个 Stage,每个 Stage 代表一组操作,比如:Build、Test、Deploy,Stage 是一个逻辑分组的概念,可以跨多个 Node
- stages有如下特点 :
- 所有 stages 会按照顺序运行,即当一个 stage 完成后,下一个 stage 才会开始
- 只有当所有 stages 成功完成后,该构建任务 (Pipeline) 才算成功
- 如果任何一个 stage 失败,那么后面的 stages 不会执行,该构建任务 (Pipeline) 失败
-
Step:步骤,Step 是最基本的操作单元,可以是打印一句话,也可以是构建一个 Docker 镜像,由各类 Jenkins 插件提供,比如命令:sh ‘make’,就相当于我们平时 shell 终端中执行 make 命令一样。
6.1.3 如何创建jenkins的Pipeline
-
Pipeline 脚本是由 Groovy 语言实现的,但是我们没必要单独去学习 Groovy,用到啥查啥就行
-
Pipeline 支持两种语法:Declarative(声明式)和 Scripted Pipeline(脚本式)语法
-
Pipeline 也有两种创建方法:
- 在 Jenkins 的 Web UI 界面中输入脚本;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FpsGobPJ-1689070560537)(/media/202306/2023-06-26_152047_6615350.2213918430167876.png)]
- 通过创建一个 Jenkinsfile 脚本文件放入项目源码库中,然后jenkins配置SCM,点击构建后拉取源代码,jenkins会从源代码/项目根目录下载入Jenkinsfile文件来执行规定的构建(推荐该方式)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BqqjHqYi-1689070560537)(/media/202306/2023-06-26_152100_2795890.35089682273965983.png)]
6.2 牛刀小试
在6.1.3小节说到创建jenkins的pipeline流水线有两种方式,虽然推荐使用方式二,但我们还是了解下方式一的使用,即在 Jenkins 的 Web UI 界面中输入脚本的方式
访问jenkins:172.16.10.16:7096
主页-》新建任务/job-》输入一个任务名称,如test1-》点击流水线-》点击确定
在最下方的 Pipeline 区域选择Pipeline Script然后输入如下脚本,然后点击保存
下面的脚本只是一个简单小模板、小demo
node {
stage('Clone') {
echo "1.Clone Stage"
}
stage('Test') {
echo "2.Test Stage"
}
stage('Build') {
echo "3.Build Stage"
}
stage('Deploy') {
echo "4. Deploy Stage"
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xRZ4kstM-1689070560537)(/media/202306/2023-06-26_152120_9773130.7929037413099966.png)]
保存后,然后点击立即构建,可以查看Console Output的输出
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cD29mzVn-1689070560537)(/media/202306/2023-06-26_152127_4877710.731852257936164.png)]
6.3 在指定的slave中构建任务
在6.2中我们用一个简单的pipeline小demo完成了演示,但是构建任务并没有在jenkins的slave中运行。而在实际应用中,一些slave里有特定的环境,我们的构建任务必须在该环境里运行,这就需要我们指定slave运行,如何指定呢?
需要用到我们之前添加slave pod时指定的label标签,我们之前在2.4小节的步骤4里指定过一个slave pod并将其label设置为egon-test1,这个标签就是我们选中该slave pod的唯一标识
可以参照6.2小节新建一个pipeline,填入新脚本如下
node('egon-test1') {
stage('Clone') {
echo "1.Clone Stage"
}
stage('Test') {
echo "2.Test Stage"
}
stage('Build') {
echo "3.Build Stage"
}
stage('Deploy') {
echo "4. Deploy Stage"
}
}
点击构建,然后查看console output,可以看到选中了一个agent名为 test1-w0crf,而test1-w0crf就是在k8s临时创建的用于构建本次任务的slave pod,构建完毕后会自动删除该slave pod
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FM1CTLP0-1689070560538)(/media/202306/2023-06-26_152156_4561620.6236519107052064.png)]
6.4 对接k8s
重头戏在这里。。。
前面几个小节我们了解了如何创建pipeline,如何构建等基本流程,本节我们就创建一个SCM的流水线,然后完成构建后推送到k8s环境里
我们的大致思路是。以一个go程序为例
-
一:在gitab里创建一个项目,该项目用来管理go程序,创建完毕后记录该项目的http或ssh链接信息
-
二:在jenkins里创建一个go的流水线
构建触发器,定义一个随机字符串作为token值,并选择Pipeline script from SCM,在SCM配置好代码仓库的链接信息
注意:不需要填写任何pipeline的代码
- 三:在gitlab里配置webhook
在gitlab里配置好webhook执行jenkins的地址,地址里包含在jenkins里生成的token值
-
四:编写一个名为Jenkinsfile的文件(强调首字母是一个大写的字母,看清楚了),把pipeline的代码写到里面,然后扔给开发,开发人员会将该文件放到它的项目根目录下
-
五:go开发人员通过自己的开发机上传go项目代码(该项目根目录下包含一个名为jenkinsfile的文件)到gitlab中的仓库greenhat里,然后会按照jenkinsfile的规定触发一整套流程
所以看明白没有,如果是java程序,套路也是一样,上面的一、二、三、四、五步走一遍,再创建一套针对java的就可以了
下面以一个go程序为例,演示
6.4.1、在gitab里创建一个项目,提供该项目的链接信息(略,之前已经创建过了详见3.3小节)
项目名为:greenhat,不用回头找了,我把项目的git的链接地址贴到这里了
# 1、项目的SSH方式链接地址
ssh://git@git.k8s.local:30022/root/greenhat.git
# 2、项目的http方式链接地址
http://git.k8s.local:1180/root/greenhat.git
6.4.2、在jenkins里创建一个go的流水线
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ain7GvwA-1689070560538)(/media/202306/2023-06-26_152302_3826260.6474672269122657.png)]
构建触发器——》触发远程构建,按照提示的格式填入我们的jenkins地址,其中TOKEN_NAME可以随便写一个字符,但它很重要,因为在gitlab的webhook里就需要用到该地址,此处我们写的是:egonlao6
安装下面的提示信息,我们拼出一个uri地址如下,该地址需要配置到gitlab里的webhook用来webhook访问jenkins中指定的流水线。
http://172.16.10.16:7096/job/go-pipeline-demo/build?token=egonlao6
所以你应该听懂逻辑了,每创建一个jenkins的pipeline都可以指定一个身份令牌,然后依据提示拼成一个uri地址给某个webhook用
我们接下来的大致思路如下
# 一、在jenkins里创建两条用于构建不同项目的流水线
# 1.1
go-pipeline-demo:得到它的token假设为egonlao6,拼有一个它的uri地址,假设为http://172.16.10.16:7096/job/go-pipeline-demo/build?token=egonlao6
# 1.2
java-pipeline-demo:得到它的token假设为egonlao8,拼一个它的uri地址,假设为http://172.16.10.16:7096/job/java-pipeline-demo/build?token=egonlao8
# 二:
1、在gitlab里创建一个项目A
2、在gitlab里点击项目A,为其创建一个webhook,该webhook配置上http://172.16.10.16:7096/job/go-pipeline-demo/build?token=egonlao6
3、当我们往项目A里push代码,则会触发它的webook,然后会依据webhook配置的地址触发jenkins中go-pipeline-demo这条流水线的运行
1、在gitlab里创建一个项目B
2、在gitlab里点击项目B,为其创建一个webhook,该webhook配置上http://172.16.10.16:7096/job/java-pipeline-demo/build?token=egonlao8
3、当我们往项目A里push代码,则会触发它的webook,然后会依据webhook配置的地址触jenkins中java-pipeline-demo这条流水线的运行
在gitlab里创建第一个webhook,配上上一条流水线的uri地址,这就打通了某个项目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vHpt3xRP-1689070560538)(/media/202306/2023-06-26_152340_2277140.1490087956633318.png)]
往下拖,拖到最后,在流水线定义里选择:Pipeline script from SCM,然后在SCM哪里选择git,配置好git信息
jenkins会去git的项目地址里拉取代码,然后在代码根目录下找到一个叫jenkinsfile的文件执行,该文件里写的就是如何构建的代码
如下图配置链接git的地址后
1、红色提示可能会显示解析失败,你需要明确一点此处的配是用于jenkins访问gitlab,需要为jenkins添加解析到git.k8s.local域名的解析,jenkins运行在k8s里,所以需要在coredns添加解析,参考3.2小节完成添加即可
2、红色提示链接拒绝,需要为jenkins添加链接gitlab的认证信息,需要强调的一点是,Repository URL我们没有填入的gitlab地址是ssh协议的,是因为我们是在一个Slave Pod里完成构建,如果采用SSH的方式去访问gitlab代码仓库的话就需要频繁的去更新 SSH-KEY,所以我们这里填入http协议的链接地址,并且采用直接使用用户名和密码的形式来方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pWMCwGur-1689070560538)(/media/202306/2023-06-26_152351_2461060.8802709793847103.png)]
添加Credentials
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TqzMApfd-1689070560538)(/media/202306/2023-06-26_152401_0712050.27908461139916807.png)]
往下拖
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JLIe1FVd-1689070560539)(/media/202306/2023-06-26_152406_5456540.995651538418265.png)]
然后在Credentials里选择刚刚创建的凭证,此时Jenkins 就可以正常访问到 GitLab 了
接着往下拖,配置用于构建的分支,分两种情况讨论
- 1、如果所有的分支我们都想要进行构建的话,只需要将 Branch Specifier 区域留空即可
- 2、一般情况下不同的环境对应的分支才需要构建,比如 master、develop、test 等,平时开发的 feature 或者 bugfix 的分支没必要频繁构建,我们这里就只配置 master 和 develop 两个分支用于构建。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b15dUKb1-1689070560539)(/media/202306/2023-06-26_152421_1325910.016553839049230357.png)]
点击保存
6.4.3、在gitlab里配置webhook
前往 Gitlab 中
项目已经创建好了,我们直接为项目创建webhooks,指向上面的流水线即可
点击projects-》点击greenhat项目-》settings-》 Webhooks
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zCtVDxNv-1689070560539)(/media/202306/2023-06-26_152434_8029180.5982714735909546.png)]
把5.4.2定义的token拿过来,拼出url地址:http://172.16.10.16:7096/job/go-pipeline-demo/build?token=egonlao6
注意:
1、下图中的Secret Token之所以可以为空,是因为我们通过url地址问号后的内容传递了该token值
2、选中的Trigger代表在xxx事件发生时,会触发webhook对接jenkins的流水线,此处我们选择两个跟push有关的事件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i1yuLs5F-1689070560539)(/media/202306/2023-06-26_152448_6181200.5919665759406758.png)]
点击Add webhook,在屏幕上面会出现一行粉字:Url is blocked: Requests to the local network are not allowed
则需要进入 GitLab首页,点击下图所示Admin Area-》Settings -> NetWork -> 勾选 Outbound requests,然后–》点击save changes
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oIoO2dO1-1689070560539)(/media/202306/2023-06-26_152457_8910290.023205952234381044.png)]
点击save changes之后,回到项目的webhooks界面点击测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NHNG2Ory-1689070560540)(/media/202306/2023-06-26_152504_4150330.5577954675906976.png)]
点击Push events后会报一个403错误,需要做三件事
- 1、需要进入jenkins的页面:http://172.16.10.16:7096/,、配置jenkins安全选项
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bJMUplNa-1689070560540)(/media/202306/2023-06-26_152516_4963430.8751554966664858.png)]
系统管理-》全局安全配置->勾选匿名用户具有可读权限
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a519GUrh-1689070560540)(/media/202306/2023-06-26_152523_3013130.9213625805200839.png)]
- 2、由于这里的 Jenkins 是比较新的 (2.346.1) 版本,该版本已经默认取消了 CSRF 的安全配置入口,所以我们需要手动执行一段脚本来禁用 CSRF 的跨站请求,系统管理 ->页面底部选择: 脚本命令行:执行下图所示的命令即可。
import jenkins.model.Jenkins
def jenkins = Jenkins.instance
jenkins.setCrumbIssuer(null)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OPe0TDQR-1689070560540)(/media/202306/2023-06-26_152537_7283180.10775837580574243.png)]
如果在设置中-全局安全设置里面,如下图展示,则说明就关闭了CSRF
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tvIyIHSM-1689070560540)(/media/202306/2023-06-26_152542_7347680.8620733742015023.png)]
也有一个方式了解一下:我们可以修改jenkins.yaml文件,为deployment增加一个env,然后重新部署jenkins.yaml
env:
- name: JAVA_OPTS
value: "-Dhudson.security.csrf.GlobalCrumbIssuerConfiguration.DISABLE_CSRF_PROTECTION=true"
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZDNoZJUN-1689070560541)(/media/202306/2023-06-26_152602_2526720.8991564258441188.png)]
3、系统管理-》插件管理-》安装GitLab插件
安装完毕后重启,在url地址后输入restart,即:http://172.16.10.16:7096/restart
然后点击系统管理-》系统配置-》找到gitlab,去掉勾选:Enable authentication for ‘/project’ end-point
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fi8lEpHg-1689070560541)(/media/202306/2023-06-26_152612_3475990.5316991356986788.png)]
然后再次点击test里的Push events,显示如下内容,代表手动触发push 事件成功,可以跑到jenkins里找到指定的流水线查看,已经开始工作,代表成功
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fa7pYxtr-1689070560541)(/media/202306/2023-06-26_152619_3862400.39386298716797896.png)]
6.4.4、创建Jenkinsfile文件
!!!!!!!!!强调:你自信看看Jekensfile文件的首字母是一个大写的字母哦,可不要创建错了!!!!!!!!!!!!!
6.4.4.1 思路概述
你可以在jenkins的web界面里编写pipeline的代码,但此处我们采用的方式是把pipeline的代码写到一个名为Jenkinsfile的文件,然后把该文件放到项目的根目录下,而且同一个项目的不同代码分支下都需要有这个Jenkinsfile文件,如此,一旦gitlab的webhook监听的事件发生,就会立刻通知指定的流水线,然后该流水线会按照配置好的信息去gitlab里拉取代码,然后去代码的根目录下找一个名为Jenkinsfile的文件,按照该文件里编写的流水线代码完成整个构建过程。
这里你可能会有几个问题
1、Jenkinsfile文件的首字母为何是大写的
2、Jenkinsfile文件为何要放在项目根目录下
这两问题都是二b才会问的问题,你好好瞅一眼5.1.3里我们当初在创建pipeline时,是我们自己规定的这个文件名,而且首字母写了一个大写的。
那么Jenkinsfile里应该如何编写呢?基本的逻辑就是
1、拉取代码
2、测试
3、构建(安装依赖包、编译等)
4、构建镜像
5、推送镜像到镜像仓库
6、编写k8s的yaml文件
7、kubectl更新yaml文件中的镜像地址与tag
从测试到更新 YAML 文件属于 CI 流程,后面部署属于 CD 的流程。
基本额pipeline模板如下
node('egon-test1') {
stage('Clone') {
echo "1.Clone Stage"
}
stage('Test') {
echo "2.Test Stage"
}
stage('Build') {
echo "3.Build Docker Image Stage"
}
stage('Push') {
echo "4.Push Docker Image Stage"
}
stage('YAML') {
echo "5.Change YAML File Stage"
}
stage('Deploy') {
echo "6.Deploy Stage"
}
}
注意我们在2.4小节的步骤4 里定义过一个pod Template,即slave pod的生成模板,该模板里启动pod引用的是一个镜像cnych/jenkins:jnlp6
也就说,如果我们真的按照上面写的流水线来的,node(‘egon-test1’) 也是选中用该镜像cnych/jenkins:jnlp6启动的一个slave pod来进行构建,如果我们要构建的是go程序,那么该镜像就不适用了,我们需要用一个具有go环境的镜像才可以。如果你真这么做了,看似可以,实则是一个坑。为什么呢?
因为除了go程序之外,你还有可能会构建java,构建python,那么你需要定制一个非常大的镜像,里面有所有你想要的环境,而且还要有git工具、docker工具、kubectl等工具,能想象到该镜像会变得多大了吧,而且很不灵活。
有没有更好的方式呢?有
就是我们先制作一个个小镜像/或者基于现成的也行
1、一个有go环境的小镜像
2、一个安装有docker的小镜像
3、一个安装有helm工具的小镜像
4、一个安装有kubectl工具的小镜像
然后我们可以删掉之前创建的pod Template
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2zOLAHLm-1689070560541)(/media/202306/2023-06-26_152659_7905130.8474461306035092.png)]
然后在pipeline的代码里里动态生成pod template,在里面写好要引用的镜像 ,然后在每个stage里调用专门的镜像就可以,我们可以看到pod slave里会启动多个容器。
这么做的好处是,如果我们想构建另外一条用于构建java程序的流水线,那么我们只需要再创建了一个拥有java环境的小镜像就可以,至于2、3、4提到的镜像都是可以复用的
综上,在做好小镜像之后
1、针对java项目,我们就在java项目根目录下创建一个Jenkinsfile文件,在该文件里写入pipeline流水线代码
2、针对go项目,我们就在go项目根目录下创建一个Jenkinsfile文件,在该文件里写入pipeline流水线代码
3、针对go项目,我们就在python项目根目录下创建一个Jenkinsfile文件,在该文件里写入pipeline流水线代码
这三个Jenkinsfile文件涉及到的安装有docker工具的镜像、helm工具的镜像、kubectl工具的镜像都可以复用
6.4.4.2 准备工作之创建三个凭证
说明:我要要创建登录两套k8s集群的凭证、以及一个登录harbor镜像仓库的凭证,为啥要创建凭证呢?
构建完毕后一定会执行kubectl命令将构建结果提交到k8s集群里。我们可以将访问集群的 `kubeconfig` 文件拷贝到 kubectl 容器的 `~/.kube/config` 文件下面去,这样我们就可以在容器中访问 Kubernetes 集群了,但是由于我们构建是在 Slave Pod 中去构建的,Pod 就很有可能每次调度到不同的节点去,这就需要保证每个节点上有 `kubeconfig` 文件才能挂载成功,所以这里我们使用下面的方式。
获取测试环境与生产环境k8s集群的~/.kube/config文件,然后添加到jenkins里/创建凭证,这么做的目的是为了下一步我们创建pod Template里引用的镜像在执行kubectl命令时可以读取到凭证,读取到不同集群的凭证就可以登录到不同的集群
1、在jenins里创建一个登录k8s集群测试环境的凭证
# 测试环境k8s:172.16.10.14
cat /root/.kube/config
登录到jenkins里:主页面-》系统管理-》Manage Credentials->Stores scoped to jenkins下点击jenkins-》点击全局凭据-》点击添加凭据
如下图所示,或者干脆直接访问地址一步到位:http://172.16.10.16:7096/credentials/store/system/domain/_/newCredentials
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oCGPjlrq-1689070560541)(/media/202306/2023-06-26_152729_8543160.8280113559831603.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2QzaHLqU-1689070560542)(/media/202306/2023-06-26_152735_6136040.2053607280681936.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jExoJPvg-1689070560542)(/media/202306/2023-06-26_152742_5393180.39101570616228976.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U4I8i5pt-1689070560542)(/media/202306/2023-06-26_152748_1732090.7889879934493486.png)]
# 然后在 Jenkinsfile 的 kubectl 容器中读取上面添加的 Secret file 文件,拷贝到 ~/.kube/config 即可:
# 例如
stage('运行 Kubectl') {
container('kubectl') {
withCredentials([file(credentialsId: 'kubeconfig_ceshi', variable: 'KUBECONFIG')]) {
echo "查看 K8S 集群 Pod 列表"
sh "mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config"
sh "kubectl get pods"
}
}
}
2、同上,在jenins里创建一个登录k8s集群生产环境的凭证
# 生产环境k8s:172.16.10.15
cat /root/.kube/config
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u3DfFlZ2-1689070560542)(/media/202306/2023-06-26_152809_2146260.07311410753303427.png)]
3、在jenins里创建一个登录harbor仓库的凭证
访问:http://172.16.10.16:7096/credentials/store/system/domain/_/newCredentials
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oVKkNPwk-1689070560542)(/media/202306/2023-06-26_152816_3309410.4475836188538851.png)]
6.4.4.3 准备工作之登录harbor创建一个存放go镜像的仓库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VuAtu3AF-1689070560542)(/media/202306/2023-06-26_152829_7311400.45183067469157834.png)]
6.4.4.4 准备工作之先在测试集群与生产集群都事先创建好test.yaml
运行在k8s中的实例
因为我们需要在k8s拉取harbor的私有仓库,需要用到账号密码,所以需要创建一个secret
kubectl create secret docker-registry registry-secret --namespace=default
--docker-server=172.16.10.16:30002 --docker-username=admin
--docker-password=Harbor12345
test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
labels:
app: test
spec:
replicas: 1
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
containers:
- name: test
image: 172.16.10.16:30002/goproject/greenhat:c2aa9e3 # 该镜像可以先填一个随便的,反正后期是要更新的
imagePullSecrets:
- name: registry-secret
6.4.4.5 准备工作之开发机push初始代码
1、开发机拉取代码
mkdir /mypro
cd /mypro/
git init
git config --global user.name "root"
git config --global user.email "email@example.com"
git remote add origin ssh://git@git.k8s.local:30022/root/greenhat.git
git pull origin master
2、编写go代码,推送到master分支
# 1、创建go.mod文件:vim go.mod
module golang
go 1.17
# 2、编写go代码:vim run.go
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("主分支.....")
time.Sleep(1000000 * time.Second)
}
# 3、提交并push到master分支
git add .
git commit -m "提交run.go"
git push origin master
3、编写go代码,推送到develop分支
# 1、创建并切换到develop分支
[root@test07 mypro]# git branch
* master
[root@test07 mypro]# git checkout -b develop
Switched to a new branch 'develop'
[root@test07 mypro]# git branch
* develop
master
[root@test07 mypro]# ls
go.mod run.go
# 2、go.mod文件已经存在
# 3、编写go代码:vim run.go
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("开发分支.....")
time.Sleep(1000000 * time.Second)
}
# 3
# 3、提交并push到master分支
git add .
git commit -m "提交run.go"
git push origin develop
上面两次push都会触发jenkins的流水线执行,但是会因为该项目的两个分支下都没有Jenkinsfile文件而执行失败
6.4.4.6 为该项目master分支创建Jenkinsfile文件并测试
1、在开发机,切换到项目根目录下,切换到主分支里,创建Jenkinsfile文件
切换到项目根目录下,切换到主分支里
[root@test07 mypro]# cd /mypro # 切换到项目根目录下
[root@test07 mypro]#
[root@test07 mypro]# git checkout master # 切换到master分支下
切换到分支 'master'
[root@test07 mypro]# ls
go.mod run.go
在项目根目录下编写Jenkinsfile文件,强调文件的首字母是一个大写的字母
// 定义pod slave的标签
def label = "slave-${UUID.randomUUID().toString()}"
// 定义动态生成的pod slave的模板,该pod启动时包含了3个容器名为golang、docker、kubectl
podTemplate(label: label, containers: [
containerTemplate(name: 'golang', image: 'okteto/golang.1.17', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'docker', image: 'docker:latest', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'kubectl', image: 'cnych/kubectl', command: 'cat', ttyEnabled: true)
], serviceAccount: 'jenkins', volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
]) {
node(label) {
def myRepo = checkout scm
// 获取开发任意git commit -m "xxx"指定的提交信息xxx
def gitCommit = myRepo.GIT_COMMIT
// 获取提交的分支
def gitBranch = myRepo.GIT_BRANCH
echo "------------>本次构建的分支是:${gitBranch}"
// 仓库地址
def registryUrl = "172.16.10.16:30002"
def imageEndpoint = "goproject/greenhat"
// 获取 git commit id 作为我们后面制作的docker镜像的tag
def imageTag = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
// 镜像
def image = "${registryUrl}/${imageEndpoint}:${imageTag}"
stage('单元测试') {
echo "1.测试阶段,此步骤略,可以根据需求自己定制"
}
stage('代码编译打包') {
try {
container('golang') {
echo "2.代码编译打包阶段"
sh """
export GOPROXY=https://goproxy.cn
GOOS=linux GOARCH=amd64 go build -v -o egongogo
"""
}
} catch (exc) {
println "构建失败 - ${currentBuild.fullDisplayName}"
throw(exc)
}
}
stage('构建 Docker 镜像') {
withCredentials([[$class: 'UsernamePasswordMultiBinding',
credentialsId: 'docker-auth',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASSWORD']]) {
container('docker') {
echo "3. 构建 Docker 镜像阶段"
sh '''
cat >Dockerfile
2、提交
git add .
git commit -m "master v1.0"
git push origin master # 一旦push完毕,会立刻引发gitlab的push事件,webhook会监听到该事件匹配,然后会利用配置好地址与token值找到jenkins具体某一条流水线触发其运行,本例中为go-pipeline-demo流水线,该流水线会先去gitlab里拉取master分支代码,然后加载该代码包根目录下的Jenkinsfile文件,按照文件规定完成构建与部署
3、查看jenkins
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-maiUnKHu-1689070560543)(/media/202306/2023-06-26_153000_1519520.53937353074671.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HNktRYHJ-1689070560543)(/media/202306/2023-06-26_153005_2807770.865992132441408.png)]
4、master分支会部署到生产k8s集群,所以我们去生产集群查看
[root@test05 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-76694586f5-c6v8v 1/1 Running 0 20s
[root@test05 ~]# kubectl logs -f test-76694586f5-c6v8v
主分支.....
6.4.4.7 为该项目的develop分支创建Jenkinsfile文件并测试
基于上一小节的基础上 继续操作
1、切换分支
cd /mypro
git checkout branch
2、把上一小节的Jenkinsfile文件一模一样放一份到当前分支根目录下
3、提交、并push到develop分支
git add .
git commit -m "测试dev分支"
git push origin develop
4、jenkins查看构建情况,同上,略
5、因为我们推送的develop分支,会发布到测试集群,所以登录测试集群
[root@test04 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-5d9cf946f9-pzt5p 1/1 Running 0 14s
[root@test04 ~]# kubectl logs -f test-5d9cf946f9-pzt5p
开发分支.....
后续测试着玩
git checkout master
修改run.go的代码,然后提交
然后到jenkins里点击参数化构建,选择master分支,构建,会发现生产集群内容更新
git checkout develop
修改run.go的代码,然后提交
然后到jenkins里点击参数化构建,选择master分支,构建,会发现测试集群内容更新
6.5 优化之为发布到生产集群添加交互式确认
强调一句话:如果是生产环境肯定不能开发人员push完毕后就直接部署到生产k8s集群中,只有develop分支才会push完自动完成整套流程发布到测试号环境,所以真正在应用的时候,应该区分对待master分支与develop分支
方式一:
1、创建一条流水线,在配置git信息时,配置只针对develop分支,然后在webhook里指向该流水线的地址与token,如此,push了develop分支才会完成整套构建自动发布到测试环境中
2、创建另外一条流水线,在配置git信息时,配置只针对master分支,不为其创建webhook,其余操作与上面的都一样,这样每次发布的时候都手动点击构建即可
方式二:修改master与develop分支下的Jenkinfile文件(这俩文件是一样的),然后专门为部署到生产环境那一个stage加上交互式
交互式代码示例
stage('快速回滚?') {
withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
container('helm') {
sh "mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config"
def userInput = input(
id: 'userInput',
message: '是否需要快速回滚?',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "YnN",
name: '回滚?'
]
]
)
if (userInput == "Y") {
sh "helm rollback devops-demo --namespace kube-ops"
}
}
}
}
master分支下的Jenkinsfile文件
// 定义pod slave的标签
def label = "slave-${UUID.randomUUID().toString()}"
// 定义动态生成的pod slave的模板,该pod启动时包含了3个容器名为golang、docker、kubectl
podTemplate(label: label, containers: [
containerTemplate(name: 'golang', image: 'okteto/golang.1.17', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'docker', image: 'docker:latest', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'kubectl', image: 'cnych/kubectl', command: 'cat', ttyEnabled: true)
], serviceAccount: 'jenkins', volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
]) {
node(label) {
def myRepo = checkout scm
// 获取开发任意git commit -m "xxx"指定的提交信息xxx
def gitCommit = myRepo.GIT_COMMIT
// 获取提交的分支
def gitBranch = myRepo.GIT_BRANCH
echo "------------>本次构建的分支是:${gitBranch}"
// 仓库地址
def registryUrl = "172.16.10.16:30002"
def imageEndpoint = "goproject/greenhat"
// 获取 git commit id 作为我们后面制作的docker镜像的tag
def imageTag = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
// 镜像
def image = "${registryUrl}/${imageEndpoint}:${imageTag}"
stage('单元测试') {
echo "1.测试阶段,此步骤略,可以根据需求自己定制"
}
stage('代码编译打包') {
try {
container('golang') {
echo "2.代码编译打包阶段"
sh """
export GOPROXY=https://goproxy.cn
GOOS=linux GOARCH=amd64 go build -v -o egongogo
"""
}
} catch (exc) {
println "构建失败 - ${currentBuild.fullDisplayName}"
throw(exc)
}
}
stage('构建 Docker 镜像') {
withCredentials([[$class: 'UsernamePasswordMultiBinding',
credentialsId: 'docker-auth',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASSWORD']]) {
container('docker') {
echo "3. 构建 Docker 镜像阶段"
sh '''
cat >Dockerfile
然后在开发机切换到master分支,修改一下run.go的内容,然后push到master分支,然后去jenkins里查看
ps:一定要注意,你必须事先以管理员账号身份登录到jenkins里,选择Y或者N才有效,未登录状态或者没有权限你可以可以看也可以选,但是你点击继续,就会报错:You need to have Job/Build permissions to submit this.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W3DNHmob-1689070560543)(/media/202306/2023-06-26_153115_2674780.8723783674776888.png)]
更进一步,我们可以为master与develop都加入回滚功能,示例代码
stage('快速回滚?') {
withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
container('helm') {
sh "mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config"
def userInput = input(
id: 'userInput',
message: '是否需要快速回滚?',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "YnN",
name: '回滚?'
]
]
)
if (userInput == "Y") {
sh "helm rollback devops-demo --namespace kube-ops"
}
}
}
}
修改两个分支下的Jenkinsfile文件,添加回滚代码,如下所示,然后为部署到生产与测试环境的部分都加上加入回滚功能
// 定义pod slave的标签
def label = "slave-${UUID.randomUUID().toString()}"
// 定义动态生成的pod slave的模板,该pod启动时包含了3个容器名为golang、docker、kubectl
podTemplate(label: label, containers: [
containerTemplate(name: 'golang', image: 'okteto/golang.1.17', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'docker', image: 'docker:latest', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'kubectl', image: 'cnych/kubectl', command: 'cat', ttyEnabled: true)
], serviceAccount: 'jenkins', volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
]) {
node(label) {
def myRepo = checkout scm
// 获取开发任意git commit -m "xxx"指定的提交信息xxx
def gitCommit = myRepo.GIT_COMMIT
// 获取提交的分支
def gitBranch = myRepo.GIT_BRANCH
echo "------------>本次构建的分支是:${gitBranch}"
// 仓库地址
def registryUrl = "172.16.10.16:30002"
def imageEndpoint = "goproject/greenhat"
// 获取 git commit id 作为我们后面制作的docker镜像的tag
def imageTag = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
// 镜像
def image = "${registryUrl}/${imageEndpoint}:${imageTag}"
stage('单元测试') {
echo "1.测试阶段,此步骤略,可以根据需求自己定制"
}
stage('代码编译打包') {
try {
container('golang') {
echo "2.代码编译打包阶段"
sh """
export GOPROXY=https://goproxy.cn
GOOS=linux GOARCH=amd64 go build -v -o egongogo
"""
}
} catch (exc) {
println "构建失败 - ${currentBuild.fullDisplayName}"
throw(exc)
}
}
stage('构建 Docker 镜像') {
withCredentials([[$class: 'UsernamePasswordMultiBinding',
credentialsId: 'docker-auth',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASSWORD']]) {
container('docker') {
echo "3. 构建 Docker 镜像阶段"
sh '''
cat >Dockerfile
测试master分支与develop分支的提交
6.6 总结Jenkinsfile的大致模板
# 基本流程就是:Clone 代码 -> 单元测试 -> Golang 编译打包 -> Docker 镜像构建/推送 -> Kubectl 部署服务。
def label = "slave-${UUID.randomUUID().toString()}"
podTemplate(label: label, containers: [
containerTemplate(name: 'golang', image: 'okteto/golang.1.17', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'docker', image: 'docker:latest', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'kubectl', image: 'cnych/kubectl', command: 'cat', ttyEnabled: true)
], serviceAccount: 'jenkins', volumes: [
hostPathVolume(mountPath: '/home/jenkins/.kube', hostPath: '/root/.kube'),
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
]) {
node(label) {
def myRepo = checkout scm
def gitCommit = myRepo.GIT_COMMIT
def gitBranch = myRepo.GIT_BRANCH
stage('单元测试') {
echo "测试阶段"
}
stage('代码编译打包') {
container('golang') {
echo "代码编译打包阶段"
}
}
stage('构建 Docker 镜像') {
container('docker') {
echo "构建 Docker 镜像阶段"
}
}
stage('运行 Kubectl') {
container('kubectl') {
echo "查看 K8S 集群 Pod 列表"
sh "kubectl get pods"
}
}
}
}
直接在 `podTemplate` 里面定义每个阶段需要用到的容器,volumes 里面将我们需要用到的 `docker.sock` 文件,需要注意的我们使用的 label 标签是是一个随机生成的,这样有一个好处就是有多个任务来的时候就可以同时构建了。
http://git.k8s.local:1180/root/greenhat/hooks/3/edit
http://172.16.10.16:30002/harbor/projects
http://172.16.10.16:7096/
jenkins安装插件安装插件:没有用的插件,可以考虑删掉
gitlab branch source
multibranch-scan-webhook-trigger
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
相关推荐: BUUCTF:[ISITDTU 2019]EasyPHP
题目地址:BUUCTF:[ISITDTU 2019]EasyPHP Refer: https://tiaonmmn.github.io/2019/07/18/ISITDTU-Easy-PHP/ 思路很简单,绕过这两个if即可任意代码执行 先看一下第一个正则匹配…