文档目录

概述

ZStack Cloud应用市场是基于IaC (Infrastructure as Code,基础设施即代码) 的开发平台,提供标准化IaC编排和简单易用的开发框架,具有强兼容性、可扩展性和混合云部署特点,帮助ISV合作伙伴、渠道和客户快速将复杂的企业级应用接入ZStack Cloud并一键部署使用,根据业务需求灵活扩展云平台能力,或将运维部署的最佳实践应用于生产。

本文档详细介绍应用制作、应用发布、应用部署的规范流程。

应用制作

背景信息

本章节介绍如何准备应用镜像和应用包,包括以下步骤:
  1. 获取应用包模板
  2. 配置应用文件
  3. 制作应用镜像

操作步骤

  1. 获取应用包模板
    1. 登录https://github.com/ZStack-Robot/marketplace-apps
    2. 在页面右上角点击Use this template > Create a new repository,将应用包模板克隆到自己的仓库。
      图 1所示:
      图 1. 克隆应用包模板


    应用包源目录结构如下:
    ├── applications
    │   ├── index.json
    │   ├── zstack_io_grafana
    │   │   └── aarch64
    │   │       └── 11.2.0
    │   │           ├── application.json
    │   │           ├── input.json
    │   │           ├── logo.png
    │   │           ├── output.json
    │   │           └── src
    │   │                 ├── main.tf
    │   │                 ├── output.tf
    │   │                 ├── scripts
    │   │                 │     └── check-health.sh
    │   │                 ├── variables.tf
    │   │                 └── version.tf
    │   │   └── x86_64
    │   │       └── 11.2.0
    │   │           ├── application.json
    │   │           ├── input.json
    │   │           ├── logo.png
    │   │           ├── output.json
    │   │           └── src
    │   │                 ├── main.tf
    │   │                 ├── output.tf
    │   │                 ├── scripts
    │   │                 │     └── check-health.sh
    │   │                 ├── variables.tf
    │   │                 └── version.tf
    目录结构说明
    • zstack_io_grafana:应用ID,请根据实际情况重命名,需和application.json中的appId值一致
    • aarch64/x86_64:应用架构,如应用只适配一种架构,可将其他架构路径删除
    • 11.2.0:应用版本号,请根据实际情况重命名
    • application.json:定义应用名称、应用描述、应用appId、应用类型、连接类型的文件
    • input.json:UI渲染文件,用于定义用户通过应用市场UI界面部署应用时需填写的表单参数
    • logo.png:应用的Logo图片
    • output.json:用于定义该应用在应用市场部署完成后,在UI界面上展示的详情字段
    • src: 存放内容为Terraform (OpenTofu) 编排的IaC文件
    • main.tf:OpenTofu执行文件入口,请参考Terraform语法编写,详见https://developer.hashicorp.com/terraform/language
    • output.tf:执行main后的结果输出,请参考Terraform语法编写,详见https://developer.hashicorp.com/terraform/language
    • script:存放应用执行脚本,脚本可以通过data导入数据,或remote-exec到运行该应用的云主机执行
    • variables.tf:OpenTofu格式的引入变量
    • version.tf:导入的Provider信息
  2. 配置应用文件
    1. 使用实际应用ID、应用架构、应用版本号重命名zstack_io_grafana、aarch64/x86_64、11.2.0文件夹
    2. 配置application.json文件
      模板
      {
        "name": "Grafana 是一款开源指标分析和可视化套件",
        "description": "Grafana 是一款开源指标分析和可视化套件,用于可视化支持各种数据源的时间序列数据,应用市场也提供对应ZStack监控模板",
        "details": "Grafana 是一款开源指标分析和可视化套件,用于可视化支持各种数据源的时间序列数据。 本应用由ZStack打包。产品中提及的相应商标归相应公司所有",
        "appId": "zstack.io.grafana",
        "category": "cloud",
        "connector_type": "zstack"
      }
      Note:
      • categoryconnector_type需使用模板中提供的默认值
      • 其他选项的值请根据实际情况修改
    3. 配置input.json文件
      模板
      [
        {
          "name": "root_disk_size",
          "required": true,
          "default_value": 107374182400,
          "min": 107374182400,
          "tag": "ZStack::rootDiskSize"
        },
        {
          "name": "memory_size",
          "required": true,
          "default_value": 4294967296,
          "min": 2147483648,
          "tag": "ZStack::memorySize"
        },
        {
          "name": "l3_network_uuids",
          "en_label": "l3_network_uuids",
          "zh_label": "网络",
          "required": true,
          "tag": "ZStack::l3NetworkUuids"
        },
        {
          "name": "cpu_num",
          "required": true,
          "default_value": 2,
          "min": 1,
          "tag": "ZStack::cpuNumber"
        },
        {
          "name": "backup_storage_uuid",
          "required": true,
          "tag": "ZStack::backupStorageUuid"
        }
      ]
      Note:
      • name:在应用市场UI界面部署应用时的表单参数
      • required:部署应用时,该参数是否必填
      • en_lable:UI界面显示的参数英文名称
      • zh_lable:UI界面显示的参数中文名称
      • default_value:参数默认值
      • min:参数最小值
      • tag:标记ZStack前缀,用于渲染UI表单。带有tag标记的字段才能展示在应用部署界面
      应用部署界面会按以下顺序展示input.json中规定的参数:
      字段 字段释义 是否必须包含在input.json中
      ZStack::cpuNumber CPU核数
      ZStack::memorySize 内存大小
      ZStack::imageStoreUuid 镜像服务器UUID 否。本参数将由系统自动生成和展示
      ZStack::rootDiskSize 根云盘大小
      ZStack::dataDiskSize 数据云盘大小 否。可以选填一个或多个,input.json中包含几个该字段,表示部署时可设置几个数据云盘
      ZStack::l3NetworkUuid 三层网络UUID 是。至少包含一个,也可以包含多个,input.json中包含几个该字段,表示部署时可设置几个三层网络
      ZStack::l3NetworkIp 指定网卡IP地址 否。如包含,必须先包含一个同组的ZStack::l3NetworkUuid。一个ZStack::l3NetworkUuid对应一个ZStack::l3NetworkIp
      ZStack::clusterUuid 集群UUID
      ZStack::hostUuid 物理机UUID
      ZStack::rootDiskPrimaryStorageUuid 根云盘主存储UUID
      ZStack::rootDiskPrimaryStorageCephPoolUuid 根云盘主存储存储池UUID 如根云盘主存储为Ceph类型,则必须包含,和ZStack::rootDiskPrimaryStorageUuid一起指定
      ZStack::dataDiskPrimaryStorageUuid 数据云盘主存储UUID 如已包含ZStack::dataDiskSize,则必须包含
      ZStack::dataDiskPrimaryStorageCephPoolUuid 数据云盘主存储存储池UUID 如已包含ZStack::dataDiskSize且数据云盘主存储为Ceph类型,则必须包含,和ZStack::dataDiskPrimaryStorageUuid一起指定
      ZStack::gpuDeviceUuid GPU设备UUID 否。如包含,则必须同时包含ZStack::gpuSpecsUuid和ZStack::gpuDeviceType
      ZStack::gpuSpecsUuid GPU规格UUID 否。如包含,则必须同时包含ZStack::gpuDeviceUuid和ZStack::gpuDeviceType
      ZStack::gpuDeviceType GPU设备类型 否。如包含,则必须同时包含ZStack::gpuDeviceUuid和ZStack::gpuSpecsUuid
    4. 配置output.json文件
      模板
      [
          {
              "name": "vm_uuids",
              "en_label": "vm_uuids",
              "zh_label": "vm_uuids",
              "type":"array",
              "tag":"ZStack:vmInstanceUuid"
          },
          {
              "name": "application_protocol",
              "en_label": "Application Protocol",
              "zh_label": "应用协议",
              "type": "string",
              "tag": "Application:protocol"
          },
          {
              "name": "application_ip",
              "en_label": "Application IP",
              "zh_label": "应用IP",
              "type":"string",
              "tag":"Application:ip"
          },
          {
              "name": "application_port",
              "en_label": "Application Port",
              "zh_label": "应用端口",
              "type":"number",
              "tag":"Application:port"
          },
          {
              "name": "default_account",
              "en_label": "Default User Name",
              "zh_label": "默认账号",
              "type":"string"
          },
          {
              "name": "default_password",
              "en_label": "Default Password",
              "zh_label": "默认密码",
              "type":"password"
          }
      ]
      Note:
      • name:应用部署完成后,UI界面展示的详情字段
      • en_lable:UI界面显示的字段英文名称
      • zh_lable:UI界面显示的字段中文名称
    5. 配置src-main.tf文件
      模板
      resource "zstack_vm" "vm" {
        name = "Grafana"
        description = "应用市场-Grafana-可视化"
        root_disk = {
          size = {{.root_disk_size}}
        }
      
        l3_network_uuids = {{.l3_network_uuids}}
        memory_size = {{.memory_size}}
        cpu_num = {{.cpu_num}}
        marketplace = true
        never_stop = true
      }
      
      variable "l3Uuids" {
        type = list(string)
        default = {{.l3_network_uuids}}
      }
      
      data "zstack_l3network" "network" {
          depends_on = [zstack_vm.vm]
          uuid = var.l3Uuids[0]
      }
      
      resource "terraform_data" "healthy_check" {
        depends_on = [zstack_vm.vm]
      
        provisioner "local-exec" {
           command     = var.wait_for_migrate_health_cmd
           environment = {
             ENDPOINT =  "http://${zstack_vm.vm.ip}:3000/"
           }
         }
      }
      Note: {{}}中的变量值由input.json文件中对应字段的值传递,即用户通过UI界面部署应用时填写的表单参数值。
    6. 配置src-output.tf文件
      模板
      output "vm_uuids" {
         value = zstack_vm.vm.uuid
      }
      
      output "application_protocol" {
         value = "http"
      }
      
      output "application_ip" {
         value = zstack_vm.vm.ip
      }
      
      output "application_port" {
         value = 3000
      }
      
      output "default_account" {
         value = "admin"
      }
      
      output "default_password" {
         value = "password"
      }
      
      output "default_host_root_password" {
         value = "password"
      }
    7. 配置src-variables.tf文件
      模板
      variable "wait_for_migrate_health_cmd" {
         description = "local-exec command to execute for determining if the Grafana url is healthy. Grafana endpoint will be available as an environment variable called ENDPOINT"
         type        = string
         default     = "start=$(date +%s); until curl -k -s $ENDPOINT >/dev/null; do sleep 4; now=$(date +%s); if [ $((now - start)) -ge 600 ]; then echo 'Timeout reached'; exit 1; fi; done"
       } 
    8. 配置src-version.tf文件
      模板
      terraform {
        required_providers {
          zstack = {
            source = "zstack.io/terraform-provider-zstack/zstack"
          }
        }
      }
      Note:
      • 如用户环境连接公网,可使用Terraform提供的Provider,详见https://registry.terraform.io/browse/providers
      • 如用户环境未连接公网,仅可使用以下Provider,详细使用方法可参考:https://registry.terraform.io/browse/providers
         template = {
              source  = "hashicorp/template"
              version = "2.2.0"
            }
            external = {
              source = "hashicorp/external"
              version = "2.3.3"
            }
            grafana = {
              source = "grafana/grafana"
              version = "3.2.1"
            }
            local = {
              source = "hashicorp/local"
              version = "2.5.1"
            }
            null = {
              source = "hashicorp/null"
              version = "3.2.3"
            }
  3. 制作应用镜像
    应用镜像需基于KVM qcow2格式的云主机镜像,可以通过Packer Qemu插件代码化制作镜像。如云主机镜像包含QGA插件,可以使用User Data参数传入,User Data内容需经过Base64编码。
    以下示例为格式化数据云盘的User Data,用户可按实际情况编写自己的User Data:
    编码前
    #cloud-config
    runcmd:
      - |
        pv_list=$(pvs --noheadings -o pv_name)
        pv_count=$(echo "$pv_list" | wc -l)
        pv_name=$(echo "$pv_list" | tr -d ' ')
    
        vg_name=$(pvs --noheadings -o vg_name $pv_name | tr -d ' ')
    
        growpart $(echo $pv_name | sed 's/[0-9]*$//') $(echo $pv_name | grep -o '[0-9]*$')
        pvresize $pv_name
    
        lv_name=$(lvs --noheadings -o lv_name --sort -size | tail -1 | tr -d ' ')
    
        lvextend -l +100%FREE /dev/$vg_name/$lv_name
    
        lv_path="/dev/$vg_name/$lv_name"
        mapper_name=$(readlink -f $lv_path | awk -F '/' '{print $3}')
    
        lv_mapper_name=$(ls -l /dev/mapper/ | grep "$mapper_name" | awk '{print $9}')
    
        blkid | grep "/dev/mapper/$lv_mapper_name" | grep -q xfs
    
        if [ $? -eq 0 ]; then
            xfs_growfs /dev/mapper/$lv_mapper_name
        else
            resize2fs /dev/mapper/$lv_mapper_name
        fi
        partprobe
    编码传入
     user_data = "I2Nsb3VkLWNvbmZpZwpydW5jbWQ6CiAgLSB8CiAgICBwdl9saXN0PSQocHZzIC0tbm9oZWFkaW5ncyAtbyBwdl9u \
    YW1lKQogICAgcHZfY291bnQ9JChlY2hvICIkcHZfbGlzdCIgfCB3YyAtbCkKICAgIHB2X25hbWU9JChlY2hvICIkcHZfbGlzdCIgfCB0ciAtZCAnI \
    CcpCgogICAgdmdfbmFtZT0kKHB2cyAtLW5vaGVhZGluZ3MgLW8gdmdfbmFtZSAkcHZfbmFtZSB8IHRyIC1kICcgJykKCiAgICBncm93cGFydCAkKG \
    VjaG8gJHB2X25hbWUgfCBzZWQgJ3MvWzAtOV0qJC8vJykgJChlY2hvICRwdl9uYW1lIHwgZ3JlcCAtbyAnWzAtOV0qJCcpCiAgICBwdnJlc2l6ZSA \
    kcHZfbmFtZQoKICAgIGx2X25hbWU9JChsdnMgLS1ub2hlYWRpbmdzIC1vIGx2X25hbWUgLS1zb3J0IC1zaXplIHwgdGFpbCAtMSB8IHRyIC1kICcg \
    JykKCiAgICBsdmV4dGVuZCAtbCArMTAwJUZSRUUgL2Rldi8kdmdfbmFtZS8kbHZfbmFtZQoKICAgIGx2X3BhdGg9Ii9kZXYvJHZnX25hbWUvJGx2X \
    25hbWUiCiAgICBtYXBwZXJfbmFtZT0kKHJlYWRsaW5rIC1mICRsdl9wYXRoIHwgYXdrIC1GICcvJyAne3ByaW50ICQzfScpCgogICAgbHZfbWFwcG \
    VyX25hbWU9JChscyAtbCAvZGV2L21hcHBlci8gfCBncmVwICIkbWFwcGVyX25hbWUiIHwgYXdrICd7cHJpbnQgJDl9JykKCiAgICBibGtpZCB8IGd \
    yZXAgIi9kZXYvbWFwcGVyLyRsdl9tYXBwZXJfbmFtZSIgfCBncmVwIC1xIHhmcwoKICAgIGlmIFsgJD8gLWVxIDAgXTsgdGhlbgogICAgICAgIHhm \
    c19ncm93ZnMgL2Rldi9tYXBwZXIvJGx2X21hcHBlcl9uYW1lCiAgICBlbHNlCiAgICAgICAgcmVzaXplMmZzIC9kZXYvbWFwcGVyLyRsdl9tYXBwZ \
    XJfbmFtZQogICAgZmkKICAgIHBhcnRwcm9iZQ=="

