基于资源编排一键交付应用之基于WaitCondition的通知机制

本文是基于资源编排一键交付连接RDS的应用的进阶篇。
随着各类应用的不断发展,许多应用开发人员对应用部署的便捷性和应用运行的稳定性都提出了很高的要求,从这个意义上来讲,应用的一键交付以及对应用做负载均衡将会是一个不错的解决方案。对开发人员而言,在交付应用时如何才能保证对应用所做的负载均衡是成功的呢?此时,有效控制应用的部署过程并且感知应用的部署结果,将具有重要的意义。
本文将为大家提供一个基于资源编排一键构建负载均衡应用并基于 WaitCondition 的通知机制掌控应用部署过程的解决方案。
首先引入一个概念:WaitCondition(等待条件)。在基于资源编排构建应用时,不得不使用资源 ALIYUN::ECS::Instance 的 UserData 属性。在创建 ECS VM 的时候,UserData 作为 ECS VM 的属性传递到 ROS,当 UserData 执行结束后,执行结果需要被 ROS 接收并进行处理,为了实现这个功能,需要引入两个资源,一个资源表示需要在一定的时间内等待一定数量的 UserData 执行的消息,在条件不满足的情况下,使栈一直保持在资源创建状态;另一个资源接收 UserData 的信息,保存 UserData 执行的结果,并且产生 Cloud Init 回调的 curl 命令,进而通知 ROS UserData 执行的结果。这两个资源依次为:

  • ALIYUN::ROS::WaitCondition

    • 创建等待条件 WaitCondition,掌控应用的部署过程
  • ALIYUN::ROS::WaitConditionHandle
    • 创建一个等待条件句柄 WaitConditionHandle,标识等待条件引用

因此,WaitCondition 使用了一种通知机制,在 UserData 执行结束后告诉开发者应用部署已完成,使开发者能够感知到应用部署的进度和状态。
为了便于说明,本文将以基于资源编排一键交付连接RDS的应用中构建的 WordPress 作为应用构建示例。基于资源编排快速构建SLB应用主要分为以下三个方面:

  • 基于资源编排构建一个负载均衡集群,用于提供应用的运行环境
  • 基于资源编排在负载均衡集群的每个节点上构建应用
  • 利用 WaitCondition 的通知机制掌控应用的部署过程

目前云资源构建的网络类型只支持 VPC,可选区域仅支持华南 1、华北 2 和华东 2,即 RegionId 只支持 cn-shenzhencn-beijingcn-shanghai。其它的 Region 和网络类型,我们也会尽快的上线。

基于资源编排快速构建SLB集群

定义一个构建 SLB 资源的编排模板,用于构建 SLB 集群。为了方便起见,本文将定义一个具有两个节点的 SLB 集群,具体的模板可参考通过资源编排快速的构建负载均衡(SLB)。

在负载均衡集群的每个节点上构建应用

定义好 SLB 的模板后,需要在该模板的资源类型 ALIYUN::ECS::Instance 中增加构建 WordPress 应用的 UserData,在模板中增加对资源类型 ALIYUN::RDS::DBInstance 的定义,并在 ALIYUN::ECS::Instance 中使用属性DependsOn,从而为 WordPress 提供对数据库的访问。在单个节点上构建 WordPress 应用的模板可详见基于资源编排一键交付连接RDS的应用。
除了以上介绍的方法外,本文将介绍一种构建负载均衡集群更为简便的方法,那就是在模板中定义资源类型 ALIYUN::ECS::InstanceGroup,并在其中利用属性 MaxAmountMinAmount 指定您需要创建的实例个数,以此来替代在模板中重复定义 ALIYUN::ECS::Instance 的繁琐工作。如下模板定义了一个拥有三个实例的负载均衡集群(部分资源的定义已省略,可详见最终模板):

