Go 應(yīng)用部署

2022-05-13 16:57 更新

程序開發(fā)完畢之后,我們現(xiàn)在要部署Web應(yīng)用程序了,但是我們?nèi)绾蝸?lái)部署這些應(yīng)用程序呢?因?yàn)镚o程序編譯之后是一個(gè)可執(zhí)行文件,編寫過(guò)C程序的讀者一定知道采用daemon就可以完美的實(shí)現(xiàn)程序后臺(tái)持續(xù)運(yùn)行,但是目前Go還無(wú)法完美的實(shí)現(xiàn)daemon,因此,針對(duì)Go的應(yīng)用程序部署,我們可以利用第三方工具來(lái)管理,第三方的工具有很多,例如Supervisord、upstart、daemontools等,這小節(jié)我介紹目前自己系統(tǒng)中采用的工具Supervisord。

daemon

目前Go程序還不能實(shí)現(xiàn)daemon,詳細(xì)的見這個(gè)Go語(yǔ)言的bug:https://github.com/golang/go/issues/227,大概的意思說(shuō)很難從現(xiàn)有的使用的線程中fork一個(gè)出來(lái),因?yàn)闆]有一種簡(jiǎn)單的方法來(lái)確保所有已經(jīng)使用的線程的狀態(tài)一致性問(wèn)題。

但是我們可以看到很多網(wǎng)上的一些實(shí)現(xiàn)daemon的方法,例如下面兩種方式:

  • MarGo的一個(gè)實(shí)現(xiàn)思路,使用Commond來(lái)執(zhí)行自身的應(yīng)用,如果真想實(shí)現(xiàn),那么推薦這種方案
d := flag.Bool("d", false, "Whether or not to launch in the background(like a daemon)")
if *d {
    cmd := exec.Command(os.Args[0],
        "-close-fds",
        "-addr", *addr,
        "-call", *call,
    )
    serr, err := cmd.StderrPipe()
    if err != nil {
        log.Fatalln(err)
    }
    err = cmd.Start()
    if err != nil {
        log.Fatalln(err)
    }
    s, err := ioutil.ReadAll(serr)
    s = bytes.TrimSpace(s)
    if bytes.HasPrefix(s, []byte("addr: ")) {
        fmt.Println(string(s))
        cmd.Process.Release()
    } else {
        log.Printf("unexpected response from MarGo: `%s` error: `%v`\n", s, err)
        cmd.Process.Kill()
    }
}
  • 另一種是利用syscall的方案,但是這個(gè)方案并不完善:
package main

import (
    "log"
    "os"
    "syscall"
)

func daemon(nochdir, noclose int) int {
    var ret, ret2 uintptr
    var err uintptr

    darwin := syscall.OS == "darwin"

    // already a daemon
    if syscall.Getppid() == 1 {
        return 0
    }

    // fork off the parent process
    ret, ret2, err = syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)
    if err != 0 {
        return -1
    }

    // failure
    if ret2 < 0 {
        os.Exit(-1)
    }

    // handle exception for darwin
    if darwin && ret2 == 1 {
        ret = 0
    }

    // if we got a good PID, then we call exit the parent process.
    if ret > 0 {
        os.Exit(0)
    }

    /* Change the file mode mask */
    _ = syscall.Umask(0)

    // create a new SID for the child process
    s_ret, s_errno := syscall.Setsid()
    if s_errno != 0 {
        log.Printf("Error: syscall.Setsid errno: %d", s_errno)
    }
    if s_ret < 0 {
        return -1
    }

    if nochdir == 0 {
        os.Chdir("/")
    }

    if noclose == 0 {
        f, e := os.OpenFile("/dev/null", os.O_RDWR, 0)
        if e == nil {
            fd := f.Fd()
            syscall.Dup2(fd, os.Stdin.Fd())
            syscall.Dup2(fd, os.Stdout.Fd())
            syscall.Dup2(fd, os.Stderr.Fd())
        }
    }

    return 0
}   

上面提出了兩種實(shí)現(xiàn)Go的daemon方案,但是我還是不推薦大家這樣去實(shí)現(xiàn),因?yàn)楣俜竭€沒有正式的宣布支持daemon,當(dāng)然第一種方案目前來(lái)看是比較可行的,而且目前開源庫(kù)skynet也在采用這個(gè)方案做daemon。

Supervisord

