Update Jan 7, 2022: Check out the follow up to this post as we document how our team upgraded to M1 Max MacBook Pros!
This is my first week at Authzed as a software engineer and I've been provided with a new M1-based MacBook Pro as my primary development machine. Apple’s new M1 chip has been receiving high praise for being quiet, battery efficient, and performant, so naturally I was excited to use my new laptop. Turns out, I wasn’t the only one excited to start using M1 MacBook Pros...
Thus far, Authzed’s services have been built and deployed on x86-based machines. The Apple M1 is an ARM based system on a chip. If someone were to find a way to build and run our tools and services on an M1, then presumably any Authzed engineer (especially those who have older Intel-based laptops with loud fans) could do their work building software while enjoying all the nice features of Apple’s new laptop. My teammates graciously volunteered me to be that person 😅.
Introducing our application stack
At Authzed, we primarily use Python and Go for our backends,Typescript and React for our frontends, and gRPC to communicate between services. We build containers and deploy to Kubernetes, using Docker Desktop on our development machines. We maintain client libraries in several other languages but this stack is our bread and butter (or rice and kimchi if you prefer).
Installing tools and dependencies
My first step was to install basic tools: homebrew, git, and docker. Thankfully now that a few months have passed since the M1 launch, most popular tools have added native support. For the few tools that don’t have an ARM build, I’ve found no noticeable difference in performance when run through Rosetta translation. And for the more niche tools, such as my terminal of choice Kitty, I found these compatibility tracking sites helpful:
Note about Docker for Mac: A preview of M1 support was only recently released on Feb 21. All of my tests were run on preview version 3.1.0 but release candidate versions have been posted since the experiences documented here.
Update: As of April 15, the GA release for M1 support is now available in Docker for Mac 3.3.1 and our containers continue to build and run on the latest version!
Next up was installing Python and Go. My initial attempt to install Python 3.8.3 failed with the error:
./Modules/posixmodule.c:9221:15: error: implicit declaration of function 'sendfile' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
ret = sendfile(in, out, offset, &sbytes, &sf, flags);
Others had encountered this and some used a patch to get v3.8.3 building:
$ pyenv install --patch 3.8.6 <<(curl -sSL https://raw.githubusercontent.com/Homebrew/formula-patches/113aa84/python/3.8.3.patch\?full_index\=1)
I wanted to avoid depending on a one-off patch and found that Python 3.9 builds natively. A quick run of our tests using 3.9 were successful so we were able to upgrade to 3.9 safely. Go added native M1 support in 1.16 so I simply installed the latest, 1.16.2, using homebrew:
$ brew install go
All of our required tools had been installed successfully! So far so good.
Building our services
We use Docker to build containers for our services. Fingers crossed, I ran docker build .
in one of our service directories in hopes that it would just work...it did not. Without revealing too much about our internal projects, the build errors fell into 2 categories: missing build dependencies and incompatible binaries.
An example of a missing build dependency issue was attempting to install the grpcio-1.34.0 package. The poetry install
step in our Dockerfiles was failing with the error:
FileNotFoundError: [Errno 2] No such file or directory: 'c++'
This indicated that the docker image was missing a C++ compiler and/or headers. The simplest solution was to change from a slim base image to a more full image.
-FROM python:3.8-slim
+FROM python:3.9
An example of incompatible binaries was attempting to install the psycopg2-binary package. The installation would fail with:
Error: pg_config executable not found.
I didn’t dig into whether the executable was actually missing or not executable on my machine. Again, the simple solution was to update base images by specifying ARM-specific Docker images.
-FROM node:14.11-alpine3.12
+FROM arm64v8/node:14.16-alpine3.12
After making the above changes to all of our Dockerfiles, voila: our containers built successfully!
One thing to remember: the initial goal was to test whether we could build and run our services for development and testing. A future step will be to set up multi-architecture builds to be able to share x86 builds as well.
Deploying to a M1 MacBook Pro
✅ Tools installed
✅ Containers built
❓ Services deployed
Tools are installed, containers are built, but what good are they if I couldn’t run them? I’ll save you the suspense and show that our full application deploys and runs on Kubernetes 1.19.7 through Docker Desktop.
The challenges of running Kubernetes on Mac are probably best left for their own post but a few tips from my experience are to turn off the “Use gRPC FUSE for file sharing” option and to remove the default shared directories except for /tmp.
The result
I set out to find out if Authzed’s tools and services would build and run on a M1 MacBook Pro and after much Googling and some trial and error, I happily reported a simple, “IT WORKS!” back to my teammates.
The first 24 hours using my laptop were filled with video chats, building containers, and Kubernetes running for most of the day, yet it was still going strong with 28% battery remaining. I didn’t hear the fan turn on once and in fact, I don’t think the internal temperature went over 130F even in the middle of builds. (The temp is running at a cool 71F as I write this post).
Safe to say, I think my teammates will be shopping for new MacBooks in the near future.
I’m just getting started here at Authzed but if you’re also interested in these tools and want to help build our fast, flexible, and scalable permissions API, check out our jobs page and join us!
Epilogue
One downside to the M1 MacBook Pros I discovered much too late is that it only supports one external monitor, although there are some workarounds using docks and proprietary display drivers. Despite my idle second monitor, I’ve found the benefits of the M1 MacBook Pro heavily outweigh this one downside.