作者 | Andy
來源 | 進擊雲原生
容器在主機的核心上運行,並獲得時鐘,但時區不是來自核心,而是來自使用者空間。在大多數情況下,默認使用協調世界時 (UTC)。
時區的不一致,會帶來很多困擾。即使程式碼與時區無關,但容器日誌與系統日誌時間相關聯排查問題也會讓人頭疼。一些應用程序使用機器的時區作為默認時區,並希望使用者設置時區。當集群中容器的時區不一致時,管理會很不容易。

k8tz
k8tz
是開源項目,請查看:github.com/k8tz/k8tz
k8tz
是一個 Kubernetes 准入控制器和一個將時區注入 Pod 的 CLI 工具。可以用作手動工具來自動轉換 Deployment 和 Pod 可以作為準入控制器安裝並使用註釋來完全自動化創建 Pod 的過程。
k8tz
可以使用hostPath
的方式,或者將emptyDir
注入initContainer
並用 TZif(時區資訊格式) 檔案填充卷。然後將emptyDir
掛載到 Pod 每個容器的/etc/localtime
和/usr/share/zoneinfo
。為了確保所需的時區有效,它向所有容器添加了TZ
環境變數。

安裝
用 Helm 安裝k8tz
准入控制器:
helm repo add k8tz https://k8tz.github.io/k8tz/
helm install k8tz k8tz/k8tz --set timezone=Asia/Shanghai
查看 Pod 狀態、Mutatingwebhookconfigurations、Service 等資源是否正常:
# kubectl get mutatingwebhookconfigurations.admissionregistration.k8s.io k8tz
NAME WEBHOOKS AGE
k8tz 1 31m
# kubectl get svc -n k8tz
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
k8tz ClusterIP 10.233.212.11 443/TCP 31m
# kubectl get pod -n k8tz
NAME READY STATUS RESTARTS AGE
k8tz-59bb7f7cbd-5dzmq 1/1 Running 0 31m

測試
現在可以創建 Pod,不需要任何額外配置:
# kubectl run -i -t ubuntu --image=ubuntu:21.04 --restart=OnFailure --rm=true --command date
Defaulted container "ubuntu" out of: ubuntu, k8tz (init)
Wed Jun 15 14:11:53 CST 2022
pod "ubuntu" deleted
此時的 Pod yaml 如下,環境變數 TZ 使用安裝時指定的 Asia/Shanghai,以及注入了 initContainers、volumeMounts、volumes 等配置:
apiVersion: v1
kind: Pod
metadata:
labels:
run: ubuntu
name: ubuntu
namespace: default
spec:
containers:
- command:
- date
env:
- name: TZ
value: Asia/Shanghai
image: ubuntu:21.04
imagePullPolicy: IfNotPresent
name: ubuntu
volumeMounts:
- mountPath: /etc/localtime
name: k8tz
readOnly: true
subPath: Asia/Shanghai
- mountPath: /usr/share/zoneinfo
name: k8tz
readOnly: true
initContainers:
- args:
- bootstrap
image: quay.io/k8tz/k8tz:0.5.0
imagePullPolicy: IfNotPresent
name: k8tz
volumeMounts:
- mountPath: /mnt/zoneinfo
name: k8tz
volumes:
- emptyDir: {}
name: k8tz
還可以指定 annotations,例如k8tz.io/timezone=Europe/London
選擇 pod 的時區:
# kubectl run -i -t ubuntu --image=ubuntu:21.04 --restart=OnFailure --rm=true --command date --annotations k8tz.io/timezone=Europe/London
Defaulted container "ubuntu" out of: ubuntu, k8tz (init)
Wed Jun 15 07:13:42 BST 2022
pod "ubuntu" deleted
或者使用註解k8tz.io/inject
禁用時區注入 Pod :
# kubectl run -i -t ubuntu --image=ubuntu:21.04 --restart=OnFailure --rm=true --command date --annotations k8tz.io/inject=false
Wed Jun 15 06:14:47 UTC 2022
pod "ubuntu" deleted
如果你想使用hostPath
而不是initContainer
方式注入時區配置,可以使用k8tz.io/strategy
註解:
# kubectl run -i -t ubuntu --image=ubuntu:21.04 --restart=OnFailure --rm=true --command date --annotations k8tz.io/strategy=hostPath
Wed Jun 15 14:15:26 CST 2022
pod "ubuntu" deleted
annotations 也可以在名稱空間中指定,並影響在名稱空間中創建的所有 pod。下面創建一個 test-k8tz namespace 用於測試:
# k create ns test-k8tz
namespace/test-k8tz created
# k annotate ns test-k8tz k8tz.io/strategy=hostPath
namespace/test-k8tz annotated
# k annotate ns test-k8tz k8tz.io/timezone=Europe/London
namespace/test-k8tz annotated
上面將策略設置為 hostPath 注入方式。因為安裝 k8tz 時默認時區已經設置為 Asia/Shanghai,所以這裡將 test-k8tz namespace 時區設置為 Europe/London,方便區分。
此時創建的 Pod 不需要加任何註解:
# kubectl run -n test-k8tz -i -t ubuntu --image=ubuntu:21.04 --restart=OnFailure --command date
Wed Jun 15 07:19:48 BST 2022
此時創建的 Pod yaml 如下,此時用的是 hostPath 注入方式:
apiVersion: v1
kind: Pod
metadata:
labels:
run: ubuntu
name: ubuntu
namespace: test-k8tz
spec:
containers:
- command:
- date
env:
- name: TZ
value: Europe/London
image: ubuntu:21.04
imagePullPolicy: IfNotPresent
name: ubuntu
volumeMounts:
- mountPath: /etc/localtime
name: k8tz
readOnly: true
subPath: Europe/London
- mountPath: /usr/share/zoneinfo
name: k8tz
readOnly: true
volumes:
- hostPath:
path: /usr/share/zoneinfo
type: ""
name: k8tz

結論
Kubernetes 中的時區問題有多種解決方案,這些解決方案可以手動實現,但在此過程中存在一些挑戰和限制。
使用k8tz
可以自動執行該過程,確保系統中所有元件的時區一致,並且所有元件都可以訪問有關不同時區的資訊。並且無需額外設置或更改現有資源即可工作,即使在節點上沒有所需檔案時也是如此。