Build Intel64-compatible Docker images from Mac M1
by bernt & torsten
I was building a tracking pixel API app for internal websites. A tracking pixel is an HTML code snippet loaded when a user visits a website. I built this app on my laptop, MAC M1, and I run my Docker container locally within Docker Dekstop.
All was good when I ran that app locally on my laptop, and I could do all the coding and testing of the app. Rolling out the App to the end user for user testing on my laptop would not be sufficient. I decide to host my app on Google Cloud Run.
My tracking pixel app is built with the Python Flask framework; the pixel app uses Google Firestore to store the count of a web page view where the iframe with URL that points to the Pixel App is installed. If you are interested, you can contribute to the app or use it as you like, code is available on GitHub.
Deploying to Cloud Run
Building the docker image and pushing it to Google Cloud Registry was straightforward. These are my docker commands for building and pushing.
$ docker build -t eu.gcr.io/<project-id>/pixelcount .
$ docker push eu.gcr.io/<project-id>/pixelcount
The <project-id> is your Google Cloud Project Id; as my work is in the Europe zone, I use eu.gcr.io as my Container Registry location.
When I tried to deploy it to Cloud Run, I encountered a problem. By the way, I use Terraform for deploying to Cloud Run. Here is the code of main.tf
# main.tf
terraform {
required_version = ">= 0.14"
required_providers {
# Cloud Run support was added on 3.3.0
google = ">= 3.3"
}
}
# Configure GCP project
provider "google" {
credentials = file("../key.json")
project = var.gcp_project_id
region = "europe-north1"
}
resource "google_project_service" "cloud_run" {
service = "iam.googleapis.com"
disable_dependent_services = true
disable_on_destroy = false
}
data "google_container_registry_image" "pixelcount" {
name = var.image_name
}
# Deploy image to Cloud Run
resource "google_cloud_run_service" "pixelcount" {
depends_on = [
google_project_service.cloud_run
]
name = "pixelcount"
location = var.gcp_region
template {
spec {
containers {
image = "eu.gcr.io/<projectid>/pixelcount"
ports {
container_port = 8080
}
}
}
}
traffic {
percent = 100
latest_revision = true
}
}
# Create public access
data "google_iam_policy" "all_users_policy" {
binding {
role = "roles/run.invoker"
members = ["allUsers"]
}
}
# Enable public access on Cloud Run service
resource "google_cloud_run_service_iam_policy" "all_users_iam_policy" {
location = google_cloud_run_service.pixelcount.location
project = google_cloud_run_service.pixelcount.project
service = google_cloud_run_service.pixelcount.name
policy_data = data.google_iam_policy.all_users_policy.policy_data
}
Every time I run the terraform code with:
terraform plan
terraform apply
I got an error deploying from my local machine
“Error waiting to create Service: resource is in failed state “Ready:False”, message: Cloud Run error: The user-provided container failed to start and listen on the port defined provided by the PORT=8080 environment variable. Logs for this revision might contain more information.”
I also went through the Google Cloud Run logs, and the message was clear:
Error detected in pixelcount version pixelcount-00001-xuk
terminated: Application failed to start: Failed to create init process: failed to load /usr/local/bin/python: exec format error
Solution
I searched and searched for what could cause this. I tried different ways – deleting the Container Registry entry, tried to use a different Container Registry, gcr.io
and us.gcr.io
, eventually, I remembered that I was working on another Python project some time ago, and I had a problem installing a Python module with – pip install <name>. At that time, the problem was associated with the Apple M1. The python module I wanted to install had not yet been available for Apple M1.
After remembering that issue, I tweaked my search. I found a different set of search results that led me to this entry on Stackoverflow, which suggested that I should use docker buildx build --platform linux/amd64
it because I have been building my Docker image for Mac M1 ARM. Google Cloud Platform requires a Linux AMD docker image.
This is because the docker images built with Apple Silicon (or another ARM64-based architecture) can create issues when deploying the images to a Linux or Windows-based AMD64 environment (e.g. Google Cloud Run etc.) For example, you may try uploading your docker image made on the M1 chip to a Google Cloud Container Registry repository, and it fails to run. Therefore, you need a way to build AMD64-based images on the ARM64 architecture, whether using Docker build (for individual images) or docker-compose build (e.g. for multi-image apps running in a docker-compose network).
I followed the advice suggested in the Stackoverflow post and use docker buildx
that is the docker command I used in the end.
docker buildx build \
--platform linux/amd64 \
--push \
-t eu.gcr.io/<project-id>/pixelcount .
That built and pushed the docker image to Container Registry. After that, I executed terraform plan & apply
that and deployed my docker image to Cloud Run without any errors. That was my journey in solving a problem that occurs from using different Chipsets. Just something I have to keep in mind in my future work.
Why It’s Important to Exercise When You’re Over 60
Many of us find ourselves in a pickle as the years pile up. Once reliable sidekicks, our...
A Poem: The Last Time
You never know when it will be,
The last time you ski down slopes of snow,
A Poem: Time Millionaire
When the morning wakes, still and clear,
No more alarms, no more rush....