├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── architecture ├── locust.drawio ├── locust.jpg ├── scaling └── scaling.jpg ├── cf-templates ├── template-app.yml └── template-test.yml └── src ├── app ├── index.php ├── login.php ├── logout.php ├── stress.php └── welcome.php ├── css └── style.css └── image └── unicorn.png /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS EC2 AutoScaling PHP App Demo 2 | 3 | The purpose of this demo is show how to use some AWS services to create a scalable architecture. We are going to create a web app architecture and do a load test. 4 | 5 | ![demo](architecture/scaling.jpg) 6 | 7 | The architecture above shows a VPC separating the subnets in public and private subnets, a high scalable application layer utilizing AutoScaling and a high scalable data layer with managed cache service **(Amazon ElastiCache)** to store sessions and managed relational database service **(Amazon RDS)** with replicas to scale reads. Also, we are storing static assets in a bucket **(Amazon S3)** and utilizing a CDN **(Amazon CloudFront)** to deliver the static assets and minimize latency to deliver dynamic content. 8 | 9 | ![test](architecture/locust.jpg) 10 | 11 | To test the application architecture we are utilizing the tool **Locust**, an open source load testing tool, to simulate users acessing our workload. 12 | 13 | To get started you will need an IAM user with the following access: 14 | 15 | * CloudFormation 16 | * EC2 17 | * CloudFront 18 | * ElastiCache 19 | * RDS 20 | * S3 21 | * VPC 22 | * CloudWatch 23 | * Route53 24 | * IAM 25 | 26 | __Notes:__ 27 | * __Tested in the N. Virginia region (us-east-1);__ 28 | * __CF is just creating two subnets (public, private-app), different from the architecture;__ 29 | * __RDS is not implemented yet to test Read Replicas.__ 30 | 31 | ## App CloudFormation 32 | 1. Download the repository or copy the CloudFormation template **template-app.yml** at cf-templates folder; 33 | 2. Open the CloudFormation console at https://console.aws.amazon.com/cloudformation 34 | 3. On the Step 1 - Specify template: Choose Upload a template file, click on Choose file button and select the **template-app.yml** located inside deploy directory 35 | 4. On the Step 2 - Specify stack details: Enter the Stack name as **'scaling-web'** 36 | 5. On the Step 3 - Configure stack options: Just click on Next button 37 | 6. On the Step 4 - Review: Enable the checkbox I acknowledge that AWS CloudFormation might create IAM resources with custom names., and click on Create Stack button 38 | 7. Wait for the stack get into status CREATE_COMPLETE 39 | 8. Under the Outputs tab, _take a note_ of **ELB value** 40 | 9. Open in a new tab the app utilizing the **ELB** DNS 41 | 42 | ## Load CloudFormation 43 | 1. Download the repository or copy the CloudFormation template **template-test.yml** at cf-templates folder; 44 | 2. Open the CloudFormation console at https://console.aws.amazon.com/cloudformation 45 | 3. On the Step 1 - Specify template: Choose Upload a template file, click on Choose file button and select the **template-test.yml** located inside deploy directory 46 | 4. On the Step 2 - Specify stack details: Enter the Stack name as **'locust'** and change the parameter **TargetEndpoint** utilizing the **ELB value** noted at the first stage. For example: **http://www.yourwebsite.com** 47 | 5. On the Step 3 - Configure stack options: Just click on Next button 48 | 6. On the Step 4 - Review: Enable the checkbox I acknowledge that AWS CloudFormation might create IAM resources with custom names., and click on Create Stack button 49 | 7. Wait for the stack get into status CREATE_COMPLETE 50 | 8. Under the Outputs tab, _take a note_ of **Locust master instance DNS** 51 | 9. Open in a new tab the locust page utilizing the **DNS** noted 52 | 53 | ## Testing 54 | 1. Now, you are going to do some load test to see the behavior of the application 55 | 2. On the **Locust** page, after the number of slaves is the total you asked for, start simulating _10000 users_ with a _hacth rate of 50_ 56 | 3. Look at the CloudWatch metrics of **Application Load Balancer** and **EC2** to see how it works 57 | 4. Repeat the test with different parameters to collect more metrics if necessary 58 | 59 | ## Launch Template 60 | 1. Go to EC2 console at https://console.aws.amazon.com/ec2 61 | 2. Go to your **AppInstance**, Actions, Instance setting and copy the user data 62 | 3. Create a new **Launch Template** 63 | 4. Select **Amazon Linux 2 64-bit (x86)** at Amazon machine image (AMI) field 64 | 5. On Instance type select **t3.micro** 65 | 6. At the networking stage select **VPC** and **WebSg** 66 | 7. Add new tag _Name_ with value of **webapp** 67 | 8. Go to Advanced Details and paste the **user data** 68 | 9. Click on **Create** 69 | 70 | ## AutoScaling 71 | 1. Go to **AutoScaling group** and click on create 72 | 2. Select **Launch Template** and filter your launch template created on the step before 73 | 3. On the **Network** stage, select the app VPC **E.g.:stackname_VPC** 74 | 4. Selct **Private1** and **Private2** subnets 75 | 5. At the Advanced Details, check _Receive traffic from one or more load balancers_ and select the **tg-app** target group 76 | 6. Check **Health Check** as **ELB** 77 | 7. Go to Review and Create 78 | 8. Now, we can create a new CloudWatch alarm and associate with the AutoScaling 79 | 9. Go to https://console.aws.amazon.com/cloudwatch 80 | 10. Select **Alarms** and create a new one 81 | 11. Select the metrics you want, on this example, we are going to select **ApplicationELB -> Per AppELB -> TargetResponseTime** 82 | 12. Minimize period to 1 minute and on conditions put greater than **0.3** 83 | 13. Next, create a new SNS topic with your email and create 84 | 14. Open a new tab, go to AutoScaling Group that we created 85 | 15. Select **Scaling Policies** and Add Policy 86 | 16. Slect __Create a scaling policy with steps__ and select the alarm you created 87 | 17. at **Take Action** Add two new instances and create 88 | 18. Edit you AutoScaling group and change **Max** to 5 89 | 18. You can do the same to remove instances when the alarm do not shoot 90 | 91 | ## Testing 92 | 1. Open the **Locust** page 93 | 2. Create a new test with more users and see the application scaling 94 | 95 | ## Next Steps 96 | * We are not utilizing on this demo, but, **CloudFront** could minimize the load on the EC2 instances, delivering the static content 97 | * We are utilizing **t3.micro** instances, we need to do a resizing based on the application behavior and number of users 98 | * To utilize your domain, we could use **Route53** 99 | * On the **Reference links** you can find some great getting started guid to help you setup Route53 and CloudFront 100 | 101 | ## Clean up 102 | 1. Delete the resources that you created (Route53, CloudFront, AutoScaling, CloudWatch) 103 | 2. Open the CloudFormation console at https://console.aws.amazon.com/cloudformation 104 | 3. Select the CloudFormation stack and click on Delete button 105 | 106 | ## Reference links 107 | * Scaling up to your first 10 million users: https://www.youtube.com/watch?v=Ma3xWDXTxRg 108 | * Getting started AutoScaling: https://docs.aws.amazon.com/autoscaling/ec2/userguide/GettingStartedTutorial.html 109 | * Locust: https://docs.locust.io/en/stable/ 110 | * Getting started with CloudFront: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.html 111 | * Getting started with Route53: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/getting-started.html 112 | 113 | ## License summary 114 | This sample code is made available under the MIT-0 license. See the LICENSE file. 115 | -------------------------------------------------------------------------------- /architecture/locust.drawio: -------------------------------------------------------------------------------- 1 | 7Vldb9ssFP41uVxl489cJm7yrlq3VWq1XVbEpg4qMRbGSbpfP7CxY7BTb11a9d0iRYrPAz7ncHgeAmTiRJv9fwzm6880QWQCrGQ/cS4nANiO54gviTzVSOC6NZAynKhOB+AW/0AKtBRa4gQVWkdOKeE418GYZhmKuYZBxuhO7/ZAiR41hynqAbcxJH30O074ukZDzzrgHxFO101k21ItG9h0VkCxhgnddSBnMXEiRimvnzb7CBFZvKYu9XvLI61tYgxl/FdeWNL51d23u6/F9OsnO99vbQSvPigvW0hKNeBvN5HKlz81RcgpznhVSG8uPiJOZE080RJJ6wJ4BmDagQ7YfUv60AHTDnTANt3bRnzbTLAD9CzNvWXEtzoJio8zpyUnOENRSzlLgCmDCRZTEVFCmcAymonqzdd8Q4Rli8fdGnN0m8NYVnUn5CKwB5pxRXobNLYqvPQqSJPL580+lfq6gLvCvUgZLfMq5JWg/WDr/TaP5euc0UfUpDQBDnDD0HZlIEyIkeoWMY4F82cEp9IrpzIIVBZBD1x6FPnjLL2urEvHUjl3Qsxm82AeCjyBxRolaiCKZyIE2h8lsN3KQqwniG4QZ0+iS/OCq5SklhIQKHt3EGbQyG/dESUIFQjVYpC2vg96EQ9KMr8hH9CTz025IjiW8ilXGeJnKf3/pVSguGSYP90fOt9Wumoc/5LIBL6YLp2FfzqltXFOrrRAV5oD+krznSGlWa+lNLentAnwiazTSjyk8mG2gT/EHAJrEbWNIlbb3mAJ3jbQNY3Lgnc6d9oGujcQboDPsOCIiZBfxManTYiZ/YVjPIBpno11YlAY5vw7YClLeEwwBv0uQ9+3nR5hVWeNQw1Br+EKkRtaYI4rdawo53QzyuBY5CLKokl2TJ6wyOuBPuC9zGNerZSILbaoXjDtYxrGWcFhFiNwGvK7vq2TPwA98rthn/sNdnLqe0epP0DN31bDKOn/VCPfKXtsNVI8J4lB4ZxFclKRFKcRiWNZ70sk/lkkZ5G8M5H4ALwvkQQ9kdyhegPz+pxpu/9lnCmLKtZJNh76mupb/V23Ox2gy/SV6BIOrKkGUVCSoqaOlPE1TWkGyeKAdicBZclMXsnJqSQ0fpQQWVV2M6MVPyDjZr8KXGLScdWxeicwz/XmvjwZiRNblhhHI5nz81MkhkhLFqPxTZlIK0V87NzSn/LOlLY3hgwRyPFWz21oUpW7G0nRzk+ycT3iTY2zWD0m9Vb3otBw5I45qgfdc1RxrB3jy2k3PdNu7Bg8Sjv/zWjnm6sWeCHtgjFHr0w7u3+Z98/wbpRPwZvxyQ09jQZu8NJlzHAEpuHb8ql/bl8QWPDqdvgqf4s916XlRXbw9+25UF3He5zfwyRhqDjRht2zphpl2p/UkR3Ycyw/sgMT5uGvv5pyhz9QncVP -------------------------------------------------------------------------------- /architecture/locust.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-ec2-autoscaling-php-app-demo/fa0447fa37efd2bf9f9aeaf62e6d11a9e13b9ac3/architecture/locust.jpg -------------------------------------------------------------------------------- /architecture/scaling: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /architecture/scaling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-ec2-autoscaling-php-app-demo/fa0447fa37efd2bf9f9aeaf62e6d11a9e13b9ac3/architecture/scaling.jpg -------------------------------------------------------------------------------- /cf-templates/template-app.yml: -------------------------------------------------------------------------------- 1 | Description: Template to create AutoScaling Web App demo 2 | 3 | ## Resources ## 4 | Resources: 5 | ## VPC public and private subnets (subnets, route tables, IGW, NAT) ## 6 | PubPrivateVPC: 7 | Type: 'AWS::EC2::VPC' 8 | Properties: 9 | CidrBlock: 172.31.0.0/16 10 | Tags: 11 | - Key: Name 12 | Value: !Join [_, [!Ref 'AWS::StackName', VPC]] 13 | PublicSubnet1: 14 | Type: 'AWS::EC2::Subnet' 15 | Properties: 16 | VpcId: !Ref PubPrivateVPC 17 | AvailabilityZone: !Select 18 | - '0' 19 | - !GetAZs '' 20 | CidrBlock: 172.31.0.0/24 21 | MapPublicIpOnLaunch: true 22 | Tags: 23 | - Key: Name 24 | Value: !Join [_, [!Ref 'AWS::StackName', Public1]] 25 | PublicSubnet2: 26 | Type: 'AWS::EC2::Subnet' 27 | Properties: 28 | AvailabilityZone: !Select 29 | - '1' 30 | - !GetAZs '' 31 | VpcId: !Ref PubPrivateVPC 32 | CidrBlock: 172.31.1.0/24 33 | MapPublicIpOnLaunch: true 34 | Tags: 35 | - Key: Name 36 | Value: !Join [_, [!Ref 'AWS::StackName', Public2]] 37 | PrivateSubnet1: 38 | Type: 'AWS::EC2::Subnet' 39 | Properties: 40 | VpcId: !Ref PubPrivateVPC 41 | AvailabilityZone: !Select 42 | - '0' 43 | - !GetAZs '' 44 | CidrBlock: 172.31.3.0/24 45 | MapPublicIpOnLaunch: false 46 | Tags: 47 | - Key: Name 48 | Value: !Join [_, [!Ref 'AWS::StackName', Private1]] 49 | PrivateSubnet2: 50 | Type: 'AWS::EC2::Subnet' 51 | Properties: 52 | VpcId: !Ref PubPrivateVPC 53 | AvailabilityZone: !Select 54 | - '1' 55 | - !GetAZs '' 56 | CidrBlock: 172.31.2.0/24 57 | MapPublicIpOnLaunch: false 58 | Tags: 59 | - Key: Name 60 | Value: !Join [_, [!Ref 'AWS::StackName', Private2]] 61 | InternetGateway: 62 | Type: 'AWS::EC2::InternetGateway' 63 | Properties: 64 | Tags: 65 | - Key: Name 66 | Value: !Join [_, [!Ref 'AWS::StackName', IGW]] 67 | GatewayToInternet: 68 | Type: 'AWS::EC2::VPCGatewayAttachment' 69 | Properties: 70 | VpcId: !Ref PubPrivateVPC 71 | InternetGatewayId: !Ref InternetGateway 72 | PublicRouteTable: 73 | Type: 'AWS::EC2::RouteTable' 74 | Properties: 75 | VpcId: !Ref PubPrivateVPC 76 | PublicRoute: 77 | Type: 'AWS::EC2::Route' 78 | DependsOn: GatewayToInternet 79 | Properties: 80 | RouteTableId: !Ref PublicRouteTable 81 | DestinationCidrBlock: 0.0.0.0/0 82 | GatewayId: !Ref InternetGateway 83 | PublicSubnet1RouteTableAssociation: 84 | Type: 'AWS::EC2::SubnetRouteTableAssociation' 85 | Properties: 86 | SubnetId: !Ref PublicSubnet1 87 | RouteTableId: !Ref PublicRouteTable 88 | PublicSubnet2RouteTableAssociation: 89 | Type: 'AWS::EC2::SubnetRouteTableAssociation' 90 | Properties: 91 | SubnetId: !Ref PublicSubnet2 92 | RouteTableId: !Ref PublicRouteTable 93 | NatGateway: 94 | Type: "AWS::EC2::NatGateway" 95 | DependsOn: NatPublicIP 96 | Properties: 97 | AllocationId: !GetAtt NatPublicIP.AllocationId 98 | SubnetId: !Ref PublicSubnet1 99 | NatPublicIP: 100 | Type: "AWS::EC2::EIP" 101 | DependsOn: PubPrivateVPC 102 | Properties: 103 | Domain: vpc 104 | PrivateRouteTable: 105 | Type: 'AWS::EC2::RouteTable' 106 | Properties: 107 | VpcId: !Ref PubPrivateVPC 108 | PrivateRoute: 109 | Type: 'AWS::EC2::Route' 110 | Properties: 111 | RouteTableId: !Ref PrivateRouteTable 112 | DestinationCidrBlock: 0.0.0.0/0 113 | NatGatewayId: !Ref NatGateway 114 | PrivateSubnet1RouteTableAssociation: 115 | Type: 'AWS::EC2::SubnetRouteTableAssociation' 116 | Properties: 117 | SubnetId: !Ref PrivateSubnet1 118 | RouteTableId: !Ref PrivateRouteTable 119 | PrivateSubnet2RouteTableAssociation: 120 | Type: 'AWS::EC2::SubnetRouteTableAssociation' 121 | Properties: 122 | SubnetId: !Ref PrivateSubnet2 123 | RouteTableId: !Ref PrivateRouteTable 124 | 125 | 126 | ## App layer (EC2 instance, ALB, Security groups, EC2 SSM Role) ## 127 | SecurityGroupHTTPALB: 128 | Type: 'AWS::EC2::SecurityGroup' 129 | Properties: 130 | GroupName: HTTPSgALB 131 | GroupDescription: HTTP(S) 132 | SecurityGroupIngress: 133 | - IpProtocol: tcp 134 | FromPort: '80' 135 | ToPort: '80' 136 | CidrIp: 0.0.0.0/0 137 | - IpProtocol: tcp 138 | FromPort: '443' 139 | ToPort: '443' 140 | CidrIp: 0.0.0.0/0 141 | VpcId: !Ref PubPrivateVPC 142 | Tags: 143 | - Key: Name 144 | Value: HTTPSgALB 145 | WebSecurityGroup: 146 | Type: 'AWS::EC2::SecurityGroup' 147 | Properties: 148 | GroupName: WebSg 149 | GroupDescription: HTTP(S) 150 | SecurityGroupIngress: 151 | - IpProtocol: tcp 152 | FromPort: '80' 153 | ToPort: '80' 154 | SourceSecurityGroupId: !Ref SecurityGroupHTTPALB 155 | - IpProtocol: tcp 156 | FromPort: '443' 157 | ToPort: '443' 158 | SourceSecurityGroupId: !Ref SecurityGroupHTTPALB 159 | VpcId: !Ref PubPrivateVPC 160 | Tags: 161 | - Key: Name 162 | Value: WebSg 163 | EC2Role: 164 | Type: 'AWS::IAM::Role' 165 | Properties: 166 | ManagedPolicyArns: 167 | - 'arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM' 168 | RoleName: EC2-SSM-SSH 169 | AssumeRolePolicyDocument: 170 | Version: 2012-10-17 171 | Statement: 172 | - Effect: Allow 173 | Principal: 174 | Service: 175 | - ec2.amazonaws.com 176 | Action: 177 | - 'sts:AssumeRole' 178 | Path: / 179 | EC2InstanceProfile: 180 | Type: 'AWS::IAM::InstanceProfile' 181 | Properties: 182 | Path: / 183 | Roles: 184 | - !Ref EC2Role 185 | EC2App: 186 | Type: 'AWS::EC2::Instance' 187 | DependsOn: 188 | - EC2InstanceProfile 189 | - MemCluster 190 | Properties: 191 | ImageId: ami-00dc79254d0461090 192 | InstanceType: t2.micro 193 | IamInstanceProfile: !Ref EC2InstanceProfile 194 | SubnetId: !Ref PrivateSubnet1 195 | SecurityGroupIds: 196 | - !Ref WebSecurityGroup 197 | UserData: 198 | Fn::Base64: 199 | !Sub | 200 | #!/bin/bash 201 | # Install dependecies 202 | sudo yum update -y 203 | sudo yum install php php-memcache git -y 204 | # Clone repository 205 | git clone https://github.com/aws-samples/aws-ec2-autoscaling-php-app-demo.git 206 | sudo mv aws-ec2-autoscaling-php-app-demo/src/app/* /var/www/html/ 207 | sudo mv aws-ec2-autoscaling-php-app-demo/src/image/ /var/www/html/ 208 | sudo mv aws-ec2-autoscaling-php-app-demo/src/css/ /var/www/html/ 209 | # Update config files 210 | sed -i 's/session.save_handler "files"/session.save_handler "memcache"/g' /etc/httpd/conf.d/php.conf 211 | sed -i 's%session.save_path "/var/lib/php/session"%session.save_path "${MemCluster.ConfigurationEndpoint.Address}:11211"%g' /etc/httpd/conf.d/php.conf 212 | # Start apache 213 | sudo service httpd start 214 | chkconfig httpd on 215 | Tags: 216 | - Key: Name 217 | Value: AppInstance 218 | TALB: 219 | Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' 220 | DependsOn: 221 | - EC2App 222 | Properties: 223 | Name: tg-app 224 | VpcId: !Ref PubPrivateVPC 225 | TargetType: instance 226 | Port: 80 227 | Protocol: HTTP 228 | HealthyThresholdCount: 2 229 | UnhealthyThresholdCount: 2 230 | HealthCheckTimeoutSeconds: 2 231 | HealthCheckIntervalSeconds: 5 232 | TargetGroupAttributes: 233 | - Key: deregistration_delay.timeout_seconds 234 | Value: 10 235 | Targets: 236 | - Id: !Ref EC2App 237 | ALB: 238 | Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' 239 | DependsOn: 240 | - TALB 241 | Properties: 242 | Subnets: 243 | - !Ref PublicSubnet1 244 | - !Ref PublicSubnet2 245 | Name: alb-app 246 | SecurityGroups: 247 | - !Ref SecurityGroupHTTPALB 248 | ALBListener: 249 | Type: 'AWS::ElasticLoadBalancingV2::Listener' 250 | Properties: 251 | DefaultActions: 252 | - Type: forward 253 | TargetGroupArn: !Ref TALB 254 | LoadBalancerArn: !Ref ALB 255 | Port: 80 256 | Protocol: HTTP 257 | 258 | ## Data layer (Elasticache memcached node, RDS MySQL) ## 259 | MemSecurityGroup: 260 | Type: 'AWS::EC2::SecurityGroup' 261 | Properties: 262 | GroupDescription: Security Group for Memcached 263 | SecurityGroupIngress: 264 | - IpProtocol: tcp 265 | FromPort: '11211' 266 | ToPort: '11211' 267 | SourceSecurityGroupId: !Ref WebSecurityGroup 268 | VpcId: !Ref PubPrivateVPC 269 | MemSubnetGroup: 270 | Type: 'AWS::ElastiCache::SubnetGroup' 271 | Properties: 272 | Description: Subnet Group for Memcached 273 | SubnetIds: 274 | - !Ref PrivateSubnet1 275 | - !Ref PrivateSubnet2 276 | MemCluster: 277 | Type: AWS::ElastiCache::CacheCluster 278 | Properties: 279 | CacheNodeType: cache.t2.micro 280 | VpcSecurityGroupIds: 281 | - !Ref MemSecurityGroup 282 | CacheSubnetGroupName: !Ref MemSubnetGroup 283 | ClusterName: web-memcached 284 | Engine: memcached 285 | NumCacheNodes: 1 286 | Port: 11211 287 | 288 | ## CDN layer (S3 bucket) ## 289 | S3Static: 290 | Type: 'AWS::S3::Bucket' 291 | Properties: 292 | BucketName: !Join [-, [!Ref 'AWS::StackName', !Ref 'AWS::AccountId']] 293 | 294 | ## Outputs ## 295 | Outputs: 296 | PubPrivateVPCID: 297 | Description: VPC ID 298 | Value: !Ref "PubPrivateVPC" 299 | Export: 300 | Name: AppVPCID 301 | ALBDNS: 302 | Description: URL to access the application 303 | Value: !GetAtt ALB.DNSName 304 | Export: 305 | Name: ALBAppURL 306 | StaticBucket: 307 | Description: Static assets 308 | Value: !Ref S3Static 309 | Export: 310 | Name: S3StaticBucket -------------------------------------------------------------------------------- /cf-templates/template-test.yml: -------------------------------------------------------------------------------- 1 | Description: Template to create VPC and EC2 resources for load test 2 | 3 | Metadata: 4 | AWS::CloudFormation::Interface: 5 | ParameterGroups: 6 | - 7 | Label: 8 | default: "Network Configuration" 9 | Parameters: 10 | - VPCName 11 | - CIDRVpc 12 | - CIDRPublicA 13 | - CIDRPublicB 14 | - 15 | Label: 16 | default: "Locust Configuration" 17 | Parameters: 18 | - TargetEndpoint 19 | - Slaves 20 | 21 | Parameters: 22 | VPCName: 23 | Type: String 24 | Default: VPC-Locust 25 | Description: 'VPC name. Default: VPC-Locust' 26 | CIDRVpc: 27 | Type: String 28 | Default: 10.100.0.0/16 29 | Description: VPC CIDR 30 | AllowedPattern: '(\d{1,3})[.](\d{1,3})[.](\d{1,3})[.](\d{1,3})[/](\d{1,2})' 31 | ConstraintDescription: Must be a CIDR range a.b.c.d/x. 32 | CIDRPublicA: 33 | Type: String 34 | Default: 10.100.11.0/24 35 | Description: VPC CIDR Subnet Public-A 36 | AllowedPattern: '(\d{1,3})[.](\d{1,3})[.](\d{1,3})[.](\d{1,3})[/](\d{1,2})' 37 | ConstraintDescription: Must be a CIDR range a.b.c.d/x. 38 | CIDRPublicB: 39 | Type: String 40 | Default: 10.100.21.0/24 41 | Description: VPC CIDR Subnet Public-B 42 | AllowedPattern: '(\d{1,3})[.](\d{1,3})[.](\d{1,3})[.](\d{1,3})[/](\d{1,2})' 43 | ConstraintDescription: Must be a CIDR range a.b.c.d/x. 44 | TargetEndpoint: 45 | Type: String 46 | Description: Endpoint of target host 47 | Default: https://www.yourwebsite.com 48 | Slaves: 49 | Type: Number 50 | Default: 10 51 | Description: 'Quantity of slaves' 52 | 53 | Mappings: 54 | RegionAndResource: 55 | us-east-1: 56 | image: ami-0b898040803850657 57 | 58 | Resources: 59 | VPC: 60 | Type: 'AWS::EC2::VPC' 61 | Properties: 62 | CidrBlock: !Ref CIDRVpc 63 | EnableDnsSupport: true 64 | EnableDnsHostnames: true 65 | Tags: 66 | - Key: Name 67 | Value: !Ref VPCName 68 | IGW: 69 | Type: 'AWS::EC2::InternetGateway' 70 | Properties: 71 | Tags: 72 | - Key: Name 73 | Value: !Join 74 | - '' 75 | - - IGW- 76 | - !Ref VPCName 77 | - Key: VPC 78 | Value: !Ref VPCName 79 | IGWAttach: 80 | Type: 'AWS::EC2::VPCGatewayAttachment' 81 | Properties: 82 | VpcId: !Ref VPC 83 | InternetGatewayId: !Ref IGW 84 | RouteTablePubA: 85 | Type: 'AWS::EC2::RouteTable' 86 | Properties: 87 | VpcId: !Ref VPC 88 | Tags: 89 | - Key: Name 90 | Value: Route-Table-Public-A 91 | - Key: VPC 92 | Value: !Ref VPCName 93 | RouteIGWA: 94 | Type: 'AWS::EC2::Route' 95 | DependsOn: IGW 96 | Properties: 97 | RouteTableId: !Ref RouteTablePubA 98 | DestinationCidrBlock: 0.0.0.0/0 99 | GatewayId: !Ref IGW 100 | RouteTablePubB: 101 | Type: 'AWS::EC2::RouteTable' 102 | Properties: 103 | VpcId: !Ref VPC 104 | Tags: 105 | - Key: Name 106 | Value: Route-Table-Public-B 107 | - Key: VPC 108 | Value: !Ref VPCName 109 | RouteIGWB: 110 | Type: 'AWS::EC2::Route' 111 | DependsOn: IGW 112 | Properties: 113 | RouteTableId: !Ref RouteTablePubB 114 | DestinationCidrBlock: 0.0.0.0/0 115 | GatewayId: !Ref IGW 116 | SubnetPubA: 117 | Type: 'AWS::EC2::Subnet' 118 | Properties: 119 | VpcId: !Ref VPC 120 | CidrBlock: !Ref CIDRPublicA 121 | AvailabilityZone: !Select 122 | - '0' 123 | - !GetAZs '' 124 | MapPublicIpOnLaunch: true 125 | Tags: 126 | - Key: Name 127 | Value: PubSubnet-A 128 | - Key: VPC 129 | Value: !Ref VPCName 130 | RouteTableAssociationPubA: 131 | Type: 'AWS::EC2::SubnetRouteTableAssociation' 132 | Properties: 133 | SubnetId: !Ref SubnetPubA 134 | RouteTableId: !Ref RouteTablePubA 135 | SubnetPubB: 136 | Type: 'AWS::EC2::Subnet' 137 | Properties: 138 | VpcId: !Ref VPC 139 | CidrBlock: !Ref CIDRPublicB 140 | AvailabilityZone: !Select 141 | - '1' 142 | - !GetAZs '' 143 | MapPublicIpOnLaunch: true 144 | Tags: 145 | - Key: Name 146 | Value: PubSubnet-B 147 | - Key: VPC 148 | Value: !Ref VPCName 149 | RouteTableAssociationPubB: 150 | Type: 'AWS::EC2::SubnetRouteTableAssociation' 151 | Properties: 152 | SubnetId: !Ref SubnetPubB 153 | RouteTableId: !Ref RouteTablePubB 154 | SecurityGroupHTTPEC2: 155 | Type: 'AWS::EC2::SecurityGroup' 156 | Properties: 157 | GroupName: HTTPSgEC2 158 | GroupDescription: HTTP(S) 159 | SecurityGroupIngress: 160 | - IpProtocol: tcp 161 | FromPort: '80' 162 | ToPort: '80' 163 | CidrIp: 0.0.0.0/0 164 | - IpProtocol: tcp 165 | FromPort: '443' 166 | ToPort: '443' 167 | CidrIp: 0.0.0.0/0 168 | - IpProtocol: -1 169 | CidrIp: !Ref CIDRVpc 170 | VpcId: !Ref VPC 171 | Tags: 172 | - Key: Name 173 | Value: HTTPSgEC2 174 | EC2Master: 175 | Type: 'AWS::EC2::Instance' 176 | Properties: 177 | ImageId: !FindInMap 178 | - RegionAndResource 179 | - !Ref 'AWS::Region' 180 | - image 181 | InstanceType: c5.large 182 | SubnetId: !Ref SubnetPubA 183 | SecurityGroupIds: 184 | - !Ref SecurityGroupHTTPEC2 185 | UserData: 186 | Fn::Base64: 187 | !Sub | 188 | #!/bin/bash -xe 189 | sudo su 190 | yum update -y 191 | yum -y groupinstall 'Development Tools' 192 | curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py 193 | python get-pip.py 194 | pip install virtualenv 195 | virtualenv locustenv 196 | source locustenv/bin/activate 197 | pip install locustio==0.13.5 198 | cat >locustfile.py <locustfile.py < 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | AWS Demo HUB 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | 55 |
56 |
57 |
58 | 59 | 60 | -------------------------------------------------------------------------------- /src/app/login.php: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /src/app/logout.php: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /src/app/stress.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/welcome.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | AWS Demo HUB 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 | 75 |
76 |
77 |
78 | 79 | 80 | -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --input-padding-x: 1.5rem; 3 | --input-padding-y: .75rem; 4 | } 5 | 6 | body { 7 | background: linear-gradient(to right, #5828C9, #E17BE5); 8 | } 9 | 10 | .card-signin { 11 | border: 0; 12 | border-radius: 1rem; 13 | box-shadow: 0 0.5rem 1rem 0 rgba(0, 0, 0, 0.1); 14 | } 15 | 16 | .card-signin .card-title { 17 | margin-bottom: 2rem; 18 | font-weight: 300; 19 | font-size: 1.5rem; 20 | } 21 | 22 | .card-signin .card-body { 23 | padding: 2rem; 24 | } 25 | 26 | .form-signin { 27 | width: 100%; 28 | } 29 | 30 | .form-signin .btn { 31 | font-size: 80%; 32 | border-radius: 5rem; 33 | letter-spacing: .1rem; 34 | font-weight: bold; 35 | padding: 1rem; 36 | transition: all 0.2s; 37 | } 38 | 39 | .form-label-group { 40 | position: relative; 41 | margin-bottom: 1rem; 42 | } 43 | 44 | .form-label-group input { 45 | height: auto; 46 | border-radius: 2rem; 47 | } 48 | 49 | .form-label-group>input, 50 | .form-label-group>label { 51 | padding: var(--input-padding-y) var(--input-padding-x); 52 | } 53 | 54 | .form-label-group>label { 55 | position: absolute; 56 | top: 0; 57 | left: 0; 58 | display: block; 59 | width: 100%; 60 | margin-bottom: 0; 61 | /* Override default `