天天看點

Docker在手,天下我有,Win10下Docker部署Gunicorn+Flask獨立鏡像

作者:劉悅技術分享

書接上回,之前一篇:Win10環境下使用Flask配合Celery異步推送實時/定時消息(Socket.io)/2020年最新攻略,闡述了如何使用Celery異步推送Websocket消息,現在我們利用Docker将這個完整項目部署起來,為什麼用Docker呢?原因很簡單,這種容器技術可以将整個項目用單個容器裝起來,僅僅隻需要維護一個簡單的配置檔案就告訴電腦每次部署要把什麼東西裝進容器,甚至把這個過程自動化,部署流程就會變得簡單、友善。

簡單了解就是Docker的鏡像就類似《精靈寶可夢》中小智手裡的精靈球,我們的項目就類似那些寵物小精靈,當我們開發完畢就可以利用DockerFile對項目進行打包制作成鏡像(小精靈被吸入精靈球),部署時就可以了解為小精靈被釋放出來進行戰鬥(通過打包好的鏡像運作容器),而Docker的倉庫則提高了鏡像的便捷性,可以讓我們随時随地隻要聯網就可以使用自己的鏡像(相當于小智不用随身攜帶精靈球,而是通過網絡随時下載下傳需要的精靈球)。

同時Docker其強大的跨平台特性,可以讓我們在任何系統下部署項目,包括經常令人诟病的Windows,值得一提的是本次在Win10下部署項目的流程同樣适用于Centos、Mac os、Ubuntu等系統,其相容性可見一斑。

關于Win10如何折騰和配置Docker,請參照這篇文章:win10系統下把玩折騰DockerToolBox以及更換國内鏡像源(各種神坑)

首先簡單看一下項目結構:

Docker在手,天下我有,Win10下Docker部署Gunicorn+Flask獨立鏡像

manage.py是項目的入口檔案,這裡我們利用Sockert.io讓Flask支援Websocket

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import pymysql
from flask import request,jsonify
from flask_cors import CORS
from flask_socketio import SocketIO,send,emit,join_room, leave_room
import urllib.parse
import user_view

from celery import Celery
from datetime import timedelta

pymysql.install_as_MySQLdb()
 
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:root@localhost:3306/md"
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

app.config['BROKER_URL'] = 'redis://localhost:6379'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379'
app.config['CELERY_ACCEPT_CONTENT'] = ['json', 'pickle']
app.config['REDIS_URL'] = 'redis://localhost:6379'
app.config['JSON_AS_ASCII'] = False

CORS(app,cors_allowed_origins="*")


app.register_blueprint(user_view.user)

db = SQLAlchemy(app)

socketio = SocketIO(app,cors_allowed_origins='*',async_mode="threading",message_queue=app.config['CELERY_RESULT_BACKEND'])


celery = Celery(app.name)
celery.conf.update(app.config)


celery.conf.CELERYBEAT_SCHEDULE = {
        
        "test":{
            "task":"get_cron",
            "schedule":timedelta(seconds=10)
        }

}




@celery.task(name="get_cron")
def get_cron():
    
    get_sendback.delay()
    

@celery.task()
def get_sendback():
    
    socketio.emit('sendback','message',broadcast=True)

@app.route('/task')
def start_background_task():
    get_sendback.delay()
    return '開始'
 

@app.route('/',methods=['GET','POST',"PUT","DELETE"])
def hello_world():
    #res = db.session.execute("insert into user (`username`) values ('123') ")

    # res = db.session.execute(" select id,username from user ").fetchall()

    # data = request.args.get("id")

    # #data = request.form.get("id")

    # print(data)

    # print(res)

    # #return 'Hello Flask'
    # return jsonify({'result': [dict(row) for row in res]})

    return jsonify({'message':'你好,Docker'})


@socketio.on('join')
def on_join(data):
    username = 'user1'
    room = 'room1'
    join_room(room)
    send(username + ' has entered the room.', room=room)

@socketio.on('message')
def handle_message(message):
    message = urllib.parse.unquote(message)
    print(message)
    send(message,broadcast=True)

@socketio.on('connect', namespace='/chat')
def test_connect():
    emit('my response', {'data': 'Connected'})

@socketio.on('disconnect', namespace='/chat')
def test_disconnect():
    print('Client disconnected')
 
