记一次饱经挫折的阿里云ROS部署经历

前言

最近在参加的几个项目测评里,我发现**“一键部署”这功能真心好用,省下了不少宝贵时间和力气,再加上看到阿里云现在有个开源上云**的活动。趁着这波热潮,今天就聊聊怎么从头开始,一步步搞定阿里云的资源编排服务(ROS),希望能对刚接触这块的朋友有点帮助。咱们一块儿边学边实践,让云上的项目部署变得更简单高效。

资源编排

阿里云的资源编排服务(Resource Orchestration Service,简称ROS)是一种基础设施即代码(IaC)服务,它允许用户通过编写模板来定义和管理云上的各种资源。这些资源包括但不限于ECS( Elastic Compute Service)实例、RDS(Relational Database Service)数据库实例、SLB(Server Load Balancer)负载均衡器等。ROS的核心价值在于自动化部署及运维,它能自动处理资源的创建、配置及之间的依赖关系,从而极大地简化了云计算资源的管理过程。

在ROS中,用户根据ROS提供的模板规范,使用JSON或YAML格式编写资源栈模板。模板中不仅定义了所需的资源类型和数量,还明确了资源之间的逻辑依赖,确保资源按正确的顺序被创建和配置。这样,无论是新建环境、更新配置还是进行大规模的架构调整,都可通过执行模板脚本来实现,保证了操作的一致性和可重复性。

资源编排地址如下:https://ros.console.aliyun.com/cn-hangzhou/welcome

image.png

下面我们还是以实战为导向,从最基础的ROS脚本开始看起,一步步拆解。

ROS模板

首先我们先看一下ROS模板结构及参数说明。

{
  "ROSTemplateFormatVersion" : "2015-09-01",

  "Description" : "模板描述信息,可用于说明模板的适用场景、架构说明等。",
  "Metadata" : {
    // 关于模板的元数据信息,例如存放用于可视化的布局信息。
  },
  "Parameters" : {
    // 定义创建资源栈时,用户可以定制化的参数。
  },

  "Mappings" : {
    // 定义映射信息表,映射信息是一种多层的Map结构。
  },

  "Conditions": {
    // 使用内部条件函数定义条件。这些条件确定何时创建关联的资源。
  },

  "Resources" : {
    // 所需资源的详细定义,包括资源间的依赖关系、配置细节等。
  },

  "Outputs" : {
    // 用于输出一些资源属性等有用信息。可以通过API或控制台获取输出的内容。
  }
}

【参数说明】:

  • ROSTemplateFormatVersion必选,ROS支持的模板版本号,当前版本号:2015-09-01。

  • Description:可选,模板的描述信息。可用于说明模板的适用场景、架构说明等。通常情况下,对模板进行详细描述,有利于用户理解模板的内容。

  • Metadata:可选,模板编写者可以使用Metadata来存放与模板相关的元数据信息,内容可以为JSON格式。

  • Parameters:可选,定义创建资源栈时,模板用户可以定制化的参数。通常,模板的编辑者会把ECS的规格设计成一个参数。参数支持默认值。使用参数可以增强模板的灵活性,提高复用性。使用模板创建资源栈时,可以根据实际的评估结果来选择合适的规格。更多详细信息,请参见参数(Parameters)。

  • Mappings:可选,Mappings定义了一个多层的映射表,可以通过Fn::FindInMap函数来选择Key对应的值,或根据不同的输入参数值作为Key来查找映射表。例如,您可以根据Region不同,自动查找Region-镜像映射表,从而找到适用的镜像。更多详细信息,请参见映射(Mappings)。

  • Conditions:可选,Conditions使用Fn::And、Fn::Or、Fn::Not、Fn::Equals定义条件。多个条件之间使用半角逗号(,)隔开。在创建或更新资源栈时,系统先计算模板中的所有条件,然后再创建资源。创建与true条件关联的所有资源,忽略与false条件关联的所有资源。更多详细信息,请参见条件(Conditions)。

  • Resources:可选,用于详细定义使用该模板创建的资源栈所包含的资源,包括资源间的依赖关系、配置细节等。更多详细信息,请参见资源(Resources)。

  • Outputs:可选,用于输出一些资源属性等有用信息。可以通过API或控制台获取输出的内容。更多详细信息,请参见输出(Outputs)。

通过上面可以看出最核心、最关键的部分其实是ParametersResources , 因为这两个决定了模板设计者定义一系列可配置的参数和将要创建或管理的所有云资源及其配置,是直接涉及资源创建和配置的核心逻辑参数。

下面我们来创建第一个案例。

基于ROS模板创建VPC

在创建之前,我们需要先知道ROS支持的所有资源类型,这里需要查看资源类型索引

image.png

如果在这个列表中没有出现的产品,就是暂时不支持采用ROS来创建的,这是需要明确的第一点。

如上图,我们看到VPC是支持使用ROS来创建的,接下来我们再开展第二步,点击这个蓝色链接位置,查看它的详细语法、属性及返回值。

image.png

到这里,我们基本就明白它的语法格式及书写方式了,下面第三步,上手开写。

回到资源编排界面,点击【我的模板】→【创建模板】,进去ROS在线编辑界面开始编辑。

image.png

初始的模板如下,我们选择为yaml格式来进行编写。

image.png

下面咱们一行行的分析:

ROSTemplateFormatVersion

