手把手教您将 libreoffice 移植到函数计算平台

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
函数计算FC,每月15万CU 3个月
简介:

LibreOffice 是由文档基金会开发的自由及开放源代码的办公室套件。LibreOffice 套件包含文字处理器、电子表格、演示文稿程序、矢量图形编辑器和图表工具、数据库管理程序及创建和编辑数学公式的应用程序。借助 LibreOffice 的命令行接口可以方便地将 office 文件转换成 pdf。如下所示:

$ soffice --convert-to pdf --outdir /tmp /tmp/test.doc

一个完整版本的 LibreOffice 大小为 2 GB,而函数计算运行时缓存目录 /tmp 空间限制为 512M,zip 程序包大小限制为 50M。好在社区已经有项目 aws-lambda-libreoffice 成功的将 libreoffice 移植到 AWS Lambda 平台,基于前人的方法和经验,本人创建了 fc-libreoffice 项目,使 libreoffice 成功的运行在阿里云函数计算平台。fc-libreoffice 在 aws-lambda-libreoffice 的基础上解决了如下问题:

  1. 重新编译和裁剪 libreoffice ,使其适配 FC nodejs8 runtime 内置的 gcc 和内核版本;
  2. 安装运行时缺失的 libssl3 依赖;
  3. 借助 OSS 运行时下载解压,以绕过 zip 程序包 50M 的限制;
  4. 制作了一个 example 项目,支持一键部署,快速体验。

本文侧重于记述整个移植过程,记录关键步骤以备忘,也为类似的转换工具移植到函数计算平台提供参考。如果您对于如何快速搭建一个廉价且可扩展的 word 转换 pdf 云服务更感兴趣,可以阅读另一篇文章《五分钟上线——函数计算 Word 转 PDF 云服务》

准备工作

在开始之前建议找一个台配置较好的 Debain/Ubuntu 机器,libreoffice 编译比较消耗计算资源。并在机器上安装和配置如下工具:

  • docker-ce 安装方法参考官方安装文档
  • fun 一款函数计算的编排工具,用于快速部署函数计算应用。

    MacOS 平台可以使用如下方法安装

    brew tap vangie/formula
    brew install fun

    其他平台可以通过 npm 安装

    npm install @alicloud/fun -g
  • ossutil oss 的命令行工具。将其下载并放置到 $PATH 所在目录。

编译 libreoffice

我们会采用 fc-docker 提供的 aliyunfc/runtime-nodejs8:build docker 镜像进行编译。fc-docker 提供了一系列的 docker 镜像,这些 docker 镜像环境非常接近函数计算的真实环境。因为我们打算把 libreoffice 跑在 nodejs8 环境中,所以我们选用了 aliyunfc/runtime-nodejs8:build,build 标签镜像相比于其他镜像会多一些构建需要的基础包

启动一个编译环境

通过如下命令可启动一个用于构建 libreoffice 的容器。

docker run --name libre-builder --rm  -v $(pwd):/code -d -t --cap-add=SYS_PTRACE --security-opt seccomp=unconfined aliyunfc/runtime-nodejs8:build bash

上面的命令,我们启动了一个名为 libre-builder 的容器并把当前目录挂载到容器内文件系统的 /code 目录。附加参数 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined 是 cpp 程序编译需要的,否则会报出一些警告。-d 表示以后台 daemon 的方式启动。-t 表示启动 tty,配合后面的 bash 命令是为了卡主容器不退出。而 --rm 表示一旦容器停止了就自动删除容器。

安装编译工具

接下来进入容器安装编译工具

apt-get install -y ccache
apt-get build-dep -y libreoffice

ccache 是一个编译工具,可以加速 gcc 对同一个程序的多次编译。尽管第一次编译会花费长一点的时间,有了ccache,后续的编译将变得非常非常快。

apt-get 的 build-dep 子命令会建立某个要编译软件的环境。具体行为就是把所有依赖的工具和软件包都安装上。

克隆源码

git clone --depth=1 git://anongit.freedesktop.org/libreoffice/core libreoffice
cd libreoffice

记得加上 --depth=1 参数,因为 libreoffice 项目比较大,进行全量克隆会比较费时间,对于编译来说 git 提交历史没有意义。

配置并编译

# 如果多次编译,该设置可以加速后续编译
ccache --max-size 16 G && ccache -s

通过 --disable 参数去掉不需要的模块,以减少最终编译产物的体积。

