• Other contents of the series

    1. Docker quickly creates lightweight portable container ✓
    2. Docker&flask quickly builds service interfaces
    3. Docker and UWSGI high performance WSGI server production deployment requirements
    4. Docker&gunicorn high performance WSGI server production deployment requirements
    5. Docker, Nginx and Gunicorn implement load balancing
    6. Docker&ngxtop and parsing nginx logs in real time
    7. Docker&supervisor monitors your services
    8. Docker and PyInstaller two-step build small volume container
    9. Locust does high concurrency testing on your service
    10. Postman is a popular API debugging tool

Environment depends on

  • This tutorial is based on a Redhat Linux server
python: 3.8.3 click==8.0.1 Flask==2.0.1 flask-limiter ==1.4 itsDangerous ==2.0.1 Jinja2==3.0.1 limits==1.5.1 MarkupSafe==2.0.1 The six = = 1.16.0 Werkzeug = = 2.0.1 WTForms = = 2.3.3Copy the code
  • Main Contents of this paper
    • It includes deploying the Flask service, mounting folders, setting the flask logs, setting the parameter verification section, setting the limit of the number of requests for fixed IP addresses, and setting the IP whitelist.

Docker&flask creates containers

  1. Python file
    • Set debug=True to automatically restart the service when the file is updated
import flask, json
from flask import request
import platform

Create a service, treating the current Python file as a service
app = flask.Flask(__name__)

@app.route('/test', methods=['get'])
def login() :
    username = request.values.get('name')
    pwd = request.values.get('pwd')
    system = platform.system()
    systemnode = platform.node()
    system_info = "Platform is {0} & running node is {1}".format(system, systemnode)
    if username and pwd:
        if username=='xiaoming' and pwd=='111':
            resu = {'code': 200.'message': 'Login successful'.'system':system_info}
            return json.dumps(resu, ensure_ascii=False)
        else:
            resu = {'code': -1.'message': 'Wrong account password'.'system':system_info}
            return json.dumps(resu, ensure_ascii=False)
    else:
        resu = {'code': 10001.'message': 'Parameters cannot be empty'.'system':system_info}
        return json.dumps(resu, ensure_ascii=False)


if __name__ == '__main__':
    app.run(debug=True, port=2222, host="0.0.0.0")
Copy the code
  1. Dockerfile file
The FROM python: 3.8 WORKDIR/home/myfirstapi/RUN ln - sf/usr/share/zoneinfo/Asia/Shanghai/etc/localtime COPY.. RUN the PIP install -r requirements.txt -q -i https://pypi.tuna.tsinghua.edu.cn/simple && \ rm -rf /var/cache/apk/* expose 2222 CMD ["python3", "flask_test.py"]Copy the code
  1. We specially set it up for testing convenience
    • Debug =True in Python scripts, the service restarts automatically when the script is updated
    • The Docker container sets up data volumes so that local changes can be automatically synchronized to the container.
Docker flask build -t test/ Dockerflask :1.0. Then map port 2222 and mount /root/first_api/flask_api to the specified directory of the container docker run -d p 2222:2222 --name docker_flask_api -v / root/first_api/flask_api: / home/myfirstapi/test/dockerflask: 1.0Copy the code
  • The results are as follows:

Flask Setup log

  • Refer to Python’s Logger library
    def login():
        ...
        app.logger.debug('this is a DEBUG message')
        app.logger.info('this is an INFO message')
        app.logger.warning('this is a WARNING message')
        app.logger.error('this is an ERROR message')
        app.logger.critical('this is a CRITICAL message')
    Copy the code
  • The result is as follows

Flask adds parameter verification

  • Set the validation section
    from wtforms.fields import simple
    from wtforms import Form, StringField, IntegerField
    from wtforms.validators import Length, Regexp, NumberRange, AnyOf, DataRequired
    class parameters_validation(Form) :  
        username = StringField(validators=[AnyOf(values = ["xiaoming"."laolitou"])])
        pwd = StringField(validators = [DataRequired() 
        Length(max=4.min=2,message="the length of the pwd must between %(min)d and %(max)d"),
        Regexp(regex="\d+",message="pwd must be start with numbers") ",@app.route('/test', methods=['get'])
    def login() :
        form = parameters_validation(request.args)
        if form.validate():
            username = form.username.data
            pwd = form.pwd.data
            ...
        else:
            return jsonify(form.errors)
        
        if __name__ == '__main__':
       		 app.run(debug=True, port=2222, host="0.0.0.0")
    
    Copy the code
  • The result is as follows

Flask added IP restrictions

  • Flask Sets the number of IP access times

    from flask import Flask
    from flask_limiter import Limiter
    from flask_limiter.util import get_remote_address
    
    app = Flask(__name__)
    limiter = Limiter(
        app,
        key_func=get_remote_address,
        default_limits=["5 per day"."2 per hour"])@app.route("/1times")
    @limiter.limit("1 per day")
    def slow() :
        return "Only one visit per day."
    
    @app.route("/2times") 
    def fast() :
        return "Five times a day, two times an hour."
    
    @app.route("/nolimits")
    @limiter.exempt      # No access rate limit
    def ping() :
        return "Access is unlimited."
    
    if __name__ == '__main__':
        app.run(debug=True, port=2222, host="0.0.0.0")
    Copy the code
  • The result is as follows

    • With this, it’s easy to add a limit to the number of times you can offer a monthly service

Flask Sets the IP address whitelist

  • Flask Sets the IP address whitelist and provides services for only some IP addresses
    from flask import abort, Flask, render_template, request
    ALLOWED_IPS = ['10.92'.'10.91']
    
    app = Flask(__name__)
    
    @app.errorhandler(403)
    def permission_error(e) :
        return Error 403: %s%e
    
    @app.before_request
    def limit_remote_addr() :
        client_ip = str(request.remote_addr)
        valid = False
        for ip in ALLOWED_IPS:
            if client_ip.startswith(ip) or client_ip == ip:
                valid = True
                break
        if not valid:
            abort(403)
    
    @app.route('/', methods = ['GET'])
    def home() :
        return "Your IP: {}".format(request.remote_addr)
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', debug=True)
Copy the code
  • The result is as follows

subsequent

  • Generally speaking, when we formally provide a service, we need to do load balancing, after all, we need to consider the user experience;
  • In nginx load balancing, request limits and IP whitelists can also be configured.