Serverless 工程实践 | 传统 Web 框架迁移-阿里云开发者社区

开发者社区> 阿里巴巴云原生> 正文
登录阅读全文

Serverless 工程实践 | 传统 Web 框架迁移

简介: 在 Serverless 这种新的架构或者说是新的编程范式下,使用全新的思路来做 Serverless 应用是再好不过的了,但实际并非如此。原生的 Serverless 开发框架是非常少的。以 Web 框架为例,目前主流的 Web 框架均不支持 Serverless 部署,所以将传统框架更简单、更快速、更科学地部署到 Serverless 架构上就是一个值得探讨的问题。

作者 | 刘宇


前言:与其说 Serverless 架构是一个新的概念 / 架构,不如说它是一个全新的思路、一种新的编程范式。


在 Serverless 这种新的架构或者说是新的编程范式下,使用全新的思路来做 Serverless 应用是再好不过的了,但实际并非如此。原生的 Serverless 开发框架是非常少的。以 Web 框架为例,目前主流的 Web 框架均不支持 Serverless 部署,所以将传统框架更简单、更快速、更科学地部署到 Serverless 架构上就是一个值得探讨的问题。


请求集成方案


请求集成方案实际上就是把真实的 API 网关请求直接传递给 FaaS 平台,而不在中途增加任何转换逻辑。以阿里云函数计算的 HTTP 函数为例,当想要把传统框架(例如 Django、Flask、Express、Next.js 等)部署到阿里云函数计算平台上,并且享受 Serverless 带来的按量付费、弹性伸缩等红利时,得益于阿里云函数计算的 HTTP 函数和 HTTP 触发器,使用者不仅可以快速、简单地将框架部署到阿里云函数计算上,还可以保持和传统开发一样的体验。以 Python 的 Bottle 框架为例,开发一个 Bottle 项目:


# index.py
import bottle

@bottle.route('/hello/<name>')
def index(name):
   return "Hello world"

if __name__ == '__main__':
   bottle.run(host='localhost', port=8080, debug=True)


当想要把该项目部署到阿里云函数计算上时,只需要增加一个 default_app 对象即可:


app = bottle.default_app()


整个项目实现如下:


# index.py
import bottle

@bottle.route('/hello/<name>')
def index(name):
   return "Hello world"

app = bottle.default_app()

if __name__ == '__main__':
   bottle.run(host='localhost', port=8080, debug=True)


当想在阿里云函数计算平台创建函数时,将函数入口设置为 index.app 即可。除了 Bottle 框架之外,其他的 Web 框架的操作方法是类似的。再以 Flask 为例:


# index.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
   return 'Hello, World!'

if __name__ == '__main__':
   app.run(
       host="0.0.0.0",
       port=int("8001")
   )


在配置函数的时候设置入口函数为 index.app,即可保证该 Flask 项目运行在函数计算平台上。


当然,除了使用已有的语言化的 Runtime,还可以考虑使用 Custom Runtime 和 Custom Container 来实现。例如,一个 Web 项目完成之后,我们可以编写一个 Bootstrap 文件(在 Bootstrap 文件中写一些启动命令即可),要启动一个 Express 项目,把 Express 项目准备好之后,直接通过 Bootstrap 实现:


#!/usr/bin/env bash
export PORT=9000
npm run star


除了上面的方法,其实阿里云函数计算还提供了更简单的 Web 框架迁移方案,即直接将传统 Web 框架迁移到函数计算中。我们在函数计算控制台找到应用中心,可以看到 Web 应用框架,如下图所示:


1.png

image.gif阿里云函数计算应用中心


选择好对应的环境之后,只需要上传代码、做好简单的配置,即可让传统 Web 项目运行在阿里云函数计算平台上。如果通过开发者工具进行部署,以 Serverless Devs 为例,可以先创建 index.py:


# -*- coding: utf-8 -*-
from bottle import route, run


@route('/')
def hello():
    return "Hello World!"
    


run(host='0.0.0.0', debug=False, port=9000)


然后编写资源和行为描述文件:


edition: 1.0.0          
name: functionApp      
access: defaule  

services:
   bottleExample:                                            #服务名称
       component: devsapp/bottle                             #组件名称
       actions:
           pre-deploy:                                       #在deploy之前运行
               - run: pip3 install -t . -r requirements.txt  #要运行的命令行
                   path: ./src                               #命令行运行的路径
       props:                                                #组件的属性值
           region: cn-shenzhen
           service:
               name: serverless-devs-bottle
               description: Serverless Devs示例程序
           function:
               name: bottle
               description: bottle项目
               memorySize: 256
               code:
                   src: ./src
               customContainerConfig:
                   command: '["python3"]'
                   args: '["./bottle/index.py"]'                                        #服务名称       component: devsapp/bottle                             #组件名称       actions:           pre-deploy:                                       #在deploy之前运行               - run: pip3 install -t . -r requirements.txt  #要运行的命令行                   path: ./src                               #命令行运行的路径       props:                                                #组件的属性值           region: cn-shenzhen           service:               name: serverless-devs-bottle               description: Serverless Devs示例程序           function:               name: bottle               description: bottle项目               memorySize: 256               code:                   src: ./src               customContainerConfig:                   command: '["python3"]'                   args: '["./bottle/index.py"]'


完成之后,执行 deploy 指令:


s deploy


部署如图所示:


2.png

image.gif在 Serverless Devs 上部署 bottle 框架


根据返回的网址,可以看到如下图所示的结果:image.gif

3.png

Serverless Devs 部署结果预览


综上所述,通过阿里云函数计算进行传统 Web 框架的部署和迁移是相对方便的,并且得益于 HTTP 函数与 HTTP 触发器,整个过程侵入性非常低。当然,将传统 Web 框架部署到阿里云函数计算时,可选方案也是比较多的。


  • 编程语言化的 Runtime:只需要写好函数入口即可。
  • Custom Runtime:只需要写好 Bootstrap 即可。
  • Custom Container:直接按照规范上传镜像文件即可。


部署途径也是多种多样的,具体如下。

  • 直接在控制台创建函数。
  • 在应用中心处创建 Web 应用。
  • 使用开发者工具直接部署。



其它方案


相对于阿里云的 HTTP 函数以及 HTTP 触发器而言,AWS、华为云、腾讯云等 FaaS 平台需要借助 API 网关以及转换层来实现将传统 Web 框架部署到 FaaS 平台。


如图所示,通常情况下使用 Flask 等框架实际上要通过 Web Server 进入下一个环节,而云函数更多是一个函数,本不需要启动 Web Server,所以可以直接调用 wsgi_app 方法。


4.png

传统WSGI Web Server工作原理示例


这里的 environ 就是需要对 event/context 等进行处理的对象,也就是所说的转换层要做的工作;start_response 可以认为是一种特殊的数据结构,例如 response 结构形态等。以 Flask 项目为例,在腾讯云云函数上,转换层结构如下:


import sys
import json
from urllib.parse import urlencode
from flask import Flask
try:
   from cStringIO import StringIO
except ImportError:
   try:
       from StringIO import StringIO
   except ImportError:
       from io import StringIO
from werkzeug.wrappers import BaseRequest
def make_environ(event):
   environ = {}
   for hdr_name, hdr_value in event['headers'].items():
       hdr_name = hdr_name.replace('-', '_').upper()
       if hdr_name in ['CONTENT_TYPE', 'CONTENT_LENGTH']:
           environ[hdr_name] = hdr_value
           continue
       http_hdr_name = 'HTTP_%s' % hdr_name
       environ[http_hdr_name] = hdr_value
   apigateway_qs = event['queryStringParameters']
   request_qs = event['queryString']
   qs = apigateway_qs.copy()
   qs.update(request_qs)
   body = ''
   if 'body' in event:
       body = event['body']
   environ['REQUEST_METHOD'] = event['httpMethod']
   environ['PATH_INFO'] = event['path']
   environ['QUERY_STRING'] = urlencode(qs) if qs else ''
   environ['REMOTE_ADDR'] = 80
   environ['HOST'] = event['headers']['host']
   environ['SCRIPT_NAME'] = ''
   environ['SERVER_PORT'] = 80
   environ['SERVER_PROTOCOL'] = 'HTTP/1.1'
   environ['CONTENT_LENGTH'] = str(len(body))
   environ['wsgi.url_scheme'] = ''
   environ['wsgi.input'] = StringIO(body)
   environ['wsgi.version'] = (1, 0)
   environ['wsgi.errors'] = sys.stderr
   environ['wsgi.multithread'] = False
   environ['wsgi.run_once'] = True
   environ['wsgi.multiprocess'] = False
   BaseRequest(environ)
   return environ
class LambdaResponse(object):
   def __init__(self):
       self.status = None
       self.response_headers = None
   def start_response(self, status, response_headers, exc_info=None):
       self.status = int(status[:3])
       self.response_headers = dict(response_headers)
class FlaskLambda(Flask):
   def __call__(self, event, context):
       if 'httpMethod' not in event:
           return super(FlaskLambda, self).__call__(event, context)
       response = LambdaResponse()
       body = next(self.wsgi_app(
           make_environ(event),
           response.start_response
       ))
       return {
           'statusCode': response.status,
           'headers': response.response_headers,
           'body': body
       }


当然,转换在某些情况下还是比较麻烦的,所以在很多时候,我们可以借助常见的开发者工具进行传统 Web 框架的部署,例如借助开源的开发者工具 Serverless Devs、Serverless Framework 等。


关于作者:

image.gif

刘宇(江昱)国防科技大学电子信息专业在读博士,阿里云 Serverless 产品经理,阿里云 Serverless 云布道师,CIO 学院特聘讲师。


新书推荐


IMG_9633.jpeg


本书会通过多个开源项目、多个云厂商的多款云产品,以及多种途径向读者介绍什么是 Serverless 架构、如何上手 Serverless 架构、不同领域中 Serverless 架构的应用以及如何从零开发一个 Serverless 应用等。本书可以帮助读者将 Serverless 架构融入到自己所在的领域,把 Serverless 项目真实落地,获得 Serverless 架构带来的技术红利。


Serverless 工程系列文


第一篇:Serverless 工程实践 | 从云计算到 Serverless

第二篇:Serverless 工程实践 | 细数 Serverless 的配套服务


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
阿里巴巴云原生
使用钉钉扫一扫加入圈子
+ 订阅

关注云原生中间件、微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生技术趋势、云原生大规模的落地实践

官方博客
最新文章
相关文章