亚马逊AWS官方博客
在Amazon Graviton2机型上使用自定义Runtime开发无服务JAVA应用
介绍
Amazon Graviton处理器是亚马逊云科技针对运行在Amazon Elastic Compute Cloud(Amazon EC2)实例的云上工作负载,而专门开发的具备高性价比的ARM架构CPU芯片。自从2019年问世以来,Amazon Graviton芯片极大地提高了云用户的投资回报率(ROI)和云上资源的拥有成本(TCO)。作为目前Amazon Graviton芯片家族的主力,相比同配置的X86芯片,Amazon Graviton2能够带来最多40%的性能提升。Amazon Serverless Application Model(简称Amazon SAM),是一款由亚马逊云科技研发的,帮助开发者更快速高效地开发云上无服务器应用的利器(可以点击链接查看相关的workshop,以便快速理解Amazon SAM)。
Amazon SAM在X86平台上为广大无服务器应用开发者提供了便利。那么,如何将Amazon SAM搬到Amazon Graviton2上,使得开发者既能享受Amazon Graviton2带来的性价比,也能充分利用Amazon SAM强大的功能,以加速应用开发?同时,由于开发者各自的技术栈千差万别,而亚马逊云科技目前支持的编程语言种类和运行时环境版本均有限,对于特定的、不默认支持的运行时环境,Amazon SAM初学者可能会感到无从下手。
本篇就以JAVA 18(亚马逊云科技默认支持JAVA8及JAVA11)运行时为例,详细演示如何在Amazon Graviton2上,利用Amazon SAM来开发一个入门的JAVA无服务器应用。
整体架构
下图展示了本文所示例子的整体架构。首先,我们在Amazon Graviton2芯片的EC2实例上创建一个开发环境并安装Amazon SAM CLI。然后,我们在实例上编写应用的代码。开发完成后,我们使用Amazon SAM CLI的相关命令构建,测试以及部署我们的应用。最后,我们使用Amazon CloudFormation将我们的应用部署在Amazon API Gateway和Amazon Lambda上。
图一 示例应用架构
搭建开发环境
在X86平台的很多Amazon SAM开发教程中,我们会用到Amazon Cloud9这个服务。Amazon Cloud9可以使我们方便快捷的搭建起一个运行在云端的IDE。很遗憾,目前用户还不能创建基于Amazon Graviton2实例的Amazon Cloud9环境。因此,在本文示例中,我们将在一个Amazon Gravation2实例上从头搭建一个Amazon SAM开发环境。具体步骤如下:
- 在亚马逊云科技控制台启动一个Amazon Graviton2实例,例如medium。这个演示中我们使用Amazon Linux 2操作系统,可以点击链接查看如何启动一个Amazon EC2实例的详细步骤。
- 在Amazon EC2实例上安装Amazon SAM CLI。可以点击链接查看安装的详细步骤。请注意安装详细步骤中的第三步“Install Docker”,虽然文档中标注为Optional,但是本例中我们需要安装,不要跳过,因为我们会使用Docker来构建我们的自定义JAVA运行时。在写这篇文章的时候,安装详细步骤里面的第四步“Install the AWS SAM CLI”,需要针对Amazon Graviton2实例做出一些调整才可以顺利进行。文档中在ARM平台上的安装命令是“pip install aws-sam-cli”,需要被替换如下:
- sudo yum –y update
- sudo yum -y install gcc
- sudo yum install python3-devel
- pip3 install aws-sam-cli
开发代码
执行完上述步骤,我们就搭建了开发本文例子的开发环境,由于我们会开发无服务器应用,后续的代码逻辑会放置在Amazon Lambda Function中运行,因此开发环境中无需安装JDK。我们会使用Amazon SAM CLI来协助我们编写代码,由于Amazon SAM提供了很多项目模板,因此我们不必从头开始编写我们的代码。
初始化项目
点击链接查看使用Amazon SAM CLI初始化一个hello-world应用的详细步骤。由于我们还要基于自动生成的代码模板做进一步修改,因此详细步骤中的内容,我们仅需要执行第二步“Build your application”之前的内容(不包括第二步)。稍后,完成代码编写后,我们会构建和部署我们的应用。
在执行“sam init”命令之后,SAM CLI会收集一系列问题的答案,以便创建符合我们要求的项目代码模板。以下是我们针对本文示例的问题回答:
- Which template source would you like to use? 1 – AWS Quick Start Templates
- Choose an AWS Quick Start application template? 1 – Hello World Example
- Use the most popular runtime and package type? (Python and zip) [y/N]: N
- Which runtime would you like to use? 5 – java11
- What package type would you like to use? 1 – Zip
- Which dependency manager would you like to use? 2 – maven
- Would you like to enable X-Ray tracing on the function(s) in your application? N
对于问题4,由于Amazon SAM当前默认只支持JAVA8和11(这也是本文写作的目的之一——使用任意JAVA版本运行时),我们选择JAVA11版本。我们当前只是想要利用Amazon SAM CLI自动生成的代码框架,因此JAVA版本的选择在目前无关紧要。
从Amazon SAM CLI自动生成的JAVA代码中可以看到,代码所完成的逻辑非常简单,在生成的Lambda函数的实现中,我们只是以JSON格式返回一个“hello world”字符串以及调用者的IP地址。
构建自定义运行时
由于JAVA 18不是一个Amazon SAM默认支持的JAVA运行时版本,所以我们必须自己构建运行时。这篇博客文章详细讲述了如何为Lambda函数构建一个任意版本的JAVA运行时。其中,modular runtime image,class data sharing以及tiered compilation等技术被用来缩小运行时体积以及提升Lambda函数的性能。在我们的文章中,我们也会借鉴这些技术来优化Lambda函数的冷启动问题。在本文的例子中,我们使用Docker container来构建我们的运行时。由于我们的代码最终会运行在ARM平台上,所以镜像需要在arm64版本的Linux镜像上来构建。这也是下面Dockerfile截图中使用“–platform=linux/arm64/v8”参数的原因。
Dockerfile的其他部分与x86平台上的内容一致。Dockerfile中的内容被分成了几个部分,通过相关的注释不难理解各部分的作用。完整的文件内容如下:
需要注意的是Dockerfile的最后一段,runtime.tar的内部结构是固定的,这是Lambda函数自定义运行时所要求的。如果读者对于相关打包要求不熟悉,可参考此文档。
在Amazon SAM Template中指定自定义运行时
在Amazon SAM应用开发中,一个很关键的部分就是template.yaml文件的编写。自定义运行时的指定也需要在这个文件中进行。以下是本例中template.yaml文件需要修改的关键部分:
可以参考此文档查看在Amazon SAM中构建自定义运行时的详细步骤。注意这里所指的“构建运行时”与文章前面“构建自定义运行时”部分的“运行时”是同一个,我们前面编写的Dockerfile需要在此处被调用。template.yaml文件针对于本文示例的修改涉及三处(上图红框部分),分别解释如下:
- HelloWorldFunction.Properties.Runtime需要设置为” provided.al2”,而不是通常的”provided”(x86架构)。该值表明Lambda函数会运行于自定义运行时,“provided.al2”表示未来的运行时是arm架构的。
- HelloWorldFunction.Properties.Architectures需要设置为“arm64”。
- HelloWorldFunction.Metadata.BuildMethod需要设置为“makefile”。
依照Amazon SAM构建自定义运行时的要求,在Resources.HelloWorldFunction.Metadata.BuildMethod设置为“makefile“以后,我们需要在一个名为”Makefile“的文件中编写真正的构建命令脚本,并且Makefile应该位于Resources.HelloWorldFunction.Properties.CodeUri所指定的目录之下。本例中的Makefile的完整内容如下:
首先,我们使用docker命令来构建JAVA 18的运行时。然后,我们使用生成的镜像来启动一个容器,并将镜像内的运行时文件包抽取出来。最后一步稍显复杂,其目的如下:SAM CLI进行构建时,会将所生成的中间artifacts存放于特定隐藏目录,然后将该目录内文件统一打包。此处如果我们不对runtime.tar进行解包,就会造成Amazon SAM CLI打包生成的文件包内多一层目录,后续Lambda函数调用时,会遇到无法找到bootstrap文件的错误。
使用Amazon SAM构建和部署应用
做完上述准备后,我们就可以使用Amazon SAM CLI来构建和部署我们的应用了。进入应用的根目录,运行“sam build“命令,就会启动构建过程。我们的应用中创建了一个Lambda函数和一个API Gateway。如果需要在本机进行测试,我们可以在构建完成后,在命令行运行”sam local start-api“来启动一个本地的API Gateway环境来进行测试。以下是一个示例:
如上图所示,运行完后,会生成一个本地API Gateway的URL。由于我们在应用的template.yaml文件中定义的API Gateway方法的Path为“/hello“,所以要测试本例中的方法,需要使用如下地址测试:http://127.0.0.1:3000/hello。在命令行中输入”curl http://127.0.0.1:3000/hello“之后,会得到如下结果:
{ "message": "hello world", "location": "44.192.90.179" }
确认我们的代码功能正常之后,我们可以继续将我们的应用部署到云端。执行如下命令:
sam deploy –guided
像之前创建Amazon SAM应用一样,我们也需要回答一系列问题来完成交互式部署,对于本文中的例子,所有问题均可直接回车,选择默认值,除了以下这个问题:
HelloWorldFunction may not have authorization defined, Is this okay? [y/N].
此处选择‘y’。此问题是提示我们,应用中定义的Lambda函数配置了API Gateway,但是并没有为其关联Authorization机制,会导致部署之后生成的URL对整个互联网开放,具有安全隐患。为了保持简洁,我们示例中选择忽略这个提醒,但是在生产环境中,不建议如此设置。命令执行完成后,会输出如下信息:
基于我们应用template.yaml中的定义,命令行中最后会输出可以调用的API Gateway方法的URL(上图红框所示)。我们可以在浏览器或命令行中访问这个URL,看到和本地测试时类似的结果。至此,我们完整的演示了整个应用的开发流程,查看链接可以获得完整的示例代码。
结论
Amazon Graviton2给了亚马逊云科技用户一个非常具有竞争力,高性价比的计算平台新选择。因此,开发者们会希望尝试将他们运行在传统x86架构上的工作和负载迁移到Amazon Graviton2架构上。对于云上无服务器应用的开发,SAM是一个强有力的工具,其提供了单部署配置,本地调试测试功能,内置了相关的最佳实践,可以加速开发工作,并且其基于Amazon CloudFormation,可以使用CDK进行部署,平滑了开发者的学习曲线。考虑到目前JAVA仍是最流行的开发语言之一,我们撰写了这篇文章,以期对想要在Amazon Graviton2上进行无服务器应用开发的开发者们,尤其是无服务器应用的初学者们,提供相关的帮助。相信读者使用了上述服务后,可以感受到相关服务为开发工作带来的好处,以上服务仍在不断演进中,感兴趣的读者可以保持关注。