@app.route("/sendback",methods=['GET'])
def sendback():

    socketio.emit('sendback','message')

    return 'ok'
 
if __name__ == '__main__':

    socketio.run(app,debug=True,host="0.0.0.0",port=5000)           

接下來使用Gunicorn+gevent來運作Flask項目,Gunicorn伺服器作為wsgi app的容器,能夠與各種Web架構相容(flask,django等),得益于gevent等技術,使用Gunicorn能夠在基本不改變wsgi app代碼的前提下,大幅度提高wsgi app的性能。那到底怎麼提升性能?說簡單點,Gunicorn 預設的網絡模型是 select ,當我們把worker 替換成 gevent 後,則改為 epoll 監聽模型,關于select、poll、epoll請參照這篇文章:關于Tornado:真實的異步和虛假的異步,這裡不再贅述。

安裝相應的庫

pip install gunicorn gevent --user           

編輯項目目錄下的gunicorn.conf.py

workers = 3    # 程序數
worker_class = "gevent"   # 異步模式
bind = "0.0.0.0:5000"           

由于Gunicorn并不支援Windows環境,是以隻需要寫好配置,不需要運作。

編輯項目目錄下的requirements.txt檔案,這裡面都是我們項目所依賴的庫

flask==1.0.2
flask-cors
flask-socketio
flask-sqlalchemy
pymysql
celery
gunicorn
gevent
redis==3.3.11           

随後在項目目錄下建立一個 Dockerfile 檔案,這個檔案可以了解為打包鏡像的腳本,你需要這個鏡像做什麼,就把任務寫到腳本中,Docker通過執行這個腳本來打包鏡像

FROM python:3.6
WORKDIR /Project/myflask

COPY requirements.txt ./
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

COPY . .
ENV LANG C.UTF-8
CMD ["gunicorn", "manage:app", "-c", "./gunicorn.conf.py"]           

可以看到,我們項目的鏡像首先基于python3.6這個基礎鏡像,然後聲明項目目錄在/Project/myflask中,拷貝依賴表,之後安裝相應的依賴,這裡在安裝過程中我們指定了國内的源用來提高打包速度,最後利用gunicorn運作項目,值得一提的是,ENV LANG C.UTF-8是為了聲明Docker内部環境中的編碼,防止中文亂碼問題。

最後我們就可以愉快的打包整個項目了,在項目根目錄下執行

docker build -t 'myflask' .           
Docker在手,天下我有,Win10下Docker部署Gunicorn+Flask獨立鏡像

此時看到Docker通過讀取Dockerfile檔案來下載下傳所需的基礎鏡像和依賴庫,這裡一定要指定Docker的下載下傳源,否則速度會非常緩慢,打包好的鏡像檔案大概有1g左右。

下載下傳結束之後,可以看到myflask這個鏡像已經靜靜躺在鏡像庫中了,運作

docker images           

指令來檢視

Docker在手,天下我有,Win10下Docker部署Gunicorn+Flask獨立鏡像

然後我們就可以利用這個鏡像來通過容器跑Flask項目了,運作指令

docker run -it --rm -p 5000:5000 myflask           

這裡的指令是通過端口映射把docker内部的端口5000映射到主控端的5000端口上,後面的參數是鏡像名稱。我們看到,在Win10下,已經不可思議的通過Gunicorn把Flask跑起來了,這在之前沒有Docker技術之前是不可想象的。

Docker在手,天下我有,Win10下Docker部署Gunicorn+Flask獨立鏡像

通過網址通路一下,這裡注意一點,就是Windows系統下,通路Docker容器需要通過配置設定的ip來通路,而不是我們常用的localhost。

Docker在手,天下我有,Win10下Docker部署Gunicorn+Flask獨立鏡像

完全沒有任何問題。

Docker在手,天下我有,Win10下Docker部署Gunicorn+Flask獨立鏡像

結語:到這裡我們的 Docker+Flask + Gunicorn就部署完畢了,将這個鏡像上傳Dockerhub倉庫,在任何時間、任何地點、任何系統上,隻要連着網、隻要我們想,就都可以在短短1分鐘之内部署好我們的項目,這就是Docker技術對開發人員最好的饋贈。最後奉上項目位址:https://gitee.com/QiHanXiBei/myflask