keeganwitt / docker-gradle

Docker images with Gradle
https://hub.docker.com/_/gradle/
Apache License 2.0
145 stars 73 forks source link

Gradle daemon incredibly slow to start on macOS #215

Open MidKnightXI opened 2 years ago

MidKnightXI commented 2 years ago

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)

marcmesh commented 2 years ago

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

MidKnightXI commented 2 years ago

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 commented 2 years ago

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
keeganwitt commented 2 years ago

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

MidKnightXI commented 2 years ago

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.

keeganwitt commented 2 years ago

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.

danon commented 2 years ago

It's also slow on Windows.

Same for WSL on Windows.

MidKnightXI commented 2 years ago

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

keeganwitt commented 2 years ago

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/.

MidKnightXI commented 2 years ago

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

keeganwitt commented 2 years ago

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 commented 2 years ago

@Danon is your use case also regarding Android SDK?

danon commented 2 years ago

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

keeganwitt commented 2 years ago

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?

danon commented 2 years ago

@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.

keeganwitt commented 2 years ago

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.