Merging other languages
This commit is contained in:
374
en/12.3.md
374
en/12.3.md
@@ -1,181 +1,193 @@
|
||||
# 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的一个实现思路,使用Commond来执行自身的应用,如果真想实现,那么推荐这种方案
|
||||
|
||||
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的方案,但是这个方案并不完善:
|
||||
|
||||
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,通过文本编辑器修改这个文件,下面是一个示例的配置文件:
|
||||
|
||||
;/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>)
|
||||
# 12.3 Deployment
|
||||
|
||||
After completion of the development program, we now want to deploy Web applications, but how do we deploy these applications do ? Because after compiling Go programs is an executable file, written a C program using daemon readers must know you can achieve the perfect background program runs continuously, but still not perfect at present Go realization daemon, therefore, for a Go application deployment, we can use third-party tools to manage, there are many third-party tools, such as Supervisord, upstart, daemon tools, etc. This section describes my own system using current tools Supervisord.
|
||||
|
||||
## Daemon
|
||||
|
||||
Currently Go programs can not be achieved daemon, see the Go detailed language bug: <`http://code.google.com/p/go/issues/detail?id=227`>, probably mean that it is difficult from the the use of existing fork a thread out, because there is no simple way to ensure that all the threads have used state consistency problem.
|
||||
|
||||
But we can see some implementations daemon many online methods, such as the following two ways:
|
||||
|
||||
- MarGo an implementation of the idea of using Command to run their own applications, if you really want to achieve, it is recommended that such programs
|
||||
|
||||
<pre>
|
||||
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()
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
- Another solution is to use the syscall, but this solution is not perfect:
|
||||
|
||||
```
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
The above proposed two implementations Go's daemon program, but I still do not recommend you to realize this, because the official announcement has not officially support daemon, of course, the first option is more feasible for now, but it is currently open source library skynet in adopting this program do daemon.
|
||||
|
||||
## Supervisord
|
||||
|
||||
Go has been described above, there are two options currently are to realize his daemon, but the government itself does not support this one, so it is recommended that you use sophisticated tools to manage our third-party applications, here I'll give you a present more extensive use of process management software: Supervisord. Supervisord is implemented in Python a very useful process management tool. supervisord will help you to manage applications into daemon process, but can be easily turned on via the command, shut down, restart, etc, and it is managed by the collapse will automatically restart once the process so that you can ensure that the program execution is interrupted in the case there are self-healing capabilities.
|
||||
|
||||
> I stepped in front of a pit in the application, because all applications are made Supervisord parent born, then when you change the operating system file descriptor after, do not forget to restart Supervisord, light restart following application program useless. I just had the system installed after the first installed Supervisord, then start the deployment process, modify the file descriptor, restart the program, that the file descriptor is already 100,000, but in fact Supervisord this time or the default 1024, led him to manage the process All descriptors is 1024. pressure after opening up the system to start a newspaper run out of file descriptors, search for a long time to find the pit.
|
||||
|
||||
### Supervisord installation
|
||||
|
||||
Supervisord can `sudo easy_install supervisor` installation, of course, can also Supervisord official website to download, unzip and go to the folder where the source code, run `setup.py install` to install.
|
||||
|
||||
- Must be installed using easy_install setuptools
|
||||
|
||||
Open the `http://pypi.python.org/pypi/setuptools# files`, depending on your system version of python download the appropriate file, and then execute `sh setuptoolsxxxx.egg`, so that you can use easy_install command to install Supervisord.
|
||||
|
||||
### Supervisord Configure
|
||||
|
||||
Supervisord default configuration file path is `/etc/supervisord.conf`, through a text editor to modify this file, the following is a sample configuration file:
|
||||
|
||||
;/etc/supervisord.conf
|
||||
[unix_http_server]
|
||||
file = /var/run/supervisord.sock
|
||||
chmod = 0777
|
||||
chown= root:root
|
||||
|
||||
[inet_http_server]
|
||||
# Web management interface settings
|
||||
port=9001
|
||||
username = admin
|
||||
password = yourpassword
|
||||
|
||||
[supervisorctl]
|
||||
; Must 'unix_http_server' match the settings inside
|
||||
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
|
||||
; Manage the configuration of a single process, you can add multiple program
|
||||
[program: blogdemon]
|
||||
command =/data/blog/blogdemon
|
||||
autostart = true
|
||||
startsecs = 5
|
||||
user = root
|
||||
redirect_stderr = true
|
||||
stdout_logfile =/var/log/supervisord/blogdemon.log
|
||||
|
||||
### Supervisord management
|
||||
|
||||
After installation is complete, there are two Supervisord available command line supervisor and supervisorctl, command explained as follows:
|
||||
|
||||
- Supervisord, initial startup Supervisord, start, set in the configuration management process.
|
||||
- Supervisorctl stop programxxx, stop one process(programxxx), programxxx for the [program: blogdemon] in configured value, this example is blogdemon.
|
||||
- Supervisorctl start programxxx, start a process
|
||||
- Supervisorctl restart programxxx, restarting a process
|
||||
- Supervisorctl stop all, stop all processes, Note: start, restart, stop will not load the latest configuration files.
|
||||
- Supervisorctl reload, load the latest configuration file, and press the new configuration to start, manage all processes.
|
||||
|
||||
## Summary
|
||||
|
||||
This section we describe how to implement daemon of the Go, but due to the current lack of Go-daemon implementation, need to rely on third-party tools to achieve the application daemon management approach, so here describes a process using python to write management tools Supervisord by Supervisord can easily put our Go up and application management.
|
||||
|
||||
|
||||
## Links
|
||||
|
||||
- [Directory](preface.md)
|
||||
- Previous section: [Errors and crashes](12.2.md)
|
||||
- Next section: [Backup and recovery](12.4.md)
|
||||
|
||||
Reference in New Issue
Block a user