部署基于 python wsgi web 框架的工程到函数计算

218 阅读5分钟
原文链接: click.aliyun.com

本文旨在介绍如何将基于 WSGI web 框架构建的工程部署到函数计算 python runtime 的具体操作过程,在介绍操作过程之前,先了解几个概念。

相关概念导读

函数计算 HTTP 触发器

HTTP 触发器是众多函数计算触发器中的一种,通过发送 HTTP 请求触发函数执行。主要适用于快速构建 Web 服务等场景。HTTP 触发器支持 HEAD、POST、PUT、GET 和 DELETE 方式触发函数。具体详情可以参考 HTTP 触发器

什么是WSGI

WSGI的全称是 Web Server Gateway Interface,简单翻译就是 Web 服务器网关接口。具体来说,WSGI 是一个规范,定义了 Web 服务器如何与 Python 应用程序进行交互,使得使用 Python 写的 Web 应用程序可以和 Web 服务器对接起来。最新官方版本是在 Python 的 PEP-3333 定义的。

WSGI 如何工作

在 WSGI 中定义了两个角色,Web 服务器端称为 server 或者 gateway,应用程序端称为 application 或者 framework(因为 WSGI 的应用程序端的规范一般都是由具体的框架来实现的,下面统一使用 server 和 application 这两个术语,WSGI 相当于是 Web 服务器和 Python 应用程序之间的桥梁。
server 端会先收到用户的请求,然后会根据规范的要求调用 application 端,然后 server 会将调用 application 返回的结果封装成 HTTP 响应后再发送给客户端,如下图所示:

wsgi-fc

如果想了解更多关于WSGI的内容,请查阅 PEP-3333

函数计算遇见 WSGI

FC python runtime 是 server,用户的函数是 application,applicaiton 可以完全自己实现,也可以基于 wsgi 的 web 框架上进行函数开发,具体可以参考 HTTP 触发器 Python-Runtime , 本文主要讲解如何运用 python wsgi 的 web 框架开发的工程部署到函数计算环境中。

Frameworks that run on WSGI

目前有很不少 Frameworks 是基于 WSGI 协议的,比如 Flask,Django 等,具体可以参考 Frameworks that run on WSGI, 本文讲解两个框架的的工程如何部署在函数计算中:

函数计算部署flask工程

本示例中我们会部署一个简单的基于 flask 的工程到函数计算中,runtime 是基于python2.7 , ( python3 步骤一样),具体步骤如下:

完整的示例代码包可以点击 flask-demo 下载。
代码包目录结构示意图:
f
1. 利用pip install -t . flask 命令将flask lib下载到和代码在同一个目录中,如下图所示:

flask
main.py 代码如下:

  #!/usr/bin/env python
  # coding=utf-8
  from flask import Flask
  from flask import request
  from flask import make_response

  app = Flask(__name__)

  base_path = ''

  @app.route('/', methods=['GET', 'POST'])
  def home():
      resp = make_response('<h1>Home<h1>', 200)
      return resp

  @app.route('/signin', methods=['GET'])
  def signin_form():
      global base_path
      html = '''<form action="{}/signin" method="post">
           <p><input name="username"></p>
           <p><input name="password" type="password"></p>
           <p><button type="submit">Sign In</button></p>
           </form>'''.format(base_path)

      resp = make_response(html, 200)
      return resp

  @app.route('/signin', methods=['POST'])
  def signin():
      if request.form['username']=='admin' and request.form['password']=='password':
          html = '<h3>Hello, admin!</h3>'
      else:
          html = '<h3>Bad username or password.</h3>'
      resp = make_response(html, 200)
      return resp

  def handler(environ, start_response):
      import urlparse 
      parsed_tuple = urlparse.urlparse(environ['fc.request_uri'])
      li = parsed_tuple.path.split('/')
      global base_path
      if not base_path:
          base_path = "/".join(li[0:5])
      return app(environ, start_response)
2. 执行下面的脚本,创建对应的 service,function 和 HTTP 触发器
  #!/usr/bin/env python
  # coding=utf-8
  import fc2
  client = fc2.Client(
      endpoint='<your endpoint>', # your endpoint
      accessKeyID='<your ak_id>', # your ak_id
      accessKeySecret='<your ak_secret>' # your ak_secret
      )

  service_name = 'flask-demo'
  funciton_name = 'test'
  trigger_name = "my_trigger"

  client.create_service(service_name)
  
  client.create_function(
      service_name, funciton_name, 'python2.7',  'main.handler',
      codeDir='./flaskDir/')

  res = client.get_function(service_name, funciton_name)
  print res.data
  trigger_config = {
              "authType" : "anonymous",
              "methods" : ["GET", "POST"],
      }
  client.create_trigger(service_name, funciton_name , trigger_name, "http", trigger_config, "dummy_arn", "")
  print client.get_trigger(service_name,  funciton_name, trigger_name).data
3. 脚本执行成功后,给函数创建了 HTTP 触发器,就可以通过 url ( ( a c c o u n t − i d ) ." role="presentation">(account-id).(region).fc.aliyuncs.com/2016-08-15/proxy/serviceName/functionName/action?hello=world) 访问函数,打开浏览器可以看到如下效果:
注:如果 account-id 为 12345 ,region 为 cn-shanghai , serviceName为flask-demo, functionName 为test, 那么访问函数的 url 就是 12345.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/flask-demo/test/
  • Home
    p1
  • Sign In
    p2
  • 提交正确的用户名和密码
    p3
  • 提交错误的用户名和密码
    p4

函数计算部署 django 工程

本示例中我们会部署一个简单的基于 django 的工程到函数计算中,runtime 是基于python 2.7 , (python 3 步骤一样), 具体步骤如下:

完整的示例代码包可以点击 django-demo 下载。
代码包目录结构示意图:
d
1. 利用pip install -t . django 命令将 django lib 下载到和代码在同一个目录中。

main.py 代码如下:

  #!/usr/bin/env python
  # coding=utf-8
  import sys
  import os

  # load local django
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
  sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "HelloWorld"))

  import django

  print (django.__version__)

  import HelloWorld.view

  from HelloWorld.wsgi import application

  def handler(environ, start_response):
      import urlparse
      parsed_tuple = urlparse.urlparse(environ['fc.request_uri'])
      li = parsed_tuple.path.split('/')
      global base_path
      if not HelloWorld.view.base_path:
          HelloWorld.view.base_path = "/".join(li[0:5])
      return application(environ, start_response)
  
