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

Ability to run Earthly on an M1 computer that uses a remote x86 Docker #1895

Closed
vladaionescu opened this issue May 24, 2022 · 5 comments · Fixed by #2191
Closed

Ability to run Earthly on an M1 computer that uses a remote x86 Docker #1895

vladaionescu opened this issue May 24, 2022 · 5 comments · Fixed by #2191
Assignees
Labels
type:enhancement Small feature requests / adjustments

Comments

@vladaionescu
Copy link
Member

Setup

  • User has an M1 laptop
  • User uses a remote Docker daemon, which is running on an x86

Problem

User wants to run Earthly locally, but as Earthly attempts to start the buildkit daemon in Docker, it pulls the arm64 version of the image, instead of the x86 version of it. This causes a crash as buildkit cannot be emulated.

What should happen instead

  • Earthly should detect that the Docker daemon is running on x86 (which may be different than the current host's architecture) and thus use an x86 version of the Earthly image.
  • Earthly builds run on this kind of setup should default to use platform x86, when no platform is specified (it's possible that the new platform logic behind a feature flag already does this)
@vladaionescu vladaionescu added the type:enhancement Small feature requests / adjustments label May 24, 2022
@vladaionescu
Copy link
Member Author

Possible workaround

@dchw
Copy link
Collaborator

dchw commented May 24, 2022

We actually already expose this information, too! Its in this structure:

type FrontendInfo struct {
ClientVersion string
ClientAPIVersion string
ClientPlatform string
ServerVersion string
ServerAPIVersion string
ServerPlatform string
ServerAddress string
}

And you get it (regardless of frontend) with this method on a given frontend:

Information(ctx context.Context) (*FrontendInfo, error)

Implementations:

  • Docker:
    func (dsf *dockerShellFrontend) Information(ctx context.Context) (*FrontendInfo, error) {
    output, err := dsf.commandContextOutput(ctx, "version", "--format={{json .}}")
    if err != nil {
    return nil, err
    }
    type versionInfo struct {
    Version string
    APIVersion string
    OS string
    Arch string
    }
    type info struct {
    Client versionInfo
    Server versionInfo
    }
    allInfo := info{}
    json.Unmarshal([]byte(output.string()), &allInfo)
    host, exists := os.LookupEnv("DOCKER_HOST")
    if !exists {
    host = "/var/run/docker.sock"
    }
    return &FrontendInfo{
    ClientVersion: allInfo.Client.Version,
    ClientAPIVersion: allInfo.Client.APIVersion,
    ClientPlatform: fmt.Sprintf("%s/%s", allInfo.Client.OS, allInfo.Client.Arch),
    ServerVersion: allInfo.Server.Version,
    ServerAPIVersion: allInfo.Server.APIVersion,
    ServerPlatform: fmt.Sprintf("%s/%s", allInfo.Server.OS, allInfo.Server.Arch),
    ServerAddress: host,
    }, nil
    }
  • Podman:
    func (psf *podmanShellFrontend) Information(ctx context.Context) (*FrontendInfo, error) {
    output, err := psf.commandContextOutput(ctx, "info", "--format={{.Host.RemoteSocket.Exists}}")
    if err != nil {
    return nil, err
    }
    hasRemote, err := strconv.ParseBool(output.string())
    if err != nil {
    return nil, errors.Wrapf(err, "info returned invalid value %s", output.string())
    }
    args := []string{"version", "--format=json"}
    if hasRemote {
    args = append([]string{"-r"}, args...)
    }
    output, err = psf.commandContextOutput(ctx, args...)
    if err != nil {
    return nil, err
    }
    type versionInfo struct {
    Version string
    APIVersion string
    OSArch string
    }
    type info struct {
    Client versionInfo
    Server versionInfo
    }
    allInfo := info{}
    json.Unmarshal([]byte(output.string()), &allInfo)
    host := "daemonless"
    if hasRemote {
    output, err = psf.commandContextOutput(ctx, "info", "--format={{.Host.RemoteSocket.Path}}")
    if err != nil {
    return nil, err
    }
    host = string(output.string())
    }
    return &FrontendInfo{
    ClientVersion: allInfo.Client.Version,
    ClientAPIVersion: allInfo.Client.APIVersion,
    ClientPlatform: allInfo.Client.OSArch,
    ServerVersion: allInfo.Server.Version,
    ServerAPIVersion: allInfo.Server.APIVersion,
    ServerPlatform: allInfo.Server.OSArch,
    ServerAddress: host,
    }, nil
    }

@vladaionescu
Copy link
Member Author

vladaionescu commented May 24, 2022

Oh great - that's really useful! I'll play with this this weekend. EDIT: Didn't have time.

@vladaionescu
Copy link
Member Author

Note that interactive mode might not work immediately in this setup and more tinkering may be necessary. Also, we should test that local registry-based outputs still work fine in this case.

@vladaionescu vladaionescu changed the title Ability to run Earthly on an M1 computer that uses are remote Docker Ability to run Earthly on an M1 computer that uses a remote Docker May 24, 2022
@vladaionescu vladaionescu changed the title Ability to run Earthly on an M1 computer that uses a remote Docker Ability to run Earthly on an M1 computer that uses a remote x86 Docker May 24, 2022
@r-LaForge
Copy link
Contributor

r-LaForge commented Sep 15, 2022

Digging into the problem more I believe @alexcb is correct and we need to use that frontend information.
We are using an incorrect assumption when running the buildkit image.

  • We assume the frontend host platform is the same as the current machine's platform.
  • We assume the message will contain "Unable to find image" if and only if it supports the frontend.

The below shows the second assumption is not true either.

$ docker run --rm --platform fake/arch scratch
Unable to find image 'scratch:latest' locally
docker: Error response from daemon: 'scratch' is a reserved name.
See 'docker run --help'.

@r-LaForge r-LaForge self-assigned this Sep 16, 2022
r-LaForge added a commit that referenced this issue Sep 17, 2022
fixes #1895

Running Earthly from an M1 mac to a remote docker host running x86 was
broken as described in the issue.
We need to ensure an appropriate image is pulled that is. compatible
with the architecture the docker host itself.

The solution is to use the frontend information we collect via the
`Information` call to determine if the current client's platform also
works on the server.

If it is supported, this acts as a sort of preference to the client's
platform.
If not, it will default to the server platform to allow the buildkitd
container to 

Co-authored-by: Vlad A. Ionescu <446771+vladaionescu@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:enhancement Small feature requests / adjustments
Projects
None yet
3 participants