文档首页/ 云商店/ 接入指南/ 商品接入相关接口/ 自动部署接入指南/ 最佳实践/ Linux单机ECS+RDS完成springboot单机自动化交付应用上架
更新时间:2025-09-09 GMT+08:00
分享

Linux单机ECS+RDS完成springboot单机自动化交付应用上架

应用场景

本文介绍基于springboot+软件包方式的单机License类应用构建并完成自动化交付的实践。

实践说明

本示例会构建基于springboot+软件包方式的单机License类应用实现一键自动化交付上云的实践,具体的构建流程如下:

  1. 应用开发:云资源清单梳理、软件包制作、应用部署脚本的开发、自动部署模板的开发。
  2. 应用上架:将上一步开发的软件包和脚本托管到云商店并完成漏洞扫描。
  3. 应用购买:作为用户完成该自动部署测试。

应用部署架构

本应用软件主体部署在一台虚拟机上,软件运行依赖数据库,在本示例中使用华为云提供的高阶云服务RDS,依赖的云资源为ECS、RDS、EIP,本服务示例通过弹性公网IP的8765端口对外提供服务,该软件云上部署组网结构为:

应用开发

  1. 根据应用架构梳理资源清单如下 :

    序号

    区域

    云服务类型

    云服务规格说明

    数量

    1

    华北-北京四

    弹性公网IP

    带宽费用:独享 | 全动态BGP | 按带宽计费 | 10Mbit/s (可选)

    1

    2

    华北-北京四

    弹性云服务器

    规格:X86 | 统一计算增强型 | c7.large.2 | 2核 | 4G

    1

    3

    华北-北京四

    云数据库 RDS for MySQL

    MySQL | 8.0 | 单机 | 独享型 | 2核4GB | SSD云盘 | 40GB

    1

  2. 软件包制作
    1. jar包构建:本示例使用的软件代码可以从华为云开发体验馆 Codelabs中获取,使用如下命令构建本demo的jar包;
      cd code/linux-single-ecs-rds-demo
      mvn clean install 
    2. 创建应用目录:jar包构建完成后,使用如下结构创建软件包目录;
      software
       |——script
       |
       |——runtime_dependencies  #运行时依赖
       |     └—openjdk-17.0.2_linux-x64_bin.tar.gz          #jdk
       |——services #程序包
       |     └—DemoApp-0.0.1-SNAPSHOT.jar               #应用软件
       |——config
       └——...

部署脚本开发

  • 分别完成下列脚本的开发,并将脚本置于script目录下:

目录

说明

bin

该目录下存放的是用户软件包的可执行信息,比如可执行的bin文件,依赖的压缩包文件等。

scripts

该目录下存放的是生命周期脚本。

在创建应用时,可以根据生命周期脚本的位置指定执行命令。比如install阶段,指定"bash scripts/install.sh",执行安装脚本。

  • 软件包应用支持的生命周期如下:
  • 安装(install):软件安装命令。
  • 启动后处理(poststart):软件启动后操作。
  • 启动(start):软件启动命令。
  • 重启(restart):执行软件重启命令,用于应用健康检查失败恢复使用。
  • 停止前处理(prestop):软件停止前操作。
  • 停止(stop):软件停止命令。
  • 更新(update):软件升级命令。
  • 卸载(uninstall):软件卸载命令。
  • 脚本示例如下:
software 
 |——script        #基础运行时安装脚本、程序包部署、安装脚本
 |     |-install.sh      #部署、安装脚本
 |     |-configure.sh    #配置初始化脚本
 |     |-start.sh        #启动脚本
 |     └—....sh          #其他脚本
 |
 |——runtime_dependencies  #运行时依赖
 |     └—openjdk-17.0.2_linux-x64_bin.tar.gz          #jdk
 |——services #程序包
 |     └—DemoApp-0.0.1-SNAPSHOT.jar               #应用软件
 |——config                #软件所需配置文件,数据库初始化脚本等
 |     |—init_db.sql     #RDS 初始化脚本
 |     |-application.properties.tpl   #应用初始化配置文件
 └——...

