在 Kubernetes 中限制 Pod占用的最大內(nèi)存資源,有兩種方式實(shí)現(xiàn),基于pod級(jí)的以及基于命名空間級(jí)的
一、兩種配置方式對(duì)比

image.png
二、創(chuàng)建Helloworld項(xiàng)目
2.1 創(chuàng)建java項(xiàng)目
新建一個(gè)普通的helloworld項(xiàng)目,在控制器中添加如下代碼
@GetMapping("/memory")
public String memory() {
try {
int gigabytes = 2;
long startTime = System.currentTimeMillis();
// 分配指定大小的內(nèi)存
allocateMemory(gigabytes);
long endTime = System.currentTimeMillis();
System.out.printf("成功分配 %d GB 內(nèi)存,耗時(shí) %.2f 秒%n",
gigabytes, (endTime - startTime) / 1000.0);
} catch (Exception e) {
e.printStackTrace();
}finally {
return "success";
}
}
/**
* 分配指定GB大小的內(nèi)存
*/
public static void allocateMemory(int gigabytes) {
final int CHUNK_SIZE = 100 * 1024 * 1024; // 每次分配100MB
long totalBytes = (long) gigabytes * 1024 * 1024 * 1024;
int chunks = (int) (totalBytes / CHUNK_SIZE);
List<byte[]> memoryChunks = new ArrayList<>();
// 分段分配內(nèi)存,避免一次性分配過(guò)大導(dǎo)致OOM
for (int i = 0; i < chunks; i++) {
byte[] chunk = new byte[CHUNK_SIZE];
// 填充數(shù)據(jù)確保內(nèi)存被實(shí)際使用
for (int j = 0; j < CHUNK_SIZE; j += 4096) {
chunk[j] = 42;
}
memoryChunks.add(chunk);
// 打印進(jìn)度
if ((i + 1) % 10 == 0) {
System.out.printf("已分配 %.2f GB 內(nèi)存%n", (i + 1) * 0.1);
}
}
// 處理剩余的不足CHUNK_SIZE的部分
long remainingBytes = totalBytes % CHUNK_SIZE;
if (remainingBytes > 0) {
byte[] chunk = new byte[(int) remainingBytes];
for (int j = 0; j < remainingBytes; j += 4096) {
chunk[j] = 42;
}
memoryChunks.add(chunk);
}
}
說(shuō)明:程序中添加了一個(gè)接口:/memory ,每次調(diào)用就會(huì)占用系統(tǒng)2G內(nèi)存空間
2.2 準(zhǔn)備部署文檔
$ cat helloworld.yml
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: helloworld
name: helloworld
namespace: dc-prod-ns
spec:
replicas: 1
selector:
matchLabels:
k8s-app: helloworld
strategy:
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
k8s-app: helloworld
spec:
containers:
- env:
- name: APP_OPTIONS
value: '-Xms8192m -Xmx8192m -Xss1024k'
envFrom:
- configMapRef:
name: dc-appvar
image: helloworld:202507010334
imagePullPolicy: Never
name: helloworld
ports:
- containerPort: 8080
protocol: TCP
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 60
---
apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: helloworld
name: helloworld
namespace: dc-prod-ns
spec:
ports:
- name: http-8080
nodePort: 31510
port: 31510
protocol: TCP
targetPort: 8080
selector:
k8s-app: helloworld
type: NodePort
三、創(chuàng)建建LimitRange
$ cat mem-limit.yml
apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
namespace: dc-prod-ns
spec:
limits:
- type: Container
max:
memory: "4Gi" # 容器最大內(nèi)存限制
min:
memory: "100Mi" # 容器最小內(nèi)存限制
defaultRequest:
memory: "1Gi" # 將默認(rèn)請(qǐng)求值改為 1Gi
$ kubectl apply -f mem-limit.yml
$ kubectl get limits -n dc-prod-ns
NAME CREATED AT
mem-limit-range 2025-07-01T02:10:48Z
$ kc describe limits mem-limit-range -n dc-prod-ns
Name: mem-limit-range
Namespace: dc-prod-ns
Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
---- -------- --- --- --------------- ------------- -----------------------
Container memory 100Mi 4Gi 1Gi 4Gi -
四、LimitRange VS JAVA啟動(dòng)參數(shù)
4.1 Xmx > LimitRange-Max
說(shuō)明:從步驟2.2中可以看到,這里傳入了java程序啟動(dòng)的環(huán)境變量值:-Xms8192m -Xmx8192m -Xss1024k
表明java程序初始與最大堆內(nèi)存都是8G > LimitRange中的Max限制:4G
現(xiàn)在查看pod狀態(tài)
$ kubectl apply -f helloworld.yml
$ kubectl top pods -n dc-prod-ns | grep hello # 啟動(dòng)后占用397Mi
helloworld-6f84dd79d4-vhdzn 3m 397Mi
$ curl http://192.168.xx.xx:31510/memory # K8S任意節(jié)點(diǎn)IP
success
# 稍等一會(huì)兒
$ kubectl top pods -n dc-prod-ns | grep hello
helloworld-6f84dd79d4-rt9gn 162m 3983Mi
$ curl http://192.168.xx.xx:31510/memory # 再請(qǐng)求一次
curl: (52) Empty reply from server # 請(qǐng)求失敗
$ kubectl get pods -n dc-prod-ns | grep hellow
helloworld-6f84dd79d4-rt9gn 1/1 Running 2 8m24s
$ kubectl logs helloworld-6f84dd79d4-rt9gn -n dc-prod-ns
......
./run.sh: line 6: 22 Killed java $APP_OPTIONS -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Djava.security.egd=file:/dev/./urandom -jar /app.jar
# 此時(shí)可以看到pod因?yàn)閛om被kill了。
# 因此,由于此時(shí)探針無(wú)效,pod就被重啟了
$ kg events -n dc-prod-ns
......
13m Normal Killing pod/helloworld-6f84dd79d4-vhdzn Stopping container helloworld
13m Normal SuccessfulCreate replicaset/helloworld-6f84dd79d4 Created pod: helloworld-6f84dd7
從上面實(shí)驗(yàn)可以看出,當(dāng)Xmx較大時(shí):
- 不影響pod創(chuàng)建,因?yàn)閖ava啟動(dòng)時(shí)并沒有立即實(shí)際占用這么大的內(nèi)存
- 但是當(dāng)程序占用內(nèi)存超過(guò)命名空間的limits-Max值時(shí),pod就會(huì)OOM
4.2 Xmx <= LimitRange-Max
修改java啟動(dòng)參數(shù)為:-Xms4096m -Xmx4096m -Xss1024k
此時(shí) Xmx <= limit-Max
$ kubectl top pods -n dc-prod-ns | grep hello # 剛啟動(dòng)后占用387M內(nèi)存
helloworld-6f84dd79d4-rt9gn 2m 387Mi
$ curl http://192.168.xx.xx:31510/memory
success
# 稍等一會(huì)兒
$ kubectl top pods -n dc-prod-ns | grep hello
helloworld-5dd89d9f68-qpfmw 3m 3094Mi
# 此后多次重復(fù)請(qǐng)求 http://192.168.xx.xx:31510/memory,發(fā)現(xiàn)pod占用內(nèi)存在到達(dá)3996Mi后不再增長(zhǎng),且也未發(fā)生oom
可以看到,此時(shí)Xmx有效地抑制了程序?qū)τ趦?nèi)存的占用
因此,在pod中定義的java啟動(dòng)參數(shù),Xmx 一定不能大于LimitRange中定義的Max值,否則就有可能發(fā)生oom
五、Pod中顯式配置Limits
5.1 pod中的limits比LimitRange中定義的小
修改helloworld的部署文件,添加limits.memory 部分
$ cat helloworld.yml
image: helloworld:202507010334
resources:
limits:
memory: "512Mi" 限制最大內(nèi)存為 512 MiB
......
$ kubectl apply -f helloworld.yml
deployment.apps/helloworld configured
service/helloworld unchanged
$ kubectl top pods -n dc-prod-ns | grep hello
helloworld-b799bbcd4-tsrzw 844m 379Mi
$ curl http://192.168.xx.xx:31510/memory # 只調(diào)用一次就被killed了
curl: (52) Empty reply from server
因?yàn)樵趐od中定義的limits.memory比較小,因此很快就觸發(fā)了oom
5.2 Pod中的limits比LimitRange中定義的大
$ cat helloworld.yml
image: helloworld:202507010334
resources:
limits:
memory: "5Gi" 限制最大內(nèi)存為 512 MiB
......
$ kubectl get events -n dc-prod-ns
Error creating: pods "helloworld-55f7b48d5c-zgqd8" is forbidden: maximum memory usage per Container is 4Gi, but limit is 5Gi
可以看到,若Pod中的limits比LimitRange中定義的大,此時(shí)會(huì)無(wú)法創(chuàng)建pod
也即:個(gè)體要服從全局
六、總結(jié)
- 使用LimitRange來(lái)統(tǒng)一定義資源限定比較簡(jiǎn)潔
- 在pod部署文件中定義java啟動(dòng)參數(shù)時(shí),Xmx 一定不能大于LimitRange中定義的Max值,否則就有可能發(fā)生oom
- 如果要實(shí)現(xiàn)精確的資源請(qǐng)求與限制,可以在pod部署文件中指定,但是要注意limit不能超過(guò)LimitRange中對(duì)應(yīng)值