默认版本号,这里不用修改,直接过

ROSTemplateFormatVersion: '2015-09-01'

Description

这里不是必填,不过也可以填一下,暂且起名为my first VPC-test

Description: my first VPC-test

Parameters

这里到重头戏了,几乎所有的可定制化参数都是在这里修改的,在比较复杂的ROS脚本中几乎都会用到,但是这里为了方便演示,也选择暂时不填写,等下后面测试时再填写后对比。

Parameters: {}

Resources

这里是用来详细定义使用该模板创建的资源栈所包含的资源,包括资源间的依赖关系、配置细节等,是必填项

而其具体的格式,可以看到如下:

image.png

这里我们沿用该格式,并且剔除一些不需要的参数,最终命令如下:

 VPC:
    Type: ALIYUN::ECS::VPC
    Properties:
      VpcName: VPC-test
      CidrBlock: 192.168.0.0/24
      

这里面就只包含了最基础的CidrBlock(专有网络网段)和VpcName(专有网络名称)。

Mappings、Metadata、Conditions

这里也暂时不涉及,可以直接删除掉。

Outputs

这里用于输出一些资源属性等有用信息,这里我们可以尝试输出一下VPC的ID和关联的虚拟路由器ID

Outputs:
  VPCId:
    Value:
       Fn::GetAtt:
        - VPC
        - VRouterId
  VRouterId:
    Value:
      Fn::GetAtt:
        - VPC
        - VRouterId

参照返回示例如下

image.png

最终ROS的整体脚本如下:

ROSTemplateFormatVersion: '2015-09-01'
Description: my first VPC-test
Resources:
  VPC:
    Type: ALIYUN::ECS::VPC
    Properties:
      VpcName: VPC-test
      CidrBlock: 192.168.0.0/24
Outputs:
  VPCId:
    Value:
       Fn::GetAtt:
        - VPC
        - VRouterId
  VRouterId:
    Value:
      Fn::GetAtt:
        - VPC
        - VRouterId

点击预览,查看资源架构,我们可以看到如图所示的架构:

image.png

确定之后点击保存模板,命名后可在模板空间中查看和直接创建栈:

image.png
image.png

选择失败时回滚,点击创建:

image.png

此时可以看到开始创建资源栈,等待一段时间:

image.png

创建成功后如下图所示:

image.png

点击输出,我们可以看到outputs中设定的输出的关键字

image.png

而假如我们不需要使用了,也可以直接点击删除资源栈:

image.png

以上是手动编写脚本的流程了,在实际调试中还是有一些复杂的,下面我们就来结合案例,完成这次参赛的ROS部署作品。

基于ROS部署MaxKB

MaxKB

MaxKB是基于LLM大语言模型的知识库问答系统,旨在成为企业的最强大脑。它支持开箱即用,无缝嵌入到第三方业务系统,并提供多模型支持,包括主流大模型和本地私有大模型,为用户提供智能问答交互体验和灵活性。

image.png

另外说一句,我的关于知识库侧测评的文章也已经快写完了,这几个月参加护网有点太累了,几乎没时间来写,抱歉抱歉!

编写ROS 模板

这里我们还是一步步的来,逐步解析。

ROSTemplateFormatVersion

老规矩,不变。

ROSTemplateFormatVersion: '2015-09-01'

Description

因为是参赛的作品,所以我这里也写的详细一点吧,下面也是带上了中英的描述。

Description:
  en: Setting up the large language model-based knowledge base question and answer system, MaxKB, on an ECS (Elastic Compute Service) instance.
  zh-cn:ECS实例上搭建大语言模型的知识库问答系统MaxKB。

Parameters

这里涉及到的就比较复杂了,因为此处是必须要进行一些自定义参数设置的,我们进到ECS的语法下面详细查看

说实话,有点吓人,居然这么多!

image.png

我们按照下面这个表格来一一排查:

image.png

ZoneId

这里首先申明ZoneId , 至于为什么先申明ZoneId呢?这是因为区域(Zone)的选择对创建的云资源(如ECS实例、RDS数据库等)有着直接的影响,且下面在申明实例规格时会用到。

ZoneId:
    Type: String
    Label:
      en: Availability Zone
      zh-cn: 可用区ID
    AssociationProperty: ALIYUN::ECS::Instance:ZoneId

AssociationProperty 说明ZoneId参数与ECS实例的ZoneId属性关联。这意味着在模板中使用此参数的地方,我们所选择的值将会直接应用于创建ECS实例时的可用区选择。这样的设计确保了模板的逻辑与阿里云ECS服务的实际属性紧密集成,提高了模板的准确性和易用性。

InstanceType

ECS实例规格 ,必填项 ,这里需要参考实例规格族。

 InstanceType:
    Type: String
    Label:
      en: Instance Type
      zh-cn: 实例类型
    AssociationProperty: ALIYUN::ECS::Instance::InstanceType
    AssociationPropertyMetadata:
      ZoneId: ${ZoneId}
      DefaultValueStrategy: recent

同样,这里的 AssociationProperty 是指明参数与ECS实例的InstanceType属性关联;而 AssociationPropertyMetadata 则是表示实例类型的可用性依赖于ZoneId中选择的区域ID,且当用户没有明确指定实例类型时,系统会选择最近常用或推荐的实例类型作为默认选项。

SystemDiskCategory