应用发布

本章节介绍如何将准备好的应用包和镜像发布到应用市场,包括以下三种发布方式:
  • 单独发布:将应用包和镜像上传至本地云环境,仅供自己使用。
  • 指定共享发布:将应用包和镜像上传至自己的CDN,允许自己和伙伴用户使用。
  • 共享发布:将应用包和镜像上传至ZStack Cloud官方CDN,允许所有用户使用。

单独发布

  1. 执行python package_repo.py 脚本,生成images目录。
  2. 将应用镜像存放在images目录下,并重命名为image.qcow2
  3. 执行以下命令,生成应用bin包:
    python package_bin --${AppId} --${Version} --${Arch} --include_images
  4. 在以下路径查看生成的bin包:target/application_bins/${AppID}/${Arch}/${Version}/${PackageName}.bin
  5. 将bin包上传至ZStack Cloud管理节点,并执行以下命令:
    [root@localhost~] bash ${PackageName}.bin
    Note: 如使用双管理节点,需在两个管理节点分别上传bin包并执行该命令
  6. ZStack Cloud主菜单,点击运营管理 > 应用市场 > 默认应用,在默认应用界面,点击同步应用,即可看到发布的应用。

指定共享发布

  1. 执行python package_repo.py 脚本,生成application.tar.gz和新的index.json
  2. 在以下路径查看application.tar.gztarget/application_bins/${app_id}/${arch}/${version}/xxx.bin
  3. 将应用镜像重命名为image.qcow2
  4. application.tar.gzimage.qcow2按以下目录结构上传到自己的CDN:
    ├── index.json
    ├── ${AppId}
    │   └── ${Arch}
    │       └── ${Version}
    │           └── application.tar.gz
    │           └── image.qcow2    
  5. 登录ZStack Cloud管理节点,执行以下命令,将CDN链接嵌入repositories中:
    [root@localhost~]zstack-cli    #进入CLI命令行
    - >>>LogInByAccount accountName=admin password=${AdminPassword}    #登录admin账号
    admin >>>UpdateGlobalConfig name=repositories category=marketplace value="file:///opt/zstack-marketplace-repo;${CustomCDNAddress}"    #将CDN链接嵌入repositories
    admin >>>exit    #退出CLI命令行
  6. 执行以下命令,重启管理节点:
    [root@localhost~] zstack-ctl restart_node
  7. 检查marketplace-server/config/config.yaml文件中的repositories是否如下:
    repositories:
    - file:///opt/zstack-marketplace-repo
    - ${CustomCDNAddress}
  8. ZStack Cloud主菜单,点击运营管理 > 应用市场 > 默认应用,在默认应用界面,点击同步应用,即可看到发布的应用。

