博客 / 詳情

返回

Golang 輪子之 Supervisor

Supervisor 是一個強大的 進程管理工具。

在非容器化管理的服務器上, Supervisor 是有非常廣泛的使用場景的。

例如:

服務批量重啓,多服務按順序啓動,服務oom後自動拉起,服務std日誌收集等,甚至服務健康檢查它都能做。

原 Supervisor (Python)

git: https://github.com/Supervisor...

doc: http://supervisord.org/

新輪子 Supervisor (Golang)

git: https://github.com/ochinchina...

對比

兩個 Supervisor 對比

指標\語言 Python Golang
起源 2004 2017
當前版本 4.2.4 0.7.3
語言版本要求 2.7+ 或 3.4+ 1.11+
*unix 支持 支持
MacOS 支持 支持
Widnows 不支持 能跑
安裝包大小 Pyton環境(40MB) + 腳本(490KB) 4.2MB
Web GUI 支持 支持

功能支持情況

共呢個\語言 Python Golang
分組 支持 支持
掛了自動拉起 支持 支持
定時重啓 支持 支持
web端管理 支持 支持
監控文件自動重啓 支持 支持
依賴順序啓動 支持 支持
...

這裏只是列舉了常用的功能,基本都實現了的,依靠golang按需runtime+可執行代碼打包後,二進制部署相較 python 是更為方便和小巧的。

安裝

gihub 上沒有二進制包,需要clone代碼,手動編譯。

$ git clone https://github.com/ochinchina/supervisord
$ cd supervisord
$ go generate

# 以下代碼會編譯出 linux 平台二進制可執行文件
$ GOOS=linux go build -tags release -a -ldflags "-linkmode external -extldflags -static" -o supervisord
# mac 下
$ go build -tags release -o supervisord

試試

$ ./supervisord --help
Usage:
  supervisord [OPTIONS] <command>

Application Options:
  -c, --configuration= the configuration file
  -d, --daemon         run as daemon
      --env-file=      the environment file

Help Options:
  -h, --help           Show this help message

Available commands:
  ctl      Control a running daemon
  init     initialize a template
  service  install/uninstall/start/stop service
  version  show the version of supervisor

使用

  • 先創建一個配置文件
$ vi supervisor.conf
[program:test]
command = watch -n 5 "echo Hello!"
  • 啓動
$ supervisord -c supervisor.conf
INFO[2022-10-15T17:31:24+08:00] load configuration from file                  file=./supervisor.conf
INFO[2022-10-15T17:31:24+08:00] create process:test
INFO[2022-10-15T17:31:24+08:00] stop listening
INFO[2022-10-15T17:31:24+08:00] try to start program                          program=test
DEBU[2022-10-15T17:31:24+08:00] wait program exit                             program=test
INFO[2022-10-15T17:31:25+08:00] success to start program                      program=test

## 此時該 supervisord 會前台運行,退出終端,或者 Ctrl+C 都會推出,會結束所有的程序。
^CINFO[2022-10-15T17:32:39+08:00] receive a signal to stop all process & exit   signal=interrupt
INFO[2022-10-15T17:32:39+08:00] stop the program                              program=test
INFO[2022-10-15T17:32:39+08:00] force to kill the program                     program=test
INFO[2022-10-15T17:32:39+08:00] Send signal to program                        program=test signal=killed
INFO[2022-10-15T17:32:39+08:00] program stopped with status:signal: killed    program=test
INFO[2022-10-15T17:32:39+08:00] program exited                                program=test
INFO[2022-10-15T17:32:39+08:00] Stopped by user, don't start it again         program=test
  • 啓動並運行到後台
$ supervisord -c supervisor.conf -d

這樣就啓動了

http 管理

supervior 同樣提供了 Web GUI 管理入口,我們來啓用配置試試

[program:test]
command = watch -n 5 "echo Hello"

[inet_http_server]
port=127.0.0.1:9001

訪問: http://127.0.0.1:9001 即

web_gui

同樣支持 http Auth, 按照如下配置

[inet_http_server]
port=127.0.0.1:9001
username=test1
password=thepassword
注意: Shutdown 是停掉 supervisor 服務本身,包括 Web 入口,需要登陸到服務器,手動啓動後,才能繼續使用。要停掉所有自程序,選擇全部然後點擊 Stop Select

文件監控

當我們部署,或更新程序時,希望 supervisor 能自動關閉,並運行新的可執行文件,那麼 文件監控 功能就派上用場了。

go-supervisor 支持多種文件監控模式:

  1. 執行的程序本身監控
  2. 某個文件夾內監控
  3. 文件監控
  • 配置方式

    [program:golang]
    command = /Users/paulxu/golang/go-learn/main -conf ./Users/paulxu/golang/go-learn/config.toml
    restart_when_binary_changed=true

這裏的測試代碼我放到文章最後了

