通過skaffold快速部署微服務(wù)

通過skaffold快速部署微服務(wù)

隨著技術(shù)的不斷發(fā)展捧韵,程序員們熟悉的傳統(tǒng)單體應(yīng)用開發(fā)流程都许,漸漸地?zé)o法適應(yīng)當(dāng)下微服務(wù)化的潮流趨勢陌僵。同時隨著云原生開發(fā)的理念不斷推廣惨撇,越來越多的服務(wù)運行在不可變的基礎(chǔ)設(shè)施之上胎撤,隨之而來的是傳統(tǒng)單體應(yīng)用開發(fā)流程與云化程度日益加深服務(wù)之間的隔閡越發(fā)巨大晓殊,開發(fā)人員越來越難以容忍重復(fù)繁瑣且容易出錯的低效率開發(fā)流程。因此伤提,一款面向開發(fā)人員而運維實施人員的持續(xù)構(gòu)建與持續(xù)部署工具 skaffold 應(yīng)運而生

skaffold簡介

skaffold 是一款 Google 推出的持續(xù)構(gòu)建與持續(xù)部署工具巫俺,它主要面向開發(fā)人員而非運維實施人員,目標(biāo)是打破本地開發(fā)與云化部署之間的隔閡肿男,減輕開發(fā)人員的心智負(fù)擔(dān)介汹,幫助開發(fā)人員專注于出色地完成日常開發(fā)工作,避免開發(fā)人員在紛亂繁雜的運維流程中過多消耗寶貴的精力與時間舶沛。

基本架構(gòu)

architecture.png

skaffold 的工作流按照開發(fā)流程的不同階段嘹承,分為4個部分組成:

  1. 本地開發(fā)(文件同步)
  2. 持續(xù)構(gòu)建
  3. 持續(xù)測試
  4. 持續(xù)部署

以上四個部分均可以根據(jù)實際需求進行定制化修改。

本地開發(fā)

skaffold 對主流的編程語言以及配套使用的技術(shù)棧都有著非常不錯的支持如庭,例如 Go 叹卷、JavaJavaScript

本地開發(fā)的核心內(nèi)容是 文件同步坪它,文件同步的監(jiān)聽對象大概可以分為 源代碼編譯產(chǎn)物 骤竹。

skaffold 官方的推薦做法是監(jiān)聽源代碼變動,然后自動化把源代碼復(fù)制到Docker容器中進行編譯和構(gòu)建往毡。

這種做法的問題不少蒙揣,首先是源代碼變動非常頻繁,而編譯和構(gòu)建過程往往非常耗時开瞭,因此自動觸發(fā)構(gòu)建不太合理懒震。

其次罩息,在Docker容器中編譯和構(gòu)建,需要掌握編寫 Multi Stage Dockerfile 技能挎狸,否則構(gòu)建出來的鏡像大小會占據(jù)非常大的空間扣汪,另外還要消耗本就不寬裕的帶寬進行鏡像傳輸断楷。

最后锨匆,在Docker容器中編譯和構(gòu)建,要解決環(huán)境變量冬筒,代理設(shè)置恐锣、緩存構(gòu)建中間結(jié)果等一系列問題,對新手非常不友好舞痰。

因此土榴,個人推薦,在本地開發(fā)環(huán)節(jié)盡量采用手動觸發(fā)編譯構(gòu)建响牛,通過監(jiān)聽編譯產(chǎn)物的方式來觸發(fā)熱更新等流程玷禽。

持續(xù)構(gòu)建

因為選擇手動觸發(fā)編譯,所以本環(huán)節(jié)的內(nèi)容主要講述如何打包鏡像的內(nèi)容

目前 skaffold 官方支持的構(gòu)建方式有三種:Docker呀打、Jib(maven/gradle)矢赁、Bazel

  • Docker
  • Jib(maven/gradle)
  • Bazel

這里以最常見 Docker 為例:

build:
  local:
    push: false                        # 鏡像打包成功后是否推送到遠(yuǎn)端的鏡像倉庫
  artifacts:                           # 支持打包多個不同組件的鏡像
    - image: datacenter-eureka         # 打包后的鏡像名稱
      context: "eureka"                # Dockerfile相對路徑,就放在eureka目錄下
      docker:
        dockerfile: Dockerfile
    - image: datacenter-school         # 打包后的鏡像名稱
      context: "school"                # Dockerfile相對路徑
      docker:
        dockerfile: Dockerfile
    - image: datacenter-teacher        # 打包后的鏡像名稱
      context: "teacher"               # Dockerfile相對路徑
      docker:
        dockerfile: Dockerfile
    - image: datacenter-student        # 打包后的鏡像名稱
      context: "student"               # Dockerfile相對路徑
      docker:
        dockerfile: Dockerfile

