通過jenkins交付微服務到kubernetes

隨着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交付微服務到kubernetes
一、傳統的發佈流程是怎麼樣的?
現在的這個發佈流程設計還是要和自己的項目中去考量,佈置一個大體的拓撲圖,那麼比如沒有這樣的場景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 [email protected]: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功能
通過jenkins交付微服務到kubernetes
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.

通過jenkins交付微服務到kubernetes
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地址:[email protected]: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,首次登陸需要手動設置密碼

通過jenkins交付微服務到kubernetes
通過jenkins交付微服務到kubernetes
將代碼推送到gitlab中
創建ms的倉庫名稱
通過jenkins交付微服務到kubernetes
創建完需要我們將我們的本地的代碼上傳到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://[email protected]': 
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://[email protected]': 
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中

通過jenkins交付微服務到kubernetes
另外就是如果其他的開發人員需要將代碼提交到gitlab上的話,那麼我們就需要給他去創建一個用戶,設置一定的權限,然後制定好密碼,告訴他,它使用自己的賬號去管理自己的項目代碼
通過jenkins交付微服務到kubernetes
另外就是在使用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 * [email protected]:~

這裏呢之前遇到一個問題當部署gitlab之後如果是使用的docker去部署的gitlab默認的22端口改成2222端口之後當用其他服務器進行scp或者ssh登錄的時候就會顯示拒接

[root@k8s-master db]# scp order.sql [email protected]:/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 [email protected] -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
通過jenkins交付微服務到kubernetes
三、在Kubernetes中部署jenkins
通過jenkins交付微服務到kubernetes
在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 [email protected]: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

通過jenkins交付微服務到kubernetes
一般最好進去的時候不安裝插件,第一默認使用的國外的源,下載插件的時候很慢,我們需要做做一些優化
通過jenkins交付微服務到kubernetes
通過jenkins交付微服務到kubernetes

先創建一個test的pipeline的語法流水線,熟悉一下怎麼發佈任務
通過jenkins交付微服務到kubernetes
在這裏就可以看到語法的格式,這裏分爲兩種,以pipeline開頭的也叫聲明式語法,主要遵循的Groovy的相同語法來實現的pipeline {}
這個也是比較主流的方式。
通過jenkins交付微服務到kubernetes
測試一個hello的語法,默認提供的pipeline,進行使用
通過jenkins交付微服務到kubernetes
直接build,這個就能構建
通過jenkins交付微服務到kubernetes
當我們構建完成後,這裏會顯示一個執行的步驟顯示一個具體的內容,可以看到jenkins的工作目錄,默認傳統部署jenkins的目錄爲/root/.jenkins下,而作爲在k8s部署jenkins需要考慮數據的持久化了,因爲pod遇到不確定的因素進行重啓之後,那麼這個pod的數據就會丟失,所以針對這個問題,我們就需要將這個pod的工作目錄掛載到持久捲上,這樣的話,即使pod重啓飄移到其他的節點也能讀取到相應的數據了。
通過jenkins交付微服務到kubernetes
可以看到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交付微服務到kubernetes
目錄也持久化了,看到新增的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交付微服務到kubernetes
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支持參數化構建。
通過jenkins交付微服務到kubernetes
這裏也就是pipeline一些的語法,比如選擇parameters參數化構建,選擇框式參數choice parameter,比如給它幾個值,然後讓它動態的去輸入,或者憑據參數credentials parameter,或者文件參數file parameter,或者密碼參數password parameter,或者運行參數 run parameter,或者字符串參數 string paramether ,或者多行字符串參數multi-line string parameter,或者布爾型參數boolean parameter

