Skip to content

Enable WCOW custom frontends to communicate with BuildKit via named pipe bridge #5868

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

billywr
Copy link
Contributor

@billywr billywr commented Mar 21, 2025

PR adds support for custom frontends in WCOW by enabling gRPC communication from the container child process to the BuildKit in host machine via a named pipe.

Currently, the custom frontend bridge uses stdio file descriptors (FDs), which work well for Linux container child processes and also work in Windows containers. However, they are not supported for communication between WCOW container 'child' processes and the host machine process. As a result, custom frontends running in Windows containers are unable to communicate with the host machine's BuildKit instance

This change:

  • Introduces a named pipe (\\.\pipe\buildkit-frontend-bridge) specifically for WCOW custom frontend containers.
  • Mounts the pipe into the container .

Fixes #4892

Guide to test

STEP 1.
Compile frontend binary
You can use go code in this location: https://github.com/moby/buildkit/blob/master/frontend/dockerfile/cmd/dockerfile-frontend/main.go
use go build -o ... to create dockerfile-frontend.exe

STEP 2.
Build frontend image and push it your docker hub
The dockerfile contents to build the frontend image:

FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
LABEL moby.buildkit.frontend.network.none="true"
LABEL moby.buildkit.frontend.caps="moby.buildkit.frontend.inputs,moby.buildkit.frontend.subrequests,moby.buildkit.frontend.contexts"

COPY dockerfile-frontend.exe C:\dockerfile-frontend.exe

ENTRYPOINT ["C:\\dockerfile-frontend.exe"]

Can use this buildctl command to build frontend image

