Goでは現在デーモンの実装は公式にサポートされていない。詳細はissue 227をご覧頂きたい。
そのため、現状はGoでforegroundで動くプログラムを作り、デーモン化はsupervisordなど外部のプログラムに任せるべきである。実際私もWebアプリケーションのデプロイはそうしている。
しかし、それでもGoだけでデーモンを立ち上げるプログラムを書きたいことがある。
その際に使える選択肢を紹介したい。(例をシンプルにするためいろいろ省略している。Macでは動いた。)
1. syscallでforkを行う
_, ret, _ := syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)
if ret != 1 {
// Exit parent
os.Exit(0)
}
for {
// do something in child process
}
syscallパッケージで普通にforkして親プロセスをexitする方法。
しかし、issue 227で言われてる通り、これは複数スレッドと相性が悪く、goroutineを使う多くのGoのプログラムのデーモン化には向かない。
2. 自身をexecする
if len(os.Args) == 1 {
// exec itself
cmd := exec.Command(os.Args[0], "--child")
cmd.Start()
} else {
for {
// do something in child
}
}
自身のパスをos.Args[0]
により取得し、exec.Cmd.Start()
やos.StartProcess()
により外部プログラムとしてexecする方法。
build-web-application-with-golangでは外部のプログラムに頼らない場合はこの方法が推奨されている。
このやり方に使えるライブラリ
この方式でデーモンを実装するためのライブラリを紹介する。用途に合わせて好きなものを選べば良いと思う。
- VividCortex/godaemon
- APIがシンプル。デーモンで開きっぱなしにするファイルが指定できる。
- sevlyar/go-daemon
- シグナルハンドラを定義する機構がある。pidやログを書き出す先、working directory等の指定ができる。
3. serviceとして登録する
takama/daemonを使って、init.dやlaunchctlにより自身をserviceとして登録し、OSの機能でstartする。
これは外部のプログラムに頼っているといえるが、Goだけで実装できるという点では変わらない。
ちゃんとserviceとして実装したい場合はこの方法を使うのが良い。
まとめ
現在Go標準ライブラリではデーモンの実装はサポートされていないが、os.StartProcess()
などで自身を実行することによりデーモンを実装することができる。