# the most important part. Run ./autogen.sh --help to see wha each option means
./autogen.sh --disable-report-builder --disable-lpsolve --disable-coinmp \
    --enable-mergelibs --disable-odk --disable-gtk --disable-cairo-canvas \
    --disable-dbus --disable-sdremote --disable-sdremote-bluetooth --disable-gio --disable-randr \
    --disable-gstreamer-1-0 --disable-cve-tests --disable-cups --disable-extension-update \
    --disable-postgresql-sdbc --disable-lotuswordpro --disable-firebird-sdbc --disable-scripting-beanshell \
    --disable-scripting-javascript --disable-largefile --without-helppack-integration \
    --without-system-dicts --without-java --disable-gtk3 --disable-dconf --disable-gstreamer-0-10 \
    --disable-firebird-sdbc --without-fonts --without-junit --with-theme="no" --disable-evolution2 \
    --disable-avahi --without-myspell-dicts --with-galleries="no" \
    --disable-kde4 --with-system-expat --with-system-libxml --with-system-nss \
    --disable-introspection --without-krb5 --disable-python --disable-pch \
    --with-system-openssl --with-system-curl --disable-ooenv --disable-dependency-tracking

开始编译

make

最终的编译结果位于 ./instdir/ 目录下。

精简尺寸

使用 strip 命令去除二进制文件中的符号信息和编译信息

# this will remove ~100 MB of symbols from shared objects
strip ./instdir/**/*

删除不必要的文件

# remove unneeded stuff for headless mode
rm -rf ./instdir/share/gallery \
    ./instdir/share/config/images_*.zip \
    ./instdir/readmes \
    ./instdir/CREDITS.fodt \
    ./instdir/LICENSE* \
    ./instdir/NOTICE

验证

使用如下命令,测试一下编译出来的 soffice 是否能正常将 txt 文件转换成 pdf 文件。

echo "hello world" > a.txt
./instdir/program/soffice --headless --invisible --nodefault --nofirststartwizard \
    --nolockcheck --nologo --norestore --convert-to pdf --outdir $(pwd) a.txt

打包

# archive
tar -zcvf lo.tar.gz instdir

然后使用如下命令将 lo.tar.gz 文件从容器文件系统拷贝到宿主机文件系统。

docker cp libre-builder:/code/libreoffice/lo.tar.gz ./lo.tar.gz

Gzip vs Zopfli vs Brotli
Gzip 、Zopfli 和 Brotli 是三种开源的压缩算法,对于一个 130M 的 chromium 文件,分别采用这三种压缩算法最大 level 的压缩效果是

文件 算法 MiB 压缩比 解压耗时
chromium - 130.62 - -
chromium.gz Gzip 44.13 66.22% 0.968s
chromium.gz Zopfli 43.00 67.08% 0.935s
chromium.br Brotli 33.21 74.58% 0.712s

从上面的结果看 Brotli 算法的效果最优。

由于 aliyunfc/runtime-nodejs8:build 是基于 debain jessie 发行版的。在 debain jessie 上安装 brotli 较为麻烦,所以我们借助 ubuntu 容器安装 brotli 工具,将 tar.gz 格式转为 tar.br 格式。

docker run --name brotli-util --rm -v $(pwd):/root -w /root -d -t ubuntu:18.04 bash
docker exec -t brotli-util apt-get update
docker exec -t brotli-util apt-get install -y brotli
docker exec -t brotli-util gzip -d lo.tar.gz
docker exec -t brotli-util brotli -q 11 -j -f lo.tar

然后当前目录会多一个 lo.tar.br 文件。

安装依赖

在函数计算 nodejs8 环境中运行 soffice ,需要安装通过 npm 安装 tar.br 的解压依赖包 @shelf/aws-lambda-brotli-unpacker 和 通过 apt-get 安装 libnss3 依赖。先启动一个 nodejs8 的容器,以保证依赖的安装环境和运行时环境是一致的。

docker run --rm --name libreoffice-builder -t -d -v $(pwd):/code --entrypoint /bin/sh aliyunfc/runtime-nodejs8

注意:@shelf/aws-lambda-brotli-unpacker 存在 native binding,所以在开发机 MacOS 上 npm install 打包上传是无法工作。

docker exec -t libreoffice-builder npm install

由于函数计算运行时无法安装全局的 deb 包,所以需要将 deb 和依赖的 deb 包下载下来,再安装到当前工作目录而不是系统目录。当前工作目录下可以随代码一起打包上传。

docker exec -t libreoffice-builder apt-get install -y -d -o=dir::cache=/code libnss3
docker exec -t libreoffice-builder bash -c 'for f in $(ls /code/archives/*.deb); do dpkg -x $f $(pwd) ; done;'

