【K8S類型系統】一文梳理 K8S 各類型概念之間的關系(GVK/GVR/Object/Schema/RestMapper)

參考

k8s 官方文檔
  • https://kubernetes.io/zh-cn/docs/reference/
  • https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/
重點
  • Kubernetes源碼學習-kubernetes基礎數據結構 - 知乎 重點

  • Kubernetes類型系統 | 李乾坤的博客 重點

  • k8s源碼學習-三大核心數據結構_51CTO博客_c++數據結構 重點

  • Kubernetes源碼分析(二)----資源Resource_kubernetes resources-CSDN博客 重點

  • 淺說Kubernetes中API的概念_kubernetes api-CSDN博客

  • Kubernetes API Server源碼學習(二):OpenAPI、API Resource的裝載、HTTP Server具體是怎么跑起來的?_apiserver openapi-CSDN博客 重點

  • Kubernetes API 概念 | Kubernetes

  • 3.1.3 訪問kubernetes REST API · Kubernetes Documentation

  • Kubernetes源碼分析(二)----資源Resource_kubernetes resources-CSDN博客

  • K8s 的核心是 API 而非容器(二):從開源項目看 k8s 的幾種 API 擴展機制(2023) 重點

  • 【k8s基礎篇】k8s基礎2之GVK與GVR-CSDN博客

  • Kubernetes API Server源碼學習(二):OpenAPI、API Resource的裝載、HTTP Server具體是怎么跑起來的?_apiserver openapi-CSDN博客 重點

  • Kubernetes源碼開發之旅三:API Server源碼剖析_嗶哩嗶哩_bilibili blibli 視頻教學,不錯

  • Kubernetes API Server handler 注冊過程分析 | 云原生社區(中國) 重點

  • 源碼解析:K8s 創建 pod 時,背后發生了什么(三)(2021)

  • 一文讀懂 Kubernetes APIServer 原理 - k8s-kb - 博客園

  • 【k8s基礎篇】k8s基礎3之接口文檔_k8s接口文檔-CSDN博客

  • 【k8s基礎篇】k8s基礎2之GVK與GVR_gvk gvr-CSDN博客

次要
  • REST: Part 1 - HTTP API 設計思路 - ZengXu’s BLOG
  • kubernetes官方案例sample-controller詳解
  • Kubernetes API Server handler 注冊過程分析 | 云原生社區(中國)
  • 面向API server CRUD的探索
  • K8S中為什么需要Unstructured對象
  • Kubernetes編程——client-go基礎—— 深入 API Machinery —— Golang 類型轉換為 GVK、GVR 和 HTTP 路徑,API Machinery概覽 - 左揚 - 博客園
  • Kubernetes CRD 系列:Api Server 和 GVK?
  • Kubernetes CRD 系列:Client-Go 的使用
  • kubernetes二次開發系列(1):client-go
  • Kubernetes源碼 - 隨筆分類 - 人艱不拆_zmc - 博客園
  • k8s中的所有api-resources類型簡介 - 知乎
  • Kubernetes的Group、Version、Resource學習小記 - 掘金
  • sample-apiserver分析 - 簡書
  • kube-apiserver代碼分析 - API多版本初探 - 劉達的博客
  • 理解 Kubernetes 對象 | Kubernetes學習筆記
  • Kubernetes 中的資源對象 · Kubernetes 中文指南——云原生應用架構實戰手冊
  • 從 Kubernetes 中的對象談起 - 面向信仰編程
  • 理解Kubernetes的RBAC鑒權模式-騰訊云開發者社區-騰訊云
  • Kubectl exec 的工作原理解讀 - 米開朗基楊 - 博客園
  • Kubernetes RESTMapper源碼分析 - 人艱不拆_zmc - 博客園

理解 Restful API(簡單了解一下)

  • 理解RESTful架構 - 阮一峰的網絡日志

  • 怎樣用通俗的語言解釋REST,以及RESTful? - 知乎

  • 什么是REST風格? 什么是RESTFUL?(一篇全讀懂)_rest風格和restful風格區別-CSDN博客

REST之所以晦澀難懂,是因為前面主語(Resource )被去掉了。
全稱是: Resource Representational State Transfer。
指的是客戶端通過操作資源的表現形式(Representation)來實現狀態(State)的轉移。這個概念強調了在 RESTful 架構中,客戶端通過與資源的交互來實現應用程序狀態的轉移,而不需要服務器端保持客戶端的狀態信息分解開來講解:
Resource:資源,在 REST 中指的是網絡中的任何實體,可以是一個文檔、一張圖片、一個視頻、一個人員、一個部門等等。每個資源都有一個唯一的標識符(URI或URL, 通常是 URL,這兩個可以認為是一個東西,URL 是 URI 的子集)用于標識和定位。;
Representational:資源的呈現形式,通常是通過某種媒體類型(如 JSON、XML、HTML 等)來表示資源的數據。客戶端通過獲取資源的表現形式,并在需要時對其進行操作,來實現狀態的轉移;
State Transfer:狀態變化。指的是客戶端通過與資源的交互,改變了資源的狀態。這種狀態轉移通常通過標準的 HTTP 方法(如 GET、POST、PUT、DELETE 等)來實現,每個 HTTP 方法都對應了一種不同的狀態轉移操作。Rest 是一種架構風格,就是一種設計思想,其落地的方案需要依托底層傳輸協議,一般采用 HTTP 協議,其形成的 API 也就叫做 Restful API(稱之為 Rest 風格 API)

