通過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)
skaffold
的工作流按照開發(fā)流程的不同階段嘹承,分為4個部分組成:
- 本地開發(fā)(文件同步)
- 持續(xù)構(gòu)建
- 持續(xù)測試
- 持續(xù)部署
以上四個部分均可以根據(jù)實際需求進行定制化修改。
本地開發(fā)
skaffold
對主流的編程語言以及配套使用的技術(shù)棧都有著非常不錯的支持如庭,例如 Go
叹卷、Java
、JavaScript
等
本地開發(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ù)棧非常實用,例如 Java
和 Javascript
昼榛,但對于 Go
這類不支持 熱更新
的技術(shù)棧來說效果十分有限境肾,因為即便文件同步完成,依然重啟主進程才能讓修改后的功能生效褒纲。
接著說 Jib(maven/gradle)
准夷,Jib
也是由谷歌開發(fā)的一款專門針對 Java
生態(tài)的 CI/CD
工具,跟 skaffold
通用 CI/CD
不同莺掠,同時還有 VSCode
和 IDEA
插件衫嵌, 但作者本人并沒有用過,所以等以后有機會再展開講彻秆。
至于 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
稠通、maven
或 Gradle
這類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.yaml
和 tests/*
,所以都刪除了碌识。
然后把 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 的 Service
和 StatefulSet
娇钱。由于 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
便可以通過憑證與 k8s
的 APIServer
通信啃擦。
修改環(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
用戶纲熏,以及不想在本地安裝 docker
的 Mac
用戶,
可以嘗試安裝 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ù) COPY
和 ADD
等指令蜡励,自動選擇監(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 的代碼熱更新功能同時適用于
Java
和Javascript
等技術(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í)行的,如果是 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
再解壓的操作轰豆,后續(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
的配置與使用祷杈。