比如選擇choice parameter,選擇型參數
通過jenkins交付微服務到kubernetes
比如發佈的git地址不一樣,那麼就需要多個地址可以去選擇,需要使用choice parameter選擇框型參數,這個可以體現在構建的頁面,也可以體現在configure配置的頁面,這樣配置也比較麻煩,所以直接在configure裏面直接添加對應的參數就可以
先配置一個看一下效果
通過jenkins交付微服務到kubernetes
將生成的選擇型參數添加到指定的pipeline的語法中,save一下
通過jenkins交付微服務到kubernetes
再回到項目中可以看到build名字從build now換成了build with parameters,也就是增加了幾行的的配置,可以進行選擇的去拉去哪個分支下的代碼了
通過jenkins交付微服務到kubernetes
也可以在configure去配置,這兩個地方都可以添加,要是再添加一個直接add parameter,可以選擇多種類型的參數幫助我們去構建這個多樣式的需求
通過jenkins交付微服務到kubernetes
再比如分支這一塊,可能每次打的分支都不同,這個不是固定的,所以需要一個git的參數化構建,那麼這個就需要動態的去從選擇的git地址獲取到當前的所有的分支
還有一個分佈的機器不同,這個也可以使用剛纔的choice parameter,將多個主機的ip也進去

通過jenkins交付微服務到kubernetes
將這個生產的語法,複製到pipeline語法中
choice choices: ['10.4.7.12', '10.4.7.21', '10.4.7.22'], description: '發佈哪臺node上', name: 'host'
通過jenkins交付微服務到kubernetes
構建一下,發現也可以使用選擇型參數了,類似這個的人工交互就可以選擇多個參數了,可以寫一個通用的模版,就處理人工交互的邏輯
通過jenkins交付微服務到kubernetes
現在我們可以去人工選擇了,這裏面的值怎麼獲取到,我們處理不同的項目,必須在這裏面去實現,比如選擇這個git之後,拉取這個代碼編譯構建,這些可能都是一些相同點,不同點發佈的機器不一樣,所以要選擇用戶是拿到的哪個git地址,發佈的哪個機器,在腳本里去拿到,其實默認這個name就是一個變量,jenkins已經將這個賦予變量,並且pipeline可以直接獲取這個變量名,就是剛纔定義的git,host這個名字,那麼我們從剛纔設置的parameters裏去測試這個變量可不可以拿到,拿到的話說明這個就很好去處理
通過jenkins交付微服務到kubernetes
構建一下,現在構建成功後已經是構建成功了,也已經獲取到剛纔我們的git這個參數下的值了
通過jenkins交付微服務到kubernetes

有了這些方式,就可以將這些不同點通過裏面的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交付微服務到kubernetes
也就是提前創建好幾個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去處理
通過jenkins交付微服務到kubernetes
這個的話也就是可以預先起一些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
通過jenkins交付微服務到kubernetes
這裏連接k8s直接使用service的地址就可以,因爲我們部署了coredns直接可以就要解析到這個k8s,點擊連接測試可以連接
通過jenkins交付微服務到kubernetes
如果jenkins是在外部去部署的,也就是單獨哪臺服務器去部署的,走的傳統部署,也就是要連接k8s對外的地址也就是https://10.4.7.11:6443這個端口,並且將它的CA添加進來添加一個憑據,這樣才能連接成功
通過jenkins交付微服務到kubernetes
並且還有將jenkins的地址也寫上,http://jenkins.default,或者寫成連接ip的地址也可以
通過jenkins交付微服務到kubernetes
現在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交付微服務到kubernetes

現在就可以去測試能不能動態的去創建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交付微服務到kubernetes
現在可以在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微服務
通過jenkins交付微服務到kubernetes
現在編寫這個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 [email protected] -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')
      }

然後需要將這個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.

通過jenkins交付微服務到kubernetes

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交付微服務到kubernetes
點擊jenkins
通過jenkins交付微服務到kubernetes
add 添加憑據
通過jenkins交付微服務到kubernetes
填寫harbor的用戶名和密碼,密碼Harbor12345
描述隨便寫,
通過jenkins交付微服務到kubernetes
再添加第二個
通過jenkins交付微服務到kubernetes
git的用戶名和密碼
通過jenkins交付微服務到kubernetes
然後更新一下,把密鑰放到指定的pipeline中
通過jenkins交付微服務到kubernetes
將這個id放到pipeline中
通過jenkins交付微服務到kubernetes
將生成的密鑰認證放到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的文件
通過jenkins交付微服務到kubernetes
通過jenkins交付微服務到kubernetes
將這個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下面
    通過jenkins交付微服務到kubernetes

最後進行測試發佈在pipeline的配置指定發佈的服務進行發佈
查看pod的狀態

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章