當(dāng)運行 skaffold dev 時贬丛,會按照 編譯 —> 構(gòu)建 -> 測試 -> 部署 的標(biāo)準(zhǔn)流程走一遍撩银。
當(dāng)監(jiān)聽到指定路徑下的文件發(fā)生變化時,skaffold工具會嘗試通過類似于 kubectl cp 命令的方式豺憔,直接把產(chǎn)生變化后的文件拷貝到運行中的容器內(nèi)部额获,避免重新走一遍編譯構(gòu)建/上傳鏡像的步驟,減少同步代碼更改而消耗的時間恭应。

需要特別注意抄邀,這種方式對于支持 代碼熱更新 的技術(shù)棧非常實用,例如 JavaJavascript昼榛,但對于 Go 這類不支持 熱更新 的技術(shù)棧來說效果十分有限境肾,因為即便文件同步完成,依然重啟主進程才能讓修改后的功能生效褒纲。

接著說 Jib(maven/gradle) 准夷,Jib 也是由谷歌開發(fā)的一款專門針對 Java 生態(tài)的 CI/CD 工具,跟 skaffold 通用 CI/CD 不同莺掠,同時還有 VSCodeIDEA 插件衫嵌, 但作者本人并沒有用過,所以等以后有機會再展開講彻秆。

至于 Bazel 是微軟開發(fā)的全平臺構(gòu)建工具楔绞,主要支持 C# 語言结闸,甚至連前端相關(guān) Javascript 項目也可以使用,但缺點就是非常笨重酒朵,這里也不展開講桦锄。

此外, skaffold 還支持 Customize 自定義構(gòu)建蔫耽,這個方式的構(gòu)建更自由可控结耀,比如有些 WSL 環(huán)境的用戶不愿意為了安裝 Docker 環(huán)境而反復(fù)折騰,甚至有些企業(yè)內(nèi)部不允許員工在開發(fā)電腦安裝虛擬機等等匙铡,通過自定義構(gòu)建流程都可以解決图甜,放到文章最后再詳細(xì)講解。

持續(xù)測試

skaffold 官方的測試方案是把代碼拷貝到定制化的測試環(huán)境容器中執(zhí)行測試用例鳖眼,這種方法非常麻煩黑毅,測試相關(guān)的內(nèi)容這里就不展開講。
感興趣的讀者可以查看 skaffold官方配置文檔

持續(xù)部署

skaffold 官方支持的部署方式有很多種钦讳,這里以 helm 為例:

deploy:
  helm:
    releases:
    - name: datacenter
      chartPath: package
      artifactOverrides:
        image:
          eureka: datacenter-eureka      # 鏡像名稱要跟前面構(gòu)建環(huán)節(jié)的鏡像名稱保持一致矿瘦,但不能出現(xiàn)鏡像標(biāo)簽
          school: datacenter-school      # 鏡像名稱要跟前面構(gòu)建環(huán)節(jié)的鏡像名稱保持一致,但不能出現(xiàn)鏡像標(biāo)簽
          teacher: datacenter-teacher    # 鏡像名稱要跟前面構(gòu)建環(huán)節(jié)的鏡像名稱保持一致愿卒,但不能出現(xiàn)鏡像標(biāo)簽
          student: datacenter-student    # 鏡像名稱要跟前面構(gòu)建環(huán)節(jié)的鏡像名稱保持一致缚去,但不能出現(xiàn)鏡像標(biāo)簽
      imageStrategy:
        helm: {}

配置參考

完整的配置文件可以參考:datacenter

上手實踐

前期準(zhǔn)備

點擊上述兩個組件的鏈接,下載到本地掘猿,再講二進制加入 PATH 環(huán)境變量病游,詳細(xì)安裝過程不再贅述。

基本開發(fā)環(huán)境配置

JDK 稠通、mavenGradle 這類Java開發(fā)必備的工具請自行安裝衬衬,這里就不展開講了。

初始化helm chart

在一個空白目錄下運行 helm create datacenter 命令改橘,即可快速創(chuàng)建 chart 包滋尉,包的目錄結(jié)構(gòu)如下所示:

├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

可以根據(jù)自身的實際需求,增刪修改包的文件內(nèi)容飞主,例如這里用不到 hpa.yaml 狮惜、 serviceaccount.yamltests/* ,所以都刪除了碌识。

然后把 datacenter 重命名為 package 碾篡,然后移動到原本的代碼目錄下,這是約定成俗的習(xí)慣筏餐。

部署MySQL服務(wù)

經(jīng)典的Web應(yīng)用往往離不開數(shù)據(jù)庫开泽,而在k8s上運行數(shù)據(jù)庫,則需要提供持久化存儲魁瞪,否則數(shù)據(jù)庫的容器重啟后數(shù)據(jù)就丟失了穆律。

首先惠呼,在 package/templates 目錄下創(chuàng)建 pv.yaml 文件,然后寫入以下內(nèi)容:

kind: PersistentVolume
apiVersion: v1
metadata:
  name: mysql-pv-volume
  namespace: spring-cloud
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/opt/data/mysql"

解釋:創(chuàng)建持久化卷 PersistentVolume 峦耘,簡稱 PV剔蹋,存儲路徑就用宿主機目錄 /opt/data/mysql

然后,在同一個目錄下創(chuàng)建 pvc.yaml 文件辅髓,然后寫入以下內(nèi)容:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
  namespace: spring-cloud
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi

解釋:創(chuàng)建持久卷使用聲明 PersistentVolumeClaim 泣崩,簡稱 PVC,綁定前面創(chuàng)建的 PV利朵。
PVC 的容量是 2G 律想,必須小或等于 PV 的容量猎莲,否則無法綁定绍弟,可以根據(jù)實際情況調(diào)整容量大小。

為了防止卸載過程中意外刪除PV卷導(dǎo)致數(shù)據(jù)丟失的情況著洼,helm不會執(zhí)行刪除 PV 的操作樟遣,必須要用戶手動執(zhí)行。
因此如果部署過程出現(xiàn)PVC與PV無法綁定而導(dǎo)致無法繼續(xù)的情況身笤,請手動刪除再重新PV以及PVC的方式排除故障

最后豹悬,在同一個目錄下創(chuàng)建 statefulset.yaml 文件,然后寫入以下內(nèi)容:

apiVersion: v1
kind: Service
metadata:
  name: mysql                  # 同一個命名空間的其他服務(wù)可以通過域名 “mysql” 來訪問 MySQL 服務(wù)
  namespace: spring-cloud      # 通過命名空間來隔離不同的項目
spec:
  type: ClusterIP
  ports:
  - name: mysql
    protocol: TCP
    port: 3306
    targetPort: 3306
  selector:
    app: mysql                 # 通過定義標(biāo)簽選擇器液荸,只轉(zhuǎn)發(fā)請求給帶有 app: mysql 的Pod
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: spring-cloud
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: bocloud@2019    # 密碼屬于高度敏感機密瞻佛,建議在生成環(huán)境中通過 ServiceAccount 和 ConfigMap 等方式注入
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim   # 將前面定義的pvc掛載成卷,給容器使用

解釋:創(chuàng)建 mysql 的 ServiceStatefulSet娇钱。由于 mysql 是個數(shù)據(jù)庫伤柄,屬于 有狀態(tài)應(yīng)用 ,所以建議使用 StatefulSet 來管理文搂。
另外由于 k8s 的機制問題适刀,Pod 重啟后IP地址會改變,所以Pod之間的通信不適合直接通過訪問 Pod IP 的方式進行煤蹭,最佳實踐是通過創(chuàng)建特定 Service ,請求方的 Pod 向特定 Service 發(fā)送請求笔喉,再由特定 Service 轉(zhuǎn)發(fā)請求給被請求方的 Pod。

部署微服務(wù)

package/templates 目錄下清空 deployment.yaml 文件硝皂,然后寫入以下內(nèi)容:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: datacenter-dep
  namespace: spring-cloud      # 同一個項目的命名空間一定要相同
  labels:
    app: datacenter            # 自定義標(biāo)簽
spec:
  replicas: 1                       # 副本數(shù)量
  selector:
    matchLabels:
      app: datacenter               # 通過統(tǒng)計有多少個帶有app: datacenter的Pod來確定副本的數(shù)量
  template:
    metadata:
      labels:
        app: datacenter             # 給Pod打上app: datacenter標(biāo)簽常挚,方便統(tǒng)計
    spec:
      containers:
      - name: school
        image: {{.Values.image.school.repository}}:{{.Values.image.school.tag}} # 注入真正的鏡像
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_ADDRESS
          value: mysql:3306
        - name: MYSQL_PASSWORD
          value: bocloud@2019           # 密碼屬于高度敏感機密,不建議在真實環(huán)境使用明文密碼稽物,這里僅為展示
        ports:
        - containerPort: 8084
      - name: teacher
        image: {{.Values.image.teacher.repository}}:{{.Values.image.teacher.tag}} # 注入真正的鏡像
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_ADDRESS
          value: mysql:3306
        - name: MYSQL_PASSWORD
          value: bocloud@2019           # 密碼屬于高度敏感機密奄毡,不建議在真實環(huán)境使用明文密碼,這里僅為展示
        ports:
        - containerPort: 8082
      - name: student
        image: {{.Values.image.student.repository}}:{{.Values.image.student.tag}} # 注入真正的鏡像
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_ADDRESS
          value: mysql:3306
        - name: MYSQL_PASSWORD
          value: bocloud@2019           # 密碼屬于高度敏感機密姨裸,不建議在真實環(huán)境使用明文密碼秧倾,這里僅為展示
        ports:
        - containerPort: 8083

解釋:根據(jù) k8s 的規(guī)范要求怨酝,應(yīng)該通過 Deployment 來部署 無狀態(tài)應(yīng)用

然后那先,在同一個目錄下清空 service.yaml 文件农猬,然后寫入以下內(nèi)容:

apiVersion: v1
kind: Service
metadata:
  name: datacenter
  namespace: spring-cloud
spec:
  type: ClusterIP
  selector:
    app: datacenter
  ports:
    - name: school
      protocol: TCP
      port: 8084
      targetPort: 8084
    - name: teacher
      protocol: TCP
      port: 8082
      targetPort: 8082
    - name: student
      protocol: TCP
      port: 8083
      targetPort: 8083

解釋:創(chuàng)建 Service 暴露到集群內(nèi)部,供集群內(nèi)部的其他服務(wù)調(diào)用

最后售淡,修改 package/values.yaml 文件斤葱,然后寫入以下內(nèi)容:

image:
  school:
    repository: datacenter-school
    tag: latest
  teacher:
    repository: datacenter-teacher
    tag: latest
  student:
    repository: datacenter-student
    tag: latest

解釋:helm 推薦通過 values.yaml 文件統(tǒng)一管理 chart 模板的變量。
skaffold 也是通過修改 values.yaml 注入不同的鏡像名稱揖闸,動態(tài)更新運行中容器鏡像

安裝配置kubectl和docker

安裝kubectl與k8s通信

版本:v1.23.0

點擊上述鏈接揍堕,下載到本地,再講二進制加入 PATH 環(huán)境變量汤纸,詳細(xì)安裝過程不再贅述衩茸。

K8S配置 一般保存在主節(jié)點的 ~/.kube 目錄下,將完整目錄復(fù)制到本地目錄下贮泞,打開目錄下的 .kube/config楞慈,可以類似的內(nèi)容如下:

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: "****"  # 證書頒發(fā)機構(gòu)的證書
    server: https://172.24.86.22:6443   # k8s的apiserver地址
  name: kubernetes                      # k8s集群的名稱
contexts:
- context:
    cluster: kubernetes                        # 上下文的k8s集群的名稱
    user: kubernetes-admin                     # 上下文的k8s憑證的用戶名稱
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes   # 設(shè)置當(dāng)前上下文的所使用的k8s集群
kind: Config
preferences: {}
users:
- name: kubernetes-admin             # k8s憑證的用戶名稱
  user:
    client-certificate-data: "****"  # 用戶證書
    client-key-data: "****"          # 用戶密鑰

然后設(shè)置環(huán)境變量 KUBECONFIG 指向本地的 ./kube/config 路徑,kubectl 便可以通過憑證與 k8sAPIServer 通信啃擦。
修改環(huán)境變量后囊蓝,記得運行命令更新環(huán)境變量,Windows平臺執(zhí)行 refreshenv 命令令蛉。

安裝 docker 用于打包鏡像

如果你的本地電腦環(huán)境存在docker環(huán)境聚霜,可以跳過docker安裝配置環(huán)節(jié)

安裝Docker客戶端

點擊這里
下載到本地珠叔,再講二進制加入 PATH 環(huán)境變量蝎宇,詳細(xì)安裝過程不再贅述。

配置Docker

假設(shè)你的WSL環(huán)境中存在 Docker 環(huán)境运杭,又或者遠(yuǎn)程Linux服務(wù)器上存在 Docker 環(huán)境夫啊,
可以通過修改 Docker 守護進程的配置,將 Docker 進程暴露到內(nèi)網(wǎng)供其他設(shè)備進行使用辆憔。

首先撇眯,編輯 /etc/systemd/system/multi-user.target.wants/docker.service 文件,將 ExecStart 行改成以下內(nèi)容:

ExecStart=/usr/bin/dockerd --containerd=/run/containerd/containerd.sock

重點是去掉 fd:// 虱咧,接著編輯 /etc/docker/daemon.json 文件熊榛,重點是 hosts 加上 fd://tcp://0.0.0.0:10086

注意事項:升級docker后會覆蓋當(dāng)前設(shè)置腕巡,導(dǎo)致docker無法正常運行玄坦,需要參考上述步驟重新設(shè)置 docker.service 文件才能正常運行

端口號可以根據(jù)實際情況調(diào)整

{
  "hosts": [
    "fd://",
    "tcp://0.0.0.0:10086"
  ],
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn/",
    "https://dockerhub.azk8s.cn/",
    "https://hub-mirror.c.163.com/"
  ],
  "exec-opts": ["native.cgroupdriver=systemd"],
    "log-driver": "json-file",
    "log-opts": {
      "max-size": "100m"
  },
  "storage-driver": "overlay2"
}

接口執(zhí)行以下命令,重啟 docker

systemctl daemon-reload
systemctl restart docker

最后,在本地設(shè)置環(huán)境變量 DOCKER_HOST=<eth0IP>:10086 煎楣,把 <eth0IP> 換成遠(yuǎn)程Linux服務(wù)器的真實IP豺总。

由于WSL每次重啟eth0的IP會變化,需要重新設(shè)置 DOCKER_HOST 變量

在本地命令行界面择懂,執(zhí)行 docker info 命令檢查是否設(shè)置成功喻喳。

C:\WINDOWS\system32>docker info
Client:
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc., v0.7.1)
  compose: Docker Compose (Docker Inc., v2.2.1)
  scan: Docker Scan (Docker Inc., 0.9.0)

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 30
 Server Version: 20.10.12
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: systemd
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 7b11cfaabd73bb80907dd23182b9347b4245eb5d
 runc version: v1.0.2-0-g52b36a2
 init version: de40ad0
 Security Options:
  seccomp
   Profile: default
 Kernel Version: 5.10.74.3-microsoft-standard-WSL2+
 Operating System: Ubuntu 20.04.3 LTS
 OSType: linux
 Architecture: x86_64
 CPUs: 8
 Total Memory: 7.239GiB
 Name: LAPTOP-MAGICBOOKPRO
 ID: EXNQ:FGLE:MROB:C7FG:WJXC:R7YV:HUFB:6A46:4KAW:LG2A:TM3J:SAAB
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Registry Mirrors:
  https://docker.mirrors.ustc.edu.cn/
  https://dockerhub.azk8s.cn/
  https://hub-mirror.c.163.com/
 Live Restore Enabled: false

填寫skaffold配置并運行

在項目根目錄下,創(chuàng)建 skaffold.yaml 文件困曙,填寫以下內(nèi)容

apiVersion: skaffold/v2beta26                    # version of the configuration.
kind: Config                                     # always Config.
metadata:
  name: datacenter
build:
  local:
    push: false
  artifacts:
    - image: datacenter-school         # must match in artifactOverrides
      context: "school"
      docker:
        dockerfile: Dockerfile
    - image: datacenter-teacher        # must match in artifactOverrides
      context: "teacher"
      docker:
        dockerfile: Dockerfile
    - image: datacenter-student        # must match in artifactOverrides
      context: "student"
      docker:
        dockerfile: Dockerfile
deploy:
  helm:
    releases:
    - name: datacenter
      chartPath: package
      artifactOverrides:
        image:
          school: datacenter-school               # no tag present!
          teacher: datacenter-teacher               # no tag present!
          student: datacenter-student               # no tag present!
      imageStrategy:
        helm: {}

先執(zhí)行以下命令創(chuàng)建 spring-cloud 命名空間表伦,k8s 通過命名空間來隔離不同微服務(wù)的資源

kubectl create namespace spring-cloud

再執(zhí)行 skaffold dev 命令,如果前面的步驟和配置都正確慷丽,應(yīng)該可以看到以下輸出

C:\Users\huang\Documents\datacenter>skaffold dev
time="2022-02-14T00:13:29+08:00" level=warning msg="failed to detect active kubernetes cluter node platform. Specify the correct build platform in the `skaffold.yaml` file or using the `--platform` flag" subtask=-1 task=DevLoop
Listing files to watch...
 - datacenter-eureka
 - datacenter-school
 - datacenter-teacher
 - datacenter-student
Generating tags...
 - datacenter-eureka -> datacenter-eureka:13577a5
 - datacenter-school -> datacenter-school:13577a5
 - datacenter-teacher -> datacenter-teacher:13577a5
 - datacenter-student -> datacenter-student:13577a5
Checking cache...
 - datacenter-eureka: Found. Tagging
 - datacenter-school: Found. Tagging
 - datacenter-teacher: Found. Tagging
 - datacenter-student: Found. Tagging
Tags used in deployment:
 - datacenter-eureka -> datacenter-eureka:769afdbaaf2a35acada2a56cf1d1cccbc8a8ab8196396a8ff9e2803cf6a49490
 - datacenter-school -> datacenter-school:b89167e724932d41e40945a39ff04d84e419345957c4c0a022e7c4694153b609
 - datacenter-teacher -> datacenter-teacher:9d013f9295b7bd3e75b68b2d8a9df434a77cbc9514df1ae36a967b6841c4328f
 - datacenter-student -> datacenter-student:3f5267479ce35cec929485edce5f1dfc2cb1017136bbc90f2a0de5cd4f48f808
Starting deploy...

Ctrl+C 即可停止服務(wù)蹦哼,如果 kubernetes 集群中依舊存在 datacenter 相關(guān)的資源,可以通過 helm uninstall datacenter 手動清除要糊。

[可選]使用Buildah代替Docker

對于 WSL1 或者 嫌棄在 WSL2 安裝 docker 環(huán)境太麻煩的 windows 用戶纲熏,以及不想在本地安裝 dockerMac 用戶,
可以嘗試安裝 redhat 開源的 buildah

按照官方教程自行安裝即可杨耙。
安裝結(jié)束后運行 buildah image赤套, 若遇到以下錯誤:

kernel does not support overlay fs: 'overlay' is not supported over <unknown> at "/home/zaoying/.local/share/containers/storage/overlay": backing file system is unsupported for this graph driver
WARN[0000] failed to shutdown storage: "kernel does not support overlay fs: 'overlay' is not supported over <unknown> at \"/home/zaoying/.local/share/containers/storage/overlay\": backing file system is unsupported for this graph driver"
ERRO[0000] exit status 125

只需要安裝 fuse-overlayfs 即可:

# for debian/ubuntu
apt install fuse-overlayfs

buildah 基于 fork 模型,不需要 daemon 守護進程珊膜,因此不依賴于 systemd ,不需要root權(quán)限即可運行宣脉。
安裝完后即可使用车柠,不需要額外的配置。但 skaffold 尚未提供 buildah 官方支持塑猖,因此需要自定義構(gòu)建腳本竹祷。

apiVersion: skaffold/v2beta26                    # version of the configuration.
kind: Config                                     # always Config.
metadata:
 name: datacenter
build:
 local:
   push: false
 artifacts:
   - image: datacenter-school         # must match in artifactOverrides
     context: "school"
     custom:
       buildCommand: |
         buildah bud -t $IMAGE -f .
   - image: datacenter-teacher        # must match in artifactOverrides
     context: "teacher"
     custom:
       buildCommand: |
         buildah bud -t $IMAGE -f .
   - image: datacenter-student        # must match in artifactOverrides
     context: "student"
     custom:
       buildCommand: |
         buildah bud -t $IMAGE -f .
deploy:
 helm:
   releases:
   - name: datacenter
     chartPath: package
     artifactOverrides:
       image:
         school: datacenter-school               # no tag present!
         teacher: datacenter-teacher               # no tag present!
         student: datacenter-student               # no tag present!
     imageStrategy:
       helm: {}

更多詳細(xì)的自定義構(gòu)建器幫助,請查看官方文檔

[可選]代碼熱更新

代碼熱更新在日常的開發(fā)過程非常實用羊苟,可以加快特性開發(fā)與功能驗證的效率塑陵。

skaffold 可以解析 Dockerfile ,根據(jù) COPYADD 等指令蜡励,自動選擇監(jiān)聽和同步哪些文件令花。

當(dāng)修改完代碼后,手動執(zhí)行 mvn clean & mvn package 命令凉倚,skaffold 監(jiān)聽jar包變動兼都,自動重新打包鏡像并替換。

整個過程算不上真正意思上的熱更新稽寒,主要的原因是 spring boot 通過 jar 包部署扮碧,每次只做了很小的改動都需要重新打包鏡像,耗費非常多的時間。

如果改為 exploded war 的方式部署慎王,就可以實現(xiàn) class 粒度的 熱更新 蚓土。

直接跳過 mvn package 打包環(huán)節(jié),直接將編譯的中間產(chǎn)物 .class 字節(jié)碼文件同步到運行中的容器中赖淤,從而在不重啟容器的前提下實現(xiàn)代碼熱更新北戏。

理論上來說,skaffold 的代碼熱更新功能同時適用于 JavaJavascript 等技術(shù)棧漫蛔。但限于篇幅嗜愈,本文僅限于Java。

首先要改造 pom.xml 配置莽龟,把 <package>jar</package> 改成 <package>war</package>

    <packaging>war</packaging>

然后加上 spring-boot-starter-tomcat 蠕嫁,scope 改為 provided,以及增加 start-class 屬性毯盈,填寫全路徑的啟動類

  ...
    <properties>
    ...
        <start-class>com.springcloud.eureka.SchoolApplication</start-class>
    ...
    </properties>
  ...
  <dependencys>
    ...
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <scope>provided</scope>
    </dependency>
    ...
  </dependencys>

打開啟動類 SpringApplication.java 剃毒,增加 extends SpringBootServletInitializer 并重載 configure 方法

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class SchoolApplication extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
       return application.sources(SchoolApplication.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(SchoolApplication.class, args);
    }
}

接著,修改 Dockerfile, 如果你不愿意在生產(chǎn)環(huán)境采用 war 搂赋,這里可以另存為 Dockerfile.dev

FROM tomcat:9.0.62-jre11-temurin-focal
RUN rm -rf /usr/local/tomcat/webapps.dist
# 通過修改server.xml的方式修改端口
RUN sed -i 's/redirectPort="8443"http://' /usr/local/tomcat/conf/server.xml && sed -i 's/8005/8004/' /usr/local/tomcat/conf/server.xml && sed -i 's/8080/8084/' /usr/local/tomcat/conf/server.xml
COPY target/exploded /usr/local/tomcat/webapps/ROOT
COPY target/classes /usr/local/tomcat/webapps/ROOT/WEB-INF/classes
CMD ["catalina.sh", "run"]

以上操作都是針對單個服務(wù)赘阀,因此每個服務(wù)都要重復(fù)一遍上述操作,但以下操作則是針對整體服務(wù)脑奠。

最后修改 skaffold.yaml 基公,增加自定義構(gòu)建腳本

apiVersion: skaffold/v2beta26                    # version of the configuration.
kind: Config                                     # always Config.
metadata:
  name: datacenter
build:
  local:
    push: false
  artifacts:
    - image: datacenter-eureka         # must match in artifactOverrides
      context: "eureka"
      custom:
        buildCommand: |
          mvn clean package && 7z x target/eureka-0.0.1-SNAPSHOT.war -otarget/exploded && docker build -t %IMAGE% -f Dockerfile.dev %BUILD_CONTEXT%
        dependencies:
          paths:
          - target/classes
          - Dockerfile.dev
          ignore:
          - target/exploded
    - image: datacenter-school         # must match in artifactOverrides
      context: "school"
      custom:
        buildCommand: |
          mvn clean package && 7z x target/school-0.0.1-SNAPSHOT.war -otarget/exploded && docker build -t %IMAGE% -f Dockerfile.dev %BUILD_CONTEXT%
        dependencies:
          paths:
          - target/classes
          - Dockerfile.dev
          ignore:
          - target/exploded
    - image: datacenter-teacher        # must match in artifactOverrides
      context: "teacher"
      custom:
        buildCommand: |
          mvn clean package && 7z x target/teacher-0.0.1-SNAPSHOT.war -otarget/exploded && docker build -t %IMAGE% -f Dockerfile.dev %BUILD_CONTEXT%
        dependencies:
          paths:
          - target/classes
          - Dockerfile.dev
          ignore:
          - target/exploded
    - image: datacenter-student        # must match in artifactOverrides
      context: "student"
      custom:
        buildCommand: |
          mvn clean package && 7z x target/student-0.0.1-SNAPSHOT.war -otarget/exploded && docker build -t %IMAGE% -f Dockerfile.dev %BUILD_CONTEXT%
        dependencies:
          paths:
          - target/classes
          - Dockerfile.dev
          ignore:
          - target/exploded
deploy:
  helm:
    releases:
    - name: datacenter
      chartPath: package
      artifactOverrides:
        image:
          eureka: datacenter-eureka               # no tag present!
          school: datacenter-school               # no tag present!
          teacher: datacenter-teacher               # no tag present!
          student: datacenter-student               # no tag present!
      imageStrategy:
        helm: {}

圖中所展示的 buildCommand 是在 Windows 平臺執(zhí)行的,如果是 LinuxMacOS宋欺,請參考以下命令

# 打包eureka
mvn clean package && unzip target/eureka-0.0.1-SNAPSHOT.war -o target/exploded && docker build -t $IMAGE -f Dockerfile.dev $BUILD_CONTEXT

# 打包school
mvn clean package && unzip target/school-0.0.1-SNAPSHOT.war -o target/exploded && docker build -t $IMAGE -f Dockerfile.dev $BUILD_CONTEXT

# 打包teacher
mvn clean package && unzip target/teacher-0.0.1-SNAPSHOT.war -o target/exploded && docker build -t $IMAGE -f Dockerfile.dev $BUILD_CONTEXT

# 打包student
mvn clean package && unzip target/student-0.0.1-SNAPSHOT.war -o target/exploded && docker build -t $IMAGE -f Dockerfile.dev $BUILD_CONTEXT

僅第一次運行需要打包 war 再解壓的操作轰豆,后續(xù)可通過 mvn compile 即可以 class 為粒度實現(xiàn) 熱更新

總結(jié)

本次演示所使用的微服務(wù)項目是很多年前筆者為了學(xué)習(xí) Spring Cloud 而編寫的 Demo
時隔多年 Spring Cloud 已經(jīng)不再推薦 Eureka 作為服務(wù)發(fā)現(xiàn)與注冊中心齿诞。
同時 k8s 本身也支持將 CoreDNS 作為服務(wù)發(fā)現(xiàn)/注冊的組件使用酸休。
所以讀者不必糾結(jié) Demo 代碼中的錯誤,因為本文的重點是 skaffold 的配置與使用祷杈。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斑司,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子但汞,更是在濱河造成了極大的恐慌宿刮,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件特占,死亡現(xiàn)場離奇詭異糙置,居然都是意外死亡,警方通過查閱死者的電腦和手機是目,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門谤饭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事揉抵⊥鋈荩” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵冤今,是天一觀的道長闺兢。 經(jīng)常有香客問我,道長戏罢,這世上最難降的妖魔是什么屋谭? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮龟糕,結(jié)果婚禮上桐磁,老公的妹妹穿的比我還像新娘。我一直安慰自己讲岁,他們只是感情好我擂,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缓艳,像睡著了一般校摩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上阶淘,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天衙吩,我揣著相機與錄音,去河邊找鬼舶治。 笑死分井,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的霉猛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼珠闰,長吁一口氣:“原來是場噩夢啊……” “哼惜浅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起伏嗜,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤坛悉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后承绸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體裸影,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年军熏,在試婚紗的時候發(fā)現(xiàn)自己被綠了轩猩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖均践,靈堂內(nèi)的尸體忽然破棺而出晤锹,到底是詐尸還是另有隱情,我是刑警寧澤彤委,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布鞭铆,位于F島的核電站,受9級特大地震影響焦影,放射性物質(zhì)發(fā)生泄漏车遂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一斯辰、第九天 我趴在偏房一處隱蔽的房頂上張望舶担。 院中可真熱鬧,春花似錦椒涯、人聲如沸柄沮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祖搓。三九已至,卻和暖如春湖苞,著一層夾襖步出監(jiān)牢的瞬間拯欧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工财骨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留镐作,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓隆箩,卻偏偏與公主長得像该贾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子捌臊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容