testcontainers-java icon indicating copy to clipboard operation
testcontainers-java copied to clipboard

Make it possible to use build docker image using BuildKit

Open pawelebe opened this issue 5 years ago • 13 comments

As per Docker release 18.09 it is possible to mount cache (using BuildKit) explained

Example dockerfile

# syntax = docker/dockerfile:1.0-experimental

#RUN --mount=type=cache,target=/home/gradle/.gradle gradle build

However when build such image, it is gives an error

Dockerfile parse error line 12: Unknown flag: mount

Code to build:

new GenericContainer<>(
                new ImageFromDockerfile()
                                .withDockerfile(Path.of("Dockerfile").toAbsolutePath())
        )

I order to build such docker image from CLI, you need to set DOCKER_BUILDKIT=1 in order to use buildkit.

While analyzing docker cli source code it seems like it is appending such query parameter to REST API call version=2

  • Prepare build parameters https://github.com/docker/cli/blob/master/cli/command/image/build_buildkit.go#L207
  • sending request via client https://github.com/docker/cli/blob/b350e14b1f83e1870a234ff2c554450707218204/vendor/github.com/docker/docker/client/image_build.go#L136

Unfortunately, I do not see any documented version parameters in the official API documentation. https://docs.docker.com/engine/api/v1.40/#operation/ImageBuild

pawelebe avatar Jun 10 '20 10:06 pawelebe

AFAIK BuildKit isn't just a Docker API call, but a whole new API.

Given that even Docker's official Python library (that is used in Docker Compose) have decided that it is easier to delegate it to an external binary, I am not sure there is much we can do here, unless we do the same (call docker build which will require Docker CLI).

/cc @rnorth

bsideup avatar Jun 10 '20 10:06 bsideup

@bsideup thanks for super fast reply... so given that it seems like not so easy to implement, do you have any workaround to recommend?

My first idea is to get a dockerfile as string, do something like sed /--mount=type=cache.*// (remove all buildkit related stuff) and then pass it to testcontainers

pawelebe avatar Jun 10 '20 12:06 pawelebe

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you believe this is a mistake, please reply to this comment to keep it open. If there isn't one already, a PR to fix or at least reproduce the problem in a test case will always help us get back on track to tackle this.

stale[bot] avatar Sep 11 '20 02:09 stale[bot]

This issue has been automatically closed due to inactivity. We apologise if this is still an active problem for you, and would ask you to re-open the issue if this is the case.

stale[bot] avatar Sep 27 '20 08:09 stale[bot]

I'm interested in this, @pawelebe did you find a workaround?

cesdperez avatar Jun 08 '21 07:06 cesdperez

Let's reopen - we shouldn't have let this get stale, so I apologise.

I think that buildkit is something we'll have to keep in mind, particularly if we want Testcontainers to retain the ability to build Dockerfiles while more and more buildkit features become relied upon.

That said, I can't see any immediate way to do it without the docker binary. This may be something we deem to be OK enough.

For now as a workaround I'd suggest executing docker build -t SOMENAME . (ideally from Java code, but perhaps from outside) and then pass the tagged image name to Testcontainers to run.

rnorth avatar Jun 08 '21 08:06 rnorth

It's a bit janky but I used the workaround specified by @morth https://github.com/trajano/spring-cloud-demo/blob/78ad12e7db4198fbedd94d2b2a4e87f5ae5ff187/gateway/src/test/java/net/trajano/swarm/gateway/ContainerTests.java#L55-L61 perhaps people can use it as an example.

UPDATE: corrected the line number as @sharongur pointed out

The lines were supposed to show how I started a docker compose build inside the container similar to @morth idea but can do multiple containers at once

trajano avatar Sep 29 '22 06:09 trajano

is there any update regarding this issue?

i've upgraded my projects dockerfiles to use buildkit buildx and now all my testcontainer arent working anymore...

i didnt quite understand how the demo you linked is of any help @trajano, would you mind elaborating ?

sharongur avatar Mar 30 '23 10:03 sharongur

@trajano so you are using the local machines docker-compose to build the image instead of the testcontainers dockerfile infrastructure and then just use the created image with testcontainers?

sharongur avatar Mar 30 '23 12:03 sharongur

That's correct, that way you take advantage of everything buildx has to offer including cache mounts and secrets.

trajano avatar Mar 30 '23 12:03 trajano

With testcontainers facing:

13:34:21.960 DEBUG [docker-java-stream--505742923] t.localhost/testcontainers/36kg4mfjztahmewr  ---> 2e32511ef14e
13:34:21.960 DEBUG [docker-java-stream--505742923] t.localhost/testcontainers/36kg4mfjztahmewr Step 8/17 : COPY --chmod=ugo+x entrypoint.sh /entrypoint.sh
13:34:21.960 DEBUG [docker-java-stream--505742923] t.localhost/testcontainers/36kg4mfjztahmewr 
13:34:21.960 ERROR [docker-java-stream--505742923] t.localhost/testcontainers/36kg4mfjztahmewr the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled

while using ImageFromDockerfile.

NOTE: BuildKit is default since Docker 23.

pekuz avatar Feb 20 '24 12:02 pekuz

Unfortunately, I do not see any documented version parameters in the official API documentation. https://docs.docker.com/engine/api/v1.40/#operation/ImageBuild

There's now a documented version parameter (since February 2024). Value 2 enables BuildKit. See https://github.com/moby/moby/pull/47259.

qerub avatar Jun 11 '24 09:06 qerub

@qerub It still requires a significant client-side implementation though.

kiview avatar Aug 19 '24 12:08 kiview

I bumped into this as well today. However, in my specific case I was able to set up a workaround using gradle:

build.gradle.kts

tasks.register("buildDockerImage", type = Exec::class) {
    // Point this to your build directory
    workingDir(project.rootDir.resolve(".."))
    commandLine("docker", "build", "-t", "dansdata/database:test", ".")
}

tasks.named("test").get().dependsOn(tasks.named("buildDockerImage"))

DansdataDbContainer.kt

class DansdataDbContainer private constructor() :
    JdbcDatabaseContainer<DansdataDbContainer>(DockerImageName.parse("dansdata/database:test")) {

FelixZY avatar Nov 23 '24 14:11 FelixZY