2. HelloWorld 工程目录如下:
  |____HelloWorld
  | |______init__.py
  | |____view.py
  | |____settings.py # 视图文件
  | |____urls.py # 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"。
  | |____wsgi.py 
  |____db.sqlite3
  |____manage.py

urls.py 代码如下:

  from django.conf.urls import url
  from django.contrib import admin
  from . import view

  urlpatterns = [
      url(r'^admin/', admin.site.urls),
      url(r'^$', view.home),
      url(r'^signin$', view.signin),
      url(r'^signin_form$', view.signin_form),
  ]

view.py 代码如下:

  #!/usr/bin/env python
  # coding=utf-8
  from django.views.decorators.csrf import csrf_exempt
  from django.http import HttpResponse

  base_path = ""

  def home(request):
      return HttpResponse("<h1>Home</h1>", status=200)

  def signin_form(request):
      # action url 中的service_name,function_name need replace
      html = '''<form action="{}/signin" method="post">
           <p><input name="username"></p>
           <p><input name="password" type="password"></p>
           <p><button type="submit">Sign In</button></p>
           </form>'''.format(base_path)

      resp = HttpResponse(html,status=200)
      return resp

  @csrf_exempt
  def signin(request):
      if request.POST['username']=='admin' and request.POST['password']=='password':
          html = '<h3>Hello, admin!</h3>'
      else:
          html = '<h3>Bad username or password.</h3>'
      resp = HttpResponse(html, status=200)
      return resp
3. 执行下面的脚本,创建对应的 service ,function 和 HTTP 触发器
  #!/usr/bin/env python
  # coding=utf-8
  import fc2
  client = fc2.Client(
      endpoint='<your endpoint>', # your endpoint
      accessKeyID='<your ak_id>', # your ak_id
      accessKeySecret='<your ak_secret>' # your ak_secret
      )

  service_name = 'django-demo'
  funciton_name = 'test'
  trigger_name = "my_trigger"

  client.create_service(service_name)
  
  client.create_function(
      service_name, funciton_name, 'python2.7',  'main.handler',
      codeDir='./djangoDir/')

  res = client.get_function(service_name, funciton_name)
  print res.data
  trigger_config = {
              "authType" : "anonymous",
              "methods" : ["GET", "POST"],
      }
  client.create_trigger(service_name, funciton_name , trigger_name, "http", trigger_config, "dummy_arn", "")
  print client.get_trigger(service_name,  funciton_name, trigger_name).data
4. 脚本执行成功后,给函数创建了 HTTP 触发器,就可以通过 url ( ( a c c o u n t − i d ) ." role="presentation">(account-id).(region).fc.aliyuncs.com/2016-08-15/proxy/serviceName/functionName/action?hello=world) 访问函数,打开浏览器可以看到如下效果:
注:如果 account-id 为 12345,region 为 cn-shanghai , serviceName 为 fcService , functionName 为 test , 那么访问函数的 url 就是 12345.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/fcService/test/
  • home

d1

  • signin_form

d2

  • 提交正确的用户名和密码

d3

  • 提交错误的用户名和密码
d4