共享发布

  1. 执行python package_repo.py 脚本,生成application.tar.gz和新的index.json
  2. 在以下路径查看application.tar.gztarget/application_bins/${app_id}/${arch}/${version}/xxx.bin
  3. 将应用镜像重命名为image.qcow2
  4. application.tar.gzimage.qcow2交付给ZStack Cloud官方技术支持,由技术支持将应用包和镜像上传到ZStack官方CDN。
  5. ZStack Cloud主菜单,点击运营管理 > 应用市场 > 默认应用,在默认应用界面,点击同步应用,即可看到发布的应用。
图 2所示:
图 2. 同步应用


应用部署

背景信息

本章节介绍如何部署已发布到应用市场的应用,主要包括以下步骤:
  1. 上传应用镜像(可选)
  2. 安装应用
  3. 访问应用
  4. 调试应用(可选)

操作步骤

  1. 上传应用镜像(可选)
    安装应用时,系统会自动从CDN下载应用镜像,镜像下载需要一定时间。如需加快应用安装速度或当前环境未连接公网,请提前获取应用镜像,并上传到指定位置:
    • 下载地址:
      • ZStack Cloud CDN:点击此处
      • 未发布到ZStack Cloud CDN的应用:请联系开发方获取
    • 上传方法:
      • 将镜像命名为image.qcow2,并分别存放在管理节点和镜像仓库的以下路径 (如没有则新建):/opt/zstack-marketplace-repo/{appName}/{Architecture}/{version},例如:/opt/zstack-marketplace-repo/zstack_io_grafana/x86_64/11.2.0/image.qcow2
      • 为确保上传路径及文件名正确,建议联系官方技术支持执行本步骤。
  2. 安装应用

    ZStack Cloud主菜单,点击运营管理 > 应用市场 > 默认应用,选择需要安装的应用,点击安装应用,弹出安装应用界面。

    安装应用界面,填写表单参数,并确认安装。

    图 3所示:
    图 3. 安装应用


  3. 访问应用

    ZStack Cloud主菜单,点击运营管理 > 应用市场 > 默认应用 > 已安装,选择需要访问的应用,点击访问地址后的链接,即可跳转访问。

    图 4所示:
    图 4. 访问应用


  4. 调试应用(可选)
    如需进行应用调试,可参考以下信息:
    • 查看应用UUID:在ZStack Cloud主菜单右上角点击任务,点击展开安装该应用的任务详情,查看application_uuid字段。
      图 5所示:
      图 5. 应用UUID


    • 查看应用日志:在管理节点查看/var/lib/zstack/marketplace-server/server.log
    • 查看应用目录:在管理节点进入/var/lib/zstack/marketplace-server/apps/${application_uuid}/instance/${instance_uuid},可以查看对应的terraform.tfstate状态文件,如需手工调试请执行以下命令:
      ```bash
      export TF_CLI_CONFIG_FILE=/var/lib/zstack/marketplace-server/.terraformrc && /var/lib/zstack/marketplace-server/tools/tofu apply -var "host=cloud api endpoint" -var "port=8080" -var "session_id=xxxxx"
      ​```
应用市场开发使用教程 | 5.4.12 | ZStack Cloud · ZCF | ZStack 资源中心