Resource Orchestration Service服務ROS(Resource Orchestration Service)支援Count功能,用於大量建立資源,滿足複雜情境或者動態化情境的需求。本文為您介紹如何使用Count功能進行大規模部署。
擴容情境
本情境將使用Count功能,大量建立ECS(隨用隨付的ECS)。以建立3000個ECS為例,共計需要36183個資源,具體如下:
| 資源 | 數量 | 說明 |
| ALIYUN::ECS::NetworkInterface | 9000個 | 1個ECS要綁定3個彈性網卡(ENI)。 |
| ALIYUN::ECS::NetworkInterfaceAttachment | 9000個 | 該資源用於綁定彈性網卡(ENI)到Virtual Private Cloud類型執行個體上。 |
| ALIYUN::VPC::EIP | 9000個 | 1個彈性網卡(ENI)要綁定1個Elastic IP Address(EIP)。 |
| ALIYUN::VPC::EIPAssociation | 9000個 | 該資源用於綁定Elastic IP Address(EIP)。 |
| ALIYUN::ECS::InstanceGroup | 3個 | 1個ALIYUN::ECS::InstanceGroup最多包含1000個ECS資源。 |
| ALIYUN::VPC::CommonBandwidthPackage | 90個 | 100個Elastic IP Address(EIP)需要1個共用頻寬包。 |
| ALIYUN::VPC::CommonBandwidthPackageIp | 90個 | 該資源用於添加Elastic IP Address(EIP)到共用頻寬中。 |
因為每個資源棧最多容納300個資源,無法滿足需求,故需結合嵌套資源棧功能和Count功能設計解決方案。
方案一:基礎方案
首先,假定ECS數量為M,將資源劃分到子資源棧中,並使用父資源棧進行整合。
- 子資源棧A:用於建立ECS,綁定ENI和EIP。假定ECS數量為N,0<=N<=20,那麼子資源棧最多包含1+240=241個資源。
- 20個ECS:1個ALIYUN::ECS::InstanceGroup資源。
- ENI+EIP:20*12=240。
- 子資源棧B:用於建立共用頻寬包,關聯共用頻寬包和EIP。子資源棧中共用頻寬包數量為3,那麼子資源棧最多包含(1+1)*3=6個資源。
- 因為1個ECS綁定了3個EIP,對應於三條網路線路,所以放3個共用頻寬包。
- 因為100個EIP要加入1個共用頻寬包中,所以1個子資源棧B最多可以容納300個EIP。1個子資源棧A產生最多60個EIP,所以一個子資源棧B對應於5個子資源棧A。
- 父資源棧:主要用於整合子資源棧。
資源總數為150+30=180,能夠放入一個資源棧中。
- 子資源棧A:數量為(M+20-1)//20,最大值為150。因為每5個子資源棧A的輸出會作為1個子資源B的輸入。子資源棧A的個數需要對齊到5,這樣會包含一些N取值為0的子資源棧A。所以最終數量為((M+20-1)//20+4)//5*5,最大值仍為150。
- 子資源棧B:數量為子資源棧A的1/5,即((M+20-1)//20+4)//5,最大值為30。
其次,進行模板設計。
- 子資源棧A
- Eni[0]、Eni[1]、Eni[2]綁定到Servers[0],Eni[3]、Eni[4]、Eni[5]綁定到Servers[1],依此類推,Eni[3*i]、Eni[3*i+1]、Eni[3*i+2]綁定到Servers[i],0<=i<=N-1。
- Eip-ChinaTelecom[i]綁定到Eni[3*i]、Eip-ChinaUnicom[i]綁定到Eni[3*i+1]、Eip-ChinaMobile[i]綁定到Eni[3*i+2],0<=i<=N-1。這樣1個ECS就綁定了3個不同ISP的EIP。
說明 此處ISP使用ChinaTelecom、ChinaUnicom和ChinaMobile僅作為樣本。ROSTemplateFormatVersion: '2015-09-01' Parameters: NumberOfEcs: Type: Number MinValue: 0 MaxValue: 20 Conditions: NonEmpty: Fn::Not: Fn::Equals: - 0 - Ref: NumberOfEcs Resources: Servers: Type: ALIYUN::ECS::InstanceGroup Condition: NonEmpty Properties: MinAmount: Ref: NumberOfEcs MaxAmount: Ref: NumberOfEcs Eni: Type: ALIYUN::ECS::NetworkInterface Count: Fn::Calculate: - '{0}*3' - 0 - - Ref: NumberOfEcs Properties: null EniBinder: Type: ALIYUN::ECS::NetworkInterfaceAttachment Count: Fn::Calculate: - '{0}*3' - 0 - - Ref: NumberOfEcs Properties: InstanceId: Fn::Select: - Fn::Calculate: - '{0}//3' - 0 - Ref: ALIYUN::Index - Fn::GetAtt: - Servers - InstanceIds NetworkInterfaceId: Fn::Select: - Ref: ALIYUN::Index - Ref: Eni Eip-ChinaTelecom: Type: ALIYUN::VPC::EIP Count: Ref: NumberOfEcs Properties: Isp: ChinaTelecom Eip-ChinaTelecom-Binder: Type: ALIYUN::VPC::EIPAssociation Count: Ref: NumberOfEcs Properties: InstanceId: Fn::Select: - Fn::Calculate: - '{0}*3' - 0 - - Ref: ALIYUN::Index - Ref: Eni AllocationId: Fn::Select: - Ref: ALIYUN::Index - Ref: Eip-ChinaTelecom Eip-ChinaUnicom: Type: ALIYUN::VPC::EIP Count: Ref: NumberOfEcs Properties: Isp: ChinaUnicom Eip-ChinaUnicom-Binder: Type: ALIYUN::VPC::EIPAssociation Count: Ref: NumberOfEcs Properties: InstanceId: Fn::Select: - Fn::Calculate: - '{0}*3+1' - 0 - - Ref: ALIYUN::Index - Ref: Eni AllocationId: Fn::Select: - Ref: ALIYUN::Index - Ref: Eip-ChinaUnicom Eip-ChinaMobile: Type: ALIYUN::VPC::EIP Count: Ref: NumberOfEcs Properties: Isp: ChinaMobile Eip-ChinaMobile-Binder: Type: ALIYUN::VPC::EIPAssociation Count: Ref: NumberOfEcs Properties: InstanceId: Fn::Select: - Fn::Calculate: - '{0}*3+2' - 0 - - Ref: ALIYUN::Index - Ref: Eni AllocationId: Fn::Select: - Ref: ALIYUN::Index - Ref: Eip-ChinaMobile Outputs: Eips-ChinaTelecom: Value: Ref: Eip-ChinaTelecom Eips-ChinaUnicom: Value: Ref: Eip-ChinaUnicom Eips-ChinaMobile: Value: Ref: Eip-ChinaMobile - 子資源棧B
這是一個普通模板,ChinaTelecom的EIP加入ChinaTelecom的共用頻寬包,ChinaUnicom的EIP加入ChinaUnicom的共用頻寬包,ChinaMobile的EIP加入ChinaMobile的共用頻寬包。
說明 此處使用ChinaTelecom、ChinaUnicom和ChinaMobile僅作為樣本。ROSTemplateFormatVersion: '2015-09-01' Parameters: Eips-ChinaTelecom: Type: Json Eips-ChinaUnicom: Type: Json Eips-ChinaMobile: Type: Json Resources: CommonBandwidthPackage-ChinaTelecom: Type: ALIYUN::VPC::CommonBandwidthPackage Properties: null CommonBandwidthPackage-ChinaTelecom-IpBinder: Type: ALIYUN::VPC::CommonBandwidthPackageIp Properties: Eips: Ref: Eips-ChinaTelecom BandwidthPackageId: Ref: CommonBandwidthPackage-ChinaTelecom CommonBandwidthPackage-ChinaUnicom: Type: ALIYUN::VPC::CommonBandwidthPackage Properties: null CommonBandwidthPackage-ChinaUnicom-IpBinder: Type: ALIYUN::VPC::CommonBandwidthPackageIp Properties: Eips: Ref: Eips-ChinaUnicom BandwidthPackageId: Ref: CommonBandwidthPackage-ChinaUnicom CommonBandwidthPackage-ChinaMobile: Type: ALIYUN::VPC::CommonBandwidthPackage Properties: null CommonBandwidthPackage-ChinaMobile-IpBinder: Type: ALIYUN::VPC::CommonBandwidthPackageIp Properties: Eips: Ref: Eips-ChinaMobile BandwidthPackageId: Ref: CommonBandwidthPackage-ChinaMobile - 父資源棧嵌套資源棧要求模板通過URL提供,將兩個子資源棧的模板加入OSS Bucket。假定子資源棧A的模板地址為oss://templates/resourses-a,子資源棧B的模板地址為oss://templates/resourses-b。
- 資源A為子資源棧A。
NumberOfEcs的取值運算式為(1-(({1}+1)//(({0}+19)//20+1)+999)//1000)*((({0}-20*{1})//20+{0})//({0}+1)*(20-({0}-{0}//20*20))+({0}-{0}//20*20)),計算方法如下:
- 限制條件:Fn::Calculate只能使用加、減、乘、浮點除(/)、整數除(//)五種運算。
- 取值規則:從第0組開始,每次從M中取20個;如果不足20個,則取剩下的所有;如果已取完,則為0。通過舉例瞭解子資源棧A中NumberOfEcs參數應該的取值:
- 當M=1時,A.Count為5,NumberOfEcs依次為1、0、0、0、0。
- 當M=20時,A.Count為5,NumberOfEcs依次為20、0、0、0、0。
- 當M=99時,A.Count為5,NumberOfEcs依次為20、20、20、20、19。
- 當M=100時,A.Count為5,NumberOfEcs依次為20、20、20、20、20。
- 當M=101時,A.Count為10,NumberOfEcs依次為20、20、20、20、20、1、0、0、0、0。
- 數學技巧:
- 技巧1:如何將序列 0, 1, 2, ... 轉換成 0, 1, 1, ...?
使用 F(x, t)=(x + t) // (t + 1),其中t>=Max(x)。當x為0時,F(0)=0;當x>=1,F(x)>=1,而 F(x) = (x + t) // (t + 1) <= (x + Max(x)) // (Max(x) + 1) <= (Max(x) + Max(x)) // (Max(x) + 1) < 2,所以F(x)=1。
- 技巧2:如何將0、1的序列轉換成P、Q的序列?
令G(x, P, Q)=x*(Q-P)+P,x取值為0或1。f(0)=P,f(1)=Q。
- 技巧3:如果計算M和N的餘數?
M%N = M-M//N*N。
- 技巧1:如何將序列 0, 1, 2, ... 轉換成 0, 1, 1, ...?
- 運算式:
定義N=20,U=(M+N-1)//N,V=(U+4)//5*5,則U表示對齊到5前子資源棧A的數量,V表示對齊到5後子資源棧A的數量,也就是實際數量。
當M=101時,觀察f(i)=(M-N*i)//N(i為編號,對應於ALIYUN::Index偽參數,0<=i<V,U=6,V=10),其值依次為f(0)=5、f(1)=4、f(2)=3、f(3)=2、f(4)=1、f(5)=0、f(6)=-1、f(7)=-2、f(8)=-3、f(9)=-4。
- 0<=i<U
序列為5、4、3、2、1、0,利用技巧1,可以將其轉換為1、1、1、1、1、0。
令t = M >= Max(f(i)),則g(i) = F(f(i), M) = ((M-N*i)//N + M) // (M+1),這是序列函數。利用技巧2,可以將序列轉換為20、20、20、20、20、1,也就是0<=i<U時NumberOfEcs的取值。
令P=M%N,Q=N, h(i) = G(g(i), M%N, N) = (((M-N*i)//N + M) // (M+1)) * (N - (M-M//N*N)) + (M-M//N*N)。
- i>=U
0<=i<U時,值為1;i>=U時,值為0。觀察k(i)=(i+1)//(U+1),當0<=i<U時,值為0;i>=U時,值>=1。
提示1,即可轉換成0和1的序列。我們估算下k(i)的最大值:Max(k(i))=V//(U+1)=(U+4)//5*5//(U+1)<=4,t取4即可,這裡我們取999。
新序列p(i) = 1- F(k(i), 999) = 1 - ((i+1)//((M+N-1)//N+1) + 999) // 1000。當0<=i<U時,值為1;i>=U時,值0。
最終的序列q(i) = p(i) * h(i) = (1 - ((i+1)//((M+N-1)//N+1) + 999) // 1000) * (((M-N*i)//N + M) // (M+1)) * (N - (M-M//N*N)) + (M-M//N*N)。令M={0}, i={1}, N=20代入q(i)得到運算式:(1-(({1}+1)//(({0}+19)//20+1)+999)//1000)*((({0}-20*{1})//20+{0})//({0}+1)*(20-({0}-{0}//20*20))+({0}-{0}//20*20))。
- 0<=i<U
- 資源B為子資源棧B。
子資源棧B[i]會將子資源棧A[5*i]、A[5*i+1]、A[5*i+2]、A[5*i+3]、A[5*i+4]的輸出拼接在一起作為輸入。
- 資源A為子資源棧A。
ROSTemplateFormatVersion: '2015-09-01'
Parameters:
NumberOfEcs:
Type: Number
MinValue: 0
Resources:
A:
Type: ALIYUN::ROS::Stack
Count:
Fn::Calculate:
- (({0}+19)//20+4)//5*5
- 0
- - Ref: NumberOfEcs
Properties:
TemplateURL: oss://templates/resourses-a
Parameters:
NumberOfEcs:
Fn::Calculate:
- (1-(({1}+1)//(({0}+19)//20+1)+999)//1000)*((({0}-20*{1})//20+{0})//({0}+1)*(20-({0}-{0}//20*20))+({0}-{0}//20*20))
- 0
- - Ref: NumberOfEcs
- Ref: ALIYUN::Index
B:
Type: ALIYUN::ROS::Stack
Count:
Fn::Calculate:
- (({0}+19)//20+4)//5
- 0
- - Ref: NumberOfEcs
Properties:
TemplateURL: oss://templates/resourses-b
Parameters:
Eips-ChinaTelecom:
Fn::ListMerge:
- Fn::Select:
- Fn::Calculate:
- 5*{0}
- 0
- - Ref: ALIYUN::Index
- Fn::GetAtt:
- A
- Eips-ChinaTelecom
- Fn::Select:
- Fn::Calculate:
- 5*{0}+1
- 0
- - Ref: ALIYUN::Index
- Fn::GetAtt:
- A
- Eips-ChinaTelecom
- Fn::Select:
- Fn::Calculate:
- 5*{0}+2
- 0
- - Ref: ALIYUN::Index
- Fn::GetAtt:
- A
- Eips-ChinaTelecom
- Fn::Select:
- Fn::Calculate:
- 5*{0}+3
- 0
- - Ref: ALIYUN::Index
- Fn::GetAtt:
- A
- Eips-ChinaTelecom
- Fn::Select:
- Fn::Calculate:
- 5*{0}+4
- 0
- - Ref: ALIYUN::Index
- Fn::GetAtt:
- A
- Eips-ChinaTelecom5個資源棧A對應一個資源棧B,5是固定的,無法實現動態。因為Fn::Select已經支援slice選取功能,通過Start:Stop:Step的方式從列表中選取多個元素,所以可以實現動態化。
結合宙斯情境,使用Fn::Min簡化運算式。新模板如下:
ROSTemplateFormatVersion: '2015-09-01'
Parameters:
NumberOfEcs:
Type: Number
MinValue: 0
Resources:
A:
Type: ALIYUN::ROS::Stack
Count:
Fn::Calculate:
- ({0}+19)//20
- 0
- - Ref: NumberOfEcs
Properties:
TemplateURL: oss://templates/resourses-a
Parameters:
NumberOfEcs:
Fn::Min:
- 20
- Fn::Calculate:
- '{0}-{1}*20'
- 0
- - Ref: NumberOfEcs
- Ref: ALIYUN::Index
B:
Type: ALIYUN::ROS::Stack
Count:
Fn::Calculate:
- (({0}+19)//20+4)//5
- 0
- - Ref: NumberOfEcs
Properties:
TemplateURL: oss://templates/resourses-b
Parameters:
Eips-ChinaTelecom:
Fn::ListMerge:
- Fn::Select:
- Fn::Replace:
- Start:
Fn::Calculate:
- 5*{0}
- 0
- - Ref: ALIYUN::Index
Stop:
Fn::Calculate:
- 5*({0}+1)
- 0
- - Ref: ALIYUN::Index
- Start:Stop
- Fn::GetAtt:
- A
- Eips-ChinaTelecom方案二:最佳化方案
將資源進行重新劃分:
- 子資源棧A:將ALIYUN::VPC::CommonBandwidthPackageIp加入子資源棧A中。
- 子資源棧B:將所有ALIYUN::VPC::CommonBandwidthPackage加入一個子資源棧B中。子資源棧B本身的數量不再需要使用Count,但裡面的資源需要使用Count。子資源棧B輸出建立的共用頻寬包ID列表,並傳遞給子資源棧A。
將所有參數動態化:
- 將子資源A的ECS數量最大值20,變成參數MaxNumberOfEcsPerStack。
- 將100個EIP存放的共用頻寬包數量1,變成參數MaxNumberOfEipPerBandwidthPackage。
- 在父資源中建立子資源A時,傳遞EipIndexOffset=ALIYUN::Index * MaxNumberOfEcsPerStack。
- 在子資源棧A中通過計算構建動態數量的ALIYUN::VPC::CommonBandwidthPackageIp,將EIP分組後綁定到不同的共用頻寬包。
最佳化後模板如下:
- 子資源棧A
令A=EipIndexOffset,B=NumberOfEcs,C=MaxNumberOfEipPerBandwidthPackage,i=ALIYUN::Index:A//C 為Stack內第一個EIP要綁定的共用頻寬包的編號。(A+B-1)//C 為Stack內最後一個EIP要綁定的共用頻寬包的編號。所以CommonBandwidthPackage-ChinaTelecom-IpBinder.Count = ((A+B-1)//C-A//C)+1。
所以CommonBandwidthPackage-ChinaTelecom-IpBinder.BandwidthPackageId=CommonBandwidthPackage-ChinaTelecom-List[A//C+i]。組內的Eip-ChinaTelecom該如何分散到CommonBandwidthPackage-ChinaTelecom-IpBinder,我們從全域的角度看:
全域編號在 [ (A//C+i)*C, (A//C+i+1)*C ) 之間的CommonBandwidthPackage-ChinaTelecom-IpBinder[ (A//C+i) ] 中。將全域編號轉換成局部編號,只需要減去位移,即 [ (A//C+i)*C - A, (A//C+i+1)*C - A ) 。但這個範圍必須限制到 [0, B]中,所以結果為[ Max((A//C+i)*C - A, 0), Min((A//C+i+1)*C - A, B) ]。
ROSTemplateFormatVersion: '2015-09-01' Parameters: NumberOfEcs: Type: Number MinValue: 1 MaxNumberOfEipPerBandwidthPackage: Type: Number MinValue: 1 EipIndexOffset: Type: Number CommonBandwidthPackage-ChinaTelecom-List: Type: Json CommonBandwidthPackage-ChinaUnicom-List: Type: Json CommonBandwidthPackage-ChinaMobile-List: Type: Json Conditions: NonEmpty: Fn::Not: Fn::Equals: - 0 - Ref: NumberOfEcs Resources: CommonBandwidthPackage-ChinaTelecom-IpBinder: Type: ALIYUN::VPC::CommonBandwidthPackageIp Count: Fn::Calculate: - (({0}+{1}-1)//{2}-{0}//{2})+1 - 0 - - Ref: EipIndexOffset - Ref: NumberOfEcs - Ref: MaxNumberOfEipPerBandwidthPackage Properties: Eips: Fn::Select: - Fn::Replace: - Start: Fn::Max: - 0 - Fn::Calculate: - ({0}//{2}+{1})*{2}-{0} - 0 - - Ref: EipIndexOffset - Ref: ALIYUN::Index - Ref: MaxNumberOfEipPerBandwidthPackage Stop: Fn::Min: - Ref: NumberOfEcs - Fn::Calculate: - ({0}//{2}+{1}+1)*{2}-{0} - 0 - - Ref: EipIndexOffset - Ref: ALIYUN::Index - Ref: MaxNumberOfEipPerBandwidthPackage - Start:Stop - Ref: Eip-ChinaTelecom BandwidthPackageId: Fn::Select: - Fn::Calculate: - '{0}//{2}+{1}' - 0 - - Ref: EipIndexOffset - Ref: ALIYUN::Index - Ref: MaxNumberOfEipPerBandwidthPackage - Ref: CommonBandwidthPackage-ChinaTelecom-List - 子資源棧B
ROSTemplateFormatVersion: '2015-09-01' Parameters: Count: Type: Number Resources: CommonBandwidthPackage-ChinaTelecom: Type: ALIYUN::VPC::CommonBandwidthPackage Count: Ref: Count Properties: null CommonBandwidthPackage-ChinaUnicom: Type: ALIYUN::VPC::CommonBandwidthPackage Count: Ref: Count Properties: null CommonBandwidthPackage-ChinaMobile: Type: ALIYUN::VPC::CommonBandwidthPackage Count: Ref: Count Properties: null Outputs: CommonBandwidthPackage-ChinaTelecom-List: Value: Ref: CommonBandwidthPackage-ChinaTelecom CommonBandwidthPackage-ChinaUnicom-List: Value: Ref: CommonBandwidthPackage-ChinaUnicom CommonBandwidthPackage-ChinaMobile-List: Value: Ref: CommonBandwidthPackage-ChinaMobile - 父資源棧
ROSTemplateFormatVersion: '2015-09-01' Parameters: NumberOfEcs: Type: Number MinValue: 0 MaxNumberOfEcsPerStack: Type: Number MinValue: 1 Default: 20 MaxNumberOfEipPerBandwidthPackage: Type: Number MinValue: 1 Default: 100 Resources: B: Type: ALIYUN::ROS::Stack Properties: TemplateURL: oss://templates/resourses-b Parameters: Count: Fn::Calculate: - ({0}+{1}-1)//{1} - 0 - - Ref: NumberOfEcs - Ref: MaxNumberOfEipPerBandwidthPackage A: Type: ALIYUN::ROS::Stack Count: Fn::Calculate: - ({0}+{1}-1)//{1} - 0 - - Ref: NumberOfEcs - Ref: MaxNumberOfEcsPerStack Properties: TemplateURL: oss://templates/resourses-a Parameters: NumberOfEcs: Fn::Min: - Ref: MaxNumberOfEcsPerStack - Fn::Calculate: - '{0}-{1}*{2}' - 0 - - Ref: NumberOfEcs - Ref: ALIYUN::Index - Ref: MaxNumberOfEcsPerStack MaxNumberOfEipPerBandwidthPackage: Ref: MaxNumberOfEipPerBandwidthPackage EipIndexOffset: Fn::Calculate: - '{0}*{1}' - 0 - - Ref: MaxNumberOfEcsPerStack - Ref: ALIYUN::Index CommonBandwidthPackage-ChinaTelecom-List: Fn::GetAtt: - B - CommonBandwidthPackage-ChinaTelecom-List CommonBandwidthPackage-ChinaUnicom-List: Fn::GetAtt: - B - CommonBandwidthPackage-ChinaUnicom-List CommonBandwidthPackage-ChinaMobile-List: Fn::GetAtt: - B - CommonBandwidthPackage-ChinaMobile-List