使用资源编排 ROS 轻松部署高可用架构网站——以 WordPress 为例
介绍
WordPress是一款免费开源的网站内容管理系统(CMS),它可以帮助用户简单快捷地创建和管理自己的网站,包括博客、新闻网站、电子商务网站、社交网络等等。WordPress 有丰富的主题和插件库,使得用户可以轻松地为网站定制外观和功能。WordPress 的易用性和可扩展性使其成为世界上最受欢迎的网站建设工具之一。
资源编排服务(Resource Orchestration Service, ROS)是阿里云提供基于基础设施即代码(Infrastructure as Code, IaC) 理念的自动化部署服务,我们可以通过定义一个 JSON/YAML/Terraform
模板,轻松部署一套云上单点网站环境。比如部署一套 WordPress 环境,包括创建 VPC、ECS 等云资源,在 ECS 实例中安装 PHP、MySQL 和 WordPress 等。
架构说明
一个经典的高可用架构网站往往包含如下内容:
- 使用负载均衡 SLB 实例,承接整个网站的访问请求,并根据一定策略路由至后端服务器(ECS 实例)
- 一个绑定有 EIP 的 NAT 网关,可配置 SNAT 规则,使得后端服务器具备访问公网的能力
- 基于弹性伸缩 ESS 在多个不同的可用区构建出的服务器集群。使用 ESS 的好处在于:
- 能够根据一定策略(比如 CPU、内存利用率等)自动弹出或释放服务器
- 支持多可用区、多种实例规格,最大程度确保可成功创建出服务器
- 使用 ESS 生命周期挂钩机制可在弹出服务器后进行如程序配置等工作
- 使用 RDS 的高可用区 MySQL 数据库,确保数据库的高可用
整体的架构图如下:
在控制台上手动创建这些资源会非常耗时,无法轻易在其他地域部署相同的架构。而使用像 ROS 这样的 IaC 服务则可以高效地部署,且由于使用了标准化的模板,能够轻松在多个地域部署相同架构。
部署步骤
-
配置模板参数:选择负载均衡实例规格、两个不同的可用区、ECS 实例类型、RDS 实例类型、数据库密码
-
点击【下一步】,然后【创建】。部署完成后,点击资源栈的输出,即可看到 WordPress 服务的地址。点击链接即可体验 WordPress 的功能。
部署原理
我们可以看到通过 ROS 可以非常快捷地部署阿里云上的各种云资源(比如 VPC、VSwitch、ECS 实例等)和应用程序(比如 WordPress)。如果想了解是如何做到的,那么可以阅读此章节。
1、编写 ROS 模板。在如下模板中定义了:
Resources
:定义了 VPC、VSwitch、安全组&规则、ESS 相关资源(在 ScalingConfiguration 中配置了安装 WordPress 的命令)、RDS 相关资源、SLB 相关资源、NAT 网关& EIP。Parameters
:定义了常用的参数,比如可用区、ECS 实例类型。Outputs
:定义了自定义输出,比如 WordPress 服务的地址。
ROSTemplateFormatVersion: '2015-09-01'
Description:
en: Build elastic and highly available services by CLB, ESS, NAT, EIP, RDS and OOS. This
solution takes WordPress as an example. Two ECS instances are deployed in two
zones through ESS, and are automatically bound to the default server group of
CLB to provide external services. Among them, the ECS instance uses the RDS high-availability
instance as the database, and has public network access through the NAT gateway
bound to the EIP, and the CLB listens to the WordPress 80 port and perform health
checks. In addition, scaling rules are configured in ESS, and when the average
CPU value exceeds or falls below a certain threshold, the capacity will be automatically
scaled.
zh-cn:
基于CLB、ESS、NAT、EIP、RDS、OOS构建弹性高可用服务。本方案以WordPress为例,通过ESS在2个可用区分别部署2台ECS实例,并自动绑定到CLB的默认服务器组,从而对外提供服务。其中,ECS实例使用RDS高可用版实例作为数据库,通过绑定了EIP的NAT网关具备公网访问能力,CLB监听WordPress
80端口并进行健康检查。此外,ESS中配置了伸缩规则,当CPU平均值超过或低于特定阈值时,会自动扩缩容。
Parameters:
LoadBalancerSpec:
Type: String
Label:
en: LoadBalancer Specifications
zh-cn: 负载均衡实例规格
AssociationProperty: ALIYUN::SLB::Instance::InstanceType
Default: slb.s1.small
ZoneId1:
Type: String
Label:
en: VSwitch Availability Zone1
zh-cn: 可用区1
AssociationProperty: 'ALIYUN::ECS::Instance::ZoneId'
ZoneId2:
Type: String
Label:
en: VSwitch Availability Zone2
zh-cn: 可用区2
AssociationProperty: 'ALIYUN::ECS::Instance::ZoneId'
AssociationPropertyMetadata:
ExclusiveTo:
- ZoneId1
InstanceType1:
Type: String
Label:
en: Instance Type
zh-cn: 实例类型1
AssociationProperty: 'ALIYUN::ECS::Instance::InstanceType'
AssociationPropertyMetadata:
InstanceChargeType: PostPaid
SystemDiskCategory: cloud_essd
ZoneId: ${ZoneId1}
InstanceType2:
Type: String
Label:
en: Instance Type
zh-cn: 实例类型2
AssociationProperty: 'ALIYUN::ECS::Instance::InstanceType'
AssociationPropertyMetadata:
InstanceChargeType: PostPaid
SystemDiskCategory: cloud_essd
ZoneId: ${ZoneId2}
RdsInstanceClass:
Type: String
Label:
en: RDS Instance Class
zh-cn: RDS实例规格
AssociationProperty: ALIYUN::RDS::Instance::InstanceType
AssociationPropertyMetadata:
ZoneId: ${ZoneId1}
EngineVersion: "8.0"
Engine: MySQL
Category: HighAvailability
DBInstanceStorageType: cloud_essd
RdsDBPassword:
Type: String
Label:
en: RDS Database Account Password
zh-cn: RDS数据库账号密码
Description:
en: 'The password must be 8 to 32 characters in length and must contain at least
three of the following types: uppercase letters, lowercase letter, digits,
and special characters. Special characters include <span style="background:#E7E9EB;"><b>!@#$%^&*()_+-=</b></span>'
zh-cn: 必须包含三种及以上类型:大写字母、小写字母、数字、特殊符号。长度为8~32位。特殊字符包括<span style="background:#E7E9EB;"><b>!@#$%^&*()_+-=</b></span>
AllowedPattern:
^(?=.*[a-zA-Z])(?=.*[a-z0-9])(?=.*[a-z!@#$%^&*()_+=-])(?=.*[A-Z0-9])(?=.*[A-Z!@#$%^&*()_+=-])(?=.*[0-9!@#$%^&*()_+=-])[a-zA-Z0-9!@#$%^&*()_+=-]{8,32}$
NoEcho: true
CommonName:
Type: String
Default: ha
Resources:
Vpc:
Type: ALIYUN::ECS::VPC
Properties:
VpcName:
Fn::Sub: ${CommonName}-vpc
CidrBlock: 192.168.0.0/16
VSwitch1:
Type: ALIYUN::ECS::VSwitch
Properties:
ZoneId:
Ref: ZoneId1
VpcId:
Ref: Vpc
VSwitchName:
Fn::Sub: ${CommonName}-vsw-001
CidrBlock: 192.168.1.0/24
VSwitch2:
Type: ALIYUN::ECS::VSwitch
Properties:
ZoneId:
Ref: ZoneId2
VpcId:
Ref: Vpc
VSwitchName:
Fn::Sub: ${CommonName}-vsw-002
CidrBlock: 192.168.2.0/24
SecurityGroup:
Type: ALIYUN::ECS::SecurityGroup
Properties:
VpcId:
Ref: Vpc
SecurityGroupName:
Fn::Sub: ${CommonName}-sg
SecurityGroupIngress:
- PortRange: 80/80
Priority: 1
SourceCidrIp: 0.0.0.0/0
IpProtocol: tcp
NicType: internet
SecurityGroupEgress:
- PortRange: '-1/-1'
Priority: 1
IpProtocol: all
DestCidrIp: 0.0.0.0/0
NicType: internet
- PortRange: '-1/-1'
Priority: 1
IpProtocol: all
DestCidrIp: 0.0.0.0/0
NicType: intranet
ClbLoadBalancer:
Type: ALIYUN::SLB::LoadBalancer
Properties:
LoadBalancerName:
Fn::Sub: ${CommonName}-clb
PayType: PayOnDemand
AddressType: internet
LoadBalancerSpec:
Ref: LoadBalancerSpec
ClbListener:
Type: ALIYUN::SLB::Listener
Properties:
ListenerPort: 80
Bandwidth: 10
HealthCheck:
HttpCode: http_2xx,http_3xx,http_4xx,http_5xx
HealthCheckType: http
UnhealthyThreshold: 3
Timeout: 5
HealthyThreshold: 3
Port: 80
URI: /
Interval: 5
LoadBalancerId:
Ref: ClbLoadBalancer
BackendServerPort: 80
Protocol: http
RdsInstance:
Type: ALIYUN::RDS::DBInstance
Properties:
ZoneId:
Ref: ZoneId1
VpcId:
Ref: Vpc
VSwitchId:
Ref: VSwitch1
DBInstanceDescription:
Fn::Sub: ${CommonName}-rds-instance
Engine: MySQL
DBInstanceStorage: 100
EngineVersion: '8.0'
Category: HighAvailability
DBInstanceStorageType: cloud_essd
DBInstanceClass:
Ref: RdsInstanceClass
SecurityIPList:
Fn::Sub: ${VSwitch1.CidrBlock},${VSwitch2.CidrBlock}
PayType: Postpaid
RdsDatabase:
Type: ALIYUN::RDS::Database
Properties:
CharacterSetName: utf8mb4
DBInstanceId:
Ref: RdsInstance
DBDescription: wordpress
DBName: wordpress
RdsAccount:
Type: ALIYUN::RDS::Account
Properties:
AccountName: wp_admin
AccountType: Normal
AccountDescription: wordpress admin
AccountPassword:
Ref: RdsDBPassword
DBInstanceId:
Ref: RdsInstance
RdsAccountPrivilege:
Type: ALIYUN::RDS::AccountPrivilege
Properties:
AccountPrivilege: ReadWrite
DBInstanceId:
Ref: RdsInstance
DBName:
Ref: RdsDatabase
AccountName:
Ref: RdsAccount
NatGateway:
Type: ALIYUN::VPC::NatGateway
Properties:
VpcId:
Ref: Vpc
VSwitchId:
Ref: VSwitch1
NatGatewayName:
Fn::Sub: ${CommonName}-nat
InternetChargeType: PayByLcu
EipBindMode: NAT
NatEip:
Type: ALIYUN::VPC::EIP
Properties:
Name:
Fn::Sub: ${CommonName}-nat-eip
DeletionProtection: false
Isp: BGP
Bandwidth: 100
InternetChargeType: PayByTraffic
NatEipAssociation:
Type: ALIYUN::VPC::EIPAssociation
Properties:
InstanceId:
Ref: NatGateway
AllocationId:
Ref: NatEip
SnatEntry:
Type: ALIYUN::VPC::SnatEntry
Properties:
SnatEntryName: public-network-access-in-vpc
SnatTableId:
Fn::GetAtt:
- NatGateway
- SNatTableId
SnatIp:
Fn::GetAtt:
- NatEipAssociation
- EipAddress
SourceCIDR: 0.0.0.0/0
EssScalingGroup:
Type: ALIYUN::ESS::ScalingGroup
Properties:
VSwitchIds:
- Ref: VSwitch1
- Ref: VSwitch2
ScalingGroupName:
Fn::Sub: ${CommonName}-asg
RemovalPolicys:
- NewestInstance
MinSize: 2
MaxSize: 10
DefaultCooldown: 300
MultiAZPolicy: COMPOSABLE
AzBalance: true
LoadBalancerIds:
- Ref: ClbLoadBalancer
DependsOn: SecurityGroup
EssScalingConfiguration:
Type: ALIYUN::ESS::ScalingConfiguration
Properties:
SecurityGroupId:
Ref: SecurityGroup
ImageId: centos_7_9_x64_20G_alibase_20220727.vhd
ScalingConfigurationName:
Fn::Sub: ${CommonName}-asc
ScalingGroupId:
Ref: EssScalingGroup
InstanceTypes:
- Ref: InstanceType1
- Ref: InstanceType2
SystemDiskCategory: cloud_essd
SystemDiskSize: 200
InstanceName:
Fn::Sub: ${CommonName}-wordpress
UserData:
Fn::Sub: |-
#!/bin/bash
script=/root/setup-wordpress.sh
cat<<\EOF>$script
#!/bin/bash
if [ ! -f .ros.provision ]; then
echo "Name: ha-service" > .ros.provision
fi
name=$(grep "^Name:" .ros.provision | awk -F':' '{print $2}' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
if [[ "$name" != "ha-service" ]]; then
echo "ha-service installed, skip"
exit 0
fi
if ! grep -q "^Step1: Install Environment$" .ros.provision; then
echo "#########################"
echo "# Install Environment"
echo "#########################"
yum -y install httpd httpd-manual mod_ssl mod_perl mod_auth_mysql sysbench
systemctl start httpd
systemctl enable httpd
systemctl status httpd
yum install -y yum-utils epel-release http://rpms.remirepo.net/enterprise/remi-release-7.rpm
yum-config-manager --enable remi-php82
yum -y install php php-opcache php-mysqlnd php-pdo php-gd php-ldap php-odbc php-pear php-xml php-xmlrpc php-mbstring php-soap
echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php
systemctl restart httpd
echo "Step1: Install Environment" >> .ros.provision
else
echo "#########################"
echo "# Environment has been installed"
echo "#########################"
fi
if ! grep -q "^Step2: Install and Config WordPress$" .ros.provision; then
echo "################################"
echo "# Install and Config WordPress"
echo "################################"
wget https://ros-template-resources.oss-cn-beijing.aliyuncs.com/WordPress/wordpress-6.3.1-zh_CN.tar.gz
tar -xvf wordpress-6.3.1-zh_CN.tar.gz -C /var/www/html
mv /var/www/html/wordpress/* /var/www/html
chown -R apache:apache /var/www/html/wordpress
chmod -R 755 /var/www/html/wordpress
mv /var/www/html/wp-config-sample.php /var/www/html/wp-config.php
sed -i 's/localhost/${RdsInstance.InnerConnectionString}/' /var/www/html/wp-config.php
sed -i 's/username_here/${RdsAccount}/' /var/www/html/wp-config.php
sed -i 's/password_here/${RdsDBPassword}/' /var/www/html/wp-config.php
sed -i 's/database_name_here/${RdsDatabase}/' /var/www/html/wp-config.php
systemctl restart httpd
echo "Step2: Install and Config WordPress" >> .ros.provision
else
echo "#########################"
echo "# WordPress has been installed and configed"
echo "#########################"
fi
EOF
EssScalingGroupEnable:
Type: ALIYUN::ESS::ScalingGroupEnable
Properties:
ScalingGroupId:
Ref: EssScalingGroup
ScalingConfigurationId:
Ref: EssScalingConfiguration
ESSLifecycleOOSRunCommandRole:
Type: ALIYUN::RAM::Role
Properties:
RoleName: ESSLifecycleOOSRunCommandRole
IgnoreExisting: true
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service:
- oos.aliyuncs.com
Version: '1'
Policies:
- PolicyName: ESSLifecycleOOSRunCommandRolePolicy
PolicyDocument:
Statement:
- Action:
- ecs:DescribeInvocationResults
- ecs:DescribeInvocations
- ecs:RunCommand
Resource:
- '*'
Effect: Allow
- Action:
- ess:CompleteLifecycleAction
Resource:
- '*'
Effect: Allow
Version: '1'
ESSLifecycleHook:
Type: ALIYUN::ESS::LifecycleHook
Properties:
LifecycleHookName:
Fn::Sub: ${CommonName}-ash-scaleout
ScalingGroupId:
Ref: EssScalingGroup
LifecycleTransition: SCALE_OUT
NotificationArn:
Fn::Sub: acs:ess:${ALIYUN::Region}:${ALIYUN::TenantId}:oos/ACS-ESS-LifeCycleRunCommand
NotificationMetadata:
Fn::Sub: |-
{
"commandContent": "bash -x /root/setup-wordpress.sh",
"commandType": "RunShellScript",
"timeout": 1200,
"OOSAssumeRole": "${ESSLifecycleOOSRunCommandRole.RoleName}",
"regionId": "${!regionId}",
"instanceIds": "${!instanceIds}",
"lifecycleHookId": "${!lifecycleHookId}",
"rateControl": "{\"Mode\":\"Concurrency\",\"MaxErrors\":0,\"Concurrency\":10}",
"lifecycleActionToken": "${!lifecycleActionToken}"
}
DependsOn: SnatEntry
EssScalingRule:
Type: ALIYUN::ESS::ScalingRule
Properties:
ScalingRuleName:
Fn::Sub: ${CommonName}-asr-scaleout
ScalingGroupId:
Ref: EssScalingGroup
ScalingRuleType: TargetTrackingScalingRule
AdjustmentType: QuantityChangeInCapacity
AdjustmentValue: 1
MetricName: CpuUtilization
TargetValue: 80
ScaleOutEvaluationCount: 3
ScaleInEvaluationCount: 3
EstimatedInstanceWarmup: 0
Outputs:
Endpoint:
Description:
zh-cn: 公网IP地址
en: Public IP Addresses
Value:
Fn::Sub:
- http://${ServerAddress}
- ServerAddress:
Fn::GetAtt:
- ClbLoadBalancer
- IpAddress
Metadata:
ALIYUN::ROS::Interface:
ParameterGroups:
- Parameters:
- LoadBalancerSpec
Label:
default:
en: CLB Configuration
zh-cn: 负载均衡配置
- Parameters:
- ZoneId1
- ZoneId2
Label:
default:
en: Availability Zone
zh-cn: 可用区配置
- Parameters:
- InstanceType1
- InstanceType2
Label:
default:
en: Instance Configuration
zh-cn: 实例配置
- Parameters:
- RdsInstanceClass
- RdsDBPassword
Label:
default:
en: RDS Configuration
zh-cn: RDS配置
TemplateTags:
- 'acs:technical-solution:high-availability-architecture:弹性高可用服务'
Hidden:
- CommonName
2、在 ROS 控制台中使用此模板创建资源栈。ROS 会自动解析出模板中资源的依赖关系,按照资源依赖顺序创建云资源。如果资源间没有依赖,则会并发创建,从而提升部署效率。ROS 会把这次创建的所有资源存放到一个“资源栈”中,后续可以方便地管理这组资源集合。比如:
- 将新模板应用到这个“资源栈”中,从而更新里面的资源。
- 删除这个“资源栈”,从而把所有的资源删掉。
总结
基于 IaC 的理念,通过定义一个模板,使用 ROS 进行自动化部署,可以非常高效快捷地部署任意云资源和应用(比如 高可用架构的 WordPress 服务)。相比于手动部署或者通过 API、SDK 的部署方式,有着高效、稳定等诸多优势,也是服务上云的最佳实践。