之前做了一個VPA項目的需求,就是需要不重啟的方式修改容器的Cgroup的值已達到垂直擴縮容的目的,項目中核心的思路如下
- 上游下發要VPA的結果的值寫入到容器的Annotation里面
- Kubelet 感知到這個 annoation 的變化
- 我們本地運行一個 Agent,里面運行一個類似的job監聽kubelet的變化,當發現annotation的值改變的時候就去修改對應容器的Cgroup的數值即可
具體步驟如下
Cgroup 是什么
簡而言之,對容器技術而言,其實現資源層面上的限制和隔離,依賴于 Linux 內核提供的 Cgroup 和 namespace 技術
cgroup 的主要作用:管理資源的分配和限制;主要限制的資源是CPU、內存、網絡和磁盤IO
namespace 的主要作用:封裝抽象、限制、隔離,使命名空間內的進程看起來擁有他們自己的全局資源
以一個最簡單的例子來校驗Cgroup的限制,我們使用docker來創建以下容器
docker run --rm -d --cpus=2 --memory=2g --name=2c2g redis:alpine
查看上述容器的ID
? dps | grep -i 2c2g
37ad8252f8de redis:alpine "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 6379/tcp 2c2g
找到 /sys/fs/cgroup/system.slice
目錄對應的容器
可看到有很多容器的選項可供查看
查看CPU和內存的Max
? docker-37ad8252f8de50694e3321364c9ef5e94ae08f45f50e633e561feb291d1c27e9.scope cat cpu.max
200000 100000
? docker-37ad8252f8de50694e3321364c9ef5e94ae08f45f50e633e561feb291d1c27e9.scope cat memory.max
2147483648 # 這里的單位是Byte
我們查看容器的占用
可以看到這個2G就是我們設定的數值
那嘗試直接修改這個cgroup的數值,是不是就能修改容器的內存和CPU的限制從而做到VPA的效果
我們嘗試修改內存由原來的2G到現在的1G
echo "1073741824" >> memory.max
再次查看內存的占用比值,可以發現確實修改已經生效
Kubelet 的接口
首先明確一點,你在管控面 kubelet get 等信息基本都是從邊緣端映射過來的,所以單機側的 kubelet 也能獲取到很多信息,比如
/configz 返回 kubelet 的相關配置信息
/metrics 暴露 kubelet 和容器的監控指標,比如 CPU 和內存的使用率,網絡流量等
/pods 返回當前節點上的所有 Pod 的信息,包括 Pod 的狀態以及容器信息等
/stats/summary 提供節點的資源使用統計信息,包括CPU、內存、網絡、磁盤等
/exex/<pod_namespace>/<pod_name>/<container_name> 提供指定容器的日志,支持tail和follow參數
/logs 用于獲取正在運行的pods和容器日志
配置服務賬號訪問kubelet
訪問 kubelet 需要使用密鑰,我們先配置服務賬號訪問,直接配置成最大的 clusterrole 權限
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: system:my-agent
subjects:- kind: ServiceAccountname: my-agentnamespace: default
roleRef:kind: ClusterRolename: system:my-agentapiGroup: rbac.authorization.k8s.io
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: system:my-agent
rules:- apiGroups:- ""resources:- nodesverbs:- get- list- watch- apiGroups:- ""resources:- nodesverbs:- proxy- apiGroups:- ""resources:- nodes/log- nodes/metrics- nodes/proxy- nodes/spec- nodes/statsverbs:- '*'
---
apiVersion: v1
kind: ServiceAccount
metadata:name: my-agentnamespace: default
上面的服務賬號創建完成之后就能看到下面的密鑰了
kubectl get secret $(kubectl get serviceaccount my-agent -o jsonpath='{.secrets[0].name}') -o jsonpath='{.data.token}' | base64 --decode
比如我這個機器上是
? ~ k get secret $(kubectl get serviceaccount my-agent -o jsonpath='{.secrets[0].name}') -o jsonpath='{.data.token}' | base64 --decode
eyJhbGciOiJSUzI1NiIsImtpZCI6IlBQTDNXcGNKenVxNGZ3OHdpam5WQ0Jnd3d3NWpMbVZtSWJETTQ0WW5EZHMifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Im15LWFnZW50LXRva2VuLXE0a2hsIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6Im15LWFnZW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiY2UyMzQzNGEtMTViYy00YzI1LWIwYTktZGQxM2E3MDllMGRlIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6bXktYWdlbnQifQ.UjNJxEbaLDm_19wVB4OPi-3u6fTG2noCQrLI7e7-PA1DoPc4SQJHzVXmPGwP6Ln4Odv-PrC0jkOpGF4DQQcQx589HsXN0zpmn6z-GJdzTsVxFFaJZpgZlfITGN99zQlr-AkRjgEm_9vUpijjjXOP0dWmPlHlmpxSYqpf3zSohIJsBySpXp1sQKboaeqTHbXU02xwsCjFDHeOyfESQcyVOtKQk9_w0Qvtiz5DcCClAZSCW8WLQPX9X7wie_vZCKlcNKiS5eKYCdymm6ZndXyidqb9Ga027_jhZnjvPO27rzBAL5wiTgonYAUau6tvB9KENfYAFF3pm2rTJZYujjUOfA
那用這個 token 是否可以訪問到機器上的 pods 信息呢
我們試著對 /configz 接口發起訪問,訪問其 kubelet 的配置信息
可以看到訪問成功
實操:部署pod并做VPA操作
手動修改
創建一個 nginx 的 deployment,設定資源限制為2core2G
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deployment
spec:replicas: 1 # 設置副本數selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:latest # 使用最新的 Nginx 鏡像resources:requests:memory: "2Gi" # 請求的內存cpu: "2" # 請求的 CPUlimits:memory: "2Gi" # 限制的內存cpu: "2" # 限制的 CPUports:- containerPort: 80 # Nginx 默認監聽的端口
創建完成之后到目錄 /sys/fs/cgroup/kubepods.slice
下面就能看到我們創建的pod
這個后綴就是我們pod的uid
進入到這個目錄下面
可以發現這個pod里面實際運行了兩個容器,一個 sandbox 容器用來隔離容器的網絡,一個業務自身的容器
分別對應的pod的信息如下
我們進入到我們的業務pod里面,修改內存的取值如下
? docker-567f43cdcb3c5630ffc17735d92b2895912a65a3e03fd5575262a52709eba00b.scope cat memory.max
2147483648
修改為1G
然后使用 docker stats 查看容器的負載使用情況
可以看到容器的內存使用上限已經修改為我們期望的1GB,并且是不需要在重啟容器的情況下進行的
參考文檔
一篇搞懂容器技術的基石: cgroup