Sample containerized nuxt-laravel project on AWS with terraform.
The project consists of a containerized Laravel + NuxtJS three-tier architecture running on ECS Fargate on top of VPC subnets across two availability zones.
Each Laravel task runs an Nginx container that forwards traffic to another container running a PHP FPM process. For Nuxt SSR to work, frontend tasks require access to backend instances. This is solved with Route53 and CloudMap for service discovery by configuring the backend's ECS service to automatically register all tasks to Route53 under a friendly DNS name that is later injected as an environment variable into frontend tasks to communicate with them.
The Application Load Balancer handles incoming internet requests and forwards them to the appropriate target group tasks. Any request path that starts with /api/
is forwarded to the backend's target group, any other path ends in the frontend's.
NAT gateway gets created inside a public subnet to provide backend tasks and RDS internet access while in private subnets.
The CI/CD flow starts with a new code push to the repository's master branch, which triggers a new CodePipeline release via webhook. The CodePipeline steps are as follows:
- Fetch and store the repository's source code in an S3 bucket.
- Start two CodeBuild processes for building the frontend and backend's docker images and push them into their respective ECR repositories.
- Start two new CodeBuild processes for initializing ECS deployments with the new frontend and backend images.
- ECS updates each ECS service with the new images and executes a rolling deployment strategy to ensure zero downtime.
-
Run
cd frontend
-
Run
cp .env.example .env
-
Run
cd ../backend
-
Run
cp .env.example .env
-
Run
docker-compose up
-
Run
docker-compose exec backend composer install
-
Run
docker-compose exec php artisan key:generate
-
Check the app running at http://localhost:3000. You should see the following screen:
-
Run
cd terraform
-
Run
cp terraform.tfvars.example terraform.tfvars
-
Login into AWS account and create a user with programmatic access and attach the Administrator access policy (arn:aws:iam::aws:policy/AdministratorAccess). This user will be used by Terraform to provision infrastructure. Copy the access key ID and secret access key and paste them into terraform.tfvars and then fill in the other variables.
-
Run
terraform init
to download dependencies. -
Run
terraform apply --auto-approve
and wait a few minutes until all resources are successfully created. Take note of thealb_dns_name
output once the apply is finished. -
Go to Codepipeline->Settings->Connections on AWS console or follow this link. Click on the recently created Github connection that is currently pending, and then click on "Update pending connection" to allow access to the Github repository.
-
Find the recently created pipeline that should be in a "Failed" state because it started executing without the Github connection being approved by clicking on either the backend or frontend service and then going to the "Deployment" tab.
-
Wait for the pipeline to finish. You can see the status of the deployment through the ECS cluster here and clicking any of the backend or frontend services and then going to the "Deployment" tab.
-
Once the deployment finishes, enter the value of
alb_dns_name
that you noted earlier into the browser, and you should see the base NuxtJS landing page with a message below that says "Response: DB connected", which means that Laravel was able to connect to the RDS database. The response was rendered server-side, so it means that the frontend task was also able to communicate with a backend task. -
To delete resources, run
terraform destroy --auto-approve
and once the destroy is completed, delete the user that you previously created.