"AttachEcs": {
  "Properties": {
    "BackendServers": [
      {
        "ServerId": {
          "Fn::Select": [
            "0",
            {
              "Fn::GetAtt": [
                "WebServerGroup",
                "InstanceIds"
              ]
            }
          ]
        },
        "Weight": 100
      },
      {
        "ServerId": {
          "Fn::Select": [
            "1",
            {
              "Fn::GetAtt": [
                "WebServerGroup",
                "InstanceIds"
              ]
            }
          ]
        },
        "Weight": 100
      },
      {
        "ServerId": {
          "Fn::Select": [
            "2",
            {
              "Fn::GetAtt": [
                "WebServerGroup",
                "InstanceIds"
              ]
            }
          ]
        },
        "Weight": 100
      }
    ],
    "LoadBalancerId": {
      "Ref": "LoadBalancer"
    }
  },
  "Type": "ALIYUN::SLB::BackendServerAttachment"
}
"LoadBalancer": {
  "Properties": {
    "AddressType": "internet",
    "InternetChargeType": "paybytraffic",
    "LoadBalancerName": "createByRos"
  },
  "Type": "ALIYUN::SLB::LoadBalancer"
},
"WebServerGroup": {
  "DependsOn": "Database",
  "Properties": {
    "ImageId": {
      "Ref": "ImageId"
    },
    "InstanceType": "ecs.s3.large",
    "IoOptimized": {
      "Ref": "IoOptimized"
    },
    "MaxAmount": 3,
    "MinAmount": 3,
    "NetworkType": {
      "Ref": "NetworkType"
    },
    "Password": {
      "Ref": "InstancePassword"
    },
    "SecurityGroupId": {
      "Ref": "SecurityGroupId"
    },
    "SystemDiskCategory": "cloud_ssd",
    "UserData": {
      "Fn::Replace": [
        {
          "ros-notify": {
            "Fn::GetAtt": [
              "WebServerGroupConditionHandle",
              "CurlCli"
            ]
          }
        },
        {
          "Fn::Join": [
            "",
            [
              "#!/bin/sh",
              "\n",
              "DatabaseUser=",
              {
                "Ref": "DBUser"
              },
              "\n",
              "DatabasePwd=",
              {
                "Ref": "DBPassword"
              },
              "\n",
              "DatabaseName=",
              {
                "Ref": "DBName"
              },
              "\n",
              "DatabaseHost=",
              {
                "Fn::GetAtt": [
                  "Database",
                  "InnerConnectionString"
                ]
              },
              "\n",
              "WebRootPath='/var/www/html'\n",
              "ApacheIndex='Options Indexes FollowSymLinks'\n",
              "ApacheIndexReplace='Options -Indexes FollowSymLinks'\n",
              "yum install -y curl httpd mysql-server php php-common php-mysql\n",
              "yum install -y php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpc\n",
              "chkconfig httpd on\n",
              "wget http://wordpress.org/latest.tar.gz\n",
              "tar -xzvf latest.tar.gz\n",
              "sed -i \"s/database_name_here/$DatabaseName/\" wordpress/wp-config-sample.php\n",
              "sed -i \"s/username_here/$DatabaseUser/\" wordpress/wp-config-sample.php\n",
              "sed -i \"s/password_here/${DatabasePwd:-$DatabasePwdDef}/\" wordpress/wp-config-sample.php\n",
              "sed -i \"s/localhost/$DatabaseHost/\" wordpress/wp-config-sample.php\n",
              "mv wordpress/wp-config-sample.php wordpress/wp-config.php\n",
              "cp -a wordpress/* $WebRootPath\n",
              "rm -rf wordpress*\n",
              "service httpd stop\n",
              "usermod -d $WebRootPath apache &>/dev/null\n",
              "chown apache:apache -R $WebRootPath\n",
              "sed -i \"s/$ApacheIndex/$ApacheIndexReplace/\" /etc/httpd/conf/httpd.conf\n",
              "service httpd start\n",
              "ros-notify -d '{\"data\" : \"Install Wordpress\"}'\n"
            ]
          ]
        }
      ]
    },
    "VSwitchId": {
      "Ref": "VSwitchId"
    },
    "VpcId": {
      "Ref": "VpcId"
    }
  },
  "Type": "ALIYUN::ECS::InstanceGroup"
}

值得注意的是,在构建 WordPress 的过程中,云主机需要访问外网以获取 WordPress 的最新安装包和其依赖服务,因此,在构建 WordPress 之前,应确保 VPC 环境下的云主机可以访问外网。有关 VPC 访问外网的操作可详见在专有网络中搭建SNAT网关。

加入WaitCondition,掌控应用的部署过程

正如前文所说,在应用部署时引入资源 ALIYUN::ROS::WaitConditionALIYUN::ROS::WaitConditionHandle 以掌控应用的部署过程。资源 ALIYUN::ROS::WaitCondition 有以下三个属性:

  • Count

    • 接收到成功信号的数量,即当等待条件接收到必要的成功信号后,ROS 才能继续创建堆栈资源。否则,若在超时时间之前未能接受到指定数量的成功信号, ROS 会认为未满足等待条件,从而停止堆栈资源的创建并进行回滚。
  • Handle
    • 使用内部函数 Ref 来指定 ALIYUN::ROS::WaitConditionHandle 资源。只要您在资源创建过程中添加 WaitCondition 资源,就必须将等待条件与对应的 WaitConditionHandle 资源进行关联。
  • Timeout
    • 等待接受 Count 属性指定信号数的最大允许时长(以秒计算)。为该属性指定的最长时间为 12 小时(43200秒)。

在创建资源 ALIYUN::ECS::Instance 的时候,ROS 会把 UserData 最终传递到 ECS VM,然后通过 WaitCondition 轮询的检查是否超时、是否接收到 UserData 失败的消息以及是否接收到足够多的 UserData 执行的消息。当 WaitCondition 检测到 WaitConditionHandle 接收到 UserData 执行结果的通知后,标识自己创建成功。最后 ROS 为这个模板所创建的栈标识为创建成功,否则就是创建失败。
值得注意的是,在模板中定义 ALIYUN::ROS::WaitCondition 时通常会使用属性DependsOn,以保证资源 ALIYUN::RDS::Instance 优先于资源 ALIYUN::ROS::WaitCondition 而创建。若未指定 DependsOn 属性,并且资源 ALIYUN::ROS::WaitCondition 优先于资源 ALIYUN::RDS::Instance 而创建,则资源 ALIYUN::ROS::WaitCondition 将一直处于创建等待状态,直到资源 ALIYUN::ROS::Instance 创建完成。
UserData 执行的状态信息是通过 'ros_notify -d {"status" : "SUCCESS", "reason": "signal1", "data": data1, "id" : "1"}' 这行命令传回来的,该命令中的参数解释如下:

  • status

    • UserData 执行的状态,有两个值:"SUCCESS"和"FAILURE"。"SUCCESS"表明 UserData 执行成功;"FAILJURE" 表明 UserData 执行失败,栈资源将进行回滚。
  • reason
    • UserData 执行的状态原因,用户自定义
  • data
    • 数据信息,用户自定义
  • id
    • 唯一Id号,标识一次通知或一个资源,用户自定义

