Linux单机ECS+RDS完成springboot单机自动化交付应用上架
应用场景
本文介绍基于springboot+软件包方式的单机License类应用构建并完成自动化交付的实践。
实践说明
本示例会构建基于springboot+软件包方式的单机License类应用实现一键自动化交付上云的实践,具体的构建流程如下:
- 应用开发:云资源清单梳理、软件包制作、应用部署脚本的开发、自动部署模板的开发。
- 应用上架:将上一步开发的软件包和脚本托管到云商店并完成漏洞扫描。
- 应用购买:作为用户完成该自动部署测试。
应用部署架构
本应用软件主体部署在一台虚拟机上,软件运行依赖数据库,在本示例中使用华为云提供的高阶云服务RDS,依赖的云资源为ECS、RDS、EIP,本服务示例通过弹性公网IP的8765端口对外提供服务,该软件云上部署组网结构为:
应用开发
- 根据应用架构梳理资源清单如下 :
序号
区域
云服务类型
云服务规格说明
数量
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
- 软件包制作
- jar包构建:本示例使用的软件代码可以从华为云开发体验馆 Codelabs中获取,使用如下命令构建本demo的jar包;
cd code/linux-single-ecs-rds-demo mvn clean install
- 创建应用目录:jar包构建完成后,使用如下结构创建软件包目录;
software |——script | |——runtime_dependencies #运行时依赖 | └—openjdk-17.0.2_linux-x64_bin.tar.gz #jdk |——services #程序包 | └—DemoApp-0.0.1-SNAPSHOT.jar #应用软件 |——config └——...
- jar包构建:本示例使用的软件代码可以从华为云开发体验馆 Codelabs中获取,使用如下命令构建本demo的jar包;
部署脚本开发
- 分别完成下列脚本的开发,并将脚本置于script目录下:
目录 |
说明 |
bin |
该目录下存放的是用户软件包的可执行信息,比如可执行的bin文件,依赖的压缩包文件等。 |
scripts |
该目录下存放的是生命周期脚本。 在创建应用时,可以根据生命周期脚本的位置指定执行命令。比如install阶段,指定"bash scripts/install.sh",执行安装脚本。
|
- 脚本示例如下:
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 #应用初始化配置文件 └——...
自动化部署模版开发
- 自动部署模板用于购买梳理的 应用部署架构 中的云资源并将其关联起来。
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
- 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。
⑥ 执行软件部署脚本
应用上架
将开发好的软件包托管至云商店,登录“卖家中心-资产中心-添加应用资产”,具体操作流程请参考:应用资产发布软件包操作指南。

商家提交应用资产发布申请后,云商店会对资产中的软件包进行自动化安全扫描。
- 如果扫描时有漏洞,资产安全审核会驳回您的申请,您需要完成漏洞的修复。
- 如安全问题已完成整改,点击“修改”,重新上传已修复的软件包再次提交审核。
- 如安全扫描存在误报,点击“申诉”,进入申诉页面,提交申诉说明。
自动化部署测试
- 测试券申请:模板测试会产生一部分云资源消耗的费用,商家可以申请测试券来抵扣;
- 登录华为云价格计算器:选择所需云资源(计费模式请选择按需,每次申请时长最长24小时)> 加入清单 > 导出云资源清单;
- 发送邮件,提交云资源清单,邮件内容格式如下:
发送邮件至 wangyinxu1@huawei.com
抄送:gengliang@huawei.com,wangzichun6@h-partners.com
邮件标题:202X年云商店中国站联营伙伴自动化部署测试券申请-xx公司-xx商品
邮件正文:202X年云商店中国站联营伙伴自动化部署测试券申请-xx公司-xx商品,所需云资源清单见附件
邮件附件:自动化部署-xx公司-xx商品云资源清单
- 登录卖家中心> 测试券管理,提交“测试券申请”,填写规则如下:
填写测试券方案名称:202X年云商店中国站联营伙伴自动化部署-xx公司-xx商品
选择申请场景:联营商品上架
选择适用商品:选择需要对接的具体商品
填写计划使用期限:不限
- 自动化部署模版在线测试,参考自动部署模板在线测试