mysql小鯨魚 殺死乙隻小鯨魚

2021-10-22 07:14:43 字數 4683 閱讀 5923

之所以會有這個主題,是因為這幾天我給mysql集群前置了乙個keepalived,為了方便也做成了docker映象,丟給k8s來部署。但實際測試時發現,當停止或者刪除keepalived容器後,網絡卡上還殘餘之前keepalived下發的virtual ip。

直接在宿主機上安裝keepalived不會有這個問題。從keepalived原始碼上來看,它會接管linux發給它的sigterm訊號,之後清理現場,包括下發給介面的虛ip,所以問題原因就比較簡單了,容器在退出時,並沒用將sigterm訊號傳遞給keepalived程序,造成keepalived強制退出,最終virtual ip殘留。

要解決這個問題,還需要從容器退出開始談起。先挖個坑,晚上填。

docker容器退出會收到哪些訊號?

不同的命令,收到的訊號不同。

docker stop

預設docker stop會向容器傳送sigterm訊號,並等待10秒;如果10秒後容器沒有退出,則會傳送sigkill訊號,強制殺死該容器。我的keepalived容器就是收到訊號10s後仍然存活,最後被sigkill殺死的,docker ps -a檢視容器的status,可以看到exited (137) 2 hours ago。137=128+9,即退出原因是收到了訊號9(即sigkill)。

如果你的應用在退出時要做的事情比較多,可以docker stop的時候寬限幾天。

docker stop ----time=30 ***

docker kill

預設docker kill會向容器傳送sigkill訊號,即直接殺死容器,不叨叨。不過你可以通過–signal=sig***引數來修改docker kill傳送的訊號。注意docker kill和linux kill命令的預設行為不同,後者預設傳送的是sigterm(類似docker stop)。docker kill和linux kill -9的行為是類似的。

docker rm -f

docker rm -f會傳送sigkill殺死容器,並清理容器痕跡,類似docker kill && docker rm。

應用應該處理哪個訊號?

新寫的應用,建議捕捉sigterm,因為這是docker stop時預設發出的訊號;舊有的應用,則需要一房一價,具體問題具體分析,例如nginx需要sigquit,apache需要sigwinch。keepalived比較友好:

/* terminate handler */

void

sigend(void *v, int sig)

int status;

/* register the terminate thread */

thread_add_terminate_event(master);