这些值都是可选的,用户可以部分指定或者全部指定,也可以什么都不指定。如果什么都不指定或只指定部分信息,waitConditionHandle 处理的时候,会赋上默认值,默认值是成功。Ros_notify 最终会被替换成由 WaitConditionHandle 所生成的真正的 curl 命令串。后面的 -d 是发送给 ROS 的详细执行信息。这个是可选的,用户可以根据需要进行添加。所以在模板中,需要使用以下两个函数:

  • Fn::Replace

    • 用 WaitConditionHandle 提供的 curlCli 替换 ros_notify
  • Fn::GetAtt
    • 获取 WaitConditionHandle 的输出值 curlCli

除此之外,WaitCondition 也有一个输出信息 Data,可输出 UserData 执行返回的所有信息。
以下定义了两个利用已有 VPC 资源构建负载均衡的 WordPress 的模板。
首先是利用资源类型 ALIYUN::RDS::Instance 定义了具有两个节点的负载均衡集群的模板,值得注意的是,为了方便起见,模板中的两个 WebServer 共用一个 WaitCondition,所以模板中的资源 WaitCondition 未使用属性 DependsOn。以下是模板详情:

{
  "ROSTemplateFormatVersion": "2015-09-01",
  "Parameters": {
    "DBName": {
      "AllowedPattern": "[a-z]{1}[a-z0-9-_]*[a-z0-9]{1}",
      "ConstraintDescription": "由 2~64 个字符的小写字母、数字、下划线或中划线组成,开头需为字母,结尾需为字母或数字。",
      "Default": "wordpress",
      "Description": "WordPress数据库名称",
      "MaxLength": "64",
      "MinLength": "2",
      "Type": "String"
    },
    "DBPassword": {
      "AllowedPattern": "[a-zA-Z0-9-_]*",
      "ConstraintDescription": "由 6~32 个字符的字母、数字、中划线或下划线组成。",
      "Default": "wpADMIN123",
      "Description": "WordPress数据库密码",
      "MaxLength": "32",
      "MinLength": "6",
      "Type": "String"
    },
    "DBUser": {
      "AllowedPattern": "[a-z]{1}[a-z0-9_]*[a-z0-9]{1}",
      "ConstraintDescription": "由 2~16 个字符的小写字母,数字或下划线组成、开头需为字母,结尾需为字母或数字。",
      "Default": "wpuser",
      "Description": "WordPress数据库用户名",
      "MaxLength": "16",
      "MinLength": "2",
      "Type": "String"
    },
    "ImageId": {
      "Description": "镜像文件 ID,表示启动实例时选择的镜像资源, <a href='#/product/cn-shenzhen/list/imageList' target='_blank'>查看镜像资源</a>",
      "Type": "String"
    },
    "InstancePassword": {
      "AllowedPattern": "[a-zA-Z0-9]*",
      "ConstraintDescription": "可包含大小写字母,数字和特殊字符",
      "Default": "vmADMIN123",
      "Description": "ECS实例的登录密码",
      "MaxLength": "41",
      "MinLength": "8",
      "Type": "String"
    },
    "SecurityGroupId": {
      "Description": "安全组Id",
      "Type": "String"
    },
    "VSwitchId": {
      "Description": "已创建的VSwitch的ID",
      "Type": "String"
    },
    "VpcId": {
      "Description": "已创建的vpc的ID",
      "Type": "String"
    },
    "ZoneId": {
      "Default": "cn-shenzhen-a",
      "Description": "可用区 Id",
      "Type": "String"
    }
  },
  "Resources": {
    "AttachEcs": {
      "Properties": {
        "BackendServers": [
          {
            "ServerId": {
              "Fn::GetAtt": [
                "WebServerA",
                "InstanceId"
              ]
            },
            "Weight": 100
          },
          {
            "ServerId": {
              "Fn::GetAtt": [
                "WebServerB",
                "InstanceId"
              ]
            },
            "Weight": 100
          }
        ],
        "LoadBalancerId": {
          "Ref": "LoadBalancer"
        }
      },
      "Type": "ALIYUN::SLB::BackendServerAttachment"
    },
    "CreateListener": {
      "Properties": {
        "BackendServerPort": 80,
        "Bandwidth": 1,
        "HealthCheck": {
          "HealthyThreshold": 3,
          "HttpCode": "http_2xx,http_3xx,http_4xx,http_5xx",
          "Interval": 2,
          "Timeout": 5,
          "UnhealthyThreshold": 3
        },
        "ListenerPort": "80",
        "LoadBalancerId": {
          "Ref": "LoadBalancer"
        },
        "Protocol": "http",
        "Scheduler": "wrr"
      },
      "Type": "ALIYUN::SLB::Listener"
    },
    "Database": {
      "Properties": {
        "DBInstanceClass": "rds.mysql.t1.small",
        "DBInstanceDescription": "ros",
        "DBInstanceStorage": "50",
        "DBMappings": [
          {
            "CharacterSetName": "utf8",
            "DBName": {
              "Ref": "DBName"
            }
          }
        ],
        "Engine": "MySQL",
        "EngineVersion": "5.6",
        "MasterUserPassword": {
          "Ref": "DBPassword"
        },
        "MasterUsername": {
          "Ref": "DBUser"
        },
        "PreferredBackupPeriod": [
          "Monday",
          "Wednesday"
        ],
        "PreferredBackupTime": "23:00Z-24:00Z",
        "SecurityIPList": "0.0.0.0/0",
        "VPCId": {
          "Ref": "VpcId"
        },
        "VSwitchId": {
          "Ref": "VSwitchId"
        }
      },
      "Type": "ALIYUN::RDS::DBInstance"
    },
    "LoadBalancer": {
      "Properties": {
        "AddressType": "internet",
        "InternetChargeType": "paybytraffic",
        "LoadBalancerName": "createByRos"
      },
      "Type": "ALIYUN::SLB::LoadBalancer"
    },
    "WebServerA": {
      "DependsOn": "Database",
      "Properties": {
        "ImageId": {
          "Ref": "ImageId"
        },
        "InstanceType": "ecs.s2.large",
        "IoOptimized": "optimized",
        "Password": {
          "Ref": "InstancePassword"
        },
        "SecurityGroupId": {
          "Ref": "SecurityGroupId"
        },
        "SystemDiskCategory": "cloud_ssd",
        "UserData": {
          "Fn::Replace": [
            {
              "ros-notify": {
                "Fn::GetAtt": [
                  "WebServerConditionHandle",
                  "CurlCli"
                ]
              }
            },
            {
              "Fn::Join": [
                "",
                [
                  "#!/bin/sh",
                  "\n",
                  "DatabaseUser=",
                  {
                    "Ref": "DBUser"
                  },
                  "\n",
                  "DatabasePwd=",
                  {
                    "Ref": "DBPassword"
                  },
                  "\n",
                  "DatabaseName=",
                  {
                    "Ref": "DBName"
                  },
                  "\n",
                  "DatabaseHost=",
                  {
                    "Fn::GetAtt": [
                      "Database",
                      "InnerConnectionString"
                    ]
                  },
                  "\n",
                  "WebRootPath='/var/www/html'\n",
                  "ApacheIndex='Options Indexes FollowSymLinks'\n",
                  "ApacheIndexReplace='Options -Indexes FollowSymLinks'\n",
                  "yum install -y curl httpd mysql-server php php-common php-mysql\n",
                  "yum install -y php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpc\n",
                  "chkconfig httpd on\n",
                  "wget http://wordpress.org/latest.tar.gz\n",
                  "tar -xzvf latest.tar.gz\n",
                  "sed -i \"s/database_name_here/$DatabaseName/\" wordpress/wp-config-sample.php\n",
                  "sed -i \"s/username_here/$DatabaseUser/\" wordpress/wp-config-sample.php\n",
                  "sed -i \"s/password_here/${DatabasePwd:-$DatabasePwdDef}/\" wordpress/wp-config-sample.php\n",
                  "sed -i \"s/localhost/$DatabaseHost/\" wordpress/wp-config-sample.php\n",
                  "mv wordpress/wp-config-sample.php wordpress/wp-config.php\n",
                  "cp -a wordpress/* $WebRootPath\n",
                  "rm -rf wordpress*\n",
                  "service httpd stop\n",
                  "usermod -d $WebRootPath apache &>/dev/null\n",
                  "chown apache:apache -R $WebRootPath\n",
                  "sed -i \"s/$ApacheIndex/$ApacheIndexReplace/\" /etc/httpd/conf/httpd.conf\n",
                  "service httpd start\n",
                  "ros-notify -d '{\"id\" : \"WebServerA\", \"data\" : \"Install Wordpress\"}'\n"
                ]
              ]
            }
          ]
        },
        "VSwitchId": {
          "Ref": "VSwitchId"
        },
        "VpcId": {
          "Ref": "VpcId"
        }
      },
      "Type": "ALIYUN::ECS::Instance"
    },
    "WebServerB": {
      "DependsOn": "Database",
      "Properties": {
        "ImageId": {
          "Ref": "ImageId"
        },
        "InstanceType": "ecs.s2.large",
        "IoOptimized": "optimized",
        "Password": {
          "Ref": "InstancePassword"
        },
        "SecurityGroupId": {
          "Ref": "SecurityGroupId"
        },
        "SystemDiskCategory": "cloud_ssd",
        "UserData": {
          "Fn::Replace": [
            {
              "ros-notify": {
                "Fn::GetAtt": [
                  "WebServerConditionHandle",
                  "CurlCli"
                ]
              }
            },
            {
              "Fn::Join": [
                "",
                [
                  "#!/bin/sh",
                  "\n",
                  "DatabaseUser=",
                  {
                    "Ref": "DBUser"
                  },
                  "\n",
                  "DatabasePwd=",
                  {
                    "Ref": "DBPassword"
                  },
                  "\n",
                  "DatabaseName=",
                  {
                    "Ref": "DBName"
                  },
                  "\n",
                  "DatabaseHost=",
                  {
                    "Fn::GetAtt": [
                      "Database",
                      "InnerConnectionString"
                    ]
                  },
                  "\n",
                  "WebRootPath='/var/www/html'\n",
                  "ApacheIndex='Options Indexes FollowSymLinks'\n",
                  "ApacheIndexReplace='Options -Indexes FollowSymLinks'\n",
                  "yum install -y curl httpd mysql-server php php-common php-mysql\n",
                  "yum install -y php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpc\n",
                  "chkconfig httpd on\n",
                  "wget http://wordpress.org/latest.tar.gz\n",
                  "tar -xzvf latest.tar.gz\n",
                  "sed -i \"s/database_name_here/$DatabaseName/\" wordpress/wp-config-sample.php\n",
                  "sed -i \"s/username_here/$DatabaseUser/\" wordpress/wp-config-sample.php\n",
                  "sed -i \"s/password_here/${DatabasePwd:-$DatabasePwdDef}/\" wordpress/wp-config-sample.php\n",
                  "sed -i \"s/localhost/$DatabaseHost/\" wordpress/wp-config-sample.php\n",
                  "mv wordpress/wp-config-sample.php wordpress/wp-config.php\n",
                  "cp -a wordpress/* $WebRootPath\n",
                  "rm -rf wordpress*\n",
                  "service httpd stop\n",
                  "usermod -d $WebRootPath apache &>/dev/null\n",
                  "chown apache:apache -R $WebRootPath\n",
                  "sed -i \"s/$ApacheIndex/$ApacheIndexReplace/\" /etc/httpd/conf/httpd.conf\n",
                  "service httpd start\n",
                  "ros-notify -d '{\"id\" : \"WebServerB\", \"data\" : \"Install Wordpress\"}'\n"
                ]
              ]
            }
          ]
        },
        "VSwitchId": {
          "Ref": "VSwitchId"
        },
        "VpcId": {
          "Ref": "VpcId"
        }
      },
      "Type": "ALIYUN::ECS::Instance"
    },
    "WebServerConditionHandle": {
      "Type": "ALIYUN::ROS::WaitConditionHandle"
    },
    "WebServerWaitCondition": {
      "Properties": {
        "Count": 2,
        "Handle": {
          "Ref": "WebServerConditionHandle"
        },
        "Timeout": 600
      },
      "Type": "ALIYUN::ROS::WaitCondition"
    }
  },
  "Outputs": {
    "Data": {
      "Value": {
        "Fn::GetAtt": [
          "WebServerWaitCondition",
          "Data"
        ]
      }
    },
    "InnerConnectionString": {
      "Value": {
        "Fn::GetAtt": [
          "Database",
          "InnerConnectionString"
        ]
      }
    },
    "InstanceId-A": {
      "Value": {
        "Fn::GetAtt": [
          "WebServerA",
          "InstanceId"
        ]
      }
    },
    "InstanceId-B": {
      "Value": {
        "Fn::GetAtt": [
          "WebServerB",
          "InstanceId"
        ]
      }
    },
    "IpAddress": {
      "Description": "The ip address of the load balancer.",
      "Value": {
        "Fn::GetAtt": [
          "LoadBalancer",
          "IpAddress"
        ]
      }
    },
    "LoadBalancerId": {
      "Description": "The id of load balance created.",
      "Value": {
        "Fn::GetAtt": [
          "LoadBalancer",
          "LoadBalancerId"
        ]
      }
    }
  }
}

