├── LICENSE ├── README.md ├── api_integration_pipeline.ipynb └── images ├── dockkuber.jpg └── fastapi.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 jax-25 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastAPI Demo Pipeline 2 | ![Docker & Kubernetes Workflow](images/fastapi.png) 3 | Hey there! 👋 I'm excited to share an end-to-end example that walks you through: 4 | 5 | 1. **Writing** a simple FastAPI server with health, GET, and POST endpoints 6 | 2. **Packaging** that server into a Docker container 7 | 3. **Pushing** the image to Docker Hub 8 | 4. **Deploying** the container on a Kubernetes cluster 9 | 5. **Inspecting** and managing your app with `kubectl` 10 | 6. (Bonus) **Automating** the whole flow with a CI/CD pipeline 11 | 12 | --- 13 | ## Pipeline Architecture 14 | 15 | ![Docker & Kubernetes Workflow](images/dockkuber.jpg) 16 | 17 | ## 📋 Prerequisites 18 | 19 | - **Python 3.11+** 20 | - **Docker** installed and running 21 | - **kubectl** CLI configured 22 | - **Kubernetes** (either Docker Desktop’s built-in or Minikube) 23 | 24 | --- 25 | 26 | ## 🚀 Getting Started 27 | 28 | ### 1. Clone this repository 29 | 30 | ```bash 31 | git clone https://github.com/jax-25/fastapi-demo.git 32 | cd fastapi-demo 33 | ``` 34 | 35 | ### 2. Run the API locally in Docker 36 | 37 | ```bash 38 | # Build the Docker image 39 | docker build -t fastapi-demo:latest . 40 | 41 | # Run the container 42 | docker run --rm -d -p 8000:8000 --name fastapi-demo fastapi-demo:latest 43 | 44 | # Check the health endpoint 45 | curl http://localhost:8000/ 46 | # → { "status": "healthy" } 47 | ``` 48 | 49 | ### 3. Push to Docker Hub 50 | 51 | 1. Create a repository on Docker Hub, e.g. `yourdockerid/fastapi-demo`. 52 | 2. Tag and push: 53 | 54 | ```bash 55 | docker tag fastapi-demo:latest yourdockerid/fastapi-demo:latest 56 | docker push yourdockerid/fastapi-demo:latest 57 | ``` 58 | 59 | --- 60 | 61 | ## ☸️ Deploying to Kubernetes 62 | 63 | 1. **Enable Kubernetes** 64 | - Docker Desktop: Settings → Kubernetes → Enable → Apply & Restart 65 | - Minikube: `minikube start` 66 | 67 | 2. **Apply manifests**: 68 | 69 | ```bash 70 | kubectl apply -f deployment.yaml 71 | kubectl apply -f service.yaml 72 | ``` 73 | 74 | 3. **Verify** your pods and service: 75 | 76 | ```bash 77 | kubectl get pods -l app=fastapi-demo 78 | kubectl get svc fastapi-demo-svc 79 | ``` 80 | 81 | 4. **Access your API**: 82 | 83 | ```bash 84 | curl http://localhost/user/111 85 | # or via NodePort/LoadBalancer external IP 86 | ``` 87 | --- 88 | 89 | ## ✨ CI/CD Automation (Bonus) 90 | 91 | You’ll find a sample GitHub Actions workflow in `.github/workflows/ci-cd.yaml` that: 92 | 93 | 1. Builds the Docker image 94 | 2. Pushes to Docker Hub 95 | 3. SSH’s into your server/cluster and triggers a rolling restart 96 | 97 | --- 98 | 99 | ## 🤝 Acknowledgments 100 | 101 | Thanks for checking this out! Feel free to tweak, fork, and make it your own. If you run into any issues, drop me a line or open an issue here on GitHub. 102 | -------------------------------------------------------------------------------- /api_integration_pipeline.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "69d6791c", 6 | "metadata": {}, 7 | "source": [ 8 | "# End-to-End FastAPI Pipeline: Code → Docker → Kubernetes\n", 9 | "\n", 10 | "Hi, I’m walking you through a full pipeline:\n", 11 | "1. **Python API**: health check, GET and POST endpoints\n", 12 | "2. **Containerization**: Dockerfile, build, run\n", 13 | "3. **Docker Hub**: create repo, tag, push\n", 14 | "4. **Kubernetes**: enable, deploy YAML, inspect\n", 15 | "\n", 16 | "All code uses dummy data so you can follow along." 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "id": "b78676ea", 22 | "metadata": {}, 23 | "source": [ 24 | "## 1. Define our FastAPI app (`server.py`)\n", 25 | "We'll create three endpoints:\n", 26 | "- `GET /` for a health check\n", 27 | "- `GET /user/{userid}` returning dummy user data\n", 28 | "- `POST /user` accepting a JSON body\n" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "id": "35b96445", 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "%%bash\n", 39 | "cat > server.py << 'EOF'\n", 40 | "from fastapi import FastAPI, HTTPException\n", 41 | "from pydantic import BaseModel\n", 42 | "\n", 43 | "app = FastAPI()\n", 44 | "\n", 45 | "# Dummy in-memory storage\n", 46 | "library = {\n", 47 | " 111: {\"name\": \"Alice\", \"date\": \"2025-04-25\", \"service\": \"DemoService\"}\n", 48 | "}\n", 49 | "\n", 50 | "class UserIn(BaseModel):\n", 51 | " name: str\n", 52 | " date: str\n", 53 | " service: str\n", 54 | "\n", 55 | "@app.get(\"/\", tags=[\"health\"])\n", 56 | "async def healthcheck():\n", 57 | " return {\"status\": \"healthy\"}\n", 58 | "\n", 59 | "@app.get(\"/user/{userid}\", tags=[\"user\"])\n", 60 | "async def get_user(userid: int):\n", 61 | " user = library.get(userid)\n", 62 | " if not user:\n", 63 | " raise HTTPException(status_code=404, detail=\"User not found\")\n", 64 | " return user\n", 65 | "\n", 66 | "@app.post(\"/user\", tags=[\"user\"])\n", 67 | "async def create_user(user: UserIn):\n", 68 | " new_id = max(library.keys()) + 1\n", 69 | " library[new_id] = user.dict()\n", 70 | " return {\"id\": new_id, **user.dict()}\n", 71 | "EOF\n", 72 | "\n", 73 | "echo \"Created server.py:\" && sed -n '1,20p' server.py" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "id": "37d6fffa", 79 | "metadata": {}, 80 | "source": [ 81 | "## 2. `requirements.txt`\n", 82 | "List our dependencies." 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "id": "79fdd70b", 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "%%bash\n", 93 | "cat > requirements.txt << 'EOF'\n", 94 | "fastapi\n", 95 | "uvicorn[standard]\n", 96 | "EOF\n", 97 | "\n", 98 | "echo \"requirements.txt:\" && cat requirements.txt" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "id": "6902f135", 104 | "metadata": {}, 105 | "source": [ 106 | "## 3. Build & Run Locally with Docker\n", 107 | "**Dockerfile**:" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "id": "fe47dfde", 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "%%bash\n", 118 | "cat > Dockerfile << 'EOF'\n", 119 | "FROM python:3.11-slim\n", 120 | "WORKDIR /app\n", 121 | "COPY requirements.txt .\n", 122 | "RUN pip install --no-cache-dir -r requirements.txt\n", 123 | "COPY server.py .\n", 124 | "CMD [\"uvicorn\", \"server:app\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\"]\n", 125 | "EOF\n", 126 | "\n", 127 | "echo \"Dockerfile:\" && sed -n '1,20p' Dockerfile" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "id": "18a91d12", 133 | "metadata": {}, 134 | "source": [ 135 | "### Build and run:" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "id": "074e7860", 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "%%bash\n", 146 | "# Build the Docker image\n", 147 | "docker build -t fastapi-demo:latest .\n", 148 | "\n", 149 | "# Run the container\n", 150 | "docker run --rm -d -p 8000:8000 --name fastapi-demo fastapi-demo:latest\n", 151 | "\n", 152 | "echo \"Container running at http://localhost:8000\"\n" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "id": "5a6aad2d", 158 | "metadata": {}, 159 | "source": [ 160 | "## 4. Push to Docker Hub\n", 161 | "1. Create a repo on Docker Hub (e.g. `yourdockerid/fastapi-demo`).\n", 162 | "\n", 163 | "2. Tag & push:" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "id": "a3524d51", 170 | "metadata": {}, 171 | "outputs": [], 172 | "source": [ 173 | "%%bash\n", 174 | "# Tag the image\n", 175 | "docker tag fastapi-demo:latest yourdockerid/fastapi-demo:latest\n", 176 | "\n", 177 | "# Push to Docker Hub\n", 178 | "docker push yourdockerid/fastapi-demo:latest\n" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "id": "1824e3ee", 184 | "metadata": {}, 185 | "source": [ 186 | "## 5. Enable Kubernetes (Docker Desktop or Minikube)\n", 187 | "- **Docker Desktop**: Settings → Kubernetes → Enable → Apply & Restart\n", 188 | "- **Minikube**: `minikube start`" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "id": "177aee50", 194 | "metadata": {}, 195 | "source": [ 196 | "## 6. Kubernetes Manifests\n", 197 | "### Deployment (`deployment.yaml`)\n" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": null, 203 | "id": "e8bef976", 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "%%bash\n", 208 | "cat > deployment.yaml << 'EOF'\n", 209 | "apiVersion: apps/v1\n", 210 | "kind: Deployment\n", 211 | "metadata:\n", 212 | " name: fastapi-demo\n", 213 | "spec:\n", 214 | " replicas: 3\n", 215 | " selector:\n", 216 | " matchLabels:\n", 217 | " app: fastapi-demo\n", 218 | " template:\n", 219 | " metadata:\n", 220 | " labels:\n", 221 | " app: fastapi-demo\n", 222 | " spec:\n", 223 | " containers:\n", 224 | " - name: fastapi-demo\n", 225 | " image: yourdockerid/fastapi-demo:latest\n", 226 | " ports:\n", 227 | " - containerPort: 8000\n", 228 | "EOF\n", 229 | "\n", 230 | "echo \"deployment.yaml:\" && sed -n '1,20p' deployment.yaml" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "id": "d8b14524", 236 | "metadata": {}, 237 | "source": [ 238 | "### Service (`service.yaml`)\n" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "id": "fbbff20b", 245 | "metadata": {}, 246 | "outputs": [], 247 | "source": [ 248 | "%%bash\n", 249 | "cat > service.yaml << 'EOF'\n", 250 | "apiVersion: v1\n", 251 | "kind: Service\n", 252 | "metadata:\n", 253 | " name: fastapi-demo-svc\n", 254 | "spec:\n", 255 | " selector:\n", 256 | " app: fastapi-demo\n", 257 | " type: LoadBalancer\n", 258 | " ports:\n", 259 | " - port: 80\n", 260 | " targetPort: 8000\n", 261 | "EOF\n", 262 | "\n", 263 | "echo \"service.yaml:\" && sed -n '1,20p' service.yaml" 264 | ] 265 | }, 266 | { 267 | "cell_type": "markdown", 268 | "id": "ad040346", 269 | "metadata": {}, 270 | "source": [ 271 | "## 7. Deploy to Kubernetes\n" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": null, 277 | "id": "933cf828", 278 | "metadata": {}, 279 | "outputs": [], 280 | "source": [ 281 | "%%bash\n", 282 | "kubectl apply -f deployment.yaml\n", 283 | "kubectl apply -f service.yaml\n", 284 | "kubectl get pods,svc\n" 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "id": "819dae1c", 290 | "metadata": {}, 291 | "source": [ 292 | "## 8. Verify & Inspect\n", 293 | "- **Check pods**: `kubectl get pods -l app=fastapi-demo`\n", 294 | "- **Logs**: `kubectl logs `\n", 295 | "- **Service endpoint**: e.g. `http://localhost/user/111` or `curl http://localhost/user`" 296 | ] 297 | }, 298 | { 299 | "cell_type": "markdown", 300 | "id": "c694e4dc", 301 | "metadata": {}, 302 | "source": [ 303 | "---\n", 304 | "This notebook walked through:\n", 305 | "1. Writing a FastAPI server with health, GET, POST endpoints\n", 306 | "2. Packaging into Docker\n", 307 | "3. Pushing to Docker Hub\n", 308 | "4. Enabling Kubernetes\n", 309 | "5. Creating Deployment & Service YAMLs\n", 310 | "6. Deploying & inspecting the running app\n", 311 | "\n", 312 | "Feel free to customize and share this end-to-end example!" 313 | ] 314 | } 315 | ], 316 | "metadata": {}, 317 | "nbformat": 4, 318 | "nbformat_minor": 5 319 | } 320 | -------------------------------------------------------------------------------- /images/dockkuber.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jax-25/E2E_FULL_API_Demo/1252bec66a1a1117473c053c7fe8de8127c6e64b/images/dockkuber.jpg -------------------------------------------------------------------------------- /images/fastapi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jax-25/E2E_FULL_API_Demo/1252bec66a1a1117473c053c7fe8de8127c6e64b/images/fastapi.png --------------------------------------------------------------------------------