官网链接
前置准备
搭建好一套k8s集群,可以参考我写的这篇教程:搭建k8s集群
k8s官方的镜像站在国内是拉不下来的,有几种方法解决:
- 在拉取镜像的虚拟机/服务器上科学上网
- 配置k8s的镜像源,目前国内只有阿里云支持改版后的k8s镜像源(registry.k8s.io)。
- 需要拉取镜像的时候,指定拉取策略为本地拉取(
imagePullPolicy:Never),每次需要拉取镜像前都手动拉取/上传一份镜像到服务器上再导入镜像
这里给出阿里云镜像源的配置教程:
旧版的k8s直接修改/etc/containerd/config.toml里的mirror信息,添加上阿里云的镜像站就行。但是新版的不支持inline或者说暂时兼容,未来不支持。所以这里就只给出新版k8s镜像源配置教程。
修改/etc/containerd/config.yaml,填入下列信息(如果你已经有了config.yaml且这个配置文件是从containerd默认配置里生成的,那直接备份,然后使用下面的内容)。sudo vim /etc/containerd/config.yaml
1
2
3
4
5
6
7
8
9
10
|
version = 2
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
|
创建/etc/containerd/certs.d目录,在这个目录填入docker.io和registry.k8s.io的镜像源。
注意:k8s里修改镜像源之后,使用kubectl describe pod <pod_name> 查看时还是显示的docker.io和registry.k8s.io。配置镜像源只物理修改从哪里修改,不改镜像拉取的逻辑源。所以改好镜像源之后也不太好验证成功,随便拉个镜像sudo crictl pull nginx:1.14.2,能拉下来就是成了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# Docker Hub 加速
sudo mkdir -p /etc/containerd/certs.d/docker.io
sudo tee /etc/containerd/certs.d/docker.io/hosts.toml << 'EOF'
server = "https://registry-1.docker.io"
[host."https://docker.m.daocloud.io"]
capabilities = ["pull", "resolve"]
EOF
# K8s 镜像加速
sudo mkdir -p /etc/containerd/certs.d/registry.k8s.io
sudo tee /etc/containerd/certs.d/registry.k8s.io/hosts.toml << 'EOF'
server = "https://registry.k8s.io"
[host."https://registry.cn-hangzhou.aliyuncs.com/google_containers"]
capabilities = ["pull", "resolve"]
override_path = true
EOF
|
到这里,镜像源就配置好了,如果不出意外,文件目录应该是下面这样:
1
2
3
4
5
6
7
|
rust@k8s1:/etc/containerd$ ll
总计 28
drwxr-xr-x 3 root root 4096 2月 4 10:31 ./
drwxr-xr-x 144 root root 12288 2月 2 17:01 ../
drwxr-xr-x 4 root root 4096 2月 2 16:44 certs.d/
-rw-r--r-- 1 root root 423 2月 2 19:02 config.toml
-rw-r--r-- 1 root root 886 12月 19 02:48 config.toml.dpkg-dist
|
修改完配置文件后需要重启containerd:
1
2
|
sudo systemctl restart containerd
sudo systemctl status containerd
|
创建命名空间
创建一个命名空间,以便将本练习中创建的资源与集群的其余部分隔离。
1
|
kubectl create namespace mem-example
|
指定内存请求和限制
要为容器指定内存请求,需要在容器资源清单中包含resources: requests字段。同理,要指定内存限制,需包含resources: limits
在本练习中,将创建一个拥有一个容器的 Pod。 容器将会请求100 MiB内存,并且内存会被限制在200 MiB以内。
这是Pod的配置文件(stress镜像从k8s官网拉不下来,daocloud上也没有,好在问ai发现华为云有):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
apiVersion: v1
kind: Pod
metadata:
name: memory-demo
namespace: mem-example
spec:
containers:
- name: memory-demo-ctr
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/polinux/stress:latest
resources:
requests:
memory: "100Mi"
limits:
memory: "200Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
|
创建memory-request-limit.yaml文件,并应用。
1
2
|
vim memory-request-limit.yaml
kubectl apply -f memory-request-limit.yaml
|
配置文件的 args 部分提供了容器启动时的参数。 “–vm-bytes”, “150M” 参数告知容器尝试分配 150 MiB 内存。
1
2
3
4
5
6
7
|
# 查看pod是否已在运行
rust@k8s1:~$ kubectl get pod memory-demo --namespace=mem-example
NAME READY STATUS RESTARTS AGE
memory-demo 1/1 Running 0 4m17s
# 查看pod详细信息
kubectl get pod memory-demo --namespace=mem-example --output=yaml
|
结果显示pod申请了100MiB内存,限制最多使用200MiB内存。
获取pod指标数据
下载metric-server官方yaml文件,修改metric-server镜像为国内镜像源,在 args 下面添加:–kubelet-insecure-tls(解决连接 kubelet 的证书问题,自建集群里很常见)。
1
2
3
4
|
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# 编辑components.yaml文件
vim components.yaml
|
大概是在140行左右,改成下面的内容:
1
2
3
4
5
6
7
8
9
|
containers:
- args:
- --cert-dir=/tmp
- --secure-port=10250
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls
image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server:v0.8.1
|
应用修改后的components.yaml,查看运行状态:
1
2
3
4
5
6
7
8
9
10
11
|
kubectl apply -f components.yaml
# 过几十秒后查看
kubectl get pod -n kube-system | grep metrics-server
rust@k8s1:~$ kubectl get pod -n kube-system | grep metrics-server
metrics-server-6468f846d6-9tggg 1/1 Running 0 11m
# 如果pod没在运行,查看pod事件,定位故障原因
kubectl describe pod <metrics-server_name> -n kube-system
|
运行 kubectl top 命令,获取该 Pod 的指标数据:
1
2
3
4
5
|
kubectl top pod memory-demo --namespace=mem-example
rust@k8s1:~$ kubectl top pod memory-demo --namespace=mem-example
NAME CPU(cores) MEMORY(bytes)
memory-demo 79m 150Mi
|
删除pod
1
|
kubectl delete pod memory-demo --namespace=mem-example
|
超出容器限制的内存
当节点拥有足够的可用内存时,容器可以使用其请求的内存。 但是,容器不允许使用超过其限制的内存。 如果容器分配的内存超过其限制,该容器会成为被终止的候选容器。 如果容器继续消耗超出其限制的内存,则终止容器。如果终止的容器可以被重启,则 kubelet 会重新启动它,就像其他任何类型的运行时失败一样。
在本练习中,将创建一个 Pod,尝试分配超出其限制的内存。
这是一个 Pod 的配置文件,其拥有一个容器,该容器的内存请求为 50 MiB,内存限制为 100 MiB:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-2
namespace: mem-example
spec:
containers:
- name: memory-demo-2-ctr
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/polinux/stress:latest
imagePullPolicy: IfNotPresent
resources:
requests:
memory: "50Mi"
limits:
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]
|
创建编辑配置文件,应用配置文件。
1
2
|
vim memory-request-limit-2.yaml
kubectl apply -f memory-request-limit-2.yaml
|
查看 Pod详细信息,发现pod由于使用内存超出限制被杀掉
1
2
3
|
rust@k8s1:~$ kubectl get pod memory-demo-2 --namespace=mem-example
NAME READY STATUS RESTARTS AGE
memory-demo-2 0/1 OOMKilled 3 (36s ago) 52s
|
以yaml格式输出详细信息,输出结果显示由于内存溢出,pod已被杀死
1
2
3
4
5
6
7
8
9
10
|
kubectl get pod memory-demo-2 --output=yaml --namespace=mem-example
lastState:
terminated:
containerID: containerd://376e96fffa7a75aa31aef9da861cfd5a25bc87d17a67c02a507a3897e2a77840
exitCode: 137
finishedAt: "2026-02-10T03:11:25Z"
reason: OOMKilled
startedAt: "2026-02-10T03:11:25Z"
|
查看pod事件,可以发现pod不断被重启:
1
2
3
4
5
6
7
8
9
10
|
kubectl describe pod memory-demo-2 --namespace=mem-example
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 4m53s default-scheduler Successfully assigned mem-example/memory-demo-2 to k8s2
Normal Pulled 3m18s (x5 over 4m52s) kubelet Container image "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/polinux/stress:latest" already present on machine
Normal Created 3m18s (x5 over 4m52s) kubelet Created container: memory-demo-2-ctr
Normal Started 3m18s (x5 over 4m52s) kubelet Started container memory-demo-2-ctr
Warning BackOff 3m3s (x10 over 4m51s) kubelet Back-off restarting failed container memory-demo-2-ctr in pod memory-demo-2_mem-example(5d9cf5d7-2d00-45f4-af71-bd64f4002eab)
|
删除 Pod
1
|
kubectl delete pod memory-demo-2 --namespace=mem-example
|
超过整个节点容量的内存
内存请求和限制是与容器关联的,而Pod也有内存请求和限制。Pod的内存请求是Pod中所有容器的内存请求之和,Pod 的内存限制是Pod中所有容器的内存限制之和。
Pod的调度基于请求。只有当节点拥有足够满足Pod内存请求的内存时,才会将Pod调度至节点上运行。
在本练习中,将创建一个 Pod,其内存请求超过了集群中的任意一个节点所拥有的内存。
这是该 Pod 的配置文件,其拥有一个请求 1000 GiB 内存的容器,这超过了集群中任何节点的容量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-3
namespace: mem-example
spec:
containers:
- name: memory-demo-3-ctr
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/polinux/stress:latest
imagePullPolicy: IfNotPresent
resources:
requests:
memory: "1000Gi"
limits:
memory: "1000Gi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
|
创建填入上面的内容并应用配置文件。
1
2
|
vim memory-request-limit-3.yaml
kubectl apply -f memory-request-limit-3.yaml
|
查看pod状态:
1
2
3
|
rust@k8s1:~$ kubectl get pod memory-demo-3 --namespace=mem-example
NAME READY STATUS RESTARTS AGE
memory-demo-3 0/1 Pending 0 13s
|
输出结果显示:Pod 处于 PENDING 状态。 这意味着,该 Pod 没有被调度至任何节点上运行,并且它会无限期的保持该状态
查看Pod事件,输出结果显示由于节点内存不足,该容器无法被调度。
1
2
3
4
5
6
7
|
kubectl describe pod memory-demo-3 -n mem-example
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 2m42s default-scheduler 0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 Insufficient memory. preemption: 0/3 nodes are available: 1 Preemption is not helpful for scheduling, 2 No preemption victims found for incoming pod.
|
删除 Pod:
1
|
kubectl delete pod memory-demo-3 --namespace=mem-example
|
原理介绍与补充
如果没有为一个容器指定内存限制,则自动遵循以下情况之一:
通过为集群中运行的容器配置内存请求和限制,可以有效利用集群节点上可用的内存资源。 通过将 Pod 的内存请求保持在较低水平,可以更好地安排 Pod 调度。 通过让内存限制大于内存请求,可以实现:
- Pod 可以进行一些突发活动,从而更好的利用可用内存。
- Pod 在突发活动期间,可使用的内存被限制为合理的数量
CPU的申请与限制和内存相似。需要注意的是,CPU和内存的单位:
- 内存资源的基本单位是字节(byte),可以使用这些后缀将内存表示为纯整数或定点整数:E、P、T、G、M、K、Ei、Pi、Ti、Gi、Mi、Ki
- CPU资源的基本单位是毫核(millicore),1000m =1核。CPU并不直接对应物理CPU核心数量,而是表示调度器分配给容器的CPU时间比例。容器进程可能被调度到服务器的任意CPU上执行(优先NUMA节点)。只有当节点开启
cpu-manager-policy=static,Pod 的request == limit且为整核,才会获得独占 CPU(pin 到特定核)。
底层实现基于Linux的CFS(Completely Fair Scheduler)+cgroup v1/v2来限制CPU配额(quota)和周期(period)。
清理现场
删除命名空间。下面的命令会删除根据这个任务创建的所有 Pod:
1
|
kubectl delete namespace mem-example
|