INFO[2022-10-15T20:33:20+08:00] program is changed, restart it                program=golang
INFO[2022-10-15T20:33:20+08:00] stop the program                              program=golang
INFO[2022-10-15T20:33:20+08:00] force to kill the program                     program=golang
INFO[2022-10-15T20:33:20+08:00] Send signal to program                        program=golang signal=killed
INFO[2022-10-15T20:33:20+08:00] program stopped with status:signal: killed    program=golang
INFO[2022-10-15T20:33:20+08:00] program exited                                program=golang
INFO[2022-10-15T20:33:20+08:00] Stopped by user, don't start it again         program=golang
INFO[2022-10-15T20:33:21+08:00] try to start program                          program=golang
DEBU[2022-10-15T20:33:21+08:00] wait program exit                             program=golang
INFO[2022-10-15T20:33:22+08:00] success to start program                      program=golang

監控到變化後,重啓方式也有兩種,一種是:直接kill。另一種是發送信號量給程序,讓程序自行處理。

注意: 如果 supervisor 本身發了 kill 信號給程序,程序自己結束了,superviosr 默認也不會幫你在重啓程序,它的設計邏輯時,我只負責發信號,其他程序自理。這裏你可以手動新增一條配置:
[program:golang]
command = /Users/paulxu/golang/go-learn/main -conf ./Users/paulxu/golang/go-learn/config.toml
restart_when_binary_changed=true
autostart=true #  這行配置
如果管理了在線的大流量服務,推薦使用第二種,平滑重啓,因為直接kill程序,會導致請求處理一半,或事務進行到一半中止,進而數據不一致。

好的,我們再次調整配置

[program:golang]
command = /Users/paulxu/golang/go-learn/main -conf ./Users/paulxu/golang/go-learn/config.toml
restart_when_binary_changed=true
restart_signal_when_binary_changed=9 # SIGKILL

來看下日誌:

INFO[2022-10-15T20:37:58+08:00] program is changed, restart it                program=golang
INFO[2022-10-15T20:37:58+08:00] Send signal to program                        program=golang signal=terminated
INFO[2022-10-15T20:37:58+08:00] program stopped with status:exit status 0     program=golang
INFO[2022-10-15T20:37:58+08:00] program exited                                program=golang
INFO[2022-10-15T20:37:58+08:00] Don't start the stopped program because its autorestart flag is false  program=golang

注意這裏的日誌,説的是,supersivor 給程序發了 信號,但是程序退出了,由於,你啓用自動重啓配置,所有,沒有啓動該程序。

這裏是信號發錯了,調整一下:

[program:golang]
command = /Users/paulxu/golang/go-learn/main -conf ./Users/paulxu/golang/go-learn/config.toml
restart_when_binary_changed=true
restart_signal_when_binary_changed=SIGHUP # 1 這裏填數字字符都行

這下重新啓動supervisor,看下效果。

程序運行日誌:

2022-10-16 11:00:27.754 [INFO] main.go:13: start
2022-10-16 11:00:27.754 [INFO] main.go:21: waiting signal~
2022-10-16 11:00:28.755 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:29.757 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:30.761 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:31.765 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:32.768 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:33.771 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:34.774 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:35.779 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:36.783 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:37.659 [INFO] main.go:32: golang get signal hangup [sighup]
2022-10-16 11:00:37.788 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:38.790 [INFO] main.go:17: golang program is running~

這樣之後,我們就能實現部署新的程序後,自動平滑重啓程序了。

注意:在 Web 端,手動 stop/start 程序,不會發信號量到程序!

監控文件夾

剛剛展示目標程序變更,自動重啓。那麼配置文件更新了,自動重啓如何配置呢?

注意:如果程序內自動監控了文件變化並更新配置(推薦這樣做),則不需要 supervisor 來發信號給程序本身了。

這裏新增了兩行配置,1.配置監控存放配置文件的文件夾,2. 配置文件夾內文件變化時,發什麼信號通知程序。

[program:golang]
command = /Users/paulxu/golang/go-learn/main -conf /Users/paulxu/golang/go-learn/config/config.toml
restart_when_binary_changed=true
restart_signal_when_binary_changed=SIGHUP
restart_directory_monitor=/Users/paulxu/golang/go-learn/config/
restart_signal_when_file_changed=SIGHUP

測試代碼

package main

import (
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/gogf/gf/frame/g"
)

func main() {
    g.Log().Line().Info("start!!")
    go func() {
        for {
            time.Sleep(time.Second)
            g.Log().Line().Info("golang program is running~")
        }
    }()

    g.Log().Line().Info("waiting signal~")
    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
    for {
        select {
        case s := <-c:
            switch s {
            case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
                g.Log().Line().Infof("golang get signal %+v", s)
                return
            case syscall.SIGHUP:
                g.Log().Line().Infof("golang get signal %+v [sighup]", s)
            default:
                g.Log().Line().Infof("golang get other signal %+v", s)
                return
            }
        }
    }
}

最後

好的,今天給大家介紹了一款 superviorgolang輪子,以及基本使用方法。可以看到一些常用的基礎和golang碰撞後,擦出了不一樣的火花。由於 Go 語言的編譯工具鏈會全靜態鏈接構建二進制文件,All in One 的設計理念,對運維部署時非常友好的。期待更多這樣的輪子。

雖然,在當前容器化時代,它的使用場景被進一步擠壓,但是在小型站點,實體機上使用還是很方便的。

關注我,瞭解更多golang知識~

qrcode

user avatar konstantinos-kouratoras 頭像
1 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.