共计 37663 个字符,预计需要花费 95 分钟才能阅读完成。
随着 Kubernetes 的遍地开花,Kubernetes 的优势可以说是深入人心,很多企业也是利用 Kubernetes,来实现更高效的交付和更好地提高我们的资源使用率,推动标准化,适应云原生。
随着 Kubernetes 和云原生加速企业产品落地,现在总结以下几点
1)更快的应用开发与交付
2)天然适合微服务,是微服务和 Devops 的桥梁
3)可移植性,支持公有云,私有云,裸机,虚拟机
4)标准化的应用开发与发布:声明式 API 和 Operator
5)自动化运维:弹性伸缩(HPA),故障自愈,负载均衡,配置管理等
另外就是交付 spring cloud 到 k8s 之前说一下微服务的概念
什么是微服务?
早在 2011 年的 5 月份在威尼斯的一个架构研讨会上微服务的概念就被人提起了,当时的参会者对它的描述只是一种通用的软件并没给出明确的定义是什么,之后随着技术的不断发展,在 2014 年詹姆斯里维斯以及它的伙伴马丁福勒在它的微博中发表了一篇有关于微服务特点的文章,对微服务进行了全面的阐述,之后微服务就走进了我们的视野
https://martinfowler.com/articles/microservices.html
这是关于网站的描述
在这个文章中并给出微服务一个明确的定义,微服务其实是一种软件的架构风格,是一种将单体架构
拆分为小的服务进行去开发,每个服务都运行在自己的进程中,采用的是轻量级的 restful 或者 http 进行通信,并且都是独立开发独立部署和测试的,可以使用多种语言进行开发
对于微服务有一个关键点叫化整为零,把一个大的应用却成一个小的不同的应用
比如嘀嘀打车,早期在一个互联网应用上基本上都是单体架构,不是分布式的
单体情况下把很多程序都写一个程序中,然后一台服务器对所有服务进行运行,但是随着并发的提高,这种单体架构显然承受不了了,这样的话就需要我们对我们软件的指责进行慢慢的划分,将其剥离出来,形成一个一个的微服务,也就是多个微服务的模块形成一个完整的应用,这些都是独立部署独立运行的,一个微服务也会运行在一个虚拟机里面。
spring cloud 微服务体系的组成
服务发现(Eureka,Cousul,zookeeper)
也就是注册中心最为微服务必不可少的中央管理者,服务发现的主要职责就是将其它的微服务模块进行登记与管理,这就相当于生活中来了一家公司,去工商局进行登记一样的道理,在服务发现中它主包含了三个子模块,分别是 eureka,cousul,zookeeper,这些 spring cloud 底层都支持的注册中心,一般常用的是 eureka 和 consul,那么微服务构建好了之后,那么微服务与微服务直接怎么进行服务直接的通信,或者微服务遇到了故障无法达到请求的话(hystrix/ribbon/openfeign)
另外就是路由与过滤主要是针对对外接口的暴露的,这里主要涉及 zuul,spring cloud gateway,这两个组件主要为外部的调用者比如其他的系统和我们的微服务进行通信的时候,由外到内是怎么彼此进行访问的,那么这些就是由这两个组件进行完成的
配置中心就是存放我们应用程序配置的地方,可能我们有上百个应用程序,那么每个应用程序都是一个微服务,那么就会产生一个很严重的问题,就是这些配置文件放在什么地方比如每个服务下都放一个 xml,或者 yml,维护起来是非常不方便的,因为改一个参数,就要对所有的应用进行调整,为了解决这个问题配置中心就出现了,相当于又提供了一个微服务把我们应用中所有的配置文件,都放在了配置中心中,那么其他应用都是通过配置中心来获取到这些配置文件的而不是我们要这个这个配置文件放到每个程序中,这样的好处就是可以将我们的配置文件进行集中的管理,只需要改一个地方所有地方都能生效
spring cloud 微服务组成
消息总线,spring cloud stream 或者 spring cloud bus 就跟我们的消息 mq 差不多就是我们发布一个信息,到我们队列里面由其他的微服务或者其他的应用进行获取提供了系统与系统之间或者微服务与微服务之间的消息传递过程,这个中间增加了一个额外的东西叫做消息总线,具体的消息总线可以是 mq 或者是 Redis,不同的厂商实现了不同的实现
安全控制是针对我们安全的管理,在我们传统网站开发的时候,应用的访问控制有授权的可以使用这个功能,没有授权的就无法进行访问,安全控制在 spring cloud 中也是存在的提供了 AUTH2.0 方案的支持,链路监控就是对我们消息传递的过程要进行统筹和监控,比如系统中有 10 个微服务,而这 10 个微服务是彼此依赖的,第一个微服务它是底层最基础的用户管理而第二个微服务是基于用户管理开发一个权限管理,在往上是应用管理,应用系统的扩展,每一个微服务之间彼此之间进行依赖在顶层我们进行调用的时候会安装微服务的调用顺序一级一级消息往下传递,这样做有一个问题来了,如果中间有个环节出现了问题,没有响应服务我们在使用的角度当前我们的请求失败了,但是具体的环节不知道是在那一块出现问题,那么链路监控就是让我们快递定位消息传递过程哪个阶段进行出错,有助于我们问题的排查
spring cloud cli 命令行工具,来实现我们开发来实现的一些功能,spring cloud cluster 是对我们集群管理的一个辅助工具
现在去交付微服务到 k8s 中举个 demo 仅供参考
一、发布流程设计
二、准备基础环境
三、在 Kubernetes 中部署 jenkins
四、jenkins pipeline 及参数化构建
五、jenkins 在 k8s 中动态创建代理
六、自定义构建 jenkins-slave 镜像
七、基于 kubernetes 构建 jenkins ci 系统
八、pipeline 集成 helm 发布 spring cloud 微服务
一、传统的发布流程是怎么样的?
现在的这个发布流程设计还是要和自己的项目中去考量,布置一个大体的拓扑图,那么比如没有这样的场景 jenkins 去发布微服务,它是一个怎么样的流程,作为运维来讲首先要去拉代码,开发已经将这个项目开发好了,并推送到 git 仓库中或者部署的私有的 gitlab 代码仓库中,第一步做的就是将这个代码拉下来,拉完代码,一般代码都是 Java 应用,会设计到一个编译,编译出来一个可部署的包,一般微服务是 jar 包,或者直接启动的应用程序,然后就开始去封装这个服务了,一般就是将这个 jar 包或者应用程序,通过 dockerfile 去达成一个可部署的镜像,这个镜像一般会自己制作 jdk 环境,或者就是 jre 环境,就是基础镜像能够运行这个镜像的底包,最后一步就是部署到 k8s 中,这里就会写一些 yaml 文件了去把这个镜像部署到 k8s 中,也就是容器的编排,另外还要考虑怎么将这个应用暴露出去,让用户访问到。
使用 jenkins 自动话发布的流程是这么样的?
显然这种方式发布多个微服务很不高效,所以就需要 ci/cd,这么一说,那么有 jenkins 了,怎么将这种方式自动化起来,减少人工的干预。
上面那张图,首先是这样的,开发将代码推送到 git 仓库中,通过 commit 提交上去,然后再到 jenkins 了,它负责的任务就是 checkout 代码的拉取,code compile 代码的编译,docker build &push,镜像的构建与推送到 harbor 仓库中,然后 deploy,将应用部署到 k8s 中,这里呢由于可能是很多的微服务,那么我们就需要模版的代替,去发布微服务,这里我们就会需要用到它原生的 helm 微服务发布工具来到 k8s 当中去 deploy,发布到测试环境中去,然后通过 slb 提供一个统一的出口,发布出去,中间产生的镜像也都会存放到 harbor 仓库中,当 QA 测试没有问题,这个镜像也就可以去发布生产环境中。
为什么需要 jenkins slave 架构
另外这里还提到了一个 jenkins,slave 的一个架构,主要的是可以动态的可以完成这些任务,动态的去调度一个机器和一个 pod 来完成这几步的任务,因为当任务很多时,也就是都在 jenkins master 去做,显然任务多了负载就高了,所以就需要引入这个 slave 去解决这个问题。
二、准备基础环境,所需的组件来完成我们流程的发布
1、k8s——(ingress controller、coredns、pv 自动供给)2、harbor, 并启用 chart 存储功能,将我们的 helm 打成 chart 并存放到 harbor 中
3、helm-v3 工具,主要来实现模版化,动态的将应用渲染安装与卸载,更好的去管理微服务
4、gitlab 代码仓库,docker-compose 实现
5、MySQL,微服务数据库
6、在 k8s 中部署 eureka(注册中心)
1、检查 k8s 基础组件的环境是否安装:
1、默认我的这个基础的组件都是安装好的,ingress 和 coredns
[root@k8s-master1 ~]# kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
ingress-nginx nginx-ingress-controller-2vs56 1/1 Running 0 5h3m
ingress-nginx nginx-ingress-controller-586gw 1/1 Running 0 167m
ingress-nginx nginx-ingress-controller-pxztr 1/1 Running 0 5h3m
ingress-nginx nginx-ingress-controller-qp266 1/1 Running 0 5h6m
kube-system coredns-59fb8d54d6-vjn62 1/1 Running 0 5h7m
kube-system kube-flannel-ds-amd64-2hnkf 1/1 Running 0 5h7m
kube-system kube-flannel-ds-amd64-2smpl 1/1 Running 0 5h7m
kube-system kube-flannel-ds-amd64-jbrv4 1/1 Running 0 167m
kube-system kube-flannel-ds-amd64-jsxdf 1/1 Running 0 5h7m
kubernetes-dashboard dashboard-metrics-scraper-566cddb686-mddlb 1/1 Running 0 5h7m
kubernetes-dashboard kubernetes-dashboard-c4bc5bd44-wpgc7 1/1 Running 0 5h7m
1.2、k8s pv 的自动供给,这里当然也可以使用 Ceph 持久化存储,由于我的测试环境配置不够,先拿 NFS 对有状态的应用实现自动的 PV 供给。
先准备一台 NFS 服务器为 K8S 提供存储支持
[root@k8s-node3 ~]# yum -y install nfs-utils
创建共享的目录
[root@k8s-node3 ~]# mkdir /ifi/kubernetes -p
[root@k8s-node3 ~]# cat /etc/exports
/ifi/kubernetes 10.4.7.0/24(rw,no_root_squash)
[root@k8s-node3 kubernetes]# systemctl start nfs
[root@k8s-node3 ~]# systemctl enable nfs
并且要在每个 Node 上安装 nfs-utils 包,用于 mount 挂载时用。[root@k8s-master1 ~]# mount -t nfs 10.4.7.22:/ifi/kubernetes /mnt
由于 K8S 不支持 NFS 动态供给,还需要先安装 nfs-client-provisioner 插件
修改 nfs 的服务端地址和挂载的目录,这是我 nfs-client 的地址,如果借鉴的需要将 id_rsa.pub 给我
git clone git@gitee.com:zhaocheng172/nfs-client.git
[root@k8s-master1 nfs-client]# kubectl get pod
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 35m
nfs-client-provisioner-86dff449dd-68ngn 1/1 Running 0 106s
2、镜像仓库 Harbor
2.1 安装 docker 与 docker-compose
# wget http://mirrors.aliyun.com/docker-ce/linux/CentOS/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
# yum install docker-ce -y
# systemctl start docker
# systemctl enable docker
docker-compose 的下载地址:https://docs.docker.com/compose/install/
curl -L https://github.com/docker/compose/releases/download/1.25.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
2.2 解压离线包部署
下载地址:https://github.com/goharbor/harbor
# tar zxvf harbor-offline-installer-v1.10.1.tgz
# cd harbor
# vi harbor.yml
hostname: harbor.zhaocheng.com
# ./prepare
# ./install.sh --with-chartmuseum
# docker-compose ps
–with-chartmuseum 参数表示启用 Charts 存储功能。
进行访问:
http://harbor.zhaocheng.com
这个已经启动 chart 功能
2.3 配置 Docker 可信任
由于 habor 未配置 https,还需要在 node 节点上的 docker 配置可信任。
# cat /etc/docker/daemon.json
{"registry-mirrors": ["https://38vve9ja.mirror.aliyuncs.com"],
"insecure-registries": ["harbor.zhaocheng.com"]
}
# systemctl restart docker
3、helm-v3 工具
3.1 安装 helm 工具
[root@k8s-master1 helm]# wget https://get.helm.sh/helm-v3.0.0-linux-amd64.tar.gz
[root@k8s-master1 helm]# tar xf helm-v3.0.0-linux-amd64.tar.gz
[root@k8s-master1 helm]# mv linux-amd64/helm /usr/bin/
[root@k8s-master1 helm]# helm --help
3.2 安装 push 插件
# git clone https://gitee.com/zhaocheng172/helm-push.git
# tar zxvf helm-push_0.7.1_linux_amd64.tar.gz
# mkdir -p /root/.local/share/helm/plugins/helm-push
# chmod +x bin/*
# mv bin plugin.yaml /root/.local/share/helm/plugins/helm-push
3.3 添加 repo
# helm repo add --username admin --password Harbor12345 myrepo http://192.168.30.27/chartrepo/library
3.4 推送与安装 Chart
helm 安装好有默认的模版,那么我们先使用它的进行生成一个 chart 包,用于我们测试推送到我们的 Harbor 仓库中,这个 Chart 是当我们部署完之后,官方默认自带的模版,小的 demo,install 之后是一个 Nginx 的应用
[root@k8s-master ~]# mkdir chart-test
[root@k8s-master ~]# cd chart-test/
[root@k8s-master chart-test]# helm create test
[root@k8s-master chart-test]# helm install test01 test
NAME: test01
LAST DEPLOYED: Sat Mar 14 17:10:20 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=test,app.kubernetes.io/instance=test01" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace default port-forward $POD_NAME 8080:80
这里是直接使用他们自己写的模版创建的 pod
查看这个 pod 已经正常运行,而这个 test01 是我们自己起的名字,这个部署的时候一般是我们的微服务的名称
要是去加个 –dry-run 的话就是预先执行,一般看看这个模版有没有都执行成功,有没有问题
[root@k8s-master chart-test]# helm install test02 --dry-run test/
[root@k8s-master chart-test]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-564dddddd6-dgv26 1/1 Running 8 26h
test01-f7f8c5759-zv8kf 1/1 Running 0 31s
没什么问题的话我们就需要将这个 chart 达成一个包,以便下次的时候再用
[root@k8s-master chart-test]# helm package test/
Successfully packaged chart and saved it to: /root/chart-test/test-0.1.0.tgz
[root@k8s-master chart-test]# ls
test test-0.1.0.tgz
推送到我们的镜像 harbor 仓库中
[root@k8s-master chart-test]# helm push test-0.1.0.tgz --username=admin --password=Harbor12345 http://192.168.30.27/chartrepo/library
Pushing test-0.1.0.tgz to http://192.168.30.27/chartrepo/library...
Done.
tgz 包安装 helm,一般用于之前制作的包,比如我们之前制作的模版,如果想使用了,那么直接就可以再使用
[root@k8s-master ~]# helm install test02 test-0.1.0.tgz
NAME: test02
LAST DEPLOYED: Sat Mar 14 17:49:00 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
4、部署 gitlab,这里分别我整理了两个方法来部署 gitlab,第一个是英文版页面,第二个是中文版页面
4.1# 安装 gitlab,建议 2c2g+
yum -y install policycoreutils openssh-server openssh-clients postfix
# 修改 postfix
sed -i 's/inet_interfaces = localhost/inet_interfaces = all/g' /etc/postfix/main.cf
sed -i 's/inet_protocols = all/inet_protocols = ipv4/g' /etc/postfix/main.cf
sudo systemctl enable postfix
sudo systemctl start postfix
curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
sudo EXTERNAL_URL="https://gitlab.example.com" yum install -y gitlab-ce
gitlab 配置文件路径
/etc/gitlab/gitlab.rb
external_url 'http://10.4.7.200:9999'
# 端口可以变动
gitlab-ctl reconfigure,这个初始化配置比较久
# gitlab 启动初始化操作
# gitlab-ctl restart
重启
4.2、使用 docker-compose 部署中文版 Gitlab
1)首先在一台新的服务器上安装 docker-ce, 并使用 docker 加速器,使用中科大的加速器 / 阿里云加速器
2)部署安装 docker-compose
3)安装 gitlab, 编辑 docker-compose 文件
[root@localhost ~]# mkdir gitlab
[root@localhost ~]# cd gitlab/
[root@localhost gitlab]# vim docker-compose.yml
yml 地址:git@gitee.com:zhaocheng172/gitlab-docker-compose.yml.git
4)创建相关目录,config 存储 gitlab 配置信息,data 存储数据库,logs 存储日志
[root@localhost gitlab]# mkdir -p /opt/gitlab/{config,data,logs}
[root@localhost gitlab]# ls -l /opt/gitlab/
total 0
drwxr-xr-x. 2 root root 6 Mar 14 19:44 config
drwxr-xr-x. 2 root root 6 Mar 14 19:44 data
drwxr-xr-x. 2 root root 6 Mar 14 19:44 logs
5)拉取 gitlan 中文版镜像,这个提前可以拉取下来,不然执行 docker-compose 需要等待
[root@localhost gitlab]# docker pull zhaocheng172/gitlab-ce-zh:latest
6)启动 gitlab 容器
在启动前,先要修改宿主机的 22 端口,因为宿主机占用了 22 端口,所以启动容器会失败,因为映射不过来,修改如下:vim /etc/ssh/sshd_config
默认在第 17 行修改:Port 2222
修改后,重启 sshd:systemctl restart sshd
[root@localhost gitlab]# netstat -anpt | grep 22
tcp 0 0 0.0.0.0:2222 0.0.0.0: LISTEN 31889/sshd
tcp6 0 0 :::2222 ::: LISTEN 31889/sshd
7)正式启动
[root@localhost gitlab]# docker-compose up -d
Creating network "gitlab_default" with the default driver
Creating gitlab ... done
[root@localhost gitlab]# docker-compose ps
Name Command State Ports
---------------------------------------------------------------------------------------------------------------
gitlab /assets/wrapper Up (health: starting) 0.0.0.0:22->22/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
8)设置 gitlab 开机启动
[root@localhost gitlab]# chmod +x /etc/rc.local
[root@localhost gitlab]# ls -l /etc/rc.local
lrwxrwxrwx. 1 root root 13 Mar 14 12:57 /etc/rc.local -> rc.d/rc.local
[root@localhost gitlab]# echo "cd /root/gitlab && docker-compose up -d" >> /etc/rc.local
9)gitlab 管理页面
输入配置的 ip 或者域名
这里我是使用的域名登陆
另外就是如果这台服务开启了 selinux 的话,要是重启机器的话会导致 ssh 连接不上,解决方法要不就是关闭 selinux,要不就是开启支持的端口,默认 docker-compose 部署 gitlab 官方推荐的是修改 22 端口,所以要修改一些配置
或者要不就是直接
#sed -i 's/enforcing/disabled/' /etc/selinux/config # 永久
# 安装修改工具
yum -y install policycoreutils-Python
# 查看 selinux 中的 ssh 的端口, 输出为 22
semanage port -l | grep ssh
# 新增端口
semanage port -a -t ssh_port_t -p tcp 2222
由于我是拿测试机做的所以我需要将这个写入我 windows 的 C:\Windows\System32\drivers\etc\hosts
192.168.30.27 harbor.zhaocheng.com
192.168.30.28 gitlab.zhaocheng.com
docker-compose 重启命令, 这个是需要在 docker-compose.yml 下才能重启
docker-compose restart
用户:root, 首次登陆需要手动设置密码
将代码推送到 gitlab 中
创建 ms 的仓库名称
创建完需要我们将我们的本地的代码上传到 gitlab 中来
现在我们先克隆一下我们这个 ms 的仓库到本地来
设置 git 全局设置,这里我们需要我们的用户也就是我们 gitlab 去登录这个平台的账号,提交代码的时候我们需要达到 master 分支,其他的开发去使用这个 gitlab 的时候我们需要给他去创建一个用户,也就是在 gitlab 去创建的,以便它们提交代码到仓库中
[root@k8s-master1]# mkdir ms
[root@k8s-master1]# cd ms
[root@k8s-master1 ms]# git config –global user.name “zhaocheng”
[root@k8s-master1 ms]# git config –global user.email “zhaocheng”
看到只有一个仓库。里面是空的,这个地方需要输入我们 gitlab 的账号与密码, 这样的话才能有访问控制,让你去拉这个代码
[root@k8s-master ms]# git clone http://192.168.30.28/root/ms.git
Cloning into 'ms'...
Username for 'http://192.168.30.28': root
Password for 'http://root@192.168.30.28':
warning: You appear to have cloned an empty repository.
当我们 git clone 之后这里有遗留下的空目录,因为这是刚才我们去测试去拉的代码,如有代码的话就拉取下来了
[root@k8s-master ms]# ls
ms
现在将开发写的代码解压之后,将里面的服务都拷贝到刚才我们拉取代码遗留的空仓库中
[root@k8s-master ms]# ls
ms simple-microservice-master simple-microservice-master.zip
[root@k8s-master ms]# cp simple-microservice-master/* -rf ms
[root@k8s-master ms]# cd ms
[root@k8s-master ms]# ls
basic-common eureka-service k8s lombok.config pom.xml product-service stock-service
db gateway-service LICENSE order-service portal-service README.md
提交缓存区[root@k8s-master1 ms]# git add .
使用 commit 打个 tag
格式 git commit -a -m “ 提交添加的注释信息 ” 或者写成先添代码
[root@k8s-master1 ms]# git commit -m "all"
推送到 gitlab 中
[root@k8s-master ms]# git push origin master
Username for 'http://192.168.30.28': root
Password for 'http://root@192.168.30.28':
Counting objects: 515, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (411/411), done.
Writing objects: 100% (515/515), 15.62 MiB | 17.78 MiB/s, done.
Total 515 (delta 60), reused 0 (delta 0)
remote: Resolving deltas: 100% (60/60), done.
To http://192.168.30.28/root/ms.git
* [new branch] master -> master
现在已经将代码推送到 gitlab 中
另外就是如果其他的开发人员需要将代码提交到 gitlab 上的话,那么我们就需要给他去创建一个用户,设置一定的权限,然后制定好密码,告诉他,它使用自己的账号去管理自己的项目代码
另外就是在使用 jenkins 做持续集成的时候,如果传统的一般会需要对 gitlab 做 SSH 秘钥对的认证,也就是在 jenkins 生成秘钥对,私钥自己留着将公钥放在右上角的 gitlab 账号处,会看到 settings 的下面,将公钥放到指定框内,也就是 jenkins 需要有权限去拉取 gitlab 上的代码,现在已经支持在 Jenkins 中以 key 的形式存储的 slavepod 中
另外就是分支说明
master 主分支,有且只有一个
release 线上分支,一般为线上版本,线上版本发布后,会将 release 分支合并到 master
develop 开发分支,通常给测试部署环境或者打包的分支,每个人在自己的分支上开发完成后,向 develop 分支合并
feature 通常为一个功能分支或者个人分支,一般有很多个,通常合并完成后会删除
5、mysql 微服务数据库
导入数据库到 Mysql
[root@k8s-master1 ~]# cd simple-microservice-dev3
[root@k8s-master1 simple-microservice-dev3]# ls
basic-common gateway-service lombok.config portal-service stock-service
db k8s order-service product-service
eureka-service LICENSE pom.xml README.md
[root@k8s-master1 simple-microservice-dev3]# cd db/
[root@k8s-master1 db]# ls
order.sql product.sql stock.sql
[root@k8s-master1 db]# scp * root@192.168.30.24:~
这里呢之前遇到一个问题当部署 gitlab 之后如果是使用的 docker 去部署的 gitlab 默认的 22 端口改成 2222 端口之后当用其他服务器进行 scp 或者 ssh 登录的时候就会显示拒接
[root@k8s-master db]# scp order.sql root@192.168.30.28:/root/
Permission denied (publickey).
lost connection
这里我安装了一个 mariadb 的实例进行测试
[root@harbor-mysql-gitlab ~]# yum install mariadb mariadb-server mariadb-devel
[root@harbor-mysql-gitlab ~]# systemctl start mariadb
[root@harbor-mysql-gitlab ~]# netstat -anpt |grep 3306
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 6226/mysqld
修改 mariadb 的密码,默认没有密码
[root@harbor-mysql ~]# mysql -u root -p
MariaDB [(none)]> set password for root@localhost = password('666666');
MariaDB [(none)]> flush privileges;
创建数据库
MariaDB [(none)]> create database tb_order;
MariaDB [(none)]> create database tb_product;
MariaDB [(none)]> create database tb_stock;
进入 order 数据库,把我们根目录下的 sql 语句导入我们的数据库中
MariaDB [(none)]> use tb_order;
MariaDB [tb_order]> source /root/order.sql;
进入 product 数据库,把我们根目录下的 sql 语句导入我们的数据库中
MariaDB [tb_order]> use tb_product ;
MariaDB [tb_product]> source /root/product.sql
进入 stock 数据库,把我们根目录下的 sql 语句导入我们的数据库中
MariaDB [tb_product]> use tb_stock ;
Database changed
MariaDB [tb_stock]> source /root/stock.sql;
MariaDB [tb_stock]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| tb_order |
| tb_product |
| tb_stock |
| test |
+--------------------+
7 rows in set (0.00 sec)
6、将 eureaka 集群部署到 k8s 中
[root@k8s-master ~]# yum install java-1.8.0-openjdk maven -y
软件包 1:java-1.8.0-openjdk-1.8.0.222.b10-0.el7_6.x86_64 已安装
将 eureka-service 进行 maven 编译可以执行的 jar 包
[root@k8s-master simple-microservice-dev1]# mvn clean package -D maven.test.skip=true
会在 target 下面生成 jar 包
[root@k8s-master eureka-service]# ls
Dockerfile pom.xml src target
将这个 eureka 制作成一个镜像,然后编排 yaml 文件好让这个容器运行起来
[root@k8s-master1 eureka-service]# cat Dockerfile
FROM java:8-jdk-alpine
RUN apk add -U tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY ./target/eureka-service.jar ./
EXPOSE 8888
CMD java -jar -Deureka.instance.hostname=${MY_POD_NAME}.eureka.ms /eureka-service.jar
构建并上传到我们的 harbor 仓库中,[root@k8s-master eureka-service]# docker build -t eureka .
这里需要我们去我们的 Harbor 创建一个项目仓库的名称,这里是创建的 microservice,并达成推送我们镜像的 tag 名称,这样推送的时候会直接找我们镜像仓库的地址 [root@k8s-master eureka-service]# docker tag eureka 192.168.30.27/microservice/eureka:latest
上传到我们的 harbor 上
这里需要登录一下我们的 harbor 仓库,才能上传
[root@k8s-master ~]# docker login 192.168.30.27
Username: admin
Password:
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
[root@k8s-master eureka-service]# docker push 192.168.30.27/microservice/eureka:latest
创建 k8s 登录 harbor 信息认证,这里并提前创建好命名空间 ms, 这样 k8s 才能到 harbor 仓库拉取镜像有这个 secret
先创建一个 ms 的命名空间
[root@k8s-master eureka-service]# kubectl create ns ms
namespace/ms created
[root@k8s-master eureka-service]# kubectl get ns
NAME STATUS AGE
default Active 68m
ingress-nginx Active 55m
kube-node-lease Active 68m
kube-public Active 68m
kube-system Active 68m
ms Active 4s
[root@k8s-master k8s]# kubectl create secret docker-registry registry-pull-secret --docker-server=192.168.30.27 --docker-username=admin --docker-password=Harbor12345 --docker-email=admin@zhaocheng.com -n ms
查看状态
[root@k8s-master ~]# kubectl get pod,svc,ing -n ms
NAME READY STATUS RESTARTS AGE![](https://s4.51cto.com/images/blog/202003/28/0c07c95b7ce22d59b778082427c7d2bd.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
pod/eureka-0 1/1 Running 1 16h
pod/eureka-1 1/1 Running 0 16h
pod/eureka-2 1/1 Running 1 16h
访问 eureka
三、在 Kubernetes 中部署 jenkins
在 k8s 中去部署 jenkins 需要注意的是本身它需要一个存储,就是它需要存储这些插件和配置的 job 都是需要存储的,而我们部署到 k8s 中这个 pod 是不固定的,有可能在第一个节点也有可能在第二个节点,那就需要保证这个 jenkins 这个存储功能换个节点也能实时读取到,这就用到了 k8s 的 pv 和 pvc 了,就会使用到持久化存储,这样的话就会用到 pv 的自动供给,然后用 jenkins 持久化目录到 pv 上,持久到远程的存储上
创建一个 service-account 绑定到 rbac 里面,rbac 主要就是设置一些权限让 jenkins 访问到 k8s,service-connout 主要是让 pod 访问到 apiserver, 它去 apiserver 调度创建 pod
这是我 jenkins 的地址,如果借鉴的需要将 id_rsa.pub 给我
git clone git@gitee.com:zhaocheng172/jenkins-k8s.git
[root@k8s-master1 jenkins]# ls
deployment.yml ingress.yml rbac.yml service-account.yml service.yml
deployment 参数详解:
主要来部署 jenkins 的容器的配置
serviceAccountName: jenkins 使用 service-account 创建的名字
image: jenkins/jenkins:lts 使用官方长期维护 lts 镜像
- containerPort: 8080 jenkins ui 的访问的端口
- containerPort: 50000 slave 访问 master 访问 jenkins 所使用的端口
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home 说到 jenkins 持久化数据,在 jenkins 中需要持久化的目录就是它的工作目录,也就是它所产生有状态的数据都放在这个 /var/jenkins_home 下
volumes:
- name: jenkins-home
persistentVolumeClaim:
claimName: jenkins-home 而持久化的这个目录也都会落到这个 pvc 这个卷
name: jenkins-home
spec:
storageClassName: "managed-nfs-storage"
accessModes: ["ReadWriteOnce"] 而 pvc 这里就是我们创建使用的 storageclass 自动供给来提供的 pv,申请 5G,设置都可读写
查看创建的 storage class
[root@k8s-master1 jenkins]# kubectl get sc
NAME PROVISIONER AGE
managed-nfs-storage fuseim.pri/ifs 22h
service 参数:
这里主要就是使用 nodeport 去开放一个端口,另外一般生产环境都使用 ingress,ingress 需要关联 service,还要使用 nginx 做反向代理,将这个域名通过 slb 发布出去[root@k8s-master1 jenkins]# kubectl apply -f .
四、jenkins pipeline 及参数化构建
jenkins pipeline 是一套插件,支持在 jenkins 中实现集成和持续交付管道;
pipeline 通过特定语法对简单到复杂的传输管道进行建模;
- 声明式:遵循与 Groovy 相同语法。pipeline{}
- 脚本式:支持 Groovy 大部分功能,也是非常表达和灵活的工具。node{}
*jenkins pipeline 的定义被写入一个文本文件,称为 jenkinsfile。
去 Manage jenkins 去安装插件,找到 manage plugins,点击 Available 检索 pipeline,进行安装,安装后选择 install without restart
现在去创建一个流水线进行测试一下
部署完之后可以使用 log 查看登录的密码
[root@k8s-master jenkins]# kubectl logs jenkins-7d5fbd857d-tmwbm
6e22df63432f474f863dc07c9d291967
一般最好进去的时候不安装插件,第一默认使用的国外的源,下载插件的时候很慢,我们需要做做一些优化
先创建一个 test 的 pipeline 的语法流水线,熟悉一下怎么发布任务
在这里就可以看到语法的格式,这里分为两种,以 pipeline 开头的也叫声明式语法,主要遵循的 Groovy 的相同语法来实现的 pipeline {}
这个也是比较主流的方式。
测试一个 hello 的语法,默认提供的 pipeline, 进行使用
直接 build,这个就能构建
当我们构建完成后,这里会显示一个执行的步骤显示一个具体的内容,可以看到 jenkins 的工作目录,默认传统部署 jenkins 的目录为 /root/.jenkins 下,而作为在 k8s 部署 jenkins 需要考虑数据的持久化了,因为 pod 遇到不确定的因素进行重启之后,那么这个 pod 的数据就会丢失,所以针对这个问题,我们就需要将这个 pod 的工作目录挂载到持久卷上,这样的话,即使 pod 重启飘移到其他的节点也能读取到相应的数据了。
可以看到 k8s 的持久化目录是在这个 jobs 目录下,将我们构建的内容和数据都放在这里了。
[root@k8s-node3 ~]# ll /ifi/kubernetes/default-jenkins-home-pvc-0d67f7f5-2b31-4dc8-aee2-5e7b9e0e7e19/jobs/test/
总用量 8
drwxr-xr-x 3 1000 1000 50 1月 13 11:36 builds
-rw-r--r-- 1 1000 1000 1021 1月 13 11:36 config.xml
-rw-r--r-- 1 1000 1000 2 1月 13 11:36 nextBuildNumber
测试一个 hello 的 pipeline 的语法格式
pipeline {
agent any
stages {stage('Build') {
steps {echo "hello"
}
}
stage('test') {
steps {echo "hello"
}
}
stage('deploy') {
steps {echo "hello"
}
}
}
}
控制台输出了三个任务,分别执行 hello,大概就是这个样子
目录也持久化了,看到新增的 jenkins 的项目任务
[root@k8s-node3 ~]# ll /ifi/kubernetes/default-jenkins-home-pvc-0d67f7f5-2b31-4dc8-aee2-5e7b9e0e7e19/jobs/
总用量 0
drwxr-xr-x 3 1000 1000 61 1 月 13 11:36 test
drwxr-xr-x 3 1000 1000 61 1 月 13 14:04 test-demo
jenkins pipeline 就像一个管道的建模一样, 在这个脚本里完成了整个生命周期各个阶段,从 development 开发提交代码 commit id, 到 build 构建,再到 test 测试,再到 stage 步骤做一些处理,deploy 部署到 dev 或者 qa 环境中,最后到线上, 其实在这个流程中它是有一个目的的,刚开始是在开发环境,最终是把它带到线上环境,而中间一系列的流程都是通过管道的形式串起来,而这个管道这个模型是通过 pipeline 去书写的,这个语法就是这个模型,需要把这个生命周期的所需的都套进这个模型中来,然后由 jenkins pipeline 去管理
第一用这个 pipepine 它有很大的特点
1、可视化页面,每个步骤都可以可视化展示,方便我们去解决每个步骤的相关问题
2、每个步骤都写脚本里面了,只需要维护这个脚本就好了,而这个脚本可以写的具有通用性,如果想写多个项目时,比如发布 3 组微服务,那么第一个写的 pipeline,那么也同样适用于第二个和第三个微服务的模版。那么这个需要考虑它们有哪些不同点?
不同点:
1)拉取 git 代码的地址不一样
2)分支名也不一样,因为是不同的 git 地址,所以打的分支名也不一样。
3)部署的机器也不一样,有可能这几个服务部署在 node1, 另外的服务部署在 node2 或者 node3
4)打出的包名不一样
所以要把这些不同点,做成一种人工交互的形式去发布,这样的话这个脚本才具有通用性,发布服务才能使用这写好的 pipeline 发布更多的微服务,而且 jenkins pipeline 支持参数化构建。
这里也就是 pipeline 一些的语法,比如选择 parameters 参数化构建,选择框式参数 choice parameter,比如给它几个值,然后让它动态的去输入,或者凭据参数 credentials parameter, 或者文件参数 file parameter,或者密码参数 password parameter,或者运行参数 run parameter, 或者字符串参数 string paramether , 或者多行字符串参数 multi-line string parameter, 或者布尔型参数 boolean parameter
比如选择 choice parameter,选择型参数
比如发布的 git 地址不一样,那么就需要多个地址可以去选择,需要使用 choice parameter 选择框型参数,这个可以体现在构建的页面,也可以体现在 configure 配置的页面, 这样配置也比较麻烦,所以直接在 configure 里面直接添加对应的参数就可以
先配置一个看一下效果
将生成的选择型参数添加到指定的 pipeline 的语法中,save 一下
再回到项目中可以看到 build 名字从 build now 换成了 build with parameters,也就是增加了几行的的配置,可以进行选择的去拉去哪个分支下的代码了
也可以在 configure 去配置,这两个地方都可以添加,要是再添加一个直接 add parameter,可以选择多种类型的参数帮助我们去构建这个多样式的需求
再比如分支这一块,可能每次打的分支都不同,这个不是固定的,所以需要一个 git 的参数化构建,那么这个就需要动态的去从选择的 git 地址获取到当前的所有的分支
还有一个分布的机器不同,这个也可以使用刚才的 choice parameter,将多个主机的 ip 也进去
将这个生产的语法,复制到 pipeline 语法中
choice choices: [‘10.4.7.12’, ‘10.4.7.21’, ‘10.4.7.22’], description: ‘ 发布哪台 node 上 ’, name: ‘host’
构建一下,发现也可以使用选择型参数了,类似这个的人工交互就可以选择多个参数了,可以写一个通用的模版,就处理人工交互的逻辑
现在我们可以去人工选择了,这里面的值怎么获取到,我们处理不同的项目,必须在这里面去实现,比如选择这个 git 之后,拉取这个代码编译构建,这些可能都是一些相同点,不同点发布的机器不一样,所以要选择用户是拿到的哪个 git 地址,发布的哪个机器,在脚本里去拿到,其实默认这个 name 就是一个变量,jenkins 已经将这个赋予变量,并且 pipeline 可以直接获取这个变量名,就是刚才定义的 git,host 这个名字,那么我们从刚才设置的 parameters 里去测试这个变量可不可以拿到,拿到的话说明这个就很好去处理
构建一下,现在构建成功后已经是构建成功了,也已经获取到刚才我们的 git 这个参数下的值了
有了这些方式,就可以将这些不同点通过里面的 agent 和 shell 脚本来处理了, 写 pipeline 参数化构建就是满足更多的一个需求,能适配更多的项目,能让人工干预的做一些复杂的任务
五、jenkins 在 k8s 中动态创建代理
如何在 k8s 中动态的创建 slave 代理?
当完成这些任务之后考虑的问题,这些任务都是在 jenkins 机器去完成的,那么这个也肯定是在 pod 中去运行的,因为我们的是将 jenkins 部署在 pod 中的,也就是这当前的这个节点去完成的拉取代码,编译,构建镜像,发布,那么可能会遇到一个问题,那么项目很多,每天做持续集成很高,十几次甚至上百次,面对这样的一个需求量,当前的这个 pod 是很难支撑的,就好比刚才的 job,有十几个人去运行,来运行不同的服务,本来是可以几分钟完成的事情,最后导致 10 多分钟才执行完成,这样的话就很耽误项目进度了,所以就需要使用 jenkins 的 master-slave 架构了,而 master 只负责调度分配,slave 来完成这些 job 任务,而 slave 是由物理机或者虚拟机存在的,和 master 保持通信,只要有任务就下发到 slave 节点,这样就解决了单 jenkins 的性能问题了
也就是提前创建好几个 jenkins-slave,在其他节点让它们待定着,当 master 有人点 job 构建了,这个 jenkins 会帮你把这个 job 具体做的事,转发到 slave 去干活,master 也就启到一个领导的角色,它本身就没什么压力了,只负责调度了,那么如果不用 k8s 的容器的这样架构,就好比在一台机器上装了一个 jenkins,然后找台主机做 slave, 在 manage node, 添加 new node, 然后这个就会通过 master 下发任务让 slave 去完成了。
那么我们的 jenkins 是从 k8s 去部署的,所以我们的 slave 也是从 k8s 中去考虑,就可以将这个 slave 当成一个 pod 去处理,master 就拿这个 pod 去处理
这个的话也就是可以预先起一些 slave, 也可以动态的去创建 slave,要是预先启动 slave 的话,可能会消耗一些资源,这些资源也并不是必须要消耗的,所以就要考虑动态的去考虑 slave,也就是即开即用,不用就销毁了,当这个量大的时候,这个就比较明显了,所以在 k8s 中去创建代理可以通过插件去做到的
直接去安装一个 kubernetes 的插件
安装插件的时候我们需要在我们的持久化目录中去修改我们的国外地址
[root@k8s-node3 updates]# sed -i 's/http:\/\/updates.jenkins-ci.org\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' default.json && \
sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' default.json
在 jenkins 页面上 restart, 重启 jenkins 生效
要想动态的去在 k8s 中创建 slave pod, 所以要连接 k8s,定义连接 k8s 的地址是多少,创建 slave pod 连接 jenkins 的地址是多少
找到管理 jenkins 这里,找到系统配置 configure system
当安装好插件之后,会在最下面发现一个 cloud
这里连接 k8s 直接使用 service 的地址就可以,因为我们部署了 coredns 直接可以就要解析到这个 k8s,点击连接测试可以连接
如果 jenkins 是在外部去部署的,也就是单独哪台服务器去部署的,走的传统部署,也就是要连接 k8s 对外的地址也就是 https://10.4.7.11:6443 这个端口,并且将它的 CA 添加进来添加一个凭据,这样才能连接成功
并且还有将 jenkins 的地址也写上,http://jenkins.default,或者写成连接 ip 的地址也可以
现在 jenkins 已经知道要连接哪个 k8s,那么再去创建这个 slave 容器的 pod,要创建 pod 必须就的需要镜像,如果没有镜像,即使 jenkins 像 k8s 发起创建一个 pod,k8s 肯定不知道去创建哪个 pod,必须有镜像,这个镜像也就是 slave 的镜像,所以下一步我们需要做的就是构建一个镜像。
让 k8s 使用这个镜像拉取一个 pod, 而这个镜像制作的话,看需要什么环境的包。
六、自定义构建 jenkins-slave 镜像
jenkins slave 制作这个镜像看需要什么?
1、需要什么开发语言?因为 slave 去完成的需要代码的编译,如果是 java 的语言,那么就需要 maven 的环境,如果是 go 就需要去用 go 去编译,所以开发语言不一样,slave 所做的事环境也不一样
2、额外环境,比如 docker, 需要打镜像对微服务,以及推送到镜像仓库都需要 docker 的环境,还会使用 helm,helm 是直接将服务部署到 k8s 中。
比如就拿 java 项目为例,一般微服务也都是使用 java 去写的
代码的编译,一般使用比较多的就是 maven,这个 maven 还依赖 jdk,打包镜像需要 docker, 我们需要使用 helm 去做持续部署,所以这也就是这个镜像里面需要封装的依赖
另外还有一点,这个镜像怎么去做为一个 slave 去存在呢,传统的在页面添加一个就可以了,当连接的时候,会连接到 slave 节点帮你启动一个 agent,也就是下发一个 jar 包,这个 agent 就可以直接和 master 去交互了,那么将这个 jar 包 kill 掉,这个 slave 也就是不可用状态了,所以它们直接的一个交互就是由自己实现的一个程序去完成的,每个 slave 上面会有一个 agent,master 与 agent 去交互,agent 去完成这个 job 任务,那么这个镜像里面也得需要包含这个 agent 这个 jar 包,传统的方式会自动的帮你去安装了,但是镜像的还得自己去加上,加上之后才能去连接 jenkins,master 才能去下发任务
[root@k8s-master1 jenkins-slave]# ls
Dockerfile helm jenkins-slave kubectl settings.xml slave.jar
这个 jenkins-slave 是一个脚本主要来启动这个 slave.jar, 那么这个还需
要一个 docker 环境,本身这个 pod 就是以 docker container 启动的,那么这个 pod 也就可以做数据卷挂载了,因为这个 pod 是在每个 node 上去启动的,每个 node 上都有 docker, 那么直接就可以把每个 node 上的 docker 挂载到容器中就可以了,所以这就用到了一个 docker in docker,这个容器里面又有了一个 docker 的环境,所以我们需要写数据卷将它挂载进来,还有 kubectl 这个命名,一般主要来查看 pod 的一些状态,而且使用这个命令来完成 k8s 的各种各样的操作,但是需要它这个命令,只需要它连接 k8s 的认证信息,因为 k8s 有它的鉴权
现在将这个镜像打包成镜像推送到镜像仓库中
[root@k8s-master1 jenkins-slave]# cat Dockerfile
FROM centos:7
RUN yum install -y java-1.8.0-openjdk maven curl git libtool-ltdl-devel && \
yum clean all && \
rm -rf /var/cache/yum/* && \
mkdir -p /usr/share/jenkins
COPY slave.jar /usr/share/jenkins/slave.jar
COPY jenkins-slave /usr/bin/jenkins-slave
COPY settings.xml /etc/maven/settings.xml
RUN chmod +x /usr/bin/jenkins-slave
COPY helm kubectl /usr/bin/
ENTRYPOINT ["jenkins-slave"]
构建镜像的时候可以直接写 harbor 的地址加镜像的名称,这样直接可以 push,不然构建完还得打个 tag
[root@k8s-master jenkins-slave]# docker build . -t 192.168.30.27/library/jenkins-slave:jdk-1.8
[root@k8s-master jenkins-slave]# docker push 192.168.30.27/library/jenkins-slave
查看仓库已经将镜像推送成功
现在就可以去测试能不能动态的去创建 jenkins-slave,需要将 pipeline 这个脚本能去调用刚才我们配置的 k8s 的插件
jenkins 官方地址给出了相关的 pipeline 调用 k8s 的插件的用法
github 地址:https://plugins.jenkins.io/kubernetes
七、基于 kubernetes 构建 jenkins ci 系统
现在去动态的在 pipeline 去引用并创建一个 slave 的镜像,使用 pipeline 在 k8s 去运行这个 pod, 然后将这个 pod 发布我们的任务,刚才我们是先安装的插件,以对 jenkins 可以实现 slave 的代理,可以正常去交互,又制作了我们的 slave 的镜像,将 java 所需的配置也都封装在这个 slave 里面这样的话,我们就可以采用这个 slave 来完成下发的任务了,而 master 只作为一个管理节点给他们下发任务到 slave 上
这就是我们的 pipeline,现在目前是可以使用我们这个 slave 这个镜像来发布任务
安装 pipeline 插件
pipeline {
agent {
kubernetes {label 'jenkins-slave'
yaml """
apiVersion: v1
kind: Pod
metadata:
name: jenkins-slave
spec:
containers:
- name: jnlp
image: "192.168.30.27/library/jenkins-slave:jdk-1.8"
"""
}
}
stages {stage('Build') {
steps {echo "hello"
}
}
stage('test') {
steps {echo "hello"
}
}
stage('deploy') {
steps {echo "hello"
}
}
}
}
现在可以在 jenkins 中动态的去创建 pod,只有有下发任务的时候才去创建 pod,而 jenkins-master 只负责任务调度,slave 来做任务处理来节省资源,当任务完成之后,这个 slave-pod 也会自动的销毁
[root@k8s-master1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
jenkins-7d5fbd857d-6tgdj 1/1 Running 0 23m
jenkins-slave-gdtqz-p17b1 1/1 Running 0 40s
八、pipeline 集成 helm 发布 spring cloud 微服务
现在编写这个 pipeline 脚本来实现自动化发布微服务
#!/usr/bin/env groovy
// 所需插件: Git Parameter/Git/Pipeline/Config File Provider/kubernetes/Extended Choice Parameter
// 公共
def registry = "192.168.30.27"
// 项目
def project = "microservice"
def git_url = "http://192.168.30.28/root/ms.git"
def gateway_domain_name = "gateway.zhaocheng.com"
def portal_domain_name = "portal.zhaochengr.com"
// 认证
def image_pull_secret = "registry-pull-secret"
def harbor_registry_auth = "e5402e52-7dd0-4daf-8d21-c4aa6e47736b"
def git_auth = "a65680b4-0bf7-418f-a77e-f20778f9e737"
// ConfigFileProvider ID
def k8s_auth = "7ee65e53-a559-4c52-8b88-c968a637051e"
pipeline {
agent {
kubernetes {label "jenkins-slave"
yaml """
kind: Pod
metadata:
name: jenkins-slave
spec:
containers:
- name: jnlp
image: "${registry}/library/jenkins-slave-jdk:1.8"
imagePullPolicy: Always
volumeMounts:
- name: docker-cmd
mountPath: /usr/bin/docker
- name: docker-sock
mountPath: /var/run/docker.sock
- name: maven-cache
mountPath: /root/.m2
volumes:
- name: docker-cmd
hostPath:
path: /usr/bin/docker
- name: docker-sock
hostPath:
path: /var/run/docker.sock
- name: maven-cache
hostPath:
path: /tmp/m2
"""
}
}
parameters {gitParameter branch: '', branchFilter: '.*', defaultValue: '', description: '选择发布的分支', name: 'Branch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH'
extendedChoice defaultValue: 'none', description: '选择发布的微服务', \
multiSelectDelimiter: ',', name: 'Service', type: 'PT_CHECKBOX', \
value: 'gateway-service:9999,portal-service:8080,product-service:8010,order-service:8020,stock-service:8030'
choice (choices: ['ms', 'demo'], description: '部署模板', name: 'Template')
choice (choices: ['1', '3', '5', '7'], description: '副本数', name: 'ReplicaCount')
choice (choices: ['ms'], description: '命名空间', name: 'Namespace')
}
stages {stage('拉取代码'){
steps {checkout([$class: 'GitSCM',
branches: [[name: "${params.Branch}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [], submoduleCfg: [],
userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]
])
}
}
stage('代码编译') {
// 编译指定服务
steps {sh """
mvn clean package -Dmaven.test.skip=true
"""
}
}
stage('构建镜像') {
steps {withCredentials([usernamePassword(credentialsId: "${harbor_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {sh """
docker login -u ${username} -p '${password}' ${registry}
for service in \$(echo ${Service} |sed 's/,/ /g'); do
service_name=\${service%:*}
image_name=${registry}/${project}/\${service_name}:${BUILD_NUMBER}
cd \${service_name}
if ls |grep biz &>/dev/null; then
cd \${service_name}-biz
fi
docker build -t \${image_name} .
docker push \${image_name}
cd ${WORKSPACE}
done
"""
configFileProvider([configFile(fileId: "${k8s_auth}", targetLocation: "admin.kubeconfig")]){sh """
# 添加镜像拉取认证
kubectl create secret docker-registry ${image_pull_secret} --docker-username=${username} --docker-password=${password} --docker-server=${registry} -n ${Namespace} --kubeconfig admin.kubeconfig |true
# 添加私有 chart 仓库
helm repo add --username ${username} --password ${password} myrepo http://${registry}/chartrepo/${project}
"""
}
}
}
}
stage('Helm 部署到 K8S') {
steps {sh """
common_args="-n ${Namespace} --kubeconfig admin.kubeconfig"
for service in \$(echo ${Service} |sed 's/,/ /g'); do
service_name=\${service%:*}
service_port=\${service#*:}
image=${registry}/${project}/\${service_name}
tag=${BUILD_NUMBER}
helm_args="\${service_name} --set image.repository=\${image} --set image.tag=\${tag} --set replicaCount=${replicaCount} --set imagePullSecrets[0].name=${image_pull_secret} --set service.targetPort=\${service_port} myrepo/${Template}"
# 判断是否为新部署
if helm history \${service_name} \${common_args} &>/dev/null;then
action=upgrade
else
action=install
fi
# 针对服务启用 ingress
if [\${service_name} == "gateway-service" ]; then
helm \${action} \${helm_args} \
--set ingress.enabled=true \
--set ingress.host=${gateway_domain_name} \
\${common_args}
elif [\${service_name} == "portal-service" ]; then
helm \${action} \${helm_args} \
--set ingress.enabled=true \
--set ingress.host=${portal_domain_name} \
\${common_args}
else
helm \${action} \${helm_args} \${common_args}
fi
done
# 查看 Pod 状态
sleep 10
kubectl get pods \${common_args}
"""
}
}
}
}
pipeline 解析
1、首先去安装这几个插件
Git Parameter 可以实现动态的从 git 中获取所有分支
Git 拉取代码
Pipeline 刚才安装的 pipeline,来实现这个 pipeline 流水线的发布任务
Config File Provider 主要可以将 kubeconfig 配置文件存放在 jenkins 里,让这个 pipeline 引用这个配置文件,比如构建的 slave 镜像里面有 kubectl, 那么连接 k8s 肯定需要授权,直接拷贝这个命令肯定不好使,而且像 helm -v3 版本也是通过 kubeconfig 来连接 k8s-api 来部署的任务,这两个命令都是通过 kubeconfig 去读取的 k8s,所以我们需要将这两个命令给他们权限来连接 k8s,所以需要准备一个 kubeconfig 文件,能让这个两个工具可以读到,但是这两个命令都是在 slave 的 pod 中,所以我们需要使用这个插件将 kubeconfig 文件由 jenkins 来保存,然后再通过 jenkins 特定的语法让它拿到,让它保存到 slave 中,形成一个动态的文件,动态的加进去,然后 helm,kubectl 通过这个文件就能直接连接 k8s 集群了,这样的好处主要是安全,也可以直接将这个 kubeconfig 文件打到镜像中,也可以,但是这样也不是很安全,别人一旦拿到这个 kubeconfig 文件久相当于把 kubectl 的权限给别人了,可以访问 k8s 集群了,这样就不安全了,所以我们还是按插件的方式去安装这个, 动态的放在 jenkins 中还是比较好的
kubernetes 动态的去创建代理,好让 k8s 连接到 jenkins,可以动态的去伸缩 slave 节点
Extended Choice Parameter 进行对选择框插件进行扩展,可以多选,扩展参数构建,而且部署微服务还需要多选,比如同时发布两个微服务,三个微服务, 不可能每发布一个要点一下,这样肯定是不现实的。
2、参数含义
// 公共
def registry = “192.168.30.27” 比如镜像仓库的地址
// 项目
def project = “microservice” 项目的名称
def git_url = “http://192.168.30.28/root/ms.git” 微服务的 gitlab 的项目的 git 地址
def gateway_domain_name = “gateway.zhaocheng.com” 微服务里面有几个对外提供服务,指定域名
def portal_domain_name = “portal.zhaocheng.com” 微服务里面有几个对外提供服务,指定域名,因为不是所有的微服务都是提供域名的,所以要为需要配置的提供一个域名
// 认证
def image_pull_secret = “registry-pull-secret” 当 helm 去部署应用的时候,我们需要拉取去在拉取仓库的镜像与 k8s 进行认证,那么这个就是在 k8s 中去创建好的, 也就是创建 k8s 登录 harbor 信息的一个认证,一般会在 yaml 中定义 imagePullSecrets,镜像拉取的认证
可以通过 kubectl create secret docker-registry registry-pull-secret –docker-server=192.168.30.27 –docker-username=admin –docker-password=Harbor12345 –docker-email=admin@ooo.com -n ms 命令去创建
def harbor_registry_auth = “e5402e52-7dd0-4daf-8d21-c4aa6e47736b” 也就是拉取镜像时需要 docker login 登录一下才可以拉,一般私有的需要指定这个密钥
def git_auth = “a65680b4-0bf7-418f-a77e-f20778f9e737” 这个是拉取 git 的时候需要指定的认证需要保存一下
// ConfigFileProvider ID
def k8s_auth = “7ee65e53-a559-4c52-8b88-c968a637051e” 这就是 k8s 的认证,这个也就是保存在 kubeconfig 中
这些都是定义的公共的变量,这些变量主要是让脚本适用于一个通用性,将一些变动的值传入进去这样主要可以让项目动态的去适配了
3、动态的在 k8s 中去创建 slave-pod
pipeline {
agent {
kubernetes {
label “jenkins-slave” 指定标签
yaml “””
kind: Pod
metadata:
name: jenkins-slave 指定 pod 的名字
spec:
containers:
-
name: jnlp 默认使用 jnlp
image: “${registry}/library/jenkins-slave-jdk:1.8” 使用我们封装好的 slave 的镜像
imagePullPolicy: Always 镜像拉取策略,始终拉取镜像仓库的镜像
volumeMounts: 挂载的数据卷,我们在构建 jenkins-slave 镜像的时候,docker 需要数据卷挂载- name: docker-cmd 也就是 docker in docker
mountPath: /usr/bin/docker - name: docker-sock
mountPath: /var/run/docker.sock -
name: maven-cache 主要是将容器中 maven 拉取依赖包的缓存挂载到宿主机的 /tmp/m2 下,一旦宿主机都具备这个缓存的话,
mountPath: /root/.m2 那么以后构建都会先读取缓存,或者也就是可以将这些包放到共享存储里面 pvc 中去读取,可以在这个 yaml
volumes: 中去定义也都可以- name: docker-cmd
hostPath:
path: /usr/bin/docker 将宿主机上的 /var/bin/docker 和 /var/run/docker.sock 挂载到容器中的目录 mountpath:对应目录中 - name: docker-sock 这样容器就可以使用 docker 命令了,
hostPath:
path: /var/run/docker.sock - name: maven-cache
hostPath:
path: /tmp/m2
“””
}
}
4、参数化构建
parameters {
gitParameter branch: ”, branchFilter: ‘.‘, defaultValue: ”, description: ‘ 选择发布的分支 ’, name: ‘Branch’, quickFilterEnabled: false, selectedValue: ‘NONE’, sortMode: ‘NONE’, tagFilter: ‘‘, type: ‘PT_BRANCH’ ### 动态的去获取参数
extendedChoice defaultValue: ‘none’, description: ‘ 选择发布的微服务 ’, \ ### 加了扩展,可以多选发布多个微服务
微服务找出我们需要哪些需要人工交互的
就是使用的这套微服务都适用于这套 chart 模版
1、微服务名称,以及针对一些服务需要带上域名,另外比如去配置的微服务的名字都是不一样的,这个名字是保证是唯一的,需要使用 include,,一般写在_helpers。tpl 下,因为我们部署的时候已经拿到微服务的名称了,所以 helm 起的名字也是微服务的名字,然后再加上公用的标签就区分出来了,另外就是微服务的端口也是不一样的
2、端口,每个微服务的端口也都不一
3、命名空间 使用 helm -n 就可以部署到指定的命名空间了
4、副本数 这个本来在 helm 中是 3 个副本,我们可以通过传参的形式变成 5 或者 2 都可以
5、资源的限制,本身这个 k8s 中的限制是无法满足一个 java 应用的限制的,一般 1.8jdk 版本是不兼容的,新的版本是兼容的,所以手动的去指定它的对内存的大小,这个一般在 dockerfile 启用 jar 包的时候带入
6、chart 模版的选择 可能一个项目满足不了一个项目,那么可能就得需要两个模版来实现
multiSelectDelimiter: ‘,’, name: ‘Service’, type: ‘PT_CHECKBOX’, \
value: ‘gateway-service:9999,portal-service:8080,product-service:8010,order-service:8020,stock-service:8030’
choice (choices: [‘ms’, ‘demo’], description: ‘ 部署模板 ’, name: ‘Template’)
choice (choices: [‘1’, ‘3’, ‘5’, ‘7’], description: ‘ 副本数 ’, name: ‘ReplicaCount’)
choice (choices: [‘ms’], description: ‘ 命名空间 ’, name: ‘Namespace’)
} - name: docker-cmd
- name: docker-cmd 也就是 docker in docker
然后需要将这个 chart 模版添加到 repo 里
[root@k8s-master1 ~]# helm repo add --username admin --password Harbor12345 myrepo http://192.168.30.27/chartrepo/library
"myrepo" has been added to your repositories
[root@k8s-master1 ~]# helm repo list
NAME URL
myrepo http://192.168.30.27/chartrepo/library
将 helm 制作完成后打包并 push 到仓库中,然后当我们部署的时候就去拉这个 helm 模版地址
[root@k8s-master1 ~]# helm push ms-0.1.0.tgz --username=admin --password=Harbor12345 http://192.168.30.27/chartrepo/library
Pushing ms-0.1.0.tgz to http://192.168.30.27/chartrepo/library...
Done.
5、jenkins-slave 所执行的具体任务
stages {stage('拉取代码'){
steps {checkout([$class: 'GitSCM',
branches: [[name: "${params.Branch}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [], submoduleCfg: [],
userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]] 它需要将这个参数传给上面的 git parameters, 让它能够动态的 git 地址中拉取所有的分支,])
}
}
stage('代码编译') {
// 编译指定服务
steps {sh """
mvn clean package -Dmaven.test.skip=true
"""
}
}
stage('构建镜像') {
steps {withCredentials([usernamePassword(credentialsId: "${harbor_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) { 这里使用了一个凭据的认证将连接 harbor 认证信息保存到凭据里面,为了安全性,使用了凭据的引用,动态的将它保存到变量中,然后通过调用变量的形式去登录这镜像仓库,这样的话就不用在 pipeline 中去体现密码了,sh """
docker login -u ${username} -p '${password}' ${registry}
for service in \$(echo ${Service} |sed 's/,/ /g'); do
service_name=\${service%:*} 因为我们是部署的微服务,所以我们需要很多的服务的构建,所以这里加了一个 for 循环
它调用的 $service 正是参数化构建中的选择的 services, 然后根据不同的服务推送到镜像仓库,image_name=${registry}/${project}/\${service_name}:${BUILD_NUMBER}
cd \${service_name}
if ls |grep biz &>/dev/null; then
cd \${service_name}-biz
fi
docker build -t \${image_name} .
docker push \${image_name}
cd ${WORKSPACE}
done
""" 之前说需要 kubeconfig 这个配置存到 jenins 中的 slave 的 pod 中,起个名字叫 admin.kubeconfig
configFileProvider([configFile(fileId: "${k8s_auth}", targetLocation: "admin.kubeconfig")]){sh """
# 添加镜像拉取认证 当使用拉取镜像的认证信息的时候就可以直接指定 admin.kubeconfig 了,它就能连接到这个集群了
kubectl create secret docker-registry ${image_pull_secret} --docker-username=${username} --docker-password=${password} --docker-server=${registry} -n ${Namespace} --kubeconfig admin.kubeconfig |true
# 添加私有 chart 仓库到这个 pod 中
helm repo add --username ${username} --password ${password} myrepo http://${registry}/chartrepo/${project}
"""
}
}
}
}
------------------------------------------------------------------------------------------------------------------------------------------
6、deploy,使用 helm 部署到 k8s 中
stage('Helm 部署到 K8S') {
steps {sh """ 定义公共的参数,使用 helm,kubectl 都要加 namespace 命名空间,连接 k8s 认证的 kubeconfig 文件
common_args="-n ${Namespace} --kubeconfig admin.kubeconfig"
for service in \$(echo ${Service} |sed 's/,/ /g'); do for 循环每个微服务的端口都不一样,所以在微服务这里添加微服务的名字和它对应的端口,把选择的服务和端口进行拆分
service_name=\${service%:*}
service_port=\${service#*:}
image=${registry}/${project}/\${service_name}
tag=${BUILD_NUMBER}。jenkins 构建的一个编号
helm_args="\${service_name} --set image.repository=\${image} --set image.tag=\${tag} --set replicaCount=${replicaCount} --set imagePullSecrets[0].name=${image_pull_secret} --set service.targetPort=\${service_port} myrepo/${Template}"
# 判断是否为新部署
if helm history \${service_name} \${common_args} &>/dev/null;then 那么加一个判断看看是不是部署了,为假就 install,为真就 upgrade
action=upgrade 旧部署的使用 upgrade 更新
else
action=install 新部署的使用 install
fi
# 针对服务启用 ingress
if [\${service_name} == "gateway-service" ]; then
helm \${action} \${helm_args} \
--set ingress.enabled=true \ 为 true 就启用 ingress,因为 chart 肯定默认的为 force,就是不启用 ingress
--set ingress.host=${gateway_domain_name} \
\${common_args}
elif [\${service_name} == "portal-service" ]; then
helm \${action} \${helm_args} \
--set ingress.enabled=true \
--set ingress.host=${portal_domain_name} \
\${common_args}
else
helm \${action} \${helm_args} \${common_args}
fi
done
# 查看 Pod 状态
sleep 10
kubectl get pods \${common_args}
"""
}
}
}
}
修改一些认证信息,这个 pipeline 最好先从一个地方复制一下,修改完再放进 pipeline
gitlab 的项目地址 def git_url = "http://192.168.30.28/root/ms.git"
修改 harbor 的凭据
选择 credentials
点击 jenkins
add 添加凭据
填写 harbor 的用户名和密码,密码 Harbor12345
描述随便写,
再添加第二个
git 的用户名和密码
然后更新一下,把密钥放到指定的 pipeline 中
将这个 id 放到 pipeline 中
将生成的密钥认证放到 pipeline 中
// 认证
def image_pull_secret = “registry-pull-secret”
def harbor_registry_auth = “7177c1f3-9e6b-435c-b4cc-187c742c4516”
def git_auth = “28484aa2-aeb4-479b-ad43-cf12c2a7d445”
然后将我们需要的插件都安装上
// 所需插件: Git Parameter/Git/Pipeline/Config File Provider/kubernetes/Extended Choice Parameter
现在去添加 kubeconfig 的文件
将这个 ID 放到我们 k8s-auth 的 pipeline 中,这个配置文件是 k8s 连接 kubeconfig 的 ID,如果是 kubeadm 部署的需要到[root@k8s-master ~]# cat /root/.kube/config 这个文件下将文件拷贝到 jenkins 中
apiVersion: v1
clusters:
- cluster:
certificate-authority-data:
如果是二进制部署的需要到 k8s 的证书下面找到 kubeconfig.sh 下面手动进行生成,bash kubeconfig.sh,会生成一个 admin 客户端的连接 k8s 配置,将这个配置粘贴到 jenkins 的刚才生成 config 的 content 下面
最后进行测试发布在 pipeline 的配置指定发布的服务进行发布
查看 pod 的状态
: