警告:實用警告
作為 Kubernetes 維護者,我們一直在尋找在保持相容性的同時提高可用性的方法。在我們開發功能、分類錯誤和回答支援問題時,我們累積了對 Kubernetes 使用者有幫助的資訊。過去,分享這些資訊的方式僅限於帶外方法,例如發行說明、公告電子郵件、文件和部落格文章。除非有人知道要尋找這些資訊並設法找到它,否則他們將無法從中受益。
在 Kubernetes v1.19 中,我們新增了一項功能,允許 Kubernetes API 伺服器向 API 客戶端發送警告。警告是使用標準 Warning
響應標頭發送的,因此它不會以任何方式更改狀態碼或響應正文。這允許伺服器發送任何 API 客戶端都易於讀取的警告,同時保持與先前客戶端版本的相容性。
kubectl
v1.19+ 在 stderr
輸出中以及 k8s.io/client-go
客戶端程式庫 v0.19.0+ 在日誌輸出中顯示警告。k8s.io/client-go
行為可以按進程或按客戶端覆蓋。
棄用警告
我們使用這項新功能的第一種方式是針對使用已棄用的 API 發送警告。
Kubernetes 是一個大型、快速發展的專案。跟上每個版本中的變更可能令人望而生畏,即使對於全職從事該專案的人來說也是如此。一種重要的變更類型是 API 棄用。隨著 Kubernetes 中的 API 升級到 GA 版本,預發佈 API 版本將被棄用並最終移除。
即使有延長的棄用期,並且棄用包含在發行說明中,它們仍然可能難以追蹤。在棄用期間,預發佈 API 保持功能正常,允許幾個版本過渡到穩定 API 版本。但是,我們發現使用者通常甚至沒有意識到他們依賴於已棄用的 API 版本,直到他們升級到停止提供該版本的發行版本。
從 v1.19 開始,每當向已棄用的 REST API 發出請求時,都會在 API 響應中返回警告。此警告包含有關不再提供 API 的發行版本以及替換 API 版本的詳細資訊。
由於警告源自伺服器,並在客戶端級別被攔截,因此它適用於所有 kubectl 命令,包括高階命令(如 kubectl apply
)和低階命令(如 kubectl get --raw
)。
這有助於受棄用影響的人員了解他們發出的請求已棄用、他們需要多久才能解決問題以及他們應該改用哪個 API。當使用者應用他們沒有建立的 manifest 時,這尤其有幫助,因此他們有時間聯繫作者以請求更新的版本。
我們也意識到使用已棄用 API 的人通常與負責升級叢集的人不是同一個人,因此我們新增了兩個面向管理員的工具,以幫助追蹤已棄用 API 的使用情況並確定何時升級是安全的。
指標
從 Kubernetes v1.19 開始,當向已棄用的 REST API 端點發出請求時,kube-apiserver
進程中會將 apiserver_requested_deprecated_apis
儀表指標設定為 1
。此指標具有 API group
、version
、resource
和 subresource
的標籤,以及指示不再提供 API 的 Kubernetes 發行版本的 removed_release
標籤。
這是一個使用 kubectl
、prom2json 和 jq 的範例查詢,以確定已從 API 伺服器的當前實例請求了哪些已棄用的 API。
kubectl get --raw /metrics | prom2json | jq '
.[] | select(.name=="apiserver_requested_deprecated_apis").metrics[].labels
'
輸出
{
"group": "extensions",
"removed_release": "1.22",
"resource": "ingresses",
"subresource": "",
"version": "v1beta1"
}
{
"group": "rbac.authorization.k8s.io",
"removed_release": "1.22",
"resource": "clusterroles",
"subresource": "",
"version": "v1beta1"
}
這顯示已在此伺服器上請求了已棄用的 extensions/v1beta1
Ingress 和 rbac.authorization.k8s.io/v1beta1
ClusterRole API,並且將在 v1.22 中移除。
我們可以將該資訊與 apiserver_request_total
指標結合起來,以獲取有關向這些 API 發出的請求的更多詳細資訊。
kubectl get --raw /metrics | prom2json | jq '
# set $deprecated to a list of deprecated APIs
[
.[] |
select(.name=="apiserver_requested_deprecated_apis").metrics[].labels |
{group,version,resource}
] as $deprecated
|
# select apiserver_request_total metrics which are deprecated
.[] | select(.name=="apiserver_request_total").metrics[] |
select(.labels | {group,version,resource} as $key | $deprecated | index($key))
'
輸出
{
"labels": {
"code": "0",
"component": "apiserver",
"contentType": "application/vnd.kubernetes.protobuf;stream=watch",
"dry_run": "",
"group": "extensions",
"resource": "ingresses",
"scope": "cluster",
"subresource": "",
"verb": "WATCH",
"version": "v1beta1"
},
"value": "21"
}
{
"labels": {
"code": "200",
"component": "apiserver",
"contentType": "application/vnd.kubernetes.protobuf",
"dry_run": "",
"group": "extensions",
"resource": "ingresses",
"scope": "cluster",
"subresource": "",
"verb": "LIST",
"version": "v1beta1"
},
"value": "1"
}
{
"labels": {
"code": "200",
"component": "apiserver",
"contentType": "application/json",
"dry_run": "",
"group": "rbac.authorization.k8s.io",
"resource": "clusterroles",
"scope": "cluster",
"subresource": "",
"verb": "LIST",
"version": "v1beta1"
},
"value": "1"
}
輸出顯示僅向這些 API 發出讀取請求,並且向監看已棄用的 Ingress API 發出了最多的請求。
您也可以透過以下 Prometheus 查詢找到該資訊,該查詢返回有關向將在 v1.22 中移除的已棄用 API 發出的請求的資訊。
apiserver_requested_deprecated_apis{removed_release="1.22"} * on(group,version,resource,subresource)
group_right() apiserver_request_total
稽核註解
指標是檢查是否正在使用已棄用的 API 以及使用速率的快速方法,但它們不包含足夠的資訊來識別特定的客戶端或 API 物件。從 Kubernetes v1.19 開始,對已棄用 API 的請求的稽核事件包含 "k8s.io/deprecated":"true"
的稽核註解。管理員可以使用這些稽核事件來識別需要更新的特定客戶端或物件。
自訂資源定義
除了 API 伺服器能夠警告有關已棄用 API 的使用之外,從 v1.19 開始,CustomResourceDefinition 可以指示其定義的資源的特定版本已棄用。當向自訂資源的已棄用版本發出 API 請求時,將返回警告訊息,這與內建 API 的行為一致。
CustomResourceDefinition 的作者也可以根據需要自訂每個版本的警告。這允許他們在需要時提供指向遷移指南或其他資訊的指標。
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
name: crontabs.example.com
spec:
versions:
- name: v1alpha1
# This indicates the v1alpha1 version of the custom resource is deprecated.
# API requests to this version receive a warning in the server response.
deprecated: true
# This overrides the default warning returned to clients making v1alpha1 API requests.
deprecationWarning: "example.com/v1alpha1 CronTab is deprecated; use example.com/v1 CronTab (see http://example.com/v1alpha1-v1)"
...
- name: v1beta1
# This indicates the v1beta1 version of the custom resource is deprecated.
# API requests to this version receive a warning in the server response.
# A default warning message is returned for this version.
deprecated: true
...
- name: v1
...
准入 Webhook
准入 webhook 是將自訂策略或驗證與 Kubernetes 集成的主要方式。從 v1.19 開始,准入 webhook 可以返回警告訊息,這些訊息將傳遞給請求 API 的客戶端。警告可以與允許或拒絕的准入響應一起返回。
作為範例,為了允許請求但警告已知無法正常運作的組態,准入 webhook 可以發送此響應。
{
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": "<value from request.uid>",
"allowed": true,
"warnings": [
".spec.memory: requests >1GB do not work on Fridays"
]
}
}
如果您正在實作返回警告訊息的 webhook,以下是一些提示:
- 請勿在訊息中包含 "Warning:" 前綴(該前綴由客戶端在輸出時添加)。
- 使用警告訊息來描述發出 API 請求的客戶端應更正或注意的問題。
- 簡潔扼要;如果可能,請將警告限制為 120 個字元。
准入 webhook 可以透過多種方式使用這項新功能,我期待看到人們提出什麼。以下是一些幫助您入門的想法:
- webhook 實作新增了“抱怨”模式,在該模式下,它們返回警告而不是拒絕,以允許在開始強制執行策略之前嘗試策略以驗證其是否按預期運作。
- “lint”或“vet”風格的 webhook,檢查物件並在未遵循最佳實務時顯示警告。
自訂客戶端處理
使用 k8s.io/client-go
程式庫發出 API 請求的應用程式可以自訂如何處理從伺服器返回的警告。預設情況下,警告會在收到時記錄到 stderr,但此行為可以按進程或按客戶端自訂。
此範例示範如何使您的應用程式的行為類似於 kubectl
,覆蓋全進程的訊息處理以消除重複的警告,並在使用彩色輸出時突出顯示訊息。
import (
"os"
"k8s.io/client-go/rest"
"k8s.io/kubectl/pkg/util/term"
...
)
func main() {
rest.SetDefaultWarningHandler(
rest.NewWarningWriter(os.Stderr, rest.WarningWriterOptions{
// only print a given warning the first time we receive it
Deduplicate: true,
// highlight the output with color when the output supports it
Color: term.AllowsColorOutput(os.Stderr),
},
),
)
...
下一個範例示範如何構建忽略警告的客戶端。這對於操作所有資源類型的元數據(在運行時使用探索 API 動態找到)並且不會從有關特定資源已棄用的警告中受益的客戶端很有用。對於需要使用特定 API 的客戶端,不建議抑制棄用警告。
import (
"k8s.io/client-go/rest"
"k8s.io/client-go/kubernetes"
)
func getClientWithoutWarnings(config *rest.Config) (kubernetes.Interface, error) {
// copy to avoid mutating the passed-in config
config = rest.CopyConfig(config)
// set the warning handler for this client to ignore warnings
config.WarningHandler = rest.NoWarnings{}
// construct and return the client
return kubernetes.NewForConfig(config)
}
Kubectl 嚴格模式
如果您想確保盡快注意到棄用並儘早開始解決它們,kubectl
在 v1.19 中新增了 --warnings-as-errors
選項。當使用此選項調用時,kubectl
會將從伺服器收到的任何警告視為錯誤,並以非零退出代碼退出。
這可以用於 CI 作業中,以將 manifest 應用於當前伺服器,並且需要以零退出代碼通過才能使 CI 作業成功。
未來可能性
現在我們有了一種在上下文中向使用者傳達有幫助資訊的方式,我們已經在考慮其他方法,我們可以利用這種方式來改善人們使用 Kubernetes 的體驗。我們接下來關注的幾個領域是警告有關已知有問題的值,由於相容性原因,我們無法完全拒絕這些值,以及警告有關使用已棄用的欄位或欄位值(例如使用 beta os/arch 節點標籤的選擇器,在 v1.14 中已棄用)。我很高興看到該領域取得進展,繼續使 Kubernetes 更易於使用。