老大給我7天讓我將項目從SpringCloud改造到K8S

之前項目使用的是springcloud,主要使用到的組件有 spring gateway, nacos, minio, load balancer,open-feign等,然后我們的微服務通過docker部署到虛擬機里面的。

但是出于安全的考慮,需要將它遷移到azure的aks(kubernetes)中,所以需要將spring cloud改造成spring boot。這樣就不用自己維護虛擬機的安全策略,也不需要去關注補丁了。

梳理項目結構

項目是一個一個微服務組織起來的,大概業(yè)務類的服務有5個,公共服務有4個。 設計到的改造主要集中在gateway, auth中,公共包的一些改造比較少,主要是將open-feign的訪問改為通過url進行調(diào)用,而不是之前通過服務名來。

而在kubernetes中,我們使用Traefik2來代替gateway的功能,不知道traefik2的,可以去翻翻之前的文章。

同時對于授權,需要提供一個授權接口,配合traefik2使用,這樣每一個請求都會進行授權的驗證。

開始改造

確定分支

最開始肯定是新拉一個分支進行這些改動,即便沒改好也不影響其他人,所以我們先把分支名定好就叫做 feature/AKS-migrate。

改造gateway

首先把pom文件中的不需要的依賴包注釋掉,比如spring cloud gateay, nacos, sentinel等spring cloud相關組件。注釋掉之后
去看代碼中有哪些報錯,就針對性的修改。

我們的項目中使用蠻多gateway的filter以及handler,最開始我看他們都是使用的webflux,我就想我單獨引入這個包,代碼是不是最小的改動就行了呢?

這樣嘗試過之后我發(fā)現(xiàn)不行,因為項目中使用最多還是@RestController,如果使用webflux的方式,那么很多filter不生效的。

所以這種方式也不行,但是代碼改得太多了,我只好回退到注釋pom文件依賴的那一步。

沒辦法,只能讀之前對應的代碼邏輯,然后將其轉換了。

讀取gateway filter的代碼,將其轉換成spring filter,直接繼承 org.springframework.web.filter.OncePerRequestFilter 即可,然后將之前的邏輯搬過來。

需要注意的是如果是全局filter需要放到公共包里面。

handler也是一樣的,將其轉換成filter,需要注意執(zhí)行順序。

這樣,核心代碼改造完畢,可以開啟調(diào)試了

遇到的坑

處理上面說的webflux的問題外,將springcloud變成springboot后,我們之前的配置文件名稱是bootstrap.yml,bootstrap-dev.yml文件,但是改成了springboot后,配置文件名要改成application.yml,application-env.yml。

不然你會發(fā)現(xiàn)你啟動不了,說找不到文件,這個坑也是自己把自己給坑了。

然后就是要將gateway的filter轉換成spring filter的時候要注意一定要保證之前的邏輯完全移植過來了,我就遇到一個在改造前可以重復讀取request的流,但是改造后這段代碼報錯了,就是因為沒有將這段邏輯移植過去的問題。

改造nacos

前面提到了nacos主要在open-feign的調(diào)用中以及變量注入中使用到了。feign那個好改,只需要指定url參數(shù)即可,這樣就可以去掉nacos的依賴了。 然后變量注入同樣的我們可以使用Kubernetes的ConfigMap以及Secret來代替。

所以我們需要將以前配置到nacos中的變量放到配置文件中,這樣變量可以直接通過Kubernetes進行注入了。

我們在各個環(huán)境中只需要有一份代碼(一個鏡像),部署的時候只需要注入的配置不一樣就可以了,這樣就可以保證各個環(huán)境代碼一致。

比如之前的配置是這樣的

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
    password: 123456

改造后配置文件的值為

spring:
  redis:
    host: ${env_redis_host}
    port: ${env_redis_port}
    database: ${env_redis_database}
    password: ${env_redis_password}

而這里的變量是通過ConfigMap進行配置的,到時候會注入到容器環(huán)境變量中,這樣spring就可以從環(huán)境變量中獲取到值了。

部署

之前使用的jenkins方式部署,jenkins也是自己搭建的,現(xiàn)在全部遷移到了azure github上,所以這里直接使用azure的pipeline進行部署。 而我們管理k8s的資源則使用的是helm。

比如我項目中使用helm生成后結構如下

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
│                   
└─templates         
        configmap.yaml
        deployment.yaml
        hpa.yaml    
        secret.yaml 
        service.yaml
        _helpers.tpl

這里只需要部署的時候指定不同的value 文件,就可以實現(xiàn)同一個鏡像部署到不同的環(huán)境了。

dev目錄下config.yaml,secret.yaml文件內(nèi)容大致如下:

# config.yaml
env_redis_host: localhost
env_redis_port: 6379
env_redis_database: 1


#secret.yaml

env_redis_password: 123456

在template中configmap.yaml, secret.yaml中主要是如何將文件內(nèi)容轉換成對應的yaml

#values.yaml 指定有哪些文件
configOverrides:
  - config/dev/config.yaml
secretOverrides:
  - config/dev/secret.yaml



# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "think-manifesto.fullname" . }}-configmap
  namespace: {{ .Values.nameSpace }}
data:
{{- $files := .Files }}
{{- range  .Values.configOverrides }}
{{- range $key, $value :=  ($files.Get (printf "%s" .) | fromYaml) }}
{{ $key | indent 2 }}: {{ $value | quote }}
{{- end }}
{{- end }}

# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: {{ include "think-manifesto.fullname" . }}-secret
  namespace: {{ .Values.nameSpace }}
type: Opaque
data:
{{- $files := .Files }}
{{- range  .Values.secretOverrides }}
{{- range $key, $value :=  ($files.Get (printf "%s" .) | fromYaml) }}
{{ $key | indent 2 }}: {{ $value | b64enc }}
{{- end }}
{{- end }}


最后給我deployment.yaml的例子,大家可以參考下

apiVersion: apps/v1
kind: Deployment
metadata:
  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我就不給出來了,基本上是其他模板需要什么就寫在上面就好了。

和Kustomize相比,helm安裝第三方chart很方便,它有自己的倉庫,這里附上我安裝traefik2的命令

# 添加traefiK倉庫
helm repo add traefik   https://traefik.github.io/charts    
#添加國內(nèi)倉庫       
helm repo add stable http://mirror.azure.cn/kubernetes/charts       
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts       
helm repo update       
helm repo list

helm install --set deployment.kind=DaemonSet --set namespaceOverride=traefik --set service.enabled=false traefik traefik/traefik

本地驗證

當你寫好了上面的chart之后,如果你本地沒有kubernetes環(huán)境(因為它可能在服務器才存在),而你又想要在本地進行驗證你寫得是否有問題,那么可以使用下面的命令。

// 將下面的變量替換成你自己的。 chart-name表示chart的名字,chart-dir表示chart地址
helm template --dry-run --debug --disable-openapi-validation ${chart-name} .\${chart-dir}\

然后如果你想要在k8s環(huán)境中安裝的時候,而k8s環(huán)境又在遠端服務器,那么你可以將chart打包,然后到服務器中進行安裝,然后也可以在
將chart上傳到服務器中,然后進行安裝(服務器中要先安裝helm)。

寫到最后

自此,從springcloud遷移到k8s集群總算是完成了。因為是第一次使用helm(以前都用的kustomize),所以在helm這里耗費了一些功夫,主要是排查錯誤方面的,不過不得不說helm的文檔寫得不錯,很清晰。

再然后就是代碼改造以及一些配置問題,因為遷移azure,所以上面的關于它pipeline的一些配置不是很清楚,不過好在可以直接練習他們的運維,還是幫我們解決了一些問題的。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容