Another step with Apple containers!
Introduction
My initial blog post detailing my first practical experience with Apple containerization technology sparked a flurry of discussion and significant inquiries from my colleagues. The central question raised — and the point of subsequent internal debate — was whether this capability possessed the necessary maturity and reliability for industrial-scale deployment. My answer to that is an unequivocal “YES.” This certainty stems from further comprehensive testing, which has solidified my conviction that this capacity provided by Apple is not only viable today but is also poised for accelerated development and is undeniably here to stay.
Tests
For my first (new) test, I wrote a vey basic “Hello World” app in Go.
package main
import (
"fmt"
)
func main() {
// Simple application logic
fmt.Println("Hello from the Go container!")
}
Then I made a Dockerfile to build an image which runs this code.
FROM golang:1.22-alpine AS builder
ENV CGO_ENABLED=0
WORKDIR /app
COPY go-hello.go .
RUN go build -o app -ldflags "-s -w" go-hello.go
FROM scratch
ENTRYPOINT ["/app"]
COPY --from=builder /app/app /app
For the first step, I used Podman to build an image 👇
podman build -t go-hello .
podman run go-hello
# for Docker
docker build -t go-hello .
docker run go-hello
Running the image gives the following output;
Hello from the Go container!
OK, now I build the same image with the “container” from Apple on my MacOS.
# start the engine
container system start
container builder start --cpus 8 --memory 32g
# build the image
container build --tag go-test --file Dockerfile .
...
[+] Building 8.2s (9/9) FINISHED
=> [resolver] fetching image...docker.io/library/golang:1.22-alpine 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> oci-layout://docker.io/library/golang:1.22-alpine@sha256:1699c10032ca2582ec89a24a1312d986a3f094aed3d5c1147b19880afe40e052 2.2s
=> => resolve docker.io/library/golang:1.22-alpine@sha256:1699c10032ca2582ec89a24a1312d986a3f094aed3d5c1147b19880afe40e052 0.1s
=> => sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1 32B / 32B 0.0s
=> => sha256:4861bab1ea04dbb3dd5482b1705d41beefe250163e513588e8a7529ed76d351c 127B / 127B 0.0s
=> => sha256:fa1868c9f11e67c6a569d83fd91d32a555c8f736e46d134152ae38157607d910 297.86kB / 297.86kB 0.0s
=> => sha256:52f827f723504aa3325bb5a54247f0dc4b92bb72569525bc951532c4ef679bd4 3.99MB / 3.99MB 0.0s
=> => extracting sha256:52f827f723504aa3325bb5a54247f0dc4b92bb72569525bc951532c4ef679bd4 0.1s
=> => extracting sha256:fa1868c9f11e67c6a569d83fd91d32a555c8f736e46d134152ae38157607d910 0.0s
=> => sha256:90fc70e12d60da9fe07466871c454610a4e5c1031087182e69b164f64aacd1c4 66.29MB / 66.29MB 0.3s
=> => extracting sha256:90fc70e12d60da9fe07466871c454610a4e5c1031087182e69b164f64aacd1c4 1.8s
=> => extracting sha256:4861bab1ea04dbb3dd5482b1705d41beefe250163e513588e8a7529ed76d351c 0.0s
=> => extracting sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 159B 0.0s
=> [linux/arm64/v8 builder 1/4] WORKDIR /app 0.4s
=> [linux/arm64/v8 builder 2/4] COPY go-hello.go . 0.0s
=> [linux/arm64 builder 3/4] RUN go build -o app -ldflags "-s -w" go-hello.go 1.8s
=> [linux/arm64/v8 stage-1 1/1] COPY --from=builder /app/app /app 0.0s
=> exporting to oci image format 0.1s
=> => exporting layers 0.1s
=> => exporting manifest sha256:4357214a4637b5297da08bb3cc84ebcdd0fa89b9b4208cb60bbba859faa1ef04 0.0s
=> => exporting config sha256:7d618301e78cf16c98f0d472556bb8c883bbf4d87ea675811716385c041fbdd0 0.0s
=> => exporting manifest list sha256:1c9e4da40926ae74bc397abd23acf07ee2dfb28ee0c14f275b960ba1a695e32d 0.0s
=> => sending tarball 0.0s
Successfully built go-test:latest
Let’s run it 🏃♀️
container run --rm go-test
Hello from the Go container!
Comparing my images by using Podman Desktop 🏅
This application could be deployed, for instance on my Minikube cluster with no problem!
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-hello-deployment
namespace: apps
labels:
app: go-hello
spec:
replicas: 1
selector:
matchLabels:
app: go-hello
template:
metadata:
labels:
app: go-hello
spec:
containers:
- name: go-hello-container
image: go-hello:latest
imagePullPolicy: Never
# for my specific configuration
minikube start --driver=podman --kubernetes-version=v1.28.0
# for a specific namespace
kubectl create namespace apps
kubectl apply -f go-hello-deployment.yaml
Nice, now let’s build a Java application! Below is the simple version of “Hello World” written in the most simple Java application ever 😶
public class HelloWorld {
public static void main(String[] args) {
// Print the greeting to standard output
System.out.println("Hello, Java World from the container!");
}
}
And the adequate Dockerfile 🧺
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY HelloWorld.java .
RUN javac HelloWorld.java
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=builder /app/HelloWorld.class .
CMD java -cp . HelloWorld
Now let us build and run this code!
container build --tag java-hello --file Dockerfile .
...
[+] Building 2.2s (12/12) FINISHED
=> [resolver] fetching image...docker.io/library/eclipse-temurin:21-jdk-alpine 0.0s
=> [resolver] fetching image...docker.io/library/eclipse-temurin:21-jre-alpine 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> oci-layout://docker.io/library/eclipse-temurin:21-jdk-alpine@sha256:89517925fa675c6c4b770bee7c44d38a7763212741b0d6fca5a5103caab21a97 0.0s
=> => resolve docker.io/library/eclipse-temurin:21-jdk-alpine@sha256:89517925fa675c6c4b770bee7c44d38a7763212741b0d6fca5a5103caab21a97 0.0s
=> oci-layout://docker.io/library/eclipse-temurin:21-jre-alpine@sha256:990397e0495ac088ab6ee3d949a2e97b715a134d8b96c561c5d130b3786a489d 0.0s
=> => resolve docker.io/library/eclipse-temurin:21-jre-alpine@sha256:990397e0495ac088ab6ee3d949a2e97b715a134d8b96c561c5d130b3786a489d 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 297B 0.0s
=> CACHED [linux/arm64/v8 stage-1 1/3] WORKDIR /app 0.0s
=> CACHED [linux/arm64/v8 builder 1/4] WORKDIR /app 0.0s
=> CACHED [linux/arm64/v8 builder 2/4] COPY HelloWorld.java . 0.0s
=> CACHED [linux/arm64 builder 3/4] RUN javac HelloWorld.java 0.0s
=> CACHED [linux/arm64/v8 stage-1 2/3] COPY --from=builder /app/HelloWorld.class . 0.0s
=> exporting to oci image format 0.3s
=> => exporting layers 0.0s
=> => exporting manifest sha256:f468154e8f4d3d7830c0a9294df9a518004c2b5e431b28a41eb12cbf9ac1f006 0.0s
=> => exporting config sha256:0aac090feabd2c49d6de93877ac5029dc5ae0ba39df3c343e27e286385d42fa5 0.0s
=> => exporting manifest list sha256:c004a43800e34f66948cf45a56dcb6b484e7ade4c8dd510c3c0d6ea20a3d3b81 0.0s
=> => sending tarball 0.3s
Successfully built java-hello:latest
> container run --rm java-hello
Hello, Java World from the container!
Et voilà 🍾
Conclusion
As established through my initial and subsequent testing, I am convinced that Apple containerization is a fundamental game-changer in the deployment landscape. This technology doesn’t just offer an interesting alternative; it delivers the operational flexibility and robust architecture required to build and confidently deploy industry-grade applications, signaling a significant shift in how we approach enterprise development.
Links
- Apple Container: https://github.com/apple/container
- Package Installation: https://github.com/apple/container/releases
Top comments (0)