下面定义ECS实例系统盘的类型,其中,AssociationPropertyMetadata 里面包含了一些元数据来指导和约束系统盘类型的选择:

SystemDiskCategory:
    Type: String
    Label:
      en: System Disk Type
      zh-cn: 系统盘类型
    AssociationProperty: ALIYUN::ECS::Disk::SystemDiskCategory
    AssociationPropertyMetadata:
      LocaleKey: DiskCategory
      ZoneId: ${ZoneId}
      InstanceType: ${InstanceType}
      AutoSelectFirst: true
      AutoChangeType: false
    Default: cloud_essd
InstancePassword

下面定义了用于登录ECS实例的密码规则,确保了安全性的同时也提供了一定的灵活性:

InstancePassword:
    Type: String
    Label:
      en: Instance Password
      zh-cn: 实例密码
    Description:
      en: Server login password, Length 8-30, must contain three(Capital letters,
        lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol
        in).
      zh-cn: 服务器登录密码,长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)。
    ConstraintDescription:
      en: Length 8-30, must contain three(Capital letters, lowercase letters, numbers,
        ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol in).
      zh-cn: 长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)。
    AssociationPropertyMetadata:
      Visible:
        Condition:
          Fn::Equals:
          - ${SelectInstance}
          - false
    Default:
    AllowedPattern: '[0-9A-Za-z\_\-\&:;''<>,=%`~!@#\(\)\$\^\*\+\|\{\}\[\]\.\?\/]+$'
    MinLength: 8
    MaxLength: 30
    NoEcho: true

所以Parameters此处的完整脚本如下:

Parameters:
  ZoneId:
    Type: String
    Label:
      en: Availability Zone
      zh-cn: 可用区ID
    AssociationProperty: ALIYUN::ECS::Instance:ZoneId
  InstanceType:
    Type: String
    Label:
      en: Instance Type
      zh-cn: 实例类型
    AssociationProperty: ALIYUN::ECS::Instance::InstanceType
    AssociationPropertyMetadata:
      ZoneId: ${ZoneId}
      DefaultValueStrategy: recent
  SystemDiskCategory:
    Type: String
    Label:
      en: System Disk Type
      zh-cn: 系统盘类型
    AssociationProperty: ALIYUN::ECS::Disk::SystemDiskCategory
    AssociationPropertyMetadata:
      LocaleKey: DiskCategory
      ZoneId: ${ZoneId}
      InstanceType: ${InstanceType}
      AutoSelectFirst: true
      AutoChangeType: false
    Default: cloud_essd
  InstancePassword:
    Type: String
    Label:
      en: Instance Password
      zh-cn: 实例密码
    Description:
      en: Server login password, Length 8-30, must contain three(Capital letters,
        lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol
        in).
      zh-cn: 服务器登录密码,长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)。
    ConstraintDescription:
      en: Length 8-30, must contain three(Capital letters, lowercase letters, numbers,
        ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol in).
      zh-cn: 长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)。
    AssociationPropertyMetadata:
      Visible:
        Condition:
          Fn::Equals:
          - ${SelectInstance}
          - false
    Default:
    AllowedPattern: '[0-9A-Za-z\_\-\&:;''<>,=%`~!@#\(\)\$\^\*\+\|\{\}\[\]\.\?\/]+$'
    MinLength: 8
    MaxLength: 30
    NoEcho: true

Resources

这里开始创建资源,分析架构所需,涉及产品如下:

VPC

定义一个虚拟私有云(VPC)资源,192.168.0.0/16 指定了VPC的IP地址范围。

Vpc:
    Type: ALIYUN::ECS::VPC
    Properties:
      CidrBlock: 192.168.0.0/16
VSwitch

定义一个虚拟交换机(VSwitch)的配置:Ref: ZoneId 表示虚拟交换机将被创建在指定的可用区(Zone)中,这里的ZoneId是一个引用,其实际值需要在模板的参数或映射部分提前定义或通过外部参数传入;Ref: Vpc 指明该虚拟交换机隶属于哪个VPC。这里同样使用了引用,意味着VSwitch将与之前定义的VPC关联。确保在模板中已经定义了VPC资源或者通过参数传递了正确的VPC ID;192.168.0.0/24 定义了虚拟交换机的IP地址范围。

 VSwitch:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      ZoneId:
        Ref: ZoneId
      VpcId:
        Ref: Vpc
      CidrBlock: 192.168.0.0/24

SecurityGroup

创建安全组:Ref: Vpc 指定该安全组将被创建在哪个VPC中,这里通过引用之前定义的VPC资源(Vpc),确保安全组与目标VPC正确关联。这一步是必要的,因为每个安全组都必须绑定到一个特定的VPC。

SecurityGroup:
    Type: ALIYUN::ECS::SecurityGroup
    Properties:
      VpcId:
        Ref: Vpc
SecurityGroupIngress_80

定义了入站规则:Ref: SecurityGroup 引用了之前定义的安全组ID,这条入站规则将被添加到该安全组中;0.0.0.0/0 指定任何IP地址(全网段)都可以作为流量的来源,这意味着对于这条规则,不限制访问的来源IP地址;**tcp **指定了该规则适用的协议类型,这里是TCP;intranet 表示这条规则仅适用于VPC内部的通信;80/80 定义了允许访问的端口范围,这里只开放了TCP 80端口,通常用于HTTP服务。

