透過skaffold快速部署微服務
隨著技術的不斷髮展,程式設計師們熟悉的傳統單體應用開發流程,漸漸地無法適應當下微服務化的潮流趨勢。同時隨著雲原生開發的理念不斷推廣,越來越多的服務執行在不可變的基礎設施之上,隨之而來的是傳統單體應用開發流程與雲化程度日益加深服務之間的隔閡越發巨大,開發人員越來越難以容忍重複繁瑣且容易出錯的低效率開發流程。因此,一款面向開發人員而運維實施人員的持續構建與持續部署工具 skaffold
應運而生
skaffold簡介
[skaffold]() 是一款 Google
推出的持續構建與持續部署工具,它主要面向開發人員而非運維實施人員,目標是打破本地開發與雲化部署之間的隔閡,減輕開發人員的心智負擔,幫助開發人員專注於出色地完成日常開發工作,避免開發人員在紛亂繁雜的運維流程中過多消耗寶貴的精力與時間。
基本架構
skaffold
的工作流按照開發流程的不同階段,分為4個部分組成:
- 本地開發(檔案同步)
- 持續構建
- 持續測試
- 持續部署
以上四個部分均可以根據實際需求進行定製化修改。
本地開發
skaffold
對主流的程式語言以及配套使用的技術棧都有著非常不錯的支援,例如 Go
、Java
、JavaScript
等
本地開發的核心內容是 檔案同步
,檔案同步的監聽物件大概可以分為 原始碼
和 編譯產物
。
skaffold
官方的推薦做法是監聽原始碼變動,然後自動化把原始碼複製到Docker容器中進行編譯和構建。
這種做法的問題不少,首先是原始碼變動非常頻繁,而編譯和構建過程往往非常耗時,因此自動觸發構建不太合理。
其次,在Docker容器中編譯和構建,需要掌握編寫 Multi Stage Dockerfile
技能,否則構建出來的映象大小會佔據非常大的空間,另外還要消耗本就不寬裕的頻寬進行映象傳輸。
最後,在Docker容器中編譯和構建,要解決環境變數,代理設定、快取構建中間結果等一系列問題,對新手非常不友好。
因此,個人推薦,在本地開發環節儘量採用手動觸發編譯構建,透過監聽編譯產物的方式來觸發熱更新等流程。
持續構建
因為選擇手動觸發編譯,所以本環節的內容主要講述如何打包映象的內容
目前 skaffold
官方支援的構建方式有三種:Docker
、Jib(maven/gradle)
、Bazel
- Docker
- Jib(maven/gradle)
- Bazel
這裡以最常見 Docker
為例:
build:
local:
push: false # 映象打包成功後是否推送到遠端的映象倉庫
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
當執行 skaffold dev
時,會按照 編譯 —> 構建 -> 測試 -> 部署
的標準流程走一遍。
當監聽到指定路徑下的檔案發生變化時,skaffold工具會嘗試透過類似於 kubectl cp
命令的方式,直接把產生變化後的檔案複製到執行中的容器內部,避免重新走一遍編譯構建/上傳映象的步驟,減少同步程式碼更改而消耗的時間。
需要特別注意,這種方式對於支援 程式碼熱更新
的技術棧非常實用,例如 Java
和 Javascript
,但對於 Go
這類不支援 熱更新
的技術棧來說效果十分有限,因為即便檔案同步完成,依然重啟主程式才能讓修改後的功能生效。
接著說 Jib(maven/gradle)
,Jib
也是由谷歌開發的一款專門針對 Java
生態的 CI/CD
工具,跟 skaffold
通用 CI/CD
不同,同時還有 VSCode
和 IDEA
外掛, 但作者本人並沒有用過,所以等以後有機會再展開講。
至於 Bazel
是微軟開發的全平臺構建工具,主要支援 C#
語言,甚至連前端相關 Javascript
專案也可以使用,但缺點就是非常笨重,這裡也不展開講。
此外, skaffold 還支援 Customize
自定義構建,這個方式的構建更自由可控,比如有些 WSL
環境的使用者不願意為了安裝 Docker
環境而反覆折騰,甚至有些企業內部不允許員工在開發電腦安裝虛擬機器等等,透過自定義構建流程都可以解決,放到文章最後再詳細講解。
持續測試
skaffold
官方的測試方案是把程式碼複製到定製化的測試環境容器中執行測試用例,這種方法非常麻煩,測試相關的內容這裡就不展開講。
感興趣的讀者可以檢視 skaffold官方配置文件
持續部署
skaffold
官方支援的部署方式有很多種,這裡以 helm
為例:
deploy:
helm:
releases:
- name: datacenter
chartPath: package
artifactOverrides:
image:
eureka: datacenter-eureka # 映象名稱要跟前面構建環節的映象名稱保持一致,但不能出現映象標籤
school: datacenter-school # 映象名稱要跟前面構建環節的映象名稱保持一致,但不能出現映象標籤
teacher: datacenter-teacher # 映象名稱要跟前面構建環節的映象名稱保持一致,但不能出現映象標籤
student: datacenter-student # 映象名稱要跟前面構建環節的映象名稱保持一致,但不能出現映象標籤
imageStrategy:
helm: {}
配置參考
完整的配置檔案可以參考:datacenter
上手實踐
前期準備
點選上述兩個元件的連結,下載到本地,再講二進位制加入 PATH
環境變數,詳細安裝過程不再贅述。
基本開發環境配置
像 JDK
、maven
或 Gradle
這類Java開發必備的工具請自行安裝,這裡就不展開講了。
初始化helm chart
在一個空白目錄下執行 helm create datacenter
命令,即可快速建立 chart
包,包的目錄結構如下所示:
├── Chart.yaml
├── charts
├── templates
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── service.yaml
│ ├── serviceaccount.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
可以根據自身的實際需求,增刪修改包的檔案內容,例如這裡用不到 hpa.yaml
、 serviceaccount.yaml
和 tests/*
,所以都刪除了。
然後把 datacenter
重新命名為 package
,然後移動到原本的程式碼目錄下,這是約定成俗的習慣。
部署MySQL服務
經典的Web應用往往離不開資料庫,而在k8s上執行資料庫,則需要提供持久化儲存,否則資料庫的容器重啟後資料就丟失了。
首先,在 package/templates
目錄下建立 pv.yaml
檔案,然後寫入以下內容:
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"
解釋:建立持久化卷 PersistentVolume
,簡稱 PV
,儲存路徑就用宿主機目錄 /opt/data/mysql
然後,在同一個目錄下建立 pvc.yaml
檔案,然後寫入以下內容:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
namespace: spring-cloud
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
解釋:建立持久卷使用宣告 PersistentVolumeClaim
,簡稱 PVC
,繫結前面建立的 PV
。 PVC
的容量是 2G
,必須小或等於 PV
的容量,否則無法繫結,可以根據實際情況調整容量大小。
為了防止解除安裝過程中意外刪除PV卷導致資料丟失的情況,helm不會執行刪除 PV 的操作,必須要使用者手動執行。
因此如果部署過程出現PVC與PV無法繫結而導致無法繼續的情況,請手動刪除再重新PV以及PVC的方式排除故障
最後,在同一個目錄下建立 statefulset.yaml
檔案,然後寫入以下內容:
apiVersion: v1
kind: Service
metadata:
name: mysql # 同一個名稱空間的其他服務可以透過域名 “mysql” 來訪問 MySQL 服務
namespace: spring-cloud # 透過名稱空間來隔離不同的專案
spec:
type: ClusterIP
ports:
- name: mysql
protocol: TCP
port: 3306
targetPort: 3306
selector:
app: mysql # 透過定義標籤選擇器,只轉發請求給帶有 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 # 密碼屬於高度敏感機密,建議在生成環境中透過 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掛載成卷,給容器使用
解釋:建立 mysql 的 Service
和 StatefulSet
。由於 mysql 是個資料庫,屬於 有狀態應用
,所以建議使用 StatefulSet
來管理。
另外由於 k8s
的機制問題,Pod 重啟後IP地址會改變,所以Pod之間的通訊不適合直接透過訪問 Pod IP 的方式進行,最佳實踐是透過建立特定 Service
,請求方的 Pod 向特定 Service 傳送請求,再由特定 Service 轉發請求給被請求方的 Pod。
部署微服務
在 package/templates
目錄下清空 deployment.yaml
檔案,然後寫入以下內容:
apiVersion: apps/v1
kind: Deployment
metadata:
name: datacenter-dep
namespace: spring-cloud # 同一個專案的名稱空間一定要相同
labels:
app: datacenter # 自定義標籤
spec:
replicas: 1 # 副本數量
selector:
matchLabels:
app: datacenter # 透過統計有多少個帶有app: datacenter的Pod來確定副本的數量
template:
metadata:
labels:
app: datacenter # 給Pod打上app: datacenter標籤,方便統計
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 # 密碼屬於高度敏感機密,不建議在真實環境使用明文密碼,這裡僅為展示
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 # 密碼屬於高度敏感機密,不建議在真實環境使用明文密碼,這裡僅為展示
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 # 密碼屬於高度敏感機密,不建議在真實環境使用明文密碼,這裡僅為展示
ports:
- containerPort: 8083
解釋:根據 k8s
的規範要求,應該透過 Deployment
來部署 無狀態應用
。
然後,在同一個目錄下清空 service.yaml
檔案,然後寫入以下內容:
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
解釋:建立 Service
暴露到叢集內部,供叢集內部的其他服務呼叫
最後,修改 package/values.yaml
檔案,然後寫入以下內容:
image:
school:
repository: datacenter-school
tag: latest
teacher:
repository: datacenter-teacher
tag: latest
student:
repository: datacenter-student
tag: latest
解釋:helm 推薦透過 values.yaml
檔案統一管理 chart
模板的變數。skaffold
也是透過修改 values.yaml
注入不同的映象名稱,動態更新執行中容器映象
安裝配置kubectl和docker
安裝kubectl與k8s通訊
版本:v1.23.0
點選上述連結,下載到本地,再講二進位制加入 PATH
環境變數,詳細安裝過程不再贅述。
K8S配置
一般儲存在主節點的 ~/.kube
目錄下,將完整目錄複製到本地目錄下,開啟目錄下的 .kube/config
,可以類似的內容如下:
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: "****" # 證照頒發機構的證照
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 # 設定當前上下文的所使用的k8s叢集
kind: Config
preferences: {}
users:
- name: kubernetes-admin # k8s憑證的使用者名稱稱
user:
client-certificate-data: "****" # 使用者證照
client-key-data: "****" # 使用者金鑰
然後設定環境變數 KUBECONFIG
指向本地的 ./kube/config
路徑,kubectl
便可以透過憑證與 k8s
的 APIServer
通訊。
修改環境變數後,記得執行命令更新環境變數,Windows平臺執行 refreshenv
命令。
安裝 docker
用於打包映象
如果你的本地電腦環境存在docker環境,可以跳過docker安裝配置環節
安裝Docker客戶端
點選這裡,
下載到本地,再講二進位制加入 PATH
環境變數,詳細安裝過程不再贅述。
配置Docker
假設你的WSL環境中存在 Docker
環境,又或者遠端Linux伺服器上存在 Docker
環境,
可以透過修改 Docker
守護程式的配置,將 Docker
程式暴露到內網供其他裝置進行使用。
首先,編輯 /etc/systemd/system/multi-user.target.wants/docker.service
檔案,將 ExecStart
行改成以下內容:
ExecStart=/usr/bin/dockerd --containerd=/run/containerd/containerd.sock
重點是去掉 fd://
,接著編輯 /etc/docker/daemon.json
檔案,重點是 hosts
加上 fd://
和 tcp://0.0.0.0:10086
,
注意事項:升級docker後會覆蓋當前設定,導致docker無法正常執行,需要參考上述步驟重新設定 docker.service 檔案才能正常執行
埠號可以根據實際情況調整
{
"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"
}
介面執行以下命令,重啟 docker
systemctl daemon-reload
systemctl restart docker
最後,在本地設定環境變數 DOCKER_HOST=<eth0IP>:10086
,把 <eth0IP>
換成遠端Linux伺服器的真實IP。
由於WSL每次重啟eth0的IP會變化,需要重新設定 DOCKER_HOST
變數
在本地命令列介面,執行 docker info
命令檢查是否設定成功。
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配置並執行
在專案根目錄下,建立 skaffold.yaml
檔案,填寫以下內容
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: {}
先執行以下命令建立 spring-cloud
名稱空間,k8s 透過名稱空間來隔離不同微服務的資源
kubectl create namespace spring-cloud
再執行 skaffold dev
命令,如果前面的步驟和配置都正確,應該可以看到以下輸出
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
即可停止服務,如果 kubernetes
叢集中依舊存在 datacenter
相關的資源,可以透過 helm uninstall datacenter
手動清除。
[可選]使用Buildah代替Docker
對於 WSL1
或者 嫌棄在 WSL2
安裝 docker
環境太麻煩的 windows
使用者,以及不想在本地安裝 docker
的 Mac
使用者,
可以嘗試安裝 redhat
開源的 buildah
按照官方教程自行安裝即可。
安裝結束後執行 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許可權即可執行。
安裝完後即可使用,不需要額外的配置。但 skaffold
尚未提供 buildah
官方支援,因此需要自定義構建指令碼。
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: {}
更多詳細的自定義構建器幫助,請檢視官方文件
[可選]程式碼熱更新
程式碼熱更新在日常的開發過程非常實用,可以加快特性開發與功能驗證的效率。
skaffold
可以解析 Dockerfile
,根據 COPY
和 ADD
等指令,自動選擇監聽和同步哪些檔案。
當修改完程式碼後,手動執行 mvn clean & mvn package
命令,skaffold
監聽jar包變動,自動重新打包映象並替換。
整個過程算不上真正意思上的熱更新,主要的原因是 spring boot
透過 jar
包部署,每次只做了很小的改動都需要重新打包映象,耗費非常多的時間。
如果改為 exploded war
的方式部署,就可以實現 class
粒度的 熱更新
。
直接跳過 mvn package
打包環節,直接將編譯的中間產物 .class
位元組碼檔案同步到執行中的容器中,從而在不重啟容器的前提下實現程式碼熱更新。
理論上來說,skaffold 的程式碼熱更新功能同時適用於Java
和Javascript
等技術棧。但限於篇幅,本文僅限於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
, 如果你不願意在生產環境採用 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"//' /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"]
以上操作都是針對單個服務,因此每個服務都要重複一遍上述操作,但以下操作則是針對整體服務。
最後修改 skaffold.yaml
,增加自定義構建指令碼
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
平臺執行的,如果是 Linux
或 MacOS
,請參考以下命令
# 打包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
再解壓的操作,後續可透過 mvn compile
即可以 class
為粒度實現 熱更新
總結
本次演示所使用的微服務專案是很多年前筆者為了學習 Spring Cloud
而編寫的 Demo
。
時隔多年 Spring Cloud
已經不再推薦 Eureka
作為服務發現與註冊中心。
同時 k8s
本身也支援將 CoreDNS
作為服務發現/註冊的元件使用。
所以讀者不必糾結 Demo
程式碼中的錯誤,因為本文的重點是 skaffold
的配置與使用。