從應用訪問Pod元數據-DownwardApi的應用
對於某些需要調度之後才能知道的數據,比如 pod 的 ip,主機名,或者 pod 自身的名稱等等,k8s 依舊很貼心的提供了 Downward API 的方式來獲取此類數據,並且可以通過環境變量或者文件(downwardApi卷中)來傳遞 pod 的元數據。
可以傳遞的容器數據包括如下:
- pod的名稱,IP,所在命名空間,運行節點的名稱,運行所歸屬的服務賬户名稱
- 每個容器請求的 CPU 和內存的使用量
- 每個容器可以使用的 CPU 和內存的限制
- pod 的標籤
- pod 的註解
通過環境變量暴露元數據
創建一個但容器的 pod
$ vim ./dowanwardapi-learn.yaml apiVersion: v1 kind: Pod metadata: name: downward-learn spec: containers: - name: main image: busybox command: ["sleep", "999999"] resources: requests: cpu: "15m" memory: "100Ki" limits: cpu: "100m" memory: "4Mi" env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName - name: CONTAINER_CPU_REQUEST_MILLICORES valueFrom: resourceFieldRef: resource: requests.cpu divisor: "1m" - name: CONTAINER_MEMORY_LIMIT_KIBIBYTES valueFrom: resourceFieldRef: resource: limits.memory divisor: "1Ki"
創建完成後我們可以使用 kubectl exec 命令來查看容器中的所有環境變量,如下:
$ kubectl exec downward-learn env kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead. PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=downward-learn POD_NAME=downward-learn POD_IP=10.44.0.3 NODE_NAME=node1 CONTAINER_CPU_REQUEST_MILLICORES=15 CONTAINER_MEMORY_LIMIT_KIBIBYTES=4096 KUBERNETES_PORT_443_TCP_PROTO=tcp KUBERNETES_PORT_443_TCP_PORT=443 KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1 KUBERNETES_SERVICE_HOST=10.96.0.1 KUBERNETES_SERVICE_PORT=443 KUBERNETES_SERVICE_PORT_HTTPS=443 KUBERNETES_PORT=tcp://10.96.0.1:443 KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443 HOME=/root
但是有一些變量並不能根據環境變量暴露,比如 lables 標籤和 annotations 註釋等等,但是這些可以使用過 dowanwardApi 卷的方式來進行載入。
通過 downwardAPI 捲來傳遞元數據
和環境變量一樣,通過文件卷的方式也需要顯式指定元數據字段來暴露給進程,如下:
apiVersion: v1 kind: Pod metadata: name: downward-learn spec: containers: - name: main image: busybox command: ["sleep", "999999"] resources: requests: cpu: "15m" memory: "100Ki" limits: cpu: "100m" memory: "4Mi" volumeMounts: - name: downward mountPath: /etc/downard volumes: - name: downward downwardAPI: iterms: - path: "podName" fileRef: fieldPath: metadata.name ......
其中聲明元數據和配置的方式沒什麼不同。
但是其中需要注意的是,如果是暴露容器級的元數據時,比如容器可使用的資源限制和資源請求(如使用字段 resourceFieldRef),必須指定引入資源字段的容器名稱,比如:
spec: volumes: - name: downward downwardAPI: items: - path: "containerCpuRequestMilliCores" resourceFieldRef: containerName: main ## 容器名稱 resource: requests.cpu divisor: 1m
這裏由於引入了cpu資源限制,所以也貼一下所用到的,在 pod 內查詢當前的 cpu 使用進程代碼,因為 pod 容器內的查詢 cpu 使用和物理機上的還不太一樣,在網上找了一些但是都不太適合容器使用,所以自己寫了一個
$ vim cpu.go package handler import ( "fmt" "os" linuxproc "github.com/c9s/goprocinfo/linux" ) var ( prevUsageUser int64 = 0 prevUsageSystem int64 = 0 cpuNum = 1 // 設置該容器內使用的cpu個數 ) func GetCpuCount(stat *linuxproc.Stat) (count float64) { cfsQuota := getCgoupValueByPath("/sys/fs/cgroup/cpu/cpu.cfs_quota_us") cfsPeriod := getCgoupValueByPath("/sys/fs/cgroup/cpu/cpu.cfs_period_us") if cfsQuota == -1 { return float64(len(stat.CPUStats)) } return float64(cfsQuota) / float64(cfsPeriod) } func CpuCountToString(c float64) string { if c == float64(int64(c)) { return fmt.Sprintf("%v", c) } return fmt.Sprintf("%0.1f", c) } // GetCpuUsage should be called every 1 seconds. not quite precise. func GetCpuUsage(cpus float64) (user, system, idle float64) { var currentUsageUser, currentUsageSystem int64 currentUsageUser = getCgoupValueByPath("/sys/fs/cgroup/cpuacct/cpuacct.usage_user") currentUsageSystem = getCgoupValueByPath("/sys/fs/cgroup/cpuacct/cpuacct.usage_sys") if prevUsageUser == 0 && prevUsageSystem == 0 { prevUsageUser = currentUsageUser prevUsageSystem = currentUsageSystem return } user = float64(currentUsageUser-prevUsageUser) / 10000000 / cpus // / 1000,000,000 * 100 = /10,000,000 system = float64(currentUsageSystem-prevUsageSystem) / 10000000 / cpus // / 1000,000,000 * 100 = /10,000,000 idle = 100 - user - system if idle < 0 { idle = 0 } prevUsageUser = currentUsageUser prevUsageSystem = currentUsageSystem return } func getCgoupValueByPath(path string) int64 { data, err := os.ReadFile(path) if err != nil { return 0 } var value int64 n, err := fmt.Sscanf(string(data), "%d", &value) if err != nil || n != 1 { return 0 } return value }
計算出來當前的容器數值換算為:use 80% = 800
這樣在容器內就能根據元數據和監控腳本時刻監控容器的 cpu 使用率了。
「其他文章」
- 記一次批量更新整型類型的列 → 探究 UPDATE 的使用細節
- 編碼中的Adapter,不僅是一種設計模式,更是一種架構理念與解決方案
- 線程池底層原理詳解與源碼分析
- 30分鐘掌握 Webpack
- 線性迴歸大結局(嶺(Ridge)、 Lasso迴歸原理、公式推導),你想要的這裏都有
- Django 之路由層
- 【前端必會】webpack loader 到底是什麼
- day42-反射01
- 中心化決議管理——雲端分析
- HashMap底層原理及jdk1.8源碼解讀
- 詳解JS中 call 方法的實現
- 打印 Logger 日誌時,需不需要再封裝一下工具類?
- 初識設計模式 - 代理模式
- 設計模式---享元模式
- 密碼學奇妙之旅、01 CFB密文反饋模式、AES標準、Golang代碼
- [ML從入門到入門] 支持向量機:從SVM的推導過程到SMO的收斂性討論
- 從應用訪問Pod元數據-DownwardApi的應用
- Springboot之 Mybatis 多數據源實現
- Java 泛型程序設計
- CAS核心思想、底層實現