簡單總結(說人話):

  • 通過操作資源的表現形式來實現狀態的轉移

    • 向 URL (資源)發送請求,請求的內容(一般為 json 形式,表現形式),來獲取服務端存儲的對象內容或更新服務端存儲的內容(狀態的轉移,若是 GET 請求就是獲取,POST 或 PUT 就是更新或新建,DELETE 就是刪除)
  • 你會想,上面描述不就是正常發起一個 http 請求嘛( 請求某個 URL,請求內容就是 Body 為 json 形式)

    • 其實這么理解沒錯,但是相對于以往 http 有些不同
    • REST 強調兩個重要概念【資源和狀態轉移】,資源可以理解為 URL,狀態轉移可以理解依靠HTTP 方法(如 GET、POST、PUT、DELETE 等)來實現
  • 下面舉例

    • # 1. 以往 API 設計
      # 獲取 id=3 的文件
      POST 127.0.0.1:8080/unrestful/getFile?id=3
      # 獲取所有文件
      POST 127.0.0.1:8080/unrestful/getAllFiles
      # 刪除 id=3 的文件
      POST 127.0.0.1:8080/unrestful/deleteFile?id=3
      # 更新 id=3 的文件
      POST 127.0.0.1:8080/unrestful/updateFile?id=3# 2. Restful API 設計
      # 獲取 id=3 的文件
      GET 127.0.0.1:8080/restful/files?id=3
      # 獲取所有文件
      GET 127.0.0.1:8080/restful/files
      # 刪除 id=3 的文件
      DELETE 127.0.0.1:8080/restful/files?id=3
      # 更新 id=3 的文件
      POST 127.0.0.1:8080/restful/files?id=3# 總結
      # 1. UnRestful API,將資源和對資源的操作耦合在一起,只采用 POST HTTP 操作,因此 URL 很復雜
      # 2. Restful API,將資源和對資源的操作解耦,用不同 HTTP 方法(GET/POST/UPDATE)來代表對資源的不同操作,URL僅代表資源,因此更加清晰易懂
      # 3. 當程序員調用 UnRestful API 時,需要記住資源的不同操作方法(getFile、getAllFiles、deleteFile、updateFile),若是新增了一個對象,如 Student,同時 API 設計不規范的話(如 getAllStudents 寫為 getStudents,getFile 寫成 fetchStudent),那會更增加理解、記憶、及編程難度
      # 4. 當程序員調用 Restful API 時,只需要記住 HTTP 方法(GET/POST/UPDATE)和對應的資源名稱(files),若新增了 Students,那么只需要記住新增的名稱 students(注意,一般在 URL 中都是小寫字母)
      
  • 總結,記住這幾句話

    • 看 URL 就知道要什么(資源)
    • 看 HTTP Method 就知道干什么(對資源的操作)
    • 看 HTTP Status Code 就知道結果如何(操作后的結果)
  • 看了上面內容,能理解 URL 就是資源(Resource)就可以,這個有助于理解下面 K8S 中的 Resource

理解 K8S 各資源術語及概念

總覽

簡稱全稱(或翻譯)作用
Kind資源類型用于確定創建哪種資源,
并結合 Group、Version,形成 GVK 在 Schema 中尋找對應的數據結構,
從而進行資源對象的創建
Object資源對象GVK 創建的存儲在 etcd 中的資源實體,稱之為資源對象
ResourceAPIResource
(接口資源)
Kind 的小寫字母復數形式,該結構具有多種屬性字段
(如是否具有 Namespace 概念,如 Pod 具有,ClusterRole 不具有)
主要用于結合 Group、Version,形成 GVR ,從而生成資源 URI (HTTP Path、URL)
VersionAPIVersion
(接口版本)
用于管理 Resource 和 Kind 資源,相當于版本管理
Resource 和 Kind 資源可以多個 Version,如 v1aplha1、v1beta1、v1 等,
相當于版本管理,每個版本可能在上一版本新增了某些屬性(字段)
GroupAPIGroup
(接口組)
用于管理 Resource 和 Kind 資源,相當于功能職責管理
應該出于這種考慮,Resource 和 Kind 有多種功能,同種或類似功能的放在一個 Group(比如認證相關放在rbac.authorization.k8s.io Group 中,網絡相關放在 networking.k8s.io Group 中,部署相關放在 apps 組中等)
因此形成了如此組織結構:Group --> Version --> Kind 或 Resource
apiserver串聯上面概念apiserver 可以理解為【 http Server】,那么就需要 http path --> handler 的映射關系
外部只有知道 http path (GVR)才能發起請求(如創建、更改、刪除等),可以說認為此 http path 就是 Resource(可以查看上面 Restful API 介紹),之后 handler 收到請求后,進行后來處理(此處是內部邏輯 GVK),創建對應的資源對象實體(Object)進行存儲
以 Pod 創建說明此過程
0. 首先 apiserver 預先注冊了 http path 與 handler 的對應關系,然后對外提供服務
1. 用戶提交 Pod yaml,創建 Pod,其中制定了 GVK
2. Pod yaml 相當于請求的 body 內容,提交到哪個 http path 呢?
首先通過 yaml 獲取 GVK,然后通過 RestMapper 機制(k8s內部邏輯)轉換為 GVR,之后將 GVR 轉換為 http path
3. 之后該 http path 對應的 handler 該處理了,首先通過 GVK 查詢 Schema (k8s 內部的注冊表,記錄了 GVK 與資源數據結構的對應關系)知道到此次創建的是 Pod Struct,而不是 Deployment,之后將 Pod yaml 中的信息填寫到此 Pod Struct 字段中
4. 之后需要持久化,將該 Pod Struct 進行持久化處理,存入到 etcd 數據庫中,該存儲實體稱之為 Object
  • kube-apiserver作為整個Kubernetes集群操作etcd的唯一入口,負責Kubernetes各資源的認證&鑒權、校驗以及CRUD等操作,提供RESTful APIs,供其它組件調用:
  • 一文讀懂 Kubernetes APIServer 原理 - k8s-kb - 博客園

在這里插入圖片描述

  • Create 請求的流程可以總結為下圖
  • Kubernetes API Server handler 注冊過程分析 | 云原生社區(中國)
  • 在這里插入圖片描述

對象(OBJECT) 和 資源(RESOURCE) 的區別

  • K8S - API 對象 - 潛水滴海軍的blog

  • RESTful API 中的基本概念是資源,每個資源都分配有唯一標識它的 URI 或統一資源標識符(或稱之為 http path 、http 端點、URL,本文中以下這幾個名稱表達意思相同)。

例如,在 Kubernetes API 中,應用程序部署由部署資源表示。

集群中所有部署的集合是在/api/v1/deployments. 當您使用該 GET 方法向此 URI 發送 HTTP 請求時,您會收到一個列出集群中所有部署實例的響應。

每個單獨的部署實例也有自己唯一的 URI,通過它可以對其進行操作。因此,單個部署作為另一個 REST 資源公開。您可以通過向資源 URI 發送 GET 請求來檢索有關部署的信息,并且可以使用PUT請求對其進行修改。

在這里插入圖片描述

因此,一個對象可以通過多個資源公開。如圖所示:

  • 當您查詢 deployments 資源時(/apis/apps/v1/namespces/myns/deployments),命名的 Deployment 對象實例 mydeploy 作為集合元素返回
  • 當您直接查詢單個資源 URI 時(/apis/apps/v1/namespces/myns/deployments/mydeploy),則作為單個對象返回。

