論consul正確的關閉姿勢


最近在工作中發現一個有意思的現象,我用 ctrl+c 關閉本地 consul 的時候,報警系統並沒有發出告警,說我的 node 異常,自己看了一下代碼,發現 consul 的關閉還是有點貓膩的,仔細來講講

consul agent 在正常關閉的時候會向集群發送 leave 信令,宣告自己離開集群,那么什么才叫正常關閉呢?

還是上代碼:
摘自command.go handleSignals方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
func (c *Command) handleSignals(config *Config, retryJoin <-chan struct{}, retryJoinWan <-chan struct{}) int {
signalCh := make(chan os.Signal, 4)
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
 
// Wait for a signal
WAIT:
var sig os.Signal
select {
case s := <-signalCh:
sig = s
case <-c.rpcServer.ReloadCh():
sig = syscall.SIGHUP
case <-c.ShutdownCh:
sig = os.Interrupt
case <-retryJoin:
return 1
case <-retryJoinWan:
return 1
case <-c.agent.ShutdownCh():
// Agent is already shutdown!
return 0
}
c.Ui.Output(fmt.Sprintf( "Caught signal: %v", sig))
 
// Check if this is a SIGHUP
if sig == syscall.SIGHUP {
if conf := c.handleReload(config); conf != nil {
config = conf
}
goto WAIT
}
 
// Check if we should do a graceful leave
graceful := false
if sig == os.Interrupt && !(*config.SkipLeaveOnInt) {
graceful = true
} else if sig == syscall.SIGTERM && config.LeaveOnTerm {
graceful = true
}
 
// Bail fast if not doing a graceful leave
if !graceful {
return 1
}
 
// Attempt a graceful leave
gracefulCh := make(chan struct{})
c.Ui.Output( "Gracefully shutting down agent...")
go func() {
if err := c.agent.Leave(); err != nil {
c.Ui.Error(fmt.Sprintf( "Error: %s", err))
return
}
close(gracefulCh)
}()
 
// Wait for leave or another signal
select {
case <-signalCh:
return 1
case <-time.After(gracefulTimeout):
return 1
case <-gracefulCh:
return 0
}
}

首先 agent 監聽了三個系統信令,os.Interrupt, syscall.SIGTERM, syscall.SIGHUP

看26行,syscall.SIGHUP,用於 reloadconfig, 這個忽略掉

接下來才是進入正題,如何正確的關閉 consul

首先我們要知道 consul agent 關閉后 consul server 一般是什么體現.

  1. consul ui 顯示 Node 異常, check 顯示是 fialling
  2. consul ui 中搜索不到這個 node

是什么造成這個差別呢,答案在上面代碼里面的第49行到55行,正常關閉后, node 會向 server 發送一條 leave 的信令,告訴 server 我離開了,你不用管我了,但是如果是宕機的話,又不能發送這個 leave 信令,讓 server 知道我falling 了,這里通過34行定義的graceful來處理,如果配置里面說需要發送 leave 信令,那么就發送一個.
這里說一下, consul 的 service 或者 node 狀態監控可以通過consul_alert這個項目來做(這里吐個槽,雖然我在用這個程序做報警,但是真心覺得這個代碼寫得不敢恭維,不停的 exet 子進程,不停的初始化,想要寫alert 擴展的,一定要考慮重復初始化到知道內存泄漏)

原理說完,槽吐完,接下來說點好的,這也是我覺得 consul 開發者很貼心的一個地方.

作者把os.Interrupt, syscall.SIGTERM兩個信令分開處理,

os.Interrupt

這個信令對應的其實就是 ctrl+c, 這一般是我們在開發時才會用到,那么和這個信令配合的配置是config.SkipLeaveOnInt ,這個配置項不配置,默認就是 false, 那么`sig == os.Interrupt && !(config.SkipLeaveOnInt)` 就為 true, 接着就會執行 leave信令,這完全適用於我們在開發環境中,使用自己開發電腦上的 consul agent, 退出就自動注銷,不用怕收到報警.

syscall.SIGTERM

這個信令一般我們執行kill -15 ${pid}就會發送,當然kill ${pid}默認就是發送15號信令,這個信令配合config.LeaveOnTerm配置項來處理是否發送 leave 信令,這種一般是 agent 在后台運行時才會用到的,這種情況大家都知道主要場景是在生產環境,如果配置config.LeaveOnTerm= true 的話,那么停機維護的時候,也收不到煩人的報警.貼心吧

有人說,宕機怎么辦,我很遺憾的告訴你 os.KILL 信令,程序接收到也沒有時間執行下面的操作,直接被殺掉了,在服務器宕機的時候也許都沒機會發出 kill 命令,在程序被 kernel 殺掉也就是 kill -9,當然程序也沒辦法執行下面的操作,當然在 server 端看到的狀態就是 falling,接收收到報警,找運維人員處理就好了.

最后總結一下,關閉 consul 的正確姿勢:

在前台運行的情況下:

ctrl+c +最簡化配置即可正常關閉,

在后台運行的情況下:

配置中指定LeaveOnTerm: true,維護時,使用 kill -15 ${pid}來關閉進程,即可正常關閉掉 node, 並注銷成功.

補充說明:
leave 的信令發送時異步發送的,所以如果網絡不太好的情況下,也許會 leave 還沒完全發出去程序就關閉掉了.代碼見上面49行,我沒想明白為什么要一步處理,同步處理,就算發送失敗重試三次再管也好的….


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM