
我們總是需要使用 Kubernetes 自定義我們的部署,我不知道為什么,但現(xiàn)在主要的工具是 HELM,它拋棄了我們?cè)?docker 和 Kubernetes 上學(xué)到的所有邏輯。在這里給大家介紹一個(gè)替代品,叫做 Kustomize。

Kustomize 不是一個(gè)新工具,它自 2017 年以來一直在建設(shè)中,并在 1.14 版本中作為原生 kubectl 子命令引入。是的,你沒聽錯(cuò),它現(xiàn)在直接嵌入到你日常使用的工具中,所以你可以扔掉 helm 命令。
哲學(xué)
當(dāng)使用 Git 作為 VCS、創(chuàng)建 Docker 鏡像或在 Kubernetes 中聲明資源時(shí),Kustomize 試圖遵循你在日常工作中使用的理念。
所以,首先,Kustomize 就像 Kubernetes,它是完全聲明式的!你說你想要什么,系統(tǒng)就會(huì)提供給你,你不必遵循命令式的方式并描述希望它如何構(gòu)建事物。
其次,它像 Docker 一樣工作。你有很多層,每一層都在修改之前的層。多虧了這一點(diǎn),你可以不斷地寫出高于他人的東西,而不會(huì)在你的配置中增加復(fù)雜性。構(gòu)建的結(jié)果將是添加基礎(chǔ)和你在其上應(yīng)用的不同層。
最后,與 Git 一樣,你可以使用遠(yuǎn)程庫作為你工作的開始,并在其上添加一些定制。
使用
基礎(chǔ)
要開始使用 Kustomize,你需要有原始的 yaml 文件來描述你要部署到集群中的任何資源。我們這里的示例將這些文件將存儲(chǔ)在文件夾 ./k8s/base/ 中。
這些文件將永遠(yuǎn)不會(huì)被修改,我們將在它們上面應(yīng)用自定義來創(chuàng)建新的資源定義。
注意:你可以隨時(shí)使用命令 kubectl apply -f ./k8s/base/ 構(gòu)建基礎(chǔ)模板(例如,用于開發(fā)環(huán)境)。 在此示例中,我們將使用一個(gè) Service 和一個(gè) Deployment 資源:
apiVersion: v1
kind: Service
metadata:
name: sl-demo-app
spec:
ports:
- name: http
port: 8080
selector:
app: sl-demo-app
apiVersion: apps/v1
kind: Deployment
metadata:
name: sl-demo-app
spec:
selector:
matchLabels:
app: sl-demo-app
template:
metadata:
labels:
app: sl-demo-app
spec:
containers:
- name: app
image: foo/bar:latest
ports:
- name: http
containerPort: 8080
protocol: TCP
我們將在該文件夾中添加一個(gè)名為 kustomization.yaml 的新文件:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- service.yaml
- deployment.yaml
該文件將是你核心文件,它描述了你使用的資源,這些資源文件是相對(duì)于當(dāng)前文件的路徑。
注意:這個(gè) kustomization.yaml 文件在運(yùn)行 kubectl apply -f ./k8s/base/ 時(shí)可能會(huì)導(dǎo)致錯(cuò)誤,你可以使用參數(shù) --validate=false 運(yùn)行它,或者干脆不對(duì)整個(gè)文件夾運(yùn)行命令。
要將該 base 基礎(chǔ)模板應(yīng)用到集群,你只需執(zhí)行以下命令:
$ kubectl apply -k k8s/base
但是這樣會(huì)直接將我們的模板應(yīng)用到集群中,有時(shí)候我們可能希望將模板渲染出來看下結(jié)果是否正確,這個(gè)時(shí)候我們可以去直接使用 kustomize 的命令 kustomize build 來實(shí)現(xiàn),所以我們需要單獨(dú)安裝下 kustomize,對(duì)于 Mac 用戶可以直接使用 brew 命令一鍵安裝,其他系統(tǒng)可以直接前往 Release 頁面下載二進(jìn)制文件,然后放入 PATH 路徑即可。
要查看將在你的集群中應(yīng)用了什么,我們將在本文中主要使用命令 kustomize build 而不是 kubectl apply -k。
kustomize build k8s/base 命令的結(jié)果如下,會(huì)直接將兩個(gè)文件連接在一起:
$ kustomize build k8s/base
apiVersion: v1
kind: Service
metadata:
name: sl-demo-app
spec:
ports:
- name: http
port: 8080
selector:
app: sl-demo-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sl-demo-app
spec:
selector:
matchLabels:
app: sl-demo-app
template:
metadata:
labels:
app: sl-demo-app
spec:
containers:
- image: foo/bar:latest
name: app
ports:
- containerPort: 8080
name: http
protocol: TCP
定制
現(xiàn)在,我們想要針對(duì)特定 場(chǎng)景來定制我們的應(yīng)用程序,例如,針對(duì)我們的生產(chǎn)環(huán)境,接下來我們將看到如何通過一些修改來增強(qiáng)我們的基礎(chǔ)。本文的主要目標(biāo)不是涵蓋 Kustomize 的全部功能,而是作為一個(gè)標(biāo)準(zhǔn)示例向你展示此工具背后的理念。
首先,我們將創(chuàng)建文件夾 k8s/overlays/prod,其中包含一個(gè) kustomization.yaml 文件,包含以下內(nèi)容:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
如果我們構(gòu)建它,我們將看到與之前構(gòu)建 base 時(shí)相同的結(jié)果。
$ kustomize build k8s/overlays/prod
為 Deployment 定義環(huán)境變量
在 base 中,我們沒有定義任何環(huán)境變量,現(xiàn)在我們將添加 env 變量到 base 中去,要做到這一點(diǎn),非常簡(jiǎn)單,我們只需要?jiǎng)?chuàng)建我們想要在 base 之上應(yīng)用的 yaml 塊,并在 kustomization.yaml 中引用它。
custom-env.yaml 包含的環(huán)境變量如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
name: sl-demo-app
spec:
template:
spec:
containers:
- name: app # (1)
env:
- name: CUSTOM_ENV_VARIABLE
value: Value defined by Kustomize ??
注意:這里的 name (1) 非常重要,可以讓 Kustomize 找到需要修改的正確容器。
你可以看到這個(gè) yaml 文件本身是無效的,但它只描述了我們想在之前的 base 上添加的內(nèi)容。
我們只需將此文件添加到 k8s/overlays/prod/kustomization.yaml 中的 patches 屬性下面即可:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: custom-env.yaml
如果現(xiàn)在我們來構(gòu)建,將會(huì)得到下面的結(jié)果:
$ kustomize build k8s/overlays/prod
apiVersion: v1
kind: Service
metadata:
name: sl-demo-app
spec:
ports:
- name: http
port: 8080
selector:
app: sl-demo-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sl-demo-app
spec:
selector:
matchLabels:
app: sl-demo-app
template:
metadata:
labels:
app: sl-demo-app
spec:
containers:
- env:
- name: CUSTOM_ENV_VARIABLE
value: Value defined by Kustomize ??
image: foo/bar:latest
name: app
ports:
- containerPort: 8080
name: http
protocol: TCP
可以看到我們上面定義的 env 塊已應(yīng)用在我們的 base 之上了,現(xiàn)在 CUSTOM_ENV_VARIABLE 將出現(xiàn)在我們的 deployment.yaml 中。
patches 屬性中可以直接指定一個(gè) yaml 文件,也可以直接在該屬性這里修改資源,補(bǔ)丁可以包括容器鏡像、端口、環(huán)境變量等。比如可以在 kustomization.yaml 文件中添加以下內(nèi)容:
patches:
- target:
kind: Deployment
name: sl-demo-app
patch: |-
- op: replace
path: /spec/template/spec/containers/0/image
value: my-image:latest
上述內(nèi)容將基礎(chǔ)資源中名為 sl-demo-app 的 Deployment 的容器鏡像修改為 my-image:latest。
更改副本數(shù)量
接下來我們想添加有關(guān)副本數(shù)量的信息,像之前一樣,僅包含定義副本所需的額外信息的塊或 yaml 就足夠了:
# replica-and-rollout-strategy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: sl-demo-app
spec:
replicas: 10
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
和以前一樣,我們將它添加到 kustomization.yaml 中的 patches 列表中:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: custom-env.yaml
- path: replica-and-rollout-strategy.yaml
同樣執(zhí)行命令 kustomize build k8s/overlays/prod 后會(huì)得到如下結(jié)果:
$ kustomize build k8s/overlays/prod
apiVersion: v1
kind: Service
metadata:
name: sl-demo-app
spec:
ports:
- name: http
port: 8080
selector:
app: sl-demo-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sl-demo-app
spec:
replicas: 10
selector:
matchLabels:
app: sl-demo-app
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app: sl-demo-app
spec:
containers:
- env:
- name: CUSTOM_ENV_VARIABLE
value: Value defined by Kustomize ??
image: foo/bar:latest
name: app
ports:
- containerPort: 8080
name: http
protocol: TCP
可以看到副本數(shù)和 rollingUpdate 策略已經(jīng)應(yīng)用在 base 之上了。
通過命令行使用 Secret 定義
我們經(jīng)常會(huì)從命令行將一些變量設(shè)置為 Secret 數(shù)據(jù),這里我們使用 kustomize 的一個(gè)子命令來編輯 kustomization.yaml 并為創(chuàng)建一個(gè) Secret,如下所示:
$ cd k8s/overlays/prod
$ kustomize edit add secret sl-demo-app --from-literal=db-password=12345
上面的命令會(huì)修改 kustomization.yaml 文件并在其中添加一個(gè) SecretGenerator,內(nèi)容如下所示:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: custom-env.yaml
- path: replica-and-rollout-strategy.yaml
secretGenerator:
- literals:
- db-password=12345
name: sl-demo-app
type: Opaque
同樣如果從示例項(xiàng)目的根文件夾運(yùn)行 kustomize build k8s/overlays/prod 命令將獲得以下輸出。
apiVersion: v1
data:
db-password: MTIzNDU=
kind: Secret
metadata:
name: sl-demo-app-gkmm8tkdd7
type: Opaque
---
apiVersion: v1
kind: Service
metadata:
name: sl-demo-app
spec:
ports:
- name: http
port: 8080
selector:
app: sl-demo-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sl-demo-app
spec:
replicas: 10
selector:
matchLabels:
app: sl-demo-app
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app: sl-demo-app
spec:
containers:
- env:
- name: CUSTOM_ENV_VARIABLE
value: Value defined by Kustomize ??
image: foo/bar:latest
name: app
ports:
- containerPort: 8080
name: http
protocol: TCP
注意上面生成的 Secret 對(duì)象名稱是 sl-demo-app-gkmm8tkdd7 而不是 sl-demo-app,這是正常的,如果 Secret 內(nèi)容發(fā)生變化,就可以觸發(fā) Deployment 的滾動(dòng)更新。
如果想在我們的 Deployment 中使用這個(gè) Secret,我們只需要像以前一樣添加一個(gè)使用 Secret 的新層定義即可。比如我們將 db-password 值以環(huán)境變量的方式注入 pod,則可以聲明如下的層文件:
# database-secret.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: sl-demo-app
spec:
template:
spec:
containers:
- name: app
env:
- name: "DB_PASSWORD"
valueFrom:
secretKeyRef:
name: sl-demo-app
key: db.password
然后在 kustomization.yaml 文件 pathes 中添加上面的層文件:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: custom-env.yaml
- path: replica-and-rollout-strategy.yaml
- path: database-secret.yaml
secretGenerator:
- literals:
- db-password=12345
name: sl-demo-app
type: Opaque
構(gòu)建后可以得到如下的結(jié)果:
$ kustomize build k8s/overlays/prod
apiVersion: v1
data:
db-password: MTIzNDU=
kind: Secret
metadata:
name: sl-demo-app-gkmm8tkdd7
type: Opaque
---
apiVersion: v1
kind: Service
metadata:
name: sl-demo-app
spec:
ports:
- name: http
port: 8080
selector:
app: sl-demo-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sl-demo-app
spec:
replicas: 10
selector:
matchLabels:
app: sl-demo-app
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app: sl-demo-app
spec:
containers:
- env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
key: db.password
name: sl-demo-app-gkmm8tkdd7
- name: CUSTOM_ENV_VARIABLE
value: Value defined by Kustomize ??
image: foo/bar:latest
name: app
ports:
- containerPort: 8080
name: http
protocol: TCP
可以看到 Deployment 中新增了 DB_PASSWORD 這部分內(nèi)容,使用的 secretKeyRef.name 名稱則為 Secret 的名稱。
更改鏡像
與 Secret 一樣,有一個(gè)自定義指令允許直接從命令行更改鏡像或標(biāo)簽,如果你是通過 CI/CD 來發(fā)布應(yīng)用這將非常有用,如下所示:
$ cd k8s/overlays/prod
$ TAG_VERSION=3.4.5 # (1)
$ kustomize edit set image foo/bar=foo/bar:$TAG_VERSION
這里的 TAG_VERSION 通常是我們的 CI/CD 系統(tǒng)來定義的,上面的命令執(zhí)行后,kustomization.yaml 文件中會(huì)新增一個(gè) images 的屬性,里面包括 newName 和 newTag 兩個(gè)屬性:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: custom-env.yaml
- path: replica-and-rollout-strategy.yaml
- path: database-secret.yaml
secretGenerator:
- literals:
- db-password=12345
name: sl-demo-app
type: Opaque
images:
- name: foo/bar
newName: foo/bar
newTag: 3.4.5
同樣執(zhí)行 build 命令后得到的 Deployment 中的 image 就是 foo/bar:3.4.5。
總結(jié)
通過上面的這些簡(jiǎn)單示例我們可以了解如何利用 Kustomize 的強(qiáng)大功能來定義 Kubernetes 資源文件,甚至無需使用模板系統(tǒng)。我們所做的所有修改文件都將應(yīng)用在原始文件之上,而無需使用花括號(hào)和命令式修改對(duì)其進(jìn)行更改。
Kustomize 中還有很多高級(jí)用法,例如 mixins 和繼承邏輯或其他允許為每個(gè)創(chuàng)建的對(duì)象定義名稱、標(biāo)簽或命名空間的指令,我們后續(xù)再來介紹。