65.9K
CodeProject 正在变化。 阅读更多。
Home

Azure 上的 Java:添加容器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2021年4月27日

CPOL

5分钟阅读

viewsIcon

3869

在本文中,我们将创建一个 Java 微服务来处理 Azure 表存储。

上一篇文章中,我们准备了一个 Azure Kubernetes 集群来托管我们的容器化 Java 应用程序。在本系列最后一篇文章中,我们将创建一个 Java 微服务来处理Azure 表存储。然后,我们将对该微服务进行容器化,将其推送到容器注册表,并将容器化后的服务部署到我们的 Azure Kubernetes 集群。

创建 Java 微服务

我们将首先使用Quarkus框架来创建我们的微服务。请遵循这些说明开始。或者,一个更简单的替代方法是使用 Visual Studio Code。它有一个专用的 Quarkus 扩展,可以帮助您生成项目,其中还将包含 Dockerfile。安装 Quarkus 扩展后,使用 Visual Studio Code 命令面板开发 Quarkus 项目。

按如下方式配置您的 Quarkus 项目

  • 构建工具:Maven
  • 项目 groupId:com.temperatureservice
  • 项目 artifactId:temperature-service
  • 项目版本:1.0.0-SNAPSHOT
  • 包名:com.temperatureservice
  • 资源名:Temperature

生成的项目将在 docker 文件夹中包含四个 Dockerfile,以及 Temperature.java 文件(位于 java/com/temperatureservice 下),其中包含一个简单的 REST API,其中有一个 hello world 方法。我们将用一个从 SensorReadings 表中读取最近温度值的方法来替换此方法。为此,请在 pom.xml 文件的 dependencies 部分添加一个条目

<dependency>
  <groupId>com.microsoft.azure</groupId>
  <artifactId>azure-storage</artifactId>
  <version>4.2.0</version>
</dependency>

这会添加对 Java 的Azure 存储 SDK的引用。接下来,向项目中添加 SensorReadings

import com.microsoft.azure.storage.table.TableServiceEntity;
 
public class SensorReading extends TableServiceEntity{
    private String PartitionKey;
    private String RowKey;
    private String Temperature;
 
    // PartitionKey
    public String getPartitionKey() { return this.PartitionKey; }
    public void setPartitionKey(String key) { this.PartitionKey = key; }
 
    // RowKey
    public String getRowKey() { return this.RowKey; }
    public void setRowKey(String key) { this.RowKey = key; }
 
    // Temperature
    public String getTemperature() { return this.Temperature; }
    public void setTemperature(String temperature) { this.Temperature = temperature; }
}

上述类具有与我们在本系列第一篇文章中创建的类相同的结构。它派生自 TableServiceEntity — Azure 存储 SDK 用于序列化和反序列化传输到或从 Azure 表存储传输的对象的基础类。

现在让我们按如下方式修改 Temperature.java

@Path("/temperature")
public class Temperature {    
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String Get() {        
 
        try {
            CloudTable cloudTable = GetCloudTable("SensorReadings");
 
            // Filter items by the timestamp
            String filter = GetTimeFilter();                
 
            TableQuery<SensorReading> tableQuery =
                TableQuery.from(SensorReading.class)
                .where(filter);                    
 
            Iterable<SensorReading> sensorReadings = cloudTable.execute(tableQuery);                
            
            // Return recent temperature
            if(sensorReadings.iterator().hasNext()){
                SensorReading sensorReading = sensorReadings.iterator().next();
 
                return sensorReading.getTemperature();
            }
            else 
            {
                return "No recent sensor readings available";
            }
        }
        catch (Exception e) { 
            return(e.toString());
        }
    }
 
    // Other methods
}

上面的代码将连接到Azure 存储表实例,并检索过去一分钟的传感器读数。要连接到表,请添加以下辅助方法

private static CloudTable GetCloudTable(String tableName) { 
    // Connection string
    final String connectionString = 
        "DefaultEndpointsProtocol=https;" +
        "AccountName=<YOUR_ACCOUNT_NAME>;" +
        "AccountKey=<YOUR_KEY>" + 
        "TableEndpoint=<YOUR_ENDPOINT>;";
 
    try {
        // Get storage account
        CloudStorageAccount storageAccount = CloudStorageAccount.parse(connectionString);
 
        // Create the table client
        CloudTableClient tableClient = storageAccount.createCloudTableClient(); 
 
        // Get table reference
        return tableClient.getTableReference(tableName); 
        
    } catch (Exception e) { 
        return null;
    } 
}

该方法返回 CloudTable 实例,我们从中获取项目。要创建过滤器,请使用 GetTimeFilter 辅助方法

private static final String GetTimeFilter() { 
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
        "yyyy-MM-dd'T'HH:mm:ss.SS'Z'");
                   
    String dateTimeString = ZonedDateTime.now(ZoneId.of("UTC"))
        .minusMinutes(1).format(formatter);
 
    return String.format("Timestamp ge datetime'%s'", dateTimeString);
}

此辅助方法创建一个形状为 Timestamp ge datetime'2021-03-09T08:09:07.54Z' 的过滤器,其中 datetime 部分是动态创建的。