SecurityGroupIngress_80:
    Type: ALIYUN::ECS::SecurityGroupIngress
    Properties:
      SecurityGroupId:
        Ref: SecurityGroup
      SourceCidrIp: 0.0.0.0/0
      IpProtocol: tcp
      NicType: intranet
      PortRange: 80/80
InstanceGroup

定义实例群组:引用上面创建的Vpc、VSwitch和SecurityGroupId实例;指定使用CentOS 7.9的镜像;设置了实例的名称为MaxKB;后面继续引入实例类型、系统盘的类别和密码

InstanceGroup:
    Type: ALIYUN::ECS::InstanceGroup
    Properties:
      VpcId:
        Ref: Vpc
      VSwitchId:
        Ref: VSwitch
      SecurityGroupId:
        Ref: SecurityGroup
      ImageId: centos_7_9
      InstanceName: MaxKB
      InstanceType:
        Ref: InstanceType
      SystemDiskCategory:
        Ref: SystemDiskCategory
      Password:
        Ref: InstancePassword
      IoOptimized: optimized
      MaxAmount: 1
DS_Instances

通过DS_Instances数据源来获取通过InstanceGroup创建的ECS实例列表。

 DS_Instances:
    Type: DATASOURCE::ECS::Instances
    Properties:
      InstanceIds:
        Fn::GetAtt:
        - InstanceGroup
        - InstanceIds
InstallMaxKB

这里使用到Formation模板中的ALIYUN::ECS::RunCommand资源类型配置,用于在特定的ECS实例上执行一系列Shell脚本命令。

InstallMaxKB:
Type: ALIYUN::ECS::RunCommand
    Properties:
      InstanceIds:
        Fn::GetAtt:
        - InstanceGroup
        - InstanceIds
      Type: RunShellScript
      Sync: true
      Timeout: 1800
      CommandContent: |
          #!/bin/bash
        echo "#########################"
        echo "# Install Docker"
        echo "#########################"
        wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
        yum -y install docker-ce
        systemctl start docker
        systemctl enable docker

        echo "#########################"
        echo "# Install MaxKB"
        echo "#########################"

        yum -y install jq
        DOCKER_DAEMON_JSON="/etc/docker/daemon.json"
        sudo jq --arg registry "$NEW_REGISTRY" '. + { "registry-mirrors": [https://0jt38sli.mirror.aliyuncs.com] }' $DOCKER_DAEMON_JSON > ${DOCKER_DAEMON_JSON}.temp && sudo mv ${DOCKER_DAEMON_JSON}.temp $DOCKER_DAEMON_JSON
        sudo systemctl daemon-reload
        sudo systemctl restart docker
        docker run -d --name=maxkb -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data 1panel/maxkb

最终,Resources下脚本如下:

Resources:
  Vpc:
    Type: ALIYUN::ECS::VPC
    Properties:
      CidrBlock: 192.168.0.0/16
  VSwitch:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      ZoneId:
        Ref: ZoneId
      VpcId:
        Ref: Vpc
      CidrBlock: 192.168.0.0/24
  SecurityGroup:
    Type: ALIYUN::ECS::SecurityGroup
    Properties:
      VpcId:
        Ref: Vpc
  SecurityGroupIngress_80:
    Type: ALIYUN::ECS::SecurityGroupIngress
    Properties:
      SecurityGroupId:
        Ref: SecurityGroup
      SourceCidrIp: 0.0.0.0/0
      IpProtocol: tcp
      NicType: intranet
      PortRange: 80/80
  InstanceGroup:
    Type: ALIYUN::ECS::InstanceGroup
    Properties:
      VpcId:
        Ref: Vpc
      VSwitchId:
        Ref: VSwitch
      SecurityGroupId:
        Ref: SecurityGroup
      ImageId: centos_7_9
      InstanceName: dify
      InstanceType:
        Ref: InstanceType
      SystemDiskCategory:
        Ref: SystemDiskCategory
      Password:
        Ref: InstancePassword
      IoOptimized: optimized
      MaxAmount: 1
  DS_Instances:
    Type: DATASOURCE::ECS::Instances
    Properties:
      InstanceIds:
        Fn::GetAtt:
        - InstanceGroup
        - InstanceIds
InstallMaxKB:
Type: ALIYUN::ECS::RunCommand
    Properties:
      InstanceIds:
        Fn::GetAtt:
        - InstanceGroup
        - InstanceIds
      Type: RunShellScript
      Sync: true
      Timeout: 1800
      CommandContent: |
       #!/bin/bash
        echo "#########################"
        echo "# Install Docker"
        echo "#########################"
        wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
        yum -y install docker-ce
        systemctl start docker
        systemctl enable docker

        echo "#########################"
        echo "# Install MaxKB"
        echo "#########################"

        yum -y install jq
        DOCKER_DAEMON_JSON="/etc/docker/daemon.json"
        sudo jq --arg registry "$NEW_REGISTRY" '. + { "registry-mirrors": [https://0jt38sli.mirror.aliyuncs.com] }' $DOCKER_DAEMON_JSON > ${DOCKER_DAEMON_JSON}.temp && sudo mv ${DOCKER_DAEMON_JSON}.temp $DOCKER_DAEMON_JSON
        sudo systemctl daemon-reload
        sudo systemctl restart docker
        docker run -d --name=maxkb -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data 1panel/maxkb

