從一個有趣的問題引出很多人都在關注的 Kube.NETes LTS 的問題。
有趣的問題
2019 年,一個名為 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提到了一個有趣的問題,如果一個 kube-apiserver 已經一年沒有重啟過了,那么這個 kube-apiserver 就無法再正常工作了。
issue 作者給出了自己的定位的原因:kube-apiserver 沒有更新自簽的 LoopbackClient 證書相關內容。從下面代碼中可以看到證書過期時間被設置為了 1 年。
// create self-signed cert+key with the fake server.LoopbackClientServerNameOverride and
// let the server return it when the loopback client connects.
certPem, keyPem, err := certutil.GenerateSelfSignedCertKey(server.LoopbackClientServerNameOverride, nil, nil)
if err != nil {
return fmt.Errorf("fAIled to generate self-signed certificate for loopback connection: %v", err)
}
certProvider, err := dynamiccertificates.NewStaticSNICertKeyContent("self-signed loopback", certPem, keyPem, server.LoopbackClientServerNameOverride)
if err != nil {
return fmt.Errorf("failed to generate self-signed certificate for loopback connection: %v", err)
}
---
// GenerateSelfSignedCertKeyWithFixtures creates a self-signed certificate and key for the given host.
// Host may be an IP or a DNS name. You may also specify additional subject alt names (either ip or dns names)
// for the certificate.
//
// If fixtureDirectory is non-empty, it is a directory path which can contain pre-generated certs. The format is:
// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.crt
// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.key
// Certs/keys not existing in that directory are created.
func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, alternateDNS []string, fixtureDirectory string) ([]byte, []byte, error) {
validFrom := time.Now().Add(-time.Hour) // valid an hour earlier to avoid flakes due to clock skew
maxAge := time.Hour * 24 * 365 // one year self-signed certs
注:LoopbackClient 是在 kube-apiserver 中用來訪問自身時使用的,例如 kube-apiserver 在啟動時需要獲取 Service,EndPoint 等信息(AA 中用到了),就用到了這個 LoopbackClient。
同時也給出了用來檢查自己集群相關證書過期時間的方法,可以通過重啟 kube-apiserver 來臨時解決這個問題。
# replace {Master_IP} with your master IP and 6443 with your apiserver port
curl --resolve apiserver-loopback-client:6443:{Master_IP} -k -v https://apiserver-loopback-client:6443/healthz
root@kind-control-plane:/# curl --resolve apiserver-loopback-client:6443:172.17.0.2 -k -v https://apiserver-loopback-client:6443/healthz
* Added apiserver-loopback-client:6443:172.17.0.2 to DNS cache
* Hostname apiserver-loopback-client was found in DNS cache
* Trying 172.17.0.2:6443...
* TCP_NODELAY set
* Connected to apiserver-loopback-client (172.17.0.2) port 6443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=apiserver-loopback-client@1577103676
* start date: Dec 23 11:21:16 2019 GMT
* expire date: Dec 22 11:21:16 2020 GMT
* issuer: CN=apiserver-loopback-client-ca@1577103676
* SSL certificate verify result: self signed certificate in certificate chain (19), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x55d565d9c1d0)
> GET /healthz HTTP/2
> Host: apiserver-loopback-client:6443
> User-Agent: curl/7.65.3
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 200
< cache-control: no-cache, private
< content-type: text/plain; charset=utf-8
< x-content-type-options: nosniff
< content-length: 2
< date: Wed, 23 Dec 2020 14:29:30 GMT
<
* Connection #0 to host apiserver-loopback-client left intact
Feature Or Bug?
這個問題發生的概率比較低,需要讓 kube-apiserver 持續運行 1 年才會觸發,當然你也可以通過調整服務器時間到一年后來馬上讓它復現。
社區大佬也是做出了回復,在當時 k8s 每 3 個月發布一個 minor 版本,同時遵循「N-2 支持政策」,這意味著僅 3 個最新的 minor 版本(N、N-1 和 N-2)會獲得安全和錯誤修復。也就意味著如果集群管理員按照社區規范管理 k8s 集群的話就不會出現運行了 1 年以上的 k8s 組件,也就不會觸發這個問題。
同時也提到了有一個新的提案正在討論中,用來把支持的版本從過去 9 個月提升到 12 ~ 14 個月,但在當時這也僅僅還只是個提案。最終這個 issue 并沒有被當做是 bug,至今社區版本中此處仍然硬編碼了個 1 年的過期時間。
而神奇的是,在距離上面 issue 被提到的四年后的今天,如果你上網搜索一下這個問題的話,你會發現在阿里云官網文檔[2]中也提到了這個問題。
API Server是ACK集群管控面的核心組件,其中內置了用于其內部LoopbackClient[3]服務端工作的證書。該證書在社區版本中有效期為1年[4]且無法自動輪轉,只有當API Server Pod發生重啟時,才會自動輪轉更新。社區暫無延長該證書有效期的計劃,更多信息請參見#86552。
考慮到不同用戶的運維習慣,容器服務 Kubernetes 版于近期調整了該內置證書的默認過期時間,修改后有效期為10年。
影響范圍
API Server內置LoopbackClient證書的有效期為1年的ACK托管集群和ACK專有集群。
- 在2023年03月15日之前創建的ACK集群,API Server內置LoopbackClient證書的有效期為1年。
- 在2023年03月15日及以后新建或升級至1.20.11或以上版本的ACK集群,API Server內置LoopbackClient證書的有效期為10年,不受影響。
阿里云文檔中說了自 2023 年 03 月 15 日之后的版本已經設置證書有效期為 10 年,也就是說阿里云其實是修改了這部分源碼的。而且還特意在最后加了這么一句:
重要:對于短期無法操作升級的ACK專有集群,請登錄集群的所有Master節點,手動重啟API Server。
阿里云作為國內最大的公有云廠商,最后也不得不修改代碼去解決這個問題,從側面也說明了另一個問題,即使已經 2023 年(當時)了,還有很多公司使用的 k8s 版本是遠遠落后于官方支持的版本的,甚至還存在一些已經運行了超過一年或者快要超過一年的集群。也不知道是該替阿里云的穩定性高興(足以支持 k8s 跑一年都不重啟),還是感慨一個四年前的問題在四年后終于得到了大家的關注。amazing and iteresting ~
Kubernetes LTS
一個如此復雜的系統,最初的支持周期連 1 年都不到,需要用戶每年一升級,升級一次工作量也大,這絕對是對用戶心智的一種折磨。從結果看,確實也是存在不少的用戶在使用過程中并沒有完全遵循社區規范。
從提案1498-kubernetes-yearly-support-period[5] 被接受后,k8s 版本的支持時間從之前的 9 個月調整到了 14 個月,包含 12 個月的支持期和 2 個月的升級周期,v1.19 是第一個享受此待遇的版本。從提案KEP-2572: Defining the Kubernetes Release Cadence[6] 被接受后,也就是 2020 年(有一部分新冠的原因),調整發版(minor)周期從每年四次到每年三次,v1.22 是第一個按這個節奏發版的版本。
- Support Policy during the first 12 months.
- Same as the current 9 months policy.
- Support Policy during the final +2 months.
- CVE assigned by Product Security Committee initiated to release branch by Product Security Committee
- Cherry-pick of upgrade scenario bug fix Approved by owning SIG and Patch Release Team
- Only critical security patches and upgrade blocking fixes, i.e.:
但從結果看,無論是 9 還是 14,這個時間對于用戶來說仍然不夠長(要不然阿里云就沒必要在 2023 年還專門提上面問題了)。拿這個時間和眾多的 linux 發行版 LTS 對比看的話,這個時間相當短了。
網上也不斷有人為此發聲:Why Kubernetes needs an LTS?[7]。
原因如下:
第一,Kubernetes 是一個復雜的容器編排系統,由許多不同的組件和模塊組成。這些組件和模塊需要經過持續地維護和更新,以確保其安全性和穩定性。通過提供 LTS 版本,可以為用戶提供一個穩定的基礎,使他們能夠在長期內使用 Kubernetes 而不必頻繁升級。
其次,許多組織在使用 Kubernetes 時會構建復雜的應用程序和基礎架構。這些應用程序和基礎架構可能依賴于特定版本的 Kubernetes,并且可能需要進行大量的測試和驗證才能在新版本上運行。通過提供 LTS 版本,可以確保這些組織能夠在長期內維持其應用程序和基礎架構的穩定性,而不必擔心由于升級到新版本而導致的不兼容性和故障。
此外,許多組織可能面臨著合規性和監管要求。這些要求可能要求他們使用特定版本的軟件,并且在一段時間內保持該版本的支持。通過提供 LTS 版本,Kubernetes 可以滿足這些合規性和監管要求,使組織能夠在其環境中使用 Kubernetes 而不必擔心違反規定。
最后,對于那些不具備大規模升級和遷移能力的組織來說,LTS 版本可以提供更長時間的支持和穩定性。這些組織可能沒有足夠的資源和時間來頻繁升級和遷移他們的應用程序和基礎架構。通過提供 LTS 版本,Kubernetes 可以幫助這些組織保持其系統的穩定性和可靠性,而不必承擔頻繁升級的風險和成本。
社區曾在 2019 年 2 月成立了 LTS 工作組,上面的第一個提案就是相關的產出,最終在 2020 年 10 月關閉了這個工作組。直到 2023 年 4 月又重新評估 LTS 的需求,7 月份正式重啟了 LTS 工作組,截至目前并沒有一些實質性的進展。詳情可以參考 slack[8] 或者 google 會話[9]。
拓展
雖然官方 LTS 工作組還沒有實質進展,但 DaoCloud 有一個項目專門用來維護歷史 k8s 版本,參考 klts[10],其關注點主要是 CVE 和 Critical issues。
- CVEs that were cherry-pick approved and merged after pull requests[10]
- CVEs that were found and fixed recently[11]
- Kubernetes-related CVEs that were found and listed on cve.org recently[12]
總結
k8s 14 個月的維護周期顯然無法滿足很多用戶的需求,社區也清楚存在這個問題,也有專門的 LTS 工作組在跟進,在不久的將來,或許能看到 kubernetes LTS 的落地。
最后還想多說一點,選擇使用哪個版本,升不升級,什么時間升級,并沒有一個絕對的對錯,適合自己的才是最好的,提防角色互換,從玩家變成被玩的人,但無論如何還是要對所負責的東西有絕對的把控力。對待一些公眾號之間對某些技術理念的爭執(互噴)也一樣,單純意識形態上的爭論對指導具體工作沒有多大的意義。不可否認里面一些觀念還是挺好的,也容易被人接受,但重要的還是要結合自己的實際場景,多思考,可以看看聽聽,但千萬不要著相了。
>>>>參考資料
- [1]issue#86552: https://Github.com/kubernetes/kubernetes/issues/86552
- [2]阿里云文檔: https://www.alibabacloud.com/help/zh/ack/product-overview/validity-period-change-for-api-server-internal-certificates
- [3]code1:https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/server/options/serving_with_loopback.go#L52-L61
- [4]code2:https://github.com/kubernetes/kubernetes/blob/69c3b23abdbda53d14e14afc2af2bdfef23ac7b0/staging/src/k8s.io/client-go/util/cert/cert.go#L40C7-L40C19
- [5]1498-kubernetes-yearly-support-period: https://github.com/kubernetes/enhancements/tree/master/keps/sig-release/1498-kubernetes-yearly-support-period
- [6]2572-release-cadence:
- https://github.com/kubernetes/enhancements/tree/master/keps/sig-release/2572-release-cadence
- [7]Why Kubernetes needs an LTS: https://matduggan.com/why-kubernetes-needs-an-lts/
- [8]slack#lts: https://kubernetes.slack.com/messages/wg-lts
- [9]google#lts: https://groups.google.com/a/kubernetes.io/g/wg-lts
- [10]ktls: https://github.com/klts-io/kubernetes-lts
- [11]cve pr:
- https://github.com/kubernetes/kubernetes/pulls?q=is%3Apr+is%3Amerged+label%3Acherry-pick-approved+CVE
- [12]cve fixed:
- https://www.cvedetails.com/vulnerability-list/vendor_id-15867/product_id-34016/Kubernetes-Kubernetes.html
- [13]cve k8s: https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=Kubernetes
作者丨Kaku
來源丨公眾號:云原生散修(ID:cloudnative_sanxiu)