Create 0316-launcher-decoupling#336
Conversation
Signed-off-by: Hemant Goyal <87599584+Hemant28codes@users.noreply.github.com>
|
Maintainers, As you review this RFC please queue up issues to be created using the following commands: Issues(none) |
| # Motivation | ||
| [motivation]: #motivation | ||
|
|
||
| The current exporter implementation unconditionally creates a fresh file-based layer to include the launcher binary, which overwrites any existing files or symlinks at that path in the run image. This prevents platforms from providing a customized launcher directly within their base images, which is necessary for certain environments or security configurations. |
There was a problem hiding this comment.
which is necessary for certain environments or security configurations
Hi! Could you expand a bit on this? It seems to be the key point behind the motivation here, but currently is a bit vague?
I'm guessing the reason is so that app image rebases can pick up new launcher versions for patching Go vulns reported in the binary by security scanners, or something similar?
There was a problem hiding this comment.
Yes that's the main reason, I will update the motivation section.
There was a problem hiding this comment.
Updated the motivation section.
Signed-off-by: Hemant Goyal <87599584+Hemant28codes@users.noreply.github.com>
| # Summary | ||
| [summary]: #summary | ||
|
|
||
| This RFC proposes a mechanism to allow the launcher binary to be provided by the run image instead of being unconditionally injected by the lifecycle's exporter phase. By introducing a specific flag, the exporter can be instructed to skip the creation of the launcher binary layer, relying instead on a pre-existing binary or symlink at the designated path in the run image. |
There was a problem hiding this comment.
Could this be an argument to rebase instead? I like the idea of being able to update this, but I'm not a fan of stuffing more things into our run images.
Ex: pack rebase --lifecycle <new lifecycle> where that's a version, image ref, etc.. like we do with run-image
There was a problem hiding this comment.
We don't use pack rebase to swap run images, we directly use lifecycle for builds. If this is an argument to rebase, it won't solve the case when someone is not using pack rebase to swap run image layers
There was a problem hiding this comment.
"stuffing more things into our run images." - This will be totally optional, as the launcher binary needs to be part of run image, and as the new run image is available it should contain the upgraded launcher.
There was a problem hiding this comment.
"pack rebase --lifecycle " - we don't want to upgrade the entire lifecycle as part of this, because in our builder we have lifecycle installed, which is then used to create the final app image by its creator binary. The final export phase which adds the /cnb/lifecycle/launcher is what we need to have it configurable and pointing through run image.
There was a problem hiding this comment.
We don't use pack rebase to swap run images, we directly use lifecycle for builds. If this is an argument to rebase, it won't solve the case when someone is not using pack rebase to swap run image layers
I don't follow your use case then. If you're rebuilding, then won't the lifecycle just include the most recent launcher? Why do you need to have it take something different from a different location?
"stuffing more things into our run images." - This will be totally optional, as the launcher binary needs to be part of run image, and as the new run image is available it should contain the upgraded launcher.
Yes, but I want to use this feature. 😄 It's a good feature and something I know will address needs people have voiced to me.
There are a lot of pack build and pack rebase users, so I think this proposal should include them as well. Requiring them to customize a run image is going to be a non-starter for a lot of users. Passing a flag to pack rebase, much easier.
There was a problem hiding this comment.
"pack rebase --lifecycle " - we don't want to upgrade the entire lifecycle as part of this, because in our builder we have lifecycle installed, which is then used to create the final app image by its creator binary. The final export phase which adds the /cnb/lifecycle/launcher is what we need to have it configurable and pointing through run image.
If lifecycle is what's adding the launcher in your case, then perhaps we could add an argument to lifecycle to specify where it loads launcher?
There was a problem hiding this comment.
"I don't follow your use case then. If you're rebuilding, then won't the lifecycle just include the most recent launcher? Why do you need to have it take something different from a different location?" - we are not rebuilding, we build the container once, but instead of using pack rebase, we have our mechanism of swapping the run image by some layer annotations.
There was a problem hiding this comment.
As a part of proposal, it can be extended to pack rebase as well, but only adding it in the pack, will not cover the case for users directly using lifecycle to build the container.
There was a problem hiding this comment.
we are not rebuilding, we build the container once, but instead of using pack rebase, we have our mechanism of swapping the run image by some layer annotations.
That's fine, but what you're talking about is a bespoke solution, whereas many people use pack rebase. If the proposal covers both cases, I'm 100% in. This is something that will help a lot.
There was a problem hiding this comment.
Sure we can have both the usecases covered.
Just to give you an idea of what our usecase is:
- Current Setup
Source code + Builder(contains lifecycle) -----(Build)----> Application image(contains launcher from lifecycle in builder + run image)
- Proposed Setup
Source code + Builder(contains lifecycle) -----(Build)----> Application image( run image), Run image -> will have launcher installed
One question here:
Can we have different versions for lifecycle building the application and the lifecycle version from which we are downloading the launcher?
Signed-off-by: Hemant Goyal <87599584+Hemant28codes@users.noreply.github.com>
| # How it Works | ||
| [how-it-works]: #how-it-works | ||
|
|
||
| A new flag (e.g., --launcher-in-run-image) will be added to the exporter phase. |
There was a problem hiding this comment.
Should this be a boolean or is it better to pass in a path to the launcher? It shouldn't matter whether its in the Run image or Build image as long the users of the pack/lifecycle decide to create that launcher at some point. So in effect is it better to have --launcher-path=<my_launcher>path and the equivalent CNB_LAUNCHER_PATH? @dmikusa FYI
There was a problem hiding this comment.
Actually yes this makes sense.
|
Thanks for this RFC — the use case is real. Security scanners flagging launcher CVEs that force full rebuilds across thousands of app images is genuinely painful, and worth solving. After the working group discussion I dug into the lifecycle code a bit, and I think there's a different mechanism that gets to the same goal without changing the OCI artifact structure. The problem with the current proposal Skipping the launcher layer at build time ( What the metadata already gives us The { "launcher": { "sha": "sha256:3e4529cbb..." } }The rebaser already reads this label in full. It knows exactly which layer is the launcher. It just doesn't use that today beyond bookkeeping. A different approach: extend rebase with Instead of changing the build artifact, extend the lifecycle rebase \
--run-image new-run:v2 \
--launcher-image buildpacksio/launcher:0.20.1 \
app-image:latestThe rebaser uses This covers both workflows For For the annotation-based workflow you described — these are independent operations. Your existing run image swap stays untouched. You'd only call Custom launchers also work: platforms already use What this needs
Happy to help think through the spec language or the imgutil side if this direction makes sense to the group. |
|
@jjbustamante the approach you suggested, won't this require running lifecycle rebase --launcher-image for each application image built? This is our current architecture -
If you see this architecture, after building the container once, we actually only have filesystem storing it, and at the serving time just pulling all the layers and run the clone. So "lifecycle rebase --launcher-image" running this everytime is kind of impossible in this case for every user. Can we maybe have something like we have some annotation at the time of building it itsleff which actually creates a symlink to the run image, so whenever we see that there is a new launcher that will be updated in the run image, and symlink will start pointing to that. |
|
One more thing worth calling out — RFC #333 ("Rebase Buildpack Contributed Layers") by @jabrown85 is working on a related problem: extending rebase to selectively patch any buildpack-contributed layer using a metadata file and a patch OCI image. The That actually strengthens the case for this RFC to focus narrowly on what #333 can't solve: the high-scale, annotation-based swap architecture where a per-image rebase operation isn't viable, and the launcher needs to be a dependency of the run image rather than a self-contained layer. Both RFCs will also share infrastructure work — specifically, imgutil needs to support mid-stack layer replacement by digest, which neither can do today. Worth coordinating there. It might be useful to cross-reference both RFCs and make explicit which use cases each one is meant to address. |
That works for me, so long as this use case is capture on #333. This would be the approach used by probably 99% of the Paketo users.
Does this mean that if I don't want this new symlink functionality, then there's no change to how things work presently? Since this is being added for a very specific subset of users/use cases, I just want to make sure this is an opt-in feature. |
Updated the detailed working section for this. Signed-off-by: Hemant Goyal <87599584+Hemant28codes@users.noreply.github.com>
|
Hi @jjbustamante I have modified the RFC to include the specific details mentioned in your above comment. |
If you don't want to use the |
|
@Hemant28codes could you rename the file to |
Updated RFC name Signed-off-by: Hemant Goyal <87599584+Hemant28codes@users.noreply.github.com>
Signed-off-by: Hemant Goyal <87599584+Hemant28codes@users.noreply.github.com>
Signed-off-by: Hemant Goyal <87599584+Hemant28codes@users.noreply.github.com>
Signed-off-by: Hemant Goyal <87599584+Hemant28codes@users.noreply.github.com>
|
Two questions I have in mind:
|
yes, this works. The run image vendor controls what binary sits at the spec-defined launcher path, so it can be the standard CNB launcher, a custom variant, or CNB-plus-extras. This is actually something you can already do at build time with the lifecycle's |
I do not think the launcher will stop depending on the spec, the whole idea of the spec is to define the rules and then a Platform implementor like Google, could replace it with their own binary but satisfying the spec. What IS achievable is a compatibility window. A newer launcher can run an older-built app as long as its supported API version range covers the version recorded in the app metadata. Implications for the RFC that I think should be called out explicitly:
io.buildpacks.run-image.launcher.platform-apis = "0.7,0.8,...,0.15"
io.buildpacks.run-image.launcher.buildpack-apis = "0.7,0.8,...,0.11"
I understand, from a Google perspective, you will be rebasing the run image and probably skipping the validations because at that scale, it is hard to do, but these implications are more around CNB and how to guarantee things will not break for others. |
|
Oh okk make sense @jjbustamante |
|
@jjbustamante is it possible to set buildpack API version at build time in image environment varriables? |
This right here is where things get a bit tricky. A run image does not support just one app. Without the build process placing a launcher layer that is known to be compatible with the app being built that just went through all the build and validations - there is no real paved path to making sure produced app images can always successfully run. That indeterminism makes me a bit uncomfortable. If you are the ones doing the building and the running it works fairly well. You are in complete control. But if you are producing builders for the community this gets a lot more messy IMO. How long will the run-image provided launcher support Platform API X.XX? If end users are rebasing weekly - will they caught out by the sudden symlink change of the launcher and now their previously 2 yr old working app is not launchable?
No, because each buildpack may target different buildpack APIs. The metadata in the image itself has the information on the buildpack APIs. I'll give this some more thought this week if I can find time |
|
@Hemant28codes I believe you said your platform doesn't really store the entire produced image after a buildpacks build. Is there a reason you don't only store the App Layers? If you are already omitting the base layers so you can stitch them together on the platform to execute the App Image, can you also stop storing the launcher and mount I'd also like to get your thoughts on producing a much smaller launcher. The scanning tools often pick up the launcher layer because it is go based. The CVEs that pop are almost entirely false for launcher but we patch anyway. If you instead bundled a much smaller surface area version of launcher - would that satisfy your needs without complicating distribution of app images? I'd be willing to try and transform launcher into a rust version, like @hone did once in the past. |
|
Hi @jabrown85 "can you also stop storing the launcher and mount https://hub.docker.com/r/buildpacksio/lifecycle into /cnb like you do the base layers" - for this part, will the issue of compatibility with which platform and buildpack API version the image is built still remain? If this is not the case and the launcher you are suggesting is independent of platform and buildpack API, then can't we just have it in the run image itslef, in that way also it won't be tied to build. "How long will the run-image provided launcher support Platform API X.XX "- For this we are planning to have a go binary which will act as a meta launcher - containing various launcher (let's say one corresponding to each major version), and based on platform api version and buildpack api version present in launcher it will invoke the appropriate launcher. |
|
The suggestion to produce a much smaller launcher is a great idea and would certainly be beneficial for reducing the scan surface area. That being said, we believe the proposal to decouple the launcher and allow it to be provided by the run image offers a significant additional advantage. The primary edge of this approach is the ability to update the launcher in running applications without requiring a full rebuilds. I think giving the ability to users to allow custom launchers decoupled from lifecycle will help regardless of launcher . |


No description provided.