利用无服务器和Kubernetes原生Java减少二氧化碳排放
主要结论
- 越来越多的企业正在将应用工作负载转移到多云和混合云平台上,这些平台具有更好的可扩展性和性能,但二氧化碳排放量也更大。
- Java并不是优化Kubernetes云性能的第一选择,尽管根据TIOBE指数,Java仍然是第三大热门编程语言。
- 无服务器架构的目的是减少按需计算的消耗时间。
- 原生Java编译旨在解决性能问题,同时也减少Kubernetes和无服务器平台的计算资源利用率。
- Kubernetes原生Java框架Quarkus提供了容器优先和原生功能,其首次启动时间为几毫秒,常驻集大小(RSS)内存极小,在速度和规模上最大限度地减少了云服务的资源消耗。
随着采用云部署的兴起,IT部门消耗更少的物理服务器,更少的电力,并最终通过减少碳排放帮助缓解气候变化。云架构对此有帮助,因为他们不需要维护孤立的计算资源,而是能够在云上有效地共享可用资源,在他们所处的位置和需要的时候,保持他们的业务服务运行。
然而,云计算运动的这些好处并没有在短时间内对二氧化碳排放产生明显影响。这是由于云计算的采用速度比转移到无碳基础设施要快得多。例如,谷歌云今天是碳中和的,但他们正在努力投入更多的努力,成为无碳和可持续的云计算系统。
同时,开发人员和架构师继续把最大的努力放在优化应用程序的性能上,如更小的容器图像,更快的启动和响应时间,以及更少的内存足迹。他们相信,这最终能够减少应用层的计算消耗。
为不同时代设计的Java
Java诞生于27年前,用于运行具有各种优势的商业服务,如高网络吞吐量、长运行进程和可变系统的动态行为。几十年前,这些确实是很好的功能,开发者可以通过互联网编写灵活而丰富的应用程序,然后在从物理服务器到虚拟机的基础设施上的几个应用服务器上运行。
然而,自从Kubernetes和Linux容器被释放到世界上之后,情况发生了变化。它给我们带来了一个新的范式,重新架构现有的应用程序,应该在云上处理牛过猫。新的应用程序的主要特点是可移植性、不可更改性和快速的可扩展性。
不幸的是,在这个新时代,Java的动态特性并不是一个很大的好处。尽管如此,企业仍然保持着大量建立在Java堆栈上的关键业务应用,这可能是提升和转移工作负载到云平台上的一个障碍。这也使企业失去了减少二氧化碳排放的机会,因为他们需要在传统的基础设施上花更多的钱来保留单片机应用。
具有讽刺意味的是,根据TIOBE指数,Java仍然是第三大最受欢迎的编程语言。关于这一趋势,许多开源项目和工具(如Shenandoah GC)出来了,通过扩展、短暂状态和在不可变系统上更小的内存足迹,在吞吐量管理方面优化Java性能。遗憾的是,这些努力还不足以让开发者把Java应用留在Kubernetes集群上,而不是采用Javascript和Python等替代方案。
无服务器的Java
作为减少云计算计算资源的无尽努力的一部分,许多企业已经意识到,通过定期监测应用程序的工作负载和资源使用情况,所有的业务服务并不需要一直运行(例如,24 x 7 x 365)。
例如,某些服务(如订单服务)只被终端用户和第三方访问不到整个服务的10%。在这种情况下,无服务器架构可以让你在某段时间内(如5分钟或30秒)没有网络流量进入应用时,自动将应用缩减为零。
事实上,无服务器行为不仅可以应用于基于HTTP的微服务,还可以应用于来自物联网(IoT)边缘设备和Kafka消息服务器的分布式流服务。
这里是你作为一个Java开发者的问题,"Java如何处理无服务器架构?"。更大的问题是 "Java到底能不能适应开发无服务器应用?" 根据NewRelic的调查,传统上,开发人员倾向于不在AWS Lambda上运行Java应用,原因是重量级包和动态行为,如图1所示。
/filters:no_upscale()/articles/reduce-CO2-with-serveless/en/resources/2pasted%20image%200-5-1655123496222.jpeg)
图1.出于对无服务器的热爱
这就是为什么更多的开发者希望将Node.Js和Python应用引入无服务器平台和功能即服务(FaaS),而不是进化现有的Java应用。不要放弃你的Java技能组合!你会在下一节中弄清楚如何让你的Java应用更适合无服务器架构。
原生的本地Java
构建原生可执行的Java应用不仅带来了巨大的好处,如更快的启动和响应时间,更小的内存占用,而且还解决了传统Java栈的上述挑战。让我们深入了解一下本地可执行文件的工作原理吧!本机可执行文件是使用超前(AOT)编译器构建的,该编译器会制作一个独立的本机镜像,其中包含应用类、依赖库和运行时。你可以理解为它类似于Linux容器镜像,包含了在任何容器运行时和Kubernetes上运行应用程序所需的所有东西。
有了本地可执行文件,你就不再需要Java虚拟机(JVM)来运行你的Java应用程序了。相反,原生镜像可以在底层虚拟机上运行,底层虚拟机是GraalVM中的运行时组件(如垃圾收集器、线程调度)。
Java的原生编译也让开发者在无服务器工作负载中坚持使用Java应用,因为原生可执行文件可以减少冷启动的启动时间,这也是许多企业想要采用无服务器架构时的最大挑战之一。
下面是一个快速入门,介绍如何安装必要的C库和依赖项,以便在操作系统上为你的Java应用编译一个本地可执行文件镜像。
安装C语言库
为了获得对C语言原生编译的支持,你还需要使用以下命令安装GCC和相关库。
- Fedora发行版:
$ sudo dnf install gcc glibc-devel zlib-devel libstdc++-static
- 蝶变发行版:
$ sudo apt-get install build-essential libz-dev zlib1g-dev
- macOS
$ xcode-select --install
配置GraalVM
根据你的操作系统设置GRAALVM_HOME环境变量。
- Linux
$ export GRAALVM_HOME=$HOME/Development/graalvm/
- macOS
$ export GRAALVM_HOME=$HOME/Development/graalvm/Contents/Home/
安装本地镜像工具:
${GRAALVM_HOME}/bin/gu install native-image
如果你还没有这样做,请使用以下命令设置 JAVA_HOME 环境变量。
$ export JAVA_HOME=${GRAALVM_HOME}
然而,制作本地映像需要提前提供很多关于你的应用程序的信息。只有当一个类或方法被明确注册后,反射才会起作用。这将要求Java开发者在构建本地可执行镜像之前,转换所有现有的应用程序以注册反射。
开始使用Kubernetes原生Java Quarkus
如果你可以继续开发你的云原生微服务,而不用花太多时间处理反射,那么你是否只需要在部署到Kubernetes集群之前构建一个原生可执行镜像?我很确定这对Java开发者来说是很好的。
Quarkus是一个开源项目,旨在提供一个标准的Java堆栈,使Java开发者不仅可以在OpenJDK上构建一个容器优先的应用程序,还可以编译一个本地可执行文件在Kubernetes集群上运行,有以下好处:
Quarkus还提供了一个扩展,即Funqy,旨在跨无服务器平台编写可移植的无服务器函数,如OpenShift无服务器、Knative、AWS Lambda、Azure Functions和谷歌云平台。
下面是一个快速指南,介绍如何使用Quarkus创建一个新的无服务器函数并进行本地可执行的编译。
创建一个新的无服务器Java项目
让我们用Quarkus命令行工具搭建一个新的Quarkus项目来创建一个函数。
$ quarkus create quarkus-serverless-example -x funqy-http
这个命令允许你下载Funqy扩展,以启用Quarkus Funqy功能,输出应该是这样的:
Creating an app (default project type, see --help).
-----------
selected extensions:
- io.quarkus:quarkus-funqy-http
applying codestarts...
java
maven
quarkus
config-properties
dockerfiles
maven-wrapper
funqy-http-codestarts
-----------
[SUCCESS] ✅ Quarkus项目已经成功生成在:
--> /Users/USERNAME/quarkus-serverless-example
-----------
探索功能
进入你创建项目的根目录,然后打开src/main/java/org/acme 目录中的MyFunctions.java文件。默认生成一个简单的函数方法(fun) ,以返回一个问候信息。@Funq 注解使一般的方法成为一个函数,你可以通过RESTful API访问。
@Funq
public String fun(FunInput input) {
return String.format("Hello %s!", input != null ? input.name : "Funqy");
}
你可以用一个新的函数或现有的函数添加你的业务逻辑。现在让我们暂时保留默认的代码。
构建并部署一个本地可执行文件到Kubernetes上
Quarkus提供了一个OpenShift扩展来构建应用程序,然后将其部署到Kubernetes集群上。执行以下Quarkus命令行来添加扩展:
$ cd quarkus-serverless-example
$ quarkus ext add openshift
输出应该是这样的:
Looking for the newly published extensions in registry.quarkus.io
[SUCCESS] ✅ Extension io.quarkus:quarkus-openshift已被安装
在src/main/resources 目录中的application.properties 文件中添加以下用于 Kubernetes 部署的配置。你需要将YOUR_NAMESPACE 替换为你部署功能的命名空间(例如doh-dev ):
quarkus.container-image.group=YOUR_NAMESPACE
quarkus.container-image.registry=image-registry.openshift-image-registry.svc:5000
quarkus.kubernetes-client.trust-certs=true
quarkus.kubernetes.deployment-target=knative
quarkus.kubernetes.deploy=true
quarkus.openshift.build-strategy=docker
quarkus.openshift.expose=true
当你设置以下配置时,你也可以使用容器运行时间(如Docker或Podman)构建一个本地可执行镜像。
quarkus.native.container-build=true
注意,你可以在这里找到解决方案库。
为了部署该功能,你可以使用你自己的Kubernetes集群(例如minikube),但我会鼓励你使用红帽OpenShift的开发者沙盒,当你注册一个免费账户时,它提供了一个共享的Kubernetes集群。该沙盒使你能够在10分钟内启动一个新的Kubernetes集群,而无需在本地文件系统上进行任何安装或配置。
执行以下Quarkus命令行,构建并部署功能到Kubernetes集群。
$ quarkus build --native --no-tests
输出应该以BUILD SUCCESS 消息结束。
当你进入OpenShift Dev Console中的Topology 视图时,你的Java函数(例如:quarkus-serverless-example-00001 )已经被部署。由于Knative服务默认设置为30秒,如果没有网络流量到函数pod,函数可能会被缩减为零,如图2所示:
/filters:no_upscale()/articles/reduce-CO2-with-serveless/en/resources/1pasted%20image%200-4-1655123496222.jpeg)
图2.拓扑结构视图中的功能
注意,你可以给REV和KSVC添加一个新的标签,将pod显示为Quarkus函数,这可以让你在Topology视图中轻松区分其他pod。使用下面的oc命令行。
- 为REV添加一个Quarkus标签
oc label rev/quarkus-serverless-example-00001 app.openshift.io/runtime=quarkus --overwrite
- 在KSVC上添加一个功能标签
oc label ksvc/quarkus-serverless-example boson.dev/function=true --overwrite
复制Route URL,然后粘贴以下CURL命令行来访问该功能。例如,该路线看起来像quarkus-serverless-example-doh-dev.apps.sandbox.x8i5.p1.openshiftapps.com。
$ curl --header "Content-Type: application/json" \
--request POST \
--data '{"name":"Daniel"}' \
YOUR_ROUTE_URL/fun
输出结果应该是这样的:
Hello Daniel!
当你回到拓扑视图时,你会看到函数舱在一秒钟内自动上升,如图3所示:
/filters:no_upscale()/articles/reduce-CO2-with-serveless/en/resources/2pasted%20image%200-3-1655123496222.jpeg)
图3.扩容功能
当你查看pod的日志时,你会看到Java无服务器功能正以native 的形象运行。它也花了17毫秒的时间来启动,如图4所示。
/filters:no_upscale()/articles/reduce-CO2-with-serveless/en/resources/2pasted%20image%200-2-1655123496222.jpeg)
图4.一个本地可执行文件的启动时间
多么超音速和亚原子的应用啊!从现在开始,这些新的Java无服务器函数可以让你在Kubernetes上优化资源性能,减少二氧化碳排放。
总结
本文指导您了解Java无服务器应用程序如何在容器平台(如Kubernetes)上通过比其他任何编程语言更高的资源密度来帮助您的组织减少二氧化碳排放,如图5所示。
/filters:no_upscale()/articles/reduce-CO2-with-serveless/en/resources/2Untitled-6-1655123496222.jpeg)
图5.容器平台上多个应用程序的资源密度
开发人员还可以通过选择三个GraalVM发行版之一,即Oracle GraalVM社区版(CE)、Oracle GraalVM企业版(EE)和Mandrel,为Java应用程序构建一个本地镜像。在这里可以找到更多关于GraalVM和Mandel之间的区别。要继续你的Kubernative原生Java之旅,请访问这个网站。