将通过 Jib 容器化的 Micronaut 应用部署到 Google Kubernetes Engine

1. 概览

Micronaut 简介

Micronaut 是一种基于 JVM 的现代全栈框架,用于构建易于测试的模块化微服务和无服务器应用。Micronaut 旨在以极低的内存占用量实现极佳的启动时间、快速的吞吐量。开发者可以使用 Micronaut 使用 Java、Groovy 或 Kotlin 进行开发。

Micronaut 可提供:

  • 启动时间短,内存消耗低 - 基于反射的 IoC 框架会针对代码中的每个字段、方法和构造函数加载和缓存反射数据,而使用 Micronaut 时,应用的启动时间和内存消耗不会受代码库大小的限制。
  • 声明式、响应式、编译时 HTTP 客户端 - 以声明方式构建响应式 HTTP 客户端,这些客户端在编译时实现,从而减少内存消耗。
  • 基于 Netty 构建的非阻塞 HTTP 服务器 - Micronaut 的 HTTP 服务器具有流畅的学习曲线,能够让您尽可能轻松地公开可供 HTTP 客户端使用的 API。
  • 快速轻松地测试 - 轻松启动单元测试中的服务器和客户端,并即时运行它们。
  • 高效的编译时依赖项注入和 AOP - Micronaut 提供了一个简单的编译时面向切面的编程 API,不使用反射。
  • 构建完全反应式且非阻塞的应用 - Micronaut 支持任何实现 Reactive Streams 的框架,包括 RxJava 和 Reactor。

如需了解详情,请访问 Micronaut 网站

Kubernetes 简介

Kubernetes 是一个开源项目,可在从笔记本电脑到高可用性多节点集群、从公有云到本地部署、从虚拟机到裸金属等众多不同环境中运行。

在本实验中,您需要将基于 Groovy 的简单 Micronaut 微服务部署到在 Kubernetes Engine 上运行的 Kubernetes

此 Codelab 的目标是将微服务作为在 Kubernetes 上运行的副本服务来运行。您可以将您在机器上开发的代码转换为 Docker 容器映像,然后在 Kubernetes Engine 上运行该映像。

下图展示了此 Codelab 中各个部分的作用,以帮助您理解这些部分是如何协同工作的。在您学习此 Codelab 的过程中,可以参考以下资源;看看结尾都应该没有问题(但您可以暂时先忽略这些)。

Kubernetes Codelab 示意图 1 (2).png

在此 Codelab 中,通过使用 Kubernetes Engine(在 Compute Engine 上运行的一种由 Google 托管的 Kubernetes 版本)等代管式环境,您可以更专注于体验 Kubernetes,而不是设置底层基础架构。

如果您有兴趣在本地机器(例如开发笔记本电脑)上运行 Kubernetes,则应考虑使用 Minikube。这提供了用于开发和测试目的的单节点 Kubernetes 集群的简单设置。如果您愿意,可以使用 Minikube 完成此 Codelab。

Jib 简介

Jib 是一种开源工具,可让您为 Java 应用构建 Docker 和 OCI 映像。它可以作为 Maven 和 Gradle 的插件以及 Java 库提供。

Jib 的目标是:

  • 快速 - 快速部署您的更改。Jib 将应用拆分为多个层,将依赖项与类分离。现在,您不必等待 Docker 重建整个 Java 应用,只需部署更改的层即可。
  • 可重现 - 具有相同内容重建容器映像始终会生成相同的映像。再也不会触发不必要的更新。
  • Daemonless - 减少 CLI 依赖项。在 Maven 或 Gradle 中构建 Docker 映像,并将其推送到您选择的任何注册表。无需再编写 Dockerfile 和调用 Docker build/push。

如需详细了解 Jib,请访问 GitHub 项目页面

关于本教程

本教程使用 Jib 工具中的示例代码为 Java 应用构建容器。

此示例是一个使用 Micronaut 框架和 Apache Groovy 编程语言的简单 hello world 服务。

学习内容

  • 如何使用 Jib 将简单的 Java 应用打包为 Docker 容器
  • 如何在 Kubernetes Engine 上创建 Kubernetes 集群。
  • 如何将 Micronaut 服务部署到 Kubernetes Engine 上的 Kubernetes 中
  • 如何对服务进行纵向扩容和发布升级。
  • 如何访问 Kubernetes 图形信息中心。

所需条件

  • 一个 Google Cloud Platform 项目
  • 一个浏览器,例如 ChromeFirefox
  • 熟悉标准的 Linux 文本编辑器,例如 Vim、EMACs 或 Nano

您打算如何使用本教程?

仅阅读教程内容 阅读并完成练习

您如何评价自己在构建 HTML/CSS Web 应用方面的经验水平?