buildctl build `
--frontend=dockerfile.v0 `
--local context="path to your build context" `
--local dockerfile="path to your dockefile" `
--output type=image,name=yourdockerhuburl/dockerfrontend:latest,push=true `
--no-cache 

STEP 3.
Use the frontend image in your docker builds

say you have a dockerfile below, notice the use of #syntax to make use of your custom dockerfile frontend

yourdockerhuburl/dockerfrontend:latest comes from the image you pushed in step 2

# syntax=yourdockerhuburl/dockerfrontend:latest

FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
RUN echo hello from WCOW custom frontend

@billywr billywr force-pushed the wcow-custom-frontend-npipe-bridge branch 25 times, most recently from 5804377 to 00a8147 Compare March 25, 2025 08:55
@billywr billywr force-pushed the wcow-custom-frontend-npipe-bridge branch from 00a8147 to 60d6695 Compare March 31, 2025 12:21
@billywr billywr force-pushed the wcow-custom-frontend-npipe-bridge branch 2 times, most recently from c49d3ce to 941a28b Compare April 22, 2025 10:34
@github-actions github-actions bot removed the area/dependencies Pull requests that update a dependency file label Apr 22, 2025
@billywr billywr force-pushed the wcow-custom-frontend-npipe-bridge branch 4 times, most recently from 47512e2 to 858e9ab Compare April 23, 2025 04:55
@billywr billywr force-pushed the wcow-custom-frontend-npipe-bridge branch 2 times, most recently from cb88c54 to d43314a Compare April 25, 2025 12:10
Copy link
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason stdio FDs would not work for wcow? It doesn't have any dependency on UNIX sockets, just that containers have stdin and stdout capability.

if v, ok := ctx.Value(appdefaults.ContextKeyCustomFrontend).(bool); ok && v {
sid := func() string {
for _, e := range meta.Env {
if strings.HasPrefix(e, "BUILDKIT_SESSION_ID=") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this local const

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also maybe BUILDKIT_FRONTEND_GRPC_PIPE_ID

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also maybe BUILDKIT_FRONTEND_GRPC_PIPE_ID

got little bit lost here , did you switching BUILDKIT_SESSION_ID with BUILDKIT_FRONTEND_GRPC_PIPE_ID or naming const BUILDKIT_FRONTEND_GRPC_PIPE_ID

@@ -514,11 +517,20 @@ func serveLLBBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLB
pb.RegisterLLBBridgeServer(server, lbf)

go func() {
if isWindowsPlatform {
listener = createNPipeListener(sid)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if sessionID should be used for this? SessionID can be controlled by the user, but this one looks like something that should be internal (like a private property on llbBridge maybe)

Copy link
Contributor Author

@billywr billywr May 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using the session ID as a suffix to the named pipe to ensure uniqueness and avoid collisions during parallel builds. I’m not sure the idea of a private property on llbBridge guarantees uniqueness unless it generates something like a UUID per build, and it would still need to be passed as an environment variable for the container process to access. Open to your suggestions on a better approach.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I meant still some unique ID but something that can't be controlled from the client side and is purely internal in the llbBridge.

@billywr billywr force-pushed the wcow-custom-frontend-npipe-bridge branch 2 times, most recently from 489fecc to 1bf2b7f Compare May 7, 2025 08:11
@billywr
Copy link
Contributor Author

billywr commented May 8, 2025

What's the reason stdio FDs would not work for wcow? It doesn't have any dependency on UNIX sockets, just that containers have stdin and stdout capability.

I encountered the error Unavailable: connection error: desc = transport: failed to write client preface: write /dev/stdout: file already closed before configuring a named pipe. Notably, /dev/stdout is not a valid stdio path in Windows, where STDIO (STDIN, STDOUT, STDERR) is managed as handles rather than files. While /dev/stdout could be an abstraction for the STDOUT handle in WCOW, no failures were expected. However, the error persisted, and there is no direct evidence suggesting a premature exit of the frontend executable (dockerfile-frontend.exe) caused it.

The successful resolution with a named pipe without modifying the frontend code strongly indicates that STDIO is unreliable in WCOW buildkit custom frontend scenarios[I have to get better reasons why that is the case, but one possibility could be the container child process might not have inherited the STDOUT handle].

stdout stdin & stderror work just fine like in the case where WCOW container is launched in interactive mode.

@tonistiigi
Copy link
Member

Notably, /dev/stdout is not a valid stdio path in

Why would this matter? Where do we open /dev/stdout as path from filesystem. Are you saying os.Stdout does not work on windows? Do you have a trace for that error?

@billywr
Copy link
Contributor Author

billywr commented May 14, 2025

Notably, /dev/stdout is not a valid stdio path in

Why would this matter? Where do we open /dev/stdout as path from filesystem. Are you saying os.Stdout does not work on windows? Do you have a trace for that error?

1. main.main
   buildkit/frontend/dockerfile/cmd/dockerfile-frontend/main.go:30
   │
   └──> 2. github.com/moby/buildkit/frontend/gateway/grpcclient.RunFromEnvironment
        buildkit/frontend/gateway/grpcclient/client.go:100
        │
        └──> 3. github.com/moby/buildkit/frontend/gateway/grpcclient.New
             buildkit/frontend/gateway/grpcclient/client.go:49
             │
             └──> 4.github.com/moby/buildkit/frontend/gateway/pb.(*LLBBridgeClient).Ping
                  buildkit/frontend/gateway/pb/gateway_grpc.pb.go:148
                  │
                  └──> 5. google.golang.org/grpc.(*ClientConn).Invoke
                       buildkit/vendor/google.golang.org/grpc/call.go:35
                       │
                       └──> Error: "transport: failed to write client preface: write /dev/stdout: file already closed
                       

@billywr billywr marked this pull request as draft May 14, 2025 09:28
@billywr billywr force-pushed the wcow-custom-frontend-npipe-bridge branch from 1bf2b7f to 0f5b443 Compare June 16, 2025 06:36
@billywr billywr force-pushed the wcow-custom-frontend-npipe-bridge branch 6 times, most recently from 2b33214 to 5eafea6 Compare June 16, 2025 08:37
Signed-off-by: Billy Owire <billyowire95@gmail.com>
@billywr billywr force-pushed the wcow-custom-frontend-npipe-bridge branch from 5eafea6 to 21b4850 Compare June 16, 2025 08:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

WCOW fails to load custom frontend
4 participants