-
Couldn't load subscription status.
- Fork 5
Proof of concept: usage/help screen as the default process type #229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Our buildpacks currently set `web` as the default process type, and the Procfile buildpack sets that as the default, too, if present. The process type's command wrapper (e.g. `/lifecycle/launcher/web`) is set as the `ENTRYPOINT` for the image. This wrapper sets the env vars from all buildpacks and layers, and execs the process type's command. Any arguments provided to docker run after the image name are forwarded to this command as additional arguments.
As a result, one typically cannot `docker run <image-name> bash -c "echo hello world"` without specifying `--entrypoint launcher`.
A workaround employed by some buildpacks is to define the default process type's command as `["bash", "-c"]`, and then make the default argument(s) whatever the actual command is.
However, this does not allow for additional arguments, so the above example doesn't work either (only `docker run <image-name> bash`, without further arguments).
Furthermore, having `web` be the default process type is not actually useful in practice, because a user must pass at least `-e PORT=$PORT` and `-p $LOCALPORT:$PORT` to the `docker run` command for the image to work:
$ docker run --rm -e PORT=8080 -p 8080:8080 image-name
This proof of concept disables the default process flag for `web`, and sets a new process type, `usage`, as the default instead. This `usage` program shows a help screen with information on how to invoke the image, a list of process types, and usage information for individual process types.
A simple invocation of the image shows usage details and a list of process types:
$ docker run --rm image-name
The `help` sub-command gives details on how to use a particular process type:
$ docker run --rm image-name help web
In addition, unless the first argument is `help` or `--help`, the `usage` program `exec`s all remaining arguments, meaning it's possible to launch arbitrary commands out of the box:
$ docker run --rm image-name bash -c "echo 'hello world'"
$ docker run --rm image-name uname -a
$ docker run --rm image-name -- help # in case 'help' is a binary on $PATH (the 'bash' help command is just a builtin)
This proof of concept is intended to showcase the soundness of this approach, and allow others to test the usability of the idea. For the final implementation, the following should probably be changed:
- move implementation to its own buildpack
- make that buildpack last in the list for all groups in the heroku builder (so that it overrides default setting of `web` by the Procfile buildpack)
- probably have that buildpack emit some special default notes about `-e` and `-p` for the `web` process case
- skip execution when building on Heroku (so that no `usage` process shows up in the process table)
- possibly convert the entrypoint `usage.sh` program from Bash to Rust
1. this implementation currently only works on projects without a `Procfile` (since the Procfile buildpack runs last and sets `web` as default)
2. Heroku looks for a `web` process type to boot an app for HTTP traffic, not the default process type, so no impact there
3. Once added to the builder, a dedicated buildpack would immediately provide basic usage info to all existing buildpacks!
GUS-W-19330133
The idea here is to allow buildpacks to provide some more info for specific process types by placing a file (named after the process type) into a particular layer (whose name is known to the usage program, which can then find the usage info text file). In a standalone implementation of the buildpack, the default text for the `web` process could also be different, and remind users to supply `-e PORT` and `-p` options to `docker run`.
|
The default "welcome" screen: dzuelke@localhost:~$ docker run --rm php-cnb-hello-world
██╗ ██╗███████╗██╗ ██████╗ ██████╗ ██╗ ██╗███████╗
██║ ██║██╔════╝██║ ██╔════╝██╔═══██╗███╗ ███║██╔════╝
██║ █╗ ██║█████╗ ██║ ██║ ██║ ██║█╔████╔█║█████╗
██║███╗██║██╔══╝ ██║ ██║ ██║ ██║█║╚██╔╝█║██╔══╝
╚███╔███╔╝███████╗███████╗╚██████╗╚██████╔╝█║ ╚═╝ █║███████╗
╚══╝╚══╝ ╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ═╝ ╚╝╚══════╝
This help screen is the default process type for your CNB app image.
It can provide general instructions, list available process types, show help
for specific process types, and execute arbitrary commands.
Invoking this Usage Help
========================
Running your image without any arguments, or with `help' or `--help',
will display this screen:
$ docker run --rm <this-image>
$ docker run --rm <this-image> help
$ docker run --rm <this-image> --help
Basic Usage Summary
===================
$ docker run --rm <image-name> (help | --help) [process-type]
$ docker run --rm --entrypoint <process-type> <this-image> [<argument>...]
$ docker run --rm [-it] <this-image> [--] <command> [<argument>...]
Available Process Types
=======================
The following process types are available in this image:
- web
Getting Help for a Process Type
===============================
To show help for a process type, pass its name after `help', like so:
$ docker run --rm <this-image> help <process-type>
Launching a Process Type
========================
To launch a specific process type, specify it as the `--entrypoint', e.g.:
$ docker run --rm --entrypoint <process-type> <this-image>
Some process types may require certain environment variables to be set, or
ports to be forwarded from the container, in order to be usable. Refer to
the help output for the respective process type for further information.
For example, a `web' process type typically requires a forwarded port, and
the environment variable `$PORT' specifying the in-container port number:
$ docker run --rm --entrypoint web -p 8080:8080 -e PORT=8080 <this-image>
Executing commands
==================
When no entrypoint is specified (or with `--entrypoint usage'), and when
not providing `help' or `--help' as the first argument after the image name,
the given arguments will be executed as regular commands.
To launch an interactive shell, use the `-it' option, and specify `bash' as
the command:
$ docker run --rm -it <this-image> bash
You may pass arbitrary additional arguments to commands, for example:
$ docker run --rm -it <this-image> bash --login
To completely bypass this help tool, specify `--entrypoint launcher', e.g.:
$ docker run --rm -it --entrypoint launcher <this-image> uname -a
Further reading
===============
For additional documentation on how to run buildpacks-built images, refer to
the documentation at Buildpacks.io:
https://buildpacks.io/docs/for-app-developers/how-to/build-outputs/specify-launch-process/ |
|
It shows a shorter introduction if explicitly called with dzuelke@localhost:~$ docker run --rm php-cnb-hello-world --help
██╗ ██╗ ███████╗ █████╗ █████╗ ███████╗
██║ ██║ ██╔════╝██╔══██╗ ██╔═══╝ ██╔════╝
██║ ██║ ███████╗███████║ ██║ ███╗█████╗
██║ ██║ ╚════██║██╔══██║ ██║ ██║██╔══╝
╚██████╔╝ ███████║██║ ██║ ╚█████╔╝███████╗
╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚════╝ ╚══════╝
Basic Usage Summary
===================
$ docker run --rm <image-name> (help | --help) [process-type]
$ docker run --rm --entrypoint <process-type> <this-image> [<argument>...]
$ docker run --rm [-it] <this-image> [--] <command> [<argument>...]
Available Process Types
=======================
The following process types are available in this image:
- web
… |
|
Calling dzuelke@localhost:~$ docker run --rm php-cnb-hello-world help web
██╗ ██╗ ███████╗ █████╗ █████╗ ███████╗ ██╗ ██╗███████╗██████╗
██║ ██║ ██╔════╝██╔══██╗ ██╔═══╝ ██╔════╝ ██║ ██║██╔════╝██╔══██╗
██║ ██║ ███████╗███████║ ██║ ███╗█████╗ ██╗ ██║ █╗ ██║█████╗ ██████╔╝
██║ ██║ ╚════██║██╔══██║ ██║ ██║██╔══╝ ╚═╝ ██║███╗██║██╔══╝ ██╔══██╗
╚██████╔╝ ███████║██║ ██║ ╚█████╔╝███████╗ ██╗ ╚███╔███╔╝███████╗██████╔╝
╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚════╝ ╚══════╝ ╚═╝ ╚══╝╚══╝ ╚══════╝╚═════╝
This process type starts PHP-FPM and the Apache HTTPD web server and serves
the application on the port specified in environment variable `PORT'.
Launching this Process Type
===========================
This process type requires a forwarded port to serve traffic on, and the
environment variable `PORT' specifying the in-container port number:
$ docker run --rm --entrypoint web -p 8080:8080 -e PORT=8080 <this-image>
The command above maps port 8080 on the host to port 8080 in the container.
Additional Usage Information
============================
The launched program, `heroku-php-apache2', supports various options and
arguments for customizing PHP-FPM or HTTPD, or e.g. specifying a document root.
Complete usage information is available by passing argument `--help' to
the `web' entrypoint:
$ docker run --rm --entrypoint web <this-image> --help
|
|
Arbitrary commands can be executed against the image: dzuelke@localhost:~$ docker run --rm php-cnb-hello-world bash -c 'echo "hello world"'
hello world
dzuelke@localhost:~$ docker run --rm php-cnb-hello-world uname -a
Linux f1b32a12d4a8 6.10.14-linuxkit #1 SMP Sat May 17 08:28:57 UTC 2025 aarch64 aarch64 aarch64 GNU/Linux
dzuelke@localhost:~$ docker run --rm -ti php-cnb-hello-world bash
heroku@f41b68441f8d:/workspace$ echo "hi"; exit
hi
exit |
I wonder if
|
|
Yeah, possibly, @edmorley. |
|
Related discussion: heroku/buildpacks#15 |
(scroll down to comments to see what the output looks like in practice 🙃)
Background
Our buildpacks currently set
webas the default process type, and the Procfile buildpack sets that as the default, too, if present. The process type's command wrapper (e.g./lifecycle/launcher/web) is set as theENTRYPOINTfor the image. This wrapper sets the env vars from all buildpacks and layers, and execs the process type's command. Any arguments provided to docker run after the image name are forwarded to this command as additional arguments.As a result, one typically cannot
docker run <image-name> bash -c "echo hello world"without specifying--entrypoint launcher.A workaround employed by some buildpacks is to define the default process type's command as
["bash", "-c"], and then make the default argument(s) whatever the actual command is.However, this does not allow for additional arguments, so the above example doesn't work either (only
docker run <image-name> bash, without further arguments).The issue with
webas the defaultFurthermore, having
webbe the default process type is not actually useful in practice, because a user must pass at least-e PORT=$PORTand-p $LOCALPORT:$PORTto thedocker runcommand for the image to work:Just launching the default process using
docker run image-namemay cause a successful startup, including a message of having bound to a port (many servers will default to e.g.8080if no$PORTis in the env), but it will not be reachable, and without some easy to access documentation, users may not quickly determine what they are doing wrong.Solution
This proof of concept disables the default process flag for
web, and sets a new process type,usage, as the default instead. Thisusageprogram shows a help screen with information on how to invoke the image, a list of process types, and usage information for individual process types.A simple invocation of the image shows usage details and a list of process types:
The
helpsub-command gives details on how to use a particular process type:In addition, unless the first argument is
helpor--help, theusageprogramexecs all remaining arguments, meaning it's possible to launch arbitrary commands out of the box:Future
This proof of concept is intended to showcase the soundness of this approach, and allow others to test the usability of the idea. For the final implementation, the following should probably be changed:
webby the Procfile buildpack)usageprocess shows up in theheroku psprocess table)$DYNO-eand-pfor thewebprocess caseweb-e/-pstuff forweb)usage.shprogram from Bash to Rust/layers/config/metadata.tomlENTRYPOINTwaslauncher, allowingdocker run --rm image-name webetcNotes
Procfile(since the Procfile buildpack runs last and re-setswebto be the default)webprocess type to boot an app for HTTP traffic, not the default process type, so no impact there 💜GUS-W-19330133