其次是利用资源类型 ALIYUN::ECS::InstanceGroup 一次性定义了具有三个节点的负载均衡集群的模板,值得注意的是,在定义模板前用户并不知道 InstanceGroup 中的每个 Instance 的资源 ID,所以在调用命令 ros-notify -d 时不应该指定资源ID,而是让其自动去获取,以下是模板详情:

{
  "ROSTemplateFormatVersion": "2015-09-01",
  "Parameters": {
    "DBName": {
      "AllowedPattern": "[a-z]{1}[a-z0-9-_]*[a-z0-9]{1}",
      "ConstraintDescription": "由 2~64 个字符的小写字母、数字、下划线或中划线组成,开头需为字母,结尾需为字母或数字。",
      "Default": "wordpress",
      "Description": "WordPress数据库名称",
      "MaxLength": "64",
      "MinLength": "2",
      "Type": "String"
    },
    "DBPassword": {
      "AllowedPattern": "[a-zA-Z0-9-_]*",
      "ConstraintDescription": "由 6~32 个字符的字母、数字、中划线或下划线组成。",
      "Default": "wpADMIN123",
      "Description": "WordPress数据库密码",
      "MaxLength": "32",
      "MinLength": "6",
      "Type": "String"
    },
    "DBUser": {
      "AllowedPattern": "[a-z]{1}[a-z0-9_]*[a-z0-9]{1}",
      "ConstraintDescription": "由 2~16 个字符的小写字母,数字或下划线组成、开头需为字母,结尾需为字母或数字。",
      "Default": "wpuser",
      "Description": "WordPress数据库用户名",
      "MaxLength": "16",
      "MinLength": "2",
      "Type": "String"
    },
    "ImageId": {
      "Description": "镜像文件 ID,表示启动实例时选择的镜像资源, <a href='#/product/cn-shenzhen/list/imageList' target='_blank'>查看镜像资源</a>",
      "Type": "String"
    },
    "InstancePassword": {
      "AllowedPattern": "[a-zA-Z0-9]*",
      "ConstraintDescription": "可包含大小写字母,数字和特殊字符",
      "Default": "vmADMIN123",
      "Description": "ECS实例的登录密码",
      "MaxLength": "41",
      "MinLength": "8",
      "Type": "String"
    },
    "IoOptimized": {
      "AllowedValues": [
        "none",
        "optimized"
      ],
      "Description": "The 'optimized' instance can provide better IO performance. Support 'none' and 'optimized' only, default is 'none'.",
      "Type": "String"
    },
    "NetworkType": {
      "AllowedValues": [
        "vpc",
        "classic"
      ],
      "Default": "classic",
      "Description": "Instance network type. Support 'vpc' and 'classic', for compatible reason, default is 'classic'. If vswitch id and vpc id is specified, the property will be forced to be set to 'vpc'  ",
      "Type": "String"
    },
    "SecurityGroupId": {
      "Description": "安全组Id",
      "Type": "String"
    },
    "VSwitchId": {
      "Description": "已创建的VSwitch的ID",
      "Type": "String"
    },
    "VpcId": {
      "Description": "已创建的vpc的ID",
      "Type": "String"
    },
    "ZoneId": {
      "Default": "cn-shenzhen-a",
      "Description": "可用区 Id",
      "Type": "String"
    }
  },
  "Resources": {
    "AttachEcs": {
      "Properties": {
        "BackendServers": [
          {
            "ServerId": {
              "Fn::Select": [
                "0",
                {
                  "Fn::GetAtt": [
                    "WebServerGroup",
                    "InstanceIds"
                  ]
                }
              ]
            },
            "Weight": 100
          },
          {
            "ServerId": {
              "Fn::Select": [
                "1",
                {
                  "Fn::GetAtt": [
                    "WebServerGroup",
                    "InstanceIds"
                  ]
                }
              ]
            },
            "Weight": 100
          },
          {
            "ServerId": {
              "Fn::Select": [
                "2",
                {
                  "Fn::GetAtt": [
                    "WebServerGroup",
                    "InstanceIds"
                  ]
                }
              ]
            },
            "Weight": 100
          }
        ],
        "LoadBalancerId": {
          "Ref": "LoadBalancer"
        }
      },
      "Type": "ALIYUN::SLB::BackendServerAttachment"
    },
    "CreateListener": {
      "Properties": {
        "BackendServerPort": 80,
        "Bandwidth": 1,
        "HealthCheck": {
          "HealthyThreshold": 3,
          "HttpCode": "http_2xx,http_3xx,http_4xx,http_5xx",
          "Interval": 2,
          "Timeout": 5,
          "UnhealthyThreshold": 3
        },
        "ListenerPort": "80",
        "LoadBalancerId": {
          "Ref": "LoadBalancer"
        },
        "Protocol": "http",
        "Scheduler": "wrr"
      },
      "Type": "ALIYUN::SLB::Listener"
    },
    "Database": {
      "Properties": {
        "DBInstanceClass": "rds.mysql.t1.small",
        "DBInstanceDescription": "ros",
        "DBInstanceStorage": "50",
        "DBMappings": [
          {
            "CharacterSetName": "utf8",
            "DBName": {
              "Ref": "DBName"
            }
          }
        ],
        "Engine": "MySQL",
        "EngineVersion": "5.6",
        "MasterUserPassword": {
          "Ref": "DBPassword"
        },
        "MasterUsername": {
          "Ref": "DBUser"
        },
        "PreferredBackupPeriod": [
          "Monday",
          "Wednesday"
        ],
        "PreferredBackupTime": "23:00Z-24:00Z",
        "SecurityIPList": "0.0.0.0/0",
        "VPCId": {
          "Ref": "VpcId"
        },
        "VSwitchId": {
          "Ref": "VSwitchId"
        }
      },
      "Type": "ALIYUN::RDS::DBInstance"
    },
    "LoadBalancer": {
      "Properties": {
        "AddressType": "internet",
        "InternetChargeType": "paybytraffic",
        "LoadBalancerName": "createByRos"
      },
      "Type": "ALIYUN::SLB::LoadBalancer"
    },
    "WebServerGroup": {
      "DependsOn": "Database",
      "Properties": {
        "ImageId": {
          "Ref": "ImageId"
        },
        "InstanceType": "ecs.s3.large",
        "IoOptimized": {
          "Ref": "IoOptimized"
        },
        "MaxAmount": 3,
        "MinAmount": 3,
        "NetworkType": {
          "Ref": "NetworkType"
        },
        "Password": {
          "Ref": "InstancePassword"
        },
        "SecurityGroupId": {
          "Ref": "SecurityGroupId"
        },
        "SystemDiskCategory": "cloud_ssd",
        "UserData": {
          "Fn::Replace": [
            {
              "ros-notify": {
                "Fn::GetAtt": [
                  "WebServerGroupConditionHandle",
                  "CurlCli"
                ]
              }
            },
            {
              "Fn::Join": [
                "",
                [
                  "#!/bin/sh",
                  "\n",
                  "DatabaseUser=",
                  {
                    "Ref": "DBUser"
                  },
                  "\n",
                  "DatabasePwd=",
                  {
                    "Ref": "DBPassword"
                  },
                  "\n",
                  "DatabaseName=",
                  {
                    "Ref": "DBName"
                  },
                  "\n",
                  "DatabaseHost=",
                  {
                    "Fn::GetAtt": [
                      "Database",
                      "InnerConnectionString"
                    ]
                  },
                  "\n",
                  "WebRootPath='/var/www/html'\n",
                  "ApacheIndex='Options Indexes FollowSymLinks'\n",
                  "ApacheIndexReplace='Options -Indexes FollowSymLinks'\n",
                  "yum install -y curl httpd mysql-server php php-common php-mysql\n",
                  "yum install -y php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpc\n",
                  "chkconfig httpd on\n",
                  "wget http://wordpress.org/latest.tar.gz\n",
                  "tar -xzvf latest.tar.gz\n",
                  "sed -i \"s/database_name_here/$DatabaseName/\" wordpress/wp-config-sample.php\n",
                  "sed -i \"s/username_here/$DatabaseUser/\" wordpress/wp-config-sample.php\n",
                  "sed -i \"s/password_here/${DatabasePwd:-$DatabasePwdDef}/\" wordpress/wp-config-sample.php\n",
                  "sed -i \"s/localhost/$DatabaseHost/\" wordpress/wp-config-sample.php\n",
                  "mv wordpress/wp-config-sample.php wordpress/wp-config.php\n",
                  "cp -a wordpress/* $WebRootPath\n",
                  "rm -rf wordpress*\n",
                  "service httpd stop\n",
                  "usermod -d $WebRootPath apache &>/dev/null\n",
                  "chown apache:apache -R $WebRootPath\n",
                  "sed -i \"s/$ApacheIndex/$ApacheIndexReplace/\" /etc/httpd/conf/httpd.conf\n",
                  "service httpd start\n",
                  "ros-notify -d '{\"data\" : \"Install Wordpress\"}'\n"
                ]
              ]
            }
          ]
        },
        "VSwitchId": {
          "Ref": "VSwitchId"
        },
        "VpcId": {
          "Ref": "VpcId"
        }
      },
      "Type": "ALIYUN::ECS::InstanceGroup"
    },
    "WebServerGroupConditionHandle": {
      "Type": "ALIYUN::ROS::WaitConditionHandle"
    },
    "WebServerGroupWaitCondition": {
      "DependsOn": "WebServerGroup",
      "Properties": {
        "Count": 3,
        "Handle": {
          "Ref": "WebServerGroupConditionHandle"
        },
        "Timeout": 600
      },
      "Type": "ALIYUN::ROS::WaitCondition"
    }
  },
  "Outputs": {
    "CurlCli": {
      "Value": {
        "Fn::GetAtt": [
          "WebServerGroupConditionHandle",
          "CurlCli"
        ]
      }
    },
    "Data": {
      "Value": {
        "Fn::GetAtt": [
          "WebServerGroupWaitCondition",
          "Data"
        ]
      }
    },
    "InstanceIds": {
      "Description": "The instance id list of created ecs instance",
      "Value": {
        "Fn::GetAtt": [
          "WebServerGroup",
          "InstanceIds"
        ]
      }
    },
    "IpAddress": {
      "Description": "The ip address of the load balancer.",
      "Value": {
        "Fn::GetAtt": [
          "LoadBalancer",
          "IpAddress"
        ]
      }
    },
    "LoadBalancerId": {
      "Description": "The id of load balance created.",
      "Value": {
        "Fn::GetAtt": [
          "LoadBalancer",
          "LoadBalancerId"
        ]
      }
    }
  }
}

