直击前沿技术:云原生应用低代码开发平台实践

2022-01-21 20:08:10 浏览数 (1)

近几年来,低代码和开发平台成为了技术圈子的热点话题。

很多企业也开始尝试使用低代码来快速地搭建应用,从而减少开发成本和运维成本。FreeWheel核心业务开发团队在打造云原生微服务架构的过程中,搭建新服务的需求在日趋增多。

为了应对这一挑战,我们研发了基于AWS的低代码开发平台。本文就从低代码和开发平台的基本概念讲起,带你体验低代码开发实践之路。

1什么是低代码和开发平台

低代码(Low-code)是一种全新的开发范式,开发人员仅仅需要少量的代码甚至是0代码就可以快速地完成服务的搭建。

低代码开发平台(Low-Code Development Platform, LCDP)是基于低代码和可视化界面的开发平台。咨询公司Forrester Research在2014年6月首次给出了它的定义:

低代码开发平台旨在通过很少的代码降低服务在全生命周期的开发成本,从而实现业务的快速交付。

从定义不难看出,低代码开发平台是一种提效降本的重要手段。为此,低代码开发平台应当具备以下3种能力。

1. 可视化界面

可以把低代码开发平台理解成一种IDE。

用户可以从它的组件库里以可视化甚至是拖拽的方式,像搭积木一样完成服务的创建。

另外,和传统的IDE如Visual Studio的MFC所支持的可视化能力相比,低代码开发平台应当有能力支持端到端的可视化编程。

2. 规模化生产

用户往往需要搭建不同类型的服务,甚至是不同语言的服务,这就需要平台具备规模化生产的能力。可以通过提供服务模板功能来做到这一点。不同的模板对应不同的业务场景下的最佳实践,用户搭建服务时选择合适的模板即可。

3. 全生命周期管理

低代码开发平台支持软件的全生命周期的管理。

通过平台,我们不仅可以轻松地设计并开发服务,也能够一键部署服务,还能够满足服务的运维的需求。

平台对服务生命周期的管理也会带来聚合效应,使得平台成为服务的百科全书。

2低代码开发平台的优势

事实上,低代码开发的诞生可以追溯到2000年代初期,典型代表如Visual Studio的MFC等工具。但与这些早期工具不同的是,低代码不等于零代码,而是要少写代码,比如通过少写重复代码来提高生产力,通过少写基础代码来屏蔽底层技术细节等。

那么,低代码开发平台可以给企业带来什么呢?

  • 提效降本

低代码开发平台致力于以工业化标准化的方式替代传统手工作坊式的软件开发。平台提供了众多的基础设施、公共组件、自动化流水线等功能。这使业务团队能够从重复的工作中释放出来,更多的聚焦在业务本身。

  • 质量保证

质量保证始终是软件开发绕不开的话题。线上故障频发,项目延期交付甚至成了行业常态。低代码开发平台的引入将规范化软件开发的流程,减少人工出错的可能。

  • 团队协作

软件开发过程非常的复杂,往往也需要不同职能团队的配合。

但在传统的开发模式下,各个团队往往各司其职,长期来看会形成团队壁垒,使得跨团队的沟通极其低效。

而低代码开发平台的引入会使得诸如业务开发团队、基础设施开发团队、运维团队工作在同一个平台下,轻松打破团队间的壁垒,实现高效地协作。

3低代码开发平台构建之路

经过数月的开发、试错与重构,团队打造了基于AWS云原生的低代码开发平台——Bingo

该平台包含了一套Web UI,用户可以通过可视化界面创建新的服务。平台对规模化生产、CICD、监控、日志等功能提供了支持。

随着平台功能的不断完善,运维团队人员也加入了Bingo开发团队,Bingo平台开始在推广使用。

▊ 平台的技术选型

针对Bingo平台,我们从以下几个方面进行了技术对比和选型。

  • 前端技术栈

前端技术栈选择了React。一方面,React有着非常成熟的社区与生态;另外一方面,开发团队有着丰富的React使用经验。

  • 后端技术栈

后端编程语言选择了Golang。和其他Web框架如Ruby on Rails相比,Golang使用虽然烦琐,却有着更好的性能。此外,这也与团队微服务的技术栈一致。

  • 数据存储

数据库选择了Amazon Relational Database Service(RDS)。文件存储选择了Amazon Simple Storage Service(S3)和Amazon Elastic File System(EFS)。云原生的方式极大地降低了维护成本。

  • 部署方式

