通过阿里云的资源编排服务,ROS,可以很方便的创建一组资源。但是创建资源只是第一步,接下来我们需要把应用部署上去,有两种方式供你使用,ROS 的 UserData 脚本或者集成配置管理工具,例如 Chef 或Puppet。本文将重点讲述ROS 集成 Chef 实现应用的部署与配置。关于使用 UserData 脚本的方式,大家可以参考《基于资源编排一键交付连接RDS的应用》。
通过 Chef,你可以自动实现在 ECS 上的部署配置应用而不用手动构建各种脚本。通过把 Chef 和 ROS 集成,这样能更方便的把生产资源和部署应用集成到一块,并且通过ROS的模板,可以实现随时随地无限次的搭建相同应用环境,很轻松的把搭建环境集成到 DevOps 中去。
本文将以部署 WordPress 为例,讲解如何通过 ROS 去构建一个高可用的 Web Server 环境,最后通过执行 Chef 的 Recipe 在高可用的 Web Server 基础设施环境中安装配置 WordPress 。通过最终的这个 ROS模板,你可以简洁快速的获取一个 WordPress 应用。当然你还可以通过 Git 或 SVN 管理这个 ROS 的模板,实现对自己 WordPress 应用环境的版本控制。
ROS 安装部署 WordPress 架构概览
此模板讲创建一个高可用的带有负载均衡能力的 WordPress 环境,并通过阿里云的 RDS 提供后端的数据存储。基本的架构如下图所示:
在上面的架构图里面,通过 ROS 的 ALIYUN::ECS::InstanceGroup 创建多个 ECS 节点,这样就可以保证 WordPress 有多个实例,提高 WordPress 的高可用性。在 ECS 的前面部署了一个 SLB, 来保证均衡后端 ECS 的负载,给用户暴露唯一的 WordPress 访问地址,同时能够在添加或减少后端 ECS 服务器的时候,用户无感知。SLB 通过 ALIYUN::SLB::LoadBalancer 创建。通过 ALIYUN::SLB::Listener 配置SLB监听那些端口, ALIYUN::SLB::BackendServerAttachment 把后端服务器 ECS 加入到SLB监听列表中。通过使用 RDS 提供 WordPress 的后端数据存储能力。ROS 通过 ALIYUN::RDS::DBInstance 创建 RDS 实例配置数据库。以上所有的资源都部署在一个安全组里面,通过安全组控制数据出入规则,提高安全性。
最终,当 ECS 实例启动的时候,ROS 通过利用 Chef 的本地模式安装和配置 WordPress。 Chef 的本地模式是使用本地的 Chef 仓库来管理 cookbook 而不用通过 Chef Server。
WordPress 一键部署
点击一键部署后,默认会在华北2 region 部署 WordPress。 如果你需要调整 region,请点击页面右下角的【上一步】,然后重新选择 region,接着点击【下一步】,你只需要填入如下图中必填的信息或者根据你的需求调整信息后,点击【创建】按钮就可以部署一套 WordPress 高可用环境。
模板详解
创建 VPC 网络
在本例中,所有资源的都处于VPC网络下,保证网络的隔离性和安全性。为了保证 ECS 能够访问外网,获取到 Chef 的安装包,下载到 WordPress 的 cookbook,我们配置了 VPC 的 SNAT 网关。请参考《新玩法,ROS帮你一键搭建NatGateway让VPC与Internet的互访》,了解如何详细的配置你的 VPC 网络。
"SNatEntry": {
"Type": "ALIYUN::ECS::SNatEntry",
"DependsOn": "WPLoadBalancer",
"Properties": {
"SNatIp": {
"Fn::Select": [
"0",
{
"Fn::GetAtt": [
"NatGateway",
"BandwidthPackageIps"
]
}
]
},
"SourceVSwitchId": {
"Fn::GetAtt": [
"VSwitch",
"VSwitchId"
]
},
"SNatTableId": {
"Fn::GetAtt": [
"NatGateway",
"SNatTableId"
]
}
}
},
"NatGateway": {
"Type": "ALIYUN::ECS::NatGateway",
"Properties": {
"Spec": "Small",
"NatGatewayName": "NatGateway",
"BandwidthPackage": [
{
"IpCount": 1,
"Bandwidth": 5
}
],
"VSwitchId": {
"Ref": "VSwitch"
},
"VpcId": {
"Fn::GetAtt": [
"Vpc",
"VpcId"
]
}
}
},
"Vpc": {
"Type": "ALIYUN::ECS::VPC",
"Properties": {
"CidrBlock": "192.168.0.0/16"
}
},
"VSwitch": {
"Type": "ALIYUN::ECS::VSwitch",
"Properties": {
"CidrBlock": "192.168.33.0/24",
"ZoneId": {
"Fn::Select": [
"0",
{
"Fn::GetAZs": {
"Ref": "ALIYUN::Region"
}
}
]
},
"VpcId": {
"Fn::GetAtt": [
"Vpc",
"VpcId"
]
}
}
}
创建安全组
在本例中,所有的ECS都加入到一个默认安全组。同时,给安全组资源配置允许外部用户可通过80和22端口访问 WordPress 部署环境。
"DefaultSecurityGroup": {
"Type": "ALIYUN::ECS::SecurityGroup",
"Properties": {
"Description": "DDC default security group",
"SecurityGroupIngress": [
{
"SourceCidrIp": "0.0.0.0/0",
"IpProtocol": "tcp",
"NicType": "intranet",
"PortRange": "22/22"
},
{
"SourceCidrIp": "0.0.0.0/0",
"IpProtocol": "tcp",
"NicType": "intranet",
"PortRange": "443/443"
},
{
"SourceCidrIp": "0.0.0.0/0",
"IpProtocol": "tcp",
"NicType": "intranet",
"PortRange": "80/80"
}
],
"SecurityGroupEgress": [
{
"IpProtocol": "all",
"DestCidrIp": "0.0.0.0/0",
"NicType": "intranet",
"PortRange": "-1/-1",
"Priority": 1
}
],
"VpcId": {
"Ref": "Vpc"
}
}
}
创建 ECS 实例
本例中,创建了两种 ECS 实例,一种是用 ALIYUN::ECS::InstanceGroup 创建用来部署 WordPress 的 ECS实例,这个资源可以通过 MaxAmount 来指定一次创建多少台 ECS 实例。一个是用 ALIYUN::ECS::Instance 创建一台运维使用的跳板机。跳板机也部署在相同的 VPC 网络中,但是给跳板机分配了公网 IP。
"WPEcsInstance": {
"Type": "ALIYUN::ECS::InstanceGroup",
"DependsOn": "SNatEntry",
"Properties": {
"IoOptimized": {
"Ref": "WPEcsIoOptimized"
},
"ImageId": {
"Ref": "WPEcsImageId"
},
"SecurityGroupId": {
"Fn::GetAtt": [
"DefaultSecurityGroup",
"SecurityGroupId"
]
},
"Password": {
"Ref": "WPEcsInstancePassword"
},
"MinAmount": {
"Ref": "WPEcsMaxAmount"
},
"AllocatePublicIP": "false",
"SystemDiskCategory": {
"Ref": "WPEcsSystemDiskCategory"
},
"UserData": {
"Fn::Replace": [
{
"ros-notify": {
"Fn::GetAtt": [
"WPEcsConditionHandle",
"CurlCli"
]
}
},
{
"Fn::Join": [
"",
[
"#!/bin/sh\n",
"apt-get update\n",
"apt-get install -y rails\n",
"apt-get install -y unzip\n",
"\n",
"wget -P /tmp http://ros-om-dependence.oss-cn-shanghai.aliyuncs.com/chef-ubuntu-64/chef_12.18.31-1_amd64.deb\n",
"dpkg -i /tmp/chef_12.18.31-1_amd64.deb\n",
"wget -P /tmp http://ros-om-dependence.oss-cn-shanghai.aliyuncs.com/chef-ubuntu-64/chefdk_1.2.22-1_amd64.deb\n",
"dpkg -i /tmp/chefdk_1.2.22-1_amd64.deb\n",
"\n",
"mkdir -p /var/chef/chef-repo/.chef\n",
"# chef local repo setting\n",
"# wget -P /tmp http://github.com/opscode/chef-repo/tarball/master/chef-boneyard-chef-repo-605eeda.tar.gz\n",
"wget -P /tmp http://ros-om-dependence.oss-cn-shanghai.aliyuncs.com/chef-ubuntu-64/chef-boneyard-chef-repo-605eeda.tar.gz\n",
"tar -xzf /tmp/chef-boneyard-chef-repo-605eeda.tar.gz -C /var/chef/chef-repo\n",
"cp -rf /var/chef/chef-repo/chef-boneyard-chef-repo-605eeda/* /var/chef/chef-repo\n",
"rm -rf /var/chef/chef-repo/chef-boneyard-chef-repo-605eeda\n",
"echo install chef > /tmp/log\n",
"# set default knife.rb\n",
"echo \"cookbook_path [ '/var/chef/chef-repo/cookbooks' ]\" > /var/chef/chef-repo/.chef/knife.rb\n",
"echo \"node_path [ '/var/chef/chef-repo/nodes' ]\" >> /var/chef/chef-repo/.chef/knife.rb\n",
"\n",
"# set default client.rb\n",
"echo \"cookbook_path [ '/var/chef/chef-repo/cookbooks' ]\" > /var/chef/chef-repo/.chef/client.rb\n",
"echo \"node_path [ '/var/chef/chef-repo/nodes' ]\" >> /var/chef/chef-repo/.chef/client.rb\n",
"\n",
"# set init chef conf\n",
"orig_home=$HOME\n",
"export HOME='/var/chef'\n",
"\n",
"# create node list\n",
"cd /var/chef/chef-repo\n",
"chef-client -z -c /var/chef/chef-repo/.chef/client.rb\n",
"echo config chef repo >> /tmp/log\n",
"\n",
"\n",
"# download wordpress cookbook\n",
"wget -P /tmp http://ros-om-dependence.oss-cn-shanghai.aliyuncs.com/chef-ubuntu-64/wordpress.tar.gz\n",
"tar -xzf /tmp/wordpress.tar.gz -C /var/chef/chef-repo/cookbooks\n",
"\n",
"# set default knife.rb\n",
"echo \"cookbook_path [ '/var/chef/chef-repo/cookbooks/wordpress/berks-cookbooks' ]\" > /var/chef/chef-repo/.chef/knife.rb\n",
"echo \"node_path [ '/var/chef/chef-repo/nodes' ]\" >> /var/chef/chef-repo/.chef/knife.rb\n",
"\n",
"# set default client.rb\n",
"echo \"cookbook_path [ '/var/chef/chef-repo/cookbooks/wordpress/berks-cookbooks' ]\" > /var/chef/chef-repo/.chef/client.rb\n",
"echo \"node_path [ '/var/chef/chef-repo/nodes' ]\" >> /var/chef/chef-repo/.chef/client.rb\n",
"echo config wordpress cookbook >> /tmp/log\n",
"\n",
"\n",
"# set wordpress datebase conf\n",
"echo \"normal['wordpress']['db']['pass'] = '",
{
"Ref": "WPDBPassword"
},
"'\" > /var/chef/chef-repo/cookbooks/wordpress/berks-cookbooks/wordpress/attributes/aliyun_rds_config.rb\n",
"echo \"normal['wordpress']['db']['user'] = '",
{
"Ref": "WPDBUser"
},
"'\" >> /var/chef/chef-repo/cookbooks/wordpress/berks-cookbooks/wordpress/attributes/aliyun_rds_config.rb\n",
"echo \"normal['wordpress']['db']['host'] = '",
{
"Fn::GetAtt": [
"WPDBDatabase",
"InnerConnectionString"
]
},
"'\" >> /var/chef/chef-repo/cookbooks/wordpress/berks-cookbooks/wordpress/attributes/aliyun_rds_config.rb\n",
"echo \"normal['wordpress']['db']['name'] = '",
{
"Ref": "WPDBName"
},
"'\" >> /var/chef/chef-repo/cookbooks/wordpress/berks-cookbooks/wordpress/attributes/aliyun_rds_config.rb\n",
"\n",
"\n",
"echo run install wordpress cookbook >> /tmp/log\n",
"knife node run_list add -z `knife node list -z` recipe[wordpress]\n",
"\n",
"chef-client -z -c /var/chef/chef-repo/.chef/client.rb | tee -a /tmp/chef_runing_log\n",
"ros-notify\n"
]
]
}
]
},
"MaxAmount": {
"Ref": "WPEcsMaxAmount"
},
"VSwitchId": {
"Ref": "VSwitch"
},
"VpcId": {
"Ref": "Vpc"
},
"InstanceType": {
"Ref": "WPEcsInstanceType"
}
}
},
"JumpHost": {
"Type": "ALIYUN::ECS::Instance",
"Properties": {
"IoOptimized": "optimized",
"ImageId": {
"Ref": "WPEcsImageId"
},
"SecurityGroupId": {
"Fn::GetAtt": [
"DefaultSecurityGroup",
"SecurityGroupId"
]
},
"Password": {
"Ref": "WPEcsInstancePassword"
},
"AllocatePublicIP": "true",
"SystemDiskCategory": "cloud_efficiency",
"VSwitchId": {
"Ref": "VSwitch"
},
"VpcId": {
"Ref": "Vpc"
},
"InstanceType": {
"Ref": "WPEcsInstanceType"
}
}
},
ROS 通过 UserData 集成了 Chef, 首先安装 Chef 所依赖的 ruby 环境。由于网络原因,我们把 Chef 的安装包,本地仓库和相应的 cookbook 都做了镜像,方便大家在国内访问。然后配置 Chef 的 knife.rb 和 client.rb 文件指向本地仓库中的 cookbook。调用 chef-client -z 指定本地模式生成 node list。然后添加RDS实例中的数据库名称,用户名,密码和机器名称到 WordPress cookbook 的 attributes 中,保证 Chef 能正确配置 WordPress 数据库属性。最后调用下面的 Chef 命令,在本机上安装配置 WordPress 实例。
knife node run_list add -z `knife node list -z` recipe[wordpress]
chef-client -z -c /var/chef/chef-repo/.chef/client.rb
SLB
要高可用的环境,SLB 是必不可少的一个资源。通过 SLB 即可均衡分配请求到后端服务,更可以做用户无感知地增加,减少或者替换有问题的后端 ECS 实例。同时,通过 SLB 可以给用户提供一个唯一的WordPress 访问地址。本例中,创建 SLB 后,配置了 SLB 监听后端的 ECS 的80端口。
"WPLoadBalancerListener80": {
"Type": "ALIYUN::SLB::Listener",
"DependsOn": "WPLoadBalancer",
"Properties": {
"Persistence": {
"StickySession": "on",
"PersistenceTimeout": 600
},
"HealthCheck": {
"Timeout": "2",
"Port": "80",
"Interval": "5",
"HealthyThreshold": "2",
"UnhealthyThreshold": "4"
},
"LoadBalancerId": {
"Ref": "WPLoadBalancer"
},
"BackendServerPort": "80",
"Protocol": "tcp",
"Bandwidth": -1,
"ListenerPort": "80"
}
},
"WPSLBAttachment": {
"Type": "ALIYUN::SLB::BackendServerAttachment",
"Properties": {
"BackendServerList": {
"Fn::GetAtt": [
"WPEcsInstance",
"InstanceIds"
]
},
"LoadBalancerId": {
"Ref": "WPLoadBalancer"
}
}
},
"WPLoadBalancer": {
"Type": "ALIYUN::SLB::LoadBalancer",
"Properties": {
"LoadBalancerName": "WordPressLoadBalancer",
"AddressType": "internet"
}
},
RDS
WordPress 需要存储用户的博文和评论,后端数据库是必不可少的一个组件。阿里云 RDS 资源是一个很好的选择。通过 ROS 可以简便的把创建 RDS 实例和配置数据库一步搞定。
"WPDBDatabase": {
"Type": "ALIYUN::RDS::DBInstance",
"DependsOn": "SNatEntry",
"Properties": {
"DBInstanceClass": {
"Ref": "WPDBInstanceClass"
},
"DBMappings": [
{
"DBName": {
"Ref": "WPDBName"
},
"CharacterSetName": "utf8"
}
],
"ZoneId": {
"Fn::Select": [
"0",
{
"Fn::GetAZs": {
"Ref": "ALIYUN::Region"
}
}
]
},
"DBInstanceStorage": {
"Ref": "WPDBInstanceStorage"
},
"VSwitchId": {
"Ref": "VSwitch"
},
"Engine": {
"Ref": "WPDBEngine"
},
"MasterUserPassword": {
"Ref": "WPDBPassword"
},
"MasterUsername": {
"Ref": "WPDBUser"
},
"PreferredBackupPeriod": [
"Monday",
"Wednesday"
],
"VPCId": {
"Ref": "Vpc"
},
"EngineVersion": {
"Ref": "WPDBEngineVersion"
},
"PreferredBackupTime": "23:00Z-24:00Z",
"SecurityIPList": "0.0.0.0/0"
}
},
总结
从本例来看,不仅通过 ROS 能安装配置应用,也可以通过集成第三方的官配工具实现相同的目的。本例以Chef 为例相大家展示了 ROS 如何通过集成 第三方配管工具。希望通过这个例子,大家通过 Chef 不光能部署 WordPress ,更能部署自己的应用。关于 ROS 的详细指导请参考这里。