创建Stack资源

完成模板的创建后,根据 Stack 资源的创建步骤,输入必要的参数,点击 创建 按钮,即可完成资源的创建以及应用的构建。

访问WordPress

资源创建完成后,根据资源创建的输出结果,在浏览器中直接输入 [IpAddress] 或者 http://[IpAddress]/wp-admin/install.php 即可访问搭建好的 WordPress 应用。

时间: 2024-11-08 11:19:53

基于资源编排一键交付应用之基于WaitCondition的通知机制的相关文章

基于资源编排一键交付连接RDS的应用

本文是基于资源编排一键交付应用的进阶篇.众所周知,许多 Web 应用都会涉及对数据库的访问,但出于对数据的保护,通常我们在构建应用时会将应用本身和其对应的数据库分别部署在不同的机器上,以实现数据与应用相隔离.同时,为了降低应用构建的复杂度,阿里云提供了稳定可靠.可弹性伸缩的在线数据库服务 RDS,实现了对数据库的快速部署,从而使得对应用的部署工作主要集中在应用本身,所以本文将讲解如何基于资源编排实现应用与 RDS 的连接.基于资源编排一键交付连接 RDS 的应用的工作,主要包括以下两个方面: 熟

基于资源编排一键交付应用

资源编排可通过资源编排模板定义您需要创建的阿里云资源的组合,并依据您的配置来完成对资源的配置和一键销毁,快速方便的构建您的应用.本文将引导您如何基于资源编排服务快速构建一个应用.首先引入三个概念:Meta-Data.Cloud-Init 和 User-Data. Meta-DataMeta-Data 主要包括虚拟机自身的一些常用属性,如 hostname.网络配置信息.资源 InstanceId 等,其主要的形式为键值对.可以通过访问下面的地址查询 Meta-Data 信息 curl http:

