Skip to content
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

Feature proposal: A way to specify in Dockerfile frontend that a given stage is a target stage, not an intermediate stage #4834

Closed
Speeddymon opened this issue Apr 11, 2024 · 5 comments

Comments

@Speeddymon
Copy link

Speeddymon commented Apr 11, 2024

Hello! I was drawn originally to #4823 and as I was writing a comment, a thought occurred to me that probably should be a separate issue, so here we go!

One of my colleagues prefers to build all of the images for a given project from a single dockerfile by specifying target stages to the buildkit command and tagging images using the targets, rather than having each image in its own Dockerfile. Other than that, we generally follow best practices; using scratch images for the target stages, discarding intermediate build stages and the files that aren't required to run the app in the container, not using root except for where absolutely required, etc.

In #4823 is being discussed some linting ideas and one of the ideas I had was to encourage the use of scratch images.

Given my colleague's preference, I quickly realized that it would be difficult to encourage the use of scratch images if there is no way to specify in the dockerfile that a given stage will be used as a target in buildkit. This also was an issue for us when we used conftest to lint Dockerfiles; because Dockerfile has no syntax to say "this stage is going to be tagged as an image" that is universally recognized, we had to come up with an internal solution of requiring dockerfiles to have a specific name for stages that would be specified in the target flag.

So in order to be able to accomplish a goal of encouraging the use of scratch images, we would require some sort of dockerfile frontend change to allow for a stage to be designated as a target. Thinking about it a bit more, I realized that this could also allow us simplify the build process a little by dropping the requirement to specify the target flag to buildkit for this way of building images, and potentially allow the tag flag to buildkit to not be required on the command line, since the tag would then be codified in the Dockerfile.

Please let me know your thoughts on this. I'm happy to have further discussions and open to suggestions for improvement of this idea.

@Speeddymon
Copy link
Author

Speeddymon commented Apr 11, 2024

Dumb idea for how this could (maybe) work - there are probably better ways than a whole new keyword:

FROM some_image AS stage-1
RUN ...

FROM some_other_image AS second-tag-stage-1
RUN ...

FROM scratch AS second-tag-stage-2
COPY ...

FROM scratch AS stage-2
COPY ...

TO image-repo-path:tag FROM stage-2

TO second-image-repo-path:tag FROM second-tag-stage-2

Essentially, the new TO keyword would be a hard stop for a given image. Where FROM allows you to specify a scratch image to start from and pull in whatever you need to create your image, currently Buildkit stops at the end of that stage and spits out an image based on the target flag. TO would be the way to both replace needing to specify a target stage, and would also make it so that the image names don't need to be specified on the command line. This makes the overall Dockerfile more clear by being able to tell where each stage will be published as a tag, and simplifies the buildkit command line.

Edit to clarify: TO could accept variables so that your image tag can still be passed in as a build arg, or you could make the image-repo-path a variable and pass that in as a build arg as well, so that if you wanted to cut out a different tag in a local dockerfile build that isn't going to be published anywhere, that can still be done.

Edit 2: NON-GOAL: deprecation/removal of the target flag or any other flag currently used. The target flag should be respected. I leave it to the community to decide whether TO or the target flag should have higher priority/override capability.

Edit 3: Some different variations I thought of:

  1. In the original description, TO is a global keyword; thus it can be put anywhere in the Dockerfile after the stage that it's going to tag FROM. In the first case below, however, the TO keyword is part of the stage itself, so it doesn't need a FROM extension; it just takes the stage where its defined, and packages it up as a tag; defining multiple TO at the end of the dockerfile would be an error unless both stage and global versions of the keyword were to be supported.

TO as a stage keyword rather than a global keyword:

FROM some_image AS stage-1
RUN ...

FROM some_other_image AS second-tag-stage-1
RUN ...

FROM scratch AS stage-2
COPY...
ENTRYPOINT [ ... ]
TO image-repo-path:tag

FROM scratch AS second-tag-stage-2
COPY ...
ENTRYPOINT [ ... ]
TO second-image-repo-path:tag
  1. Instead of creating TO as a new global or stage keyword, FROM could be extended to support the TO argument as it already supports the AS argument. This may potentially make more sense as TAG instead of TO, but I leave that to the community to decide. Since AS defines stage names, TO would define a tag that will be published from that stage when that stage has completed building.

Extend FROM to support TO as it does AS:

FROM some_image AS stage-1
RUN ...

FROM some_other_image AS second-tag-stage-1
RUN ...

FROM scratch AS stage-2
COPY...

FROM scratch AS second-tag-stage-2
COPY ...

FROM stage-2 TO image-repo-path:tag

FROM second-tag-stage-2 TO second-image-repo-path:tag

Or more concisely, the last 4 stages could be condensed to 2 as:

FROM scratch TO image-repo-path:tag
COPY...

FROM scratch TO second-image-repo-path:tag
COPY ...

@tonistiigi
Copy link
Member

So in order to be able to accomplish a goal of encouraging the use of scratch images, we would require some sort of dockerfile frontend change to allow for a stage to be designated as a target.

But that's what the --target flag is for.

@Speeddymon
Copy link
Author

Speeddymon commented Apr 13, 2024

So in order to be able to accomplish a goal of encouraging the use of scratch images, we would require some sort of dockerfile frontend change to allow for a stage to be designated as a target.

But that's what the --target flag is for.

If you're linting a dockerfile, how does the linter know which stages are going to be specified in the --target flag to buildkit, more specifically how does the linter know what flags will be passed during a build?

If --target will be supported on the linting command, something like buildkit lint ./Dockerfile --target=my-image then I suppose I can retract this proposal. It's not quite as tight of a relationship between the linter and the builder as I'd like to see, but flexibility is important too.

Colin reached out to me about build time policies after my comments on Slack, so it seems like that may be a coming feature and something I'd use to accomplish the same goal.

@tonistiigi
Copy link
Member

Linting is atm exposed with --print=lint in buildx (although UX is still considered experimental). It takes all the same flags a build command. Eg. --print=outline very much depends on the passed build-args.

@Speeddymon
Copy link
Author

Sounds great. Based on the discussion with Colin, I understand it's being integrated in the front end so that addresses all of my points. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants