Serverless 实战 —— Funcraft + OSS + ROS 进行 CI/CD

本文涉及的产品
可观测监控 Prometheus 版,每月50GB免费额度
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
云原生网关 MSE Higress,422元/月
简介: 本文打算以一个简单的函数计算项目为例,在此基础上编写测试用例,进行配置,让其支持 CI/CD 工作流程。实现如下四个小目标:1. CI 被 git commit 提交触发;2. 执行测试(单元、集成和端对端);3. 函数打包上传到 OSS;4. 通过 ROS 部署函数到 Staging 环境。

前言

首先介绍下在本文出现的几个比较重要的概念:

函数计算(Function Compute)函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考
Funcraft:Funcraft 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档参考
OSS: 对象存储。海量、安全、低成本、高可靠的云存储服务,提供99.9999999999%的数据可靠性。使用RESTful API 可以在互联网任何位置存储和访问,容量和处理能力弹性扩展,多种存储类型供选择全面优化存储成本。
ROS:资源编排(ROS)是一种简单易用的云计算资源管理和自动化运维服务。用户通过模板描述多个云计算资源的依赖关系、配置等,并自动完成所有资源的创建和配置,以达到自动化部署、运维等目的。编排模板同时也是一种标准化的资源和应用交付方式,并且可以随时编辑修改,使基础设施即代码(Infrastructure as Code)成为可能。
CI/CD: CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD 的核心概念是持续集成、持续交付和持续部署。

目标

本文打算以一个简单的函数计算项目为例,在此基础上编写测试用例,进行配置,让其支持 CI/CD 工作流程。实现如下四个小目标:

  1. CI 被 git commit 提交触发
  2. 执行测试(单元、集成和端对端)
  3. 函数打包上传到 OSS
  4. 通过 ROS 部署函数到 Staging 环境

工作流程图

image.png

这里以大家熟悉的 Github 仓库为例,并结合 Travis CI 。当用户往示例项目 push 或者 PR(Pull Request)时,会自动触发 Travis CI 的工作任务,进行单元测试、构建打包和部署发布。

示例项目

示例项目地址为:https://github.com/vangie/tz-time ,该项目是基于 FC Http trigger 实现的简单 web 函数,访问放函数是会返回指定时区的当前时间。项目目录结构如下

tz-time
├── .funignore
├── .travis.yml
├── Makefile
├── bin
│   ├── delRosStack.sh
│   ├── deployE2EStack.sh
│   └── waitForServer.sh
├── deploy.log
├── index.e2e-test.js
├── index.integration-test.js
├── index.js
├── index.test.js
├── jest.config.e2e.js
├── jest.config.integration.js
├── package-lock.json
├── package.json
└── template.yml

部分文件作用介绍:

  • .funignore - Funcraft 部署时忽然的文件清单
  • .travis.yml - Travis CI 配置文件
  • index.js - 函数入口文件
  • *.test.js - 单元测试相关文件
  • *.integraion-test.js - 集成测试相关文件
  • *.e23-test.js - 端对端测试相关文件
  • template.yml - ROS 描述文件,用于描述函数和其他云服务

自动化测试

测试通常非常如下三类:单元测试、集成测试和 E2E 测试。在函数计算场景下,这三类测试可以通过如下方法实现。

  • 单元测试 - 使用 Mock 类测试函数,验证输入输出参数
  • 集成测试 - 使用 fun local invoke/start 模拟运行函数
  • E2E 测试 - 使用 fun deploy 部署一套 test 环境,然后通过 fun invoke 进行模拟调用或者通过 curl 直接发送

本例子只实现了单元测试,集成测试和 E2E 测试对于 travis 示例来说触发方法类似,实现方法可以参见上面的方法提示进行配置。

单元测试

FC 函数的单元测试和普通的函数并无二致。采用熟悉的单元测试框架即可,本例中使用了 jest 进行测试。下面看看一个测试用例的代码片段

jest.mock('moment-timezone');

const { tz } = require('moment-timezone');
const { handler } = require('./index');

