mirror of https://github.com/OpenVidu/openvidu.git
1433 lines
47 KiB
Plaintext
1433 lines
47 KiB
Plaintext
---
|
|
AWSTemplateFormatVersion: 2010-09-09
|
|
Description: Openvidu Pro With Master Replication
|
|
|
|
Parameters:
|
|
|
|
DomainName:
|
|
Description: 'Domain name which will be used to access OpenVidu Pro. This domain name should point to the Load Balancer URL after the stack is deployed.'
|
|
Type: 'String'
|
|
AllowedPattern: ^$|^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$
|
|
MinLength: 1
|
|
ConstraintDescription: The domain name does not have a valid domain name format
|
|
|
|
KeyName:
|
|
Description: 'Name of an existing EC2 KeyPair to enable SSH access to the instance. It is mandatory to perform some administrative tasks on instances.'
|
|
Type: 'AWS::EC2::KeyPair::KeyName'
|
|
AllowedPattern : '.+'
|
|
ConstraintDescription: 'Must be defined and to be an existing EC2 KeyPair'
|
|
|
|
OpenViduLicense:
|
|
Description: 'Visit https://openvidu.io/account'
|
|
Type: String
|
|
AllowedPattern: ^(?!\s*$).+$
|
|
MinLength: 1
|
|
NoEcho: true
|
|
ConstraintDescription: OpenVidu Pro License is mandatory
|
|
|
|
OpenViduSecret:
|
|
Description: 'Secret to connect to this OpenVidu Platform. Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens ("-") and underscores ("_")'
|
|
Type: String
|
|
AllowedPattern: ^[a-zA-Z0-9_-]+$
|
|
MinLength: 1
|
|
NoEcho: true
|
|
ConstraintDescription: 'Cannot be empty and must contain only alphanumeric characters [a-zA-Z0-9], hypens ("-") and underscores ("_")'
|
|
|
|
MediaServer:
|
|
Description: 'Media Server to be deployed in Media Nodes'
|
|
Type: String
|
|
Default: mediasoup
|
|
AllowedValues:
|
|
- mediasoup
|
|
- kurento
|
|
ConstraintDescription: 'Must be a valid EC2 instance type'
|
|
|
|
OpenViduProClusterId:
|
|
Description: 'Unique identifier for the OpenVidu Pro cluster'
|
|
Type: String
|
|
MinLength: 1
|
|
AllowedPattern: ^[a-z0-9_-]+$
|
|
ConstraintDescription: 'Cannot be empty and must contain only lowercase characters [a-z0-9], hypens ("-") and underscores ("_")'
|
|
|
|
|
|
OpenViduS3BucketName:
|
|
Description: "Bucket to save configuration and recordings. If not defined, a default one will be created"
|
|
Type: String
|
|
Default: ''
|
|
|
|
OpenViduS3ConfigAutoRestart:
|
|
Description: "If true, changes at in .env file in S3 bucket will restart automatically all master nodes."
|
|
Type: String
|
|
AllowedValues:
|
|
- true
|
|
- false
|
|
Default: true
|
|
|
|
OpenViduRecording:
|
|
Description: "If 'true', recordings will be saved in an s3 bucket created by this cloudformation, or the defined one at OpenViduS3Bucket"
|
|
Type: String
|
|
AllowedValues:
|
|
- true
|
|
- false
|
|
Default: true
|
|
|
|
CoturnInMediaNodes:
|
|
Description: "If true, Coturn will be deployed on media nodes. Otherwise it will be deployed in master nodes."
|
|
Type: String
|
|
AllowedValues:
|
|
- true
|
|
- false
|
|
Default: false
|
|
|
|
# Enable Elasticsearch and Kibana
|
|
ElasticsearchEnabled:
|
|
Description: "Choose if you want OpenVidu to use Elasticsearch."
|
|
Type: String
|
|
AllowedValues:
|
|
- true
|
|
- false
|
|
Default: true
|
|
|
|
# Elasticsearch configuration
|
|
ElasticsearchUser:
|
|
Description: "Username for Elasticsearch and Kibana. ('ElasticSearch Enabled' must be true)"
|
|
Type: String
|
|
AllowedPattern: ^$|^[^" ]+$
|
|
ConstraintDescription: Elasticsearch user is mandatory (no whitespaces or quotations allowed)
|
|
Default: elasticadmin
|
|
|
|
ElasticsearchPassword:
|
|
Description: "Password for Elasticsearch and Kibana ('ElasticSearch Enabled' must be true)"
|
|
Type: String
|
|
AllowedPattern: ^$|^[^" ]+$
|
|
NoEcho: true
|
|
ConstraintDescription: Elasticsearch password is mandatory and it should have at least 6 characters (no whitespaces or quotations allowed)
|
|
|
|
# Elasticsearch configuration
|
|
ElasticsearchUrl:
|
|
Description: "If you have an external Elasticsearch service running, put here the url to the service. If empty, an Elasticsearch service will be deployed next to OpenVidu. ('ElasticSearch Enabled' must be true)"
|
|
Type: String
|
|
AllowedPattern: (^(https?:\/\/)?([^:\/]+)(:([0-9]+))?(\/.*)?$|^$)
|
|
ConstraintDescription: "It is very important to specify the Elasticsearch URL with the port used by this service. For example: https://es-example"
|
|
|
|
KibanaUrl:
|
|
Description: "If you have an external Kibana service running, put here the url to the service. If empty, a Kibana service will be deployed next to OpenVidu. ('ElasticSearch Enabled' must be true)"
|
|
Type: String
|
|
AllowedPattern: (^(https?:\/\/)?([^:\/]+)(:([0-9]+))?(\/.*)?$|^$)
|
|
ConstraintDescription: "It is very important to specify the url with port used by this service. For example: https://kibana-example"
|
|
|
|
LoadBalancerCertificateARN:
|
|
Description: 'Amazon certificate arn resource to load into the LoadBalancer'
|
|
Type: String
|
|
AllowedPattern: '.+'
|
|
ConstraintDescription: The Load Balancer domain name must be defined
|
|
|
|
AwsInstanceTypeOV:
|
|
Description: 'Specifies the EC2 instance type for your OpenVidu Server Pro Node'
|
|
Type: String
|
|
Default: c5.xlarge
|
|
AllowedValues:
|
|
- t2.medium
|
|
- t2.large
|
|
- t2.xlarge
|
|
- t2.2xlarge
|
|
- t3.large
|
|
- t3.xlarge
|
|
- t3.2xlarge
|
|
- m4.large
|
|
- m4.xlarge
|
|
- m4.2xlarge
|
|
- m4.4xlarge
|
|
- m4.10xlarge
|
|
- m4.16xlarge
|
|
- m5.large
|
|
- m5.xlarge
|
|
- m5.2xlarge
|
|
- m5.4xlarge
|
|
- m5.8xlarge
|
|
- m5.12xlarge
|
|
- m5.16xlarge
|
|
- m5.24xlarge
|
|
- c4.large
|
|
- c4.xlarge
|
|
- c4.2xlarge
|
|
- c4.4xlarge
|
|
- c4.8xlarge
|
|
- c5.large
|
|
- c5.xlarge
|
|
- c5.2xlarge
|
|
- c5.4xlarge
|
|
- c5.9xlarge
|
|
- c5.12xlarge
|
|
- c5.18xlarge
|
|
- c5.24xlarge
|
|
- c6a.large
|
|
- c6a.xlarge
|
|
- c6a.2xlarge
|
|
- c6a.4xlarge
|
|
- c6a.8xlarge
|
|
- c6a.12xlarge
|
|
- c6a.16xlarge
|
|
- c6a.24xlarge
|
|
- c6a.32xlarge
|
|
- c6a.48xlarge
|
|
- c6a.metal
|
|
ConstraintDescription: 'Must be a valid EC2 instance type'
|
|
|
|
AwsInstanceTypeKMS:
|
|
Description: 'Specifies the EC2 instance type for your Media Nodes'
|
|
Type: String
|
|
Default: c5.xlarge
|
|
AllowedValues:
|
|
- t2.medium
|
|
- t2.large
|
|
- t2.xlarge
|
|
- t2.2xlarge
|
|
- t3.large
|
|
- t3.xlarge
|
|
- t3.2xlarge
|
|
- m4.large
|
|
- m4.xlarge
|
|
- m4.2xlarge
|
|
- m4.4xlarge
|
|
- m4.10xlarge
|
|
- m4.16xlarge
|
|
- m5.large
|
|
- m5.xlarge
|
|
- m5.2xlarge
|
|
- m5.4xlarge
|
|
- m5.8xlarge
|
|
- m5.12xlarge
|
|
- m5.16xlarge
|
|
- m5.24xlarge
|
|
- c4.large
|
|
- c4.xlarge
|
|
- c4.2xlarge
|
|
- c4.4xlarge
|
|
- c4.8xlarge
|
|
- c5.large
|
|
- c5.xlarge
|
|
- c5.2xlarge
|
|
- c5.4xlarge
|
|
- c5.9xlarge
|
|
- c5.12xlarge
|
|
- c5.18xlarge
|
|
- c5.24xlarge
|
|
- c6a.large
|
|
- c6a.xlarge
|
|
- c6a.2xlarge
|
|
- c6a.4xlarge
|
|
- c6a.8xlarge
|
|
- c6a.12xlarge
|
|
- c6a.16xlarge
|
|
- c6a.24xlarge
|
|
- c6a.32xlarge
|
|
- c6a.48xlarge
|
|
- c6a.metal
|
|
ConstraintDescription: 'Must be a valid EC2 instance type'
|
|
|
|
MinMasterNodes:
|
|
Description: 'Minimun number of Master Nodes'
|
|
Type: Number
|
|
Default: 1
|
|
MinValue: 1
|
|
ConstraintDescription: 'A Minimun number of Master Nodes is mandatory'
|
|
|
|
MaxMasterNodes:
|
|
Description: 'Maximum number of Master Nodes'
|
|
Type: Number
|
|
Default: 4
|
|
MinValue: 1
|
|
ConstraintDescription: 'A Maximum number of Master Nodes is mandatory'
|
|
|
|
DesiredMasterNodes:
|
|
Description: 'Desired number of Master Nodes'
|
|
Type: Number
|
|
Default: 1
|
|
MinValue: 1
|
|
ConstraintDescription: 'A desired number of Master Nodes is mandatory'
|
|
|
|
MinMediaNodes:
|
|
Description: 'Minimun number of Media Nodes'
|
|
Type: Number
|
|
Default: 1
|
|
MinValue: 1
|
|
ConstraintDescription: 'A Minimun number of Master Nodes is mandatory'
|
|
|
|
MaxMediaNodes:
|
|
Description: 'Maximum number of Media Nodes'
|
|
Type: Number
|
|
Default: 4
|
|
MinValue: 1
|
|
ConstraintDescription: 'A Maximum number of Master Nodes is mandatory'
|
|
|
|
ScaleUpMediaNodesAvgCpu:
|
|
Description: 'Scale up media nodes when avg cpu is greater the specified value'
|
|
Type: Number
|
|
Default: 70
|
|
MinValue: 0
|
|
ConstraintDescription: 'A desired number of Master Nodes is mandatory'
|
|
|
|
ScaleDownMediaNodesAvgCpu:
|
|
Description: 'Scale down media nodes when avg cpu is below the specified value'
|
|
Type: Number
|
|
Default: 30
|
|
MinValue: 1
|
|
ConstraintDescription: 'A desired number of Master Nodes is mandatory'
|
|
|
|
DesiredMediaNodes:
|
|
Description: 'Desired number of Media Nodes'
|
|
Type: Number
|
|
Default: 1
|
|
MinValue: 1
|
|
ConstraintDescription: 'A desired number of Master Nodes is mandatory'
|
|
|
|
OpenViduVPC:
|
|
Description: 'Dedicated VPC for OpenVidu cluster'
|
|
Type: AWS::EC2::VPC::Id
|
|
AllowedPattern: ^.+$
|
|
ConstraintDescription: You must specify a VPC ID
|
|
|
|
OpenViduSubnets:
|
|
Description: 'Subnet for OpenVidu cluster'
|
|
Type: List<AWS::EC2::Subnet::Id>
|
|
AllowedPattern: ^.+$
|
|
ConstraintDescription: You must specify a subnet ID
|
|
|
|
#start_mappings
|
|
Mappings:
|
|
OVAMIMAP:
|
|
eu-west-1:
|
|
AMI: OV_MASTER_REPLICATION_AMI_ID
|
|
|
|
KMSAMIMAP:
|
|
eu-west-1:
|
|
AMI: KMS_AMI_ID
|
|
#end_mappings
|
|
|
|
Metadata:
|
|
'AWS::CloudFormation::Interface':
|
|
ParameterGroups:
|
|
- Label:
|
|
default: OpenVidu configuration
|
|
Parameters:
|
|
- DomainName
|
|
- OpenViduProClusterId
|
|
- OpenViduLicense
|
|
- OpenViduSecret
|
|
- MediaServer
|
|
- OpenViduS3BucketName
|
|
- OpenViduS3ConfigAutoRestart
|
|
- OpenViduRecording
|
|
- CoturnInMediaNodes
|
|
- Label:
|
|
default: Elasticsearch and Kibana configuration
|
|
Parameters:
|
|
- ElasticsearchEnabled
|
|
- ElasticsearchUrl
|
|
- KibanaUrl
|
|
- ElasticsearchUser
|
|
- ElasticsearchPassword
|
|
- Label:
|
|
default: EC2 and Autoscaling configuration
|
|
Parameters:
|
|
- AwsInstanceTypeOV
|
|
- AwsInstanceTypeKMS
|
|
- KeyName
|
|
- MinMasterNodes
|
|
- MaxMasterNodes
|
|
- DesiredMasterNodes
|
|
- MinMediaNodes
|
|
- MaxMediaNodes
|
|
- DesiredMediaNodes
|
|
- ScaleUpMediaNodesAvgCpu
|
|
- ScaleDownMediaNodesAvgCpu
|
|
- Label:
|
|
default: Load Balancer Certificate Configuration
|
|
Parameters:
|
|
- LoadBalancerCertificateARN
|
|
- Label:
|
|
default: Networking configuration
|
|
Parameters:
|
|
- OpenViduVPC
|
|
- OpenViduSubnets
|
|
ParameterLabels:
|
|
# OpenVidu General Configuration
|
|
DomainName:
|
|
default: 'Domain Name'
|
|
OpenViduProClusterId:
|
|
default: 'OpenVidu Pro Cluster Id'
|
|
OpenViduLicense:
|
|
default: 'OpenVidu Pro License'
|
|
OpenViduSecret:
|
|
default: 'OpenVidu Secret'
|
|
MediaServer:
|
|
default: 'Media Server'
|
|
OpenViduS3BucketName:
|
|
default: 'S3 Bucket where recordings and OpenVidu configuration will be stored'
|
|
OpenViduS3ConfigAutoRestart:
|
|
default: 'Auto Restart OpenVidu on S3 bucket .env changes'
|
|
OpenViduRecording:
|
|
default: 'Enable OpenVidu Recording'
|
|
CoturnInMediaNodes:
|
|
default: 'Deploy Coturn in Media Nodes. (Experimental)'
|
|
# Elasticsearch and Kibana Configuration
|
|
ElasticsearchEnabled:
|
|
default: "Enable Elasticsearch and Kibana"
|
|
ElasticsearchUrl:
|
|
default: 'Elasticsearch URL'
|
|
KibanaUrl:
|
|
default: 'Kibana URL'
|
|
ElasticsearchUser:
|
|
default: 'Elasticsearch and Kibana username'
|
|
ElasticsearchPassword:
|
|
default: 'Elasticsearch and Kibana password'
|
|
# SSL Certificate Configuration
|
|
LoadBalancerCertificateARN:
|
|
default: 'ARN of the AWS Certificate. This certificate must be valid for "Domain Name"'
|
|
# EC2 And Autoscaling Configuration
|
|
AwsInstanceTypeOV:
|
|
default: "Master Nodes instance type"
|
|
AwsInstanceTypeKMS:
|
|
default: 'Media Nodes instance type'
|
|
KeyName:
|
|
default: 'SSH Key Name'
|
|
MinMasterNodes:
|
|
default: 'Minimum Master Nodes'
|
|
MaxMasterNodes:
|
|
default: 'Maximum Master Nodes'
|
|
DesiredMasterNodes:
|
|
default: 'Desired Master Nodes'
|
|
DesiredMediaNodes:
|
|
default: 'Desired Media Nodes'
|
|
MinMediaNodes:
|
|
default: 'Minimum Media Nodes'
|
|
MaxMediaNodes:
|
|
default: 'Maximum Media Nodes'
|
|
ScaleUpMediaNodesAvgCpu:
|
|
default: 'Scale Up Media Nodes on Average CPU'
|
|
ScaleDownMediaNodesAvgCpu:
|
|
default: 'Scale Down Media Nodes on Average CPU'
|
|
# Networking Configuration
|
|
OpenViduVPC:
|
|
default: 'OpenVidu Pro VPC'
|
|
OpenViduSubnets:
|
|
default: 'OpenVIdu Pro Subnets'
|
|
|
|
Conditions:
|
|
CreateS3Bucket: !Equals [ !Ref OpenViduS3BucketName, '']
|
|
|
|
Rules:
|
|
|
|
# Check when Elasticsearch is enabled that all the parameters are present
|
|
ElasticsearchValidation:
|
|
RuleCondition: !Equals [ !Ref ElasticsearchEnabled, 'true' ]
|
|
Assertions:
|
|
- AssertDescription: Paramter 'Elasticsearch and Kibana username' (ElasticsearchUser) is needed when 'Enable Elasticsearch and Kibana' (ElasticsearchEnabled) is 'true'.
|
|
Assert: !Not [ !Equals [!Ref ElasticsearchUser, ''] ]
|
|
- AssertDescription: Parameter 'Elasticsearch and Kibana password' (ElasticsearchPassword) is needed when 'Enable Elasticsearch and Kibana' (ElasticsearchEnabled) is 'true'.
|
|
Assert: !Not [ !Equals [!Ref ElasticsearchPassword, ''] ]
|
|
|
|
# Check when Elasticsearch is disabled that any parameter of elasticsearch is not present
|
|
ElasticsearchDisabledValidation:
|
|
RuleCondition: !Equals [ !Ref ElasticsearchEnabled, 'false' ]
|
|
Assertions:
|
|
- AssertDescription: Parameter 'Elasticsearch URL' (ElasticsearchUrl) is not needed when 'Enable Elasticsearch and Kibana' (ElasticsearchEnabled) is 'false'.
|
|
Assert: !Equals [ !Ref ElasticsearchUrl, "" ]
|
|
- AssertDescription: Parameter 'Kibana URL' (KibanaUrl) is not needed when 'Enable Elasticsearch and Kibana' (ElasticsearchEnabled) is 'false'.
|
|
Assert: !Equals [ !Ref KibanaUrl, "" ]
|
|
- AssertDescription: Parameter 'Elasticsearch and Kibana username' (ElasticsearchUser) is not needed when 'Enable Elasticsearch and Kibana' (ElasticsearchEnabled) is 'false'.
|
|
Assert: !Equals [ !Ref ElasticsearchUser, "" ]
|
|
- AssertDescription: Parameter 'Elasticsearch and Kibana password' (ElasticsearchPassword) is not needed when 'Enable Elasticsearch and Kibana' (ElasticsearchEnabled) is 'false'.
|
|
Assert: !Equals [ !Ref ElasticsearchPassword, "" ]
|
|
|
|
|
|
Resources:
|
|
|
|
#####
|
|
# S3 bucket
|
|
#####
|
|
S3OpenViduBucket:
|
|
Type: 'AWS::S3::Bucket'
|
|
DeletionPolicy: Retain
|
|
UpdateReplacePolicy: Retain
|
|
Properties:
|
|
### Unique bucket name using Stack ID
|
|
BucketName: !Join [ "-", [ !Ref OpenViduProClusterId, !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]] ] ]
|
|
AccessControl: Private
|
|
PublicAccessBlockConfiguration:
|
|
BlockPublicAcls: true
|
|
BlockPublicPolicy: true
|
|
IgnorePublicAcls : true
|
|
RestrictPublicBuckets: true
|
|
Condition: CreateS3Bucket
|
|
|
|
#####
|
|
# Security groups
|
|
#####
|
|
|
|
# Security group with all open ports necessary for OpenVidu Pro to work
|
|
# Only the Load Balancer has access to replication-manager port 4443 which proxies to OpenVidu Pro
|
|
OpenViduSecurityGroup:
|
|
Type: 'AWS::EC2::SecurityGroup'
|
|
Properties:
|
|
GroupDescription: SSH and OpenVidu WebRTC Ports
|
|
GroupName: !Join [ "-", [ !Ref 'AWS::StackName', 'OpenViduSecurityGroup'] ]
|
|
VpcId: !Ref OpenViduVPC
|
|
Tags:
|
|
- Key: Name
|
|
Value: !Join [ "-", [ !Ref 'AWS::StackName', 'OpenViduSecurityGroup'] ]
|
|
SecurityGroupIngress:
|
|
- IpProtocol: udp
|
|
FromPort: 3478
|
|
ToPort: 3478
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: udp
|
|
FromPort: 3478
|
|
ToPort: 3478
|
|
CidrIpv6: ::/0
|
|
- IpProtocol: tcp
|
|
FromPort: 3478
|
|
ToPort: 3478
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: tcp
|
|
FromPort: 3478
|
|
ToPort: 3478
|
|
CidrIpv6: ::/0
|
|
- IpProtocol: tcp
|
|
FromPort: 4443
|
|
ToPort: 4443
|
|
SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
|
|
- IpProtocol: udp
|
|
FromPort: 40000
|
|
ToPort: 65535
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: udp
|
|
FromPort: 40000
|
|
ToPort: 65535
|
|
CidrIpv6: ::/0
|
|
- IpProtocol: tcp
|
|
FromPort: 40000
|
|
ToPort: 65535
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: tcp
|
|
FromPort: 40000
|
|
ToPort: 65535
|
|
CidrIpv6: ::/0
|
|
SecurityGroupEgress:
|
|
- IpProtocol: tcp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: tcp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIpv6: ::/0
|
|
- IpProtocol: udp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: udp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIpv6: ::/0
|
|
|
|
# This security groups Ingress rule is for OpenVidu Pro Master instances which only need access
|
|
# to other OpenVidu Pro instances at port 5443 and 4443 to proxy requests from replication-manager
|
|
OpenViduSecurityGroupIngressServer:
|
|
Type: AWS::EC2::SecurityGroupIngress
|
|
Properties:
|
|
GroupId: !Ref OpenViduSecurityGroup
|
|
IpProtocol: tcp
|
|
FromPort: 5443
|
|
ToPort: 5443
|
|
SourceSecurityGroupId: !Ref OpenViduSecurityGroup
|
|
|
|
OpenViduSecurityGroupIngressReplicationManager:
|
|
Type: AWS::EC2::SecurityGroupIngress
|
|
Properties:
|
|
GroupId: !Ref OpenViduSecurityGroup
|
|
IpProtocol: tcp
|
|
FromPort: 4443
|
|
ToPort: 4443
|
|
SourceSecurityGroupId: !Ref OpenViduSecurityGroup
|
|
|
|
LoadBalancerSecurityGroup:
|
|
Type: 'AWS::EC2::SecurityGroup'
|
|
Properties:
|
|
VpcId: !Ref OpenViduVPC
|
|
GroupDescription: Load Balancer Security group
|
|
GroupName: !Join [ "-", [ !Ref 'AWS::StackName', 'LoadBalancerSecurityGroup'] ]
|
|
Tags:
|
|
- Key: Name
|
|
Value: !Join [ "-", [ !Ref 'AWS::StackName', 'LoadBalancerSecurityGroup'] ]
|
|
SecurityGroupIngress:
|
|
- IpProtocol: tcp
|
|
FromPort: 80
|
|
ToPort: 80
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: tcp
|
|
FromPort: 80
|
|
ToPort: 80
|
|
CidrIpv6: ::/0
|
|
- IpProtocol: tcp
|
|
FromPort: 443
|
|
ToPort: 443
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: tcp
|
|
FromPort: 443
|
|
ToPort: 443
|
|
CidrIpv6: ::/0
|
|
SecurityGroupEgress:
|
|
- IpProtocol: tcp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: tcp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIpv6: ::/0
|
|
- IpProtocol: udp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: udp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIpv6: ::/0
|
|
|
|
|
|
# Redis Security group.
|
|
# Let access only to instances with OpenVidu Pro security group attached
|
|
RedisSecurityGroup:
|
|
Type: 'AWS::EC2::SecurityGroup'
|
|
Properties:
|
|
VpcId: !Ref OpenViduVPC
|
|
GroupDescription: Security Group for OpenVidu Pro Redis
|
|
GroupName: !Join [ "-", [ !Ref 'AWS::StackName', 'RedisSecurityGroup'] ]
|
|
Tags:
|
|
- Key: Name
|
|
Value: !Join [ "-", [ !Ref 'AWS::StackName', 'RedisSecurityGroup'] ]
|
|
SecurityGroupIngress:
|
|
- IpProtocol: tcp
|
|
FromPort: 6379
|
|
ToPort: 6379
|
|
SourceSecurityGroupId: !Ref OpenViduSecurityGroup
|
|
SecurityGroupEgress:
|
|
- IpProtocol: tcp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: tcp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIpv6: ::/0
|
|
|
|
# Media Nodes Security group. Let access only to
|
|
# instances with OpenVidu Pro security group attached
|
|
# to ports 3000, 4000, and 8888
|
|
MediaNodeSecurityGroup:
|
|
Type: 'AWS::EC2::SecurityGroup'
|
|
Properties:
|
|
VpcId: !Ref OpenViduVPC
|
|
GroupDescription: SSH, Media Node controller and KMS WebRTC Ports
|
|
GroupName: !Join [ "-", [ !Ref 'AWS::StackName', 'KMSSecurityGroup'] ]
|
|
Tags:
|
|
- Key: Name
|
|
Value: !Join [ "-", [ !Ref 'AWS::StackName', 'KMSSecurityGroup'] ]
|
|
SecurityGroupIngress:
|
|
- IpProtocol: tcp
|
|
FromPort: 443
|
|
ToPort: 443
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: tcp
|
|
FromPort: 443
|
|
ToPort: 443
|
|
CidrIpv6: ::/0
|
|
- IpProtocol: udp
|
|
FromPort: 443
|
|
ToPort: 443
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: udp
|
|
FromPort: 443
|
|
ToPort: 443
|
|
CidrIpv6: ::/0
|
|
- IpProtocol: tcp
|
|
FromPort: 3000
|
|
ToPort: 3000
|
|
SourceSecurityGroupId: !Ref OpenViduSecurityGroup
|
|
- IpProtocol: tcp
|
|
FromPort: 4000
|
|
ToPort: 4000
|
|
SourceSecurityGroupId: !Ref OpenViduSecurityGroup
|
|
- IpProtocol: tcp
|
|
FromPort: 8888
|
|
ToPort: 8888
|
|
SourceSecurityGroupId: !Ref OpenViduSecurityGroup
|
|
- IpProtocol: udp
|
|
FromPort: 40000
|
|
ToPort: 65535
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: udp
|
|
FromPort: 40000
|
|
ToPort: 65535
|
|
CidrIpv6: ::/0
|
|
- IpProtocol: tcp
|
|
FromPort: 40000
|
|
ToPort: 65535
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: tcp
|
|
FromPort: 40000
|
|
ToPort: 65535
|
|
CidrIpv6: ::/0
|
|
SecurityGroupEgress:
|
|
- IpProtocol: tcp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: tcp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIpv6: ::/0
|
|
- IpProtocol: udp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIp: 0.0.0.0/0
|
|
- IpProtocol: udp
|
|
FromPort: 1
|
|
ToPort: 65535
|
|
CidrIpv6: ::/0
|
|
|
|
#####
|
|
# Redis Cluster
|
|
#####
|
|
RedisClusterSubnetGroup:
|
|
Type: AWS::ElastiCache::SubnetGroup
|
|
Properties:
|
|
CacheSubnetGroupName: !Join [ "-", [ !Ref 'AWS::StackName', 'RedisSubnetGroup'] ]
|
|
Description: Subnet where to deploy Redis
|
|
SubnetIds: !Ref OpenViduSubnets
|
|
|
|
RedisCluster:
|
|
Type: AWS::ElastiCache::CacheCluster
|
|
Properties:
|
|
ClusterName: !Join [ "-", [ !Ref 'AWS::StackName', 'Redis'] ]
|
|
CacheNodeType: cache.t3.medium
|
|
Engine: redis
|
|
EngineVersion: 6.x
|
|
NumCacheNodes: 1
|
|
CacheParameterGroupName: default.redis6.x
|
|
CacheSubnetGroupName: !Ref RedisClusterSubnetGroup
|
|
VpcSecurityGroupIds:
|
|
- !Ref RedisSecurityGroup
|
|
Tags:
|
|
- Key: Name
|
|
Value: !Join [ "-", [ !Ref 'AWS::StackName', 'Redis'] ]
|
|
|
|
#####
|
|
# Media Node Autoscaling Group
|
|
#####
|
|
MediaNodeLaunchTemplateConfiguration:
|
|
Type: AWS::EC2::LaunchTemplate
|
|
Properties:
|
|
LaunchTemplateName: !Join [ "-", [ !Ref 'AWS::StackName', 'ASGMediaNodeLaunchTemplate'] ]
|
|
LaunchTemplateData:
|
|
MetadataOptions:
|
|
HttpEndpoint: enabled
|
|
HttpPutResponseHopLimit: 1
|
|
HttpTokens: required
|
|
SecurityGroupIds:
|
|
- !GetAtt MediaNodeSecurityGroup.GroupId
|
|
ImageId: !GetAtt LambdaOnCreateInvoke.MediaNodeImageId
|
|
KeyName: !Ref KeyName
|
|
InstanceType: !Ref AwsInstanceTypeKMS
|
|
BlockDeviceMappings:
|
|
- DeviceName: /dev/sda1
|
|
Ebs:
|
|
VolumeType: gp3
|
|
DeleteOnTermination: true
|
|
VolumeSize: 50
|
|
|
|
MediaNodeAutoScalingGroup:
|
|
Type: AWS::AutoScaling::AutoScalingGroup
|
|
Properties:
|
|
AutoScalingGroupName: !Join [ "-", [ !Ref 'AWS::StackName', 'ASGMediaNode'] ]
|
|
LaunchTemplate:
|
|
LaunchTemplateId: !Ref MediaNodeLaunchTemplateConfiguration
|
|
Version: !GetAtt MediaNodeLaunchTemplateConfiguration.LatestVersionNumber
|
|
MinSize: !Ref MinMediaNodes
|
|
MaxSize: !Ref MaxMediaNodes
|
|
DesiredCapacity: !Ref DesiredMediaNodes
|
|
VPCZoneIdentifier: !Ref OpenViduSubnets
|
|
NewInstancesProtectedFromScaleIn: true
|
|
Tags:
|
|
- Key: Name
|
|
Value: !Join [ "-", [ !Ref 'AWS::StackName', 'Media Node'] ]
|
|
PropagateAtLaunch: true
|
|
- Key: ov-cluster-member
|
|
Value: kms
|
|
PropagateAtLaunch: true
|
|
- Key: ov-medianode-status
|
|
Value: running
|
|
PropagateAtLaunch: true
|
|
|
|
#####
|
|
# SQS Queue
|
|
#####
|
|
SQSPolicy:
|
|
Type: AWS::SQS::QueuePolicy
|
|
Properties:
|
|
Queues:
|
|
- !Ref SQSQueue
|
|
PolicyDocument:
|
|
Statement:
|
|
- Effect: Allow
|
|
Action:
|
|
- 'sqs:SendMessage'
|
|
Principal:
|
|
Service:
|
|
- events.amazonaws.com
|
|
Resource:
|
|
- !GetAtt SQSQueue.Arn
|
|
|
|
SQSQueue:
|
|
Type: AWS::SQS::Queue
|
|
Properties:
|
|
QueueName: !Join ['-', [ !Ref 'AWS::StackName', 'SQS.fifo'] ]
|
|
FifoQueue: true
|
|
MessageRetentionPeriod: 60
|
|
VisibilityTimeout: 50
|
|
ContentBasedDeduplication: true
|
|
Tags:
|
|
- Key: Name
|
|
Value: !Join ['-', [ !Ref 'AWS::StackName', 'SQS'] ]
|
|
|
|
#####
|
|
# Load Balancer
|
|
#####
|
|
LoadBalancer:
|
|
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
|
|
Properties:
|
|
Name: !Join ['-', [ !Ref 'AWS::StackName', 'lb'] ]
|
|
Subnets: !Ref OpenViduSubnets
|
|
SecurityGroups:
|
|
- !Ref LoadBalancerSecurityGroup
|
|
Tags:
|
|
- Key: Name
|
|
Value: !Join ['-', [ !Ref 'AWS::StackName', 'lb'] ]
|
|
|
|
HttpLoadBalancerListener:
|
|
Type: "AWS::ElasticLoadBalancingV2::Listener"
|
|
Properties:
|
|
DefaultActions:
|
|
- Type: "redirect"
|
|
RedirectConfig:
|
|
Protocol: "HTTPS"
|
|
Port: '443'
|
|
Host: "#{host}"
|
|
Path: "/#{path}"
|
|
Query: "#{query}"
|
|
StatusCode: "HTTP_301"
|
|
LoadBalancerArn: !Ref LoadBalancer
|
|
Port: 80
|
|
Protocol: "HTTP"
|
|
|
|
LoadBalancerListener:
|
|
Type: 'AWS::ElasticLoadBalancingV2::Listener'
|
|
Properties:
|
|
DefaultActions:
|
|
- Type: forward
|
|
TargetGroupArn: !Ref TargetGroup
|
|
LoadBalancerArn: !Ref LoadBalancer
|
|
Port: 443
|
|
Protocol: HTTPS
|
|
Certificates:
|
|
- CertificateArn: !Ref LoadBalancerCertificateARN
|
|
|
|
RootListenerRule:
|
|
Type: 'AWS::ElasticLoadBalancingV2::ListenerRule'
|
|
Properties:
|
|
ListenerArn: !Ref LoadBalancerListener
|
|
Priority: 1
|
|
Conditions:
|
|
- Field: path-pattern
|
|
Values:
|
|
- /
|
|
Actions:
|
|
- Type: "fixed-response"
|
|
FixedResponseConfig:
|
|
StatusCode: "401"
|
|
|
|
InspectorListenerRule:
|
|
Type: 'AWS::ElasticLoadBalancingV2::ListenerRule'
|
|
Properties:
|
|
ListenerArn: !Ref LoadBalancerListener
|
|
Priority: 2
|
|
Conditions:
|
|
- Field: path-pattern
|
|
Values:
|
|
- /inspector
|
|
Actions:
|
|
- Type: "redirect"
|
|
RedirectConfig:
|
|
Protocol: "HTTPS"
|
|
Port: '443'
|
|
Host: "#{host}"
|
|
Path: "/inspector/"
|
|
Query: "#{query}"
|
|
StatusCode: "HTTP_301"
|
|
|
|
ListenerRule:
|
|
Type: 'AWS::ElasticLoadBalancingV2::ListenerRule'
|
|
Properties:
|
|
ListenerArn: !Ref LoadBalancerListener
|
|
Priority: 3
|
|
Conditions:
|
|
- Field: path-pattern
|
|
Values:
|
|
- /openvidu*
|
|
Actions:
|
|
- TargetGroupArn: !Ref TargetGroup
|
|
Type: forward
|
|
|
|
TargetGroup:
|
|
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
|
|
Properties:
|
|
Name: !Ref 'AWS::StackName'
|
|
VpcId: !Ref OpenViduVPC
|
|
Port: 4443
|
|
Protocol: HTTP
|
|
Matcher:
|
|
HttpCode: '200'
|
|
HealthCheckIntervalSeconds: 10
|
|
HealthCheckPath: /openvidu/health
|
|
HealthCheckProtocol: HTTP
|
|
HealthCheckPort: '4443'
|
|
HealthCheckTimeoutSeconds: 5
|
|
HealthyThresholdCount: 3
|
|
UnhealthyThresholdCount: 4
|
|
Tags:
|
|
- Key: Name
|
|
Value: !Join [ "-", [ !Ref 'AWS::StackName', 'TargetGroup'] ]
|
|
|
|
#####
|
|
# OpenVidu Pro Master Role
|
|
#####
|
|
OpenViduProMasterRole:
|
|
Type: 'AWS::IAM::Role'
|
|
Properties:
|
|
Tags:
|
|
- Key: Name
|
|
Value: !Join [ "-", [ !Ref 'AWS::StackName', 'MasterNodeRole'] ]
|
|
AssumeRolePolicyDocument:
|
|
Version: 2012-10-17
|
|
Statement:
|
|
- Effect: Allow
|
|
Principal:
|
|
Service:
|
|
- ec2.amazonaws.com
|
|
Action:
|
|
- 'sts:AssumeRole'
|
|
Path: /
|
|
Policies:
|
|
- PolicyName: !Join ['-', [ !Ref 'AWS::StackName', 'MasterNodePolicy'] ]
|
|
PolicyDocument:
|
|
Version: 2012-10-17
|
|
Statement:
|
|
- Effect: Allow
|
|
Action:
|
|
- 'ec2:Describe*'
|
|
- 'ec2:CreateTags'
|
|
- 'ec2:DescribeTags'
|
|
Resource: '*'
|
|
- Effect: Allow
|
|
Action:
|
|
- 'sqs:*'
|
|
Resource: !GetAtt SQSQueue.Arn
|
|
- Effect: Allow
|
|
Action:
|
|
- 'autoscaling:SetInstanceHealth'
|
|
- 'autoscaling:DescribeAutoScalingInstances'
|
|
- 'autoscaling:DescribeAutoScalingGroups'
|
|
- 'autoscaling:SetInstanceProtection'
|
|
Resource: '*'
|
|
- Effect: Allow
|
|
Action:
|
|
- 's3:DeleteObject'
|
|
- 's3:GetObject'
|
|
- 's3:PutObject'
|
|
Resource:
|
|
- Fn::If:
|
|
- CreateS3Bucket
|
|
# If bucket is created, get ARN
|
|
- !Join [ "", [ !GetAtt S3OpenViduBucket.Arn, "/*" ] ]
|
|
# If bucket name is defined, use bucket name
|
|
- !Join [ "", [ 'arn:aws:s3:::', !Ref OpenViduS3BucketName, '/*'] ]
|
|
- Effect: Allow
|
|
Action:
|
|
- 's3:ListBucket'
|
|
- 's3:GetBucketLocation'
|
|
Resource:
|
|
- Fn::If:
|
|
- CreateS3Bucket
|
|
# If bucket is created, get ARN
|
|
- !Join [ "", [ !GetAtt S3OpenViduBucket.Arn ] ]
|
|
# If bucket name is defined, use bucket name
|
|
- !Join [ "", [ 'arn:aws:s3:::', !Ref OpenViduS3BucketName ]]
|
|
RoleName: !Join ['-', [ !Ref 'AWS::StackName', 'MasterNodeRole'] ]
|
|
|
|
OpenViduProMasterInstanceProfile:
|
|
Type: 'AWS::IAM::InstanceProfile'
|
|
Properties:
|
|
Path: /
|
|
Roles:
|
|
- !Ref OpenViduProMasterRole
|
|
|
|
#####
|
|
# OpenVidu Pro Master AutoScaling Group
|
|
#####
|
|
OpenViduProMasterNodeAutoScalingLaunchTemplate:
|
|
Type: AWS::EC2::LaunchTemplate
|
|
Properties:
|
|
LaunchTemplateName: !Join [ "-", [ !Ref 'AWS::StackName', 'ASGMasterNodeLaunchConfiguration'] ]
|
|
LaunchTemplateData:
|
|
MetadataOptions:
|
|
HttpEndpoint: enabled
|
|
HttpPutResponseHopLimit: 1
|
|
HttpTokens: required
|
|
SecurityGroupIds:
|
|
- !GetAtt OpenViduSecurityGroup.GroupId
|
|
IamInstanceProfile:
|
|
Arn: !GetAtt OpenViduProMasterInstanceProfile.Arn
|
|
ImageId: !GetAtt LambdaOnCreateInvoke.MasterNodeImageId
|
|
KeyName: !Ref KeyName
|
|
InstanceType: !Ref AwsInstanceTypeOV
|
|
BlockDeviceMappings:
|
|
- DeviceName: /dev/sda1
|
|
Ebs:
|
|
VolumeType: gp3
|
|
DeleteOnTermination: true
|
|
VolumeSize: 100
|
|
UserData:
|
|
Fn::Base64:
|
|
Fn::Sub:
|
|
- |
|
|
DOMAIN_OR_PUBLIC_IP=${DomainNameVar} | \
|
|
OPENVIDU_ENTERPRISE_MEDIA_SERVER=${MediaServer} | \
|
|
OPENVIDU_PRO_LICENSE=${OpenViduLicense} | \
|
|
OPENVIDU_SECRET=${OpenViduSecret} | \
|
|
OPENVIDU_PRO_CLUSTER_ID=${OpenViduProClusterId} | \
|
|
OPENVIDU_PRO_ELASTICSEARCH_HOST=${ElasticsearchUrl} | \
|
|
OPENVIDU_PRO_KIBANA_HOST=${KibanaUrl} | \
|
|
ELASTICSEARCH_USERNAME=${ElasticsearchUser} | \
|
|
ELASTICSEARCH_PASSWORD=${ElasticsearchPassword} | \
|
|
RM_REDIS_IP=${RedisHostName} | \
|
|
RM_REDIS_PORT=${RedisPort} | \
|
|
RM_SQS_QUEUE=${SqsQueueName} | \
|
|
RM_CLOUDFORMATION_ARN=${AWS::StackId} | \
|
|
OPENVIDU_PRO_CONFIG_S3_BUCKET=${OpenViduS3BucketParam} | \
|
|
RM_MEDIA_NODES_AUTOSCALING_GROUP_NAME=${MediaNodesAutoscalingGroupName} | \
|
|
RM_MASTER_NODES_AUTOSCALING_GROUP_NAME=${MasterNodesAutoscalingGroupName} | \
|
|
OPENVIDU_RECORDING=${OpenViduRecording} | \
|
|
OPENVIDU_PRO_COTURN_IN_MEDIA_NODES=${CoturnInMediaNodes} | \
|
|
OPENVIDU_ENTERPRISE_S3_CONFIG_AUTORESTART=${OpenViduS3ConfigAutoRestart} | \
|
|
OPENVIDU_PRO_ELASTICSEARCH=${ElasticsearchEnabled}
|
|
- DomainNameVar: !Ref DomainName
|
|
RedisHostName: !GetAtt RedisCluster.RedisEndpoint.Address
|
|
RedisPort: !GetAtt RedisCluster.RedisEndpoint.Port
|
|
SqsQueueName: !GetAtt SQSQueue.QueueName
|
|
MediaNodesAutoscalingGroupName: !Join [ "-", [ !Ref 'AWS::StackName', 'ASGMediaNode'] ]
|
|
MasterNodesAutoscalingGroupName: !Join [ "-", [ !Ref 'AWS::StackName', 'ASGOpenViduProMasterNode'] ]
|
|
OpenViduS3BucketParam: !If [ CreateS3Bucket, !Ref S3OpenViduBucket, !Ref OpenViduS3BucketName ]
|
|
|
|
OpenViduProMasterNodeAutoScalingGroup:
|
|
Type: AWS::AutoScaling::AutoScalingGroup
|
|
Properties:
|
|
AutoScalingGroupName: !Join [ "-", [ !Ref 'AWS::StackName', 'ASGOpenViduProMasterNode'] ]
|
|
LaunchTemplate:
|
|
LaunchTemplateId: !Ref OpenViduProMasterNodeAutoScalingLaunchTemplate
|
|
Version: !GetAtt OpenViduProMasterNodeAutoScalingLaunchTemplate.LatestVersionNumber
|
|
TargetGroupARNs:
|
|
- !Ref TargetGroup
|
|
MinSize: !Ref MinMasterNodes
|
|
MaxSize: !Ref MaxMasterNodes
|
|
DesiredCapacity: !Ref DesiredMasterNodes
|
|
VPCZoneIdentifier: !Ref OpenViduSubnets
|
|
HealthCheckType: ELB
|
|
HealthCheckGracePeriod: 180
|
|
Tags:
|
|
- Key: Name
|
|
Value: !Join [ "-", [ !Ref 'AWS::StackName', 'Master Node'] ]
|
|
PropagateAtLaunch: true
|
|
- Key: ov-cluster-member
|
|
Value: server
|
|
PropagateAtLaunch: true
|
|
|
|
#####
|
|
# Media Node Autoscaling Lifecycle hooks
|
|
#####
|
|
LaunchMediaNodeLifeCycleHook:
|
|
Type: AWS::AutoScaling::LifecycleHook
|
|
Properties:
|
|
LifecycleHookName: !Join [ "-", [ !Ref 'AWS::StackName', 'LaunchMediaNodeLifeCycleHook'] ]
|
|
AutoScalingGroupName: !Ref MediaNodeAutoScalingGroup
|
|
LifecycleTransition: autoscaling:EC2_INSTANCE_LAUNCHING
|
|
DefaultResult: CONTINUE
|
|
HeartbeatTimeout: 30
|
|
|
|
TerminateMediaNodeLifeCycleHook:
|
|
Type: AWS::AutoScaling::LifecycleHook
|
|
Properties:
|
|
LifecycleHookName: !Join [ "-", [ !Ref 'AWS::StackName', 'TerminateMediaNodeLifeCycleHook'] ]
|
|
AutoScalingGroupName: !Ref MediaNodeAutoScalingGroup
|
|
LifecycleTransition: autoscaling:EC2_INSTANCE_TERMINATING
|
|
DefaultResult: ABANDON
|
|
HeartbeatTimeout: 30
|
|
|
|
##########
|
|
# AWS Events
|
|
##########
|
|
# Send event with launched and terminated autoscaling group instances
|
|
LifeCycleMediaNodesRule:
|
|
Type: AWS::Events::Rule
|
|
Properties:
|
|
Name: !Join ['', [ !Ref AWS::StackName, '-asg-lifecycle-rule'] ]
|
|
State: 'ENABLED'
|
|
EventPattern: !Sub |
|
|
{
|
|
"source": [ "aws.autoscaling" ],
|
|
"detail": {
|
|
"LifecycleTransition": ["autoscaling:EC2_INSTANCE_LAUNCHING", "autoscaling:EC2_INSTANCE_TERMINATING"],
|
|
"AutoScalingGroupName": [ "${MediaNodeAutoScalingGroup}" ]
|
|
}
|
|
}
|
|
Targets:
|
|
- Arn: !GetAtt SQSQueue.Arn
|
|
Id: AsgLifecycleMediaNodes
|
|
SqsParameters:
|
|
MessageGroupId: sqs-notification
|
|
InputTransformer:
|
|
InputPathsMap:
|
|
source: $.source
|
|
lifecycle-transition: $.detail.LifecycleTransition
|
|
ec2-instance: $.detail.EC2InstanceId
|
|
autoscaling-groupname: $.detail.AutoScalingGroupName
|
|
InputTemplate: >-
|
|
{
|
|
"source": <source>,
|
|
"detail": {
|
|
"LifecycleTransition": <lifecycle-transition>,
|
|
"AutoScalingGroupName": <autoscaling-groupname>,
|
|
"EC2InstanceId": <ec2-instance>
|
|
},
|
|
"role": "MediaNode"
|
|
}
|
|
|
|
# Send event to update cluster state on autoscaling actions from AWS
|
|
AutoscalingScheduleRule:
|
|
Type: AWS::Events::Rule
|
|
Properties:
|
|
Description: 'Executes periodically lambda which sends information about the OpenVidu Pro Cluster'
|
|
Name: !Join ['', [ !Ref AWS::StackName, '-asg-rule'] ]
|
|
ScheduleExpression: 'rate(1 minute)'
|
|
State: 'ENABLED'
|
|
Targets:
|
|
- Arn: !GetAtt SQSQueue.Arn
|
|
Id: AsgSchedule
|
|
SqsParameters:
|
|
MessageGroupId: sqs-notification
|
|
InputTransformer:
|
|
InputPathsMap:
|
|
time: $.time
|
|
InputTemplate: >-
|
|
{
|
|
"source": "custom.autoscaling_schedule",
|
|
"detail": {
|
|
"time": <time>
|
|
}
|
|
}
|
|
|
|
##########
|
|
# Autoscaling Policies and Alarms for Media nodes
|
|
##########
|
|
MediaNodeCPUAlarmHigh:
|
|
Type: 'AWS::CloudWatch::Alarm'
|
|
Properties:
|
|
AlarmDescription: Scale-up if CPU > 80% for 10 seconds
|
|
MetricName: CPUUtilization
|
|
Namespace: AWS/EC2
|
|
Statistic: Average
|
|
Period: 60
|
|
EvaluationPeriods: 1
|
|
Threshold: !Ref ScaleUpMediaNodesAvgCpu
|
|
AlarmActions:
|
|
- !Ref MediaNodeScaleUpPolicy
|
|
Dimensions:
|
|
- Name: AutoScalingGroupName
|
|
Value: !Ref MediaNodeAutoScalingGroup
|
|
ComparisonOperator: GreaterThanThreshold
|
|
|
|
MediaNodeCPUAlarmLow:
|
|
Type: 'AWS::CloudWatch::Alarm'
|
|
Properties:
|
|
AlarmDescription: Scale-down if CPU < 30% for 10 seconds
|
|
MetricName: CPUUtilization
|
|
Namespace: AWS/EC2
|
|
Statistic: Average
|
|
Period: 60
|
|
EvaluationPeriods: 1
|
|
Threshold: !Ref ScaleDownMediaNodesAvgCpu
|
|
AlarmActions:
|
|
- !Ref MediaNodeScaleDownPolicy
|
|
Dimensions:
|
|
- Name: AutoScalingGroupName
|
|
Value: !Ref MediaNodeAutoScalingGroup
|
|
ComparisonOperator: LessThanThreshold
|
|
|
|
MediaNodeScaleUpPolicy:
|
|
Type: 'AWS::AutoScaling::ScalingPolicy'
|
|
Properties:
|
|
AdjustmentType: ChangeInCapacity
|
|
AutoScalingGroupName: !Ref MediaNodeAutoScalingGroup
|
|
Cooldown: '60'
|
|
ScalingAdjustment: 1
|
|
|
|
MediaNodeScaleDownPolicy:
|
|
Type: 'AWS::AutoScaling::ScalingPolicy'
|
|
Properties:
|
|
AdjustmentType: ChangeInCapacity
|
|
AutoScalingGroupName: !Ref MediaNodeAutoScalingGroup
|
|
Cooldown: '60'
|
|
ScalingAdjustment: -1
|
|
|
|
##########
|
|
# Lambda to delete protected Media nodes on destroy Cloudformation
|
|
##########
|
|
LambdaOnDeleteRole:
|
|
Type: 'AWS::IAM::Role'
|
|
DeletionPolicy: Delete
|
|
Properties:
|
|
AssumeRolePolicyDocument:
|
|
Version: 2012-10-17
|
|
Statement:
|
|
- Effect: Allow
|
|
Principal:
|
|
Service:
|
|
- lambda.amazonaws.com
|
|
Action:
|
|
- 'sts:AssumeRole'
|
|
Path: /
|
|
Policies:
|
|
- PolicyName: !Join ['', [ !Ref AWS::StackName, 'l-p-on-delete'] ]
|
|
PolicyDocument:
|
|
Version: 2012-10-17
|
|
Statement:
|
|
- Effect: Allow
|
|
Action:
|
|
- 'autoscaling:DescribeAutoScalingInstances'
|
|
- 'autoscaling:DescribeAutoScalingGroups'
|
|
- 'autoscaling:SetInstanceProtection'
|
|
- 'autoscaling:UpdateAutoScalingGroup'
|
|
Resource: '*'
|
|
RoleName: !Join ['', [ !Ref AWS::StackName, 'l-r-on-delete'] ]
|
|
|
|
LambdaOnDelete:
|
|
Type: AWS::Lambda::Function
|
|
DeletionPolicy: Delete
|
|
Properties:
|
|
FunctionName: !Join ['', [ !Ref AWS::StackName, '-lambda-on-delete'] ]
|
|
Code:
|
|
ZipFile: |
|
|
from botocore.config import Config
|
|
import boto3
|
|
import cfnresponse
|
|
import time
|
|
|
|
def handler(event, context):
|
|
try:
|
|
if (event['RequestType'] == 'Delete'):
|
|
deleteResources(event, context)
|
|
else:
|
|
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
|
|
except Exception:
|
|
cfnresponse.send(event, context, cfnresponse.FAILED, {})
|
|
|
|
def deleteResources(event, context):
|
|
aws_region = event['ResourceProperties']['AwsRegion']
|
|
media_nodes_asg = event['ResourceProperties']['MediaNodesASGName']
|
|
config_client = Config(region_name=aws_region)
|
|
asg_client = boto3.client('autoscaling', config=config_client)
|
|
|
|
# Unprotect autoscaling media nodes
|
|
asg_client.update_auto_scaling_group(
|
|
AutoScalingGroupName=media_nodes_asg,
|
|
NewInstancesProtectedFromScaleIn=False,
|
|
DesiredCapacity=0,
|
|
MinSize=0,
|
|
MaxSize=0
|
|
)
|
|
time.sleep(30)
|
|
response = asg_client.describe_auto_scaling_groups(
|
|
AutoScalingGroupNames=[media_nodes_asg]
|
|
)
|
|
instance_list = response['AutoScalingGroups'][0]['Instances']
|
|
instance_id_list=[]
|
|
for instance in instance_list:
|
|
instance_id_list.append(instance['InstanceId'])
|
|
asg_client.set_instance_protection(
|
|
InstanceIds=instance_id_list,
|
|
AutoScalingGroupName=media_nodes_asg,
|
|
ProtectedFromScaleIn=False
|
|
)
|
|
|
|
cfnresponse.send(event, context, cfnresponse.FAILED, responseData)
|
|
Handler: index.handler
|
|
Role:
|
|
!GetAtt LambdaOnDeleteRole.Arn
|
|
Runtime: python3.11
|
|
Timeout: 900
|
|
|
|
LambdaOnDeleteInvoke:
|
|
Type: AWS::CloudFormation::CustomResource
|
|
DeletionPolicy: Delete
|
|
Version: "1.0"
|
|
Properties:
|
|
ServiceToken: !GetAtt LambdaOnDelete.Arn
|
|
AwsRegion: !Ref AWS::Region
|
|
MediaNodesASGName: !Ref MediaNodeAutoScalingGroup
|
|
|
|
##########
|
|
# Lambda to Copy original AMIs from eu-west-1 to the deployment region (On create)
|
|
##########
|
|
LambdaOnCreateRole:
|
|
Type: 'AWS::IAM::Role'
|
|
DeletionPolicy: Delete
|
|
Properties:
|
|
AssumeRolePolicyDocument:
|
|
Version: 2012-10-17
|
|
Statement:
|
|
- Effect: Allow
|
|
Principal:
|
|
Service:
|
|
- lambda.amazonaws.com
|
|
Action:
|
|
- 'sts:AssumeRole'
|
|
Path: /
|
|
Policies:
|
|
- PolicyName: !Join ['', [ !Ref AWS::StackName, '-lambda-policy-on-create'] ]
|
|
PolicyDocument:
|
|
Version: 2012-10-17
|
|
Statement:
|
|
# Permissions to copy original Lambda to the region where it is being deployed
|
|
- Effect: Allow
|
|
Action:
|
|
- 'ec2:DescribeImages'
|
|
- 'ec2:CopyImage'
|
|
Resource: '*'
|
|
# Describe instances to get instances which OpenVidu PRO creates
|
|
- Effect: Allow
|
|
Action:
|
|
- 'ec2:DescribeInstances'
|
|
Resource: '*'
|
|
RoleName: !Join ['', [ !Ref AWS::StackName, '-lambda-role-on-create'] ]
|
|
|
|
LambdaOnCreate:
|
|
Type: AWS::Lambda::Function
|
|
DeletionPolicy: Delete
|
|
Properties:
|
|
FunctionName: !Join ['', [ !Ref AWS::StackName, '-lambda-on-create'] ]
|
|
Code:
|
|
ZipFile: |
|
|
import boto3
|
|
import cfnresponse
|
|
from botocore.config import Config
|
|
|
|
def handler(event, context):
|
|
try:
|
|
if (event['RequestType'] == 'Create'):
|
|
copy_ami(event, context)
|
|
else:
|
|
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
|
|
except Exception:
|
|
cfnresponse.send(event, context, cfnresponse.FAILED, {})
|
|
|
|
def copy_ami_operation(source_image_id, source_region, new_ami_name, ec2_client):
|
|
own_ami_filter = [{ 'Name': 'name', 'Values': [new_ami_name] }]
|
|
amis_response = ec2_client.describe_images(Filters=own_ami_filter)
|
|
if (len(amis_response['Images']) == 1):
|
|
# If AMI exists, don't copy
|
|
return amis_response['Images'][0]['ImageId']
|
|
else:
|
|
# If AMI does not exist, copy
|
|
new_amis_response = ec2_client.copy_image(
|
|
SourceImageId=source_image_id,
|
|
SourceRegion=source_region,
|
|
Name=new_ami_name
|
|
)
|
|
return new_amis_response['ImageId']
|
|
|
|
def copy_ami(event, context):
|
|
new_images=[]
|
|
cfn_output = {}
|
|
source_image_id_master_node = event['ResourceProperties']['MasterNodeAmiSourceId']
|
|
source_image_id_media_node = event['ResourceProperties']['MediaNodeAmiSourceId']
|
|
source_region = event['ResourceProperties']['AmiSourceRegion']
|
|
deployment_region = event['ResourceProperties']['DeploymentRegion']
|
|
|
|
# Clients init
|
|
ec2_client = boto3.client('ec2', config = Config(region_name=deployment_region))
|
|
ec2_client_ov = boto3.client('ec2', config = Config(region_name=source_region))
|
|
img_exists_waiter= ec2_client.get_waiter('image_exists')
|
|
img_avail_waiter = ec2_client.get_waiter('image_available')
|
|
|
|
# Get original ami name
|
|
public_ami_master_node_filter = [{ 'Name': 'image-id', 'Values': [ source_image_id_master_node ] }]
|
|
public_ami_media_node_filter = [{ 'Name': 'image-id', 'Values': [ source_image_id_media_node ] }]
|
|
|
|
response = ec2_client_ov.describe_images(Filters=public_ami_master_node_filter)
|
|
new_ami_name_master_node = "[ OpenVidu ENTERPRISE Master Node AMI Copy ] - " + response['Images'][0]['Name']
|
|
response = ec2_client_ov.describe_images(Filters=public_ami_media_node_filter)
|
|
new_ami_name_media_node = "[ OpenVidu PRO/ENTERPRISE Media Node AMI Copy ] - " + response['Images'][0]['Name']
|
|
|
|
# Copy master node AMI and media node AMI
|
|
master_node_ami_id = copy_ami_operation(source_image_id_master_node, source_region, new_ami_name_master_node, ec2_client)
|
|
new_images.append(master_node_ami_id)
|
|
cfn_output['MasterNodeImageId'] = master_node_ami_id
|
|
media_node_ami_id = copy_ami_operation(source_image_id_media_node, source_region, new_ami_name_media_node, ec2_client)
|
|
new_images.append(media_node_ami_id)
|
|
cfn_output['MediaNodeImageId'] = media_node_ami_id
|
|
|
|
# Wait images to be available
|
|
waiter_config = {'Delay': 15, 'MaxAttempts': 59 }
|
|
response = img_exists_waiter.wait(ImageIds=new_images, WaiterConfig=waiter_config)
|
|
response = img_avail_waiter.wait(ImageIds=new_images, WaiterConfig=waiter_config)
|
|
|
|
# Return AMIs
|
|
cfnresponse.send(event, context, cfnresponse.SUCCESS, cfn_output)
|
|
|
|
Handler: index.handler
|
|
Role:
|
|
!GetAtt LambdaOnCreateRole.Arn
|
|
Runtime: python3.11
|
|
Timeout: 900
|
|
|
|
LambdaOnCreateInvoke:
|
|
Type: AWS::CloudFormation::CustomResource
|
|
DeletionPolicy: Delete
|
|
Version: "1.0"
|
|
Properties:
|
|
ServiceToken: !GetAtt LambdaOnCreate.Arn
|
|
AmiSourceRegion: 'eu-west-1'
|
|
MasterNodeAmiSourceId: !FindInMap [OVAMIMAP, 'eu-west-1', AMI]
|
|
MediaNodeAmiSourceId: !FindInMap [KMSAMIMAP, 'eu-west-1', AMI]
|
|
StackName: !Ref AWS::StackName
|
|
DeploymentRegion: !Ref AWS::Region
|
|
|
|
Outputs:
|
|
LoadBalancerUrl:
|
|
Description: LoadBalancer of OpenVidu Pro. Point your domain name to this Load Balancer.
|
|
Value: !Join [ '', [ 'https://', !GetAtt LoadBalancer.DNSName ] ]
|
|
InspectorUrl:
|
|
Description: Inspector URL using domain name.
|
|
Value: !Join [ '', [ 'https://', !Ref DomainName, '/inspector' ] ]
|