在某些情況下,資源根本不代表任何對象。

這方面的一個例子是:

Kubernetes API 允許客戶端驗證主體(人或服務)是否被授權執行 API 操作的方式。這是通過向資源提交POST請求到 /apis/authorization.k8s.io/v1/subjectaccessreviews 來完成的。接口響應表明了主體是否被授權了,執行請求正文中指定的操作。這里的關鍵是 POST 請求沒有創建任何對象。

上述例子表明,資源對象 并不是完全相同的。如果你熟悉關系型數據庫,你可以把 資源對象 比作 視圖資源 就是你與對象進行交互的 “視圖”。

用故事理解 GVK/GVR/Object/Schema/RestMapper

現在有無數個硬盤,用戶需要創建文件存儲到這些硬盤中,目前由如下規則

  • 每個硬盤命名為 Group 名,
  • 用戶通過 Kind 指定創建文件的種類(Kind 為了便于識別,都采用首字母大寫,如 Sh、Doc、Docx、Ppt、Txt、Json 文件等,對應的文件后綴為 sh、doc、docx、ppt、txt、json等,)

現在假如有兩個硬盤,

  • group1 硬盤(支持Kind 為 Sh、Doc、Txt )和 group2(支持 Kind 為 Sh、Ppt),
  • 這兩個硬盤都具有 Kind 為 Sh 的種類,就是都能創建 sh 文件(不過這兩個硬盤的 sh 用處不一樣,因此文件權限不一樣,【group1 中的 sh 文件可以執行,group2 中的 sh 文件不能執行】),
Group、Version、Kind 概念的引入

Group 表示功能職責管理,不同職責功能的 Kind 放入到不同 Group 中

Version 表示 版本管理

Kind 表示 資源的類型(創建何種類型資源)

  • 兩個硬盤都具有 Kind 為 Sh 的種類,那若此時創建 Kind=Sh 的名為 test1 文件,該存儲到哪個硬盤中?
  • 所以引入 Group 概念,創建時候應該說清出,創建【可以執行的】 sh 文件(即指定 Group=group1、Kind=Sh);但因為 group1 有多個 Kind (Sh、Doc、Txt),所以創建一個 shs 文件夾( Kind 小寫字母復數形式,表示保存此類型文件,且數量較多),所以該 sh 文件目前的存儲路徑應該為 group1硬盤\shs\test1.sh
  • 但同時我們還有個規劃,后續 Kind=Sh 的文件存儲到 group1 中會自動加上水印,因此我們將目前這個無水印版本稱之為 v1alpha1 版本,后續有水印版本稱之為 v1 版本,那怎么區分呢?
    • 答:在 Kind 的上一級創建 Version (版本)文件夾,無水印版本創建 v1alpha1 文件夾,有水印版本創建 v1 文件夾,所以 test1.sh 對應的文件夾路徑應改為 group1硬盤\v1alpha1\shs\test1.sh
  • 那就帶來一個問題,若現在創建一個【可執行的、有水印的、Kind=Sh】名為 test2 的文件,只是指定 Group=group1 和 Kind=Sh ,也無法確定該 sh 該存儲到哪個文件夾中,所以引入 Version 的概念,指定【Group=group1、Version=v1、 Kind=Sh】,這樣便可以直到存儲路徑應為group1硬盤\v1\shs ,最后的文件路徑為group1硬盤\v1\shs\test2.sh
Namespace 概念引入

Namespace 用于 資源隔離

上面需求剛解決完成,又來個需求,user1 用戶創建的 sh 文件想要和 user2 創建的 sh 文件分隔開,而現在所有用戶創建【可執行的 Group=group1、有水印的Version=v1、Kind=Sh】,都存儲在group1硬盤\v1\shs 路徑下,沒有隔離性,user1 分不清這些文件中哪個是自己創建的,哪個是 user2 創建的,比如 user1 和 user2 都創建個【可執行的 Group=group1、有水印的Version=v1、Kind=Sh】名為 test3 的文件,就會沖突

  • 為了解決這個問題,我們提出個 Namespace 的概念,在 Kind 的上一級創建 Namespace 文件夾,比如 user1 的 Namespace 文件夾命名為 user1,user2 的 Namespace 文件夾命名為 user2,這樣就分隔開了, user1 存儲路徑為 group1硬盤\v1\user1\shs\test3.sh
  • 這樣解決了【user1和user2創建文件沖突的問題】,但還有不足,就是 group1硬盤\v1\user1\shs\test3.sh 路徑可讀性不強,從后往前讀可知,test3.sh 前面是 shs,說明 test3.sh 文件時 Kind=Sh 類型,但 user1 我們就不太清楚是什么概念,同時有的用戶名若是亂碼如sdfsf,那就編程 group1硬盤\v1\sdfsf\shs\test3.sh ,就更不清楚該路徑的語義,而 Group 硬盤名 和 Version 名數量是有限的,而且用戶無法自定義,同時命名也是易懂的,所以不需要上層概念就能理解;
    • 所以為了解決此問題,我們在 Version 文件夾下一級增加 namespaces 文件夾(表名下一層級是 Namespace 概念,復數表示有眾多 Namespace 文件夾),然后 namespaces 文件夾中創建【用戶名對應的 Namespace 文件夾】,形式如下Group硬盤名\Version名\namespaces\用戶自定義Namespace文件夾\Kind文件夾\Kind文件,因此 user1 創建的 test3.sh 對應的路徑為 group1硬盤\v1\namespaces\user1\shs\test3.sh
全局概念引入

仍需要保留【非Namespace】的概念,有的資源需要全局獨一份,用于全局管控,如 ClusterRole

上面解決了用戶隔離問題,但同時還有個新的需求,group1 硬盤中 Version=v1、Kind=Doc 的文件是可全局訪問到的、是重要的,同時需要保證唯一性;因此 user1 創建 doc 文件保存到 user1 Namespace 中,user2 創建 doc 文間保存到 user2 Namespace 中,都是存儲在自己的 Namespace 文件夾中,因為文件夾的權限控制,user2 無法問到 user1 的 Namespace 文件夾、user1 也無法訪問到 user2 的 Namespace 文件夾;

  • 因此創建 Doc 文件需要考慮放到一個公共的文件夾,這樣所有 user 都可以訪問,同時保證 doc 文件的唯一(同一個文件夾不會有重名文件)
  • 基于此需求,我們對 Doc 文件的創建取消 Namespace 的概念,如創建【Group=group1硬盤、Version=v1、Kind=Doc、名為 test4】,那么對應的路徑為group1硬盤\v1\docs\test4.doc
