7天將項(xiàng)目從SpringCloud改造成K8S
之前項(xiàng)目使用的是springcloud,主要使用到的組件有 spring gateway, nacos, minio, load balancer,open-feign等,然后我們的微服務(wù)通過(guò)docker部署到虛擬機(jī)里面的。
但是出于安全的考慮,需要將它遷移到azure的aks(kubernetes)中,所以需要將spring cloud改造成spring boot。這樣就不用自己維護(hù)虛擬機(jī)的安全策略,也不需要去關(guān)注補(bǔ)丁了。
梳理項(xiàng)目結(jié)構(gòu)
項(xiàng)目是一個(gè)一個(gè)微服務(wù)組織起來(lái)的,大概業(yè)務(wù)類的服務(wù)有5個(gè),公共服務(wù)有4個(gè)。 設(shè)計(jì)到的改造主要集中在gateway, auth中,公共包的一些改造比較少,主要是將open-feign的訪問(wèn)改為通過(guò)url進(jìn)行調(diào)用,而不是之前通過(guò)服務(wù)名來(lái)。
而在kubernetes中,我們使用Traefik2來(lái)代替gateway的功能,不知道traefik2的,可以去翻翻之前的文章。
同時(shí)對(duì)于授權(quán),需要提供一個(gè)授權(quán)接口,配合traefik2使用,這樣每一個(gè)請(qǐng)求都會(huì)進(jìn)行授權(quán)的驗(yàn)證。
開始改造
確定分支
最開始肯定是新拉一個(gè)分支進(jìn)行這些改動(dòng),即便沒(méi)改好也不影響其他人,所以我們先把分支名定好就叫做 feature/AKS-migrate。
改造gateway
首先把pom文件中的不需要的依賴包注釋掉,比如spring cloud gateay, nacos, sentinel等spring cloud相關(guān)組件。注釋掉之后 去看代碼中有哪些報(bào)錯(cuò),就針對(duì)性的修改。
我們的項(xiàng)目中使用蠻多gateway的filter以及handler,最開始我看他們都是使用的webflux,我就想我單獨(dú)引入這個(gè)包,代碼是不是最小的改動(dòng)就行了呢?
這樣嘗試過(guò)之后我發(fā)現(xiàn)不行,因?yàn)轫?xiàng)目中使用最多還是@RestController,如果使用webflux的方式,那么很多filter不生效的。
所以這種方式也不行,但是代碼改得太多了,我只好回退到注釋pom文件依賴的那一步。
沒(méi)辦法,只能讀之前對(duì)應(yīng)的代碼邏輯,然后將其轉(zhuǎn)換了。
讀取gateway filter的代碼,將其轉(zhuǎn)換成spring filter,直接繼承 org.springframework.web.filter.OncePerRequestFilter 即可,然后將之前的邏輯搬過(guò)來(lái)。
需要注意的是如果是全局filter需要放到公共包里面。
handler也是一樣的,將其轉(zhuǎn)換成filter,需要注意執(zhí)行順序。
這樣,核心代碼改造完畢,可以開啟調(diào)試了
遇到的坑
處理上面說(shuō)的webflux的問(wèn)題外,將springcloud變成springboot后,我們之前的配置文件名稱是bootstrap.yml,bootstrap-dev.yml文件,但是改成了springboot后,配置文件名要改成application.yml,application-env.yml。
不然你會(huì)發(fā)現(xiàn)你啟動(dòng)不了,說(shuō)找不到文件,這個(gè)坑也是自己把自己給坑了。
然后就是要將gateway的filter轉(zhuǎn)換成spring filter的時(shí)候要注意一定要保證之前的邏輯完全移植過(guò)來(lái)了,我就遇到一個(gè)在改造前可以重復(fù)讀取request的流,但是改造后這段代碼報(bào)錯(cuò)了,就是因?yàn)闆](méi)有將這段邏輯移植過(guò)去的問(wèn)題。
改造nacos
前面提到了nacos主要在open-feign的調(diào)用中以及變量注入中使用到了。feign那個(gè)好改,只需要指定url參數(shù)即可,這樣就可以去掉nacos的依賴了。 然后變量注入同樣的我們可以使用Kubernetes的ConfigMap以及Secret來(lái)代替。
所以我們需要將以前配置到nacos中的變量放到配置文件中,這樣變量可以直接通過(guò)Kubernetes進(jìn)行注入了。
我們?cè)诟鱾€(gè)環(huán)境中只需要有一份代碼(一個(gè)鏡像),部署的時(shí)候只需要注入的配置不一樣就可以了,這樣就可以保證各個(gè)環(huán)境代碼一致。
比如之前的配置是這樣的
spring:redis:host: 127.0.0.1port: 6379database: 0password: 123456
改造后配置文件的值為
spring:redis:host: ${env_redis_host}port: ${env_redis_port}database: ${env_redis_database}password: ${env_redis_password}
而這里的變量是通過(guò)ConfigMap進(jìn)行配置的,到時(shí)候會(huì)注入到容器環(huán)境變量中,這樣spring就可以從環(huán)境變量中獲取到值了。
部署
之前使用的jenkins方式部署,jenkins也是自己搭建的,現(xiàn)在全部遷移到了azure github上,所以這里直接使用azure的pipeline進(jìn)行部署。 而我們管理k8s的資源則使用的是helm。
比如我項(xiàng)目中使用helm生成后結(jié)構(gòu)如下
C:.│ .helmignore│ Chart.yaml│ values-prod.yaml│ values-qa.yaml│ values-test.yaml│ values.yaml│├─charts├─config│ ├─dev│ │ config.yaml│ │ secret.yaml│ ││ ├─prod│ │ config.yaml│ │ secret.yaml│ ││ ├─qa│ │ config.yaml│ │ secret.yaml│ ││ └─test│ config.yaml│ secret.yaml│└─templatesconfigmap.yamldeployment.yamlhpa.yamlsecret.yamlservice.yaml_helpers.tpl
這里只需要部署的時(shí)候指定不同的value 文件,就可以實(shí)現(xiàn)同一個(gè)鏡像部署到不同的環(huán)境了。
dev目錄下config.yaml,secret.yaml文件內(nèi)容大致如下:
# config.yamlenv_redis_host: localhostenv_redis_port: 6379env_redis_database: 1#secret.yamlenv_redis_password: 123456
在template中configmap.yaml, secret.yaml中主要是如何將文件內(nèi)容轉(zhuǎn)換成對(duì)應(yīng)的yaml
#values.yaml 指定有哪些文件configOverrides:- config/dev/config.yamlsecretOverrides:- config/dev/secret.yaml# configmap.yamlapiVersion: v1kind: ConfigMapmetadata:name: {{ include "think-manifesto.fullname" . }}-configmapnamespace: {{ .Values.nameSpace }}data:{{- $files := .Files }}{{- range .Values.configOverrides }}{{- range $key, $value := ($files.Get (printf "%s" .) | fromYaml) }}{{ $key | indent 2 }}: {{ $value | quote }}{{- end }}{{- end }}# secret.yamlapiVersion: v1kind: Secretmetadata:name: {{ include "think-manifesto.fullname" . }}-secretnamespace: {{ .Values.nameSpace }}type: Opaquedata:{{- $files := .Files }}{{- range .Values.secretOverrides }}{{- range $key, $value := ($files.Get (printf "%s" .) | fromYaml) }}{{ $key | indent 2 }}: {{ $value | b64enc }}{{- end }}{{- end }}
最后給我deployment.yaml的例子,大家可以參考下
apiVersion: apps/v1kind: Deploymentmetadata:name: {{ include "think-manifesto.fullname" . }}namespace: {{ .Values.nameSpace }}labels:{{- include "think-manifesto.labels" . | nindent 4 }}spec:replicas: {{ .Values.replicaCount }}selector:matchLabels:{{- include "think-manifesto.selectorLabels" . | nindent 6 }}template:metadata:labels:{{- include "think-manifesto.selectorLabels" . | nindent 8 }}annotations:{{- if .Values.configOverrides}}checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}{{- end }}{{- if .Values.secretOverrides}}checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}{{- end }}spec:{{- with .Values.imagePullSecrets }}imagePullSecrets:{{- toYaml . | nindent 8 }}{{- end }}containers:- name: {{ .Chart.Name }}image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"imagePullPolicy: {{ .Values.image.pullPolicy }}ports:- name: {{ .Values.service.portName }}containerPort: {{ .Values.service.port }}envFrom:{{- if .Values.configOverrides }}- configMapRef:name: {{ include "think-manifesto.fullname" . }}-configmap{{- end }}{{- if .Values.secretOverrides }}- secretRef:name: {{ include "think-manifesto.fullname" . }}-secret{{- end }}{{- with .Values.livenessProbe }}livenessProbe:{{- toYaml . | nindent 12 }}{{- end }}{{- with .Values.readinessProbe }}readinessProbe:{{- toYaml . | nindent 12 }}{{- end }}resources:{{- toYaml .Values.resources | nindent 12 }}{{- with .Values.nodeSelector }}nodeSelector:{{- toYaml . | nindent 8 }}{{- end }}{{- with .Values.affinity }}affinity:{{- toYaml . | nindent 8 }}{{- end }}{{- with .Values.tolerations }}tolerations:{{- toYaml . | nindent 8 }}{{- end }}
至于values.yaml我就不給出來(lái)了,基本上是其他模板需要什么就寫在上面就好了。
和Kustomize相比,helm安裝第三方chart很方便,它有自己的倉(cāng)庫(kù),這里附上我安裝traefik2的命令
# 添加traefiK倉(cāng)庫(kù)helm repo add traefik https://traefik.github.io/charts#添加國(guó)內(nèi)倉(cāng)庫(kù)helm repo add stable http://mirror.azure.cn/kubernetes/chartshelm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/chartshelm repo updatehelm repo listhelm install --set deployment.kind=DaemonSet --set namespaceOverride=traefik --set service.enabled=false traefik traefik/traefik
本地驗(yàn)證
當(dāng)你寫好了上面的chart之后,如果你本地沒(méi)有kubernetes環(huán)境(因?yàn)樗赡茉诜?wù)器才存在),而你又想要在本地進(jìn)行驗(yàn)證你寫得是否有問(wèn)題,那么可以使用下面的命令。
// 將下面的變量替換成你自己的。 chart-name表示chart的名字,chart-dir表示chart地址helm template --dry-run --debug --disable-openapi-validation ${chart-name} .\${chart-dir}\
然后如果你想要在k8s環(huán)境中安裝的時(shí)候,而k8s環(huán)境又在遠(yuǎn)端服務(wù)器,那么你可以將chart打包,然后到服務(wù)器中進(jìn)行安裝,然后也可以在 將chart上傳到服務(wù)器中,然后進(jìn)行安裝(服務(wù)器中要先安裝helm)。
寫到最后
自此,從springcloud遷移到k8s集群總算是完成了。因?yàn)槭堑谝淮问褂胔elm(以前都用的kustomize),所以在helm這里耗費(fèi)了一些功夫,主要是排查錯(cuò)誤方面的,不過(guò)不得不說(shuō)helm的文檔寫得不錯(cuò),很清晰。
再然后就是代碼改造以及一些配置問(wèn)題,因?yàn)檫w移azure,所以上面的關(guān)于它pipeline的一些配置不是很清楚,不過(guò)好在可以直接練習(xí)他們的運(yùn)維,還是幫我們解決了一些問(wèn)題的。
