Chapter 73: DevOps Projects Part 3: Docker Websites, APIs, Compose, Registry and ECR
73.3 Project 19: Dockerize a Python Flask app
Friends, Flask APIs love small Alpine images. Today we write a tiny Flask app, pin python:3.12-alpine, build, run on 5000, and prove /health with curl. Pin python:3.12-alpine. Copy requirements.txt before app code. Prove /health with curl before you open the browser.
मित्रांनो, Flask APIs छोट्या Alpine images आवडतात. आज Flask app, python:3.12-alpine, build, port 5000, curl /health.
दोस्तों, Flask APIs छोटी Alpine images पसंद करती हैं. आज Flask app, python:3.12-alpine, build, port 5000, curl /health.
What you will build in this project
- A tiny Python Flask app with /health.
- Dockerfile FROM python:3.12-alpine.
- Image flask-api:1.0.
- Container on host port 5000.
1. Start from a real company story
At Swiggy in Bengaluru, the Python Flask service was copied by hand onto several VMs. One box had the wrong runtime version. The ops lead asked every engineer to paste the exact docker images size line into the pull request before merge. That one habit cut surprise production rollbacks. The on-call chart now shows image size next to deploy time. New joiners pair on the health curl before they touch Compose.
Errors during peak cost about ₹14 lakh in delayed orders.
Meera packed the app into python:3.12-alpine. The same tag ran on every box. Rollback was one previous tag.
Today we dockerize that Python Flask app.
Ravindra Bagale's Tip
Many students forget requirements.txt and then wonder why the image has no Flask. Copy requirements before the app code. Keep that habit. Write every command in your notebook. When something fails, copy the exact error line before you change anything else. Measure twice, change once. Never forget requirements.txt. When pip fails, read the exact package line first. Keep a notebook of image sizes after every successful build.
खूप students requirements.txt विसरतात. Image मध्ये Flask नसतो. requirements आधी COPY करा. सवय ठेवा. प्रत्येक command वहीमध्ये लिहा. चूक झाली तर आधी exact error कॉपी करा. दोनदा मोजा, एकदा बदला.
बहुत students requirements.txt भूल जाते हैं. Image में Flask नहीं होता. requirements पहले COPY करें. आदत रखिए. हर command कॉपी में लिखें. गलती हो तो पहले exact error कॉपी करें. दो बार मापें, एक बार बदलें.
2. Start and prepare
Step 1: Make project folder
Why: We need a clean folder for the Python Flask app. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: mkdir and cd. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 1: mkdir
Type this:
mkdir -p /tmp/flask-api && cd /tmp/flask-api && echo ready
You will see:
ready
What it means: Folder.
Example 2: pwd
Type this:
pwd
You will see:
/tmp/flask-api
What it means: In place.
Example 3: empty
Type this:
ls | wc -l
You will see:
0
What it means: Fresh.
Step 2: Write app files
Why: The Python Flask files are the product we ship. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: printf the source files. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 4: req
Type this:
printf '%s\n' 'flask==3.0.3' > requirements.txt && cat requirements.txt
You will see:
flask==3.0.3
What it means: Pinned Flask.
Example 5: app
Type this:
printf '%s\n' 'from flask import Flask, jsonify' 'app = Flask(__name__)' '@app.get("/health")' 'def health():' ' return jsonify(ok=True)' '@app.get("/api/hello")' 'def hello():' ' return jsonify(msg="order-api")' 'if __name__ == "__main__":' ' app.run(host="0.0.0.0", port=5000)' > app.py && wc -l app.py
You will see:
11 app.py
What it means: Tiny Flask.
Example 6: grep
Type this:
grep health app.py
You will see:
def health
What it means: Route.
Step 3: Local syntax check
Why: Broken syntax must fail before Docker. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: Language check command. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 7: pycheck
Type this:
python3 -c 'import ast;ast.parse(open("app.py").read());print("syntax_ok")'
You will see:
syntax_ok
What it means: Parses.
Example 8: ver
Type this:
python3 --version
You will see:
Python 3.12.x
What it means: Near image.
Example 9: req ok
Type this:
test -f requirements.txt && echo req_ok
You will see:
req_ok
What it means: Present.
3. Build the main pieces
Step 4: Write Dockerfile
Why: Dockerfile pins python:3.12-alpine. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: FROM pin + COPY + CMD. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 10: df
Type this:
printf '%s\n' 'FROM python:3.12-alpine' 'WORKDIR /app' 'COPY requirements.txt .' 'RUN pip install --no-cache-dir -r requirements.txt' 'COPY app.py .' 'EXPOSE 5000' 'CMD ["python","app.py"]' > /tmp/flask-api/Dockerfile && head -3 /tmp/flask-api/Dockerfile
You will see:
FROM python:3.12-alpine
WORKDIR /app
COPY requirements.txt .
What it means: Dockerfile written.
Example 11: from
Type this:
head -1 /tmp/flask-api/Dockerfile
You will see:
FROM python:3.12-alpine
What it means: Pinned base.
Example 12: ls
Type this:
ls /tmp/flask-api/Dockerfile
You will see:
/tmp/flask-api/Dockerfile
What it means: Present.
Step 5: Write dockerignore
Why: Context must stay lean. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: .dockerignore lists junk. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 13: ignore
Type this:
printf '%s\n' '.git' '*.md' '__pycache__' 'node_modules' 'target' 'bin' 'obj' > /tmp/flask-api/.dockerignore && cat /tmp/flask-api/.dockerignore
You will see:
.git
*.md
__pycache__
node_modules
target
bin
obj
What it means: Lean context.
Example 14: count
Type this:
wc -l /tmp/flask-api/.dockerignore
You will see:
7 /tmp/flask-api/.dockerignore
What it means: Seven lines.
Example 15: mode
Type this:
stat -c '%a' /tmp/flask-api/.dockerignore
You will see:
644
What it means: Safe.
Step 6: Build the image
Why: build creates the image layers. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: docker build -t flask-api:1.0. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 16: build
Type this:
docker build -t flask-api:1.0 /tmp/flask-api
You will see:
Successfully tagged flask-api:1.0
What it means: Built.
Example 17: images
Type this:
docker images flask-api --format '{{.Repository}}:{{.Tag}}'
You will see:
flask-api:1.0
What it means: Listed.
Example 18: hist
Type this:
docker history flask-api:1.0 | head -3
You will see:
IMAGE CREATED CREATED BY SIZE
What it means: Layers.
4. Automate and verify
Step 7: Run the container
Why: run starts a live container. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: docker run -d --name flask-api. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 19: run
Type this:
docker rm -f flask-api >/dev/null 2>&1; docker run -d --name flask-api -p 5000:5000 flask-api:1.0 && echo started
You will see:
started
What it means: Detached.
Example 20: ps
Type this:
docker ps --filter name=flask-api --format '{{.Names}} {{.Ports}}'
You will see:
flask-api 0.0.0.0:5000->5000/tcp
What it means: Mapped.
Example 21: sleep
Type this:
sleep 2; echo up
You will see:
up
What it means: Warmup.
Step 8: Map the port
Why: Host port 5000 reaches the app. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: -p 5000:5000. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 22: port
Type this:
ss -ltn | grep ':5000 ' | head -1 || echo listening_via_docker
You will see:
LISTEN ... :5000
What it means: Port open.
Example 23: fmt
Type this:
docker port flask-api
You will see:
5000/tcp -> 0.0.0.0:5000
What it means: Publish map.
Example 24: names
Type this:
docker inspect -f '{{.Name}}' flask-api
You will see:
/flask-api
What it means: Name.
Step 9: curl health
Why: /health proves ready. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: curl returns 200. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 25: health
Type this:
curl -s http://127.0.0.1:5000/health
You will see:
{"ok":true}
What it means: Health.
Example 26: code
Type this:
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:5000/health
You will see:
200
What it means: OK.
Example 27: hello
Type this:
curl -s http://127.0.0.1:5000/api/hello
You will see:
{"msg":"order-api"}
What it means: Route.
5. Harden and accept
Step 10: Read logs
Why: Logs show listen lines. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: docker logs tail. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 28: logs
Type this:
docker logs flask-api 2>&1 | tail -5
You will see:
listen or Started
What it means: App logged.
Example 29: follow
Type this:
echo 'docker logs -f flask-api'
You will see:
docker logs -f flask-api
What it means: Follow tip.
Example 30: inspect
Type this:
docker inspect -f '{{.State.Status}}' flask-api
You will see:
running
What it means: Running.
Step 11: Exec inside
Why: exec is for quick checks. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: docker exec. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 31: exec
Type this:
docker exec flask-api sh -c 'echo inside'
You will see:
inside
What it means: Shell works.
Example 32: pwd
Type this:
docker exec flask-api pwd
You will see:
/app
What it means: WORKDIR.
Example 33: id
Type this:
docker exec flask-api id | head -c 40; echo
You will see:
uid=...
What it means: User inside.
Step 12: Acceptance checklist
Why: Meera signs off when these pass. We do this before the next step so mistakes stay small and easy to undo. Read the output; the names and numbers tell you if the machine is healthy.
What: All green checks. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 34: smoke
Type this:
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:5000/health
You will see:
200
What it means: Smoke.
Example 35: mode
Type this:
stat -c '%a' /tmp/flask-api/Dockerfile
You will see:
644
What it means: Safe.
Example 36: Sign-off
Type this:
echo 'Meera: PASS'
You will see:
Meera: PASS
What it means: Done.
Check yourself
Q1. Base image?
python:3.12-alpine
Q2. Host port?
5000
Q3. Health path?
/health
Q4. Who fixed Swiggy?
Meera
Q5. Build command?
docker build
Q6. Why pin?
So builds stay repeatable
Friends, you dockerized a Python Flask app with a pinned image. Did you understand? Let us try it once more. Change the health text, rebuild as 1.1, and switch the container.
मित्रांनो, तुम्ही pinned image ने Python Flask app dockerize केली. health बदलून 1.1 rebuild करा.
दोस्तों, आपने pinned image से Python Flask app dockerize की. health बदल 1.1 rebuild करें.
Lab: try at home
- Write the app files.
- Write Dockerfile with pinned tag.
- Build 1.0.
- Run and curl.
- Tag 1.1 after a text change.
Recap
- Pin base image tags.
- COPY only what you need.
- -p maps host to container.
- Same image on every host.
- Logs prove listen.