libnss3 包含了许多 .so 动态链接库文件,linux 系统下 LD_LIBRARY_PATH 环境变量里的动态链接库才能被找到,而函数计算将代码目录/code 下的 lib 目录默认添加到了 LD_LIBRARY_PATH 中。所以我们写个脚本,把所有安装的 .so 文件软连接到 /code/lib 目录下

docker exec -t libreoffice-builder bash -c "rm -rf /code/archives/; mkdir -p /code/lib;cd /code/lib; find ../usr/lib -type f \( -name '*.so' -o -name '*.chk' \) -exec ln -sf {} . \;"

下载并解压 tar.br

为了使用 这个 lo.tar.br 文件,需要先上传到 OSS

ossutil cp $SCRIPT_DIR/../node_modules/fc-libreoffice/bin/lo.tar.br oss://${OSS_BUCKET}/lo.tar.br \
     -i ${ALIBABA_CLOUD_ACCESS_KEY_ID} -k ${ALIBABA_CLOUD_ACCESS_KEY_SECRET} -e oss-${ALIBABA_CLOUD_DEFAULT_REGION}.aliyuncs.com -f

在函数的 initializer 方法中下载。

module.exports.initializer = (context, callback) => {

    store = new OSS({
        region: `oss-${process.env.ALIBABA_CLOUD_DEFAULT_REGION}`,
        bucket: process.env.OSS_BUCKET,
        accessKeyId: context.credentials.accessKeyId,
        accessKeySecret: context.credentials.accessKeySecret,
        stsToken: context.credentials.securityToken,
        internal: process.env.OSS_INTERNAL === 'true'
    });

    if (fs.existsSync(binPath) === true) {
        callback(null, "already downloaded.");
        return;
    }

    co(store.get('lo.tar.br', binPath)).then(function (val) {
        callback(null, val)
    }).catch(function (err) {
        callback(err)
    });
};

然后借助于 @shelf/aws-lambda-brotli-unpacker npm 包解压 lo.tar.br

const {unpack} = require('@shelf/aws-lambda-brotli-unpacker');
const {execSync} = require('child_process');

const inputPath = path.join(__dirname, '..', 'bin', 'lo.tar.br');
const outputPath = '/tmp/instdir/program/soffice';

module.exports.handler = async event => {
  await unpack({inputPath, outputPath});

  execSync(`${outputPath} --convert-to pdf --outdir /tmp /tmp/example.docx`);
};

fun 部署函数

编写一个 template.yml 文件,将函数计算的配置都写在该文件中,然后使用 fun deploy 命令部署函数。

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  libre-svc: # service name
    Type: 'Aliyun::Serverless::Service'
    Properties:
      Description: 'fc test'
      Policies: 
        - AliyunOSSFullAccess
    libre-fun: # function name
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Handler: index.handler
        Initializer: index.initializer
        Runtime: nodejs8
        CodeUri: './'
        Timeout: 60
        MemorySize: 640
        EnvironmentVariables:
          ALIBABA_CLOUD_DEFAULT_REGION: ${ALIBABA_CLOUD_DEFAULT_REGION}
          OSS_BUCKET: ${OSS_BUCKET}
          OSS_INTERNAL: 'true'

真实场景下,把秘钥和一起变量写在 template.yml 里并不合适。为了做到代码和配置相分离,上面使用了变量占位符 ${ALIBABA_CLOUD_DEFAULT_REGION}${OSS_BUCKET}

然后使用 envsubst 进行替换

SCRIPT_DIR=`dirname -- "$0"`
source $SCRIPT_DIR/../.env

export ALIBABA_CLOUD_DEFAULT_REGION OSS_BUCKET
envsubst < $SCRIPT_DIR/../template.yml.tpl > $SCRIPT_DIR/../template.yml

cd $SCRIPT_DIR/../

上面所有的配置都写在了 .env 文件中,dotenv 是社区常见的方案,也有广泛的工具支持。

小结

本文重点介绍了编译 libreoffice 的过程,这也是移植中较为困难的部分。由于 libreoffice 又涉及到 npm 的 native binding 和 apt-get 安装到本地目录的问题,所以在函数计算依赖方面本例也是非常经典的场景。无论是编译还是依赖安装,本文中的步骤都强烈地依赖 fc-docker 镜像,正因为有了该镜像,解决了环境差异问题,大大降低了移植的难度。大文件运行时加载也是函数计算的常见问题,对于转换工具场景中常见的大文件是二进制程序,对于机器学习场景中大文件常是训练模型的数据问题,但是无论是哪一种,采用 OSS 下载解压的方法都是通用的,随着函数计算支持了 NAS,使用 NAS 挂载共享网盘的方式也是一种新的路径。