Outputs

定义了名为 MaxKBUrl 的输出项,用于提供MaxKB服务的默认访问地址:

Outputs:
  MaxKBUrl:
    Description: MaxKB default address.
    Value:
      Fn::Sub:
      - http://${IP}
      - IP:
          Fn::Jq:
          - First
          - if .[0].PublicIpAddress != [] then .[0].PublicIpAddress[0] else .[0].EipAddress.IpAddress
            end
          - Fn::GetAtt:
            - DS_Instances
            - Instances

Metadata

添加了一个标签来描述此模板的功能和背景,按照比赛要求是需要采用“手机尾号-模板用途”来编写。

Metadata:
  ALIYUN::ROS::Interface:
    TemplateTags:
    - acs:developer:gitee:0147-ECS实例上搭建大语言模型的知识库问答系统MaxKB。

整体脚本

ROSTemplateFormatVersion: '2015-09-01'
Description:
  en: Setting up the large language model-based knowledge base question and answer system, MaxKB, on an ECS (Elastic Compute Service) instance.
  zh-cn:ECS实例上搭建大语言模型的知识库问答系统MaxKB。
Parameters:
  ZoneId:
    Type: String
    Label:
      en: Availability Zone
      zh-cn: 可用区ID
    AssociationProperty: ALIYUN::ECS::Instance:ZoneId
  InstanceType:
    Type: String
    Label:
      en: Instance Type
      zh-cn: 实例类型
    AssociationProperty: ALIYUN::ECS::Instance::InstanceType
    AssociationPropertyMetadata:
      ZoneId: ${ZoneId}
      DefaultValueStrategy: recent
  SystemDiskCategory:
    Type: String
    Label:
      en: System Disk Type
      zh-cn: 系统盘类型
    AssociationProperty: ALIYUN::ECS::Disk::SystemDiskCategory
    AssociationPropertyMetadata:
      LocaleKey: DiskCategory
      ZoneId: ${ZoneId}
      InstanceType: ${InstanceType}
      AutoSelectFirst: true
      AutoChangeType: false
    Default: cloud_essd
  InstancePassword:
    Type: String
    Label:
      en: Instance Password
      zh-cn: 实例密码
    Description:
      en: Server login password, Length 8-30, must contain three(Capital letters,
        lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol
        in).
      zh-cn: 服务器登录密码,长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)。
    ConstraintDescription:
      en: Length 8-30, must contain three(Capital letters, lowercase letters, numbers,
        ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol in).
      zh-cn: 长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)。
    AssociationPropertyMetadata:
      Visible:
        Condition:
          Fn::Equals:
          - ${SelectInstance}
          - false
    Default:
    AllowedPattern: '[0-9A-Za-z\_\-\&:;''<>,=%`~!@#\(\)\$\^\*\+\|\{\}\[\]\.\?\/]+$'
    MinLength: 8
    MaxLength: 30
    NoEcho: true
Resources:
  Vpc:
    Type: ALIYUN::ECS::VPC
    Properties:
      CidrBlock: 192.168.0.0/16
  VSwitch:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      ZoneId:
        Ref: ZoneId
      VpcId:
        Ref: Vpc
      CidrBlock: 192.168.0.0/24
  SecurityGroup:
    Type: ALIYUN::ECS::SecurityGroup
    Properties:
      VpcId:
        Ref: Vpc
  SecurityGroupIngress_80:
    Type: ALIYUN::ECS::SecurityGroupIngress
    Properties:
      SecurityGroupId:
        Ref: SecurityGroup
      SourceCidrIp: 0.0.0.0/0
      IpProtocol: tcp
      NicType: intranet
      PortRange: 80/80
  InstanceGroup:
    Type: ALIYUN::ECS::InstanceGroup
    Properties:
      VpcId:
        Ref: Vpc
      VSwitchId:
        Ref: VSwitch
      SecurityGroupId:
        Ref: SecurityGroup
      ImageId: centos_7_9
      InstanceName: MaxKB
      InstanceType:
        Ref: InstanceType
      SystemDiskCategory:
        Ref: SystemDiskCategory
      Password:
        Ref: InstancePassword
      IoOptimized: optimized
      MaxAmount: 1
  DS_Instances:
    Type: DATASOURCE::ECS::Instances
    Properties:
      InstanceIds:
        Fn::GetAtt:
        - InstanceGroup
        - InstanceIds
  InstallMaxKB:
    Type: ALIYUN::ECS::RunCommand
    Properties:
      InstanceIds:
        Fn::GetAtt:
        - InstanceGroup
        - InstanceIds
      Type: RunShellScript
      Sync: true
      Timeout: 1800
      CommandContent: |
       #!/bin/bash
        echo "#########################"
        echo "# Install Docker"
        echo "#########################"
        wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
        yum -y install docker-ce
        systemctl start docker
        systemctl enable docker

        echo "#########################"
        echo "# Install MaxKB"
        echo "#########################"

        yum -y install jq
        DOCKER_DAEMON_JSON="/etc/docker/daemon.json"
        sudo jq --arg registry "$NEW_REGISTRY" '. + { "registry-mirrors": [https://0jt38sli.mirror.aliyuncs.com] }' $DOCKER_DAEMON_JSON > ${DOCKER_DAEMON_JSON}.temp && sudo mv ${DOCKER_DAEMON_JSON}.temp $DOCKER_DAEMON_JSON
        sudo systemctl daemon-reload
        sudo systemctl restart docker
        docker run -d --name=maxkb -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data 1panel/maxkb

