From 42e08937c4f9617796c21fcbafee75465e472c3e Mon Sep 17 00:00:00 2001 From: Jeff Lindsay Date: Thu, 21 Jan 2016 15:52:35 -0600 Subject: [PATCH 1/2] dropped waitgrp and posthook for now. implemented prehook. added an example using all the commands. wrote an initial readme --- README.md | 170 +++++++++++++++++++++++----------------- cmd/entrykit.go | 2 - codep/codep/README.md | 5 ++ example/Dockerfile | 22 ++++++ example/Makefile | 11 +++ example/extra.conf | 3 + example/nginx.conf.tmpl | 25 ++++++ example/reloader | 5 ++ posthook/posthook.go | 15 ---- prehook/prehook.go | 12 ++- waitgrp/waitgrp.go | 15 ---- 11 files changed, 178 insertions(+), 107 deletions(-) create mode 100644 codep/codep/README.md create mode 100644 example/Dockerfile create mode 100644 example/Makefile create mode 100644 example/extra.conf create mode 100644 example/nginx.conf.tmpl create mode 100755 example/reloader delete mode 100644 posthook/posthook.go delete mode 100644 waitgrp/waitgrp.go diff --git a/README.md b/README.md index 2968561..8837735 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,104 @@ -not a proper readme, just my design notes so far +# Entrykit +Entrypoint tools for elegant containers. + +Entrykit takes common tasks you might put in an entrypoint start script and lets you quickly set them up in your Dockerfile. It's sort of like an init process, +but we don't believe in heavyweight init systems inside containers. Instead, Entrykit takes care of practical tasks for building minimal containers. + +## Getting Entrykit + +In your Dockerfile download a release of Entrykit and extract it into your +PATH, such as `/bin` for simplicity. For best experience, run `entrykit --symlink` to set up the subcommands as regular commands. + +See the `example` directory for a demo Nginx container using Entrykit. + +## Using Entrykit + +Once set up, you use Entrykit commands in your entrypoint. You can use just one or chain them together. They all have the same usage structure: + + [[name=]task...] [-- exec] + +Commands are documented below. Commands take one or more optionally named "tasks", which is often a shell command, but is sometimes a file to operate on. Then `--` is used to define the next operation. This is similar to `&&` except it's built in to all commands so you don't need to run these commands in a subshell. + +## Commands + +### `codep` - Codependent tasks + +`codep` runs multiple processes in parallel, proxying signals, but unlike nearly every init system, it kills all processes if one process terminates. This allows the container to exit so Docker or another init system can cleanly restart it if appropriate. + +This is ideal for runtime configuration rendering tools, such as conf.d and consul-template, or anything else that makes sense to run as a co-process in the container. Don't go overboard! + +``` +ENTRYPOINT ["codep", \ + "/bin/config-reloader", \ + "/usr/sbin/nginx" ] +``` + +You can run more tasks after your tasks exit using `--`, but this is not terribly common. + +### `render` - Template rendering + +`render` takes one or more paths to files that will be rendered using [Sigil](https://github.com/gliderlabs/sigil) templating. The template is loaded from a file with the same path but with `.tmpl` added extension. For example, if you want to render a template at `/etc/nginx.conf`, then you would copy a template file to `/etc/nginx.conf.tmpl` and use `render /etc/nginx.conf`. + +This is particularly useful to use environment variables in configuration, which is our preferred way to configure containers at boot time. But it also comes with the rest of Sigil's configuration oriented templating functions. + +``` +COPY ./nginx.conf.tmpl /etc/nginx.conf.tmpl +ENTRYPOINT ["render", "/etc/nginx.conf", "--", "/usr/sbin/nginx"] ``` -entrykit - -Entrypoint tools for elegant, programmable containers - - Useful for Docker, rkt, LXC, etc - - Good for image authors. Eliminates helper/start scripts or depending on shells. - - Can be good for image users. Allows users to program/extend containers. - -/bin/entrykit --symlink - --e allow environment as well --E allow environment prefixed with EK_ --f use config file --p prefix output of tasks - -codep [-eE] [[name=]task...] [-- exec] -waitgrp [-eE] [[name=]task...] [-- exec] -render [-eE] [[name=]path...] [-- exec] -switch [-eE] [[name=]exec...] [-- exec] -prehook [-eE] [[name=]hook...] [-- exec] -posthook [-eE] [[name=]task...] [-- exec] - disable exec, allows parent process to exist entire time -undaemon? - - -/bin/entrykit -f -- - prehook - render - switch - posthook - codep - waitgrp - -specific - - no environment, inline args - - no general entrykit, specific tools -general - - open to environment - - default entrykit - -don't support && alternative to -- - even though it works without shell, - -- behavior is not always equivalent of && - -intentionally no looping or restarting tool - * primary use case is against best practice. use higher level supervisor/restart-policy - * edge use cases are minimal and aren't worth encourage bad practice - -SWITCH_SHELL=/bin/sh -RENDER_CONFIG=/config/consul.json -CODEP_NGINX=nginx -g -PREHOOK_HTPASSWD=htpasswd -bc /etc/nginx/htpasswd $HTPASSWD - -versioning - semver. - pre 1.0: only minor is used (0.3.0) - get to 1.0 quickly. - major: stable interface for commands - minor: compatible additions - patch: compatible fixes - -prehook -- /bin/consul agent -config-dir=${CONFIG_DIR:-/config} - -render - -split -join -replace -...most of strings pkg - -https://github.com/teepark/envrender/blob/master/main.go +Since you usually have more to do after the `render` command, it's typical to chain with `--`. Anything after `--` is exec'd into. + +### `switch` - Command switching + +`switch` allows you to exec into alternative processes than your normal entrypoint based on the command provided when the container is run. We typically like containers that need no command and just do their thing immediately, but sometimes there are alternative modes of operation such as getting into the shell or displaying version or help information. + +This is the first command to really take advantage of named tasks. The name of the task is the command string it will switch on, and the value is the full command it will run. For example, you can expose the shell when users run with the command `shell` with `switch shell=/bin/sh`. And as usual you can provide multiple tasks for more than one command. ``` +ENTRYPOINT ["switch", "shell=/bin/sh", "version=nginx -v", "--", "/usr/sbin/nginx"] +``` +If none of the commands are provided, it goes on to exec the next task after `--`. + +### `prehook` - Run pre-commands on start + +If there are other set up tasks to perform, you can add them with `prehook`. You can specify multiple tasks and they'll be run in order. If they fail, the chained tasks will not be run. This is particularly interesting when you use an undocumented flag that allows users to specify their own prehook commands. This is an example of how Entrykit can be used to make your containers more customizable by the user. But for now, it's just a way to run serial tasks before your final entrypoint command. + +Here we display the Nginx version before starting Nginx: +``` +ENTRYPOINT ["prehook", "nginx -V", "--", "/usr/sbin/nginx"] +``` +## Chaining + +All these commands can be used together. Here is an example of all of them being used together as demonstrated in the example directory: + +``` +ENTRYPOINT [ \ + "switch", \ + "shell=/bin/sh", \ + "version=nginx -v", "--", \ + "render", "/demo/nginx.conf", "--", \ + "prehook", "nginx -V", "--", \ + "codep", \ + "/bin/reloader 3", \ + "/usr/sbin/nginx -c /demo/nginx.conf" ] +``` + +## Other ways to define your entrypoint + +Although not documented or properly tested, there are other ways you can set up these entrypoint commands. One way is with environment variables defined previously in your Dockerfile. It would look something like this: + +``` +ENV SWITCH_SHELL=/bin/sh +ENV RENDER_CONFIG=/etc/nginx.conf +ENV CODEP_NGINX=nginx -g +ENV CODEP_CONFD=confd +ENV PREHOOK_HTPASSWD=htpasswd -bc /etc/nginx/htpasswd $HTPASSWD + +ENTRYPOINT ["entrykit -e"] +``` +There is potentially another flag implemented to read config like that from a file. However, and this might be desired, this opens up the ability for users to mess with your entrypoint! But it's only possible if explicitly enabled. + +## License + +MIT diff --git a/cmd/entrykit.go b/cmd/entrykit.go index f50e4c2..a585943 100644 --- a/cmd/entrykit.go +++ b/cmd/entrykit.go @@ -7,11 +7,9 @@ import ( "github.com/progrium/entrykit" _ "github.com/progrium/entrykit/codep" - _ "github.com/progrium/entrykit/posthook" _ "github.com/progrium/entrykit/prehook" _ "github.com/progrium/entrykit/render" _ "github.com/progrium/entrykit/switch" - _ "github.com/progrium/entrykit/waitgrp" ) var Version string diff --git a/codep/codep/README.md b/codep/codep/README.md new file mode 100644 index 0000000..d450146 --- /dev/null +++ b/codep/codep/README.md @@ -0,0 +1,5 @@ +## Standalone codep + +In case you want a standalone binary for codep, you can build this directory. + + $ go build diff --git a/example/Dockerfile b/example/Dockerfile new file mode 100644 index 0000000..1abc364 --- /dev/null +++ b/example/Dockerfile @@ -0,0 +1,22 @@ +FROM gliderlabs/alpine:latest +EXPOSE 80 + +RUN apk-install nginx +RUN mkdir -p /demo/includes +RUN echo "Hello world" > /demo/index.html +RUN echo "" > /demo/empty.html + +COPY ./nginx.conf.tmpl /demo/nginx.conf.tmpl +COPY ./reloader /bin +COPY ./entrykit /bin +RUN entrykit --symlink + +ENTRYPOINT [ \ + "switch", \ + "shell=/bin/sh", \ + "version=nginx -v", "--", \ + "render", "/demo/nginx.conf", "--", \ + "prehook", "nginx -V", "--", \ + "codep", \ + "/bin/reloader 3", \ + "/usr/sbin/nginx -c /demo/nginx.conf" ] diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000..809b8d3 --- /dev/null +++ b/example/Makefile @@ -0,0 +1,11 @@ + +build: + cp ../build/Linux/entrykit . + docker build -t entrykit-example . + rm entrykit + +run: + docker run -p 8000:80 --name entrykit-example entrykit-example + +extra: + cat extra.conf | docker exec -i entrykit-example sh -c "cat > /demo/includes/extra.conf" diff --git a/example/extra.conf b/example/extra.conf new file mode 100644 index 0000000..ff4c17f --- /dev/null +++ b/example/extra.conf @@ -0,0 +1,3 @@ +location /extra { + index /demo/index.html; +} diff --git a/example/nginx.conf.tmpl b/example/nginx.conf.tmpl new file mode 100644 index 0000000..498ac14 --- /dev/null +++ b/example/nginx.conf.tmpl @@ -0,0 +1,25 @@ +daemon off; +error_log /dev/stdout notice; +user nobody nogroup; +worker_processes 1; +events { + worker_connections 1024; +} +http { + server { + listen 80; + server_name localhost; + access_log /dev/stdout; + root /; + + location / { + index /demo/empty.html; + } + + location /{{ var "DEMO" | default "demo" }} { + index /demo/index.html; + } + + include /demo/includes/*.conf; + } +} diff --git a/example/reloader b/example/reloader new file mode 100755 index 0000000..74926cd --- /dev/null +++ b/example/reloader @@ -0,0 +1,5 @@ +#!/bin/sh +while true; do + sleep $1 + nginx -s reload +done diff --git a/posthook/posthook.go b/posthook/posthook.go deleted file mode 100644 index 6cecc2c..0000000 --- a/posthook/posthook.go +++ /dev/null @@ -1,15 +0,0 @@ -package posthook - -import ( - "fmt" - - "github.com/progrium/entrykit" -) - -func init() { - entrykit.Cmds["posthook"] = Run -} - -func Run(config *entrykit.Config) { - entrykit.Error(fmt.Errorf("Not implemented")) -} diff --git a/prehook/prehook.go b/prehook/prehook.go index 6b711fd..65a6b43 100644 --- a/prehook/prehook.go +++ b/prehook/prehook.go @@ -1,8 +1,6 @@ package prehook import ( - "fmt" - "github.com/progrium/entrykit" ) @@ -11,6 +9,12 @@ func init() { } func Run(config *entrykit.Config) { - //defer entrykit.Exec(config.Exec) - entrykit.Error(fmt.Errorf("Not implemented")) + defer entrykit.Exec(config.Exec) + for _, task := range config.Tasks { + cmd := entrykit.CommandTask(task) + err := cmd.Run() + if err != nil { + entrykit.Error(err) + } + } } diff --git a/waitgrp/waitgrp.go b/waitgrp/waitgrp.go deleted file mode 100644 index 1d3c705..0000000 --- a/waitgrp/waitgrp.go +++ /dev/null @@ -1,15 +0,0 @@ -package waitgrp - -import ( - "fmt" - - "github.com/progrium/entrykit" -) - -func init() { - entrykit.Cmds["waitgrp"] = Run -} - -func Run(config *entrykit.Config) { - entrykit.Error(fmt.Errorf("Not implemented")) -} From f4cbaee60ee8a02c6e58abf04e2b8c6f052a1603 Mon Sep 17 00:00:00 2001 From: Jeff Lindsay Date: Thu, 21 Jan 2016 16:05:11 -0600 Subject: [PATCH 2/2] versoin bump --- Makefile | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index e0f346c..4c72431 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ NAME=entrykit ARCH=$(shell uname -m) ORG=progrium -VERSION=0.3.0 +VERSION=0.4.0 .PHONY: build release diff --git a/README.md b/README.md index 8837735..d711802 100644 --- a/README.md +++ b/README.md @@ -101,4 +101,4 @@ There is potentially another flag implemented to read config like that from a fi ## License -MIT +MIT