自动化部署模版开发

  1. 自动部署模板用于购买梳理的 应用部署架构 中的云资源并将其关联起来。
    rfstemplate 
    |-- datas.tf
    |-- locals.tf
    |-- main.tf
    |-- modules
    |   |-- ecs|   
    |   |-- main.tf
    |   |   |-- outputs.tf
    |   |   |-- variables.tf
    |   |   `-- versions.tf
    |   |-- eip
    |   |   |-- main.tf
    |   |   |-- outputs.tf
    |   |   |-- variables.tf
    |   |   `-- versions.tf
    |   |-- rds_mysql_single_expansion
    |   |   |-- main.tf
    |   |   |-- outputs.tf
    |   |   |-- variables.tf
    |   |   `-- versions.tf
    |   |-- security-group
    |   |   |-- main.tf
    |   |   |-- outputs.tf
    |   |   |-- variables.tf
    |   |   `-- versions.tf
    |   `-- vpc
    |       |-- main.tf
    |       |-- outputs.tf
    |       |-- variables.tf
    |       `-- versions.tf
    |-- outputs.tf
    |-- providers.tf
    |-- variables.tf
    `-- versions.tf
  2. main.tf模版。
    # TODO asset_id,为创建的资产id;asset_version,资产版本
    data "huaweicloud_koogallery_assets" "assets" {
      asset_id      = ""  //必填
      deployed_type = "software_package"
      asset_version = "v1.0" //必填,区分大小写
    }
    ##############################################
    # 1) VPC & 子网
    ##############################################
    module "vpc" {
      source = "./modules/vpc"
      # config of vpc
      vpc_name        = "${local.app_prefix}-vpc-${local.name_suffix}"
      vpc_cidr        = var.vpc_cidr
      availability_zone = local.az_maps[local.region]
      # config of subnet
      subnet_name     = "${local.app_prefix}-subnet-${local.name_suffix}"
      vpc_subnet_cidr = var.subnet_cidr
      vpc_subnet_dns_list   = local.subnet_dns_list_maps[local.region]
      vpc_subnet_gateway_ip = var.subnet_gw
    }
    ##############################################
    # 2) 安全组(ECS 与 RDS 分离)
    ##############################################
    # ECS 安全组:放通 SSH(22) + APP(8765),出向全放
    module "sg_ecs" {
      source = "./modules/security-group"
      is_secgroup_create      = true
      name_suffix             = local.name_suffix
      secgroup_name           = "ecs-sg"
      is_delete_default_rules = true
      secgroup_rules_configuration = [
        {
          description      = "Allow SSH from remote"
          direction        = "ingress"
          ethertype        = "IPv4"
          protocol         = "tcp"
          ports            = "22"
          remote_ip_prefix = var.remote_ip_cidr
          remote_group_id  = null
        },
        {
          description      = "Allow APP 8080 from remote"
          direction        = "ingress"
          ethertype        = "IPv4"
          protocol         = "tcp"
          ports            = "8765"
          remote_ip_prefix = var.remote_ip_cidr
          remote_group_id  = null
        },
        {
          description      = "Egress all"
          direction        = "egress"
          ethertype        = "IPv4"
          protocol         = null
          ports            = null
          remote_ip_prefix = "0.0.0.0/0"
          remote_group_id  = null
        }
      ]
    }
    # RDS 安全组:仅允许来自 ECS 安全组的 3306
    module "sg_rds" {
      source = "./modules/security-group"
      is_secgroup_create      = true
      name_suffix             = local.name_suffix
      secgroup_name           = "rds-sg"
      is_delete_default_rules = true
      secgroup_rules_configuration = [
        {
          description      = "Allow MySQL from ECS SG"
          direction        = "ingress"
          ethertype        = "IPv4"
          protocol         = "tcp"
          ports            = "3306"
          remote_ip_prefix = var.remote_ip_cidr
          remote_group_id  = null
        },
        {
          description      = "Egress all"
          direction        = "egress"
          ethertype        = "IPv4"
          protocol         = null
          ports            = null
          remote_ip_prefix = var.remote_ip_cidr
          remote_group_id  = null
        }
      ]
    }
    ##############################################
    # 3) RDS MySQL
    ##############################################
    module "rds" {
      source = "./modules/rds_mysql_single_expansion"
      is_rds_create            = true
      charging_mode            = local.charging_mode
      period_unit              = local.period_unit
      period                   = local.period
      name_suffix              = local.name_suffix
      db_instance_name         = "${local.app_prefix}-rds-mysql"
      time_zone                = "UTC"
      db_availability_zone     = slice(local.rds_azs,0 ,1 )
      vpc_id                   = module.vpc.vpc_id
      subnet_id                = module.vpc.subnet_id
      security_group_id        = module.sg_rds.secgroup_id[0]
      flavor = data.huaweicloud_rds_flavors.rds_flavor.flavors[0].name
      lower_case_table_names = "1"
      # db config
      db_engines               = local.db_type
      db_version               = local.db_version
      db_password              = var.db_password
      db_port                  = local.db_port
      # backup_strategy
      keep_days  = local.db_keep_days
      start_time = local.db_start_time
      # volume config
      db_instance_storage_type = local.db_instance_storage_type
      db_allocated_storage     = local.db_allocated_storage
      #数据库
      database_name = var.db_name
      character_set = "utf8"
      #账号
      account_name = var.db_user
      account_pwd = var.db_password
      rds_tags                 = local.tags
    }
    // Create an EIP, if you do not need to create an EIP, delete the code and modules/eip directory
    module "eip" {
      source = "./modules/eip"
      is_eip_create = true
      count = 4
      name_suffix = local.name_suffix
      eip_name    = format("%s-%s", local.app_id, "eip")
      publicip_type         = local.publicip_type
      bandwidth_name        = format("%s-%s", local.app_id, "bandwidth")
      bandwidth_share_type  = local.bandwidth_share_type
      bandwidth_charge_mode = local.bandwidth_charge_mode
      bandwidth_size        = local.bandwidth_size
      charging_mode = local.charging_mode
      period_unit   = local.period_unit
      period        = local.period
      eip_tags = local.tags
    }
    ##############################################
    # 4) ECS
    ##############################################
    module "ecs" {
      source = "./modules/ecs"
      is_instance_create     = true
      instance_count         = 1
      name_suffix            = local.name_suffix
      instance_name          = "${local.app_prefix}-server-ecs"
      instance_image_id      = local.final_image_id
      instance_flavor_id     = var.ecs_flavor_id
      security_group_ids     = module.sg_ecs.secgroup_id
      admin_pass             = var.admin_password
      eip_id = module.eip[count.index].id != null && length(module.eip[count.index].id) > 0 ? module.eip[count.index].id[0] : null
      system_disk_type       = "GPSSD"
      system_disk_size       = 50
      networks_configuration = [{
        subnet_id         = module.vpc.subnet_id
        fixed_ip_v4       = null
        ipv6_enable       = false
        source_dest_check = null
        access_network    = null
      }]
        user_data = <<-EOF
        #! /bin/bash
        #1.下载软件包到/opt/software.zip
        wget -O /opt/software.zip "${local.software_url_decode}"
        #2.解压
        unzip  /opt/software.zip -d /opt/
        cd /opt/scripts
        sh mysql_init.sh "${module.rds_mysql.rds_ip[0]}" "3306" "${var.db_password}"
        sh clickhouse_install.sh
        set -e # 如果任何命令失败,立即退出
        # 从参数接收变量
        PACKAGE_URL="$package_url"
        SERVER_PORT="$server_port"
        DB_HOST="$db_host"
        DB_PORT="$db_port"
        DB_NAME="$db_name"
        DB_USER="$db_user"
        DB_PASS="$db_pass"
        echo ">>> 正在准备环境..."
        # 确保核心工具已安装 (适用于 CentOS/EulerOS 和 Ubuntu/Debian)
        if command -v yum >/dev/null 2>&1; then
          sudo yum install -y unzip wget dos2unix
        elif command -v apt-get >/dev/null 2>&1; then
          sudo apt-get update && sudo apt-get install -y unzip wget dos2unix
        fi
        # 清理旧文件
        sudo rm -rf /tmp/software.zip /tmp/software /opt/springapp
        echo ">>> 1) 下载软件包从 ${local.software_url_decode}..."
        wget -O /tmp/software.zip "${local.software_url_decode}"
        echo ">>> 2) 解压软件包..."
        sudo unzip /tmp/software.zip -d /tmp/software
        # 将所有配置导出为环境变量,供后续脚本使用
        export SERVER_PORT
        export DB_HOST
        export DB_PORT
        export DB_NAME
        export DB_USER
        export DB_PASS
        echo ">>> 3) 执行安装脚本..."
        sudo bash /tmp/software/script/install.sh
        echo ">>> 4) 渲染配置文件..."
        sudo -E bash /tmp/software/script/configure.sh
        echo ">>> 5) 初始化数据库..."
        sudo -E bash /tmp/software/script/init_db.sh
        echo ">>> 6) 重启服务..."
        sudo bash /tmp/software/script/restart.sh
        echo ">>> 部署完成!"
       EOF
      }
      instance_tags = local.tags
    }

    上述main.tf模版的作用是:

    ① 新建VPC。

    ② 基于常量配置,以固定镜像ID、CPU核数、计费模式拉起ECS实例。

    ③ 购买RDS、EIP。

    ④ 配置安全组。

    ⑤ 关联EIP。

    ⑥ 执行软件部署脚本

应用上架

将开发好的软件包托管至云商店,登录“卖家中心-资产中心-添加应用资产”,具体操作流程请参考:应用资产发布软件包操作指南

商家提交应用资产发布申请后,云商店会对资产中的软件包进行自动化安全扫描。

  • 如果扫描时有漏洞,资产安全审核会驳回您的申请,您需要完成漏洞的修复。
  • 如安全问题已完成整改,点击“修改”,重新上传已修复的软件包再次提交审核。
  • 如安全扫描存在误报,点击“申诉”,进入申诉页面,提交申诉说明。

自动化部署测试

  • 测试券申请:模板测试会产生一部分云资源消耗的费用,商家可以申请测试券来抵扣;
  1. 登录华为云价格计算器:选择所需云资源(计费模式请选择按需,每次申请时长最长24小时)> 加入清单 > 导出云资源清单;

  2. 发送邮件,提交云资源清单,邮件内容格式如下:

    发送邮件至 wangyinxu1@huawei.com

    抄送:gengliang@huawei.com,wangzichun6@h-partners.com

    邮件标题:202X年云商店中国站联营伙伴自动化部署测试券申请-xx公司-xx商品

    邮件正文:202X年云商店中国站联营伙伴自动化部署测试券申请-xx公司-xx商品,所需云资源清单见附件

    邮件附件:自动化部署-xx公司-xx商品云资源清单

  3. 登录卖家中心> 测试券管理,提交“测试券申请”,填写规则如下:

    填写测试券方案名称:202X年云商店中国站联营伙伴自动化部署-xx公司-xx商品

    选择申请场景:联营商品上架

    选择适用商品:选择需要对接的具体商品

    填写计划使用期限:不限

软件功能测试

  1. 从RFS资源栈输出获取应用访问地址;

  2. 测试基本功能是否正常;

  3. 测试数据库初始化、链接是否正常;

联营License商品发布

参考发布和修改License自动部署商品

相关文档