[{"data":1,"prerenderedAt":1168},["ShallowReactive",2],{"article-\u002Farticles\u002Fk8s-hpa":3},{"id":4,"title":5,"body":6,"date":1153,"description":1154,"excerpt":1155,"extension":1156,"meta":1157,"navigation":1034,"path":1158,"seo":1159,"stem":1160,"tags":1161,"__hash__":1167},"articles\u002Farticles\u002Fk8s-hpa.md","Kubernetes HPA 為什麼看 Request 而不是 Limit？從擴展公式反推 CPU Request／Limit 怎麼設",{"type":7,"value":8,"toc":1134},"minimark",[9,18,26,31,34,44,55,61,64,94,98,103,114,123,127,130,180,187,191,198,202,209,238,242,247,269,272,364,367,457,643,646,653,743,747,793,797,804,822,825,905,912,916,923,937,947,951,958,972,975,999,1003,1077,1092,1095,1130],[10,11,12,13,17],"p",{},"設定 HPA（Horizontal Pod Autoscaler）時，最常被問的一個問題是：「為什麼 ",[14,15,16],"code",{},"averageUtilization: 75"," 的 75% 是相對 Request 算，而不是相對 Limit？Limit 才是 Pod 真正能用到的上限啊。」",[10,19,20,21,25],{},"這個問題背後其實藏著 HPA 的設計哲學：",[22,23,24],"strong",{},"HPA 不是在 Pod 快撐不住時才救火，而是在用量超過「預期常態」的某個比例時，就提早擴展","，替新 Pod 的啟動與就緒爭取緩衝時間。理解這點之後，Request、Limit、HPA Target 三個參數該怎麼設，就會變成一個可以推導的問題，而不是憑感覺填數字。",[27,28,30],"h2",{"id":29},"先看-hpa-到底怎麼算","先看 HPA 到底怎麼算",[10,32,33],{},"HPA 控制器預設每 15 秒執行一次，從 metrics-server 取得每個 Pod 的 CPU 用量，然後套用這條公式：",[35,36,41],"pre",{"className":37,"code":39,"language":40},[38],"language-text","desiredReplicas = ceil( currentReplicas × currentUtilization \u002F targetUtilization )\n\ncurrentUtilization = 所有 Pod 實際 CPU 用量的平均 ÷ 每個 Pod 的 CPU Request\n","text",[14,42,39],{"__ignoreMap":43},"",[10,45,46,47,50,51,54],{},"舉例：目前 2 個 Pod、Request 為 ",[14,48,49],{},"800m","、Target 為 75%，兩個 Pod 平均用了 ",[14,52,53],{},"1000m","：",[35,56,59],{"className":57,"code":58,"language":40},[38],"currentUtilization = 1000m \u002F 800m = 125%\ndesiredReplicas    = ceil( 2 × 125 \u002F 75 ) = ceil(3.33) = 4\n",[14,60,58],{"__ignoreMap":43},[10,62,63],{},"從公式可以讀出幾個重要的事實：",[65,66,67,74,84],"ul",{},[68,69,70,73],"li",{},[22,71,72],{},"分母是 Request，Limit 完全不參與計算。"," 使用率可以超過 100%（上例就是 125%），因為 Pod 被允許用到 Request 以上、Limit 以下的空間。",[68,75,76,79,80,83],{},[22,77,78],{},"沒有設定 CPU Request 的 Pod，HPA 算不出使用率。"," 這時 HPA 會顯示 ",[14,81,82],{},"\u003Cunknown>","，完全不會擴展。如果 Pod 裡有 sidecar（例如 Istio 的 envoy），sidecar 也要設 Request，因為使用率是整個 Pod 所有容器的加總。",[68,85,86,89,90,93],{},[22,87,88],{},"有 10% 的容忍區間。"," 當 ",[14,91,92],{},"currentUtilization \u002F targetUtilization"," 落在 0.9～1.1 之間時，HPA 不會動作，避免在目標值附近來回抖動。",[27,95,97],{"id":96},"為什麼基於-request-而不是-limit","為什麼基於 Request 而不是 Limit？",[99,100,102],"h3",{"id":101},"_1-用-limit-算擴展時機一定太晚","1. 用 Limit 算，擴展時機一定太晚",[10,104,105,106,109,110,113],{},"Limit 是硬性天花板。CPU 用量碰到 Limit 時，Linux 的 CFS 排程器會開始 ",[22,107,108],{},"Throttling","——容器在每個 100ms 週期內用完配額就被強制暫停，反映到應用程式上就是延遲突然飆高、timeout 開始出現。記憶體碰到 Limit 則更直接，容器會被 ",[22,111,112],{},"OOMKilled","。",[10,115,116,117,122],{},"而新 Pod 從被建立到真正能接流量，要經過排程、拉 image、應用程式啟動、通過 Readiness Probe，常常要 1～3 分鐘（探針怎麼設計可以參考",[118,119,121],"a",{"href":120},"\u002Farticles\u002Fdotnet-k8s-health-checks","《Kubernetes 探針怎麼設計？》","）。如果等到接近 Limit 才觸發擴展，這段空窗期裡舊 Pod 早就被節流或砍掉了。",[99,124,126],{"id":125},"_2-request-才是預期常態用量","2. Request 才是「預期常態用量」",[10,128,129],{},"在容量規劃上，兩個參數的角色是這樣分工的：",[131,132,133,150],"table",{},[134,135,136],"thead",{},[137,138,139,144,147],"tr",{},[140,141,143],"th",{"align":142},"left","參數",[140,145,146],{"align":142},"角色",[140,148,149],{"align":142},"意義",[151,152,153,167],"tbody",{},[137,154,155,161,164],{},[156,157,158],"td",{"align":142},[22,159,160],{},"Request",[156,162,163],{"align":142},"保證水位",[156,165,166],{"align":142},"應用程式在正常流量下的基準消耗量，Kubernetes 保證一定分得到",[137,168,169,174,177],{},[156,170,171],{"align":142},[22,172,173],{},"Limit",[156,175,176],{"align":142},"突發容量（Burst capacity）",[156,178,179],{"align":142},"應對短時間尖峰的彈性空間，不保證一定分得到",[10,181,182,183,186],{},"HPA 的邏輯因此很自然：",[22,184,185],{},"用量超過常態基準的某個比例，就該加副本了","；而加副本的這段時間，超出的負載由 Request 到 Limit 之間的彈性空間來吸收。",[99,188,190],{"id":189},"_3-與-scheduler-的資源記帳方式一致","3. 與 Scheduler 的資源記帳方式一致",[10,192,193,194,197],{},"Kubernetes Scheduler 決定 Pod 要放到哪個 Node 時，看的是 ",[22,195,196],{},"Request 的加總","，而不是 Limit，也不是實際用量。HPA 也用 Request 計算，代表「HPA 決定加幾個 Pod」跟「叢集能不能放得下這些 Pod」用的是同一套帳本——HPA 算出要 4 個 Pod，Scheduler 就準確知道需要 4 × 800m 的可分配空間。",[27,199,201],{"id":200},"常見疑慮流量長期大於-request會不會無限擴展","常見疑慮：流量長期大於 Request，會不會無限擴展？",[10,203,204,205,208],{},"會一直擴展，但這是 HPA ",[22,206,207],{},"預期中","的健康行為——流量本來就超過基礎水位了，系統本來就該增加副本數來攤平負載。要防止的不是「擴展」本身，而是「擴展到把叢集資源榨乾」，正確的作法是：",[210,211,212,222,228],"ol",{},[68,213,214,221],{},[22,215,216,217,220],{},"設定 ",[14,218,219],{},"maxReplicas"," 上限","，並確認叢集（或 Cluster Autoscaler 的 Node 上限）真的放得下這麼多副本。",[68,223,224,227],{},[22,225,226],{},"設定合理的 Target Utilization","，一般落在 60%～80%。越低代表擴得越早、越保守，但閒置成本越高。",[68,229,230,233,234,237],{},[22,231,232],{},"Request 要貼近實際常態用量。"," 設得極低（例如為了塞更多 Pod 而只給 ",[14,235,236],{},"100m","），HPA 就會在常態流量下一直以為使用率爆表，副本數永遠頂在上限。",[27,239,241],{"id":240},"實戰情境常態-05-core峰值-2-core-怎麼設","實戰情境：常態 0.5 core、峰值 2 core 怎麼設",[10,243,244],{},[22,245,246],{},"情境假設（單一 Pod 的用量）：",[65,248,249,256,262],{},[68,250,251,252,255],{},"常態用量：",[14,253,254],{},"500m","（0.5 core）",[68,257,258,259,261],{},"流量翻倍：",[14,260,53],{},"（1 core）",[68,263,264,265,268],{},"極端峰值：",[14,266,267],{},"2000m","（2 core，常態的四倍）",[99,270,271],{"id":271},"推薦參數配置",[131,273,274,286],{},[134,275,276],{},[137,277,278,280,283],{},[140,279,143],{"align":142},[140,281,282],{"align":142},"建議設定值",[140,284,285],{"align":142},"說明",[151,287,288,302,316,335,350],{},[137,289,290,295,299],{},[156,291,292],{"align":142},[22,293,294],{},"CPU Request",[156,296,297],{"align":142},[14,298,49],{},[156,300,301],{"align":142},"排程的保證水位，也是 HPA 計算使用率的分母",[137,303,304,309,313],{},[156,305,306],{"align":142},[22,307,308],{},"CPU Limit",[156,310,311],{"align":142},[14,312,267],{},[156,314,315],{"align":142},"硬限制，用來吸收極端峰值，避免新 Pod 啟動空窗期被 Throttling",[137,317,318,323,328],{},[156,319,320],{"align":142},[22,321,322],{},"HPA Target",[156,324,325],{"align":142},[14,326,327],{},"75%",[156,329,330,331,334],{"align":142},"平均用量達到 ",[14,332,333],{},"600m","（800m × 75%）時開始擴展",[137,336,337,342,347],{},[156,338,339],{"align":142},[22,340,341],{},"minReplicas",[156,343,344],{"align":142},[14,345,346],{},"2",[156,348,349],{"align":142},"至少兩個副本，單一 Pod 掛掉時服務不中斷",[137,351,352,356,361],{},[156,353,354],{"align":142},[22,355,219],{},[156,357,358],{"align":142},[14,359,360],{},"10",[156,362,363],{"align":142},"依叢集容量與下游（DB、外部 API）能承受的併發數決定",[10,365,366],{},"對應的 YAML：",[35,368,372],{"className":369,"code":370,"language":371,"meta":43,"style":43},"language-yaml shiki shiki-themes github-dark","# deployment.yaml（節錄）\nresources:\n  requests:\n    cpu: 800m\n    memory: 512Mi\n  limits:\n    cpu: 2000m\n    memory: 512Mi   # 記憶體建議 Request = Limit，原因見後文\n","yaml",[14,373,374,383,394,402,415,426,434,444],{"__ignoreMap":43},[375,376,379],"span",{"class":377,"line":378},"line",1,[375,380,382],{"class":381},"sAwPA","# deployment.yaml（節錄）\n",[375,384,386,390],{"class":377,"line":385},2,[375,387,389],{"class":388},"s4JwU","resources",[375,391,393],{"class":392},"s95oV",":\n",[375,395,397,400],{"class":377,"line":396},3,[375,398,399],{"class":388},"  requests",[375,401,393],{"class":392},[375,403,405,408,411],{"class":377,"line":404},4,[375,406,407],{"class":388},"    cpu",[375,409,410],{"class":392},": ",[375,412,414],{"class":413},"sU2Wk","800m\n",[375,416,418,421,423],{"class":377,"line":417},5,[375,419,420],{"class":388},"    memory",[375,422,410],{"class":392},[375,424,425],{"class":413},"512Mi\n",[375,427,429,432],{"class":377,"line":428},6,[375,430,431],{"class":388},"  limits",[375,433,393],{"class":392},[375,435,437,439,441],{"class":377,"line":436},7,[375,438,407],{"class":388},[375,440,410],{"class":392},[375,442,443],{"class":413},"2000m\n",[375,445,447,449,451,454],{"class":377,"line":446},8,[375,448,420],{"class":388},[375,450,410],{"class":392},[375,452,453],{"class":413},"512Mi",[375,455,456],{"class":381},"   # 記憶體建議 Request = Limit，原因見後文\n",[35,458,460],{"className":369,"code":459,"language":371,"meta":43,"style":43},"# hpa.yaml\napiVersion: autoscaling\u002Fv2\nkind: HorizontalPodAutoscaler\nmetadata:\n  name: order-api\nspec:\n  scaleTargetRef:\n    apiVersion: apps\u002Fv1\n    kind: Deployment\n    name: order-api\n  minReplicas: 2\n  maxReplicas: 10\n  metrics:\n    - type: Resource\n      resource:\n        name: cpu\n        target:\n          type: Utilization\n          averageUtilization: 75\n",[14,461,462,467,477,487,494,504,511,518,528,539,549,561,572,580,594,602,613,621,632],{"__ignoreMap":43},[375,463,464],{"class":377,"line":378},[375,465,466],{"class":381},"# hpa.yaml\n",[375,468,469,472,474],{"class":377,"line":385},[375,470,471],{"class":388},"apiVersion",[375,473,410],{"class":392},[375,475,476],{"class":413},"autoscaling\u002Fv2\n",[375,478,479,482,484],{"class":377,"line":396},[375,480,481],{"class":388},"kind",[375,483,410],{"class":392},[375,485,486],{"class":413},"HorizontalPodAutoscaler\n",[375,488,489,492],{"class":377,"line":404},[375,490,491],{"class":388},"metadata",[375,493,393],{"class":392},[375,495,496,499,501],{"class":377,"line":417},[375,497,498],{"class":388},"  name",[375,500,410],{"class":392},[375,502,503],{"class":413},"order-api\n",[375,505,506,509],{"class":377,"line":428},[375,507,508],{"class":388},"spec",[375,510,393],{"class":392},[375,512,513,516],{"class":377,"line":436},[375,514,515],{"class":388},"  scaleTargetRef",[375,517,393],{"class":392},[375,519,520,523,525],{"class":377,"line":446},[375,521,522],{"class":388},"    apiVersion",[375,524,410],{"class":392},[375,526,527],{"class":413},"apps\u002Fv1\n",[375,529,531,534,536],{"class":377,"line":530},9,[375,532,533],{"class":388},"    kind",[375,535,410],{"class":392},[375,537,538],{"class":413},"Deployment\n",[375,540,542,545,547],{"class":377,"line":541},10,[375,543,544],{"class":388},"    name",[375,546,410],{"class":392},[375,548,503],{"class":413},[375,550,552,555,557],{"class":377,"line":551},11,[375,553,554],{"class":388},"  minReplicas",[375,556,410],{"class":392},[375,558,560],{"class":559},"sDLfK","2\n",[375,562,564,567,569],{"class":377,"line":563},12,[375,565,566],{"class":388},"  maxReplicas",[375,568,410],{"class":392},[375,570,571],{"class":559},"10\n",[375,573,575,578],{"class":377,"line":574},13,[375,576,577],{"class":388},"  metrics",[375,579,393],{"class":392},[375,581,583,586,589,591],{"class":377,"line":582},14,[375,584,585],{"class":392},"    - ",[375,587,588],{"class":388},"type",[375,590,410],{"class":392},[375,592,593],{"class":413},"Resource\n",[375,595,597,600],{"class":377,"line":596},15,[375,598,599],{"class":388},"      resource",[375,601,393],{"class":392},[375,603,605,608,610],{"class":377,"line":604},16,[375,606,607],{"class":388},"        name",[375,609,410],{"class":392},[375,611,612],{"class":413},"cpu\n",[375,614,616,619],{"class":377,"line":615},17,[375,617,618],{"class":388},"        target",[375,620,393],{"class":392},[375,622,624,627,629],{"class":377,"line":623},18,[375,625,626],{"class":388},"          type",[375,628,410],{"class":392},[375,630,631],{"class":413},"Utilization\n",[375,633,635,638,640],{"class":377,"line":634},19,[375,636,637],{"class":388},"          averageUtilization",[375,639,410],{"class":392},[375,641,642],{"class":559},"75\n",[99,644,645],{"id":645},"套進公式驗算一次",[10,647,648,649,652],{},"假設 ",[14,650,651],{},"minReplicas: 2","，下表是不同流量下 HPA 的反應（「單 Pod 用量」指擴展前每個 Pod 承受的量）：",[131,654,655,674],{},[134,656,657],{},[137,658,659,662,665,668,671],{},[140,660,661],{"align":142},"流量情境",[140,663,664],{"align":142},"單 Pod 用量",[140,666,667],{"align":142},"使用率",[140,669,670],{"align":142},"HPA 算出的副本數",[140,672,673],{"align":142},"擴展後單 Pod 用量",[151,675,676,695,718],{},[137,677,678,681,685,688,691],{},[156,679,680],{"align":142},"常態",[156,682,683],{"align":142},[14,684,254],{},[156,686,687],{"align":142},"62.5%",[156,689,690],{"align":142},"2（不動作）",[156,692,693],{"align":142},[14,694,254],{},[137,696,697,700,704,707,713],{},[156,698,699],{"align":142},"翻倍",[156,701,702],{"align":142},[14,703,53],{},[156,705,706],{"align":142},"125%",[156,708,709,710],{"align":142},"ceil(2 × 125 \u002F 75) = ",[22,711,712],{},"4",[156,714,715,717],{"align":142},[14,716,254],{},"（62.5%）",[137,719,720,723,727,730,736],{},[156,721,722],{"align":142},"極端峰值",[156,724,725],{"align":142},[14,726,267],{},[156,728,729],{"align":142},"250%",[156,731,732,733],{"align":142},"ceil(2 × 250 \u002F 75) = ",[22,734,735],{},"7",[156,737,738,739,742],{"align":142},"約 ",[14,740,741],{},"571m","（71%）",[99,744,746],{"id":745},"為什麼這樣設定最合理","為什麼這樣設定最合理？",[210,748,749,774,783],{},[68,750,751,754,755,757,758,761,762,764,765,767,768,770,771,773],{},[22,752,753],{},"常態用量不會觸發無效擴展。","\n如果 Request 照常態用量設成 ",[14,756,254],{},"、Target 80%，觸發點是 ",[14,759,760],{},"400m","，常態的 ",[14,763,254],{}," 就已經是 125% 使用率，HPA 會在平常就不斷擴展。把 Request 提高到 ",[14,766,49],{},"，觸發點落在 ",[14,769,333],{},"，常態 ",[14,772,254],{}," 時使用率只有 62.5%，系統會安穩維持在最小副本數。",[68,775,776,779,780,782],{},[22,777,778],{},"流量翻倍時平順擴展。","\n用量跨過 ",[14,781,333],{}," 往 1 core 走時 HPA 就開始擴展，這時離 Limit（2 core）還有一倍的空間，舊 Pod 完全不會被節流，使用者感受不到擴展正在發生。",[68,784,785,788,789,792],{},[22,786,787],{},"極端峰值時，Limit 負責掩護空窗期。","\n四倍流量瞬間湧入時，HPA 偵測 + 新 Pod 啟動通常要 1～3 分鐘。這段時間舊 Pod 靠 ",[14,790,791],{},"limit: 2000m"," 直接 Burst 到 2 core 消化請求；等新 Pod 就緒、流量被攤平，每個 Pod 的用量又回到 70% 上下的健康水位。",[27,794,796],{"id":795},"擴縮的節奏behavior-設定","擴縮的節奏：behavior 設定",[10,798,799,800,803],{},"HPA 預設的擴縮節奏是「",[22,801,802],{},"擴得快、縮得慢","」：",[65,805,806,812],{},[68,807,808,811],{},[22,809,810],{},"Scale Up","：沒有穩定窗口，每 15 秒最多可以翻倍（或多加 4 個 Pod，取較大者）。",[68,813,814,817,818,821],{},[22,815,816],{},"Scale Down","：有 ",[22,819,820],{},"300 秒","的穩定窗口——HPA 會取過去 5 分鐘內算出的最大建議值，避免流量一掉下去就馬上縮、下一波尖峰又要重新擴。",[10,823,824],{},"大多數情境用預設值就夠了。如果服務的流量是「尖峰很短、但一小時來好幾次」的鋸齒型，可以把縮容放得更保守：",[35,826,828],{"className":369,"code":827,"language":371,"meta":43,"style":43},"spec:\n  behavior:\n    scaleDown:\n      stabilizationWindowSeconds: 600   # 觀察 10 分鐘才縮\n      policies:\n        - type: Pods\n          value: 1                      # 每分鐘最多縮 1 個\n          periodSeconds: 60\n",[14,829,830,836,843,850,863,870,882,895],{"__ignoreMap":43},[375,831,832,834],{"class":377,"line":378},[375,833,508],{"class":388},[375,835,393],{"class":392},[375,837,838,841],{"class":377,"line":385},[375,839,840],{"class":388},"  behavior",[375,842,393],{"class":392},[375,844,845,848],{"class":377,"line":396},[375,846,847],{"class":388},"    scaleDown",[375,849,393],{"class":392},[375,851,852,855,857,860],{"class":377,"line":404},[375,853,854],{"class":388},"      stabilizationWindowSeconds",[375,856,410],{"class":392},[375,858,859],{"class":559},"600",[375,861,862],{"class":381},"   # 觀察 10 分鐘才縮\n",[375,864,865,868],{"class":377,"line":417},[375,866,867],{"class":388},"      policies",[375,869,393],{"class":392},[375,871,872,875,877,879],{"class":377,"line":428},[375,873,874],{"class":392},"        - ",[375,876,588],{"class":388},[375,878,410],{"class":392},[375,880,881],{"class":413},"Pods\n",[375,883,884,887,889,892],{"class":377,"line":436},[375,885,886],{"class":388},"          value",[375,888,410],{"class":392},[375,890,891],{"class":559},"1",[375,893,894],{"class":381},"                      # 每分鐘最多縮 1 個\n",[375,896,897,900,902],{"class":377,"line":446},[375,898,899],{"class":388},"          periodSeconds",[375,901,410],{"class":392},[375,903,904],{"class":559},"60\n",[10,906,907,908,911],{},"另外要注意：HPA 對",[22,909,910],{},"剛啟動的 Pod"," 有保護機制——Pod 還沒 Ready、或剛啟動時（預設 5 分鐘內）的 CPU 數據會被保守處理，避免 JIT 編譯、快取預熱這種啟動期的 CPU 尖峰讓 HPA 誤以為負載很高、連鎖擴展。這也是 Readiness Probe 要設計正確的另一個理由。",[27,913,915],{"id":914},"記憶體為什麼不建議拿來當-hpa-指標","記憶體為什麼不建議拿來當 HPA 指標？",[10,917,918,919,922],{},"HPA 也支援 ",[14,920,921],{},"memory"," 指標，但對 .NET、Java 這類有 GC 的執行環境通常不適合：",[65,924,925,931],{},[68,926,927,930],{},[22,928,929],{},"記憶體用量不會隨流量下降而回落。"," GC 回收後，執行環境常常不會把記憶體還給作業系統，擴上去之後使用率一直維持高檔，HPA 就永遠縮不回來。",[68,932,933,936],{},[22,934,935],{},"記憶體不可壓縮。"," CPU 超用只會被節流變慢，記憶體超過 Limit 是直接被 OOMKilled，沒有「Burst 一下撐過去」的空間。",[10,938,939,940,946],{},"所以一般建議是：",[22,941,942,943],{},"CPU 用 HPA 彈性擴展，記憶體則設 ",[14,944,945],{},"Request = Limit","，讓每個 Pod 拿到的記憶體是確定的，排程與 OOM 行為都可預期。如果業務上真正的瓶頸是請求量、佇列長度（例如 Kafka consumer lag），比起記憶體，更值得透過 Prometheus Adapter 或 KEDA 改用自訂指標來擴展。",[27,948,950],{"id":949},"️-架構師視角的提醒overcommit-風險","⚠️ 架構師視角的提醒：Overcommit 風險",[10,952,953,954,957],{},"上面的設定中，CPU Request（0.8）與 Limit（2.0）差距達 2.5 倍，這代表高度依賴 Kubernetes 的 ",[22,955,956],{},"Overcommit（超賣）"," 機制：Scheduler 只依 Request 把 Pod 塞進 Node，Node 上所有 Pod 的 Limit 加總可以遠大於 Node 的實體 CPU。",[65,959,960,966],{},[68,961,962,965],{},[22,963,964],{},"優點","：應對偶發峰值非常有效，平常不用為峰值預留整顆 CPU，資源利用率高。",[68,967,968,971],{},[22,969,970],{},"風險","：如果同一個 Node 上的多個 Pod「同時」迎來峰值，Node 的實體 CPU 不夠分，大家會依 Request 的比例分配 CPU 時間，結果是每個 Pod 都拿不到它以為能用的 2 core，延遲一起上升。",[10,973,974],{},"實務上的因應方式：",[210,976,977,983,993],{},[68,978,979,982],{},[22,980,981],{},"監控 Node 層級的 CPU 使用率","，不只是 Pod 層級。Node 長期高於 70%～80%，代表 Burst 空間已經被吃掉了。",[68,984,985,988,989,992],{},[22,986,987],{},"同一個服務的副本要分散到不同 Node。"," 用 ",[14,990,991],{},"topologySpreadConstraints"," 或 Pod Anti-Affinity，避免「同一波流量」讓擠在同一台 Node 上的副本同時 Burst。",[68,994,995,998],{},[22,996,997],{},"核心服務考慮縮小 Request／Limit 差距","，甚至設成 Guaranteed QoS（Request = Limit），用成本換取可預期性；非核心、可容忍變慢的服務，才放大 Overcommit 的比例。",[27,1000,1002],{"id":1001},"上線後怎麼確認-hpa-有正常運作","上線後怎麼確認 HPA 有正常運作",[35,1004,1008],{"className":1005,"code":1006,"language":1007,"meta":43,"style":43},"language-bash shiki shiki-themes github-dark","# 看目前使用率 \u002F 目標值與副本數，TARGETS 若是 \u003Cunknown> 通常是沒設 Request 或 metrics-server 異常\nkubectl get hpa order-api\n\n# 看 HPA 的事件與每次擴縮的原因\nkubectl describe hpa order-api\n\n# 看每個 Pod 的實際 CPU \u002F 記憶體用量\nkubectl top pod -l app=order-api\n","bash",[14,1009,1010,1015,1030,1036,1041,1052,1056,1061],{"__ignoreMap":43},[375,1011,1012],{"class":377,"line":378},[375,1013,1014],{"class":381},"# 看目前使用率 \u002F 目標值與副本數，TARGETS 若是 \u003Cunknown> 通常是沒設 Request 或 metrics-server 異常\n",[375,1016,1017,1021,1024,1027],{"class":377,"line":385},[375,1018,1020],{"class":1019},"svObZ","kubectl",[375,1022,1023],{"class":413}," get",[375,1025,1026],{"class":413}," hpa",[375,1028,1029],{"class":413}," order-api\n",[375,1031,1032],{"class":377,"line":396},[375,1033,1035],{"emptyLinePlaceholder":1034},true,"\n",[375,1037,1038],{"class":377,"line":404},[375,1039,1040],{"class":381},"# 看 HPA 的事件與每次擴縮的原因\n",[375,1042,1043,1045,1048,1050],{"class":377,"line":417},[375,1044,1020],{"class":1019},[375,1046,1047],{"class":413}," describe",[375,1049,1026],{"class":413},[375,1051,1029],{"class":413},[375,1053,1054],{"class":377,"line":428},[375,1055,1035],{"emptyLinePlaceholder":1034},[375,1057,1058],{"class":377,"line":436},[375,1059,1060],{"class":381},"# 看每個 Pod 的實際 CPU \u002F 記憶體用量\n",[375,1062,1063,1065,1068,1071,1074],{"class":377,"line":446},[375,1064,1020],{"class":1019},[375,1066,1067],{"class":413}," top",[375,1069,1070],{"class":413}," pod",[375,1072,1073],{"class":559}," -l",[375,1075,1076],{"class":413}," app=order-api\n",[10,1078,1079,1080,1083,1084,1087,1088,1091],{},"如果有接 Prometheus，建議再觀察 ",[14,1081,1082],{},"container_cpu_cfs_throttled_periods_total"," 對 ",[14,1085,1086],{},"container_cpu_cfs_periods_total"," 的比例——",[22,1089,1090],{},"Throttling 比例持續偏高，代表 Limit 太緊，或 HPA 擴得太慢","，即使平均 CPU 使用率看起來很健康也一樣。",[27,1093,1094],{"id":1094},"總結",[65,1096,1097,1104,1110,1116,1124],{},[68,1098,1099,1100,1103],{},"HPA 的使用率是 ",[22,1101,1102],{},"實際用量 ÷ Request","，Limit 不參與計算；沒設 Request，HPA 就不會動。",[68,1105,1106,1109],{},[22,1107,1108],{},"Request 設成「常態用量 ÷ Target」再留一點餘裕","，常態流量才不會觸發無效擴展（本例：500m ÷ 75% ≈ 667m，取 800m）。",[68,1111,1112,1115],{},[22,1113,1114],{},"Limit 的任務是掩護擴展空窗期","，讓舊 Pod 在新 Pod 就緒前能 Burst 撐住峰值。",[68,1117,1118,1123],{},[22,1119,1120,1122],{},[14,1121,219],{}," 才是防止無限擴展的開關","，而不是把 Request 設高或 Target 設高。",[68,1125,1126,1127,1129],{},"CPU 適合 HPA，記憶體建議 ",[14,1128,945],{},"；Request 與 Limit 差距越大，越要盯緊 Node 層級的資源與副本分散。",[1131,1132,1133],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}",{"title":43,"searchDepth":385,"depth":385,"links":1135},[1136,1137,1142,1143,1148,1149,1150,1151,1152],{"id":29,"depth":385,"text":30},{"id":96,"depth":385,"text":97,"children":1138},[1139,1140,1141],{"id":101,"depth":396,"text":102},{"id":125,"depth":396,"text":126},{"id":189,"depth":396,"text":190},{"id":200,"depth":385,"text":201},{"id":240,"depth":385,"text":241,"children":1144},[1145,1146,1147],{"id":271,"depth":396,"text":271},{"id":645,"depth":396,"text":645},{"id":745,"depth":396,"text":746},{"id":795,"depth":385,"text":796},{"id":914,"depth":385,"text":915},{"id":949,"depth":385,"text":950},{"id":1001,"depth":385,"text":1002},{"id":1094,"depth":385,"text":1094},"2026-10-07","設定 HPA（Horizontal Pod Autoscaler）時，最常被問的一個問題是：「為什麼 averageUtilization: 75 的 75% 是相對 Request 算，而不是相對 Limit？Limit 才是 Pod 真正能用到的上限啊。」",null,"md",{},"\u002Farticles\u002Fk8s-hpa",{"title":5,"description":1154},"articles\u002Fk8s-hpa",[1162,1163,1164,1165,1166],"Kubernetes","HPA","Autoscaling","SRE","容量規劃","hTVrarh1tSnfhO1cXy4RE8RtCtkjVdBT_3XPxqDlYJM",1792959149882]