教程:使用 VNet 中的 Azure Database for MySQL 灵活服务器在 AKS 群集上部署 Spring Boot 应用程序

适用于:Azure Database for MySQL - 灵活服务器

在本教程中,你将了解如何在后端使用 Azure Database for MySQL 灵活服务器Azure Kubernetes 服务 (AKS) 群集上部署 Spring Boot 应用程序,使其在 Azure 虚拟网络中安全地彼此通信。

注意

本教程假定你基本了解 Kubernetes 概念、Java Spring Boot 和 MySQL。 对于 Spring Boot 应用程序,我们建议使用 Azure Spring Apps。 但是,你仍然可以使用 Azure Kubernetes 服务作为目标。

先决条件

创建 Azure Database for MySQL 灵活服务器

创建资源组

Azure 资源组是一个逻辑组,用于部署和管理 Azure 资源。 我们在 chinaeast2 位置使用 az group create 命令创建一个资源组 rg-mysqlaksdemo。

  1. 打开命令提示符。
  2. 登录 Azure 帐户。
    az login
    
  3. 选择自己的 Azure 订阅。
    az account set -s <your-subscription-ID>
    
  4. 创建资源组。
    az group create --name rg-mysqlaksdemo --location chinaeast2
    

创建 Azure Database for MySQL 灵活服务器实例

现在,我们将在虚拟网络中创建 Azure Database for MySQL 灵活服务器实例(专用访问连接方法)。

  1. 为本教程中的所有资源创建一个 Azure 虚拟网络 vnet-mysqlaksdemo,并为 Azure Database for MySQL 灵活服务器实例创建子网 subnet-mysql。

    az network vnet create \
    --resource-group rg-mysqlaksdemo \
    --name vnet-mysqlaksdemo \
    --address-prefixes 155.55.0.0/16 \
    --subnet-name subnet-mysql \
    --subnet-prefix 155.55.1.0/24 
    
  2. 使用 az mysql flexible-server create 命令在上面创建的子网中创建 Azure Database for MySQL 灵活服务器实例 mysql-mysqlaksdemo。 将管理员用户名和密码替换为你的值。

    az mysql flexible-server create \
    --name mysql-mysqlaksdemo \
    --resource-group rg-mysqlaksdemo \
    --location chinaeast2 \
    --admin-user <your-admin-username> \
    --admin-password <your-admin-password> \
    --vnet vnet-mysqlaksdemo \
    --subnet subnet-mysql
    

    现在,你已经在 chinaeast2 区域中创建了一个 Azure Database for MySQL 灵活服务器实例,该服务器具有可突发 B1MS 计算、32 GB 存储空间、7 天备份保持期,并且位于所提供的子网 subnet-mysql 中。 此子网不得具有其中部署的任何其他资源,并且将委托给 Microsoft.DBforMySQL/flexibleServers。

  3. 配置新的 Azure Database for MySQL 灵活服务器数据库 demo,以便与 Spring Boot 应用程序一起使用。

    az mysql flexible-server db create \
    --resource-group rg-mysqlaksdemo \
    --server-name mysql-mysqlaksdemo \
    --database-name demo
    

创建 Azure 容器注册表

在资源组中创建私有 Azure 容器注册表。 本教程的后续步骤会将示例应用作为 Docker 映像推送到此注册表。 用注册表的唯一名称替换 mysqlaksdemoregistry

az acr create --resource-group rg-mysqlaksdemo \
--location chinaeast2 \
--name mysqlaksdemoregistry \
--sku Basic

编写应用程序代码