const EXPECTED_DATE = '2018-10-01 00:00:00';
const TIMEZONE = 'America/New_York';

describe('when call handle', () => {
    it('Should return the expected date if the provied timezone exists', () => {
        const mockReq = {
            queries: {
                tz: TIMEZONE
            }
        }
        const mockResp = {
            setHeader: jest.fn(),
            send: jest.fn()
        }

        tz.names = () => [TIMEZONE];
        tz.mockImplementation(() => {
            return {
                format: () => EXPECTED_DATE 
            }
        })

        handler(mockReq, mockResp, null);

        expect(mockResp.setHeader.mock.calls.length).toBe(1);
        expect(mockResp.setHeader.mock.calls[0][0]).toBe('content-type');
        expect(mockResp.setHeader.mock.calls[0][1]).toBe('application/json');

        expect(mockResp.send.mock.calls.length).toBe(1);
        expect(mockResp.send.mock.calls[0][0]).toBe(JSON.stringify({
            statusCode: '200',
            message: `The time in ${TIMEZONE} is: ${EXPECTED_DATE}`
        }, null, '    '));

    });
});

通过 jest.mock 对 moment-timezone 进行 mock,让 tz 被调用的时候返回预先设定好的值,而不是一个动态变化的时间。

通常该类单元测试分为三步:

  1. mock 依赖的值或者参数
  2. 调用测试函数
  3. 断言返回结果和被调用的参数

如果依赖包不存在原生依赖(依赖 linux 下的可执行文件或者 so 库文件)的使用 npm test 触发测试即可,如果有原生依赖,那测试需要跑在 fun 提供的 sbox 模拟环境里,使用如下命令触发

fun install sbox -f tz-time --cmd 'npm install'

集成测试

本例子中的集成测试会借助 fun local start 命令把函数在本地启动起来,由于函数配置了 http trigger,所以可以通过 http 请求调用函数。

集成测试我们还是才是 jest 框架进行编写,为了区别于单元测试文件 *.test.js ,集成测试文件使用 .integration-test.js 文件后缀。为了让 jest 命令独立的跑集成测试用例而不是和单元测试混和在一起,需要编撰如下文件 jest.config.integration.js

module.exports = {
    testMatch: ["**/?(*.)integration-test.js"]
};

然后在 package.json 中配置 scripts

"scripts": {
    "integration:test": "jest -c jest.config.integration.js"
}

于是可以通过执行 npm run integration:test 来执行集成测试。

然后在此基础上在 Makefile 中添加 integration-test 目标:

funlocal.PID:
    fun local start & echo $$! > $@

integration-test: funlocal.PID 
    bin/waitForServer.sh http://localhost:8000/2016-08-15/proxy/tz-time/tz-time/
    npm run integration:test
    kill -2 `cat $<` && rm $<

integration-test 目标依赖 funlocal.PID 目标,后者负责启动一个 fun local 进程,该进程会在本地启动 8000 端口。解读一下上面的 Makefile 代码

  • fun local start & echo $$! > $@ 启动 fun local 进程,并将进程 PID 写入到目标同名文件 funlocal.PID
  • bin/waitForServer.sh http://localhost:8000/2016-08-15/proxy/tz-time/tz-time/ 通过一个 url 测试 fun local 进程是否启动完成。
  • kill -2 `cat $<` && rm $< 测试完成以后销毁 fun local 进程。

npm run integration:test 会启动若干的测试用例,其中一个测试用例如下:

const request = require('request');

const url = 'http://localhost:8000/2016-08-15/proxy/tz-time/tz-time/';

describe('request url', () => {
    it('without tz', (done) => {
        request(url, (error, response, data) => {
            if (error) {
                fail(error);
            } else {
                const resData = JSON.parse(data);
                expect(resData.statusCode).toBe(200);
                expect(resData.message).toContain('Asia/Shanghai');
            }
            done();
        });
    });
});

端对端测试

端对端测试和集成测试的测试用例非常的类似,区别在于测试的服务端,端对端测试部署一套真实的环境,集成测试通过 fun local 本地模拟。