新手水平 中等水平 熟练水平

您如何评价自己在使用 Google Cloud Platform 服务方面的经验水平?

<ph type="x-smartling-placeholder"></ph> 新手 中级 熟练

2. 设置和要求

自定进度的环境设置

  1. 登录 Cloud 控制台,然后创建一个新项目或重复使用现有项目。 (如果您还没有 Gmail 或 G Suite 账号,则必须创建一个。)

dMbN6g9RawQj_VXCSYpdYncY-DbaRzr2GbnwoV7jFf1u3avxJtmGPmKpMYgiaMH-qu80a_NJ9p2IIXFppYk8x3wyymZXavjglNLJJhuXieCem56H30hwXtd8PvXGpXJO9gEUDu3cZw

ci9Oe6PgnbNuSYlMyvbXF1JdQyiHoEgnhl4PlV_MFagm2ppzhueRkqX4eLjJllZco_2zCp0V0bpTupUSKji9KkQyWqj11pqit1K1faS1V6aFxLGQdkuzGp4rsQTan7F01iePL5DtqQ

8-tA_Lheyo8SscAVKrGii2coplQp2_D1Iosb2ViABY0UUO1A8cimXUu6Wf1R9zJIRExL5OB2j946aIiFtyKTzxDcNnuznmR45vZ2HMoK3o67jxuoUJCAnqvEX6NgPGFjCVNgASc-lg

请记住项目 ID,它在所有 Google Cloud 项目中都是唯一的名称(上述名称已被占用,您无法使用,抱歉!)。它稍后将在此 Codelab 中被称为 PROJECT_ID

  1. 接下来,您需要在 Cloud 控制台中启用结算功能,才能使用 Google Cloud 资源。

运行此 Codelab 应该不会产生太多的费用(如果有费用的话)。请务必按照“清理”部分部分,其中会指导您如何关停资源,以免产生超出本教程范围的结算费用。Google Cloud 的新用户符合参与 $300 USD 免费试用计划的条件。

3. 获取 Micronaut 示例源代码

在 Cloud Shell 启动后,您可以使用命令行克隆主目录中的示例源代码,并使用 cd 命令进入包含示例服务的目录:

$ git clone https://github.com/GoogleContainerTools/jib.git
$ cd jib/examples/micronaut/

4. 快速查看代码

我们的 Micronaut 简易服务由一个控制器构成,该控制器会输出令人厌烦的 Hello World 消息:

@Controller("/hello")
class HelloController {
    @Get("/")
    String index() {
        "Hello World"
    }
}

HelloController 控制器会响应 /hello 路径下的请求,而 index() 方法会接受 HTTP GET 请求。

还可以使用 Spock 测试类检查输出中是否提供了正确的消息。

class HelloControllerSpec extends Specification {
    @Shared
    @AutoCleanup
    EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer)

    @Shared
    @AutoCleanup
    RxHttpClient client = embeddedServer.applicationContext.createBean(RxHttpClient, embeddedServer.getURL()) 

    void "test hello world response"() {
        when:
        HttpRequest request = HttpRequest.GET('/hello')
        String rsp  = client.toBlocking().retrieve(request)

        then:
        rsp == "Hello World"
    }
}

此测试并不只是简单的单元测试,它实际运行的 Micronaut 服务器堆栈(基于 Netty 框架),与生产环境中运行的相同。因此,代码在产品中的行为将与测试中的行为相同。

如需运行测试,您可以运行以下命令,看看一切是否正常:

./gradlew test

5. 在本地运行应用

您可以使用以下 Gradle 命令正常启动 Micronaut 服务:

$ ./gradlew run

应用启动后,您可以通过小 + 图标打开额外的 Cloud Shell 实例,然后使用 curl 查看是否获得了预期输出:

$ curl localhost:8080/hello

您应该会看到一个简单的“Hello World”消息。

6. 使用 Jib 将应用打包为 Docker 容器

接下来,准备好您的应用以便在 Kubernetes 上运行。为此,我们将利用 Jib 为我们完成这些繁琐的工作,因为我们无需自己触摸 Dockerfile

让我们运行以下命令来构建容器:

$ ./gradlew jibDockerBuild

您应该会看到以下输出:

Tagging image with generated image reference micronaut-jib:0.1. If you'd like to specify a different tag, you can set the jib.to.image parameter in your build.gradle, or use the --im
age=<MY IMAGE> commandline flag.

Containerizing application to Docker daemon as micronaut-jib:0.1...
warning: Base image 'gcr.io/distroless/java' does not use a specific image digest - build may not be reproducible
Getting base image gcr.io/distroless/java...
Building dependencies layer...
Building resources layer...
Building classes layer...
Finalizing...

Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, example.micronaut.Application]
Loading to Docker daemon...

