Automate Quarkus deployment with Ansible

让我们了解如何使用 Ansible构建并部署 Quarkus 应用程序。我们将了解如何通过使用 Maven 自动执行整个流程,包括从代码签出到应用程序编译,使用 Ansible 及其针对 Quarkus 的集合在目标系统上部署和启动服务(作为 systemd service)。 第一部分:应用程序代码签出、编译并在 Ansible 上打包(Ansible 运行的位置)。我们将使用其 Quarkus QuickStarts directory中提供的入门示例应用程序作为本教程的基础。我们还将使用 Ansible 的 Quarkus collection,这是一个 Ansible 扩展,它可以减轻样板文件所需的工作量,并使用 Ansible 快速构建和部署 Quarkus。

Prerequisites

如要完成本指南,您需要:

  • Roughly 15 minutes

  • An IDE

  • 安装了 JDK 17+,已正确配置 JAVA_HOME

  • Apache Maven ${proposed-maven-version}

  • 如果你想使用 Quarkus CLI, 则可以选择使用

  • 如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately

您需要在工作站上 install Ansible。完成后,您可以使用以下命令安装此专用于 Quarkus 的 Ansible 扩展:

$ ansible-galaxy collection install middleware_automation.quarkus

我们刚刚安装的 Ansible 集合仅支持使用 RPM 的 RHEL、Fedora 和其他 Linux 发行版。Ansible 将这些定义为“RedHat 系列”。虽然并非不可能,但使用其他平台(Windows、Debian、Ubuntu 等)上的内容将需要进行一些调整。

Inventory file

如果您不熟悉 Ansible,请注意必须提供清单才能使上述命令正确运行。这是一个简单的文本文件,提供了 Ansible 在其管理的目标系统上所需的信息。有关 Ansible inventory的更多信息,请参阅 Ansible 文档。

[all]

10.0.0.1
10.0.0.2

要按照本教程进行操作,您可能只想使用一台机器(localhost),并跳过 ssh 身份验证设置。可以通过使用以下清单文件轻松实现此目的:

[all]
localhost ansible_connection=local

Root access on target system

由 Ansible 集合针对 Quarkus 执行的少数任务需要目标上的管理权限(创建组和用户帐户、安装软件包)。如果 Ansible 作为 root 运行,您需要将以下选项添加到 `ansible-playbook`命令行:

$ ansible-playbook -i inventory  --ask-become-pass  ...

Tutorial

在控制器上安装 Ansible 集合后,您已经可以使用随附的剧本直接构建和部署 Quarkus 应用程序:

ansible-playbook -i inventory \
  middleware_automation.quarkus.playbook \
  -e app_name=getting-started \
  -e quarkus_app_repo_url='https://github.com/quarkusio/quarkus-quickstarts.git' \
  -e quarkus_app_source_folder='getting-started' \
  -e quarkus_path_to_folder_to_deploy=/opt/quarkus_deploy

提供给剧本的四个参数不言而喻。第一个参数 app_name`为应用程序的名称,在本示例中仅为 `getting-started。第二个参数 `quarkus_app_repo_url`为签出 Git 存储库的 URL。第三个参数为可选参数,`quarkus_app_source_folder`指定在需要时从存储库中提取源代码的子文件夹。最后一个参数表示在目标位置安装应用程序的位置。

Playbook run

成功运行上述命令后,您应该会看到类似于以下的输出:

…

PLAY [Build and deploy a Quarkus app using Ansible] ****************************

TASK [Build the Quarkus from https://github.com/quarkusio/quarkus-quickstarts.git.] ***

TASK [middleware_automation.quarkus.quarkus : Ensure required parameters are provided.] ***
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Define path to mvnw script.] *****
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure that builder host localhost has appropriate JDK installed: java-17-openjdk] ***
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Delete previous workdir (if requested).] ***
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure app workdir exists: /tmp/workdir] ***
changed: [localhost]

TASK [middleware_automation.quarkus.quarkus : Checkout the application source code.] ***
changed: [localhost]

TASK [middleware_automation.quarkus.quarkus : Build the App using Maven] *******
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Display build application log] ***
skipping: [localhost]

TASK [Deploy webapp on target.] ************************************************

TASK [middleware_automation.quarkus.quarkus : Ensure required parameters are provided.] ***
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure required OpenJDK is installed on target.] ***
skipping: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure Quarkus system group exists on target system] ***
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure Quarkus user exists on target system.] ***
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure deployement directory exits: /opt/quarkus_deploy.] ***
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Set Quarkus app source dir (if not defined).] ***
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Deploy application from  to target system] ***
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Deploy Systemd configuration for Quarkus app] ***
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Perform daemon-reload to ensure the changes are picked up] ***
skipping: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure Quarkus app service is running.] ***
ok: [localhost]

TASK [middleware_automation.quarkus.quarkus : Ensure firewalld is available.] ***
skipping: [localhost]

TASK [middleware_automation.quarkus.quarkus : Configure firewall for 8080 ports] ***
skipping: [localhost]

PLAY RECAP *********************************************************************
localhost              	: ok=15   changed=2	unreachable=0	failed=0	skipped=5	rescued=0	ignored=0
…