Bingo平台的部署可以考虑Amazon Elastic Kubernetes Service(EKS)、Amazon Elastic Compute Cloud(EC2)或者Amazon Lambda。鉴于平台可以独立于微服务集群存在,没必要部署到EKS当中。

另外,和EC2相比,Lambda更加灵活,部署更为简单,成本也更为低廉。

因此,平台选择了无服务器架构。同时,平台的CI/CD是自服务的,即Bingo的上线发布采用了Bingo平台提供的CI/CD的功能。这使得平台上线任何新功能都可以通过平台做一手的验证,然后再交付给用户。

▊ 平台架构

根据以上对比,我们选择了一套云原生 定制化组件的架构,如下图所示。

与业内流行的低代码开发平台类似,Bingo平台有一套可视化UI,即Bingo Web UI。

平台的后端(Bingo Lambda)包含模板管理、服务管理、服务创建、服务部署等功能,每个功能是一个单独的Lambda。

平台的存储层(Storage)包含数据库存储RDS与用于存储模板代码、服务代码的代码存储GitHub。

图中左边描述了日志的收集,我们通过Amazon CloudWatch收集Lambda的日志,并经由Kafka将日志通过ElasticSearch Logstash Kiabana呈现给用户。

图中右边是CI/CD部分。CI流水线会在每次服务代码改动后将服务打包并上传到远端仓库。CD流水线会从仓库中获取Lambda zip包,然后上传到S3,再完成部署。部署使用了运维团队提供的基础设施即代码(Infrastructure as Code, IaC),这就使得我们很轻松地做到了不同环境部署的自动化。

▊ 平台的落地实现

Bingo平台提供了一套可视化界面,支持服务模板的管理和服务全生命周期的管理。

服务模板的管理

我们提供了服务模板的管理功能来做到规模化的生产。针对每一种类型的服务提供一种模板,每个模板定义了一个业务场景的最佳实践。使用Bingo创建新服务时,根据业务场景选择合适的模板即可。这里举几个例子。

  • Amazon Gateway Lambda模板提供外部API
  • Amazon ALB Lambda模板提供内部API
  • Amazon EventBridge Lambda模板处理异步任务、定时任务
  • Amazon Kinesis Lambda模板处理数据流
  • gRPC 微服务模板搭建基于gRPC的微服务

模板管理功能提供了模板列表页面和模板详细信息页面。模板列表页支持模板的分页和搜索的功能。可以点击特定的模板进入详细信息页面。每个模板都有一个对应的详细信息页面。页面包含贡献者、模板名称、模板代码的Git仓库、使用场景介绍、关键字标签等。其中关键字标签支持编辑功能,可以选择添加已有标签或者创建新的标签,也可以按需删除标签。此外,还可以通过点击创建模板问题按钮,对模板提出反馈。下图是一个使用 ALB Lambda构建API的模板。

模板管理还提供了新增模板的功能。新增模板时需要填写贡献者、名称、模板代码的Git仓库、使用场景介绍、关键字标签等。其中模板代码的Git仓库需要预先准备好,并包含对应的模板代码文件。

模板代码是模板的核心内容。模板代码由说明书、Makefile、配置文件、部署描述文件、流水线文件等组成,并包含一个可执行的hello world程序。下图是ALB Lambda模板代码的目录结构,具体说明如下。

(1)说明书即README.md,内容包含模板的名称和使用说明。

(2)functions目录包含了一段可执行的代码,例如一个返回hello world的API,开发人员可以基于此进行二次开发,实现自己的业务逻辑代码,示例如下。

代码语言:javascript复制
 func GetHelloWorld(ctx context.Context, req *model.Request) (interface{}, error) {
      InitConfig()


      log.Println("Hello World!")
      log.Printf("Request path: %v", req.Path)


      //get parameter value from Lambda env
      DomainServiceUrl := os.Getenv("Bingo Service")


      message := fmt.Sprintf("Message: Hello, Bingo! Bingo service url is [%s] (Time: %s) ",
          DomainServiceUrl, time.Now().UTC().String())
      return message, nil
  }


  // Init Viper Config from Environment Variables.
  func InitConfig() {
      v := viper.New()
      v.AutomaticEnv()
      config.Set(&config.Config{Viper: *v})
  }

(3)Makefile文件定义了单元测试、测试覆盖率、打包等命令。这些命令是约定俗成的,会整合到持续集成的流水线中。

