Docker is the new standard for containers because it makes developers easily ship and deploy applications into an isolated environment and will work on every host. This is because when you create a Docker image, you can include whatever dependencies that you need. In this article I will show a couple of Docker examples to deploy python based rest-api applications.
Table of Contents
Running the First Docker Example: Hello World
I will run all docker examples in this post in Ubuntu 20.04. If you haven’t setup Docker in your Ubuntu, you can follow these instructions to install docker. After you install Docker, let’s start with running a hello world image
$ sudo docker run hello-world
[sudo] password for yeffry:
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
The Docker client contacted the Docker daemon.
The Docker daemon pulled the "hello-world" image from the Docker Hub.
...
In line 1, I ran docker with a sudo privilege. By default, Docker requires to run in privilege user. The “hello-world” is a docker image name. Docker will automatically download this image only if you have never downloaded it before.
The rest of the line is the output when the hello-world image run. After you run this image, Docker will create a container and run the application inside the container. In this hello-world container, the application is returned immediately so the container will stop.
Container is running instances of Docker image.
$ sudo docker ps -a
7807d724b1ea hello-world "/hello" 13 minutes ago Exited (0) 13 minutes ago gifted_elion
To list all the docker containers with all the statuses, you could run docker ps -a. Without -a arguments you will only see containers that are still in running state. You could see from line 2 above hello-world container status is “exited”. This means that your container has stopped.
It is a good idea to remove stopped containers. The line below will remove stopped container with the container id. You could also replace container id with the container name, in this case the container name is gifted_elion.
$ sudo docker rm 7807d724b1e
If you don’t give a container a name when running Docker, Docker will give random name.
$ sudo docker images | grep hello-world
hello-world latest bf756fb1ae65 13 months ago 13.3kB
In the above, you can see your downloaded images with command Docker images.
Python REST API Docker Example
In these Docker examples, we will build a python REST API server. Then, we’ll package it into Docker image, and then run the image.
Let’s write our python app example, and name it rest-api.py.
import json
from flask import Flask, request, jsonify, abort
app = Flask(__name__)
globalName = ""
@app.route("/")
def index():
return jsonify({"status":"success"})
@app.route("/name/<name>", methods=["POST"])
def saveName(name):
global globalName
globalName = name
return jsonify({"name": name, "method":"POST"})
@app.route("/name", methods=["GET"])
def getName():
return jsonify({"name": globalName, "method":"GET"})
app.run(host='0.0.0.0', port=5100)
We have built our python app. Now, let’s make Docker images. Firstly, to write docker images, we need to write Dockerfile. Dockerfile will contain our base Operating System, install dependencies, and run some scripts.
FROM python:3.9.1-alpine3.12
RUN pip install flask
WORKDIR /app
COPY rest-api.py .
RUN chmod u+x rest-api.py
RUN chown xfs rest-api.py
USER xfs
CMD [ "python", "./rest-api.py"]
Line 1. Base image. Checkout this link for complete python docker images. It’s a good habit to always include the version number tag. If you don’t use a tag or you just use the latest version, you might get a surprise when you update your image in the future because it may break your application.
Line 3. Install dependencies.
Line 5. Change working directory. This is like running cd command from linux terminal.
Line 6. Copy your file from host to docker image.
Line 7. Change file permission.
Line 8. Change file ownership to xfs.
Line 10. Set the user that runs the python script to xfs. Please note xfs is the user in python base image that is equivalent to www-data in Ubuntu, because they have similar userid. This is not really necessary in container.
You can run your python command as root and the container will still run in an isolated environment, because the root in container is different from root in host.
Line 11. Run the python script.
Now let’s build the image using command docker build.
$ sudo docker build -t rest-api-image .
Sending build context to Docker daemon 3.584kB
Step 1/8 : FROM python:3.9.1-alpine3.12
---> 53261e7e236b
Step 2/8 : RUN pip install flask
---> Using cache
---> 786f1c2af851
Step 3/8 : WORKDIR /app
---> Using cache
---> 86d0f375a2eb
Step 4/8 : COPY rest-api.py .
---> Using cache
---> 8fe60c9db72f
Step 5/8 : RUN chmod u+x rest-api.py
---> Using cache
---> 2933c00d654c
Step 6/8 : RUN chown xfs rest-api.py
---> Using cache
---> aa12eb4a2926
Step 7/8 : USER xfs
---> Using cache
---> 350b1fcc6787
Step 8/8 : CMD [ "python", "./rest-api.py"]
---> Using cache
---> 42f956a05b77
Successfully built 42f956a05b77
Successfully tagged rest-api-image:latest
The command docker build -t is stand for tag, which is our image name. You can also name it with name:tag format, (ex: rest-api-image:1.0). By default, it will have the latest tag.
The last argument is the path where Dockerfile and files to be copied are located. In this case the dot means to look files on the same directory where you run the docker build command is.
Docker Examples To Run Docker Image
We have built our own image. Now let’s run it.
$ sudo docker run rest-api-image:latest
Serving Flask app "rest-api" (lazy loading)
Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
Debug mode: off
Running on http://0.0.0.0:5100/ (Press CTRL+C to quit)
If you call curl http://localhost:5100, you won’t be able to connect. This is because the container is listening in their own private ip address. We need to forward the port to the host. Let’s run the docker again.
$ sudo docker run -p 5100:5100 rest-api-image:latest
This time, I added the command above with parameter -p 5100:5100. It has syntax -p <host:container>. What it means is that we bind port TCP 5100 on host IP Address and forward it to port 5100 on container.
$ curl http://localhost:5100
{"status":"success"}
Now you will be able to connect to rest-api in container.
Docker Examples To Run Docker In Background Mode
Next, we need to run our container in the background. It should automatically restart if the python app crashes.
$ sudo docker run -d --restart always --name rest-api -p 5100:5100 rest-api-image:latest
The output of the command above is only numbers. You can verify if the containers are running by typing this command: docker ps.
The parameters explanations are listed below:
- -d : Run as daemon in background
- –restart always : Restart the container if python app crash
- –name : The name of the container
- -p 5100:5100 : Assign port <host:container>
List Running Docker
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
da550593b8e0 rest-api-image:latest "python ./rest-api.py" 6 seconds ago Up 3 seconds 0.0.0.0:5100->5100/tcp rest-api
Docker Examples To See Output Logs
Our container is now running in background, we can still see the output logs by using this command:
$ sudo docker logs rest-api
Adds -f to continously stream the logs:
$ sudo docker logs -f rest-api
Now let’s try to call our API with curl command.
$ curl http://localhost:5100
{"status":"success"}
$ curl http://localhost:5100/name/geeksbeginner
405 Method Not Allowed
Method Not Allowed
The method is not allowed for the requested URL.
$ curl -X POST http://localhost:5100/name/geeksbeginner
{"method":"POST","name":"geeksbeginner"}
$ curl http://localhost:5100/name
{"method":"GET","name":"geeksbeginner"}
If you notice on line 5 above, I got an error 405 Method Not Allowed. This is because we don’t have the API /name/<name> with GET method. In line 8, I recalled the API with POST method. And if you see the docker logs screen, you will see the output logs:
$ sudo docker logs -f rest-api
Serving Flask app "rest-api" (lazy loading)
Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
Debug mode: off
Running on http://0.0.0.0:5100/ (Press CTRL+C to quit)
172.17.0.1 - - [11/Feb/2021 07:02:05] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [11/Feb/2021 07:02:14] "GET /name/geeksbeginner HTTP/1.1" 405 -
172.17.0.1 - - [11/Feb/2021 07:02:22] "POST /name/geeksbeginner HTTP/1.1" 200 -
172.17.0.1 - - [11/Feb/2021 07:02:42] "GET /name HTTP/1.1" 200 -
Docker Examples To Stop And Delete Container
In production, you will want this container to always run, but there may be cases where you want to stop that container or even delete it.
$ sudo docker stop rest-api
Let’s verify the container status.
$ sudo docker ps -a | grep rest-api
da550593b8e0 rest-api-image:latest "python ./rest-api.py" 52 minutes ago Exited (137) 26 seconds ago
From the output above your see the container status now “Exited”. Now let’s delete the container.
$ sudo docker rm rest-api
Let’s verify our container again.
$ sudo docker ps -a | grep rest-api
You should not get any output, because the container is deleted.
The difference between docker stop and docker rm is that docker stop will simply halt your container from running. Meanwhile, docker rm will delete container from local storage eg: /var/lib/docker/containers/ and including the associated resource like volume and network.
Running docker rm will only remove the container. It doesn’t remove the image.
Docker Examples To Delete Docker Image
Let’s say you want to delete a docker image. It could be an outdated version of docker image. First let’s list that rest-api-image is in local repository in your system.
$ sudo docker images
rest-api-image latest 42f956a05b77 4 days ago 55.8MB
Now let’s delete our rest-api-image.
$ sudo docker rmi rest-api-image
Untagged: rest-api-image:latest
Deleted: sha256:42f956a05b77e197b865fbeaf84e7fd65f9f650a01d0bc42a7cf795e7a0e53db
Running Command Inside A Container
You can get inside a running container and run commands by using docker exec.
$ sudo docker exec -it rest-api sh
Parameters explanation:
- -i : Interactive mode
- -t : Allocate a pseudo-TTY
- rest-api : Container name
- sh : The command inside container that you want to run
The -t or TTY is useful so you can write any input from terminal, just like when you SSH to a remote server.
I hope this article was helpful for you. And if you have any questions or comments, please feel free to put it in comments section.