在本部分,我们将对演示应用程序进行编码。 如果想要加快进度,可下载 https://github.com/Azure-Samples/tutorial-springboot-mysql-aks 处提供的已编码的应用程序,并跳转到下一部分 - 生成映像并推送到 ACR

  1. 使用 Spring Initializr 生成应用程序。

    curl https://start.spring.io/starter.tgz \
    -d dependencies=web,data-jdbc,mysql \
    -d baseDir=springboot-mysql-aks \
    -d bootVersion=2.5.6.RELEASE \
    -d artifactId=springboot-mysql-aks \
    -d description="Spring Boot on AKS connecting to Azure DB for MySQL" \
    -d javaVersion=1.8 | tar -xzvf -
    

    将在 springboot-mysql-aks 文件夹中生成一个基础 Spring Boot 应用程序。

    请使用你喜欢的文本编辑器(如 VSCode)或任何 IDE 执行后续步骤。

  2. 将 Spring Boot 配置为使用 Azure Database for MySQL 灵活服务器。

    打开 src/main/resources/application.properties 文件,添加以下片段。 此代码会从 Kubernetes 清单文件中读取数据库主机、数据库名称、用户名和密码。

    logging.level.org.springframework.jdbc.core=DEBUG
    spring.datasource.url=jdbc:mysql://${DATABASE_HOST}:3306/${DATABASE_NAME}?serverTimezone=UTC
    spring.datasource.username=${DATABASE_USERNAME}
    spring.datasource.password=${DATABASE_PASSWORD}
    spring.datasource.initialization-mode=always
    

    警告

    配置属性 spring.datasource.initialization-mode=always 意味着 Spring Boot 将在每次服务器启动时使用我们稍后将创建的 schema.sql 文件来自动生成数据库架构。 这非常适合测试,但请记住,这会在每次重启时删除数据,因此不应在生产中使用!

    注意

    我们将 ?serverTimezone=UTC 追加到配置属性 spring.datasource.url 中,以指示 JDBC 驱动程序在连接到数据库时使用 UTC 日期格式(或协调世界时)。 否则,Java 服务器将不使用与数据库相同的日期格式,这将导致错误。

  3. 创建数据库架构。

    Spring Boot 会自动执行 src/main/resources/schema.sql 来创建数据库架构。 创建包含以下内容的文件:

    DROP TABLE IF EXISTS todo;
    CREATE TABLE todo (id SERIAL PRIMARY KEY, description VARCHAR(255), details VARCHAR(4096), done BOOLEAN);
    
  4. 对 Java Spring Boot 应用程序进行编码。

    添加 Java 代码,以便使用 JDBC 在 MySQL 服务器中存储并检索数据。 在 DemoApplication 类旁创建新的 Todo Java 类并添加以下代码:

    package com.example.springbootmysqlaks;
    
    import org.springframework.data.annotation.Id;
    
    public class Todo {
    
        public Todo() {
        }
    
        public Todo(String description, String details, boolean done) {
            this.description = description;
            this.details = details;
            this.done = done;
        }
    
        @Id
        private Long id;
    
        private String description;
    
        private String details;
    
        private boolean done;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
        public String getDetails() {
            return details;
        }
    
        public void setDetails(String details) {
            this.details = details;
        }
    
        public boolean isDone() {
            return done;
        }
    
        public void setDone(boolean done) {
            this.done = done;
        }
    }
    

    此类是映射在之前创建的 todo 表上的域模型。

    若要管理该类,你需要一个存储库。 在同一包中定义一个新的 TodoRepository 接口:

    package com.example.springbootmysqlaks;
    
    import org.springframework.data.repository.CrudRepository;
    
    public interface TodoRepository extends CrudRepository<Todo, Long> {
    }
    

    此存储库是 Spring Data JDBC 管理的存储库。

    创建可存储和检索数据的控制器,完成该应用程序。 在同一包中实现 TodoController 类,并添加以下代码:

    package com.example.springbootmysqlaks;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping("/")
    public class TodoController {
    
        private final TodoRepository todoRepository;
    
        public TodoController(TodoRepository todoRepository) {
            this.todoRepository = todoRepository;
        }
    
        @PostMapping("/")
        @ResponseStatus(HttpStatus.CREATED)
        public Todo createTodo(@RequestBody Todo todo) {
            return todoRepository.save(todo);
        }
    
        @GetMapping("/")
        public Iterable<Todo> getTodos() {
            return todoRepository.findAll();
        }
    }
    
  5. 在基目录 springboot-mysql-aks 中创建新的 Dockerfile,并复制此代码片段。

    FROM openjdk:8-jdk-alpine
    RUN addgroup -S spring && adduser -S spring -G spring
    USER spring:spring
    ARG DEPENDENCY=target/dependency
    COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
    COPY ${DEPENDENCY}/META-INF /app/META-INF
    COPY ${DEPENDENCY}/BOOT-INF/classes /app
    ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.springbootmysqlaks.DemoApplication"]
    
  6. 转到 pom.xml 文件,将 pom.xml 文件中的 <properties> 集合更新为 Azure 容器注册表的注册表名称和 jib-maven-plugin 的最新版本。 注意:如果 ACR 名称包含大写字符,请务必将其转换为小写字符。

    <properties>
     	<docker.image.prefix>mysqlaksdemoregistry.azurecr.io</docker.image.prefix>
     	<jib-maven-plugin.version>3.1.4</jib-maven-plugin.version>
     	<java.version>1.8</java.version>
     </properties>
    
  7. 更新 pom.xml 文件中的 <plugins> 集合,使 <plugin> 元素包含 jib-maven-plugin 的条目,如下所示。 请注意,我们将使用 Microsoft 容器注册表 (MCR) 中的基础映像:mcr.microsoft.com/java/jdk:8-zulu-alpine,其中包含 Azure 正式支持的 JDK。 如需具有官方支持的 JDK 的其他 MCR 基础映像,请参阅 Docker Hub

    <plugin>
        <artifactId>jib-maven-plugin</artifactId>
        <groupId>com.google.cloud.tools</groupId>
        <version>${jib-maven-plugin.version}</version>
        <configuration>
            <from>
                <image>mcr.microsoft.com/java/jdk:8-zulu-alpine</image>
            </from>
            <to>
                <image>${docker.image.prefix}/${project.artifactId}</image>
            </to>
        </configuration>
    </plugin>
    

