docker-gradle icon indicating copy to clipboard operation
docker-gradle copied to clipboard

Gradle daemon incredibly slow to start on macOS

Open MidKnightXI opened this issue 2 years ago • 16 comments

So I created my own image based on the official Gradle one to build React Native app from containers and I'm using the ./gradlew assembleRelease in the /android folder but the daemon is taking more then 2500sec to start (see below).

image

The problem is that when I'm on WSL the build is working just fine (less than 700sec) and I just don't understand why it does not work on macOS (I've an M1 chip)

MidKnightXI avatar Mar 28 '22 08:03 MidKnightXI

If you are on Docker Desktop, consider https://github.com/docker/roadmap/issues/7 In a nutshell: Use latest macOS Monterey 12.3, latest Docker Desktop for Apple silicon, and activate Experimental feature

marcmesh avatar Mar 30 '22 15:03 marcmesh

If you are on Docker Desktop, consider docker/roadmap#7 In a nutshell: Use latest macOS Monterey 12.3, latest Docker Desktop for Apple silicon, and activate Experimental feature

Excuse me for not giving you that detail but I'm on the latest macOS version with experimental features.

I tried with and without and it's building at that exact same speed

MidKnightXI avatar Mar 31 '22 08:03 MidKnightXI

Okay so I published on GitHub the Dockerfile I'm talking about.

I built the image on my Mac and pushed it to the Docker Hub(The 2.1-area was amd64 only which was decreasing perf on my machine), everything looks fine but when I'm trying to build a container using it, the Gradle daemon is starting way faster 70sec ~ (which is normal) but failed to install build-tools;30.0.2 and even if I install it in the base image, the build is failing because of this error.

Full error:

#7 82.88 License for package Android SDK Platform 30 accepted.
#7 82.88 Preparing "Install Android SDK Platform 30 (revision: 3)".
#7 89.88 "Install Android SDK Platform 30 (revision: 3)" ready.
#7 89.88 Installing Android SDK Platform 30 in /sdk/platforms/android-30
#7 89.88 "Install Android SDK Platform 30 (revision: 3)" complete.
#7 89.88 "Install Android SDK Platform 30 (revision: 3)" finished.
#7 89.98 
#7 89.98 FAILURE: Build failed with an exception.
#7 89.98 
#7 89.98 * What went wrong:
#7 89.98 Could not determine the dependencies of task ':app:compileReleaseJavaWithJavac'.
#7 89.98 > Failed to install the following SDK components:
#7 89.98       build-tools;30.0.2 Android SDK Build-Tools 30.0.2
#7 89.98   Install the missing components using the SDK manager in Android Studio.
#7 89.98 
#7 89.98 
#7 89.98 * Try:
#7 89.98 Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
#7 89.98 
#7 89.98 * Get more help at https://help.gradle.org
#7 89.98 
#7 89.98 Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.
#7 89.98 
#7 89.98 You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
#7 89.98 
#7 89.98 See https://docs.gradle.org/7.2/userguide/command_line_interface.html#sec:command_line_warnings
#7 89.98 
#7 89.98 BUILD FAILED in 1m 9sUnable to list file systems to check whether they can be watched. The whole state of the virtual file system has been discarded. Reason: Could not query file systems: could not open mount file (errno 2: No such file or directory)
#7 89.98

MidKnightXI avatar Mar 31 '22 09:03 MidKnightXI

You'd have to install the Android SDK yourself. This image is just Gradle, not anything else.

keeganwitt avatar Apr 01 '22 01:04 keeganwitt

You'd have to install the Android SDK yourself. This image is just Gradle, not anything else.

That's actually what I'm doing.

ENV SDK_URL="https://dl.google.com/android/repository/commandlinetools-linux-8092744_latest.zip" \
    ANDROID_HOME="/sdk" \
    PATH="$PATH:${ANDROID_HOME}/cmdline-tools/bin" \

...

RUN curl -s ${SDK_URL} > /tools.zip \
    && unzip /tools.zip -d ${ANDROID_HOME} \
    && rm /tools.zip

ADD packages.txt /sdk

RUN mkdir -p /root/.android \
    && touch /root/.android/repositories.cfg \
    && ${ANDROID_HOME}/cmdline-tools/bin/sdkmanager --sdk_root=${ANDROID_HOME} --update \
    && yes | ${ANDROID_HOME}/cmdline-tools/bin/sdkmanager --sdk_root=${ANDROID_HOME} --licenses

RUN while read -r package; do PACKAGES="${PACKAGES}${package} "; done < /sdk/packages.txt \
    && ${ANDROID_HOME}/cmdline-tools/bin/sdkmanager --sdk_root=${ANDROID_HOME} ${PACKAGES}

package.txt:

platform-tools
extras;google;market_licensing

The thing is that the build is working on amd64 with the gradle daemon but crashing on M1 because of the above Error.

MidKnightXI avatar Apr 01 '22 08:04 MidKnightXI

I don't have a device with Apple silicon to test on and haven't confirmed the state of Android SDK support on that hardware (though it was my understanding it was supposed to work). One potential workaround would be to use install Rosetta 2 and use --platform linux/amd64.

keeganwitt avatar Apr 12 '22 05:04 keeganwitt

It's also slow on Windows.

Same for WSL on Windows.

danon avatar Apr 12 '22 13:04 danon

I don't have a device with Apple silicon to test on and haven't confirmed the state of Android SDK support on that hardware (though it was my understanding it was supposed to work). One potential workaround would be to use install Rosetta 2 and use --platform linux/amd64.

I don't really understand what you mean by using Rosetta, the SDK is already based on amd64 arch:

ENV SDK_URL="https://dl.google.com/android/repository/commandlinetools-linux-8092744_latest.zip"

I also tried with the macOS install but still the same prob

MidKnightXI avatar Apr 12 '22 18:04 MidKnightXI

I'm talking about the argument to docker run, not the contents of the Dockerfile. The --platform argument allows you to run amd64 based images under emulation. See the documentation here: https://docs.docker.com/desktop/mac/apple-silicon/.

keeganwitt avatar Apr 13 '22 02:04 keeganwitt

I'm talking about the argument to docker run, not the contents of the Dockerfile. The --platform argument allows you to run amd64 based images under emulation. See the documentation here: https://docs.docker.com/desktop/mac/apple-silicon/.

If I do that, my image would be incredibly slow: cf the first screenshot of this issue

MidKnightXI avatar Apr 16 '22 15:04 MidKnightXI

That's unsurprising, but my understanding is that there's Arm binaries for MacOS, but not for Linux, and hence not any for the Docker image that would be used on that hardware. I'd seen a couple references to compiling the build tools to target Arm, though I haven't found any recent documentation of that.

keeganwitt avatar Apr 17 '22 18:04 keeganwitt

@Danon is your use case also regarding Android SDK?

keeganwitt avatar Apr 17 '22 18:04 keeganwitt

No, it's also for a vanilla hello world java gradle.

danon avatar Apr 17 '22 22:04 danon

No, it's also for a vanilla hello world java gradle.

In your case is the daemon slow to start also?There are a lot of things that can impact performance. Was the project in a volume? If so, what was the storage driver? Do you get the same poor performance after Gradle has run once and stuff is cached? If you enable verbose logging, does that give any indication what's taking time?

keeganwitt avatar Apr 17 '22 23:04 keeganwitt

@keeganwitt This is a repo you can use to demonstrate it. https://github.com/Danon/Fish When I clone it and run it on gradle:latest the deamon starts for about 60-90 seconds.

danon avatar Apr 18 '22 09:04 danon

Documenting some research (using your Fish project as a test):

Measure-Command { docker run --rm -v "${pwd}:/home/gradle/project" -w /home/gradle/project gradle:latest gradle --no-daemon --profile tasks | Out-Default }
Remove-Item build -Recurse
Remove-Item .gradle -Recurse
Measure-Command { gradle --no-daemon --profile tasks | Out-Default }
Remove-Item build -Recurse
Remove-Item .gradle -Recurse

On my machine local is about 10 seconds compared to about a minute in Docker.

To rule in/out the volume as being a performance impact, I created

FROM gradle
RUN mkdir /home/gradle/project
WORKDIR /home/gradle/project
COPY build.gradle.kts .
COPY src/ ./src

Then ran

docker build -t gradle-time .
docker run --rm gradle-time gradle tasks
Measure-Command { docker run --rm gradle-time bash -c 'gradle --no-daemon --profile tasks; cat build/reports/profile/*.html' | Out-Default }

This didn't have any significant performance difference compared to using the volume.

Most of the time spent was in the configuration step, but looking at the output from

Measure-Command { docker run --rm -v "${pwd}:/home/gradle/project" -w /home/gradle/project gradle:latest gradle --no-daemon -d tasks | Tee-Object -FilePath docker.out } | Tee-Object -FilePath docker.out -Append

It wasn't clear that any particular log step took the majority of the time.

My current guess is filesystem performance. Although I'm not sure if that's from the storage driver, the VM, or what.

keeganwitt avatar Apr 19 '22 21:04 keeganwitt