基于资源编排和 Ansible 在 VPC 下快速交付应用

阿里云资源编排服务(ROS)为我们快速搭建和整合云计算资源环境提供一个低成本.标准化的方案.基于ROS提供的能力,我们所要做的就是将所需的资源以资源模板的形式进行定义,进而实现对所定义云资源的快速生产.除此之外,ROS 将应用交付和资源释放的过程也进行了简化. 用户基于 ROS 交付应用可以有两种途径:一种是通过 ROS 搭建资源环境,然后登陆云主机 ECS,手动实现对应用的部署和维护操作:另一种是利用文章基于资源编排一键交付应用中提到的 Cloud-Init 机制,实现对应用的一键交付.但是,

基于资源编排和 Ansible 在经典网络下快速交付应用

本文是基于资源编排和 Ansible 在 VPC 下快速交付应用的姊妹篇,即在经典网络下基于资源编排和 Ansible 实现对应用的快速交付. 在经典网络环境和 VPC 环境下交付应用的最大区别在于经典网络环境不需要 VPC.VSwitch等其他相关资源的支持,只需要 ECS 和 公网 IP 资源即可.除此之外,基于资源编排和 Ansible 在经典网络下快速交付应用的流程和步骤与在 VPC 下完全相同.下面本文仍以搭建 Redis 集群为例,展示在经典网络下快速交付应用. 基于 ROS 和 A

