I am getting an error while trying to run my ml model API in docker container and it says
Error: ' ' is not a valid port number.
This is my docker file:
FROM python:3.10
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
EXPOSE $PORT
CMD gunicorn --workers=4 --bind 0.0.0.0:$PORT app:app
and this is my app.py file:
from flask import Flask, render_template, request
import pickle
import numpy as np
from sklearn.preprocessing import StandardScaler
model=pickle.load(open('model.pkl','rb'))
app = Flask(__name__)
#app.route("/")
def main():
return render_template("index.html")
#app.route("/predict", methods = ['POST'])
def predict():
int_features=[int(x) for x in request.form.values()]
final=[np.array(int_features)]
prediction=model.predict(final)
return render_template('index.html', prediction_text='Final report: {}'.format(prediction))
if __name__ == " __main__ ":
app.run(debug=True)
and if possible, please throw some light on EXPOSE and CMD keywords used in dockerfile
You have to define the value for PORT variable.
ENV PORT=8080
Related
I am building a back-end service for a full-stack application using fastAPI and unicorn.
src/asgi.py
import uvicorn
from src import create_app
app = create_app()
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", log_level="info", reload=True)
src/init.py
from fastapi import FastAPI
from src.api.v1.auth import auth_router
from src.core.config import *
def create_app() -> FastAPI:
root_app = FastAPI()
root_app.include_router(
auth_router,
prefix="/api/v1",
tags=["auth"],
)
return root_app
Dockerfile
FROM python:3.9
RUN mkdir /app
WORKDIR /app
RUN apt update && \
apt install -y postgresql-client
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
How I am building and running:
docker build -t travian-back:v1 .
travian-back:v1 uvicorn asgi:app
There is no error at all, server is up at http://127.0.0.1:8000
Now I am trying to directly add the uvicorn asgi:app command to my Dockerfile. The reason is because I am going to use docker-compose at the end and it would be easier. This is what I have now:
Dockerfile
RUN mkdir /app
WORKDIR /app
RUN apt update && \
apt install -y postgresql-client
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "asgi:app"]
Now instead of doing travian-front:v1 uvicorn asgi:app I am doing travian-back:v1 uvicorn asgi:app , I have no error when building and running my docker image but the server can't be reached at http://127.0.0.1:8000
The thing is that you don't run the asgi file as main, since you use uvicorn to point to it. So it's not listening on 0.0.0.0 or better put, all those options are ignored.
Either invoke the asgi file directly, which I would not recommend, or drop the asgi file and use uvicorn with the --factory flag and point it to your app factory.
ENTRYPOINT ["uvicorn", "src.init:create_app", "--factory", "--host 0.0.0.0"]
I am using entrypoint here so that you can pass additional flags such as log level on run without overriding this.
docker run -p 8000:8000 myapp --log-level warning
That said, I am somewhat confused by your file name init.py. Do you mean __init__.py? If so I would not put the factory in this file, __init__.py is not meant to be used like this. Put it in a file named main.py or similar.
I have a flask api with an endpoint that creates a pdf invoice file. I use webbrowser.open_new(filename) to open the pdf in browser, and that works fine when I run it outside the container. But if run it with docker it doesn't work. Here are my files:
app.py
from flask import Flask, jsonify, request
from api_pdf import InvoicePDF
from flask_restful import Api
app = Flask(__name__)
api = Api(app)
invoice_pdf_routes = [f'/hello']
api.add_resource(InvoicePDF, *invoice_pdf_routes)
if __name__ == '__main__':
app.run(debug=True)
api_invoice.py
from flask_restful import Resource
from flask import Flask, jsonify, request
from datetime import datetime
import pdfkit
import webbrowser
class InvoicePDF(Resource):
def create_pdf(self):
pdf_content = """<html> <head><body>Invoice</body></html>"""
invoice_date = str(datetime.now().strftime('%m-%d-%Y'))
filename = "invoice_" + invoice_date + '.pdf'
options = {
"enable-local-file-access": ""
}
pdfkit.from_string(pdf_content, filename, options=options)
webbrowser.open_new_tab(filename)
return filename
def get(self):
self.create_pdf()
return True
Dockerfile
FROM python:3.9
ENV WERKZEUNG_RUN_MAIN=true \
PYTHONUNBUFFERED=1 \
PYTHONIOENCODING=UTF-8 \
FLASK_RUN_HOST=0.0.0.0 \
FLASK_APP=app.py
WORKDIR /app
COPY . /app/
RUN apt update && \
apt upgrade -y && \
apt-get install wkhtmltopdf -y && \
pip install --no-cache-dir -r requirements.txt
EXPOSE 5000
CMD ["flask", "run"]
requirements.txt
flask>=1.1.2,<2.0.0
flask-restful
pdfkit
markupsafe==2.0.1
What I am doing:
docker build -t test .
docker run -p 5000:5000 test
and on postman I do:
GET http://127.0.0.1:5000/hello
expected result:
request return True and a new browser tab open with invoice pdf file
what happens:
request return True but new browser tab doesn't open
Why does this not work inside the container? Should I be using something other than webbrowser.open_new(filename) or am I going about this completely wrong? I don't want to save the pdf in a DB and have to call that.
I have very simple flask project with just one end point that I was able to deploy into AWS using Elastic Beanstalk
The only exposed end point goes to S3 retrieves a csv file and publish data in raw format, this configuration is working fine, so I know the roles and permissions at elastic beanstalk work correctly to reach the S3 bucket.
from flask import Flask
from flask_restful import Resource, Api
import io
import boto3
import pandas as pd
application = Flask(__name__)
api = Api(application)
s3 = boto3.resource('s3')
bucket = 'my_bucket'
key = 'my_file.csv'
class Home(Resource):
def get(self):
try:
s3 = boto3.resource('s3')
obj = s3.Object(bucket, key).get()['Body']
data = pd.read_csv(io.BytesIO(obj.read()))
print("S3 Object loaded.")
except:
print("S3 Object could not be opened.")
print(data)
csv = data.to_csv(index=False)
return csv
#End points definition and application raise up
api.add_resource(Home, '/')
if __name__ == '__main__':
application.run(host='0.0.0.0')
Now I'm trying to move that to a container so I created a Dockerfile to encapsulate the minimal app:
# syntax=docker/dockerfile:1
FROM python:3.8-slim-buster
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY . .
CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]
Since I don't have additional volumes or anything extra my Dockerrun.aws.json is barely empty
{
"AWSEBDockerrunVersion": "1"
}
I'm missing something to procure the access to the S3 bucket from inside the container?
While debugging I figured out that I was not exposing the port at the dockerfile and therefore it was not able to deploy the container correctly. I also added python as an entry point and the script name as the cmd.
Finally after some investigation also realized that the container inherits all role permissions that the host has so there is not need to do any additional task
My docker file end up looking like this:
# syntax=docker/dockerfile:1
FROM python:3.8-slim-buster
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
EXPOSE 5000
COPY . .
ENTRYPOINT [ "python" ]
CMD [ "app.py" ]
Disclaimer: I am pretty green with Docker and gunicorn.
I am running a Flask application inside a docker container. I am running the following:
docker run --runtime=nvidia -it my-image:0.1
and in the logs:
[2019-09-18 18:02:21 +0000] [9] [INFO] Booting worker with pid: 9
usage: gunicorn [-h] [--model MODEL] [--cam_id CAM_ID] [--cam_width
CAM_WIDTH]
[--cam_height CAM_HEIGHT] [--scale_factor SCALE_FACTOR]
[--notxt]
gunicorn: error: unrecognized arguments: -b :8080 main:app
I know this question has been asked multiple times (here, here, and here for example) but none of the answers seemed to help.
Here is the content of my Dockerfile:
FROM gcr.io/deeplearning-platform-release/pytorch-gpu.1-1
RUN apt-get update
ADD . /app
WORKDIR /app
RUN pip install --no-cache-dir -r requirements.txt
ENTRYPOINT ["gunicorn", "-b", ":8080", "main:app"]
and main.py looks like this:
from flask import Flask, request
import model
app = Flask(__name__)
#app.route('/getduration', methods=['POST'])
def get_duration():
try:
video_url = request.args.get('video')
except:
video_url = None
try:
duration = model.run(video_url)
result = str(duration)
return result, 200
except Exception as e:
error = f"There was an error: {e}"
return error, 500
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080, debug=True)
I've tried creating a wsgi.py file containing:
from main import app
if __name__=="__main__":
app.run()
then changed my Dockerfile entrypoint:
ENTRYPOINT ["gunicorn", "-b", ":8080", "wsgi:app"]
What am I missing?
Note that if I just run python main.py inside my container, my flask app works fine. When I changed my Dockerfile entrypoint to CMD ["python", "main.py"], the server runs, but I don't get any response when I make requests.
I want to run a nltk service in docker. However I always get the error message "'nltk' is not a package". Are you able to figure out what is going wrong? During build everything works fine, the nltk version is printed. When starting the container with docker-compose up nltk I get
$ docker-compose up nltk
Recreating nltk
Attaching to nltk
nltk | Traceback (most recent call last):
nltk | File "/var/www/nltk.py", line 1, in <module>
nltk | from nltk.corpus import brown
nltk | File "/var/www/nltk.py", line 1, in <module>
nltk | from nltk.corpus import brown
nltk | ModuleNotFoundError: No module named 'nltk.corpus'; 'nltk' is not a package
docker-compose.yml
nltk:
build: docker/nltk
container_name: nltk
volumes:
- ./volumes/nltk/var/www/nltk.py:/var/www/nltk.py
environment:
HOME: /var/www
Dockerfile
FROM python:3.6
RUN mkdir /var/www
ENV HOME /var/www
WORKDIR /var/www
RUN pip install -U nltk
RUN pip install -U numpy
RUN python -m nltk.downloader -d $HOME/nltk_data all
RUN python -c "import nltk"
RUN python -c "import nltk; print(nltk.__version__)"
EXPOSE 80
CMD [ "python", "/var/www/nltk.py" ]
nltk.py
import nltk
from nltk.corpus import brown
brown.words()
final Dockerfile
FROM python:3.6
ENV NLTK_DATA /usr/share/nltk_data
RUN pip install -U nltk
RUN pip install -U numpy
RUN python -m nltk.downloader -d /usr/share/nltk_data all
EXPOSE 80
WORKDIR /var/www
CMD [ "python", "/var/www/main.py" ]
final docker-compose
nltk:
build: docker/nltk
container_name: nltk
volumes:
- ./volumes/nltk/var/www/main.py:/var/www/main.py
Try renaming nltk.py to something else. I'm guessing the import nltk and from nltk.corpus is trying to import from your nltk.py file instead of the package. The reason it works when building the image is because your nltk.py file didn't exist yet, since it is mounted at runtime from the compose file.