Resource 概念引入

由于由【Namespace】和【非Namespace】情況存在,GVK 無法判斷出資源 URI 中是否要攜帶 Namespace 路徑

而 GVR 可以解決此問題,Resource 中預先寫入【控制信息】(如 Pod 的 Namespaced = True,ClusterRole 的 Namespaced = False)

所以也分清了職責

  • GVK 專注于定位到資源的數據結構
  • GVR 專注于生成資源描述符(URI),用于找到資源對象(Object)

上面共用問題也解決了,但也帶來個問題,何時創建 Namespace 文件夾(或說哪種資源該創建 Namespace,同時應該如何控制)

  • 【Group=group1硬盤、Version=v1、Kind=Sh】,這種資源該創建 Namespace 文件夾

  • 但 【Group=group1硬盤、Version=v1、Kind=Doc】,這種資源不該創建 Namespace 文件夾

  • 而通過這種 GVK 的組合,創建資源時無法判斷是否應該創建 Namespace 文件夾;不過也有種方法解決,【在后臺將判斷邏輯寫死 Kind=Sh 就創建 Namespace 文件夾,Kind=Doc 就不創建 Namespace 文件夾,但這種方式不夠靈活,程序是寫死的,若后續這兩情況反過來,還需要重新編譯程序】 —— 因此為了解決這個問題,提出了 Resource 的概念

  • Resource ,也可以稱之為 APIResource ,是 Kind 的同名小寫復數形式(如 Kind=Sh,Resource=shs;Kind=Doc,Resource=docs),同時該 Resource 結構不同于 Kind,Kind 是 string 字符串,Resource 是結構體,Resource 內部具有個 Namespaced 屬性(若 Namespaced=True 表示創建該資源時會創建 Namespace 文件夾,反之不會創建);如 Kind=Sh 對應的 shs Resource 結構就具有 Namespaced=True,這樣提交 【Group=group1硬盤、Version=v1、Kind=Sh、user1、文件名為 test5】的創建請求時,GVK 會自動轉換為 GVR,根據 GVR 中 Resource 的設置,形成相應的存儲路徑 group1硬盤\v1\namespaces\user1\shs\test5.sh;而Kind=Doc 對應的 docs Resource 結構就具有 Namespaced=False,后續不會創建 Namespace 文件夾,不在詳解

Rest 映射、Schema、Object

Rest 映射( RestMapper):實現 GVK 和 GVR 的互相轉換

Schema:GVK 和 資源數據結構的映射表

Object:資源對象,也可以稱為 資源存儲實體

  • 那 GVK 和 GVR 具體是如何轉換的呢? —— 答:Rest 映射,有個 RestMapper 結構,此處不在詳解

    • Group/Version/Kind(Rest 映射–>)Group/Version/Resource(GVR 形成–>)路徑(path)
      group1硬盤/v1/Shgroup1硬盤/v1/shs(Namespaced=True)group1硬盤\v1\namespaces\{{user自定義那namespace}}\shs\{{用戶創建的sh文件}}
      group1硬盤/v1/Docgroup1硬盤/v1/docs(Namespaced=False)group1硬盤\v1\docs\{{用戶創建的doc文件}}
  • 最后還有個問題,GVK 相當于指定創建【何種類型的文件】,但這么多 GVK 和文件類型的對應關系,我該如何記住呢 —— 答:引入了 Schema 概念,可將 Schema 理解為一個記錄表,記錄 GVK 與創建文件類型的關系

    • Group/Version/Kind文件類型
      group1硬盤/v1alpha1/Sh無水印的 sh 文件
      group1硬盤/v1/Sh有水印的 sh 文件
      省略若干 GVK 介紹
      group1硬盤/v1/Doc有水印的 Doc 文件
  • 還差一個概念 Object,就是 GVK 對應創建出的真實文件,可理解為實際的存儲

故事總結

在這里插入圖片描述

  • Group 組、Version版本、Namespace 命名空間,這些都很容易理解,都是為了隔離,資源版本隔離(Group、Version)和用戶資源隔離(Namespace)

  • Kind 對象類型,如 Kind=Sh 對應 sh 類型文件,Kind=Doc 對應 doc 類型文件

  • Resource 可以理解為 Kind 的含義補充,用于獲取真正的資源對象;GVK 專注于類型定義,確定是哪種資源對象,GVR 專注于獲取資源對象或對資源對象進行操作;GVR 可轉換為資源對象的存儲路徑 Path,對應實際中就是 http path(對應 Restful API 中 URI、URL 的概念,可以說是文件資源標識),我們拿到了 URL 或 PATH,自然就可以訪問到資源實體,所以稱之為 Resource 很合理

  • Object 就是 GVK 創建的真正的資源存儲實體,也是通過 GVR 形成 Path 所要訪問的資源實體

  • Schema 是記錄 GVK 與 資源結構的映射,知道 GVK 便知道要創建的資源對象的數據結構

  • GVK 和 GVR 是通過 Rest 進行映射,GVK 只專注于類型定義,GVR 包含構建資源路徑的各種細節(比如是否要添加 namespace 路徑等)

  • 為什么這么定義?

    • 可將 apiserver 理解為一個普通的 http server

    • 外部視角:用戶通過 URL 才能訪問到對應的資源吧,因此會認為 URL(HTTP PATH) 為 Resource(Restful API 中也是將 URL 定義為 Resource,所以 GVR 相當于一個用于生產外部 URL Resource 的概念)

    • 內部視角:用戶提交指定 GVK 的 yaml,通過 GVK 檢索(Schema)到對應的資源結構(Go Struct),才能創建資源,并完成持久化存儲(Object)

    • apiserver 視角:本身是個 http Server,在啟動前預先注冊了 http path --> handler 的映射關系(就是用戶向指定 http path 提交請求時,需要有對應的 handler 處理);通過 http path 可知道 GVR,但 GVR 不知道對應的資源結構(Go Struct),所以 GVR 和 GVK 存在個互相轉換機制(RestMapper),這樣通過 GVK 可知道該 http path 請求對應的資源結構(Go Struct),之后便交給 handler 處理;同時有時候只知道 GVK,但需要向指定路徑 http path 發起請求,就需要通過互相轉換機制(RestMapper)將 GVK 轉換為 GVR,從而生成 http path