Outputs:
  MaxKBUrl:
    Description: MaxKB default address.
    Value:
      Fn::Sub:
      - http://${IP}
      - IP:
          Fn::Jq:
          - First
          - if .[0].PublicIpAddress != [] then .[0].PublicIpAddress[0] else .[0].EipAddress.IpAddress
            end
          - Fn::GetAtt:
            - DS_Instances
            - Instances
Metadata:
  ALIYUN::ROS::Interface:
    TemplateTags:
    - acs:developer:gitee:0147-ECS实例上搭建大语言模型的知识库问答系统MaxKB

部署测试

回到我的模板中,点击我的模板,查看对应的资源框架,确认基本无误后点击保存。

image.png

命名为MaxKB。

image.png

点击创建栈:

image.png

由于我在上述ROS脚本中申明了可用区、CPU内存配置、系统盘类型等,所以这里都需要进行选择:

image.png

选择失败时回滚,点击创建,需保证账户余额大于100元

image.png

在创建界面的资源处,可以看到ROS中定义的实例正在逐一被创建:

image.png

构建MaxKB需要花费一些时间,创建完成后如下图所示:

image.png

但是意外的是,事件里面出现执行失败了。

进入事件,点击前面的实例链接。

image.png

查看实例的日志,发现是docker超时了。

image.png

于是我增加了一个测试下载的hello-world的案例,发现是正常的:

sudo docker run hello-world

image.png

于是乎,我换到我自己的电脑上尝试,很奇怪的是,即使添加了一堆镜像源依然下载报错。。。

image.png

感觉有点不对劲了,开始尝试用git方式部署,对照着文档学习了一下,采用git本地编译部署对环境要求极为苛刻。。。转化成一键部署的脚本更是麻烦。

image.png

最后尝试了半天无果,还是放弃MaxKB的搭建吧。。。没事,咱们换个别的,反正这次重点在于ROS编写上。

既然上面我们把基本框架都搭建完了,那么理论上只需要找个能跑的脚本就可以了,思来想去,考虑到综合使用频率和实用性,我这里还是决定来试一试部署Dify。

这里在查安装方法的时候,突然看到阿里的ROS官方已经发过一篇了。。。。流汗黄豆。。。

整体的内容也是大差不差,区别就在于Dify的本地编译很方便,而MaxKB需要规定版本的python环境,很麻烦。

ROSTemplateFormatVersion: '2015-09-01'
Description:
  en: Build the large language model (LLM) application development platform Dify on
    the ECS instance (CentOS 7).
  zh-cn:ECS实例(CentOS 7)上搭建大语言模型(LLM) 应用开发平台Dify。
Parameters:
  ZoneId:
    Type: String
    Label:
      en: Availability Zone
      zh-cn: 可用区ID
    AssociationProperty: ALIYUN::ECS::Instance:ZoneId
  InstanceType:
    Type: String
    Label:
      en: Instance Type
      zh-cn: 实例类型
    AssociationProperty: ALIYUN::ECS::Instance::InstanceType
    AssociationPropertyMetadata:
      ZoneId: ${ZoneId}
      DefaultValueStrategy: recent
  SystemDiskCategory:
    Type: String
    Label:
      en: System Disk Type
      zh-cn: 系统盘类型
    AssociationProperty: ALIYUN::ECS::Disk::SystemDiskCategory
    AssociationPropertyMetadata:
      LocaleKey: DiskCategory
      ZoneId: ${ZoneId}
      InstanceType: ${InstanceType}
      AutoSelectFirst: true
      AutoChangeType: false
    Default: cloud_essd
  InstancePassword:
    Type: String
    Label:
      en: Instance Password
      zh-cn: 实例密码
    Description:
      en: Server login password, Length 8-30, must contain three(Capital letters,
        lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol
        in).
      zh-cn: 服务器登录密码,长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)。
    ConstraintDescription:
      en: Length 8-30, must contain three(Capital letters, lowercase letters, numbers,
        ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol in).
      zh-cn: 长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)。
    AssociationPropertyMetadata:
      Visible:
        Condition:
          Fn::Equals:
          - ${SelectInstance}
          - false
    Default:
    AllowedPattern: '[0-9A-Za-z\_\-\&:;''<>,=%`~!@#\(\)\$\^\*\+\|\{\}\[\]\.\?\/]+$'
    MinLength: 8
    MaxLength: 30
    NoEcho: true