上文完整的源码可以在 fc-libreoffice 项目中找到。

参考阅读

  1. https://zh.wikipedia.org/wiki/LibreOffice
  2. How to Run LibreOffice in AWS Lambda for Dirty-Cheap PDFs at Scale
  3. https://github.com/alixaxel/chrome-aws-lambda
  4. https://github.com/shelfio/aws-lambda-brotli-unpacker
相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
5月前
|
人工智能 Serverless 数据库
在函数计算上部署专属的Agent平台
Agent 是公认的可以将AI能力跟具体业务结合的技术方案,而Agent Platform 是将Agent开发流程进行整合抽象后的平台工程,能够极大的提升Agent的构建和发布效率,帮助企业获取先机。本篇文章介绍如何在函数计算上部署一套Serverless架构的Agent平台- [AgentCraft](https://agentcraft-docs.serverless-developer.com/)
83376 7
在函数计算上部署专属的Agent平台
|
2月前
|
存储 人工智能 JSON
基于函数计算FC一键部署ComfyUI绘画平台体验
【8月更文挑战第11天】基于函数计算FC一键部署ComfyUI绘画平台体验
70 1
|
3月前
|
弹性计算 分布式计算 Serverless
全托管一站式大规模数据处理和分析Serverless平台 | EMR Serverless Spark 评测
【7月更文挑战第6天】全托管一站式大规模数据处理和分析Serverless平台 | EMR Serverless Spark 评测
23687 42
|
2月前
|
Cloud Native Java Serverless
一键上天!如何将Spring PetClinic瞬间迁移到云端函数计算平台
【8月更文挑战第8天】在现代云原生开发中,将Spring应用迁移到Serverless环境正成为趋势。本文通过对比传统部署与函数计算,指导如何快速部署Spring PetClinic应用。传统部署需手动配置服务器和中间件,而函数计算则免除了这些步骤,仅需上传代码。首先,准备好Spring PetClinic源码或jar包;接着选择函数计算平台,本文以阿里云为例;随后对应用进行适配,并使用Maven构建部署包;登录阿里云控制台上传jar包并配置HTTP触发器;最后测试应用确保正常运行。
38 3
|
2月前
|
Kubernetes Serverless 调度
异步任务处理系统问题之在阿里云函数计算平台上用户提交异步任务的问题如何解决
异步任务处理系统问题之在阿里云函数计算平台上用户提交异步任务的问题如何解决
|
2月前
|
监控 Java Serverless
美团 Flink 大作业部署问题之想在Serverless平台上实时查看Spring Boot应用的日志要怎么操作
美团 Flink 大作业部署问题之想在Serverless平台上实时查看Spring Boot应用的日志要怎么操作
|
3月前
|
人工智能 前端开发 搜索推荐
详解基于百炼平台及函数计算快速上线网页AI助手
通过阿里云百炼平台,企业可在10分钟内为其网站添加智能客服系统,提升用户体验并降低成本。流程包括:创建大模型应用、配置参数(如温度系数以控制回复的随机性)、发布应用获取API密钥;使用函数计算快速搭建示例网站,并通过简单的代码更改启用AI助手功能;还可导入私有知识库增强助手的能力。前端基于NLUX开发,支持定制化需求如样式调整和历史会话管理。服务端代码提供了调用大模型获取答案的接口。借助百炼平台,企业能迅速部署即时且个性化的在线服务,适应数字化转型的需求。
|
2月前
|
运维 安全 Serverless
Serverless 平台问题之面临的挑战如何解决
全托管Serverless计算平台优势包括:免运维一站式应用管理降低运营成本;精益成本按实际用量计费;支持毫秒级弹性伸缩确保业务连续性;简化容器化部署流程;内置微服务治理功能;集成多种云服务便于Web应用管理;支持开源任务调度框架;基于标准容器接口易于集成第三方工具;提供安全隔离的应用环境;以及与云生态产品的自动集成提供全面解决方案。
51 0
|
3月前
|
Java Serverless API
云原生应用问题之将文档中的代码部署在函数计算平台上会提升用户体验如何解决
云原生应用问题之将文档中的代码部署在函数计算平台上会提升用户体验如何解决
35 0
|
5月前
|
运维 数据挖掘 Serverless
阿里云Elasticsearch Serverless助力某电商平台公司实现商品订单数据的实时写入查询
某电商平台公司采用阿里云Elasticsearch Serverless解决方案,实现商品、订单和其他关键信息的写入和查询的实时响应。
410 1

相关产品

  • 函数计算
  • 下一篇
    无影云桌面