業務的容器化剛剛搞完,線上開始告警,容器重啓,容器重啓。describe pod 查看緣由是OOMKilledjava
OOMKilled 是pod 中的進程使用的內存超過了.spec.containers[*].resources.limits.memory中定義的內存限制,在超出限制後, kubernetes 會向容器中的進程(pid=1)發送kill -9 信號。kill -9 信號對於進程來講是不可捕捉的,進程沒法在收到-9 信號後優雅的退出。 這對於業務來講是有損的。那麼爲啥進程會超過容器的limit 限制呢?
查看容器中進程的啓動參數:linux
java -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai -XX:MetaspaceSize=128m -jar bxr-web-1.0.jar
查看容器的limit限制web
k8s-master-01#kubectl get pods -n calculation bxr-web-dd656458b-8m4fb -o=custom-columns=name:.metadata.name,namespace:.metadata.namespace,memory-limit:.spec.containers[0].resources.limits.memory
name namespace memory-limit bxr-web-dd656458b-8m4fb calculation 2000Mi
進程沒有設置內存限制,可是這個業務以前在虛擬機上運行時,配置相同,啓動參數也是如此,爲何上線到容器中會常常出現OOMKilled 的狀況呢。這裏就須要說到docker對進程資源的限制。docker
docker 經過 cgroup 來控制容器使用的資源配額,包括 CPU、內存、磁盤三大方面,基本覆蓋了常見的資源配額和使用量控制。可是在java 的早期版本中(小於1.8.131),不支持讀取cgroup的限制。 默認是從/proc/目錄讀取可用內存。可是容器中的/proc目錄默認是掛載的宿主機的內存目錄。即java 讀取的到可用的內存是宿主機的內存。那麼天然會致使進程超出容器limit 限制的問題。
驗證:spa
起初, 咱們採用爲進程設置-Xmx參數來限制進程的最大heap(堆)內存。例如。 容器的limit限制爲3G。 那麼設置java進程的最大堆內存爲2.8G,採用這種方式後,容器重啓的狀況少了不少,但仍是偶爾會出現OOMKilled 的狀況。由於-xms 只能設置java進程的堆內存。 可是其餘非堆內存的佔用一旦超過預留的內存。仍是會被kubernetes kil掉。附java 內存結構:
線程
JVM內存結構主要有三大塊:堆內存、方法區和棧code
堆內存是JVM中最大的一塊由年輕代和老年代組成,而年輕代內存又被分紅三部分,Eden空間、From Survivor空間、To Survivor空間,默認狀況下年輕代按照8:1:1的比例來分配;blog
方法區存儲類信息、常量、靜態變量等數據,是線程共享的區域,爲與Java堆區分,方法區還有一個別名Non-Heap(非堆);進程
棧又分爲java虛擬機棧和本地方法棧主要用於方法的執行。內存
那麼有沒有辦法能讓java 正確識別容器的內存限制呢?這裏有三種方法:
-XX:+UseContainerSupport
值得慶幸的是,其中一些功能已被移植到8u131和9之後。可使用如下標誌打開它們。
-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap