专栏首页01ZOO从零开发无服务函数管理器:jupyter lab 插件
原创

从零开发无服务函数管理器:jupyter lab 插件

本文介绍如何制作一个 jupyter lab 的插件。作为例子,我们将制作一个运行在 jupyter 中的 serveless 函数的管理插件。和各种其他无服务函数不同的是:这是一个极其轻量级的 无服务函数 管理插件,不依赖任何其他组件,所有组件都会运行在 jupyter lab 内部。

1. 创建开发环境

1.1 安装 conda/miniconda

1.2 创建开发环境,装各种库

conda create -n jupyterlab-ext --override-channels --strict-channel-priority -c conda-forge -c anaconda jupyterlab cookiecutter nodejs git
conda activate jupyterlab-ext

2. 创建 repo

mkdir jupyter-lab-serverless

3. 创建插件项目

3.1 使用 cookiecutter 创建项目模板

➜ cookiecutter https://github.com/jupyterlab/extension-cookiecutter-ts --checkout v1.0
author_name []: u2takey
extension_name [myextension]: jupyter-lab-serverless
project_short_description [A JupyterLab extension.]: Create And Run Serverless Function in JupyterLab
repository [https://github.com/my_name/myextension]: https://github.com/u2takey/jupyter-lab-serverless

可以发现一些模板文件已经创建出来了

➜ cd jupyter-lab-serverless
➜ ll
.rw-r--r--  475 leiwang  6 Dec 19:16 README.md
.rw-r--r-- 1.2k leiwang  6 Dec 19:16 package.json
drwxr-xr-x    - leiwang  6 Dec 19:16 src
drwxr-xr-x    - leiwang  6 Dec 19:16 style
.rw-r--r--  555 leiwang  6 Dec 19:16 tsconfig.json

3.2 直接 Build安装 试一下

jlpm install
jupyter labextension install . --no-build

3.3 打开观察 第一次 安装的效果

jupyter lab --watch

# 打开 浏览器 console,可以看到
> JupyterLab extension jupyter-lab-serverless is activated!

4. 开始制作 severless 插件

这个插件将分为两个部分,一部分是 server 部分,一部分是前端部分. 我们将先创建后端部分。

4.1 server 插件部分

server 插件本质是一个 tornado handler,首先在 init 种实现load

def load_jupyter_server_extension(nb_server_app):
    初始化

定义 API,我们的函数 Server API主要的作用是完成 无服务函数的 增删查改,以及触发.

为了让实现更简单,我们用 put/delete 带函数名实现增删改,post/get 带函数名用于实现触发,而get不带函数名作为 查的实现,返回所有函数。同时为了 重启后函数能得到保存,我们使用 sqite作为本地保存(jupyter lab serverside的 state保存可能有更好的办法)。

class FunctionHandler(APIHandler):
    """
    A handler that manage serverless functions.
    """

    def initialize(self, app):
        self.logger = app.log
        self.db = app.db

    @web.authenticated
    @gen.coroutine
    def get(self, function=''):
        if function:
            # 触发
        else:
            # List
        
        
    def trigger_function(self, function, method, query, body):
        #  触发实现

    @gen.coroutine
    def post(self, function=''):
        #  触法

    @web.authenticated
    @gen.coroutine
    def put(self, function=''):
        #  增

    @web.authenticated
    @gen.coroutine
    def delete(self, function=''):
        #  删

函数的执行:

class Function(Base):
    __tablename__ = 'functions'

    # 各种字段略
    
    def __call__(self, *args, **kwargs):
        import imp
        module = imp.new_module(self.name)
        exec(self.script, module.__dict__)
        module.handle.logger = self.logger
        return module.handle(*args, **kwargs)

4.2 前端插件部分

在 index.js 中实现一个 xx Plugin 的继承

增加前端依赖的办法:

jlpm add @jupyterlab/apputils
jlpm add @jupyterlab/application
jlpm run build
/**
 * Initialization data for the jupyter-lab-serverless extension.
 */
const extension: JupyterFrontEndPlugin<void> = {
  id: 'jupyter-lab-serverless',
  requires: [IStateDB],
  autoStart: true,
  activate: activate
};

设计上,我们并没有使用 jupyterlab 插件中常用的 platte,而是增加 toolbar 上的两个 button。其中一个按钮设计为增加函数,另一个函数用于管理包括删除函数。

/**
 * Save Current Script as A Serverless Function
 */
export
class ButtonExtensionAdd implements DocumentRegistry.IWidgetExtension<NotebookPanel, INotebookModel> {

  createNew(panel: NotebookPanel, context: DocumentRegistry.IContext<INotebookModel>): IDisposable {
    let callback = () => {
      // 保存函数
    };
    let button = new ToolbarButton({
      className: 'serverless-add',
      iconClassName: 'fa fa-desktop',
      onClick: callback,
      tooltip: 'Save as Serverless Functions'
    });

    panel.toolbar.addItem('severless-add', button);
    return new DisposableDelegate(() => {
      button.dispose();
    });
  }
}

/**
 * Manager Serverless Function
 */
export
class ButtonExtensionManager implements DocumentRegistry.IWidgetExtension<NotebookPanel, INotebookModel> {
  delete(name: string){
    // 删除函数
  }
  createNew(panel: NotebookPanel, context: DocumentRegistry.IContext<INotebookModel>): IDisposable {
    let callback = () => {
      // 发送请求 获取函数
    };
    let button = new ToolbarButton({
      className: 'serverless-manager',
      iconClassName: 'fa fa-tasks',
      onClick: callback,
      tooltip: 'Show Serverless Functions'
    });

    panel.toolbar.addItem('severless-manager', button);
    return new DisposableDelegate(() => {
      button.dispose();
    });
  }
}

在回调函数中,我们实现 request 发送到刚刚实现的 server 插件,获取数据。

4.3 打包,发布插件

python3 setup.py sdist

// 发布后端插件
twine upload --skip-existing -u xx -p yy  dist/*

// 发布前端插件
npm publish --access=public

5. 使用演示

5.1. 启动

从镜像启动,镜像中到 jupyter 已经安装了 serveless 插件

docker run --rm -p 8888:8888 ccr.ccs.tencentyun.com/leiwang/jupterlab:serverless  /bin/bash  -c 'jupyter lab --ip=* --port=8888 --no-browser --allow-root'

5.2. 创建一个函数

函数需要有一个命名为 'handle' 的函数.

函数有个一个 logger,可以用于debug,logger输出内容将输出到 jupyter 后端。

def handle(event):
    logger = handle.logger
    logger.info(event)
    return event

点击保存按钮, 保存函数

image.png

5.3. 本地测试

本地测试有两种方式

一: 直接调用 handle 函数

二: 打开另一个 notebook,模拟 request 触发函数,检查效果

handle({})

注意 调用时需要带上notebook的 Authorization,这个在 jupyter notebook 启动时可以查看到。

import requests
headers = {'Authorization': 'token 4b917c156ea968fdafb81308324b06c5a9154596ebfcfd67'}
data = {"test": "testdata"}
r = requests.post('http://127.0.0.1:8888/function/test1.ipynb', json=data, headers=headers)

r.text
'{"code": "success", "data": {"method": "POST", "query": {}, "body": {"test": "testdata"}}}'

5.4. 管理函数

可以看到已经调用了一次

image.png

点击删除按钮,可以把 函数删除。

5.5. 定期执行函数

函数支持定期执行,schedule采用类似 https://schedule.readthedocs.io/en/stable/ 的语法表达方式

schedule 支持 'every'(默认1), 'unit'(默认为day), 'at' (默认为None) 三个参数

def handle(event):
    logger = handle.__dict__.get('logger')
    if logger:
        logger.info(event)
    return event

handle.schedule={'unit':'seconds'}

保存函数,观察后端日志,可以发现每秒被执行一次

image.png

5.6. 其他应用

可以用于接收 weekhook 做 ci/cd 触发,聊天机器人,定期执行脚本等等。

本文完整的代码在 https://github.com/u2takey/jupyter-lab-serverless

参考

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 从零开始用golang编写一个分布式测试工具

    当开发http 接口的时候,往往我们会关心开发的server能承受多少压力,这时候一个比较常用的工具是 apache bench。一部分情况下ab工具确实能满足...

    王磊-AI基础
  • Ansible for k8s

    后来出现了 kustomize,kustomize的设计并不依赖服务端,而是想做好本地渲染,然而几次使用之后发现,kustomize 显然还是失败了

    王磊-AI基础
  • 扩展 Kubernetes 之 FlexVolume And CSI

    kubernetes 的 volume 解决的 存储 state 的问题。State 有很多种存储方式,kubernetes 只关注其中的一部分

    王磊-AI基础
  • python——内置函数和匿名函数

    内置函数 接下来,我们就一起来看看python里的内置函数。截止到python版本3.6.2,现在python一共为我们提供了68个内置函数。它们就是pytho...

    用户1214487
  • Python模块化编程-内置函数#学习猿地

    > 内置函数就是在系统安装完python解释器时,由python解释器给提供好的函数

    学习猿地
  • NAT的3种实现方式配置示范

    NAT包括有静态NAT、动态地址NAT和端口多路复用地址转换三种技术类型。静态 NAT是把内部网络中的每个主机地址永久映射成外部网络中的某个合法地址;动态地址N...

    用户2398817
  • Python模块化编程-内置函数#学习猿地

    > 内置函数就是在系统安装完python解释器时,由python解释器给提供好的函数

    学习猿地
  • Python3学习笔记 | 十七、Python的语句与语法-文档

    在之前章节中,介绍的一些方法等,都是不全的。对于一个类型,有多少个方法、或者参数、属性等,需要查看文档。下面介绍Python里几种文档形式。

    TeamsSix
  • 复盘与PDCA

    上图是基于著名的 PDCA 循环(Plan\Do\Check\Action)而画,PDCA也称戴明环。PDCA 是为了提高质量的管理思想,是一套科学程序,注重管...

    崔文远TroyCui
  • iOS 启动连续闪退保护方案

    刘笑江

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动