Skip to content

Commit 4e8ee0d

Browse files
committed
deploy to AWS
1 parent 4947acc commit 4e8ee0d

26 files changed

Lines changed: 897 additions & 93 deletions

.dockerignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
venv/
2+
__pycache__/
3+
*.pyc
4+
*.pyo
5+
*.pyd
6+
.Python
7+
*.so
8+
*.egg
9+
*.egg-info/
10+
dist/
11+
build/
12+
.git/
13+
.gitignore
14+
.vscode/
15+
.idea/
16+
*.md
17+
.env
18+
.DS_Store
19+
logs/
20+
mlruns/
21+
*.log
22+
research/
23+
artifacts/data_ingestion/Chest-CT-Scan-data/

.github/workflows/.gitkeep

Whitespace-only changes.

.github/workflows/main.yaml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: CI/CD Pipeline
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
env:
9+
AWS_REGION: us-east-1
10+
ECR_REPOSITORY: chest-cancer-classifier
11+
ECS_SERVICE: chest-cancer-service
12+
ECS_CLUSTER: chest-cancer-cluster
13+
ECS_TASK_DEFINITION: chest-cancer-task
14+
CONTAINER_NAME: chest-cancer-app
15+
16+
jobs:
17+
build-and-deploy:
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v3
23+
24+
- name: Configure AWS credentials
25+
uses: aws-actions/configure-aws-credentials@v2
26+
with:
27+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
28+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
29+
aws-region: ${{ env.AWS_REGION }}
30+
31+
- name: Login to Amazon ECR
32+
id: login-ecr
33+
uses: aws-actions/amazon-ecr-login@v1
34+
35+
- name: Build, tag, and push image to Amazon ECR
36+
id: build-image
37+
env:
38+
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
39+
IMAGE_TAG: ${{ github.sha }}
40+
run: |
41+
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
42+
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:latest .
43+
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
44+
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
45+
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
46+
47+
- name: Download task definition
48+
run: |
49+
aws ecs describe-task-definition --task-definition ${{ env.ECS_TASK_DEFINITION }} \
50+
--query taskDefinition > task-definition.json
51+
52+
- name: Fill in the new image ID in the Amazon ECS task definition
53+
id: task-def
54+
uses: aws-actions/amazon-ecs-render-task-definition@v1
55+
with:
56+
task-definition: task-definition.json
57+
container-name: ${{ env.CONTAINER_NAME }}
58+
image: ${{ steps.build-image.outputs.image }}
59+
60+
- name: Deploy Amazon ECS task definition
61+
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
62+
with:
63+
task-definition: ${{ steps.task-def.outputs.task-definition }}
64+
service: ${{ env.ECS_SERVICE }}
65+
cluster: ${{ env.ECS_CLUSTER }}
66+
wait-for-service-stability: true
67+
68+
- name: Deployment successful
69+
run: echo "Deployment completed successfully!"