基于资源编排在专有网络环境下快速部署高可用的Dubbox服务(ZooKeeper版)

本文将介绍在专有网络VPC(Virtual Private Cloud)下,基于资源编排服务,快速部署高可用的Dubbox服务的过程.Dubbox服务采用的注册中心是ZooKeeper集群.做这件事情的意义在于:节约部署Dubbox的时间,降低部署Dubbox过程中出错的风险. ROS阿里云资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务.用户通过模板描述多个云计算资源的依赖关系.配置等,并自动完成所有资源的创建和配置,以达到自动化部署.运维

基于资源编排在经典网络环境下快速部署高可用的Dubbox服务(Redis版)

本文将介绍在经典网络环境下,基于资源编排快速部署高可用Dubbox服务的过程.做这件事情的意义在于:提供给开发者一套高可用的Dubbox服务框架,节约开发人员部署Dubbox服务的时间,并降低了部署Dubbox过程中出错的风险. ROS阿里云资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务.用户通过模板描述多个云计算资源的依赖关系.配置等,并自动完成所有资源的创建和配置,以达到自动化部署.运维等目的.编排模板同时也是一种标准化的资源和应用交付

资源编排(ROS)博文索引

资源编排ROS 是一种简单易用的云计算资源管理和自动化运维服务.用户通过模板描述多个云计算资源的依赖关系.配置等,并自动完成所有资源的创建和配置,以达到自动化部署.运维等目的. 了解更多 资源编排(ROS)之入门篇 Hello, 资源编排 不写代码也可以驾驭阿里云OpenAPI 基于阿里云构建自己的弹性应用 资源编排模板详解 通过资源编排创建一个ECS实例 资源编排最佳实践之入门篇:云服务器如何从1到N? 简单高效的云服务器单元化扩容方案 资源编排(ROS)之ECS篇 端到端构建VPC网络,安全

基于docker的持续交付系列(一):如何将app与docker整合并部署

初衷         最近docker可以说火得不要不要的,主要是源于docker对环境一致性的完美解决,极大提升了开发和运维的效率,很好地诠释了dev-ops的含义.通过以下几个链接,你可以对docker有更深入的理解: https://www.docker.com/ https://en.wikipedia.org/wiki/Docker_(software) http://www.infoq.com/cn/dockers/ 用到的工具         本着追随潮流的心态,让我们也尝试着去折

基于Docker的持续交付系列( 二):阿里云code帮你实现持续交付第一步

前言         在上一篇博文:基于docker的持续交付系列(一):如何将app与docker整合并部署中,我们对app与Docker的整合.部署进行了简单介绍, 但在实践中你会发现,每当你修改代码之后,都要手动push代码,build image,push image以及重新部署,整个流程走下来繁琐且耗时较长,给我们提倡的持续交付徒增了许多烦恼.在容器hub和阿里云code两个平台的合力之下,改进的第一步已经实现,让我们细细来看. 用到的工具         同样,我们还是使用到了下述几