if (vrrp_child > 0) {

kill(vrrp_child, sigterm);

waitpid(vrrp_child, &status, wnohang);

if (checkers_child > 0) {

kill(checkers_child, sigterm);

waitpid(checkers_child, &status, wnohang);

/* initialize signal handler */

void

signal_init(void)

signal_handler_init();

signal_set(sighup, sighup, null);

signal_set(sigint, sigend, null);

signal_set(sigterm, sigend, null);

signal_ignore(sigpipe);

不管是sigint還是sigterm,都能sigend走清理。

應用是怎麼接收到訊號的

docker會將訊號傳給pid=1的程序。

換句話說,如果你的應用跑起來時pid不為1,那麼就收不到docker發出來的訊號,也就做不到gracefully退出了。因此,我們的目標就是要讓需要gracefully退出的應用跑在pid=1的位置上。

舉個例子。

先寫個loop.sh。

#!/usr/bin/env bash

trap 'exit 0' sigterm

whiletrue; do :; done

然後是dockerfile。

from ubuntu:trusty

copy loop.sh /

cmd /loop.sh

你可以試著build/run/stop一下,會發現docker stop需要10s才能停掉這個容器,說明loop.sh沒有收到訊號。為什麼呢?我們知道訊號是發給pid=1的程序,那麼來看看容器裡的情況:

docker exec 838 ps aux

user pid %cpu %mem vsz rss tty stat start time command

root 1 0.0 0.0 4460 696 ? ss 05:26 0:00 /bin/sh -c /loop.sh

root 6 99.6 0.0 17968 2716 ? r 05:26 0:29 bash /loop.sh

root 7 0.0 0.0 15580 2156 ? rs 05:26 0:00 ps aux

顯然loop.sh收不到訊號。

dockerfile對於cmd command param1 param2這種形式的命令,具體執行的是bash -c command param1 param2,也就是上面我們看到的結果。

正確的寫法是,使用cmd ["executable","param1","param2"]這種形式,例如上面:

from ubuntu:trusty

copy loop.sh /

cmd [/loop.sh]

這樣容器啟動時,會直接執行loop.sh,不會在外面再套一層bash:

docker exec 638 ps aux

user pid %cpu %mem vsz rss tty stat start time command

root 1 91.8 0.0 17968 2864 ? rs 05:36 0:09 bash /loop.sh

root 6 0.0 0.0 15580 2032 ? rs 05:36 0:00 ps aux

這樣應用就可以拿到docker傳送的sigterm訊號了。

再舉個例子。

比如我的keepalived容器。由於容器啟動時我需要做一些配置修改(keepalived.conf),需要把這些操作放在乙個指令碼裡,所以我的cmd是:cmd ["/opt/entry-point.sh"],在entry-point.sh中啟動keepalived程序。

#!/bin/bash -x

#entry-point.sh

#replace variables in /etc/keepalived/keepalived.conf

/sbin/ipvsadm-restore < /etc/sysconfig/ipvsadm

/usr/sbin/keepalived --dont-fork --log-console --pid /keepalived.pid -d

tail -f /var/log/yum.log

entry-point.sh指令碼的確是pid=1了,但keepalived程序的pid並不是1,因為它和上面的cmd command一樣,是bash -c方式以entry-point.sh的子程序的身份執行的。正確的做法是什麼呢?

要exec,不要fork:

#!/bin/bash -x

#entry-point.sh

#replace variables in /etc/keepalived/keepalived.conf

/sbin/ipvsadm-restore < /etc/sysconfig/ipvsadm

exec /usr/sbin/keepalived --dont-fork --log-console --pid /keepalived.pid -d

此時再來看容器內的程序,keepalived就是pid=1了,這時去docker stop容器,keepalived會捕捉到sigterm訊號,然後清理掉virtual ip,gracefully的退出。

docker exec 576 ps aux

user pid %cpu %mem vsz rss tty stat start time command

root 1 0.0 0.0 111660 4000 ? ss 09:41 0:00 /usr/sbin/keepalived --dont-fork --log-console --pid /keepalived.pid -d

root 18 0.0 0.0 113904 2952 ? s 09:41 0:02 /usr/sbin/keepalived --dont-fork --log-console --pid /keepalived.pid -d

root 19 0.0 0.0 113780 2192 ? s 09:41 0:04 /usr/sbin/keepalived --dont-fork --log-console --pid /keepalived.pid -d

root 24 0.0 0.0 35884 1456 ? rs 13:48 0:00 ps aux

ref:

乙隻垂直的小爬蟲

這只垂直的小爬蟲,使用如下實現 實現的思路很簡單,我從主函式開始簡單敘述一下整個執行流程,第一步 收集需要爬取的url位址,容器我選擇的是concurrentlinkedqueue非阻塞佇列,它底層使用unsafe實現,要的就是它執行緒安全的特性 主函式 如下 static string url 新...

《殺死乙隻知更鳥》讀後感

知更鳥在美國南方是很受歡迎的鳥,只唱歌,對人毫無害處。打獵殺死乙隻知更鳥是一件很糟糕的事,意味著傷害了無辜。知更鳥是天真,善良的代表。書名這樣取,就不難猜出內容來。美國南方小鎮梅科姆,鎮上大部分人都有千絲萬縷的聯絡,低頭抬頭都是熟人,好訊息,壞訊息,各種坊間傳聞很快都會家喻戶曉。斯庫特和哥哥傑姆,父...

乙隻有野心的小爬蟲

這個問題在程式設計師中的爭議很大,這裡不拉開情懷與逼格的爭議。對於大多數人,我的建議是 先暫時使用ide,這可以在學習的過程中讓你的精力主要集中在 的編寫上,對於執行和除錯也非常方便。但是你至少應該會一點vim的基礎操作,這樣可以方便你在伺服器部署 ctrl space 基本的 完成 類 方法 屬性...