用于 Quarkus 的 Ansible 集合在此执行所有 heavy lifting。首先,它将从 Github 检出代码并从其源构建应用程序。它还确保用于此步骤的系统已安装所需的 OpenJDK。默认情况下,应用程序是在本地主机(Ansible 控制器)上构建的,但如果需要,可以在远程系统上执行此操作。构建应用程序后,集合将负责部署。

它再次检查目标系统上是否存在适当的 OpenJDK。然后确保目标系统上存在所需的用户和组。强烈建议此操作主要是为了能够使用普通用户而不是 root 帐户来运行 Quarkus 应用程序。

满足这些要求后,jar 可以部署在目标上,以及将应用程序集成到 systemd 中作为服务的所需配置。对 systemd 配置的任何更改都需要重新加载其守护程序,而集合可确保在需要时执行此操作。所有这些都就绪后,唯一剩下的事情就是启动服务本身,Ansible 也将负责此操作。

默认情况下,用于 Quarkus 的 Ansible 集合将安装并使用目标主机(或控制器)的 Yum 存储库中可用的 OpenJDK 17。如果您想要使用不同版本的 OpenJDK,请定义以下变量:

$ ansible-playbook -i inventory ...
    -e quarkus_java_package_version: java-17-openjdk

Validate that deployment was successful

出于本教程的目的,您可能希望手动检查 playbook 是否正确部署了应用程序。以下有几种方法可供选择。

首先,由于集合已集成,我们可以在其中一个目标上检查服务的状态:

# systemctl status getting-started.service
● getting-started.service - A Quarkus service named getting-started
   Loaded: loaded (/usr/lib/systemd/system/getting-started.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2023-04-13 12:48:18 UTC; 2min 40s ago
 Main PID: 853 (java)
	Tasks: 43 (limit: 1638)
   Memory: 73.3M
   CGroup: /system.slice/getting-started.service
       	└─853 /usr/bin/java -jar /opt/quarkus_deploy/quarkus-run.jar

Apr 13 12:48:18 bd71f39642c8 systemd[1]: Started A Quarkus service named getting-started.
Apr 13 12:48:19 bd71f39642c8 java[853]: __  ____  __  _____   ___  __ ____  ______
Apr 13 12:48:19 bd71f39642c8 java[853]:  --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
Apr 13 12:48:19 bd71f39642c8 java[853]:  -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
Apr 13 12:48:19 bd71f39642c8 java[853]: --\___\_\____/_/ |_/_/|_/_/|_|\____/___/
Apr 13 12:48:19 bd71f39642c8 java[853]: 2023-04-13 12:48:19,284 INFO  [io.quarkus] (main) getting-started 1.0.0-SNAPSHOT on JVM (powered by Quarkus 2.16.6.Final) started in 0.607s. Listening on: http://0.0.0.0:8080
Apr 13 12:48:19 bd71f39642c8 java[853]: 2023-04-13 12:48:19,309 INFO  [io.quarkus] (main) Profile prod activated.
Apr 13 12:48:19 bd71f39642c8 java[853]: 2023-04-13 12:48:19,310 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

您还可以手动测试应用程序是否可访问:

# curl -I http://localhost:8080/
HTTP/1.1 200 OK
accept-ranges: bytes
content-length: 3918
cache-control: public, immutable, max-age=86400
last-modified: Thu, 2 Mar 2023 11:03:18 GMT
date: Thu, 2 Mar 2023 11:03:18 GMT

我们将在本教程的下一部分和最后一部分中了解如何自动执行这些验证。

Writing a playbook

当然,您很可能会需要在此基础上进行构建,因此您可能希望编写自己的 playbook,而不是仅仅使用集合提供的 playbook。以下是此类 playbook 的示例:

- name: "Build and deploy a Quarkus app using Ansible"
  hosts: all
  gather_facts: true
  vars:
    quarkus_app_repo_url: 'https://github.com/quarkusio/quarkus-quickstarts.git'
    app_name: getting-started
    quarkus_app_source_folder: getting-started
    quarkus_path_to_folder_to_deploy: /opt/quarkus_deploy

  pre_tasks:
    - name: "Build the Quarkus from {{ quarkus_app_repo_url }}."
      ansible.builtin.include_role:
        name: quarkus
        tasks_from: build.yml
  tasks:
    - name: "Deploy Quarkus app on target."
      ansible.builtin.include_role:
        name: quarkus
        tasks_from: deploy.yml

要运行此 playbook,您再次使用 ansible-playbook 命令,但现在提供 playbook 的路径:

$ ansible-playbook -i inventory playbook.yml

您还可以自动化我们在上一节中提到的验证部分。您可以使用 ansible.builtin.assert 模块并填充 service facts 以确保 Quarkus 应用程序的 systemd 服务正在运行,以及使用 ansible.builtin.uri 来检查 Quarkus 应用程序是否可访问。

 post_tasks:
    - name: Populate service facts
      ansible.builtin.service_facts:

    - name: "Check that systemd service {{ app_name }} is running."
      ansible.builtin.assert:
        that:
          - ansible_facts.services is defined
          - ansible_facts.services["{{ app_name }}.service"] is defined
          - ansible_facts.services["{{ app_name }}.service"]['state'] == 'running'
          - ansible_facts.services["{{ app_name }}.service"]['status'] == 'enabled'
        quiet: true

    - name: "Check that Quarkus app is accessible"
      ansible.builtin.uri:
        url: 'http://localhost:8080/'

就是这样,伙计们!