Resources:
  Vpc:
    Type: ALIYUN::ECS::VPC
    Properties:
      CidrBlock: 192.168.0.0/16
  VSwitch:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      ZoneId:
        Ref: ZoneId
      VpcId:
        Ref: Vpc
      CidrBlock: 192.168.0.0/24
  SecurityGroup:
    Type: ALIYUN::ECS::SecurityGroup
    Properties:
      VpcId:
        Ref: Vpc
  SecurityGroupIngress_80:
    Type: ALIYUN::ECS::SecurityGroupIngress
    Properties:
      SecurityGroupId:
        Ref: SecurityGroup
      SourceCidrIp: 0.0.0.0/0
      IpProtocol: tcp
      NicType: intranet
      PortRange: 80/80
  InstanceGroup:
    Type: ALIYUN::ECS::InstanceGroup
    Properties:
      VpcId:
        Ref: Vpc
      VSwitchId:
        Ref: VSwitch
      SecurityGroupId:
        Ref: SecurityGroup
      ImageId: centos_7_9
      InstanceName: dify
      InstanceType:
        Ref: InstanceType
      SystemDiskCategory:
        Ref: SystemDiskCategory
      Password:
        Ref: InstancePassword
      IoOptimized: optimized
      MaxAmount: 1
  DS_Instances:
    Type: DATASOURCE::ECS::Instances
    Properties:
      InstanceIds:
        Fn::GetAtt:
        - InstanceGroup
        - InstanceIds
  InstallDify:
    Type: ALIYUN::ECS::RunCommand
    Properties:
      InstanceIds:
        Fn::GetAtt:
        - InstanceGroup
        - InstanceIds
      Type: RunShellScript
      Sync: true
      Timeout: 1800
      CommandContent: |
        #!/bin/bash
        echo "#########################"
        echo "# Install Docker"
        echo "#########################"
        wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
        yum -y install docker-ce
        systemctl start docker
        systemctl enable docker

        echo "#########################"
        echo "# Install Dify"
        echo "#########################"
        yum -y install git
        git clone --depth 1 https://github.com/langgenius/dify.git /opt/dify
        cd /opt/dify/docker
        docker compose up -d
        docker compose ps
Outputs:
  DifyUrl:
    Description: Dify default address.
    Value:
      Fn::Sub:
      - http://${IP}
      - IP:
          Fn::Jq:
          - First
          - if .[0].PublicIpAddress != [] then .[0].PublicIpAddress[0] else .[0].EipAddress.IpAddress
            end
          - Fn::GetAtt:
            - DS_Instances
            - Instances
Metadata:
  ALIYUN::ROS::Interface:
    TemplateTags:
    - acs:developer:gitee:0147-ECS实例上搭建大语言模型的知识库问答系统MaxKB

最后结果是创建成功了,但是网站打不开,我一查日志,给我看笑了。

image.png

官方文档给出的部署方式同样出现了docker超时的情况,这下估计docker真指望不到了。

离了个大谱,还是自己来吧。

先写了个部署nginx的脚本:

#!/bin/bash
#提前解决编译时所需的依赖环境、库文件;
yum install -y wget gzip gcc tar make
yum install -y pcre pcre-devel zlib-devel
#下载Nginx软件包;
wget -c http://nginx.org/download/nginx-1.24.0.tar.gz -P /usr/src/
#Cd切换/usr/src/;
cd /usr/src/
ls -l nginx-1.24.0.tar.gz
#通过Tar工具对其解压;
tar -xzvf nginx-1.24.0.tar.gz
#Cd切换至Nginx源代码目录;
cd nginx-1.24.0/
#提前创建www用户和组;
useradd -s /sbin/nologin www -M
#预编译;
./configure --prefix=/usr/local/nginx/ --user=www --group=www --with-http_stub_status_module
#编译;
make -j4
#安装;
make -j4 install
#查看Nginx软件服务是否部署成功;
ls -l /usr/local/nginx/
#启动Nginx服务进程;
/usr/local/nginx/sbin/nginx
#查看Nginx进程状态;
ps -ef|grep -aiE nginx
#Firewalld防火墙对外开放80端口;
firewall-cmd --add-port=80/tcp --permanent
systemctl reload firewalld.service 

直接丢里面执行,部署完了如下所示:

image.png
image.png

这里可以看到nginx还是部署成功了的,看来只是docker存在一些莫名奇妙的问题罢了。将这里的nginx脚本替换之前的MaxKB或者Dify的shell脚本处,勉强算是制作出第一个ROS脚本了,虽然一点用处都没有。。。

总结

如果不是这次亲手尝试来写ROS脚本也没想到会碰到那么多问题,本来其实听起来很简单的,整个逻辑也能理解,但是实际上手后,由于不熟悉,搞得这也是问题那也是问题,反反复复折腾。

第二点就是,官网上给的脚本居然也会出现执行错误的情况,这是我没想到的。

不过,也正是这些问题和挑战,让我深刻体会到了理论与实践之间的差距,也促使我更加深入地学习和探索。每解决一个问题,都是对云计算领域理解的一次深化,对ROS及背后资源编排理念的掌握也愈发熟练。

过程充满挑战,但每一次解决问题都是一次宝贵的学习机会,让我的技术栈更加坚实,也让我在云原生和DevOps的道路上迈出了坚实的一步。这种从实战中获得的经验,是任何教科书或理论学习都无法替代的。


2024年7月13日更新

昨晚研究了一晚,又写了一个部署2048游戏的ROS脚本。

ROSTemplateFormatVersion: '2015-09-01'
Description:
  en: Set up the 2048 game on an ECS instance..
  zh-cn:ECS实例上搭建2048小游戏。
Parameters:
  ZoneId:
    Type: String
    Label:
      en: Availability Zone
      zh-cn: 可用区ID
    AssociationProperty: ALIYUN::ECS::Instance:ZoneId
  InstanceType:
    Type: String
    Label:
      en: Instance Type
      zh-cn: 实例类型
    AssociationProperty: ALIYUN::ECS::Instance::InstanceType
    AssociationPropertyMetadata:
      ZoneId: ${ZoneId}
      DefaultValueStrategy: ecs.e-c1m2.large
