overseer
is a package for creating monitorable, gracefully restarting, self-upgrading binaries in Go (golang). The main goal of this project is to facilitate the creation of self-upgrading binaries which play nice with standard process managers, secondly it should expose a small and simple API with reasonable defaults.
Commonly, graceful restarts are performed by the active process (dark blue) closing its listeners and passing these matching listening socket files (green) over to a newly started process. This restart causes any foreground process monitoring to incorrectly detect a program crash. overseer
attempts to solve this by using a small process to perform this socket file exchange and proxying signals and exit code from the active process.
go get github.com/jpillora/overseer
This program works with process managers, supports graceful, zero-down time restarts and self-upgrades its own binary.
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/jpillora/overseer"
"github.com/jpillora/overseer/fetcher"
)
//create another main() to run the overseer process
//and then convert your old main() into a 'prog(state)'
func main() {
overseer.Run(overseer.Config{
Program: prog,
Address: ":3000",
Fetcher: &fetcher.HTTP{
URL: "http://localhost:4000/binaries/myapp",
Interval: 1 * time.Second,
},
})
}
//prog(state) runs in a child process
func prog(state overseer.State) {
log.Printf("app (%s) listening...", state.ID)
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "app (%s) says hello\n", state.ID)
}))
http.Serve(state.Listener, nil)
}
How it works:
overseer
uses the main process to check for and install upgrades and a child process to run Program
.Address/es
.Listener/s
for the Program
to consume.Fetcher
runs in a goroutine and checks for updates at preconfigured interval. When Fetcher
returns a valid binary stream (io.Reader
), the master process saves it to a temporary location, verifies it, replaces the current binary and initiates a graceful restart.fetcher.HTTP
accepts a URL
, it polls this URL with HEAD requests and until it detects a change. On change, we GET
the URL
and stream it back out to overseer
. See also fetcher.S3
.overseer
binary.overseer
is not a process manager.See Configuration options here and the runtime State available to your program here.
See the example/ directory and run example.sh
, you should see the following output:
$ cd example/
$ sh example.sh
BUILT APP (1)
RUNNING APP
app#1 (c7940a5bfc3f0e8633d3bf775f54bb59f50b338e) listening...
app#1 (c7940a5bfc3f0e8633d3bf775f54bb59f50b338e) says hello
app#1 (c7940a5bfc3f0e8633d3bf775f54bb59f50b338e) says hello
BUILT APP (2)
app#2 (3dacb8bc673c1b4d38f8fb4fad5b017671aa8a67) listening...
app#2 (3dacb8bc673c1b4d38f8fb4fad5b017671aa8a67) says hello
app#2 (3dacb8bc673c1b4d38f8fb4fad5b017671aa8a67) says hello
app#1 (c7940a5bfc3f0e8633d3bf775f54bb59f50b338e) says hello
app#1 (c7940a5bfc3f0e8633d3bf775f54bb59f50b338e) exiting...
BUILT APP (3)
app#3 (b7614e7ff42eed8bb334ed35237743b0e4041678) listening...
app#3 (b7614e7ff42eed8bb334ed35237743b0e4041678) says hello
app#3 (b7614e7ff42eed8bb334ed35237743b0e4041678) says hello
app#2 (3dacb8bc673c1b4d38f8fb4fad5b017671aa8a67) says hello
app#2 (3dacb8bc673c1b4d38f8fb4fad5b017671aa8a67) exiting...
app#3 (b7614e7ff42eed8bb334ed35237743b0e4041678) says hello
Note: app#1
stays running until the last request is closed.
func main() {
overseer.Run(overseer.Config{
Program: prog,
Address: ":3000",
})
}
Send main
a SIGUSR2
(Config.RestartSignal
) to manually trigger a restart
func main() {
overseer.Run(overseer.Config{
Program: prog,
NoRestart: true,
Fetcher: &fetcher.HTTP{
URL: "http://localhost:4000/binaries/myapp",
Interval: 1 * time.Second,
},
})
}
Your binary will be upgraded though it will require manual restart from the user, suitable for creating self-upgrading command-line applications.
URL
func main() {
overseer.Run(overseer.Config{
Program: prog,
Fetcher: &fetcher.HTTP{
URL: "http://localhost:4000/binaries/app-"+runtime.GOOS+"-"+runtime.GOARCH,
//e.g.http://localhost:4000/binaries/app-linux-amd64
},
})
}
overseer.Config
cannot be changed via an upgrade, the master process must be restarted.
Addresses
can only be changed by restarting the main process.mv
for moving files because mv
handles cross-partition moves unlike os.Rename
.init()
functions will run twice on start, once in the main process and once in the child process.See CONTRIBUTING.md