Built image to Docker daemon as micronaut-jib:0.1

现在映像已构建完毕,让我们在 Cloud Shell 的第一个标签页中运行 Docker 映像,看看能否看到友好的 Hello 消息:

$ docker run -it -p 8080:8080 micronaut-jib:0.1
16:57:20.255 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [cloud, gcp]
16:57:23.203 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 2926ms. Server Running: http://97b7d76ccf3f:8080

我们的服务正在运行,现在我们可以在第二个 Cloud Shell 标签页中启动 curl 命令,看看它是否正常运行:

$ curl localhost:8080/hello
Hello World

您可以在 Cloud Shell 中按 Ctrl+C 来停止容器。

7. 将容器化服务推送到注册表

现在映像按预期运行,接下来您可以将它推送到 Google Container Registry,这是一个私有代码库,用于存储 Docker 映像,该代码库可从每个 Google Cloud 项目访问(也可从 Google Cloud Platform 外部访问)。

在推送到注册表之前,请确保为项目启用了 Container Registry,方法是依次点击工具 >Container Registry如果未启用,您应该会看到以下对话框,请点击“启用 Container Registry API”将其启用:

ac812e6260ac7dfb.png

注册表准备就绪后,如需将映像推送到注册表,请启动以下命令:

$ gcloud auth configure-docker
$ docker tag micronaut-jib:0.1 \
         gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1
$ docker push gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1

借助以上命令,gcloud SDK 可以配置并授权 docker 将映像推送到您的 Container Registry 实例,将映像标记为指向其在注册表中的位置,然后将其推送到注册表。

如果一切进展顺利,一段时间后,您就应该能够看到控制台中列出的容器映像:工具 >Container Registry此时,您已经有一个项目范围的 Docker 映像可用,Kubernetes 可以访问和编排该映像,几分钟后您就会看到。

12224c4e42183b4e

8. 创建集群

现在,您可以创建 Kubernetes Engine 集群了,但在此之前,请前往 Web 控制台的 Google Kubernetes Engine 部分,等待系统初始化(应该只需要几秒钟)。

20c0587c0108b8ba

集群由 Google 管理的 Kubernetes 主 API 服务器和一组工作器节点组成。工作器节点是 Compute Engine 虚拟机。我们使用 CloudShell 会话中的 gcloud CLI 创建一个包含两个 n1-standard-1 节点的集群(此过程需要几分钟才能完成):

$ gcloud container clusters create hello-cluster \
  --num-nodes 2 \
  --machine-type n1-standard-1 \
  --zone us-central1-c

最后,您应该会看到创建的集群。

