Merge pull request 'README.md + Example + Docker build args + wmpidproxy fix' (#1) from readme into main

Reviewed-on: #1
This commit is contained in:
suyono 2023-12-26 20:57:04 +00:00
commit b2668287ae
8 changed files with 189 additions and 18 deletions

View File

@ -1,6 +1,13 @@
DESTDIR = /usr/local/bin
all: wingmate dummy oneshot spawner starter pidproxy exec
installs = install-dir
programs = wingmate pidproxy exec
ifdef TEST_BUILD
programs += oneshot spawner starter dummy
installs += install-test
endif
all: ${programs}
wingmate:
$(MAKE) -C cmd/wingmate all
@ -32,12 +39,16 @@ clean:
$(MAKE) -C cmd/experiment/spawner clean
$(MAKE) -C cmd/experiment/starter clean
install:
install -d ${DESTDIR}
install: ${installs}
$(MAKE) -C cmd/wingmate DESTDIR=${DESTDIR} install
$(MAKE) -C cmd/pidproxy DESTDIR=${DESTDIR} install
$(MAKE) -C cmd/exec DESTDIR=${DESTDIR} install
install-test:
$(MAKE) -C cmd/experiment/dummy DESTDIR=${DESTDIR} install
$(MAKE) -C cmd/experiment/oneshot DESTDIR=${DESTDIR} install
$(MAKE) -C cmd/experiment/spawner DESTDIR=${DESTDIR} install
$(MAKE) -C cmd/experiment/starter DESTDIR=${DESTDIR} install
install-dir:
install -d ${DESTDIR}

137
README.md
View File

@ -1,2 +1,139 @@
# wingmate
Wingmate is a process manager for services. It works like init. It starts and restarts services.
It also has cron feature. It is designed to run in a container/docker.
The Wingmate binary do not need any external dependency.
Just copy the binary, and exec from the entry point script.
# Getting Started
## Binaries
There are three binaries in this project: `wingmate`, `wmpidproxy`, and `wmexec`.
`wingmate` is the core binary. It reads config, starts, restarts services. It also
runs cron. Read the [configuration](#configuration) section for files needed to run
`wingmate`.
`wmpidproxy` is a helper binary for monitoring _legacy style_ service (fork, exit
initial proces, and continue in background). Read [here](#wingmate-pid-proxy-binary)
for further details about `wmpidproxy`.
`wmexec` is a helper binary for running process in different `user` or `group`.
It also useful for setting the process as process group leader.
Read [here](#wingmate-exec-binary) for further details about `wmexec`.
## Building a container image based on wingmate image in Docker Hub
Wingmate has no dependency other than `alpine` base image, so you just need to copy
the binaries directly. If you have built your application into an `alpine` based image,
all you need to do is copy whichever binary you need, crontab file (if you use cron)
and add some shell script to glue them together. Here is a Dockerfile example.
```Dockerfile
# Dockerfile
FROM suyono/wingmate:alpine as source
FROM alpine:latest
ADD --chmod=755 wingmate/ /etc/wingmate/
ADD --chmod=755 entry.sh /usr/local/bin/entry.sh
COPY --from=source /usr/local/bin/wingmate /usr/local/bin/wingmate
COPY --from=source /usr/local/bin/wmpidproxy /usr/local/bin/wmpidproxy
COPY --from=source /usr/local/bin/wmexec /usr/local/bin/wmexec
ENTRYPOINT [ "/usr/local/bin/entry.sh" ]
CMD [ "/usr/local/bin/wingmate" ]
```
You can find some examples for shell script in [alpine docker](docker/alpine) and
[bookworm docker](docker/bookworm).
## Configuration
When `wingmate` binary starts, it will look for some files. By default, it will
try to read the content of `/etc/wingmate` directory. You can change the directory
where it reads by setting `WINGMATE_CONFIG_PATH` environment variable. The structure
inside the config path should look like this.
```shell
/etc
└── wingmate
├── crontab
├── crontab.d
│   ├── cron1.sh
│   ├── cron2.sh
│   └── cron3.sh
└── service
├── one.sh
└── spawner.sh
```
First, `wingmate` will try to read the content of `service` directory. The content of
this directory should be executables (either shell scripts or binaries). The wingmate
will run every executable in `service` directory without going into any subdirectory.
Next, `wingmate` will read the `crontab` file. `wingmate` expects the `crontab` file using
common UNIX crontab file format. Something like this
```shell
┌───────────── minute (059)
│ ┌───────────── hour (023)
│ │ ┌───────────── day of the month (131)
│ │ │ ┌───────────── month (112)
│ │ │ │ ┌───────────── day of the week (06) (Sunday to Saturday)
│ │ │ │ │
│ │ │ │ │
│ │ │ │ │
* * * * * <commad or shell script or binary>
```
The command part only support simple command and arguments. Shell expression is not supported
yet. It is recommended to write a shell script and put the path to shell script in
the command part.
# Appendix
## Wingmate PID Proxy binary
`wingmate` works by monitoring its direct children process. When it sees one of its children
exited, it will start the child process again.
Sometimes you find some services work by running in the background. It means it forks a new
process, disconnect the new child from terminal, exit the parent process, and continue
running in the child process. This kind of service usually write its background process
PID in a pid file.
To monitor the background services, `wingmate` utilizes `wmpidproxy`. `wmpidproxy` runs
in foreground in-place of the background service. It also periodically check whether the
background service is still running, in current implementation it checks every second.
```shell
wmpidproxy --pid-file <path to pid file> -- <background service binary/start script>
```
#### Example
Running sshd background with `wingmate` and `wmpidproxy`: [here](example/ssh-docker)
#### Note
It is recommended to configure services to run in foreground if they support it. When services
running in foreground, they are running as direct children process of `wingmate`.
`wingmate` monitors children process effectively. Whenever a child process exited/terminated,
`wingmate` will start it again quickly. Running in foreground also removes the overhead of running
`wmpidproxy` together with the service.
## Wingmate Exec binary
`wingmate` runs all the services as its children using the same `uid`, `gid`, and in the
same process group. You can use `wmexec` to run service in different `uid`, `gid`, or make
the service process as its own process group leader.
#### Syntax
```shell
wmexec [--user <uid>[:<gid>]] [--setsid] -- <target executable>
```
| Option | Parameter | Description |
|----------|-----------|----------------------------------------------------------------------------------------------------------|
| --user | uid[:gid] | Set the real user ID and the real group id. Uid and Gid can be either in numeric form or in name form |
| --setsid | | Set the process become the leader of its own process group, effectively detaching from parent's terminal |
#### Example
You can find example for `wmexec` in [here](docker/alpine/etc/wingmate) and [here](docker/bookworm/etc/wingmate)

View File

@ -1,6 +1,8 @@
package main
import (
"bufio"
"errors"
"log"
"os"
"os/exec"
@ -92,13 +94,13 @@ func pidProxy(cmd *cobra.Command, args []string) error {
err error
pid int
sc chan os.Signal
t *time.Timer
t *time.Ticker
)
sc = make(chan os.Signal, 1)
signal.Notify(sc, unix.SIGTERM)
t = time.NewTimer(time.Second)
t = time.NewTicker(time.Second)
check:
for {
@ -130,8 +132,6 @@ func readPid(pidFile string) (int, error) {
var (
file *os.File
err error
buf []byte
n int
pid64 int64
)
@ -142,18 +142,15 @@ func readPid(pidFile string) (int, error) {
_ = file.Close()
}()
buf = make([]byte, 1024)
n, err = file.Read(buf)
if err != nil {
return 0, err
scanner := bufio.NewScanner(file)
if scanner.Scan() {
if pid64, err = strconv.ParseInt(scanner.Text(), 10, 64); err != nil {
return 0, err
}
return int(pid64), nil
} else {
return 0, errors.New("invalid scanner")
}
pid64, err = strconv.ParseInt(string(buf[:n]), 10, 64)
if err != nil {
return 0, err
}
return int(pid64), nil
}
func startProcess(arg0 string, args ...string) {

View File

@ -2,6 +2,7 @@ FROM golang:1.21-alpine as builder
ADD . /root/wingmate
WORKDIR /root/wingmate/
ARG TEST_BUILD
RUN apk add make build-base && CGO_ENABLED=1 make all && make DESTDIR=/usr/local/bin/wingmate install

View File

@ -2,6 +2,7 @@ FROM golang:1.21-bookworm as builder
ADD . /root/wingmate
WORKDIR /root/wingmate/
ARG TEST_BUILD
RUN make all && make DESTDIR=/usr/local/bin/wingmate install

View File

@ -0,0 +1,14 @@
FROM suyono/wingmate:alpine as source
FROM alpine:3.19
RUN apk update && apk add tzdata openssh-server && \
ln -s /usr/share/zoneinfo/Australia/Sydney /etc/localtime && ssh-keygen -A
COPY --from=source /usr/local/bin/wingmate /usr/local/bin/
COPY --from=source /usr/local/bin/wmpidproxy /usr/local/bin/
ADD --chmod=755 example/ssh-docker/entry.sh /usr/local/bin/entry.sh
ADD --chmod=755 example/ssh-docker/etc /etc
ENTRYPOINT [ "/usr/local/bin/entry.sh" ]
CMD [ "/usr/local/bin/wingmate" ]

View File

@ -0,0 +1,7 @@
#!/bin/sh
if [ $# -gt 0 ]; then
exec "$@"
else
exec /usr/local/bin/wingmate
fi

View File

@ -0,0 +1,3 @@
#!/bin/sh
exec /usr/local/bin/wmpidproxy --pid-file /var/run/sshd.pid -- /usr/sbin/sshd