生成映像并推送到 ACR

在命令提示符下,导航到 springboot-mysql-aks 文件夹,并运行以下命令来先设置 Azure 容器注册表的默认名称(否则将需要在 az acr login 中指定名称),生成映像,然后将映像推送到注册表。

确保在执行此步骤时 Docker 守护程序正在运行。

az config set defaults.acr=mysqlaksdemoregistry
az acr login && mvn compile jib:build

在 AKS 上创建 Kubernetes 群集

现在,我们将在虚拟网络 vnet-mysqlaksdemo 中创建一个 AKS 群集。

在本教程中,我们将在 AKS 中使用 Azure CNI 网络。 如果要改为配置 kubenet 网络,请参阅在 AKS 中使用 kubenet 网络

  1. 为要使用的 AKS 群集创建子网 subnet-aks。

    az network vnet subnet create \
    --resource-group rg-mysqlaksdemo \
    --vnet-name vnet-mysqlaksdemo \
    --name subnet-aks \
    --address-prefixes 155.55.2.0/24
    
  2. 获取子网资源 ID。

    SUBNET_ID=$(az network vnet subnet show --resource-group rg-mysqlaksdemo --vnet-name vnet-mysqlaksdemo --name subnet-aks --query id -o tsv)
    
  3. 在虚拟网络中创建一个 AKS 群集,对其附加 Azure 容器注册表 (ACR) mysqlaksdemoregistry。

        az aks create \
        --resource-group rg-mysqlaksdemo \
        --name aks-mysqlaksdemo \
        --network-plugin azure \
        --service-cidr 10.0.0.0/16 \
        --dns-service-ip 10.0.0.10 \
        --docker-bridge-address 172.17.0.1/16 \
        --vnet-subnet-id $SUBNET_ID \
        --attach-acr mysqlaksdemoregistry \
        --dns-name-prefix aks-mysqlaksdemo \
        --generate-ssh-keys
    

    在创建群集的过程中还定义了以下 IP 地址范围:

    • --service-cidr 用于为 AKS 群集中的内部服务分配 IP 地址。 可以使用任何专用地址范围,只要其符合以下要求即可:

      • 不得在群集的虚拟网络 IP 地址范围内
      • 不得与群集虚拟网络对等互连的任何其他虚拟网络重叠
      • 不得与任何本地 IP 重叠
      • 不得在范围 169.254.0.0/16、172.30.0.0/16、172.31.0.0/16 或 192.0.2.0/24 中
    • --dns-service-ip 地址是群集 DNS 服务的 IP 地址。 此地址必须在 Kubernetes 服务地址范围内。 请勿使用地址范围内的第一个 IP 地址。 子网范围内的第一个地址用于 kubernetes.default.svc.cluster.local 地址。

    • --docker-bridge-address 是 Docker 网桥地址,它表示所有 Docker 安装中显示的默认 docker0 网桥地址 。 必须选择一个不与网络上其他 CIDR(包括群集的服务 CIDR 和 Pod CIDR)冲突的地址空间。

