Skip to content

Commit de3b832

Browse files
committed
support deploy to docker, add auto-publish docker image workflow
1 parent 393c87c commit de3b832

4 files changed

Lines changed: 209 additions & 8 deletions

File tree

.dockerignore

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Git
2+
.git
3+
.gitignore
4+
5+
# Python
6+
__pycache__
7+
*.py[cod]
8+
*$py.class
9+
*.so
10+
.Python
11+
.venv
12+
venv
13+
env
14+
ENV
15+
.env
16+
.env.*
17+
!.env.example
18+
19+
# PDM
20+
.pdm.toml
21+
.pdm-python
22+
pdm.lock
23+
24+
# IDE
25+
.idea
26+
.vscode
27+
*.swp
28+
*.swo
29+
*~
30+
31+
# Testing
32+
.pytest_cache
33+
.coverage
34+
htmlcov
35+
.tox
36+
.nox
37+
38+
# Build
39+
build
40+
dist
41+
*.egg-info
42+
43+
# Logs
44+
logs
45+
*.log
46+
47+
# Documentation
48+
docs
49+
50+
# CI/CD
51+
.github
52+
53+
# Kubernetes
54+
k8s
55+
56+
# Development
57+
*.md
58+
!README.md
59+
60+
# OS
61+
.DS_Store
62+
Thumbs.db
63+
64+
# Migrations (include these, but exclude cache)
65+
migrations/__pycache__
66+
67+
# Tests (optional - uncomment to include tests in image)
68+
tests
69+
70+
# Temporary files
71+
*.tmp
72+
*.temp
73+
.cache

.github/workflows/publish-ghcr.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Publish to GHCR
2+
3+
on:
4+
push:
5+
branches:
6+
- fastapi
7+
- develop
8+
- main
9+
tags:
10+
- 'v*'
11+
12+
env:
13+
REGISTRY: ghcr.io
14+
IMAGE_NAME: ${{ github.repository }}
15+
16+
jobs:
17+
build-and-push:
18+
runs-on: ubuntu-latest
19+
permissions:
20+
contents: read
21+
packages: write
22+
23+
steps:
24+
- name: Checkout repository
25+
uses: actions/checkout@v4
26+
27+
- name: Set up Docker Buildx
28+
uses: docker/setup-buildx-action@v3
29+
30+
- name: Log in to Container Registry
31+
uses: docker/login-action@v3
32+
with:
33+
registry: ${{ env.REGISTRY }}
34+
username: ${{ github.actor }}
35+
password: ${{ secrets.GITHUB_TOKEN }}
36+
37+
- name: Extract metadata (tags, labels) for Docker
38+
id: meta
39+
uses: docker/metadata-action@v5
40+
with:
41+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
42+
tags: |
43+
type=ref,event=branch
44+
type=ref,event=pr
45+
type=semver,pattern={{version}}
46+
type=semver,pattern={{major}}.{{minor}}
47+
type=sha,prefix=
48+
49+
- name: Build and push Docker image
50+
uses: docker/build-push-action@v6
51+
with:
52+
context: .
53+
push: true
54+
tags: ${{ steps.meta.outputs.tags }}
55+
labels: ${{ steps.meta.outputs.labels }}
56+
target: production
57+
cache-from: type=gha
58+
cache-to: type=gha,mode=max

Dockerfile

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# syntax=docker/dockerfile:1
2+
3+
# ===== Build Stage =====
4+
FROM python:3.12-slim AS builder
5+
6+
# Set environment variables
7+
ENV PYTHONDONTWRITEBYTECODE=1 \
8+
PYTHONUNBUFFERED=1 \
9+
PIP_NO_CACHE_DIR=1 \
10+
PIP_DISABLE_PIP_VERSION_CHECK=1
11+
12+
WORKDIR /app
13+
14+
# Install build dependencies
15+
RUN apt-get update && apt-get install -y --no-install-recommends \
16+
build-essential \
17+
libpq-dev \
18+
&& rm -rf /var/lib/apt/lists/*
19+
20+
# Install PDM
21+
RUN pip install pdm
22+
23+
# Copy dependency files
24+
COPY pyproject.toml pdm.lock* ./
25+
26+
# Export dependencies to requirements.txt and install
27+
# If pdm.lock exists, use it; otherwise install from pyproject.toml
28+
RUN pdm lock --prod 2>/dev/null || true && \
29+
pdm export --prod -o requirements.txt --without-hashes && \
30+
pip install --prefix=/install -r requirements.txt
31+
32+
33+
# ===== Production Stage =====
34+
FROM python:3.12-slim AS production
35+
36+
# Set environment variables
37+
ENV PYTHONDONTWRITEBYTECODE=1 \
38+
PYTHONUNBUFFERED=1 \
39+
PYTHONPATH=/app \
40+
ENV=production
41+
42+
WORKDIR /app
43+
44+
# Install runtime dependencies
45+
RUN apt-get update && apt-get install -y --no-install-recommends \
46+
libpq5 \
47+
&& rm -rf /var/lib/apt/lists/* \
48+
&& groupadd -r appgroup && useradd -r -g appgroup appuser
49+
50+
# Copy installed packages from builder
51+
COPY --from=builder /install /usr/local
52+
53+
# Copy application code
54+
COPY . .
55+
56+
# Create logs directory for gunicorn
57+
RUN mkdir -p logs && chown -R appuser:appgroup /app
58+
59+
# Switch to non-root user
60+
USER appuser
61+
62+
# Expose port
63+
EXPOSE 8000
64+
65+
# Health check
66+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
67+
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/heartbeat')" || exit 1
68+
69+
# Run the application with uvicorn
70+
CMD ["python", "-m", "uvicorn", "run:app", "--host", "0.0.0.0", "--port", "8000"]

gconfig.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,23 @@
99
debug = False
1010
reload = False
1111

12-
reload_engine = 'inotify'
12+
reload_engine = "inotify"
1313

14-
bind = '127.0.0.1:6000' # ip binding (sock is optional)
15-
pidfile = 'logs/gunicorn.pid'
14+
bind = "127.0.0.1:6000" # ip binding (sock is optional)
15+
pidfile = "logs/gunicorn.pid"
1616

1717
workers = 1
1818

19-
worker_class = 'uvicorn.workers.UvicornWorker'
19+
worker_class = "uvicorn.workers.UvicornWorker"
2020
# http://www.uvicorn.org/deployment/#running-gunicorn-worker
2121

22-
loglevel = 'debug'
23-
accesslog = 'logs/gunicorn_access.log'
24-
errorlog = 'logs/gunicorn_error.log'
22+
loglevel = "debug"
23+
accesslog = "logs/gunicorn_access.log"
24+
errorlog = "logs/gunicorn_error.log"
2525

2626
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"'
2727

2828

2929
# 执行命令
3030
# gunicorn -c gconfig.py main:app
31-
# gunicorn -c gconfig.py main:app -k uvicorn.workers.UvicornWorker
31+
# gunicorn -c gconfig.py main:app -k uvicorn.workers.UvicornWorker

0 commit comments

Comments
 (0)