blah
https://github.com/KunalShah21/hello-node-app
The goal is to create a simple node web app hosted with ECS Fargate.
This document will run through:
- Repo Structure
- Infrastructure - Design Considerations
- Infrastructure - Diagram
- Infrastructure - Future Considerations
- Application - Image
- Application - Steps to Build the Image Locally
- Application - Steps to Build the Image via CI/CD
- Application - Deploy AWS CDK via CI/CD
- .github - workflow for building and pushing to GHCR
- application - simple node application
- build - contains the dockerfile
- infrastructure - contains cdk script
- cf_templates - contains CF templates generated by AWS CDK
- Multi-region arch was not considered for this simple application but it can be made possible by having another load balancer and fargate cluster in the us-west region. The CDK was set up to handle this future implementation.
- Alarms and metrics should be created for this application
The infrastructure diagram can be found under ./Hello World Node App Infrastructure.png
- The website can be hit from https://dev.celeryfinance.com/ and it gets routed to a CloudFront Distribution
- This was created to allow caching of the webpage to help alleviate traffic going to ECS resulting in a performance increase and cost reduction
- The Distribution utilizes an ACM certificate and an alternative domain name for dev.celeryfinance.com
- CloudFront forwards the request to an Application Load Balancer origin
- The ALB is placed in a public subnet and accepts only HTTPS traffic
- Access logs for the ALB need to be enabled. The bucket is setup but I was working through an access error.
- The ALB terminates SSL at the load balancer and so any subsequent traffic to the target is over HTTP
- The ALB is deployed across 3 AZs
- ALB has health checks enabled at
/on port 3030 over HTTPS
- ALB routes traffic to an ECS Fargate Service placed within a private subnet
- We did not want anyone to access our ECS cluster directly so it was placed across private subnets.
- The cluster is deployed across three AZs with minimal resources assigned to it for a simple application
- The AWS Log Driver is enabled on each container to allow logs to be delivered to CloudWatch
- Container health checks are enabled at
http://localhost:3030/
- Any outgoing traffic needed for the Cluster will be through a NAT Gateway
- This is used to reach out to GitHub Container Registry
- NAT Gateway is only deployed in a single AZ for cost purposes. This will result in an additional cost for data transfer across AZs but with minimal traffic, it should be negligible.
- Egress traffic will go out to the internet over the NAT Gateway
- A CloudWatch Logs Group is created to store logs from the containers.
- The log group is encrypted to protect data and so a KMS CMK was created to encrypt any CloudWatch logs
- Retains data for 90 days
- We likely will not need to troubleshoot logs that are a fiscal quarter old
- S3 Bucket for Access Logs
- Encrypted with SSE-S3 due to ALB limitations
- Used for CloudFront access logs
- I could not set ALB access logs, I kept having a permission error even after setting the appropriate bucket policy and ensuring the bucket only had SSE-S3 encryption and not SSE-KMS
- Lifecycle Policy that transfers to IA after 30 days
- Lifecycle Bucket Policy that transfers to Glacier after 90 days
Thresholds would be found after continued monitoring via CloudWatch Dashboards.
These alarms would trigger an SNS Topic to send an email to relevant engineers/operations.
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cloudwatch-metrics.html
| Metric | Statistic | Period | Threshold | Description |
|---|---|---|---|---|
| CPUUtilization | Average | Some period of time | Some threshold | Ensure there is enough CPU allocated for the container to avoid performance degradation |
| MemoryUtilization | Average | Some period of time | Some threshold | Ensure there is enough memory allocated for the container to avoid performance degradation |
Thresholds would be found after continued monitoring via CloudWatch Dashboards.
These alarms would trigger an SNS Topic to send an email to relevant engineers/operations.
| Metric | Statistic | Period | Threshold | Description |
|---|---|---|---|---|
| HTTPCode_ELB_4XX_Count | Sum | Some period of time | Some threshold | Errors originating from the ALB |
| HTTPCode_ELB_5XX_Count | Sum | Some period of time | Some threshold | Errors originating from the ALB |
| HTTPCode_Target_4XX_Count | Sum | Some period of time | Some threshold | Errors originating from the target (ECS) |
| HTTPCode_Target_5XX_Count | Sum | Some period of time | Some threshold | Errors originating from the target (ECS) |
Resolve the issue with ALB access logs
- The application image is based on
registry.access.redhat.com/ubi8/nodejs-18-minimal:latest - The image copies the application source code and then installs dependencies
- Next, it switches the user back to the pre-defined Node user (1001) to ensure the image doesn’t run with the root user
- Lastly, it starts the application with npm commands specified in the package.json
- Ensure that Docker Desktop is installed and running
- Ensure that no other process is using port 3030 on your machine
- Locate the shell script located at buildAndRunImage.sh
- Run the script: ./buildAndRunImage.sh
- The script will spin up the container running at http://localhost:3030
- Navigate to http://localhost:3030 and you will see the application is up and running
- GitHub workflow is created to build and deploy the image to the GitHub Container Registry at ghcr.io/kunalshah21/hello-node-app:latest
- The workflow can be triggered by a push to the main branch or manually
- The workflow utilizes the following pre-defined actions created by Docker
- https://github.com/docker/login-action
- Logs into the GHCR with the GitHub Token configured by the GitLab for the repo
- The token has permissive privileges to allow reading AND writing to packages
- https://github.com/docker/build-push-action
- When pushing the image, it gets tagged with the IMAGE_TAG that should be updated each time the docker image is updated
- When pushing the image, it gets tagged with the latest tag
- When pushing the image, the workflow adds informative labels for metadata tracking and allows others to understand the purpose of the image
- There is no scanning implemented yet by the pipeline but if I had the time, I would implement two container scan solutions.
- Two solutions help ensure that there are no false flags between the two scanning solutions. This is an ideal scenario and we’d have to consider removing duplicate vulnerabilities as well to create one consolidated report.
- https://github.com/docker/login-action
There was no explicit call out to deploy CDK with CICD under the Requirements → CICD Pipeline section so I’ve instead been deploying to AWS locally to one of my DEV AWS accounts that is configured under an Org with SSO enabled.
The script runs CDK Synth and CDK Deploy on both of the CloudFormation Stacks. First, the Base Infrastructure Stack gets deployed and then the Web Hosting Stack gets deployed.
- Ensure you have an AWS CLIv2 profile set up with the appropriate credentials
- For me, this is accomplished through aws configure sso
- Locate the shell script located at infrastructure/deployDev.sh
- The script takes two arguments, region and profile.
- If you don’t provide either of these arguments, the script will gracefully error out
- Run the script: ./infrastructure/deployDev.sh