Resources:
  Vpc:
    Type: ALIYUN::ECS::VPC
    Properties:
      CidrBlock: 192.168.0.0/16
  VSwitch:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      ZoneId:
        Ref: ZoneId
      VpcId:
        Ref: Vpc
      CidrBlock: 192.168.0.0/24
  SecurityGroup:
    Type: ALIYUN::ECS::SecurityGroup
    Properties:
      VpcId:
        Ref: Vpc
  SecurityGroupIngress_80:
    Type: ALIYUN::ECS::SecurityGroupIngress
    Properties:
      SecurityGroupId:
        Ref: SecurityGroup
      SourceCidrIp: 0.0.0.0/0
      IpProtocol: tcp
      NicType: intranet
      PortRange: 80/80
  InstanceGroup:
    Type: ALIYUN::ECS::InstanceGroup
    Properties:
      VpcId:
        Ref: Vpc
      VSwitchId:
        Ref: VSwitch
      SecurityGroupId:
        Ref: SecurityGroup
      ImageId: centos_7_9
      InstanceName: GAME
      InstanceType:
       Ref: InstanceType
      SystemDiskCategory : cloud_essd
      IoOptimized: optimized
      MaxAmount: 2
  DS_Instances:
    Type: DATASOURCE::ECS::Instances
    Properties:
      InstanceIds:
        Fn::GetAtt:
        - InstanceGroup
        - InstanceIds
  InstallGAME:
    Type: ALIYUN::ECS::RunCommand
    Properties:
      InstanceIds:
        Fn::GetAtt:
        - InstanceGroup
        - InstanceIds
      Type: RunShellScript
      Sync: true
      Timeout: 1800
      CommandContent: |
       #!/bin/bash
       yum install -y httpd wget
       systemctl start httpd
       wget 'https://computenest-artifacts-cn-hangzhou.oss-cn-hangzhou-internal.aliyuncs.com/1853370294850618/cn-beijing/1697533575326/2048.tgz' -O 2048.tgz
       tar xvf 2048.tgz
       mv 2048/* /var/www/html && rm -rf 2048

Outputs:
  GAMEUrl:
    Description: GAME default address.
    Value:
      Fn::Sub:
      - http://${IP}
      - IP:
          Fn::Jq:
          - First
          - if .[0].PublicIpAddress != [] then .[0].PublicIpAddress[0] else .[0].EipAddress.IpAddress
            end
          - Fn::GetAtt:
            - DS_Instances
            - Instances
Metadata:
  ALIYUN::ROS::Interface:
    TemplateTags:
    - acs:developer:gitee:0147-在ECS实例上搭建2048小游戏

这里实测是完全没有任何问题的。

image.png

image.png

image.png

下面我把脚本上传到我的gitee中,新建一个仓库,命名为阿里云ROS:

image.png

由于只有一个脚本,所以直接创建文件然后复制进去就可以了:

image.png

最终地址如下

image.png

我们来尝试直接一键部署,前面引入ROS的部署的链接地址:

https://ros.console.aliyun.com/region/stacks/create?hideStepRow=true&hideStackConfig=true&disableRollback=false&isSimplified=true&disableNarue&productNavBar=disabled&templateUrl=

后面拼接自己的gitee地址:

https://gitee.com/a156da16/aliyunros/blob/master/GAME.yaml

所以最终一键部署链接如下:

https://ros.console.aliyun.com/cn-hangzhou/stacks/create?hideStepRow=true&hideStackConfig=true&disableRollback=false&isSimplified=true&disableNarue&productNavBar=disabled&templateUrl=https://gitee.com/a156da16/aliyunros/blob/master/GAME.yaml

尝试了一下,有个很逆天的地方,一直显示访问被拒绝。具体原因也一直没找到,在群里找专家问了:

image.png

为了避免版本不一致,我直接复制gitee里上传的那个版本,进入ROS中进行创建,是能够识别模板参数的:

image.png

但是通过官方给的链接就是一直报错。。。

这篇就这样吧,反正总之把脚本也是写出来了,刚开始接触不是很熟悉,只能从简单的开始了。

相关推荐

  1. 面试经历

    2024-07-17 16:24:04       48 阅读
  2. nodeBB部署

    2024-07-17 16:24:04       23 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-17 16:24:04       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-17 16:24:04       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-17 16:24:04       58 阅读
  4. Python语言-面向对象

    2024-07-17 16:24:04       69 阅读

热门阅读

  1. Gmsh概述

    2024-07-17 16:24:04       18 阅读
  2. Linux环境下卸载Redis

    2024-07-17 16:24:04       20 阅读
  3. ODrive学习笔记三——串口流

    2024-07-17 16:24:04       23 阅读
  4. LinkedList

    2024-07-17 16:24:04       21 阅读
  5. AcWing 668. 游戏时间2

    2024-07-17 16:24:04       21 阅读
  6. [C/C++入门][ifelse]20、闰年判断

    2024-07-17 16:24:04       21 阅读
  7. Pycharm远程配置及Linux&Windows虚拟环境安装记录

    2024-07-17 16:24:04       23 阅读
  8. ES6基本语法(四)——对象和类

    2024-07-17 16:24:04       20 阅读
  9. TG群导航机器人:深度检索技术的创新应用

    2024-07-17 16:24:04       24 阅读