• Python 全栈系列209 so_pack


    说明

    新部署的基本服务

    最近发现好些应用,特别是贴近某些具体功能的镜像里普遍都使用了SO加密。还有些连系统账户的权限都限制了,实在懒得去破解root密码。

    当然现在站在自己的角度,也认可这样的设置,共享不一定代表完全开源。有时候想做修改只是因为作者封装镜像的时候功能没考虑全,导致有些应用不太方便。

    我觉得我的分享会比这种(连文件都无法拷贝出镜像)的开放一些,我只是避免函数的源码泄露,理论上,要把某些组件考出镜像自己再去用是没问题的。

    既然大量的封装都涉及到制作SO文件,我想有必要做一个专门的服务来进行流水线处理。

    内容

    1 需求

    Q1: 哪些文件需要打包?
    A1: 主要是py文件,但也有可能是其他文件。

    Q2: 如何避免重复打包?
    A2: 根据哈希值进行真实的打包操作。

    Q3: 如何获取打包后的文件?
    A3: 提供文件的下载方式。

    Q4: 如何上传要打包的文件?
    A4: 可以使用接口,也可以采用文件上传形式。

    2 设计

    file In so out

    服务的工作机制很简单,收到用户发来的文件,或者字符,按照文件名和毫秒时间戳将其保存为文件(uploads); 保存后,读取文件的二进制字符,计算其哈希值 1 ◯ \text{\textcircled 1} 1

    用户、文件名、哈希、创建时间将会以日志方式存到数据库 2 ◯ \text{\textcircled 2} 2

    存储数据的时候,使用的是哈希码作为文件名(如果该哈希存在,则不会重复存储) 。 3 ◯ \text{\textcircled 3} 3程序会去查看转换文件目录(so_folder)是否已经有了该文件。如果没有,就进行转换。

    4 ◯ \text{\textcircled 4} 4最后程序重定向请求到下载视图,将对应的so文件发给用户。

    为了加快实施,现在做一些简化:该服务目前只接受可json的文件SO。用户通过接口将文件内容打包为字符串发过来,服务打包后直接返回一个附件。用户可以进行重命名。

    3 实现

    so_pack最后的交付形态是镜像

    免费的镜像仓库还是阿里比较大方,给了300个镜像的免费配额;腾讯就有点抠,只有100个。当然,不管是哪个其实都够用,目前我用阿里的感觉还不错。

    一些简单的,通用的镜像我会存在公有云仓库;关于镜像有一些概念要明确:

    • 1 只配置好基础环境配置的镜像叫基础镜像
    • 2 在特定主机挂载目录启动的镜像叫工作镜像

    假定启动的服务是多分身的

    有些是Server,启动时会消耗端口资源。所以在设计的时候,一定会至少分为一个端口。为了便于记忆,这个端口会加到镜像的名称中。但是,最终分配多少端口用于分身会在「微服务运行表」中。Port如果是标量,那么就只有一个端口;如果是列表,那么就可以启动若干微服务。
    更好的做法是,微服务分配的那个端口是发给nginx的,由nginx转发给其他分身(负载均衡)。

    一个镜像对应多个微服务,一个微服务对应多个运行实例。

    运行实例将会考虑运行的主机、端口列表(server类别);如果是worker类别会简单一些。

    接下来转入具体的实现讨论。

    • 1 使用简单API
    • 2 外挂文件夹,一个是original_files, 另一个是so_files
    • 3 一个接口(api_01)接收json(file_name, file_content),计算md5, 同时读取当前original_files的文件名列表。如果存在则放弃存储,否则存入。
    • 4 接口(api_01)同时去目标查看是否存在已有的so文件,如果有,直接重定向下载。
    • 5 如果接口(api_01)发现文件没有对用的so文件,调用接口(api_02)进行转换(现在会在原文件夹生成,再使用mv命令挪到指定位置)

    3.1 基础镜像制作

    git只做开发,具体的运行容器一定是基于镜像发布的(挂载必要的参数)

    将一些常用的配置直接挂上,以后就不必挂

    docker run -it \
     -v /etc/localtime:/etc/localtime  \
     -v /etc/timezone:/etc/timezone\
     -e "LANG=C.UTF-8"\
     -w /workspace\
     someregistry/image:v108 bash
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.2 制作工作镜像

    在开发时,挂载整个目录,这样便于调试(挂文件就不会随着git更新而检测变化了)

    • 调试时挂载目录
    opt_path="/opt/xxx"
    cur_port=24063
    
    docker run -it \
     --rm \
     -v ${opt_path}:/workspace \
     -p ${cur_port}:5555 \
     IMAGE bash
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    开发完毕后,启动一个空容器,然后把文件考进去。这个服务比较基础,信息等级我觉得可以归于II级初等,所以我就没有对文件加密,而且也传到公网。如果我希望保密的话,这些文件都可以转为SO。

    docker run -it \
     IMAGE bash
    
    docker cp ./XXX/static 276a5ef7cde0:/workspace/
    
    docker cp ./XXX/config.py 276a5ef7cde0:/workspace/config.py
    docker cp ./XXX/entry_py.py 276a5ef7cde0:/workspace/entry_py.py
    docker cp ./XXX/so_transfer_setup.py 276a5ef7cde0:/workspace/so_transfer_setup.py
    docker cp ./XXX/entry_sh.sh 276a5ef7cde0:/workspace/entry_sh.sh
    
    docker commit 276a5ef7cde0 WORK_IMAGE:v1
    docker push WORK_IMAGE:v1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.3 启动工作容器

    这样,在启动工作容器时,只需要考虑一些个性化的配置,一般来说:

    • 1 替换config文件
    • 2 设定一些保存结果的文件夹
    data_path="/data/"
    cur_port=24063
    image=WORK_IMAGE
    
    docker run -d \
     --name=xxx_m7_shard_0 \
     --restart=always \
     -e "LANG=C.UTF-8" \
     -p ${cur_port}:5555 \
     -v ${data_path}/original_files:/workspace/static/original_files \
     -v ${data_path}/so_files:/workspace/static/so_files \
     ${image} \
     sh -c "sh entry_sh.sh test" 
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这样只要指定了新的文件路径就可以了

    /data/original_files  /data/so_files
    hello2_231b4c8b7edd6194c363bd7b096df96e.py
    
    • 1
    • 2

    调用示例

    with open('hello.py','r') as f:
        x = f.read()
    
    x
    "def hello():\n    print('zzz')"
    
    file_dict = {'name':'hello2.py', 'content':x}
    
    the_so_resp  = req.post(host_ip +'sotify_a_file/',json = file_dict)
    
    if the_so_resp.ok:
        print('ok')
        the_headers = the_so_resp.headers['Content-Disposition']
        the_file_name = the_headers[the_headers.find('filename=')+9:]
        the_bin_content = the_so_resp.content
        with open(the_file_name,'wb') as f:
            print('Saving File %s' % the_file_name)
            f.write(the_bin_content)
    ---
    
    ok
    Saving File hello2_231b4c8b7edd6194c363bd7b096df96e.so
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    在这里插入图片描述

    4 结语

    总体上是达到了目标,也比预期花的时间多一些。比较奇怪的是,jupyter多次导入so内核会挂掉,我不知道为啥。

  • 相关阅读:
    JimuReport积木报表 v1.6.5 版本发布—免费报表工具
    从零用VitePress搭建博客教程(7) -– 如何用Github Actions自动化部署到Github Pages?
    网络安全(一):信息收集之玩转nmap(理论篇)
    全志R128应用开发案例——获取真随机数
    金仓数据库KingbaseES客户端编程接口指南-ado.net(3. KingbaseES 驱动在 .NET 平台的配置)
    基本if选择结构以及random
    【云原生&微服务五】Ribbon负载均衡策略之随机ThreadLocalRandom
    AlexNet论文笔记
    Python趣味算法入门 - 打鱼还是晒网
    机器学习笔记之支持向量机(三)模型求解
  • 原文地址:https://blog.csdn.net/yukai08008/article/details/128129554