In the Machine Learning Zoomcamp course conducted by Alexey Grigorev (https://github.com/alexeygrigorev/mlbookcamp-code/tree/master/course-zoomcamp) in Week/Session #5, it is required to deploy a web service which can accept user provided customer data and using a trained ML model, predict whether the customer will churn or not.
For this, participants can either deploy the web service locally on their machine, inside a docker container on their machine, or in Cloud (AWS, Azure. Google etc.).
There was one more option that was suggested, which is to use Heroku to host python based web application for free. This option will especially be useful when working on the Project 1 as part of this course, and hence I thought of exploring this option and preparing this reference for others to follow if they wish to use this option.
Following are the steps to use Heroku. These have been explained with an example web app that was used for the homework of week5 of the course.
Thanks to Harshit for his video https://www.youtube.com/watch?v=1Z7nt0Fyits from where I learned the steps, and then some more reading from Heroku documentation.
Go to https://www.heroku.com/ and Signup
You can also setup Google authenticator app on your mobile to get a token (OTP) and use it for Multi-factor authentication when logging in to Heroku.
Once logged in, you can deploy web app in any of the languages - Python, Ruby, PHP, Node.js, Java, Go, Closure, Scala
There are multiple ways to deploy a web app on Heroku (via the Heroku Dashboard, using CLI from your machine), of which I used the CLI option.
There are multiple options availabe to install Heroku cli - which can be found here - https://devcenter.heroku.com/articles/heroku-cli. I chose downloading the tarball, since it does not need any installation.
a. Download tarball
To install from tarball, go to https://devcenter.heroku.com/articles/heroku-cli#tarballs and download the tarball for the Operating system of your machine. I downloaded for Ubuntu.
b. Extract tarball
Extract the tarball (Step fo Ubuntu shown below. For Windows simply use the WinZip or equivalent to extract/unzip from tarball).
c. Add heroku/bin to your PATH
To add the heroku/bin path on a Linux machine, execute the below command. For Windows refer to https://helpdeskgeek.com/windows-10/add-windows-path-environment-variable/
When using Heroku, you can deploy your Web app as a docker container to heroku, or you can deploy it without using docker. Following section explains both these options.
To be able to deploy to Heroku as a docker container, you need to first have docker installed and running. For this, you have 2 options:
-
- Use docker installed on your local machine
-
- Use Google Cloud shell which has docker pre-installed. Google Cloud shell provides a free machine if you have a Gmail account.
Steps to deploy your app to heroku as a docker container are explained below (including the slight difference for the 2 options mentioned above)
a. Prepare code base for docker deployment
Create a directory for your deployment and copy your code for the Web app, any ML model files (if/as appropriate), python package dependencies (this example shows use of pipenv and hence Pipfile and Pipfile.lock, however you can requirements.txt or files as applicable for you).
If you are using Google Cloud shell refer to guide in this link to understand how to upload your files to Google Cloud shell
b. Create Dockerfile
Create Dockerfile (filename should be exactly this) in the same directory with appropriate lines for your docker image.
Below example shows, python:3.8.12-slim being used as base image, installing pipenv, then copying the Pipfile and Pipfile.lock that specify python dependency packages, installing the dependency packages using pipenv, then copying the python code for the web app, followed by defining port to be exposed and entrypoint command that should get run when docker container starts.
Note: One very important point I struggled and understood after a long time is that you cannot map to a custom port in Heroku. Thus, for gunicorn, do not use anything like
ENTRYPOINT ["gunicorn", "--bind=0.0.0.0:9696", "sample_app:app"]
but something like
ENTRYPOINT ["gunicorn", "sample_app:app"]
Although using the first syntax worked when running docker container locally on my machine, it failed when deployed to Heroku - hence suggesting the second syntax. Will try to investigate further and update once I know better.
c. Deploy to Heroku as docker container
Run the following steps in sequence to deploy your web app as a docker container to Heroku.
If you are executing these steps from your local machine then follow steps in Option A below. However if you are executing these steps from Google Cloud shell, then follow steps in Option B below. The difference is due to the fact that the step to login from your local machine will open a web browser where you can login to Heroku providing credentials. Whereas from Google Cloud shell, the web browser tab cannot be opened for you to login. Hence for the Google Cloud shell scenario, we will use API key from Heroku.
Option A: Login to heroku from your machine : Verify heroku command is found (In step 2.2.c. above the path has already been set) in the path. Then using heroku cli, login to heroku. Press any key when asked to do so.
which heroku
heroku login
This will open a tab in your web browser asking you to login to Heroku. Login to Heroku.
Now you can close this tab and return to the command prompt on your terminal
You can skip Option B below and continue with steps provided in Login to Heroku container
Option B: Login to heroku from Google Cloud shell : From your web browser, login to Heroko and go to Account settings. On the Account setting screen, go to Applications tab and under Authorizations click Create authorization
Provide some name for the authorization and optionally set expiration time and create authorization (This will create an API key which can be used to login to Heroku from Google Cloud shell)
Copy the generated token (i.e. API key) and go to your Google Cloud shell prompt. Here, define and export a variable named HEROKU_API_KEY with value as the key you copied. Example command shown below (Please note there should not be any space before or after the =, and the key should be enclosed in ")
export HEROKU_API_KEY="8fb57c22-c44e-4dd5-b5d7-6315705b7bbe"
Run the following steps to add Google DNS server entry to a file /etc/resolv.conf (I had to do this to be able to connect to Heroku container registry from within Google cloud shell).
$ echo "nameserver 8.8.8.8" > /tmp/1
$ cat /etc/resolv.conf >> /tmp/1
$ sudo cp /tmp/1 /etc/resolv.conf
Login to Heroku container : Login to Heroku container registry.
heroku container:login
Create app in Heroku: Create an app in Heroku. Below example shows creating a app with the name ml-zoom-docker.
heroku create ml-zoom-docker
Push docker image to Heroku: Push docker image to Heroku container registry. When you run the below command, the Dockerfile will be used to build the docker image locally on your machine and then push the image to Heroku container registry. Using the -a flag you specify the application name (e.g. ml-zoom-docker app that you created above).
heroku container:push web -a ml-zoom-docker
Release container: Deploy container on Heroku. When you run the below command, a docker container will be launched in Heroku from the docker image that you pushed to Heroku container registry.
heroku container:release web -a ml-zoom-docker
Launch you app: Open your web app. For this you can go your web browser and open your application URL with appropriate path (e.g. my sample application would run at https://ml-zoom-docker.herokuapp.com/welcome - since ml-zoom-docker is the name of my app and I am serving page /welcome via my sample app)
Great !!! You web app is now running in Heroku as a docker container !!!
To deploy your Web app to Heroku, you need to have all your code in one place and create a few configuration files.
a. Prepare code base
Create a directory for your deployment and copy your code for the Web app and any ML model files (if/as appropriate)
b. Create configuration files
Specify Python version : This is an optional step, required only if you want a specific Python version to be used in Heroku to run your web app. Check the Python version (where you have the appropriate version and where you developed/tested your code) using command python -V
.
Specify the version into a file named runtime.txt [Ensure everything is lower case in the file and that you mention the full version]
Refer to https://devcenter.heroku.com/articles/python-runtimes for more details
Specify Python dependencies/packages : Create requirements.txt file and specify the Python packages required for the execution of your Web app.
You could choose to use the command pip freeze > requirement.txt
to populate requirements.txt based on all packages installed in your current active python environment. Or add the package names manually. I added manually.
Note: As part of the course homework 5, scikit-learn version 1.0 is required since the model was trained using this version. Added gunicorn, to be used as the web server to run the web app in Heroku (there could be other options, for now I know of this)
Specify commands to be executed by the app on startup : Heroku apps include a Procfile that specifies the commands that are executed by the app on startup.
The command for this example would be as below.
web: gunicorn <name of your python script without the .py>:<instance name of Flask>
- web: indicates that the web server process can receive external HTTP traffic from Heroku’s routers.
- gunicorn is the web server (software) that will be used
- app is the instance name of Flask when you define something like app = Flask(somename_foryourapp)
e.g. I used web: gunicorn w5-hw-svc-predict:app
since my python file name is w5-hw-svc-predict.py and I have defined Flask instance as app = Flask('churn')
Find more info on Procfile here
c. Deploy web app to Heroku
Heroku supports multiple methods for deployment - Git (GitHub, Heroku Git), Docker container image, Integrations. So far, I have explored the Git method using Heroku git.
Assuming you have git installed on your machine. If not install it. (On Ubuntu using sudo apt install -y git
)
Initialize local git repository : After having collected your code and defined necessary configurations files, initialize local git repository
git init
Add all the contents of the directory to git repo
git add .
Commit changes to local git repo
git commit -m "some commit message"
Login to heroku : Verify heroku command is found (In step 2.2.c. above the path has already been set) in the path. Then using heroku cli, login to heroku. Press any key when asked to do so.
which heroku
heroku login
This will open a tab in your web browser asking you to login to Heroku. Login to Heroku.
Now you can close this tab and return to the command prompt on your terminal
Create an application : You need to create a new application in Heroku before deploying the code to it. Create an application
This creates an application and also a Heroku git repo for this application. You can go to the Heroku dashboard to see the application being created with a default web page.
Push code and configurations to Heroku git for deployment : You can now push to remote git (Heroku git). Once the contents are pushed to Heroku git, it automatically triggers the deployment.
Test your Web App
You can now test whether your Web App is running successfully (now being hosted on Heroku). From your local machine (or from anywhere, where you have python installed, requests package installed and having internet access) execute the code to test your Web App.
Below is sample test scenario for the homework of Week5 of ML Zoomcamp course.