将应用程序部署到 AKS 群集

  1. 在 Azure 门户上转到 AKS 群集资源。

  2. 从任何资源视图(命名空间、工作负载、服务和流入量、存储或配置)中选择“添加”和“通过 YAML 添加” 。

    Screenshot that shows Azure Kubernetes Service resource view on Azure portal.

  3. 粘贴到以下 YAML 中。 替换 Azure Database for MySQL 灵活服务器管理员用户名和密码的值。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: springboot-mysql-aks
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: springboot-mysql-aks
      template:
        metadata:
          labels:
            app: springboot-mysql-aks
        spec:
          containers:
          - name: springboot-mysql-aks
            image: mysqlaksdemoregistry.azurecr.io/springboot-mysql-aks:latest
            env:
            - name: DATABASE_HOST
              value: "mysql-mysqlaksdemo.mysql.database.chinacloudapi.cn"
            - name: DATABASE_USERNAME
              value: "<your-admin-username>"
            - name: DATABASE_PASSWORD
              value: "<your-admin-password>"
            - name: DATABASE_NAME    
              value: "demo"     
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: springboot-mysql-aks
    spec:
      type: LoadBalancer
      ports:
      - port: 80
        targetPort: 8080
      selector:
        app: springboot-mysql-aks
    
  4. 选择 YAML 编辑器底部的“添加”,以部署应用程序。

    Screenshot that shows Add with YAML editor.

  5. 添加 YAML 文件后,资源查看器会显示 Spring Boot 应用程序。 记下外部服务中包含的链接的外部 IP 地址。

    Screenshot that shows Azure portal view of Azure Kubernetes cluster service external IP.

测试应用程序

若要测试应用程序,可使用 cURL。

首先,使用以下命令在数据库中创建一个新的待办事项。

curl --header "Content-Type: application/json" \
--request POST \
--data '{"description":"configuration","details":"congratulations, you have deployed your application correctly!","done": "true"}' \
http://<AKS-service-external-ip>

接下来,使用新的 cURL 请求,或在浏览器中输入群集外部 IP 来检索数据。

curl http://<AKS-service-external-ip>

此命令会返回待办事项列表,其中包括已创建的项。

[{"id":1,"description":"configuration","details":"congratulations, you have deployed your application correctly!","done":true}]

下面是这些 cURL 请求的屏幕截图:Screenshot that shows command line output of cURL requests.

可通过浏览器查看类似的输出:Screenshot that shows browser request output.

祝贺你! 你已在后端通过 Azure Database for MySQL 灵活服务器在 Azure Kubernetes 服务 (AKS) 群集上部署 Spring Boot 应用程序!

清理资源

若要避免 Azure 费用,应清除不需要的资源。 如果不再需要群集,可以使用 az group delete 命令删除资源组、容器服务及所有相关资源。

az group delete --name rg-mysqlaksdemo

注意

删除群集时,AKS 群集使用的 Microsoft Entra 服务主体不会被删除。 有关如何删除服务主体的步骤,请参阅 AKS 服务主体的注意事项和删除。 如果你使用了托管标识,则该标识由平台托管,不需要删除。

后续步骤