Skip to content

Commit 6213758

Browse files
Copilotdavideme
andauthored
Document how to launch FastAPI in a container with explicit minor version, Poetry, and distroless nonroot runtime for Cloud Run (#223)
* Initial plan * Implement FastAPI Docker improvements with Cloud Run support and PORT env var Co-authored-by: davideme <[email protected]> * Fix Dockerfile to use Poetry in build stage and distroless runtime Co-authored-by: davideme <[email protected]> * Use nonroot distroless image for improved security Co-authored-by: davideme <[email protected]> * Refactor launcher.py for style and consistency Updated string quotes to double quotes, improved command formatting for readability, and standardized the main entry point check. Removed unused subprocess import. --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: davideme <[email protected]> Co-authored-by: Davide Mendolia <[email protected]> Co-authored-by: Davide Mendolia <[email protected]>
1 parent e09d2ce commit 6213758

File tree

3 files changed

+94
-14
lines changed

3 files changed

+94
-14
lines changed

src/python/Dockerfile

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
FROM python:3.12 AS builder
1+
FROM python:3.12-slim AS build-env
2+
COPY . /app
3+
WORKDIR /app
24

3-
WORKDIR /usr/src/app
5+
# Install curl for Poetry installation
6+
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
47

58
# Install Poetry using the official installer
6-
RUN pip install --upgrade pip
9+
RUN pip install --no-cache-dir --upgrade pip
710
RUN curl -sSL https://install.python-poetry.org | python3 -
811

912
# Set PATH and configure Poetry
@@ -18,16 +21,13 @@ COPY pyproject.toml poetry.lock ./
1821
# Install production dependencies only
1922
RUN poetry install --only=main && rm -rf $POETRY_CACHE_DIR
2023

21-
# Copy source code
22-
COPY . .
2324

24-
25-
FROM python:3.12 AS service
26-
WORKDIR /root/app
27-
28-
# Copy the virtual environment and source code from builder stage
29-
COPY --from=builder /usr/src/app/.venv /root/app/.venv
30-
COPY --from=builder /usr/src/app /root/app
25+
FROM gcr.io/distroless/python3-debian12:nonroot
26+
COPY --from=build-env /app /app
27+
WORKDIR /app
3128

3229
# Set environment to use the virtual environment
33-
ENV PATH="/root/app/.venv/bin:$PATH"
30+
ENV PATH="/app/.venv/bin:$PATH"
31+
32+
# Use Python launcher script to handle PORT env var for Cloud Run compatibility
33+
CMD ["python", "launcher.py"]

src/python/README.md

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ To run the server, please execute the following from the root directory:
1616
poetry install
1717

1818
# Run the server
19-
poetry run uvicorn openapi_server.main:app --host 0.0.0.0 --port 8080
19+
poetry run fastapi run src/openapi_server/main.py --port 8080 --host 0.0.0.0
2020
```
2121

2222
and open your browser at `http://localhost:8080/docs/` to see the docs.
@@ -35,6 +35,56 @@ To run the server on a Docker container, please execute the following from the r
3535
docker-compose up --build
3636
```
3737

38+
### Docker Image Features
39+
40+
The Docker image uses:
41+
- **Multi-stage build**: Build stage with Poetry dependency installation, production stage with Google's distroless Python image
42+
- **Poetry dependency management**: Uses Poetry in build stage for reproducible dependency installation
43+
- **Distroless runtime**: Production stage uses `gcr.io/distroless/python3-debian12:nonroot` for minimal attack surface and non-root execution
44+
- **Port Configuration**: Supports PORT environment variable for Cloud Run compatibility via launcher script
45+
- **FastAPI CLI**: Uses the modern `fastapi run` command for production deployment
46+
47+
#### Building Docker Image
48+
49+
```bash
50+
docker build -t lamp-control-api-python .
51+
```
52+
53+
#### Running Docker Container
54+
55+
```bash
56+
# Run with default port (80)
57+
docker run -p 8080:80 lamp-control-api-python
58+
59+
# Run with custom port via environment variable (Cloud Run style)
60+
docker run -p 8080:8080 -e PORT=8080 lamp-control-api-python
61+
```
62+
63+
## Cloud Run Deployment
64+
65+
This application is optimized for Google Cloud Run deployment:
66+
67+
### Environment Variables
68+
69+
- `PORT` - The port for the HTTP server. Cloud Run automatically sets this variable for the ingress container. Defaults to 80 if not set.
70+
71+
### Cloud Run Notes
72+
73+
- The following environment variables are automatically added to all running containers except `PORT`. The `PORT` variable is only added to the ingress container.
74+
- The application will bind to `0.0.0.0:${PORT}` to accept traffic from Cloud Run's load balancer.
75+
- The Docker image uses a multi-stage build for optimal size and security.
76+
77+
### Deployment Example
78+
79+
```bash
80+
# Deploy to Cloud Run
81+
gcloud run deploy lamp-control-api \
82+
--image gcr.io/YOUR_PROJECT/lamp-control-api-python \
83+
--platform managed \
84+
--region us-central1 \
85+
--allow-unauthenticated
86+
```
87+
3888
## Tests
3989

4090
To run the tests:

src/python/launcher.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Launcher script for FastAPI application with Cloud Run PORT support.
4+
"""
5+
import os
6+
import sys
7+
8+
9+
def main():
10+
port = os.environ.get("PORT", "80")
11+
12+
# Build the fastapi run command
13+
cmd = [
14+
sys.executable,
15+
"-m",
16+
"fastapi",
17+
"run",
18+
"src/openapi_server/main.py",
19+
"--port",
20+
port,
21+
"--host",
22+
"0.0.0.0",
23+
]
24+
25+
# Execute the command, replacing the current process
26+
os.execv(sys.executable, cmd)
27+
28+
29+
if __name__ == "__main__":
30+
main()

0 commit comments

Comments
 (0)