All About Docker

From Clueless to Containerized. How I built my 1st Docker Image and dockerized a Python Flask Api

ยท

5 min read

All About Docker

I recently built Audiogram, a side project which turns your audio snippets into shareable and captivating Audiograms, all tailored to your preferences.
On the backend, Audiogram relies on a Python Flask API that leverages OpenAI Whisper to generate subtitles from audio files.

The Problem

However, a challenge arose with the core transcription process using OpenAI Whisper's Base Model, which demands 1GB of VRAM.

The dilemma emerged when hosting the Flask API on various major platforms, such as Render, as their free plans typically offer a limited 512MB of RAM. This proved insufficient for downloading the sizable OpenAI Whisper Model.

The Solution

A very skilled friend Rushi suggested that I should:

๐Ÿ’ก
Dockerise the Flask API and host on AWS EC2 Instance!

Overall Plot Twist:

OpenAI Whisper's base model, the key to transcription, is a hungry dragon, gobbling up 1GB of VRAM. Most free hosting platforms are like knights with rusty swords, limited to 512MB RAM - not enough to tame the beast. So, how did I slay this dragon? Enter Docker and AWS EC2, my loyal knights in shining armor! Let's dive into building the containerized fortress that houses Audiogram.


All About Docker

Now for the exciting part. First, let us begin by creating a Docker image.

But what is docker, docker images and docker container in the first place?

Docker? Yeah, me neither!
I am too, not well versed with docker. I had my first formal introduction to Docker, when I interned at GrowthSchool.
So I am spilling beans on my understanding of docker concepts.

Docker:
Docker is a platform that helps you package and carry your application and its dependencies, making it easy to run consistently across different environments on its own.
It a tool to containerize applications.

Docker Containers:
Container is like a shipping container for software: it bundles everything an application needs to run into a single package, making it easy to ship, deploy, and run the application anywhere.
Containers includes everything needed to run an application: code, runtime, system tools, system libraries and settings.

Containers are built using Docker Images

Docker Image:
Images are blueprints or templates for creating Docker containers.
Images contains a set of instructions and commands for containerizing a application.

Images are made using Dockerfile

๐Ÿ’ก
Think of it like - Image is the recipe specifying all the ingredients and steps to create a dish (your application) & Container is like having lunch โ€“ it's the actual instance of your application running, using the instructions from the Docker image. Docker is like a restaurant chain. They offer a menu of pre-made recipes (Docker images) for various dishes (applications)

Let's make these images to later build containers out of it!

Dockerfile:
A Dockerfile is a text document that contains all the commands a programmer could execute on the command line to assemble an image(or run an application).

Now, let's get our hands dirty!
Quickly Download Docker. Depending on the OS, the installation is different.

I'll walk through how I did the Flask API dockerization. Here's my API

# import dependencies

app = Flask(__name__)
CORS(app)
load_dotenv()

cloudinary.config(
    cloud_name=os.getenv("cloud_name"),
    api_key=os.getenv("api_key"),
    api_secret=os.getenv("api_secret")

)
# Load OpenAI Whisper
model = whisper.load_model("base")


@app.route('/')
def home():
    return 'Hello, World!'


@app.route("/transcribe", methods=["POST"])
@cross_origin()
def transcribe_audio():
    print("Whats up dusde")
    audio_url = request.json["audio_url"]

    audio_clip_name = audio_url.split('/')[-1]

    # Use OpenAI Whisper to transcribe the audio file
    transcribe = model.transcribe(audio_url)

    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")

    segments = transcribe['segments']
    for segment in segments:
        startTime = str(0)+str(timedelta(seconds=int(segment['start'])))+',000'
        endTime = str(0)+str(timedelta(seconds=int(segment['end'])))+',000'
        text = segment['text']
        segmentId = segment['id']+1
        segment = f"{segmentId}\n{startTime} --> {endTime}\n{text[1:] if text[0] == ' ' else text}\n\n"

        srtFilename = os.path.join(
            "SrtFiles", f"{audio_clip_name}_{timestamp}.srt")

        with open(srtFilename, 'a', encoding='utf-8') as srtFile:
            srtFile.write(segment)

    uploaded_file = cloudinary.uploader.upload(
        srtFilename, resource_type="raw", folder="/audiogram/srt")
    srt_url = uploaded_file["secure_url"]

    return srt_url

if __name__ == '__main__':
    app.run(debug=True, port=8000)

Now, even though this might not look any complex, it actually needs a lot of things to run:

  • Python 3.10

  • Flask (running pip install flask)

  • OpenAI Whisper ( pip install git+https://github.com/openai/whisper.git )

  • And a bunch of other dependencies specified in my requirements.txt file

Some programs might only run on specific Operating systems or with some specific version of some stuff, etc etc.

All these problems are solved by writing a simple dockerfile, that builds up a docker image.

So if it works on your system we can dockerize it and ship the container!

"Phew, glad it works somewhere! Let's stick it in a Docker container and hope it doesn't self-destruct like my coding skills under pressure."

All you need to do is make a file called Dockerfile (no extensions)

Here's my Dockerfile

FROM python:3.10
WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

RUN pip install openai-whisper
RUN python -c "import whisper; whisper.load_model('base')"

RUN apt-get update && \
    apt-get install -y ffmpeg && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

COPY . .

ENV FLASK_APP=main.py

EXPOSE 8000

CMD ["gunicorn", "--bind", "0.0.0.0:8000", "main:app"]

Perfect! let's get on to building the image

To build docker image use the docker build command and then run the image by using the docker run . command.

docker build -t transcribe .

Here, -t stands for tag basically name of the image.
I have named my image transcribe

Run the image:

docker run -p 8000:8000 -d transcribe

Here, -d, stands for --detach: Run container in background and print container ID
-p maps the port of the docker container to your system's port.

You can see your app running on http://localhost:8000

Done! We have successfully build & run a container using a docker image!

More Commands

Here are some more useful commands. Learn more from the official documentation

Use the docker ps command to get a list of running containers.
Use docker images to get list of all docker image on the system.

"docker --help" to get list of all commands.

๐Ÿ’ก
And our Python Flask API has officially moved into a cozy Docker container!

Dockerization complete! But the journey doesn't end here.

If you're curious to see how this container takes flight in the cloud, comment and let me know! I'll share the journey of deploying to AWS EC2 in the part.

Stay tuned!