k8s 實操來理解概念

準備工作
# 開啟 8080 端口,用于 http 訪問
-> % kubectl proxy --port=8080
Starting to serve on 127.0.0.1:8080
案例一(支持 Namespace 的 Deployment, apps 組,apis 前綴)
# 案例一(支持 Namespace 的 Deployment, apps 組,apis 前綴)
# 創建 deployment  名為 encode-deploy
kubectl create deployment encode-deploy --image=busybox --replicas=1 -- /bin/sh -c "while true; do sleep 3600; done"# deployment 的 GVK 為 apps/v1/Deployment
# 對應的 GVR 為 apps/v1/dployments,同時 deployment 支持 Namespace 隔離,而且該 deployment 在 default Namespace 下創建
# 所以 GVR 形成的路徑為 apps/v1/namespaces/default/deployments/encode-deploy
# 同時有個知識點需要注意:因為 k8s 開始沒有 Group 的概念,后來才有,所以 k8s 的基礎資源如 Pod 的 Group 都為空,稱之為 core,但在路徑中沒有表示,所以又創建個 api 和 apis 概念,api 只放置 core 組,其余后續的 Group 都在 apis 組
# 所以上面路徑變為 apis/apps/v1/namespaces/default/deployments/encode-deploy
# 再補上 apiserver 的地址  http://localhost:8080
# 組成全路徑,獲取 encode-deploy 信息
$ curl http://localhost:8080/apis/apps/v1/namespaces/default/deployments/encode-deploy
# 返回的結果
{"kind": "Deployment","apiVersion": "apps/v1","metadata": {"name": "encode-deploy","namespace": "default","uid": "0bfd0275-cd3c-4602-81be-f996cc055cb2","resourceVersion": "204043","generation": 3,"creationTimestamp": "2024-02-28T08:08:52Z","labels": {"app": "encode-deploy"},"annotations": {"deployment.kubernetes.io/revision": "1"},......
}
案例二(支持 Namespace 的 Pod, core 組,api 前綴)
# 案例二(支持 Namespace 的 Pod, core 組,api 前綴)
# 創建 pod  名為 encode-po
$ kubectl run encode-po --image=busybox -- /bin/sh -c "while true; do sleep 3600; done"
# 按照上面講解的,pod 的 GVK 為 Group=空 Version=v1 Kind=Pod, Group=空 稱之為 core 組 對應的路徑為 api 開頭
# 同時 Pod 的 Resource 支持 Namespace 隔離,所以 GVR 對應的 Path 為 
# api/v1/namespaces/default/pods/encode-po
$ curl http://localhost:8080/api/v1/namespaces/default/pods/encode-po
# 返回的結果
{"kind": "Pod","apiVersion": "v1","metadata": {"name": "encode-po","namespace": "default","uid": "13dd4951-321c-45e5-bcb2-33b1e60f3954","resourceVersion": "193006","creationTimestamp": "2024-02-28T03:17:20Z","labels": {"run": "encode-po"},... ...  
}
案例三(不支持 Namespace 的 ClusterRole, core 組,api 前綴)
  • 擴展內容
    • 為什么 core Group,在路徑中不顯示 core,而路徑前面多了 api?
    • 為什么非 core Group,在路徑前面多了 apis 前綴?
# 擴展內容
# - 會奇怪 Pod 為什么只有 v1,沒有 Group,因為最開始 k8s 沒有 Group 概念,后續新增的,所以最早的基礎資源 Group=空,稱之為 core Group(核心組),但實際創建 Pod 時候不會寫為 core/v1(仍是寫為 v1),稱之為 core Group 只是為了便于讀和理解,總不能說是 Pod 屬于【空Group】吧?這樣太難理解了。。。 
# 而且還有個問題,記得 GVR 轉換 http path 的 規則吧
# Resource 支持 Namespace 時
# GVK --> GVR --> HTTP PATH: /Group名/Version名/namespaces/namesapce名/Resouces名(Kind小寫復數形式)/具體對象名稱
# Resource 不支持 Namespace 時
# GVK --> GVR --> HTTP PATH: /Group名/Version名/Resouces名(Kind小寫復數形式)/具體對象名稱
# 按照轉換規則 Pod GVK(Group=空,Version=v1,Kind=Pod)對應的 Path 為,default namespace 下名為 encode-po Pod 對應的 http path 為 v1/namespaces/default/pods/encode-po
# 按照轉換規則 Deployment GVK(Group=apps,Version=v1,Kind=Pod)對應的 Path 為,default namespace 下名為 encode-deploy Deployment 對應的 http path 為 apps/v1/namespaces/default/deploy/encode-deploy 
# 可以看出,Pod 相比于 Deploy 少了個 Group 前綴,不易于理解,所以為了便于理解,在 Group 前面增加 api 和 apis 概念,規則就是只有 core Group 添加 api 前綴,其余 Group 都添加 apis 前綴,相當于一個再次分類,畢竟 Pod 等 core Group 中的資源屬于【老員工】,有個獨立的【api辦公室】是可以理解的
# 所以演變為:
# core Group 資源路徑:v1/namespaces/default/pods/encode-po --> api/v1/namespaces/default/pods/encode-po
# 非 core Group 資源路徑: apps/v1/namespaces/default/deploy/encode-deploy  --> apis/apps/v1/namespaces/default/deploy/encode-deploy
  • 案例三
# 案例三(不支持 Namespace 的 ClusterRole, core 組,api 前綴)
# 不知道 ClusterRole 是哪個組,可以采用 kubectl-apiresources 命令進行搜索
# 返回的內容:記錄了 GVR 和 GVK 的對應關系,還有 Resource 是否支持 Namespace 信息
# 第一列 Resource 名稱(可以看出是小寫字母復數形式)
# 第二列 該 Resource 的縮寫,比如 pods 的縮寫為 po,因此 kubectl get pods 等同于 kubectl get po,但同時需要注意有的沒有縮寫
# 第三列 該 Resource 和 Kind,所屬于的 Group/Version,v1 代表 core Group 沒有 Group 名
# 第四列 該 Resource 是否支持 Namespace 隔離,Namespaced=True 表示支持
# 第五輪 Kind 名稱(包含大寫字母)$ kubectl  api-resources
NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
bindings                                       v1                                     true         Binding
componentstatuses                 cs           v1                                     false        ComponentStatus
configmaps                        cm           v1                                     true         ConfigMap
endpoints                         ep           v1                                     true         Endpoints
events                            ev           v1                                     true         Event
pods                              po           v1                                     true         Pod
podtemplates                                   v1                                     true         PodTemplate
limitranges                       limits       v1                                     true         LimitRange
daemonsets                        ds           apps/v1                                true         DaemonSet
deployments                       deploy       apps/v1                                true         Deployment
replicasets                       rs           apps/v1                                true         ReplicaSet
statefulsets                      sts          apps/v1                                true         StatefulSet
... ...# 上面信息太多,想要知道 ClusterRole 屬于哪個 Group Version,可以看到如下命令
# 另外,可以看到 ClusterRole 對應的 Namespced=False, 不具備 Namespace 隔離,符合我們的測試要求
$ kubectl api-resources | grep clusterroleclusterrolebindings                            rbac.authorization.k8s.io/v1           false        ClusterRoleBinding
clusterroles                                   rbac.authorization.k8s.io/v1           false        ClusterRole# 隨意查看個已有的 ClusterRole
$ bectl get clusterrole
NAME                                                                   CREATED AT
admin                                                                  2024-01-30T09:53:07Z
cluster-admin                                                          2024-01-30T09:53:07Z
... ...# 以 admin  ClusterRole 為例, 按照上面構建路徑,并發起請求,符合預期
curl http://localhost:8080/apis/rbac.authorization.k8s.io/v1/clusterroles/admin
{"kind": "ClusterRole","apiVersion": "rbac.authorization.k8s.io/v1","metadata": {"name": "admin","uid": "37abf107-240e-474e-a65b-5827aff7cf00","resourceVersion": "336","creationTimestamp": "2024-01-30T09:53:07Z","labels": {"kubernetes.io/bootstrapping": "rbac-defaults"},"annotations": {"rbac.authorization.kubernetes.io/autoupdate": "true"},... ...   
}

SubResource 概念的引入

  • 相信你已理解上面 Resource 的概念,那 subResource 概念是什么意思呢?
  • 現在可以簡單理解為,通過 curl GVR 形成的 http path 可以獲取到一個 Deployment (此處示例為 default Namespace 下名為 encode-deploy 的 Deploy)的所有信息curl http://localhost:8080/apis/apps/v1/namespaces/default/deployments/encode-deploy
    • 此時會帶來幾個考慮
    • 用戶1只想更改 encode-deploy 的副本數 replicas,覺得返回一堆信息沒必要,增加更改復雜度
    • 用戶2比較危險,需求只想更改 encode-deploy 的副本數 replicas,但現在其可以更改該 Deployment 的所有字段
    • 所以給 deployments Resource 增加了 scale Resource 用于更改 Deployment 的副本數
      • 路徑變為了curl http://localhost:8080/apis/apps/v1/namespaces/default/deployments/encode-deploy/scale
      • 通過此路徑,可以更改 Deployment 的副本數,也就是 replicas 字段
      • 以往在 RBAC 中只能賦予 deployments 權限,現在可以賦予 deployments/scale 權限,讓用戶只能更改副本數,無法更改其余字段
subresource 作用

Question1 : 可以通過 kubectl edit 更改 deployment 副本數,為什么還提出一個 scale subresource(子資源)

  1. API 一致性和規范性:Kubernetes 的設計哲學之一是提供統一的 API。通過為資源引入 scale 子資源,可以使得副本數的管理變得更加規范和統一,無論是通過 kubectl、API 或者其他工具,用戶都可以使用相同的方法來管理副本數。這種一致性有助于降低用戶學習成本,并簡化資源管理。
  2. 權限控制和安全性:引入 scale 子資源可以使得 Kubernetes 更加靈活地進行權限控制。通過 RBAC(基于角色的訪問控制)機制,管理員可以精確地控制哪些用戶或服務賬號具有修改副本數的權限,而不必擔心他們能否編輯整個 Deployment 對象或其他資源。
  3. 自動縮放支持:許多 Kubernetes 集群中使用了自動縮放功能,例如 HorizontalPodAutoscaler (HPA)。HPA 可以根據資源使用率或自定義指標動態調整副本數。scale 子資源為這種自動縮放功能提供了支持,HPA 可以通過 scale 子資源查詢和調整資源的副本數,從而實現自動化的水平擴展和收縮。

簡而言之(說人話):

  1. 明顯 kubectl edit 對應的 curl 比 kubectl scale 繁瑣
  2. API 統一:scale subresource 出現后,statefulset、deployment、daemonset 擴縮容發起 http 請求時,只需要關注 scale 子資源,就是 curl …/scale,很統一
  3. 若采用 kubectl edit 進行擴容,用戶需要具有【修改整個 deployment 對象的 update 權限,過大】,而采用 kubectl scale,用戶只需要具有【deployment/scale 子資源的 update 權限,范圍縮小,更安全】
  4. 增加了 scale 子資源后,便于進行自動縮放功能

Question2: 為什么 curl 更改 scale subresource 后,就可以進行 deployment 的擴縮

curl -X PUT
-H “Content-Type: application/json”
–data ‘{“apiVersion”:“autoscaling/v1”,“kind”:“Scale”,“metadata”:{“name”:“encode-deploy”,“namespace”:“default”},“spec”:{“replicas”:2}}’
http://localhost:8080/apis/apps/v1/namespaces/default/deployments/encode-deploy/scale

  • 增加 scale subresource,其實就相當于【增加一個 http path 端點】
  • 以往 deployment http 端點為 http://localhost:8080/apis/apps/v1/namespaces/default/deployments/encode-deploy
  • 而該新增的 scale http path 端點,對應的 http handler ,其實功能很簡單,就是更改指定 deployment 的 spec.replicas(此例子是 encode-deploy 的 replicas),之后邏輯就很好理解了,deployment Controller 監控到 encode-deploy replicas 副本數的變化,便會進行相應的擴縮容
k8s 實操 subresource
# 開啟 8080 端口,用于 http 訪問
-> % kubectl proxy --port=8080
Starting to serve on 127.0.0.1:8080# 創建 deployment  名為 encode-deploy
kubectl create deployment encode-deploy --image=busybox --replicas=1 -- /bin/sh -c "while true; do sleep 3600; done"# 對 encode-deploy  進行擴容
# 方法1: 通過 scale subresource 進行擴容,對 scale http 端點發請求
-> % curl -X PUT \-H "Content-Type: application/json" \--data '{"apiVersion":"autoscaling/v1","kind":"Scale","metadata":{"name":"encode-deploy","namespace":"default"},"spec":{"replicas":2}}' \http://localhost:8080/apis/apps/v1/namespaces/default/deployments/encode-deploy/scale
# 成功執行
{"kind": "Scale","apiVersion": "autoscaling/v1","metadata": {"name": "encode-deploy","namespace": "default","uid": "0bfd0275-cd3c-4602-81be-f996cc055cb2","resourceVersion": "203932","creationTimestamp": "2024-02-28T08:08:52Z"},"spec": {"replicas": 2},"status": {"replicas": 1,"selector": "app=encode-deploy"}
}%# kubectl scale(等同于上面,只是進行了封裝,內部邏輯相同,實際代碼執行邏輯等同于上面 curl)
-> % kubectl scale deploy encode-deploy --replicas=2
deployment.apps/encode-deploy scaled# 方法2: 利用 kubectl edit 進行擴容,更改該 deploy 中的 spec.replicas 為 2
# 實際等同于,以下3步(未驗證,可做參考),相比于 scale subresource 繁瑣
# 1. 獲取當前 yaml 配置:curl http://localhost:8080/apis/apps/v1/namespaces/default/deployments/encode-deploy -o deployment.yaml
# 2. 修改 yaml 配置:接下來,編輯 deployment.yaml 文件,將 spec.replicas 字段的值修改為 2
# 3. 上傳新的 yaml 配置:curl -X PUT -H "Content-Type: application/yaml" --data-binary "@deployment.yaml" http://localhost:8080/apis/apps/v1/namespaces/default/deployments/encode-deploy
kubectl edit deploy encode-deploy
# 修改 spec.replicas 為 2 ,保存 esc  :wq
獲取 subresource
  • Where can I get a list of Kubernetes API resources and subresources? - Stack Overflow
  • 由于無法直接查看到 Resource 有哪些 subResource,通過此腳本可以看到有哪些 subResource
# 1. 開啟 proxy 8080 端口,http 不安全訪問
$ kubectl proxy --port=8080# 2. 執行如下腳本獲取 subresource
# 若不執行第一步,未開啟8080端口,那么將 curl -s $SERVER/ 替換為 kubectl get --raw / 即可正常執行
#!/bin/bash
SERVER="localhost:8080"APIS=$(curl -s $SERVER/apis | jq -r '[.groups | .[].name] | join(" ")')# do core resources first, which are at a separate api location
api="core"
curl -s $SERVER/api/v1 | jq -r --arg api "$api" '.resources | .[] | "\($api) \(.name): \(.verbs | join(" "))"'# now do non-core resources
for api in $APIS; doversion=$(curl -s $SERVER/apis/$api | jq -r '.preferredVersion.version')curl -s $SERVER/apis/$api/$version | jq -r --arg api "$api" '.resources | .[]? | "\($api) \(.name): \(.verbs | join(" "))"'
done# 3. 執行結果舉例
...
core pods: create delete deletecollection get list patch update watch
core pods/attach: create get
core pods/binding: create
core pods/ephemeralcontainers: get patch update
core pods/eviction: create
core pods/exec: create get
core pods/log: get
core pods/portforward: create get
core pods/proxy: create delete get patch update
core pods/status: get patch update
...

理解 k8s 主要 go 庫作用

  • Kubernetes類型系統 | 李乾坤的博客

k8s中Apimachinery、Api、Client-go庫之間的關系k8s.io/client-go, k8s.io/api, k8s.io/apimachinery 是基于Golang的 Kubernetes 編程的核心,是 schema 的實現。kubernetes 中 schema 就是 GVK 的屬性約束 與 GVR 之間的映射。

  1. apimachinery 是最基礎的庫,包括核心的數據結構,比如 Scheme、Group、Version、Kind、Resource,以及排列組合出來的 常用的GVK、GV、GK、GVR等等,再就是編碼、解碼等操作。類似于Java 中的Class/Method/Field 這些
  2. api 庫,這個庫依賴 apimachinery,提供了k8s的內置資源,以及注冊到 Scheme 的接口,這些資源比如:Pod、Service、Deployment、Namespace
  3. client-go 庫,這個庫依賴前兩個庫,提供了訪問k8s 內置資源的sdk,最常用的就是 clientSet。底層通過 http 請求訪問k8s 的 api-server,從etcd獲取資源信息

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/714355.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/714355.shtml
英文地址,請注明出處:http://en.pswp.cn/news/714355.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

前端學習第二天-html提升

達標要求 了解列表的分類 熟練掌握列表的用法 熟練掌握表格的結構構成 合并單元格 表單的組成 熟練掌握表單控件分類的使用 1.列表 1.1 無序列表 <ul>&#xff1a;定義無序列表&#xff0c;并且只能包含<li>子元素。 <li>&#xff1a;定義列表項&a…

LZO索引文件失效說明

在hive中創建lzo文件和索引時&#xff0c;進行查詢時會出現問題.hive的默認輸入格式是開啟小文件合并的&#xff0c;會把索引也合并進來。所以要關閉hive小文件合并功能&#xff01;

Matlab:元胞自動機

元胞自動機是一種基于離散空間的動態系統&#xff0c;由許多簡單單元按照某些規則進行相互作用和演化而形成的復雜結構。元胞自動機可以用于模擬物理、生物、社會等領域的現象&#xff0c;以及進行優化、圖像處理、噪聲生成等方面的應用。 例1&#xff1a;生命游戲 nextState…

maven項目報錯Cannot resolve plugin org.apache.maven.plugins:maven-war-plugin:2.2

如果IDEA整合maven沒有問題&#xff0c;還是報這個錯誤&#xff0c;很大可能是由于在下載過程中存在網絡問題&#xff0c;導致文件下載一半而停止&#xff0c;但是已經在倉庫中存在這個文件夾&#xff0c;解決方法是刪除文件夾重新下載即可。 刪除本地倉庫下的\org\apache\mav…

(算法)位運算

常見的位運算符&#xff1a; 給定一個數n判斷他的二進制第x位是0還是1 把第x位修改為1 因為是只是修改n的某個位置&#xff0c;所以不應該移動改變n 既然修改為1&#xff0c;那么就要想到 | 運算符 把第x位修改為0 因為修改為0,所以要用&運算符 位圖思想 判定字符串…

C++17之std::invoke: 使用和原理探究(全)

目錄 1.概述 2.輔助類 3.原理分析 4.總結 1.概述 在之前的 C 版本中&#xff0c;要調用不同類型的可調用對象&#xff0c;需要使用不同的語法&#xff0c;例如使用函數調用運算符 () 來調用函數或函數指針&#xff0c;使用成員訪問運算符 -> 或 . 來調用成員函數。這樣的…

二維碼門樓牌管理系統技術服務的深度解析

文章目錄 前言一、標準地址名稱的定義與重要性二、二維碼門樓牌管理系統的核心技術三、標準地址名稱在二維碼門樓牌管理中的應用四、二維碼門樓牌管理系統的優勢與挑戰五、展望未來 前言 在數字化浪潮中&#xff0c;二維碼門樓牌管理系統以其高效、便捷的特性&#xff0c;正逐…

【一】【算法分析與設計】基礎測試

排列式 題目描述 7254是一個不尋常的數&#xff0c;因為它可以表示為7254 39 x 186&#xff0c;這個式子中1~9每個數字正好出現一次 輸出所有這樣的不同的式子&#xff08;乘數交換被認為是相同的式子&#xff09; 結果小的先輸出&#xff1b;結果相同的&#xff0c;較小的乘…

js 實戰小案例

實戰 時間 js 格式化時間 <script type"text/javascript">function formatDate(date) { let year date.getFullYear(); let month String(date.getMonth() 1).padStart(2, 0); // getMonth() 返回的月份是從0開始的&#xff0c;所以要加1&#xff0c;并…

【go從入門到精通】go包,內置類型和初始化順序

大家好&#xff0c;這是我給大家準備的新的一期專欄&#xff0c;專門講golang&#xff0c;從入門到精通各種框架和中間件&#xff0c;工具類庫&#xff0c;希望對go有興趣的同學可以訂閱此專欄。 go基礎 。 Go文件名&#xff1a; 所有的go源碼都是以 ".go" 結尾&…

Mamba 環境安裝:causal-conv1d和mamba-ssm報錯解決辦法

問題描述&#xff1a; 在執行命令 pip install causal_conv1d 和 mamba_ssm 出錯&#xff1a; 解決方案&#xff1a; 1、使用網友配置好的Docker環境&#xff0c;參考&#xff1a;解決causal_conv1d和mamba_ssm無法安裝 -&#xff1e; 直接使用Mamba基礎環境docker鏡像 DockH…

java實現圖片轉pdf,并通過流的方式進行下載(前后端分離)

首先需要導入相關依賴&#xff0c;由于具體依賴本人也不是記得很清楚了&#xff0c;所以簡短的說一下。 iText&#xff1a;PDF 操作庫&#xff0c;用于創建和操作 PDF 文件。可通過 Maven 或 Gradle 引入 iText 依賴。 MultipartFile&#xff1a;Spring 框架中處理文件上傳的類…

一臺工控機的能量

使用Docker搭建EPICS的IOC記錄 Zstack EPICS Archiver在小課題組的使用經驗 以前電子槍調試&#xff0c;用一臺工控機跑起束測后臺&#xff0c;這次新光源用的電子槍加工回來又是測試&#xff0c;又是用一臺工控機做起重復的事&#xff0c;不過生命在于折騰&#xff0c;重復的…

stm32——hal庫學習筆記(IIC)

一、IIC總線協議介紹&#xff08;掌握&#xff09; 二、AT24C02介紹&#xff08;了解&#xff09; 三、AT24C02讀寫時序&#xff08;掌握&#xff09; 四、AT24C02驅動步驟&#xff08;掌握&#xff09; 五、編程實戰&#xff08;掌握&#xff09; myiic.c #include "./B…

汽車虛擬仿真技術的實現、應用和未來

汽車虛擬仿真技術是一種利用計算機模擬汽車運行的技術&#xff0c;以實現對汽車行為的分析、評估和改進。汽車虛擬仿真技術是汽車工業中重要的開發設計和測試工具&#xff0c;可以大大縮短產品研發周期、降低研發成本和提高產品質量。本文將從汽車虛擬仿真技術的實現過程、應用…

Ubuntu18.04 系統上配置并運行SuperGluePretrainedNetwork(僅使用CPU)

SuperGlue是Magic Leap在CVPR 2020上展示的研究項目&#xff0c;它是一個圖神經網絡&#xff08;Graph Neural Network&#xff09;和最優匹配層&#xff08;Optimal Matching layer&#xff09;的結合&#xff0c;訓練用于對兩組稀疏圖像特征進行匹配。這個項目提供了PyTorch代…

前端的文字的字體應該如何設置

要設置文字的字體&#xff0c;在CSS中使用font-family屬性。這個屬性可以接受一個或多個字體名稱作為其值&#xff0c;瀏覽器會按照列表中的順序嘗試使用這些字體渲染文本。如果第一個字體不可用&#xff0c;瀏覽器會嘗試使用列表中的下一個字體&#xff0c;依此類推。 字體設…

iOS消息發送流程

Objc的方法調用基于消息發送機制。即Objc中的方法調用&#xff0c;在底層實際都是通過調用objc_msgSend方法向對象消息發送消息來實現的。在iOS中&#xff0c; 實例對象的方法主要存儲在類的方法列表中&#xff0c;類方法則是主要存儲在原類中。 向對象發送消息&#xff0c;核心…

推薦一個屏幕上鼠標高亮顯示的小工具

在視頻錄制等特定場景下&#xff0c;很多人希望在點擊鼠標時能夠在屏幕上及時進行顯示&#xff0c;便于別人發現&#xff0c;提高別人的注意力。 因此&#xff0c;很多錄屏軟件中都內含顯示鼠標點擊功能。那如果不支持該怎么辦呢&#xff1f;其實&#xff0c;也是可以通過其他工…

Python 實現Excel自動化辦公(上)

在Python 中你要針對某個對象進行操作&#xff0c;是需要安裝與其對應的第三方庫的&#xff0c;這里對于Excel 也不例外&#xff0c;它也有對應的第三方庫&#xff0c;即xlrd 庫。 什么是xlrd庫 Python 操作Excel 主要用到xlrd和xlwt這兩個庫&#xff0c;即xlrd是讀Excel &am…