Dockerfile

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
FROM python:3.10-slim
2+
3+
WORKDIR /app
4+
5+
# Install system dependencies
6+
RUN apt-get update && apt-get install -y \
7+
libgomp1 \
8+
awscli \
9+
&& rm -rf /var/lib/apt/lists/*
10+
11+
# Copy requirements and install dependencies
12+
COPY requirements.txt .
13+
RUN pip install --no-cache-dir -r requirements.txt
14+
15+
# Copy application code
16+
COPY . .
17+
18+
# Create necessary directories
19+
RUN mkdir -p uploads model
20+
21+
# Environment variables (can be overridden at runtime)
22+
ENV PORT=8000 \
23+
PYTHONUNBUFFERED=1
24+
25+
# Expose port
26+
EXPOSE 8000
27+
28+
# Health check
29+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
30+
CMD python -c "import requests; requests.get('http://localhost:8000/health', timeout=5)" || exit 1
31+
32+
# Run the application
33+
CMD ["python", "app.py"]

app.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import os
2+
from dotenv import load_dotenv
3+
from fastapi import FastAPI, File, UploadFile, HTTPException
4+
from fastapi.responses import HTMLResponse
5+
from fastapi.middleware.cors import CORSMiddleware
6+
from pathlib import Path
7+
import uvicorn
8+
9+
from src.cnnClassifier.pipeline.prediction import PredictionPipeline
10+
11+
# Load environment variables
12+
load_dotenv()
13+
14+
15+
app = FastAPI(title="Chest Cancer Classification API")
16+
17+
app.add_middleware(
18+
CORSMiddleware,
19+
allow_origins=["*"],
20+
allow_credentials=True,
21+
allow_methods=["*"],
22+
allow_headers=["*"],
23+
)
24+
25+
UPLOAD_DIR = Path("uploads")
26+
UPLOAD_DIR.mkdir(exist_ok=True)
27+
28+
29+
@app.get("/", response_class=HTMLResponse)
30+
async def home():
31+
return Path("templates/index.html").read_text(encoding="utf-8")
32+
33+
34+
@app.get("/health")
35+
async def health_check():
36+
return {"status": "healthy", "service": "chest-cancer-classifier"}
37+
38+
39+
@app.post("/predict")
40+
async def predict(file: UploadFile = File(...)):
41+
if not file.content_type.startswith("image/"):
42+
raise HTTPException(status_code=400, detail="File must be an image")
43+
44+
file_path = UPLOAD_DIR / file.filename
45+
46+
try:
47+
with open(file_path, "wb") as f:
48+
f.write(await file.read())
49+
50+
pipeline = PredictionPipeline(filename=str(file_path))
51+
result = pipeline.predict()
52+
53+
return {
54+
"success": True,
55+
"prediction": result[0]["image"],
56+
"filename": file.filename
57+
}
58+
finally:
59+
if file_path.exists():
60+
os.remove(file_path)
61+
62+
63+
if __name__ == "__main__":
64+
port = int(os.environ.get("PORT", 8000))
65+
uvicorn.run(app, host="0.0.0.0", port=port)

config/config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@ prepare_base_model:
2020
training:
2121
root_dir: artifacts/training
2222
trained_model_path: artifacts/training/model.keras
23+
train_data: artifacts/data_ingestion/train
24+
test_data: artifacts/data_ingestion/test
2325

dvc.lock

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
schema: '2.0'
2+
stages:
3+
data_ingestion:
4+
cmd: python -m cnnClassifier.pipeline.stage_01_data_ingestion
5+
deps:
6+
- path: config/config.yaml
7+
hash: md5
8+
md5: 1f94bb6f29dc9091d2a0fcd50f2ae881
9+
size: 691
10+
- path: src/cnnClassifier/components/data_ingestion.py
11+
hash: md5
12+
md5: d0f692cf71607b61fab0fd8d6479fb76
13+
size: 6736
14+
- path: src/cnnClassifier/pipeline/stage_01_data_ingestion.py
15+
hash: md5
16+
md5: 474edaff6c257c86a467bbd09571a4dc
17+
size: 969
18+
params:
19+
params.yaml:
20+
AUGMENTATION: true
21+
BATCH_SIZE: 16
22+
CLASSES: 2
23+
EPOCHS: 10
24+
IMAGE_SIZE:
25+
- 224
26+
- 224
27+
- 3
28+
INCLUDE_TOP: false
29+
LEARNING_RATE: 0.001
30+
WEIGHTS: imagenet
31+
outs:
32+
- path: artifacts/data_ingestion/test
33+
hash: md5
34+
md5: 4cbca7f0d44825590faa02d59aca4120.dir
35+
size: 12816665
36+
nfiles: 94
37+
- path: artifacts/data_ingestion/train
38+
hash: md5
39+
md5: fc92da05297099a4e3766485d2f13ab8.dir
40+
size: 51220382
41+
nfiles: 372
42+
prepare_base_model:
43+
cmd: python -m cnnClassifier.pipeline.stage_02_prepare_base_model
44+
deps:
45+
- path: config/config.yaml
46+
hash: md5
47+
md5: 1f94bb6f29dc9091d2a0fcd50f2ae881
48+
size: 691
49+
- path: params.yaml
50+
hash: md5
51+
md5: 1172e0eac94614345bdf0e8a39a5f430
52+
size: 148
53+
- path: src/cnnClassifier/components/prepare_base_model.py
54+
hash: md5
55+
md5: e31b038d04ac4c2f35fbe610c1913c2e
56+
size: 5269
57+
- path: src/cnnClassifier/pipeline/stage_02_prepare_base_model.py
58+
hash: md5
59+
md5: 18d0099f2ba9406668959a43e76afd56
60+
size: 970
61+
params:
62+
params.yaml:
63+
CLASSES: 2
64+
IMAGE_SIZE:
65+
- 224
66+
- 224
67+
- 3
68+
INCLUDE_TOP: false
69+
LEARNING_RATE: 0.001
70+
WEIGHTS: imagenet
71+
outs:
72+
- path: artifacts/prepare_base_model/base_model.keras
73+
hash: md5
74+
md5: 3ef3af558322b34c1fe91016b2b00bac
75+
size: 17025246
76+
- path: artifacts/prepare_base_model/base_model_updated.keras
77+
hash: md5
78+
md5: a8eb8d210d7c461c2844d92b231d3d8a
79+
size: 17073404
80+
training:
81+
cmd: python -m cnnClassifier.pipeline.stage_03_model_trainer
82+
deps:
83+
- path: artifacts/data_ingestion/train
84+
hash: md5
85+
md5: fc92da05297099a4e3766485d2f13ab8.dir
86+
size: 51220382
87+
nfiles: 372
88+
- path: artifacts/prepare_base_model/base_model_updated.keras
89+
hash: md5
90+
md5: a8eb8d210d7c461c2844d92b231d3d8a
91+
size: 17073404
92+
- path: config/config.yaml
93+
hash: md5
94+
md5: 1f94bb6f29dc9091d2a0fcd50f2ae881
95+
size: 691
96+
- path: params.yaml
97+
hash: md5
98+
md5: 1172e0eac94614345bdf0e8a39a5f430
99+
size: 148
100+
- path: src/cnnClassifier/components/model_trainer.py
101+
hash: md5
102+
md5: bb9cd22d749846c55314a57398470b8d
103+
size: 9547
104+
- path: src/cnnClassifier/pipeline/stage_03_model_trainer.py
105+
hash: md5
106+
md5: b10a80281f4f661f0b4533ff8bf668bb
107+
size: 885
108+
params:
109+
params.yaml:
110+
AUGMENTATION: true
111+
BATCH_SIZE: 16
112+
EPOCHS: 10
113+
IMAGE_SIZE:
114+
- 224
115+
- 224
116+
- 3
117+
LEARNING_RATE: 0.001
118+
outs:
119+
- path: artifacts/training/best_model_checkpoint.keras
120+
hash: md5
121+
md5: c63394a3e8abdb1750b208049ee5e96c
122+
size: 17108695
123+
- path: artifacts/training/logs
124+
hash: md5
125+
md5: 0d018a6d430109ff1b243d0716728642.dir
126+
size: 2024095
127+
nfiles: 2
128+
- path: artifacts/training/model.keras
129+
hash: md5
130+
md5: 25584884c8412db18d58031086129399
131+
size: 17108695
132+
evaluation:
133+
cmd: python -m cnnClassifier.pipeline.stage_04_model_evaluation
134+
deps:
135+
- path: artifacts/data_ingestion/test
136+
hash: md5
137+
md5: 4cbca7f0d44825590faa02d59aca4120.dir
138+
size: 12816665
139+
nfiles: 94
140+
- path: artifacts/training/model.keras
141+
hash: md5
142+
md5: 25584884c8412db18d58031086129399
143+
size: 17108695
144+
- path: config/config.yaml
145+
hash: md5
146+
md5: 1f94bb6f29dc9091d2a0fcd50f2ae881
147+
size: 691
148+
- path: params.yaml
149+
hash: md5
150+
md5: 1172e0eac94614345bdf0e8a39a5f430
151+
size: 148
152+
- path: src/cnnClassifier/components/model_evaluation_mlflow.py
153+
hash: md5
154+
md5: c5a5f5cee1993345e8c36d35391ad424
155+
size: 8553
156+
- path: src/cnnClassifier/pipeline/stage_04_model_evaluation.py
157+
hash: md5
158+
md5: 95ec21ad5922a76fdf80f2249d987683
159+
size: 882
160+
params:
161+
params.yaml:
162+
BATCH_SIZE: 16
163+
IMAGE_SIZE:
164+
- 224
165+
- 224
166+
- 3
167+
outs:
168+
- path: scores.json
169+
hash: md5
170+
md5: ff4e4ce18f87f8abf15b19cca8f1c5fb
171+
size: 80

0 commit comments

Comments
 (0)