From 608992ff300f02116ff818c5ab4a717b31dce3f5 Mon Sep 17 00:00:00 2001 From: ja'far shodiq Date: Mon, 30 Jun 2025 17:20:37 +0700 Subject: [PATCH] deploy to cloud run --- .dockerignore | 61 +++++++++++++++++++++++++++++++++ Dockerfile | 46 +++++++++++++++++++++++++ __pycache__/app.cpython-38.pyc | Bin 12128 -> 0 bytes app.py | 3 +- cloudrun.yaml | 27 +++++++++++++++ deploy.sh | 27 +++++++++++++++ requirements_docker.txt | 29 ++++++++++++++++ 7 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 .dockerignore create mode 100644 Dockerfile delete mode 100644 __pycache__/app.cpython-38.pyc create mode 100644 cloudrun.yaml create mode 100644 deploy.sh create mode 100644 requirements_docker.txt diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7731813 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,61 @@ +# Git +.git +.gitignore + +# Python +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +env +venv +.venv +pip-log.txt +pip-delete-this-directory.txt +.tox +.coverage +.coverage.* +.pytest_cache +nosetests.xml +coverage.xml +*.cover +*.log +.mypy_cache +.dmypy.json +dmypy.json + +# IDE +.vscode +.idea +*.swp +*.swo +*~ + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Documentation +README.md +TUTORIAL.txt + +# Development files +circle.png + +# Node modules (if any) +node_modules +npm-debug.log* + +# Vercel +vercel.json + +# Large static files that can be re-generated +static/files/*.pkl +static/files/*.png +static/files/*.csv diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ed7dbaf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,46 @@ +# Use Python 3.8 slim image +FROM python:3.8-slim + +# Set working directory +WORKDIR /app + +# Install system dependencies required for ML libraries +RUN apt-get update && apt-get install -y \ + gcc \ + g++ \ + wget \ + && rm -rf /var/lib/apt/lists/* + +# Upgrade pip to version 25 (latest available) +RUN pip install --upgrade pip + +# Copy requirements first for better caching +COPY requirements_docker.txt . + +# Install Python dependencies +RUN pip install --no-cache-dir -r requirements_docker.txt + +# Download NLTK data +RUN python -c "import nltk; nltk.download('punkt'); nltk.download('stopwords')" + +# Copy application code +COPY . . + +# Create necessary directories for file uploads and static files +RUN mkdir -p static/files + +# Set environment variables +ENV FLASK_APP=app.py +ENV FLASK_ENV=production +ENV PYTHONUNBUFFERED=1 + +# Expose port +EXPOSE 8080 + +# Create non-root user for security +RUN useradd --create-home --shell /bin/bash app \ + && chown -R app:app /app +USER app + +# Command to run the application +CMD ["python", "-m", "gunicorn", "--bind", "0.0.0.0:8080", "--workers", "1", "--timeout", "120", "app:app"] diff --git a/__pycache__/app.cpython-38.pyc b/__pycache__/app.cpython-38.pyc deleted file mode 100644 index 9f023c41e5be8b9c67d3c695622b3e7fcfd399c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12128 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 diff --git a/app.py b/app.py index 83e065d..606e207 100644 --- a/app.py +++ b/app.py @@ -433,4 +433,5 @@ def visualisasi(): return render_template('visualisasi.html') if __name__ == "__main__": - app.run(debug=True) \ No newline at end of file + port = int(os.environ.get("PORT", 8080)) + app.run(host="0.0.0.0", port=port, debug=False) \ No newline at end of file diff --git a/cloudrun.yaml b/cloudrun.yaml new file mode 100644 index 0000000..c2003ae --- /dev/null +++ b/cloudrun.yaml @@ -0,0 +1,27 @@ +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + annotations: + run.googleapis.com/ingress: all + run.googleapis.com/execution-environment: gen2 + name: flask-sentiment-analysis +spec: + template: + metadata: + annotations: + run.googleapis.com/execution-environment: gen2 + run.googleapis.com/cpu-throttling: "false" + spec: + containerConcurrency: 1000 + timeoutSeconds: 300 + containers: + - image: gcr.io/PROJECT_ID/flask-sentiment-analysis + ports: + - containerPort: 8080 + resources: + limits: + memory: 2Gi + cpu: 1000m + env: + - name: PORT + value: "8080" diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..ae9b712 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Set your project variables +PROJECT_ID="your-gcp-project-id" +SERVICE_NAME="flask-sentiment-analysis" +REGION="asia-southeast1" # Singapore region (closest to Indonesia) + +echo "Building Docker image..." +docker build -t gcr.io/$PROJECT_ID/$SERVICE_NAME . + +echo "Pushing image to Google Container Registry..." +docker push gcr.io/$PROJECT_ID/$SERVICE_NAME + +echo "Deploying to Cloud Run..." +gcloud run deploy $SERVICE_NAME \ + --image gcr.io/$PROJECT_ID/$SERVICE_NAME \ + --platform managed \ + --region $REGION \ + --allow-unauthenticated \ + --memory 2Gi \ + --cpu 1 \ + --timeout 300 \ + --max-instances 10 \ + --port 8080 + +echo "Deployment completed!" +echo "Your app should be available at the URL shown above." diff --git a/requirements_docker.txt b/requirements_docker.txt new file mode 100644 index 0000000..0509bc8 --- /dev/null +++ b/requirements_docker.txt @@ -0,0 +1,29 @@ +# Core Flask dependencies +Flask==2.3.3 +gunicorn==21.2.0 +Werkzeug==2.3.7 + +# Machine Learning & Data Processing +scikit-learn==1.3.0 +pandas==2.0.3 +numpy==1.24.3 +nltk==3.8.1 +textblob==0.17.1 + +# Indonesian NLP +Sastrawi==1.0.1 + +# Social Media API +tweepy==4.14.0 + +# Translation +googletrans==4.0.0rc1 + +# Image Processing & Visualization +matplotlib==3.7.2 +wordcloud==1.9.2 +Pillow==10.0.0 + +# Utilities +requests==2.31.0 +urllib3==2.0.4