(4)配置文件是给开发环境、预发布环境、生产环境等环境使用的配置变量。

(5)部署描述文件是基于yaml的DSL,用来描述AWS云原生的部署内容。在服务实际部署时,DSL文件会被转成基础设施编排工具Terraform可以识别的tf文件。部署描述文件的代码如下。

代码语言:javascript复制
 # bingo-alb-template bingo config
  application: bingo-alb-template


  common:
    tags:
      Project: Bingo
      App: ALBTemplate


  # lambda functions
  functions:
    - name: HelloWorld  # required!
      handler: hello # required! binary name
      runtime: go1.x # optional, default go1.x
      description: API of bingo hello # optional
      timeout: 10 #optional, default 6s
      environment:
        - hello_env


  events:
    alb:
      - priority: 1
        conditions:
          path: # path is array to support multiple path
            - /bingo/hello
          method: # method is array to support multiple http METHOD
            - GET
        functions:
          - name: HelloWorld
            weight: 100

在上述示例中,模板名称为bingo-alb-template,AWS Tag是Project: Bingo和App: ALBTemplate。模板还包含一个名为HelloWorld的Lambda、一个环境变量hello_env,以及一个指向此Lambda的的ALB。ALB对外暴露了一个路径为/bingo/hello的HTTP接口。而hello_env则定义在配置文件当中。

(6)流水线文件是用来触发持续集成流水线的模板文件。

全生命周期的管理

Bingo平台支持服务的全生命周期的管理,,如下图所示,提供了从设计到开发,从集成到部署再到运维的支持。

1.设计阶段

在设计阶段,平台通过服务模板提供服务设计的最佳实践。可以参考最佳实践来进行需求的调研和AWS云原生的调研,从而避免从零开始设计新服务。也可以提炼总结新的最佳实践,并以模板代码的形式贡献到Bingo平台上,供未来使用。

2. 开发阶段

在开发阶段,平台支持快速的搭建新的服务。创建新服务需要选择模板,并填写服务的名称、描述、Git仓库的名称、Git组织的名称、持续集成流水线及服务的标签,如下图所示。

其中,Git仓库、Git组织用来唯一指定服务代码的位置。持续集成流水线会有一个默认值,指向一个预先创建好的公共的流水线。可以选择使用默认流水线,也可以填写单独搭建的流水线的地址。服务的标签特指AWS Tag。我们使用Project App Service三级Tag来区分不同的服务。AWS Tag非常重要,首先,可以用Tag定义服务的唯一标识,便于资源的管理;其次,可以基于Tag于AWS开销进行统计,并定期清理没有Tag的资源;最后,可以基于Tag做好资源权限的隔离。

在Bingo UI上填好服务信息后,点击“CREATE”按钮即可自动创建服务模板,流程如下图所示。

具体来说,自动搭建的功能包含下述步骤。

(1)验证服务的Git组织是否存在,如果不存在则退出。

(2)验证服务的Git仓库是否存在,如果存在则退出,否则创建服务的Git仓库。

(3)赋予当前用户Git仓库的开发权限。

(4)根据服务模板的名称找到对应的模板的Git仓库,然后克隆到平台服务器端。

(5)根据用户需求对模板代码进行编辑。如将模板名称替换成服务名称、按需增加或者减少公共组件库等。

(6)将代码的Git远端从模板的Git仓库修改成服务的Git仓库。

(7)使用Git命令提交代码并push到远端,从而完成框架代码的生成。

(8)平台服务器端清理临时文件,并将结果写入平台的数据库。

(9)开发人员基于Git仓库中的框架代码进行后续的业务开发。

新建的服务是一个可执行的hello world代码框架,同时会自动对接好持续集成持续部署流水线。团队成员可以直接打包和并在开发环境进行部署,也可以通过ELK查看服务日志。

3. 集成阶段

持续集成的流水线由开发人员创建新服务时候指定。我们推荐使用默认的公用流水线,从而减少维护成本。以触发公共集成流水线为例,部分代码如下。