上面已經(jīng)介紹了Go目前是有兩種方案來(lái)實(shí)現(xiàn)他的daemon,但是官方本身還不支持這一塊,所以還是建議大家采用第三方成熟工具來(lái)管理我們的應(yīng)用程序,這里我給大家介紹一款目前使用比較廣泛的進(jìn)程管理軟件:Supervisord。Supervisord是用Python實(shí)現(xiàn)的一款非常實(shí)用的進(jìn)程管理工具。supervisord會(huì)幫你把管理的應(yīng)用程序轉(zhuǎn)成daemon程序,而且可以方便的通過(guò)命令開啟、關(guān)閉、重啟等操作,而且它管理的進(jìn)程一旦崩潰會(huì)自動(dòng)重啟,這樣就可以保證程序執(zhí)行中斷后的情況下有自我修復(fù)的功能。

我前面在應(yīng)用中踩過(guò)一個(gè)坑,就是因?yàn)樗械膽?yīng)用程序都是由Supervisord父進(jìn)程生出來(lái)的,那么當(dāng)你修改了操作系統(tǒng)的文件描述符之后,別忘記重啟Supervisord,光重啟下面的應(yīng)用程序沒用。當(dāng)初我就是系統(tǒng)安裝好之后就先裝了Supervisord,然后開始部署程序,修改文件描述符,重啟程序,以為文件描述符已經(jīng)是100000了,其實(shí)Supervisord這個(gè)時(shí)候還是默認(rèn)的1024個(gè),導(dǎo)致他管理的進(jìn)程所有的描述符也是1024.開放之后壓力一上來(lái)系統(tǒng)就開始報(bào)文件描述符用光了,查了很久才找到這個(gè)坑。

Supervisord安裝

Supervisord可以通過(guò)sudo easy_install supervisor安裝,當(dāng)然也可以通過(guò)Supervisord官網(wǎng)下載后解壓并轉(zhuǎn)到源碼所在的文件夾下執(zhí)行setup.py install來(lái)安裝。

  • 使用easy_install必須安裝setuptools

    打開http://pypi.python.org/pypi/setuptools#files,根據(jù)你系統(tǒng)的python的版本下載相應(yīng)的文件,然后執(zhí)行sh setuptoolsxxxx.egg,這樣就可以使用easy_install命令來(lái)安裝Supervisord。

Supervisord配置

Supervisord默認(rèn)的配置文件路徑為/etc/supervisord.conf,通過(guò)文本編輯器修改這個(gè)文件,下面是一個(gè)示例的配置文件:

;/etc/supervisord.conf
[unix_http_server]
file = /var/run/supervisord.sock
chmod = 0777
chown= root:root

[inet_http_server]
# Web管理界面設(shè)定
port=9001
username = admin
password = yourpassword

[supervisorctl]
; 必須和'unix_http_server'里面的設(shè)定匹配
serverurl = unix:///var/run/supervisord.sock

[supervisord]
logfile=/var/log/supervisord/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB       ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10          ; (num of main logfile rotation backups;default 10)
loglevel=info               ; (log level;default info; others: debug,warn,trace)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=true              ; (start in foreground if true;default false)
minfds=1024                 ; (min. avail startup file descriptors;default 1024)
minprocs=200                ; (min. avail process descriptors;default 200)
user=root                 ; (default is current user, required if root)
childlogdir=/var/log/supervisord/            ; ('AUTO' child log dir, default $TEMP)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

; 管理的單個(gè)進(jìn)程的配置,可以添加多個(gè)program
[program:blogdemon]
command=/data/blog/blogdemon
autostart = true
startsecs = 5
user = root
redirect_stderr = true
stdout_logfile = /var/log/supervisord/blogdemon.log

Supervisord管理

Supervisord安裝完成后有兩個(gè)可用的命令行supervisor和supervisorctl,命令使用解釋如下:

  • supervisord,初始啟動(dòng)Supervisord,啟動(dòng)、管理配置中設(shè)置的進(jìn)程。
  • supervisorctl stop programxxx,停止某一個(gè)進(jìn)程(programxxx),programxxx為[program:blogdemon]里配置的值,這個(gè)示例就是blogdemon。
  • supervisorctl start programxxx,啟動(dòng)某個(gè)進(jìn)程
  • supervisorctl restart programxxx,重啟某個(gè)進(jìn)程
  • supervisorctl stop all,停止全部進(jìn)程,注:start、restart、stop都不會(huì)載入最新的配置文件。
  • supervisorctl reload,載入最新的配置文件,并按新的配置啟動(dòng)、管理所有進(jìn)程。

小結(jié)

這小節(jié)我們介紹了Go如何實(shí)現(xiàn)daemon化,但是由于目前Go的daemon實(shí)現(xiàn)的不足,需要依靠第三方工具來(lái)實(shí)現(xiàn)應(yīng)用程序的daemon管理的方式,所以在這里介紹了一個(gè)用python寫的進(jìn)程管理工具Supervisord,通過(guò)Supervisord可以很方便的把我們的Go應(yīng)用程序管理起來(lái)。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)