本例中借助 fun deploy --use-ros 部署一套环境,环境名称为 tz-e2e- 前缀带上时间戳,这样每次测试都会部署一套新的环境,不同环境之间相互不会影响。测试完成再通过 aliyun-cli 工具把 ROS 的 stack 删除即可。

下面端对端测试的 Makefile 目标:

stack_name := tz-e2e-$(shell date +%s)

e2e-test:
    # deploy e2e 
    bin/deployE2EStack.sh $(stack_name)
    # run test
    npm run e2e:test
    # cleanup
    bin/delRosStack.sh $(stack_name)
  • bin/deployE2EStack.sh $(stack_name) 负责部署一个新的 ROS stack。部署之前需要使用 fun package 构建交付物,具体如何构建交付物可以参考下一小节。
  • npm run e2e:test 运行端对端测试
  • bin/delRosStack.sh $(stack_name) 测试完成之后,清理部署的 ROS stack,会释放掉响应的云资源。

构建交付物

fun package 命令可被用于构建交付物,fun package 需要指定一个 OSS 的 bucket。fun package 命令会完成如下步骤:

  1. 将代码编译打包成 Zip 文件。
  2. 上传代码包到 OSS bucket。
  3. 生成新的文件 template.packaged.yml,其中代码本地地址改为 OSS bucket 地址。

生成的 template.packaged.yml 文件就是最终交付物,可以通过 fun deploy 命名进行部署。

持续部署

当构建环节生成了交付物以后,就可以通过 fun deploy 进行部署了。持续部署需要解决如下两个问题:

  1. 支持全新部署和升级部署
  2. 一套资源描述支持部署多套,比如 test 环境、staging 环境和 production 环境。

fun deploy 借助于 ROS,可以轻松的解决上述问题。

fun deploy --use-ros --stack-name tz-staging --assume-yes

其中:

  • --use-ros 表示借助于 ROS 进行部署,其工作机制是将 template.yml 推送到 ROS 服务,由 ROS 服务执行每个服务的新建和更新操作。如果没有该参数,fun 就会在本地解析 template.yml,调用 API 进行资源创建。ROS 有个额外的好处是可以进行部署的回滚,失败的时候能自动进行回滚。
  • --stack-name 指定一个 stack 的名称,stack 是 ROS 的概念,可以理解为一套环境。
  • --assume-yes 用于无人值守模式,跳过确认提示。

注意,此处如果不指定参数 --use-ros ,fun deploy 会采用直接调用云资源 API 进行部署, 这是 fun deploy 的默认部署方式,虽然也基本实现了幂等部署,但是仅支持部署有限的云资源(FC、OTS、API Gateway等),远不及 ROS 丰富,而且也没法做到 ROS 已支持的回滚和一键删除,所以此处不推荐。

小结

上面所有步骤的脚本化配置可以参考 Makefile 和 .travis.yml 文件。通过上述两个文件可以实现 Github 和 Travis CI 的联动,实现基于代码提交触发的 CI/CD。

本文讲述了 FC 函数

  1. 如何进行测试,特别是单元测试的自动化
  2. 如何构建交付物,通过 fun package 将代码文件上传到 OSS Bucket,让交付物编程一个文本描述文件 template.packaged.yml
  3. 如何持续部署,借助于 ROS 部署多套环境,多次更新同一套环境。

参考阅读

  1. 源码示例项目 tz-time
  2. 开发函数计算的正确姿势 —— 使用 ROS 进行资源编排
  3. Funcraft
  4. Aliyun Serverless VSCode Extension

阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

