Back to list
ð³ Python FastAPIã¢ããªã±ãŒã·ã§ã³ãDockerã§ã³ã³ããåããARM vs x86 ã¢rchitecture åé¡ã解決ãã
ð³ Containerizing a Python FastAPI Application with Docker (and Solving ARM vs x86 Architecture Issues)
Translated: 2026/3/7 8:10:58
Japanese Translation
== ã¿ã€ãã«ã®è©³çް ==
Dockerã䜿ã£ãŠã³ã³ããåããã¢ããªã±ãŒã·ã§ã³ã¯ããããã€ã®äºæž¬å¯èœæ§ãšäžè²«æ§ãæã€ããã«ããŸããéåžžãäœããèªåã®ãã·ã³ã§åäœããã°ãã©ããåãçµæã«ãªããŸãã
ããããå®äžçç°å¢ã§ã¯ãåŸã
ã«å°ããªåé¡ãéããèŠã€ãåºãããšããããŸãã
ç¹ã«ç°ãªãCPUã¢ãŒããã¯ãã£ãæã€x86 (AMD64)ãã·ã³ãšARM64ãã·ã³äžã§ã®ã¢ããªã±ãŒã·ã§ã³ãäœæããéã«ã§ãã== ã¬ã³ãŒãã®å
容 ==
ä»ç§ã¯Python FastAPIã®å°ããªã¹ã¯ãªãããã³ã³ããåããŠããããèŠãŠããŸããã
ãããç§ã®UbuntuãµãŒããŒã§ã©ã®ããã«åäœããŠãåé¡ãªãããã«èŠããŸããããå¥ã®ãã·ã³ã§éãæ§åã§ããã
調æ»ãç¶ããçµæãã·ã¹ãã éã®ã¢ãŒããã¯ãã£å·®ç°ãåå ã§ããã
ãã®èšäºã§ã¯å
šäœçãªããã»ã¹ã«ã€ããŠè©³ãã説æããŸã:
FastAPIã¢ããªã±ãŒã·ã§ã³ãDockerã§ã³ã³ããåããã«è³ã
Docker Composeã䜿çšããŠã¢ããªã±ãŒã·ã§ã³ãå®è¡ããã«è³ã
ARM vs x86 ã®ã¢ãŒããã¯ãã£å·®ç°ã®çè§£
DockerããŒã¢ã³åé¡ã®ãã©ãã«ã·ã¥ setPosition1
倿©èœãªã€ã¡ãŒãžãæ§ç¯ãã
PythonãFastAPIãDockerããŸãã¯ã¯ã©ãŠããããã€ã¡ã³ãã«åãçµãã§ããå Žåã«ã圹ç«ã¡ãŸãã
== å®éã®ãããžã§ã¯ãã®æ§é ==
ããã§ã¯éåžžã«åºæ¬çãªãµã³ãã«ã䜿çšããŸããããã¯ãã¿ã¹ã¯APIãµãŒãã¹ããšåŒã°ããå°ããªAPIãµãŒãã¹ã§ãã
=== ãããžã§ã¯ãã®æ§é ===
task-api-service
ãã
Python FastAPIã¢ããªã±ãŒã·ã§ã³ãããŒã8080ã䜿çšããŠREST APIãå
¬éããŸãã
== ã¹ããã1 â Dockerfile ãäœæãã ==
æåã®ã¹ããããšããŠãDockerãã¡ã€ã«ãäœæããŠã¢ããªã±ãŒã·ã§ã³ãã³ã³ããåããããã«ããŸãã ããã«ã¯ãäžè¬çã«äœ¿çšãããPython slimã€ã¡ãŒãžãå«ãŸããŸãããã®ã€ã¡ãŒãžã¯è»œéãªåºåºãªãŒããŒç°å¢ãæã£ãŠããŸãã
** é©çšãããžã§ã¯ãã®æé ===
FROM python:3.10-slim
* ç°å¢ variableãèšå®ããŸã
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
* ãã£ã¬ã¯ããªã®ãŠã§ã¢ãªã³ã°
WORKDIR /app
* apt-getã¢ããããŒããšã€ã³ã¹ããŒã«ïŒå¿
èŠã«å¿ããŠïŒ
RUN apt-get update && apt-get install -y libgl1 libglib2.0-0 build-essential
&& rm -rf /var/lib/apt/lists/
*
COPY requirements.txt .
* çµæãPipã€ã³ã¹ããŒã«ãã
RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
* ã³ããŒãå®è¡
COPY . .
* ããŒã8080ãéã
EXPOSE 8080
* CMD[ "uvicorn","main:app", "--host","0.0.0.0...
*ãããã®æé©åã®è©³çް === çŸåšã®ãã¡ã€ã«å
ã«äœããããŸããïŒ1ïžâ£ Pythonã®ãã£ãã·ã¥ãã¡ã€ã«ãé²ããPYTHONDONTWRITEBYTECODE=1
ã»ãã«ããšã€ã³ã¹ããŒã«ã®æ¹åã«ããããã®ã³ã³ããã¯ãããã¯ã·ã§ã³ç°å¢ã§ã䜿çšã§ããŸãã
1ïžâ£ ããã°ã©ã ã®ãã£ãã·ã¥ãã¡ã€ã«ãé²ãããšã«ããã.pyc ã®æ¡åŒµåã .py ã«å€æŽããPythonã®æ¡åŒµãµãŒã±ãããé¿ããããšãã§ããããšã«ãªããŸããããã«ãããPythonã³ãŒããã³ã³ããã«ã³ããŒããããšããããã®çµæãåæ ãããããšãé¿ããããŸãã
2ïžâ£ ãã°ãåªå
ããããšã«ããããã®ã³ã³ããã¯ããã« Docker ã®ãã°ã§èŠãããšãã§ããããã«ãªããŸããã
3ïžâ£ ãã®ãªã ãŒã ã³ãã³ãã«ãããæçµçãªã€ã¡ãŒãžãµã€ãºãå°ãããªããŸãããããã«ãããPythonã®ããã±ãŒãžã®ã«ã¯ã¬ãŒã¹ã»ããã¯æ¶ããããšãã§ããŸãã
== Step 2: ã€ã¡ãŒãžãæ§ç¯ãã ===
Docker ãã¡ã€ã«ãæºåã§ããããã€ã¡ãŒãžãäœæããŸãã (docker build ãå®è¡ããŸã)
docker build -t task-api-service . == ã³ã³ããã®å®è¡ ==
ãããŠãããã©ã®ããã«åäœãããããèŠãããšãã§ããŸããã docker run ã ãã¹ãã®ããŒããã³ã³ããã«ãããã§ããŸãã
docker run -p :8080 task-api-service 以äžãã¢ããªã±ãŒã·ã§ã³ã¯ä»¥äžã® URL ãééããããã«æç€ºãããŸã: HTTP /localhost:8088 == Docker Composeã䜿çšãã ===
è€æ°ã®ã¢ããªã±ãŒã·ã§ã³ãååšããŠãããšæåã§ç®¡çã忢ãã Docker Compose ã®äŸ¿å©ããå©çšã§ããŸããDocker Compose ãå®éã«ã¯äœã§ãããããçè§£ããã ããã°ãšæããŸãã
ãã¡ã€ã«ãäœæããŸãïŒdocker-compose.yml
version: "3.9"
services:
fastapi-app:
container_name: fastapi-app
build: (ã³ã³ããåãæå®ãã)
context:
dockerfile:
service ports:
端å£ããããããportsã¯ãhost ãš containerã®ããããã«ä»»æã®ããŒãæ°ã®ãã®ãæã€ããšãã§ããŸãã
volumesãšå€éšãã£ã¬ã¯ããªãšã®çµã¿åããããµããŒããããŠããŸãã ããã¹ãããã£ã¬ã¯ããªã®äžèº«ã ãã§ãªãããã®ãã©ã€ããŸã§ã³ããŒã§ããŸãã
gitãªã©ã®å€ããåç
§ããããã®ãå«ããŠãã ããã
== ãã£ã¡ã«ãšã¿ãŒã²ããã®å·®ç°ïŒARM vs x86ïŒ ===
ç§ã® Ubuntu åäœããã¹ãŠããŸãè¡ã£ããšãã§ãããã·ã¹ãã æ§é ã確èªããã«ã¯ä»¥äžã®ã³ãã³ããå®è¡ããŸããuname -m
ãã®çµæã¯ãã®ããã«ãªã£ãŠãã: x86_64 (ããã¯AMD64ã¢ãŒããã¯ãã£ã§ã)
ãã ããæè¿ã®ã·ã¹ãã ã§ã¯ ARM ã¢ãŒããã¯ãã£ã§ããããšãå€ããããŸããARM64ãšåŒã°ããŸãã
ããã«ãããããããæ¬¡ã®ããã€ã¹ã¯ãã®ã¢ãŒããã¯ãã£ã䜿çšããå¯èœæ§ããããŸã: AppleïŒç¹ã«iPadProïŒã ããã«ãããARMã®ããŒã¢ã³åé¡ãçºçããã®ã§ããã説æããŸãã
ãŸããDocker ã³ã³ããã«ã¯å®è¡æãã¹ãã·ã¹ãã ãµãŒãããŒãã£ã³ãŒã(ã©ã€ãã©ãª)ãååšããŸããããããããvolumesããªãã·ã§ã³ãèšå®ãããšãããã«ãããã¡ã€ã³ãåé¡ãåŒãèµ·ããããšãã§ããŸãã
volumesã®èšå®: ãdocker-compose.yml
ã.:/appã
ãã®ãã©ã€ããšå€éšãã£ã¬ã¯ããªã«çžå¯Ÿçãªãã¹ãšããŠåç
§ãããŸãã
ããããã³ãŒãã®å€æŽãåžžã«åæ ãããããã³ãŒãããŒã¹å
šäœã«åœ±é¿ãäžããŸãããããŠãããžã§ã¯ãã®åæ§ç¯ããªãŒããªããŒããªã©ãè¡ãããŸãããã®ãããäžè¬çã«å€ãã®äººã«å¯ŸããŠå¿
èŠã ãšãããŠããŸãã
â ïž Dockerã§ã¯ã©ã¹ã¿ãªã³ã°ããã¹ãããå ŽåãDocker ã€ã³ããã¯ã¹ãåé¡ãåŒãèµ·ããå¯èœæ§ããããŸãããã®åé¡ã«å¯Ÿããæãäžè¬çãªè§£æ±ºçã¯Docker Composeãä»ããå¥ã®ãããã¯ãŒã¯ãäœæããããšã«ãã£ãŠçãŸããŸããããã«ããããDockerããšãNetworkããšããã¿ã°ãæã€æ°ãããããã¯ãŒã¯åã§å®çŸã§ããŸãã
docker-compose -f docker-compose.yml --network new-net up
ä»ã®ã¢ããªã±ãŒã·ã§ã³ãã¯ã©ã¹ã¿ãªã³ã°ãããããªãæ°ãã networkã®ååã䜿çšããŸã: (ãã®æ°ããèšå®ããã network) 'new-net'
docker composeåœä»€ãå®è¡ãããšãæ¢åã®ã€ã¡ãŒãžãŸãã¯ã³ã³ããã§æ§æãããŠãããããã¯ãŒã¯ã«è€è£œãããããã«ãªããŸã new-netã ãã ããããèªèº«ã® networkãçšããŠå¥ã®ãããã¯ãŒã¯ãæ§ç¯ããããšãã§ããŸãã
æ°ãã networkãå®çŸ©ããããããnetworksãããŒã®æ°ããªãªãã·ã§ã³ãšããŠè¿œå ãããšãç¹å®ã®ã³ãã³ãã©ã€ã³ãªãã·ã§ã³ããã®ãããã¯ãŒãã³ã°ãéããŠé©çšãããŸãããŸãããdockerããšãNetworkãã¿ã°ãä»äžãããŸãã ãããã®ãããã¯ãŒã¯ã«æ°ããã³ã³ãããäœæããããšãã§ããŸãã
ãã®ãããDocker Composeã䜿ã£ãŠã¯ã©ã¹ã¿ãªã³ã°ãšãããã¯ãŒã¯ã管çããããšãäžè¬çã§ããããããnetworksãããŒã®ãªãã·ã§ã³ã䜿çšãç¶ããããšã¯éèŠã§ãã
ãŸãããã®åé¡ã¯ Docker ã³ãã³ãã©ã€ã³ããŒã«ãå«ããããšãã§ããŸãã ããã¯äžè²«æ§ãä¿¡é Œæ§ãããŠåé¡è§£æ±ºæ³ã«éåžžã«åœ¹ç«ã€ã§ãã ã€ã¡ãŒãžãšãããã¯ãŒã¯ã«å¯ŸããŠç°ãªããªãããã«ããŸãã ãããã®ãã¿ã°ãã¯ã³ã³ããåã®ãªã¹ãã«ãã£ãŠä¿æãããŸãã
ããã㯠Docker ã®è€æ°ã®ã¯ã©ã¹ã¿ãªã³ã°ã®äžéšã«ãªããç°ãªãã³ã³ããŒãã³ãããåã¢ããªã±ãŒã·ã§ã³ãå©çšå¯èœã«ãªããŸãã
Original Content
When working with containerized applications, Docker usually makes deployments predictable and consistent. Once everything is packaged inside a container, the expectation is simple: âIf it works on my machine, it should work everywhere.â
However, real-world environments sometimes introduce subtle issues â especially when applications are built on machines with different CPU architectures, such as x86 (AMD64) and ARM64.
Recently, I was containerizing a small Python FastAPI application, and I noticed something interesting. The Docker image worked perfectly on my Ubuntu server but behaved differently on another machine. After some investigation, the root cause turned out to be architecture differences between systems.
In this article, I'll walk through the entire process:
Containerizing a FastAPI application with Docker
Running the application using Docker Compose
Understanding ARM vs x86 architecture differences
Troubleshooting Docker daemon issues
Building multi-architecture images
If you work with Python, FastAPI, Docker, or cloud deployments, this guide will help you avoid some common pitfalls.
ð Project Structure
For this example, we'll use a simple API service called task-api-service.
The project structure looks like this:
task-api-service
The FastAPI application exposes a REST API running on port 8080.
ð Step 1 â Writing the Dockerfile
The first step is creating a Dockerfile to containerize the application.
We start with the official Python slim image, which provides a lightweight base environment.
FROM python:3.10-slim
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
RUN apt-get update && apt-get install -y \
libgl1 \
libglib2.0-0 \
build-essential \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --upgrade pip \
&& pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["uvicorn","main:app","--host","0.0.0.0","--port","8080"]
Why these optimizations?
There are a few small improvements here that make the container more production-friendly.
1ïžâ£ Prevent Python cache files
PYTHONDONTWRITEBYTECODE=1
This prevents .pyc files from being created inside the container.
2ïžâ£ Better logging
PYTHONUNBUFFERED=1
This ensures logs are immediately visible in Docker logs.
3ïžâ£ Removing package cache
rm -rf /var/lib/apt/lists/*
This reduces the final image size.
ð Step 2 â Building the Docker Image
Once the Dockerfile is ready, we can build the image.
docker build -t task-api-service .
After building, verify the image:
docker images
You should see the new image listed.
ð Step 3 â Running the Container
Now we can run the container.
docker run -p 8088:8080 task-api-service
The application will now be accessible at:
http://localhost:8088
Docker maps:
Host Port 8088 â Container Port 8080
âïž Step 4 â Using Docker Compose
Managing containers manually becomes inconvenient as applications grow.
This is where Docker Compose becomes useful.
Create a file called docker-compose.yml.
version: "3.9"
services:
fastapi-app:
container_name: fastapi-app
build:
context: .
dockerfile: Dockerfile
ports:
- "8088:8080"
volumes:
- .:/app
restart: unless-stopped
ð Why Use Volumes?
volumes:
- .:/app
This mounts the local project directory into the container.
Benefits include:
Instant reflection of code changes
Faster development workflow
No need to rebuild images repeatedly
If you run Uvicorn with the --reload flag, the server automatically restarts when files change.
â¶ïž Running with Docker Compose
Start the application:
docker compose up --build
Run it in the background:
docker compose up -d
Stop the containers:
docker compose down
â ïž The Architecture Problem (ARM vs x86)
Everything worked perfectly on my Ubuntu machine.
To check the system architecture, I ran:
uname -m
Output:
x86_64
This means the system uses AMD64 architecture.
However, many modern systems now use ARM architecture, including:
Apple Silicon Macs
AWS Graviton instances
Raspberry Pi servers
Running the same command on those systems usually returns:
arm64
This difference can cause compatibility issues when Docker images are built on one architecture and run on another.
ð§ Forcing a Specific Docker Platform
Docker allows specifying the target architecture during builds.
For example:
docker build --platform linux/arm64 -t task-api-service .
This forces Docker to build an ARM-compatible image.
You can also define the platform inside Docker Compose.
services:
fastapi-app:
build:
context: .
platform: linux/arm64
ports:
- "8088:8080"
ð Troubleshooting Docker Daemon Issues
During testing, I encountered an error like this:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock
This usually means the Docker daemon is not running.
To verify Docker status:
sudo systemctl status docker
If the service is inactive, restart it:
sudo systemctl restart docker
Then confirm Docker is working:
docker ps
ð Fixing Docker Permission Issues
Another common issue occurs when Docker commands require sudo.
This happens because the user is not part of the Docker group.
To fix it:
sudo usermod -aG docker $USER
Then reload the shell:
newgrp docker
Now Docker commands can run without sudo.
ð Best Practice: Multi-Architecture Docker Images
Instead of building separate images for ARM and x86 systems, Docker allows multi-architecture builds.
Using Docker Buildx:
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t myrepo/task-api-service:latest \
--push .
This creates a single image compatible with multiple architectures.
Official Docker images such as:
Python
Node.js
Nginx
all use this approach.
ð§ Final Thoughts
Docker makes it easy to package and deploy applications consistently, but architecture differences can sometimes introduce unexpected issues.
A few key lessons from this experience:
Always check system architecture using uname -m
Use Docker Compose for easier container management
Ensure the Docker daemon is running before troubleshooting builds
Consider multi-architecture images for production deployments
By understanding these concepts, you can ensure your containerized applications run smoothly across different environments, cloud platforms, and development machines.