diff --git a/.github/workflows/deploy-api-server.yml b/.github/workflows/deploy-api-server.yml new file mode 100644 index 00000000..67968831 --- /dev/null +++ b/.github/workflows/deploy-api-server.yml @@ -0,0 +1,52 @@ +name: Publish API server image to GHCR and deploys to Kubernetes Cluster + +on: + push: + branches: [main] + paths: + - 'api-server/**' + release: + types: [published] + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository_owner }}/exosphere-api-server + +jobs: + publish-image: + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate tags & labels + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest + type=sha,format=short + type=ref,event=tag # v1.2.3 → v1.2.3 + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: ./api-server + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/api-server/.dockerignore b/api-server/.dockerignore new file mode 100644 index 00000000..ba4cefd6 --- /dev/null +++ b/api-server/.dockerignore @@ -0,0 +1,26 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*.pyo +*.pyd +*.so + +# C extensions +*.egg +*.egg-info +.eggs + +# Virtual environments +.venv/ + +# Git, CI and editor config +.git/ +.github/ +.vscode/ + +# Logs and temp files +*.log + +# Other +.env +Dockerfile \ No newline at end of file diff --git a/api-server/Dockerfile b/api-server/Dockerfile new file mode 100644 index 00000000..0f9cb814 --- /dev/null +++ b/api-server/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.12-slim-bookworm +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ + +WORKDIR /api-server + +COPY pyproject.toml uv.lock ./ + +RUN uv sync --locked + +COPY . . + +EXPOSE 8000 + +CMD ["uv", "run", "run.py", "--mode", "production", "--workers", "4"] \ No newline at end of file diff --git a/api-server/app/main.py b/api-server/app/main.py index 7bff6228..dbab737f 100644 --- a/api-server/app/main.py +++ b/api-server/app/main.py @@ -59,8 +59,7 @@ async def lifespan(app: FastAPI): app.add_middleware(UnhandledExceptionsMiddleware) - -@app.get("/health-check") +@app.get("/health") def health() -> dict: return {"message": "OK"} diff --git a/api-server/docker-compose.yml b/api-server/docker-compose.yml new file mode 100644 index 00000000..99f7664e --- /dev/null +++ b/api-server/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.8' + +services: + api-server: + build: + context: . + dockerfile: Dockerfile + ports: + - "8000:8000" + env_file: + - .env + command: ["uv", "run", "run.py", "--mode", "production", "--workers", "2"] diff --git a/k8s/deployment.yml b/k8s/deployment.yml new file mode 100644 index 00000000..7b70a77a --- /dev/null +++ b/k8s/deployment.yml @@ -0,0 +1,41 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: exosphere-api-server +spec: + replicas: 2 + selector: + matchLabels: + app: exosphere-api-server + template: + metadata: + labels: + app: exosphere-api-server + spec: + containers: + - name: exosphere-api-server + image: ghcr.io/exospherehost/exosphere-api-server:latest + ports: + - containerPort: 8000 + envFrom: + - secretRef: + name: exosphere-api-server-secrets + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + readinessProbe: + httpGet: + path: /health + port: 8000 + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /health + port: 8000 + initialDelaySeconds: 5 + periodSeconds: 10 \ No newline at end of file diff --git a/k8s/service.yaml b/k8s/service.yaml new file mode 100644 index 00000000..2b2e255b --- /dev/null +++ b/k8s/service.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: exosphere-api-server +spec: + selector: + app: exosphere-api-server + ports: + - port: 8000 + targetPort: 8000 \ No newline at end of file