Files
build-web-application-with-…/zh-tw/12.3.md
2019-02-26 01:40:54 +08:00

190 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 12.3 應用部署
程式開發完畢之後我們現在要部署Web應用程式了但是我們如何來部署這些應用程式呢因為Go程式編譯之後是一個可執行檔案編寫過C程式的讀者一定知道採用daemon就可以完美的實現程式後臺持續執行但是目前Go還無法完美的實現daemon因此針對Go的應用程式部署我們可以利用第三方工具來管理第三方的工具有很多例如Supervisord、upstart、daemontools等這小節我介紹目前自己系統中採用的工具Supervisord。
## daemon
目前Go程式還不能實現daemon詳細的見這個Go語言的bug<`http://code.google.com/p/go/issues/detail?id=227`>大概的意思說很難從現有的使用的執行緒中fork一個出來因為沒有一種簡單的方法來確保所有已經使用的執行緒的狀態一致性問題。
但是我們可以看到很多網上的一些實現daemon的方法例如下面兩種方式
- MarGo的一個實現思路使用Command來執行自身的應用如果真想實現那麼推薦這種方案
```Go
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的方案但是這個方案並不完善
```Go
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
}
```
上面提出了兩種實現Go的daemon方案但是我還是不推薦大家這樣去實現因為官方還沒有正式的宣佈支援daemon當然第一種方案目前來看是比較可行的而且目前開源函式庫skynet也在採用這個方案做daemon。
## Supervisord
上面已經介紹了Go目前是有兩種方案來實現他的daemon但是官方本身還不支援這一塊所以還是建議大家採用第三方成熟工具來管理我們的應用程式這裡我給大家介紹一款目前使用比較廣泛的程序管理軟體Supervisord。Supervisord是用Python實現的一款非常實用的程序管理工具。supervisord會幫你把管理的應用程式轉成daemon程式而且可以方便的透過命令開啟、關閉、重啟等操作而且它管理的程序一旦崩潰會自動重啟這樣就可以保證程式執行中斷後的情況下有自我修復的功能。
>我前面在應用中踩過一個坑就是因為所有的應用程式都是由Supervisord父程序生出來的那麼當你修改了作業系統的檔案描述符之後別忘記重啟Supervisord光重啟下面的應用程式沒用。當初我就是系統安裝好之後就先裝了Supervisord然後開始部署程式修改檔案描述符重啟程式以為檔案描述符已經是100000了其實Supervisord這個時候還是預設的1024個導致他管理的程序所有的描述符也是1024.開放之後壓力一上來系統就開始報檔案描述符用光了,查了很久才找到這個坑。
### Supervisord安裝
Supervisord可以透過`sudo easy_install supervisor`安裝當然也可以透過Supervisord官網下載後解壓並轉到原始碼所在的資料夾下執行`setup.py install`來安裝。
- 使用easy_install必須安裝setuptools
開啟`http://pypi.python.org/pypi/setuptools#files`根據你係統的python的版本下載相應的檔案然後執行`sh setuptoolsxxxx.egg`這樣就可以使用easy_install命令來安裝Supervisord。
### Supervisord配置
Supervisord預設的配置檔案路徑為/etc/supervisord.conf透過文字編輯器修改這個檔案下面是一個示例的配置檔案
```conf
;/etc/supervisord.conf
[unix_http_server]
file = /var/run/supervisord.sock
chmod = 0777
chown= root:root
[inet_http_server]
# Web管理介面設定
port=9001
username = admin
password = yourpassword
[supervisorctl]
; 必須和'unix_http_server'裡面的設定匹配
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
; 管理的單個程序的配置可以新增多個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安裝完成後有兩個可用的命令列supervisor和supervisorctl命令使用解釋如下
- supervisord初始啟動Supervisord啟動、管理配置中設定的程序。
- supervisorctl stop programxxx停止某一個程序(programxxx)programxxx為[program:blogdemon]裡配置的值這個示例就是blogdemon。
- supervisorctl start programxxx啟動某個程序
- supervisorctl restart programxxx重啟某個程序
- supervisorctl stop all停止全部程序start、restart、stop都不會載入最新的配置檔案。
- supervisorctl reload載入最新的配置檔案並按新的配置啟動、管理所有程序。
## 小結
這小節我們介紹了Go如何實現daemon化但是由於目前Go的daemon實現的不足需要依靠第三方工具來實現應用程式的daemon管理的方式所以在這裡介紹了一個用python寫的程序管理工具Supervisord透過Supervisord可以很方便的把我們的Go應用程式管理起來。
## links
* [目錄](<preface.md>)
* 上一章: [網站錯誤處理](<12.2.md>)
* 下一節: [備份和恢復](<12.4.md>)