代码语言:javascript复制
 stage("Bingo_CI_Trigger") {
   steps{
     script {
   if ("${BRANCH_NAME}" =~ "^PR-") {
     // subci trigger
     Triggered_Build = build(job: "UI/UI_CI/bingo/Bingo_SubCI_Pipeline", propagate: false, parameters: [
     string(name: "GITHUB_TRIGGER", value: "true"), string(name: "GITHUB_PR_COMMIT", value: "${GIT_COMMIT}"), string(name: "service_name", value: "${service_name}"), string(name: "repo_git_url", value: "${SSHGitUrl}")
     ])
   } else {
     // fullci trigger
     Triggered_Build = build(job: "UI/UI_CI/bingo/Bingo_FullCI_Pipeline", propagate: false, parameters: [
     string(name: "GITHUB_TRIGGER", value: "true"), string(name: "GITHUB_BRANCH", value: "${BRANCH_NAME}"), string(name: "service_name", value: "${service_name}"), string(name: "repo_git_url", value: "${SSHGitUrl}")
     ])
   }
   if (Triggered_Build) {
     Downstream_Url = Triggered_Build.absoluteUrl
     Downstream_Res = Triggered_Build.result
     echo "DownStream Build Result: ${Downstream_Res} n${Downstream_Url}"
     if (Downstream_Res != "SUCCESS") {
       err_message = "Build Result: ${Downstream_Res}"
       ep_tools.highlight_info("error", err_message)
     }
   }
 }
   }
 }

每个Pull Request会触发subci,验证代码是否可以编译,单元测试是否可以通过。只有subci成功后代码才可以被合并。代码合并会触发fullci,触发单元测试、回归测试,并生成测试覆盖率报告。fullci会调用平台提供的bingo命令行工具对部署描述文件做格式校验,并对部署描述文件和服务代码分别打包,再上传到远端的Artifactory服务器,供部署使用。团队成员可以使用命令行工具在本地环境验证部署描述文件的正确性。

4. 部署阶段

开发人员完成开发后,可以在平台上完成一键部署。

以Serverless服务的部署为例,开发人员选择AWS账号、AWS region、部署的环境,并填写服务的版本号,然后点击部署即可。

部署流水线会从Artifactory服务器下载服务的tar包,解压后将Lambda的二进制文件以zip的形式上传到S3上,然后从Artifactory服务器下载部署描述文件包,并将其转成Terraform可以识别的tf文件,最后使用Terraform完成服务的部署,同时将配置文件以环境变量的形式应用到Lambda上。

生成的tf文件会包含AWS标签、Lambda对应S3的地址以及其他AWS配置参数。tf文件会上传到GitHub代码库中。这样一来,只需要简单地修改参数就能完成不同环境的部署,或者对Lambda的zip包进行替换。代码如下。

代码语言:javascript复制
module "Alblambda-Bingo-BingoAPI-" {


  source = "../module/bingo/alblambda/v1_0_0"


  tags = {
    ENV     = "${var.ENV}"
    Project = "Bingo"
    App     = "BingoAPI"
    Service = ""
    Owner = ""
  }


  ticket = "ODP-1000"
  enable_alb = true
  enable_log2elk = true
  lambda_pkg_s3 = "${var.lambda_pkg_s3}"
  lambda_pkg_version = "v39-rev20210816-123456-aws"
  alb_dnsname = "bingo_demo.${var.domain_name}"


  lambda_functions = [
      ... ...
  ]


  alb_rules = [
    {
      priority = 1


      conditions = {
        http_request_method = ["GET"]
        path_pattern = ["/bingo/hello"]
      }


      functions = [
      ]
      target_groups = [
        {
          arn    = "......"
          weight = 100
        }
      ]
    }
  ]


}

5. 运维阶段

Bingo平台还对服务的运维进行了支持。Bingo平台和基于ELK的日志解决方案进行了自动对接,屏蔽了烦琐的配置细节。仍旧以Serverless服务为例,日志收集流程如下图所示。

服务产生的原始日志会被CloudWatch收集。而Log Lambda会将Cloudwatch中的日志写入到Kafka,再由ELK消费日志。从Cloudwatch到ELK的过程对团队人员透明,服务部署成功后开发人员即可在ELK中查看服务的日志。

此外,Bingo平台也我们团队使用和基于Jaeger的分布式追踪系统进行了自动对接,从而对服务的上下游进行追踪。

4展望未来

Bingo平台作为云原生的低代码开发平台,短短数个月的就取得了巨大的成功。平台极大地缩短了团队搭建新服务的时间,减少了开发和维护的成本,加强了跨职能团队的协作。

平台在未来会持续的提供不同的服务模板,沉淀云原生的最佳实践,进一步增强平台扩展的能力。越来越多的服务登陆Bingo平台又将促进应用黄页的诞生。我们期待着打造一体化的可复用平台,构建Bingo的生态体系。

0 人点赞