Creating cluster hello-cluster in us-central1-c...done.
Created [https://container.googleapis.com/v1/projects/mn-gke-test/zones/us-central1-c/clusters/hello-cluster].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-c/hello-cluster?project=mn-gke-test
kubeconfig entry generated for hello-cluster.
NAME           LOCATION       MASTER_VERSION  MASTER_IP       MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
hello-cluster  us-central1-c  1.9.7-gke.7     35.239.224.115  n1-standard-1  1.9.7-gke.7   2          RUNNING

现在,您应该拥有一个由 Google Kubernetes Engine 提供支持且功能齐全的 Kubernetes 集群:

d9e1e314769753e7.png

现在是时候将您自己的容器化应用部署到 Kubernetes 集群了!从现在开始,您将使用 kubectl 命令行(已在 Cloud Shell 环境中设置)。此 Codelab 的其余部分要求 Kubernetes 客户端和服务器版本均为 1.2 或更高版本。kubectl version 将显示该命令的当前版本。

9. 将您的应用部署到 Kubernetes

Kubernetes Deployment 可以使用您刚刚创建的容器映像创建、管理和扩缩应用的多个实例。让我们使用 kubectl create deployment 命令创建将应用部署到 Kubernetes 的部署:

$ kubectl create deployment hello-micronaut \
  --image=gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1

要查看您刚刚创建的 Deployment,只需运行以下命令:

$ kubectl get deployments
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-micronaut   1         1         1            1           5m

如需查看该 Deployment 创建的应用实例,请运行以下命令:

$ kubectl get pods
NAME                               READY     STATUS    RESTARTS   AGE
hello-micronaut-5647fb98c5-lh5h7   1/1       Running   0          5m

此时,您应该让容器在 Kubernetes 的控制下运行,但您仍必须使其可供外部用户访问。

10. 允许外部流量

默认情况下,Pod 只能通过其在集群中的内部 IP 访问。为了使 hello-micronaut 容器能够从 Kubernetes 虚拟网络外部访问,您必须将 Pod 作为 Kubernetes 服务公开。

在 Cloud Shell 中,您可以结合使用 kubectl expose 命令和 --type=LoadBalancer 标志将 Pod 公开给公共互联网。创建外部可访问的 IP 时需要使用此标志:

$ kubectl expose deployment hello-micronaut --type=LoadBalancer --port=8080

此命令中使用的标志指定您将使用底层基础架构提供的负载平衡器(在本例中为 Compute Engine 负载平衡器)。请注意,您应公开 Deployment,而不是直接公开 Pod。这会使生成的服务对 Deployment 管理的所有 Pod 的流量进行负载均衡(在本例中,只有 1 个 Pod,但您稍后将添加更多副本)。

Kubernetes 主实例会创建负载平衡器和相关的 Compute Engine 转发规则、目标池和防火墙规则,以使服务完全可以从 Google Cloud Platform 外部访问。

如需查找服务的可公开访问 IP 地址,只需请求 kubectl 即可列出所有集群服务:

$ kubectl get services
NAME              TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
hello-micronaut   LoadBalancer   10.39.243.251   aaa.bbb.ccc.ddd 8080:30354/TCP   1m
kubernetes        ClusterIP      10.39.240.1     <none>          443/TCP          31m

请注意,系统针对您的服务列出了 2 个 IP 地址,它们都服务于端口 8080。一个是仅在您的云虚拟网络内可见的内部 IP;另一个是外部负载均衡的 IP在此示例中,外部 IP 地址为 aaa.bbb.ccc.ddd

现在,通过将浏览器指向以下网址,您应该就可以访问该服务:http://<EXTERNAL_IP>:8080/hello

11. 扩大服务规模

Kubernetes 提供的一项强大功能就是让您轻松扩展应用。假设您的应用突然需要更多容量;您只需指示副本控制器为应用实例管理新数量的副本:

$ kubectl scale deployment hello-micronaut --replicas=3
deployment.extensions "hello-micronaut" scaled

$ kubectl get deployment
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-micronaut   3         3         3            3           16m

请注意此处的声明式方法,您可以声明始终运行的实例数量,而不是启动或停止新实例。Kubernetes 协调循环只是确保实际情况与您的要求相符,然后根据需要采取措施。

12. 发布服务升级

在某些情况下,您部署到生产环境的应用需要问题修复或需要其他功能。Kubernetes 可帮助您将新版本部署到生产环境,而不会影响您的用户。

首先,我们来修改应用。从 Cloud Shell 打开代码编辑器。

5aee8f3d1e003571

转到 /jib/examples/micronaut/src/main/groovy/example/micronaut/HelloController.groovy,然后更新响应的值:

@Controller("/hello")
class HelloController {
    @Get("/")
    String index() {
        "Hello Kubernetes World"
    }
}

/jib/examples/micronaut/build.gradle 中,我们会更新下面这行代码,将映像版本从 0.1 升级到 0.2:

version '0.2'

然后重新构建该应用,并将其打包到最新变更中:

$ ./gradlew jibDockerBuild

标记映像并将其推送到容器映像注册表中:

$ docker tag micronaut-jib:0.2 \
         gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2
$ docker push gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2

您现在可以让 Kubernetes 顺利地将您的副本控制器更新到应用的新版本。如需更改正在运行的容器的映像标签,您需要修改现有的 hello-micronaut deployment 并将映像从 gcr.io/PROJECT_ID/micronaut-jib:0.1 更改为 gcr.io/PROJECT_ID/micronaut-jib:0.2

您可以使用 kubectl set image 命令要求 Kubernetes 通过滚动更新在整个集群中部署应用的新版本,一次一个实例:

$ kubectl set image deployment/hello-micronaut \
          micronaut-jib=gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2

deployment.apps "hello-micronaut" image updated

再次检查 http://EXTERNAL_IP:8080,看看它是否返回了新响应。

13. 回滚

糟糕,您在更新应用版本时是否出错了?可能是因为新版本包含错误,您需要快速回滚。借助 Kubernetes,您可以轻松回滚到先前的状态。让我们运行以下命令来回滚应用:

$ kubectl rollout undo deployment/hello-micronaut

查看一下 Service 的输出,我们将返回到最初的“Hello World”消息。

14. 总结

在此步骤中,您将设置一项基于 Apache Groovy 的简单 Micronaut hello world 服务,并直接从 Cloud Shell 中运行它,使用 Jib 将其打包为一个容器,并部署到 Google Kubernetes Engine。

15. 恭喜!

您已了解如何构建新的 Apache Groovy / Micronaut Web 微服务,并将其部署到 Google Kubernetes Engine 上的 Kubernetes。

了解详情

许可

此作品已获得 Creative Commons Attribution 2.0 通用许可授权。