I'm trying to setup an easy docker container with flutter installed in it. One of the commands I'm using in a RUN
statement is flutter precache
which somehow fails with the following error:
flutter precache -v
Downloading Linux arm64 Dart SDK from Flutter engine ada363ee93b17cfe31587b5102679885cb40837e...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 139M 100 139M 0 0 10.6M 0 0:00:13 0:00:13 --:--:-- 10.8M
Building flutter tool...
[ +7 ms] Unable to locate an Android SDK.
[ +2 ms] executing: uname -m
[ +2 ms] Exit code 0 from: uname -m
[ ] aarch64
[ ] executing: [/flutter/] git -c log.showSignature=false log -n 1 --pretty=format:%H
[ +3 ms] Exit code 0 from: git -c log.showSignature=false log -n 1 --pretty=format:%H
[ ] 12cb4eb7a009f52b347b62ade7cb4854b926af72
[ ] executing: [/flutter/] git tag --points-at 12cb4eb7a009f52b347b62ade7cb4854b926af72
[ +15 ms] Exit code 0 from: git tag --points-at 12cb4eb7a009f52b347b62ade7cb4854b926af72
[ ] 3.7.6
[ +4 ms] executing: [/flutter/] git rev-parse --abbrev-ref --symbolic @{upstream}
[ +3 ms] Exit code 0 from: git rev-parse --abbrev-ref --symbolic @{upstream}
[ ] origin/stable
[ ] executing: [/flutter/] git ls-remote --get-url origin
[ +3 ms] Exit code 0 from: git ls-remote --get-url origin
[ ] https://github.com/flutter/flutter.git
[ +10 ms] executing: [/flutter/] git rev-parse --abbrev-ref HEAD
[ +3 ms] Exit code 0 from: git rev-parse --abbrev-ref HEAD
[ ] stable
[ +25 ms] Downloading Material fonts...
[ +32 ms] HandshakeException: Connection terminated during handshake
[ +10 ms] Downloading Material fonts... (completed in 32ms)
[ ] Downloading Material fonts...
[ +18 ms] HandshakeException: Connection terminated during handshake
[ ] Downloading Material fonts... (completed in 18ms)
[ ] "flutter precache" took 93ms.
[ +3 ms] Failed to download https://storage.googleapis.com/flutter_infra_release/flutter/fonts/3012db47f3130e62f7cc0beabff968a33cbec8d8/fonts.zip. Ensure you
have
network connectivity and then try again.
HandshakeException: Connection terminated during handshake
[ +1 ms]
#0 throwToolExit (package:flutter_tools/src/base/common.dart:10:3)
#1 ArtifactUpdater._downloadArchive (package:flutter_tools/src/cache.dart:1062:11)
<asynchronous suspension>
#2 CachedArtifact.update (package:flutter_tools/src/cache.dart:810:5)
<asynchronous suspension>
#3 Cache.updateAll (package:flutter_tools/src/cache.dart:677:9)
<asynchronous suspension>
#4 PrecacheCommand.runCommand (package:flutter_tools/src/commands/precache.dart:168:7)
<asynchronous suspension>
#5 FlutterCommand.run.<anonymous closure> (package:flutter_tools/src/runner/flutter_command.dart:1257:27)
<asynchronous suspension>
#6 AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
<asynchronous suspension>
#7 CommandRunner.runCommand (package:args/command_runner.dart:209:13)
<asynchronous suspension>
#8 FlutterCommandRunner.runCommand.<anonymous closure> (package:flutter_tools/src/runner/flutter_command_runner.dart:283:9)
<asynchronous suspension>
#9 AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
<asynchronous suspension>
#10 FlutterCommandRunner.runCommand (package:flutter_tools/src/runner/flutter_command_runner.dart:229:5)
<asynchronous suspension>
#11 run.<anonymous closure>.<anonymous closure> (package:flutter_tools/runner.dart:64:9)
<asynchronous suspension>
#12 AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
<asynchronous suspension>
#13 main (package:flutter_tools/executable.dart:91:3)
<asynchronous suspension>
[ +1 ms] ensureAnalyticsSent: 0ms
[ ] Running 0 shutdown hooks
[ ] Shutdown hooks complete
[ ] exiting with code 1
Failed to download https://storage.googleapis.com/flutter_infra_release/flutter/fonts/3012db47f3130e62f7cc0beabff968a33cbec8d8/fonts.zip. Ensure you have
network connectivity and then try again.
HandshakeException: Connection terminated during handshake
Executing flutter precache
a second time in an interactive container (shell attached) works instantly. Any idea how to prevent/fix this error?
I already tried running the docker build
with --network=host
or adding things like update-ca-certificates
inside the container but nothing works. Also ignoring the error and adding a second RUN flutter precache
in the dockerfile doesn't work (ignoring by using RUN flutter precache; exit 0
as the first RUN
)
How could I fix this?
PS: The Dockerfile I'm currently using to build a flutter build environment on MacBook Pro M1 looks like this:
# Example Usage:
# Build the image with:
# docker build --pull --rm -f 'Dockerfile' -t flutter_build:latest .
# --pull tries to always pull new images
# --rm delete intermediate generated containers after build
# -f specifies the file to build the image
# -t sets a label_of_container:with_version
#
# Run it with:
# docker run -p 8080:80 flutter_build
# The -p exposes the internal port 80 to the
# external/public port 8080 -> visit http://localhost:8080
ARG WORKDIR=/app
# build the flutter web app
FROM ubuntu:latest AS FLUTTER_BUILDER
ARG WORKDIR
ARG USER_NAME=flutter
ARG REPO_NAME=flutter
ARG BRANCH_NAME=stable
RUN apt update -y
RUN apt install -y bash curl file git unzip zip xz-utils libglu1-mesa
# using ADD here to bypass caching of this docker layer whenever a new flutter version was released to stable
ADD https://api.github.com/repos/${USER_NAME}/${REPO_NAME}/git/refs/heads/${BRANCH_NAME} ${REPO_NAME}_${BRANCH_NAME}_version.json
RUN git clone https://github.com/${USER_NAME}/${REPO_NAME}.git -b ${BRANCH_NAME}
ENV PATH="$PATH:/flutter/bin"
RUN flutter precache
COPY . ${WORKDIR}
WORKDIR ${WORKDIR}
RUN flutter clean
RUN flutter pub get
RUN flutter build web
# serve the flutter web app
FROM nginx:latest
ARG WORKDIR
EXPOSE 80
COPY --from=FLUTTER_BUILDER ${WORKDIR}/build/web /usr/share/nginx/html
I just had to add an increased timeout to the http requests inside the docker container. Seems like sometimes the Google CDN servers needs some time to respond or the corporate network blocks them completely.
I did this by adding ENV HTTP_TIMEOUT=5000
in the Dockerfile somewhere before the RUN flutter precache
.
When it's still failing I just had to use a VPN to get out of the corporate network and it worked again.
With that change the Flutter build environment inside of the docker container works perfectly as expected even for Mac M1 and M2 (M series in general I guess) and also for Linux ARM platforms. Therefore this can also be considered as a workaround for the cirrusci/flutter
docker container which throws errors like:
===== CRASH =====
si_signo=Segmentation fault(11), si_code=1, si_addr=0x7
version=2.18.0 (stable) (Fri Aug 26 10:22:54 2022 +0000) on "linux_x64"
pid=173, thread=217, isolate_group=main(0x400305f000), isolate=main(0x40030d6000)
isolate_instructions=4001ece300, vm_instructions=4001ece300
pc 0x0000ffffa600472b fp 0x00000040103d51f0 Unknown symbol
...
...
...
pc 0x00000040020999af fp 0x00000040103d5c60 dart::MessageHandler::TaskCallback()+0x1df
pc 0x00000040021bc868 fp 0x00000040103d5ce0 dart::ThreadPool::WorkerLoop(dart::ThreadPool::Worker*)+0x148
pc 0x00000040021bccbd fp 0x00000040103d5d10 dart::ThreadPool::Worker::Main(unsigned long)+0x6d
pc 0x000000400212f488 fp 0x00000040103d5dd0 /sdks/flutter/bin/cache/dart-sdk/bin/dart+0x212f488
-- End of DumpStackTrace
qemu: uncaught target signal 6 (Aborted) - core dumped
Aborted
on most ARM architectures. As referenced in this stackoverflow post: flutter pub get fails in docker
The finally used Dockerfile to serve a flutter web app compiled and served from docker looks like this (don't forget to add CORS headers to your backend API server when serving the API from a different server or even port - these must be included especially in the preflight responses!):
# setting workdir globally (still needs to be referenced in each stage!)
ARG WORKDIR=/app
# build the flutter web app
#
# in the first stage just prepare a ubuntu container
# and install the flutter tooling to build flutter apps
FROM ubuntu:latest AS FLUTTER_BUILDER
# setting the workdir
ARG WORKDIR
# setting the github repo information from
# which the flutter sdk will be cloned.
ARG USER_NAME=flutter
ARG REPO_NAME=flutter
ARG BRANCH_NAME=stable
# preparing the ubuntu container with all necessary tools
RUN apt update -y
RUN apt install -y bash curl file git unzip zip xz-utils libglu1-mesa
# sometimes the Google servers respond a little slowly
# which resulted in a failed handshake
# increasing the timeout solves this error!
ENV HTTP_TIMEOUT=5000
# here we use ADD to invalidad cache of the docker stages below when there is a new commit/version in the
# given flutter github repo on the given branch - watch out: depending on github api here!
ADD https://api.github.com/repos/${USER_NAME}/${REPO_NAME}/git/refs/heads/${BRANCH_NAME} flutter_version.json
RUN git clone https://github.com/${USER_NAME}/${REPO_NAME}.git -b ${BRANCH_NAME}
# now set the path to flutter and build flutter tool and precache all necessary tools this is good
# because as long as ADD doesn't invalidad these stages we don't have to rebuild this expensive part
ENV PATH="$PATH:/flutter/bin"
RUN flutter precache
# copy the flutter project and change dir into it
COPY . ${WORKDIR}
WORKDIR ${WORKDIR}
# run flutter clean, pub get and then build for web
RUN flutter clean
RUN flutter pub get
RUN flutter build web
# Serve the flutter web app
#
# in the second stage just copy the builded web files from the first
# stage and serve them as static website using basic nginx container
FROM nginx:latest
# setting the workdir
ARG WORKDIR
# tell which port to use (no necessarily needed)
EXPOSE 80
# copy the built flutter static website to the nignx directory
COPY --from=FLUTTER_BUILDER ${WORKDIR}/build/web /usr/share/nginx/html