相关实践学习
【AI破次元壁合照】少年白马醉春风,函数计算一键部署AI绘画平台
本次实验基于阿里云函数计算产品能力开发AI绘画平台,可让您实现“破次元壁”与角色合照,为角色换背景效果,用AI绘图技术绘出属于自己的少年江湖。
从 0 入门函数计算
在函数计算的架构中,开发者只需要编写业务代码,并监控业务运行情况就可以了。这将开发者从繁重的运维工作中解放出来,将精力投入到更有意义的开发任务上。
相关文章
|
6月前
|
存储 编解码 Serverless
Serverless架构下的OSS应用:函数计算FC自动处理图片/视频转码(演示水印添加+缩略图生成流水线)
本文介绍基于阿里云函数计算(FC)和对象存储(OSS)构建Serverless媒体处理流水线,解决传统方案资源利用率低、运维复杂、成本高等问题。通过事件驱动机制实现图片水印添加、多规格缩略图生成及视频转码优化,支持毫秒级弹性伸缩与精确计费,提升处理效率并降低成本,适用于高并发媒体处理场景。
318 0
|
7月前
|
存储 Java Linux
SpringBoot × MinIO 极速开发指南:对象存储服务高可用实战
生成临时访问URL接口和文件预览其实是同一个方法,只是文件预览内定了七天访问,而这个方法可以自行制定,单位是秒。方法,所以返回的是地址信息,但是可以通过dubug看到Bucket中的属性,确实是当前所有桶信息。配置类中奖MinIOClient客户端注入到Springboot中。MinIO集群的在Linux上的部署可以参考:​​​​​​​。Nginx代理MinIO集群可以参考:​​​​​​​。从Bucket源码可以看出,并没有实现。
682 0
|
10月前
|
Cloud Native 安全 Serverless
云原生应用实战:基于阿里云Serverless的API服务开发与部署
随着云计算的发展,Serverless架构日益流行。阿里云函数计算(Function Compute)作为Serverless服务,让开发者无需管理服务器即可运行代码,按需付费,简化开发运维流程。本文从零开始,介绍如何使用阿里云函数计算开发简单的API服务,并探讨其核心优势与最佳实践。通过Python示例,演示创建、部署及优化API的过程,涵盖环境准备、代码实现、性能优化和安全管理等内容,帮助读者快速上手Serverless开发。
|
10月前
|
SQL 分布式计算 Serverless
基于阿里云 EMR Serverless Spark 版快速搭建OSS日志分析应用
基于阿里云 EMR Serverless Spark 版快速搭建OSS日志分析应用
242 0
|
消息中间件 弹性计算 关系型数据库
体验函数计算:高效处理多媒体文件的真实感受与实战总结
该方案在引导和文档方面做得较为详尽,仅在事件驱动机制部分略显简略。部署和代码示例实用,但需注意内存配置以避免超时。使用体验方面,函数计算表现出色,尤其在高并发场景下,显著提升了应用稳定性和成本效益。云产品如OSS、MNS等与函数计算配合流畅,ECS和RDS表现稳健。总体而言,这套方案弹性好、成本低,特别适合应对高并发或流量不确定的场景,值得推荐。
161 27
|
存储 对象存储
radosgw高可用对象存储网关实战指南
关于radosgw高可用对象存储网关的实战指南,涵盖了从基本概念到具体操作案例,再到架构设计和使用技巧的全面介绍。
379 6
|
存储 运维 Serverless
函数计算产品使用问题之OSS触发器是否可以只设置文件前缀
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
存储 前端开发 Serverless
Serverless 魔法之旅:如何用 Funcraft、OSS 和 ROS 打造超级CI/CD流水线!
【8月更文挑战第8天】在现代软件开发中,CI/CD对于提升效率与代码质量至关重要。本文介绍如何运用阿里云的Serverless服务——Funcraft、OSS及ROS构建完整的CI/CD流程。首先配置Funcraft实现代码自动化构建与部署;接着利用OSS管理静态文件,确保网站内容正确加载;最后借助ROS自动化资源创建与管理,实现代码自动部署。通过整合这些服务,不仅加速了开发进程,还保证了代码质量和部署一致性,充分发挥Serverless架构的优势。
346 5
|
监控 Java Serverless
函数计算产品使用问题之对于OSS打包的zip的保存目录,该如何操作
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
217 6

相关产品

  • 函数计算
  • 推荐镜像

    更多
  • ros