實際記錄一次在使用Dockerfile構建鏡像和容器的時候出現的問題
前景:
封裝chrome和crawler進行一個簡單的爬蟲功能
Dockerfile的EntryPoint是java -jar 啟動jar命令,當你訪問api的時候會使用Runtime.exec()方法拼裝url自動進行爬取訪問。
定位問題:
調用chrome會出現大量的chrome進程,同時任務結束的時候調用ps -aux 發現大量的進程stat狀態為Z,也就是處於僵屍狀態,調用ps -ef 發現僵屍進程的父進程Id為1
學習過程:
僵屍進程:stat狀態為Z,已經調用過exit方法,但是還是占用一部分的系統資源無法釋放。一個進程在調用exit命令結束自己的生命的時候,其實它並沒有真正的被銷毀,而是留下一個稱為僵屍進程(Zombie)的數據結構(系統調用exit, 它的作用是使進程退出,但也僅僅限於將一個正常的進程變成一個僵屍進程,並不能將其完全銷毀)。
kill 命令:kill -9 向進程發送結束命令,讓進程調用exit方法來進行結束。
我在學到這兩個東西的時候以為我可以使用kill來對僵屍進程進行清理,但是我多次嘗試並沒有成功,最后發現僵屍進程已經處於死亡狀態了,kill沒辦法再次殺死這個進程了。繼續學習
僵屍進程產生的原因:子進程執行完畢留下了一個需要父進程收屍的一個退出狀態結構,如果他的父進程沒安裝 SIGCHLD 信號處理函數調用wait或waitpid()等待子進程結束,又沒有顯式忽略該信號,那么它就一直保持僵屍狀態,如果父進程也結束了,這些子進程會自動過繼給init進程進程定期的回收。
學到這里以為搜索網上的處理方法,發現大多的都是讓我殺死僵屍進程的父進程,過繼給init進程進行回收,但是我發現他們的父進程就是init進程,這可把我整懵了,開始思考
思考過程:
容器重啟:docker創建的容器會自動將啟動的cmd或者enterpoint作為init進程,所以你想要殺死這個進程就必須將整個容器重啟,但是我又不想重啟整個容器,因為這樣我雖然能解決一些問題,但是我相當於是走了一個捷徑並沒有解決根本問題。
繼續思考,既然我殺不死這些僵屍進程,那我就只能從源頭解決了,那就是不讓父進程生成僵屍進程,然后我就懵了,因為chrome的默認命令產生的僵屍進程,我也不能修改chrome源碼啊!
尋求幫助:
在我向公司的大佬提出問題的時候大佬向我提出了一個叫tini的東西
GitHub地址:https://github.com/krallin/tini
使用方法地址里面已經有了,可以說是非常好用直接解決所有問題,但是我發現他好像在我的執行命令里面增加一些進程。然后fock()出來的都是他的子進程,所以只要直接管理他們的父進程然后回收這些子進程就可以了。原理我也不太懂,慢慢了解,學習的路程永遠是快樂的。
