Python

Run Docker Commands in Python

Hello in this tutorial, we will create a simple python flask application and run the docker commands from the python script.

1. Introduction

Flask in python programming helps to build web applications. It is easier than Django’s framework and requires less base code to implement a simple web application. To set up the application we’ll first require to install python.

1.1 Setting up Python

If someone needs to go through the Python installation on Windows, please watch this link. You can download the Python from this link.

1.2 What is Docker & Setting up Docker

In the present world, Docker is an important term,

  • Often used in CI/CD platform that packages and runs the application with its dependencies inside a container
  • Is a standard for Linux Containers
  • A Container is a runtime that runs under any Linux kernel and provides a private machine-like space under Linux

If someone needs to go through the Docker installation, please watch this video.

2. How to run Docker Commands in Python

I am using JetBrains PyCharm as my preferred IDE. You’re free to choose the IDE of your choice. Fig. 1 represents the project structure for this tutorial.

Fig. 1: Application structure

2.1 Creating a requirements file

Add the below code to the requirements file. The file will be responsible to download and install the packages required for this tutorial.

requirements.txt

Flask

2.2 Basic input html form

Define an HTML form in the templates folder with two input fields responsible to accept the name and tag required for the docker pull … command. This page will also show the success or error messages once the operation is completed.

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Index</title>
    </head>
    <body>
        <h1>Python Docker client implementation</h1>
        <hr>
        <p>
            {% with messages = get_flashed_messages() %}
              {% if messages %}
                <ul class=flashes>
                {% for message in messages %}
                  <li>{{ message }}</li>
                {% endfor %}
                </ul>
              {% endif %}
            {% endwith %}
        </p>
        <form action="/" method="POST">
            <label for="name">Image name:</label>
            <input type="text" id="name" name="name" placeholder="Enter image name">
            <br>
            <label for="tag">Tag:</label>
            <input type="text" id="tag" name="tag" placeholder="Enter tag">
            <input type="submit" value="Submit" />
        </form>
    </body>
</html>

2.3 List images html

Create an HTML page in the templates folder that will show the list of docker images available in the machine. The page is accessible from the following endpoint – /images.

images.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Images</title>
    </head>
    <body>
        <h2>Available images</h2>
        <hr>
        <p>
            <span>Total images= </span>{{data|length}}
        </p>
        <ul>
            {% for ele in data %}
            <li>{{ ele }}</li>
            {% endfor %}
        </ul>
    </body>
</html>

2.4 Python code

The python file will expose four different endpoints responsible for interacting with the frontend pages.

  • / endpoint – Responsible for showing the HTML page to the user via the HTTP GET call. The same endpoint will also be responsible to handle the HTTP POST call from the form and execute the docker pull command to fetch the required image from the docker repository
  • /images endpoint – Responsible to run the docker images command and show the result on the images.html page
  • /remove endpoint – Responsible to remove the image. I have skipped this functionality for users to play

main.py

import logging
import os
import subprocess

from flask import Flask, render_template, request, redirect, flash

# [logging config
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(filename)s:%(funcName)s:%(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    level=logging.INFO)
# logging config]

app = Flask(__name__)
app.secret_key = "somesecretkey"


# http://localhost:5000
@app.route('/', methods=['GET'])
def index():
    logging.info('Showing index page')
    return render_template('index.html')


@app.route('/', methods=['POST'])
def pull():
    image_name = request.form.get("name")
    tag = request.form.get("tag")
    try:
        updated_tag = tag if len(tag.strip()) > 0 else 'latest'
        logging.info('Pulling image= [%s] with tag= [%s]', image_name, updated_tag)
        sub_cmd = 'docker pull {img_name}:{tag}'.format(img_name=image_name, tag=updated_tag)
        subprocess.run(sub_cmd, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        flash(f'{image_name} pulled successfully from Docker hub')
    except subprocess.CalledProcessError as error:
        if "repository does not exist" in error.stderr.decode("utf-8"):
            flash(f'{image_name} not found in Docker hub')
            logging.error(error)
        elif "not found" in error.stderr.decode("utf-8"):
            flash(f'{image_name} not found for {tag} tag in Docker hub')
            logging.error(error)
        else:
            flash('Something unexpected has occurred. Check app logs')
            logging.error(error)

    return redirect('/')


# http://localhost:5000/images
@app.route('/images', methods=['GET'])
def images():
    logging.info('Listing images')
    sub_cmd = 'docker images --format "Id= {{.ID}}; Name= {{.Repository}}; Tag= {{.Tag}}"'
    # with open('output.txt', "w") as outfile:
    #     subprocess.run(sub_cmd, shell=True, check=True, stdout=outfile, stderr=outfile)
    res = subprocess.run(sub_cmd, text=True, stdout=subprocess.PIPE).stdout.splitlines()
    return render_template('images.html', data=res)


# http://localhost:5000/remove
@app.route('/remove', methods=['POST'])
def remove():
    raise Exception('Not yet implemented.')


if __name__ == '__main__':
    # Development only: run "python app.py" and open http://localhost:5000
    server_port = os.environ.get('PORT', '5000')
    app.run(debug=False, port=server_port, host='0.0.0.0')

3. Code run Docker Commands in Python demo

For localhost debugging, you can run the application by running the main.py py file in the PyCharm IDE. The application will be started on the 5000 port number and you can hit the following url – http://localhost:5000 in the browser of your choice to display the index HTML page.

Fig. 2: Welcome page

Enter the details in the input fields and click the submit button. For the demo, I have entered the image name as – hello-world and tag as – latest. Once the image is downloaded successfully a success message will be shown as in Fig. 3.

Fig. 3: Pulling docker image

In case of failure, the error message will be shown on the same page. Now to verify whether the image was downloaded successfully or not I have created another endpoint available at http://localhost:5000/images. This endpoint will trigger the docker images command to fetch all the available images in your machine and display them on the html page as shown in Fig. 4.

Fig. 4: Images list

Due to security reasons, only the required image is shown while others are cropped. That is all for this tutorial and I hope the article served you with whatever you were looking for. Happy Learning and do not forget to share!

4. Summary

In this tutorial, we learned about docker and created a simple python flask application to trigger the docker commands from different endpoints. You’re free to play around with this application and change it as per your needs. You can also download the source code of this tutorial from the Downloads section.

5. Download the Project

This was a tutorial on executing docker commands from the python script.

Download
You can download the full source code of this example here: How to run Docker Commands in Python

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button