My Apple M1 MacBook: lots of cores, memory and crashing containers
Recently, I was excited to buy an Apple M1 MacBook: lots of cores and memory. I couldn’t wait to start using it for development so I tried running the Eventuate Tram Customers and Orders example application, which illustrates how to use the Sagas and CQRS patterns to implement operations that span multiple services. Sadly, running this application on my shiny new MacBook wasn’t straightforward due to problems running Docker containers.
This is the first of series of posts that describe what I needed to do to run the application successfully. The other articles in this series are:
- Part 2 - Building multi-architecture Docker images for Intel and ARM
- Part 3 - Configuring a CircleCI-based pipeline to build multi-architecture Docker images
- Part 4 - Testing an Intel and Arm multi-architecture Docker image on CircleCI
- Part 5 - Configuring CircleCI to publish a multi-architecture Docker image
- Part 6 - Developing the Eventuate Common library on an M1/Arm MacBook
- Part 7 - Configuring CircleCI to build, test and publish multi-architecture images for Eventuate Common
- Part 8 - Building a multi-architecture Docker image for Apache Kafka
- Part 9 - Publishing multi-architecture base images for services
- Part 10 - Publishing a multi-architecture Docker image for the Eventuate CDC service
- Part 11 - The Eventuate Tram Customers and Orders example now runs on Arm/M1 MacBook!!
Not all Docker images run (well) on the M1 MacBook
It turns out that Apple M1 ARM-based architecture is a double edged sword. On the one hand, it is an energy efficient, high-performance architecture. But on the other hand, it’s a new entrant into the world of server-side development, which has been traditionally been dominated by Intel.
The problem is that Docker images are not inherently portable across multiple architectures. Many Docker images including those used by the example application are Intel specific. Docker Desktop for Mac uses QEMU to run Intel-based images on ARM. But sadly, the emulation is less than perfect. The documentation even describes it as ‘running Intel-based containers on Arm-based machines should be regarded as “best effort” only.’ While some containers run without problems, others crash or have poor performance.
Some Intel-based pre-built Docker container images fail
The Customer and Order applications runs several infrastructure services using the following pre-built Docker container images, which are all Intel-based:
eventuateio/eventuate-mysql- fails to start with
runtime: failed to create new OS thread (have 2 already; errno=22)
eventuateio/eventuate-cdc-service- frequently hangs or crashes
confluentinc/cp-zookeeper:5.2.4- seem unreliable
openzipkin/zipkin:2.21- seems fine although it might run slowly
mongo:3.6- seems fine
Poor performance of locally built Docker images
These pre-built container images aren’t the only images used by the Customer and Orders application.
The application also packages its three services as Docker images.
Those images are built using the
eventuateio/eventuate-examples-docker-images-java-example-base-image base image, which is an Intel-based image.
Despite being built locally, these images are also Intel and either crash or run extremely slowly - appearing to take 4x longer to startup.
The solution is to use multi-architecture Docker images
The solution is to change the application to use multi-architecture Docker images that support both the Intel and ARM architectures.
Hopefully, I can simply find newer multi-architecture versions of the Confluent platform and Zipkin images on Docker Hub.
It also requires changing the Eventuate platform to build multi-architecture
eventuate-examples-docker-images-java-example-base-image container images.
In later articles starting with Building multi-architecture Docker images for Intel and ARM, I describe the changes to application and the Eventuate platform that are needed to run this application on my MacBook. 00