20 กรกฎาคม 2560

Flask : deploy บน Google app engine

สิ่งที่ต้องมีก่อน deploy บน GAE
   - gunicorn
   - app.yaml

โดยโครงสร้าง file จะเป็นดังนี้
/project
  |-app.yaml
  |-config.py
  |-main.py
  /yuunai (application name)
     |-__init__.py
     |-script.py
     /templates
       |-file.html
     /static
       |-file.css

การทำงาน(deploy) จะเรียงลำดับไฟล์ดังนี้

   1. app.yaml ขึ้นมา เพื่อสร้าง environment รองรับการ deploy ตามค่าที่ประกาศไว้ ซึ่งในไฟล์นี้จะมีประกาศ entrypoint คือ gunicorn ซึ่งไปเรียกใช้ main:app
   2. gunicorn ไปอ่านที่ไฟล์ main.py ต่อเพื่อเรียกใช้ ตัวแปร app ที่ประกาศไว้
   3. main.py ประกาศตัวแปร app ให้ไปเรียก application เพื่อ start flask ขึ้นมาใช้งาน
   4. application/__init__.py ที่อยู่ใต้ directory application  ประกาศฟังก์ชั่นโดยใช้ blueprint ไปเรียก script เริ่มต้น
   5. application/script.py เรียกใช้ฟังก์ชั่นต่างๆ และ ส่งค่าไปแสดงผลที่  /application/templates/file.html และ /applicaiton/static/file.css


เนื่องจาก Flask เป็น web application รองรับการเรียกใช้บริการ request by request  (เพราะมันคือ application)  เมื่อมี request มามันจะเกิดการเข้า queue โดยเราจะใช้ gunicorn มาเป็นตัวกลางในการรับส่งข้อมูล โดยใช้คำสั่งตามนี้

pip install gunicorn
gunicorn -w 1 -b 0.0.0.0:5000 hello:app
-w 1 หมายถึง สร้าง web process ขึ้นมา 1 process
-b 0.0.0.0:5000 หมายถึง อนุญาตให้เครื่องอื่นติดต่อเข้ามาผ่าน port เบอร์ 5000
hello:app หมายถึง Application ที่ทำงานคือ app ที่อยู่ใน hello.py

/project/app.yaml
# [START runtime]
runtime: python
env: flex
entrypoint: gunicorn -b :$PORT main:app

runtime_config:
  python_version: 3
เพื่อบอกว่าจะ deploy บน GAE บน Env ไหน (standard/flex) และ ใช้ python version อะไร

 config.py ประกาศค่าคงที่ไว้ที่ไฟล์ สำหรับส่งต่อให้ application

/project/config.py
SECRET_KEY = 'secret'
DATA_BACKEND = 'datastore'
PROJECT_ID = 'yuunai-app'
CLOUD_STORAGE_BUCKET = 'yuunai'
MAX_CONTENT_LENGTH = 8 * 1024 * 1024
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
GOOGLE_OAUTH2_CLIENT_ID = 'xxx'
GOOGLE_OAUTH2_CLIENT_SECRET = 'xxx'
GOOGLE_OAUTH2_CLIENT_ID = 'xxx'
GOOGLE_OAUTH2_CLIENT_SECRET = 'xxx'

2 ค่านี้ generate จาก credential หรือ ref

1. เลือก OAuth client ID

2. เลือก app type และ ระบุ ชื่อ ใน ตย. ใช้ Python Bookshelf Client
     ต้องใส่ redirect URI ด้วย เนื่องจาก การ authen ใช้ function call back ต้องชี้จุดกลับมาให้ด้วย

    - http://localhost:8080/oauth2callback
    - http://<app-id>.appspot.com/oauth2callback
    - https://<app-id>.appspot.com/oauth2callback
    - http://<app-id>.appspot-preview.com/oauth2callback
    https://<app-id>.appspot-preview.com/oauth2callback


3.  กด create จะได้
  - client id  ==>  GOOGLE_OAUTH2_CLIENT_ID
  - client secret   ==> GOOGLE_OAUTH2_CLIENT_SECRET



/project/main.py
import yuunai
import config

from flask import Flask, render_template
app = yuunai.create_app(config)

if __name__ == '__main__':
   app.run(host='127.0.0.1', port=8080,debug=True)
import application ชื่อ yuunai (directory) โดยที่ไฟล์ __init__.py มีประกาศฟังก์ชั่น create_app เอาไว้แล้วเพื่อรับค่าคงที่จาก config.py

/project/application/__init__.py
from flask import current_app, Flask, redirect, request, session, url_for
def create_app(config, debug=False, testing=False, config_overrides=None):
    app = Flask(__name__)
    app.config.from_object(config)

    app.debug = debug
    app.testing = testing

    if config_overrides:
        app.config.update(config_overrides)
    
    # Register the Bookshelf CRUD blueprint.
    from .crud import crud
    app.register_blueprint(crud, url_prefix='/page')

    # Add a default root route.
    @app.route("/")
    def index():
        return redirect(url_for('crud.list'))


    @app.errorhandler(500)
    def server_error(e):
        return """
        An internal error occurred: <pre>{}</pre>
        See logs for full stacktrace.
        """.format(e), 500

    return app

    # Register the Bookshelf CRUD blueprint.
    from .crud import crud
    app.register_blueprint(crud, url_prefix='/page')
ประกาศ import ไฟล์ crud.py และ ค่า url เริ่มต้นเมื่อมีการใช้งาน
ประกาศ blueprint เมื่อทุกฟังก์ชั่นถูกเรียกใช้จะอยู่ภายใต้ url = /page
การใช้ href ชี้มาใต้ /page จะมาเรียกใช้ crud.py

/project/yuunai/crud.py
from flask import Blueprint, redirect, render_template, request, url_for
crud = Blueprint('crud', __name__)
# [START list]
@crud.route("/")
def list():
    return render_template("list.html")
# [END list]
register crud ให้เป็น script คล้าย index.html

/project/yuunai/templates/base.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Yuunai : where r u</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
  </head>
  <body>
    <div class="navbar navbar-default">
      <div class="container">
        <div class="navbar-header">
          <div class="navbar-brand">Yuunai --></div>
        </div>
        <ul class="nav navbar-nav">
          <li><a href="#">search</a></li>
        </ul>
      </div>
    </div>
    <div class="container">
      {% block content %}{% endblock %}
    </div>
    {{user}}
  </body>
</html>

/project/yuunai/templates/list.html
{% extends "base.html" %}

{% block content %}

<h3>hello world</h3>
<a href="/books/add" class="btn btn-success btn-sm">
  <i class="glyphicon glyphicon-plus"></i>
  Add book
</a>



{% endblock %}
เมื่อใช้ extend และ block content เข้ามาจะช่วยให้สะดวกเรื่องการตบแต่งหน้าเวป ไม่ต้องมี code ซ้ำๆ กันหลายที่

ก่อนที่จะทำการ deploy
1. ทำตามบทความก่อนหน้า มี main.py (เปลี่ยนชื่อจาก app.py) , /templates
2. ทดสอบดูบน preview

หากใช้งานได้ลอง deploy โดย run command
gcloud app deploy

ได้ตัวอย่างดังนี้


















ไม่มีความคิดเห็น:

แสดงความคิดเห็น