前言
- 有時在渲染 Kubernetes 的 yaml 檔時,不一定每個環境都有裝 helm,或其他的 templating 工具 …
- 如果又不想在當前環境裝額外的工具,只想用簡單粗暴的內建指令做到渲染變數的效果,這時就要善用
envsubst
這個指令!
說明
envsubst
是一個常見指令,可以用來把檔案中預留的變數,用「環境變數」或「指定變數」的「值」取代,然後將結果輸出至 stdout。
安裝
如果發現 command not found
1
2
3
|
envsubst
bash: envsubst: command not found
|
就代表環境沒有這個指令,需要安裝 gettext
這個套件。
不同系統的安裝方式不同 [1]:
系統 |
安裝指令 |
OS X |
brew install gettext |
Debian |
apt-get install gettext-base |
Ubuntu |
apt-get install gettext-base |
Alpine |
apk add gettext |
Arch |
Linux pacman -S gettext |
CentOS |
yum install gettext |
Fedora |
dnf install gettext |
範例
指定變數
假設我有一個檔案,例如 demo.svc.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
|
apiVersion: v1
kind: Service
metadata:
name: ${MY_SERVICE_NAME}
spec:
ports:
- name: http
port: ${MY_PORT_NUMBER}
protocol: TCP
targetPort: ${MY_PORT_NUMBER}
selector:
app: ${MY_SERVICE_NAME}
type: ClusterIP
|
我們可以把這個檔案中的 ${MY_SERVICE_NAME}
、${MY_PORT_NUMBER}
這兩個變數,用指定變數取代
1
|
<demo.svc.yaml MY_SERVICE_NAME="demo-service" MY_PORT_NUMBER=80 envsubst
|
就會順利得到渲染後的結果:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
apiVersion: v1
kind: Service
metadata:
name: demo-service
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: demo-service
type: ClusterIP
|
Note
- 當然你可以用
cat
指令:
1
|
cat demo.yaml | MY_SERVICE_NAME="demo-service" MY_PORT_NUMBER=80 envsubst
|
但是 ShellCheck - SC2002 會建議你不要這樣做
- 為了排版好閱讀起見,我會把指令寫成多行:
1
2
3
4
|
<demo.svc.yaml \
MY_SERVICE_NAME="demo-service" \
MY_PORT_NUMBER=80 \
envsubst
|
- 如果你習慣把 stdin 放在後面,也可以:
1
2
3
4
|
MY_SERVICE_NAME="demo-service" \
MY_PORT_NUMBER=80 \
envsubst \
<demo.svc.yaml
|
環境變數
如果有些變數非常常用到、或覺得每次都要指定變數太麻煩的話,可以直接 export
成環境變數:
1
2
|
export MY_SERVICE_NAME="demo-service"
export MY_PORT_NUMBER=80
|
然後就可以直接用 envsubst
渲染:
1
|
<demo.svc.yaml envsubst
|
一樣可以得到渲染後的結果:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
apiVersion: v1
kind: Service
metadata:
name: demo-service
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: demo-service
type: ClusterIP
|
保留錢字號 $
?
有的時候,我就是單純想要保留錢字號 $
,讓渲染之後的結果可以再給其他工具用、或是某些變數我不想被 envsubst
渲染,例如這種情況:
- Kubernetes deployment 的
arg
,我就是要讓指定的 container command
取得 env
裡面的變數 NAMESPACE
、同時要依據 MY_DOMAIN
的值渲染;但要讓 NAMESAPCE
維持原狀,不能被 envsubst
渲染:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 原本的寫法,但想要嘗試保留 "${NAMESPACE}"
- args:
- /bin/k3s server --disable-agent --tls-san=${NAMESPACE}.${MY_DOMAIN}
command:
- sh
- -c
env:
- name: NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
|
(錯誤示範) 該怎麼處理,使用跳脫字元?像是 \${NAMESPACE}
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 省略上面的部分
- args:
- /bin/k3s server --disable-agent --tls-san=\${NAMESPACE}.${MY_DOMAIN}
command:
- sh
- -c
env:
- name: NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
|
但是
1
|
<test.pod.yaml MY_DOMAIN="example.com" envsubst
|
渲染出來卻會是
1
2
3
4
5
6
7
8
9
10
11
|
- args:
- /bin/k3s server --disable-agent --tls-san=\.example.com
command:
- sh
- -c
env:
- name: NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
|
因為 envsubst
會先找到 ${NAMESPACE}
,但因為我們沒有指定 NAMESPACE
的值,
所以就會先被渲染成空字串,然後才剩下原有的跳脫符號 \
。
用空變數輔助
目前看到 [2] 我認為最好的方式,就是用兩個 $
符號配上一個「空變數」(根本沒有這個變數) 來完成渲染,像是這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 省略上面的部分
- args:
- /bin/k3s server --disable-agent --tls-san=$${MY_NULL_VAR}{NAMESPACE}.${MY_DOMAIN}
command:
- sh
- -c
env:
- name: NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
|
這是因為 envsubst
會先渲染 ${MY_NULL_VAR}
– 變成空值,
然後剩下來前面的 $
符號、以及後面的大括號 {NAMESPACE}
,被保留下來剛好放在一起。
這樣,
1
|
<test.pod.yaml MY_DOMAIN="example.com" envsubst
|
就能產生理想的結果:
1
2
3
4
5
6
7
8
9
10
11
|
- args:
- /bin/k3s server --disable-agent --tls-san=${NAMESPACE}.example.com
command:
- sh
- -c
env:
- name: NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
|
REF
- https://www.thegeekdiary.com/envsubst-command-not-found/
- https://stackoverflow.com/questions/24963705/is-there-an-escape-character-for-envsubst/65652760#65652760