请记住,在实现 Azure Functions 时,我们在动态生成过滤器时遇到了问题。在此,我们通过以自己的集成代码以编程方式访问云表来解决此问题。

要测试微服务,请调用以下命令

mvn compile quarkus:dev

这将启动服务。

转到您的 Azure 表并添加一个项目。然后,向本地服务发送一个 GET 请求:https://:8080/temperature。它将返回温度,如下图所示。

使用 Azure 容器注册表

基本的 Docker 术语要求您在将 Docker 镜像部署到 Kubernetes 集群之前,先将其推送到注册表。在这里,我们将使用Azure 容器注册表 (ACR) 来创建我们自己的私有注册表。

让我们转到 Azure 门户并创建一个基本 SKU 的 ACR 注册表实例。我们将使用 ACR 实例 dbacr。然后,我们需要将 ACR 附加到 AKS 集群。最简单的方法是通过 Azure CLI,调用以下命令(请参阅下面的屏幕截图)

az aks update -g aks-group -n akscluster --attach-acr dbacr

构建 Docker 镜像

要构建 Docker 镜像,我们可以使用与项目一起创建的四个 Dockerfile 之一。但是,这些仅在调用 mvn package 命令后在本地工作。为了克服这个问题,请修改 Dockerfile.jvm 以使用多阶段构建,其中首先构建应用程序,然后创建一个更轻的映像进行部署

FROM maven:3.6.3-jdk-11 AS MAVEN_BUILD
 
# Copy the pom and src code to the container
COPY ./ ./
 
# Package our application code
RUN mvn clean package
 
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3 
 
ARG JAVA_PACKAGE=java-11-openjdk-headless
ARG RUN_JAVA_VERSION=1.3.8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
# Install java and the run-java script
# Also set up permissions for user `1001`
RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
    && microdnf update \
    && microdnf clean all \
    && mkdir /deployments \
    && chown 1001 /deployments \
    && chmod "g+rwX" /deployments \
    && chown 1001:root /deployments \
    && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
    && chown 1001 /deployments/run-java.sh \
    && chmod 540 /deployments/run-java.sh \
    && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security
 
# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
 
# We make four distinct layers so if there are application changes the library layers can be reused
COPY --from=MAVEN_BUILD --chown=1001 target/quarkus-app/lib/ /deployments/lib/
COPY --from=MAVEN_BUILD --chown=1001 target/quarkus-app/*.jar /deployments/
COPY --from=MAVEN_BUILD --chown=1001 target/quarkus-app/app/ /deployments/app/
COPY --from=MAVEN_BUILD --chown=1001 target/quarkus-app/quarkus/ /deployments/quarkus/
 
EXPOSE 8080
USER 1001
 
ENTRYPOINT [ "/deployments/run-java.sh" ]

您可以通过从应用程序文件夹调用以下命令来测试 Dockerfile(请注意,最后的 . 指向 docker 构建上下文 /current directory/

docker build -f src/main/docker/Dockerfile.jvm -t quarkus/temperature-service-jvm .
 
docker run -i --rm -p 8080:8080 quarkus/temperature-service-jvm

部署到 AKS

现在一切准备就绪,我们可以通过 Azure DevOps 管道将应用程序部署到 AKS 集群。最直接的方法是通过 Azure 门户中提供的部署中心。

部署中心使您可以快速创建 CI/CD 管道和必要的 Kubernetes 清单。要开始,请将您的代码推送到 Azure Repos。然后,在部署中心中单击“下一步”。在打开的页面中,选择您的项目和分支。

在下一步中,配置 Dockerfile 路径,将端口更改为“8080”,并在 Docker 构建上下文中键入“.”。

最后,创建新的 Kubernetes 命名空间并选择您的 Azure 容器注册表。

部署中心将配置 Azure DevOps 管道。您可以通过单击 Build 超链接来访问它们。

这将把您重定向到 Azure DevOps。以下是正在运行的 build 管道的一个示例

以下是 release 管道的一个示例

测试服务

在底层,部署中心创建了由以下部分组成的 Kubernetes 清单

  • 使用推送到 Azure Kubernetes 服务的 Docker 镜像进行部署
  • 通过 Azure 负载均衡器公开应用程序的服务

要访问 Quarkus 微服务,您需要获取服务的公共 IP。最简单的方法是通过 Azure Cloud Shell,调用以下命令

az aks get-credentials -n akscluster -g aks-group
kubectl get svc --all-namespaces=true

您的服务的公共 IP 出现在 EXTERNAL-IP 列中。使用此 IP 地址向您的温度服务发送 GET 请求,如下所示

curl http://<YOUR_PUBLIC_IP>:8080/temperature

后续步骤

在本文中,我们创建了一个与 Azure 表存储协同工作的 Java 微服务。然后,我们对其进行了 Docker 化,并将其部署到了 Azure Kubernetes Service。Azure 表存储的连接字符串硬编码在服务中。实际上,您可以通过 Kubernetes 机密将其传递给 Docker 映像。

要了解有关在 Azure 上使用 Kubernetes 的更多信息,请浏览Kubernetes BundleAzure 上的 Kubernetes 入门实践

在下一期 Java on Azure 系列中,我们将了解更多关于监控和扩展容器化应用程序云原生身份验证以及利用云原生服务的信息。

© . All rights reserved.