We discuss how to use Docker to deploy a REST service designed using Python Flask.
$ mkdir -p ~/cloudmesh/containers/docker-flask
$ cd ~/cloudmesh/containers/docker-flask
In this directory we will have the following file and directory
structure, where directories are indicated with a /
at the end
~/cloudmesh/containers/docker-flask
- Dockerfile
- requirements.txt
- app/
- main.py
Include the following content in the requirements.txt
file.
Flask==0.11.1
To fulfill the requirements, please run the command
$ pip install -r requirements.txt
Next we are going to create the Dockerfile which includes all the instructions for the deployment of the REST API on docker. This file includes the following content:
We do not know if this is a good image as this was originally contributed by a student. We would prefer if the image is created from scratch by a dockerfile. Running arbitrary docker images on your machine could pose a security risk.
FROM tiangolo/uwsgi-nginx-flask:flask
COPY ./app /app
The Dockerfile contains the FROM
command which downloads a parent
image in which we install the rest of our services. The image is
hosted on Dockerhub
So in this case for the ease of the task and to keep it simple with minimum packages we are going to select an image including nginx, flask and uWSGI.
-
Nginx: An open source software for web serving capability.
-
Flask: REST API package in Python
-
uWSGI: A server and one of the protocols implemented in WSGI which is a protocol used for pure HTTP communications and load balancing.
So this image includes everything we need to work on this tutorial.
COPY: Here it will copy content from app folder created in local machine to the Docker container.
Now the Dockerfile is completed and we have everything needed to build a docker image.
Next we create the main.py file inside the app folder with the following content.
from flask import Flask
from flask import jsonify
app = Flask(__name__)
@app.route("/")
def hello():
data = {"message": "Hey I am using Docker!")
return flask.jsonify(**data)
@app.route("/cloudmesh/cpu")
def get_cpu():
data = {"cpu": "icore7")
return flask.jsonify(**data)
if __name__ == "__main__":
app.run(host="127.0.0.1", debug=True, port=80)
To build the container, please use the commands
$ cd ~/cloudmesh/containers/docker-flask
$ docker build -t sample-flask-rest-app .
If it builds successfully, you will get the following response
this image may not work and may not be secure so do not use this example!
$ docker build -t sample-flask-rest-app .
Sending build context to Docker daemon 19.15MB
Step 1/2: FROM tiangolo/uwsgi-nginx-flask:python3.7
---> ec43f17def9a
Step 2/2: COPY ./app /app
---> e8eb1bff86b8
Successfully built e8eb1bff86b8
Successfully tagged sample-flask-rest-app:latest
Note: Changing any content inside the app folder must be
updated in the container by rebuilding the image.
Run the following commands to get the REST API hosted on http://127.0.0.1:80
$ docker run -p 80:80 -t sample-flask-rest-app
If it is loaded and if it runs successfully you will see a response similar to
Checking for script in /app/prestart.sh
Running script /app/prestart.sh
Running inside /app/prestart.sh, you could add migrations to this file, e.g.:
#! /usr/bin/env console
# Let the DB start
sleep 10;
# Run migrations
alembic upgrade head
/usr/lib/python2.7/dist-packages/supervisor/options.py:296:
UserWarning: Supervisord is running as root and it is searching
for its configuration file in default locations (including its
current working directory); you probably want to specify a "-c"
argument specifying an absolute path to a configuration file for
improved security.
'Supervisord is running as root and it is searching '
2018-02-19 18:07:46,198 CRIT Supervisor running as root
(no user in config file)
2018-02-19 18:07:46,198 WARN Included extra file
"/etc/supervisor/conf.d/supervisord.conf" during parsing
2018-02-19 18:07:46,204 INFO RPC interface 'supervisor' initialized
2018-02-19 18:07:46,204 CRIT Server 'unix_http_server'
running without any
HTTP authentication checking
2018-02-19 18:07:46,204 INFO supervisord started with pid 7
2018-02-19 18:07:47,207 INFO spawned: 'nginx' with pid 10
2018-02-19 18:07:47,211 INFO spawned: 'uwsgi' with pid 11
[uWSGI] getting INI configuration from /app/uwsgi.ini
[uWSGI] getting INI configuration from /etc/uwsgi/uwsgi.ini
*** Starting uWSGI 2.0.15 (64bit) on [Mon Feb 19 18:07:47 2018] ***
compiled with version: 4.9.2 on 04 February 2018 16:11:35
os: Linux-4.4.0-112-generic #135-Ubuntu SMP
Fri Jan 19 11:48:36 UTC 2018
nodename: 7f9706084219
machine: x86_64
clock source: unix
pcre jit disabled
detected number of CPU cores: 8
current working directory: /app
detected binary path: /usr/local/bin/uwsgi
your memory page size is 4096 bytes
detected max file descriptor number: 1048576
lock engine: pthread robust mutexes
thunder lock: disabled (you can enable it with --thunder-lock)
uwsgi socket 0 bound to UNIX address /tmp/uwsgi.sock fd 3
uWSGI running as root, you can use --uid/--gid/--chroot options
*** WARNING: you are running uWSGI as root !!!
(use the --uid flag) ***
Python version: 2.7.14 (default, Dec 12 2017, 16:55:09) [GCC 4.9.2]
*** Python threads support is disabled. You can enable it
with --enable-threads ***
Python main interpreter initialized at 0x1eed1b0
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 1237056 bytes (1208 KB) for 16 cores
*** Operational MODE: preforking ***
WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter
0x1eed1b0 pid: 11 (default app)
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 11)
spawned uWSGI worker 1 (pid: 14, cores: 1)
spawned uWSGI worker 2 (pid: 15, cores: 1)
2018-02-19 18:07:48,357 INFO success: nginx entered RUNNING
state, process has stayed up for > than 1 seconds (startsecs)
2018-02-19 18:07:48,358 INFO success: uwsgi entered RUNNING
state, process has stayed up for > than 1 seconds (startsecs)
Now you can go in your web browser to the URL
To see the status of the container you can sue the command
$ docker ps -a
Note the CONTAINER ID. To sopt the container with a particular ID, use
$ docker stop dc8cccf22216
To delete the docker container image, you must first sop all instances using it and the remove the image. YOu can see the images with the command
$ docker images
Then you can locate all containers using that image while looking in the IMAGE column or using a simple fgrep in case you have many images. stop the containers using that image and that you can say
$ docker rm 74b9b994c9bd
while the number is the container id
Once you killed all containers using that image, you can remove the
image with the rmi
command.
$ docker rmi 8b3246425402