Deploy Dockerized Laravel Application Sử Dụng AWS ECS + AWS CodeBuild Chuyên mục Devops 2024-06-04 96 Lượt xem 90 Lượt thích 1 Bình luận
Introduction
Hi! Xin chào tất cả các bạn hôm nay chúng ta sẽ cùng nhau build một dự án Laravel sử dụng Dockerized, AWS RDS, ECR, ECS, Load Balancers, ECS Cluster, Task Definitions, Target Groups, Route53, AWS ACM, IAM, GitHub …
Và dưới đây là nội dung mà chúng ta sẽ thực hành trong bài hướng dẫn này:
- Introduction
- Github Setup
- Laravel Project Setup
- Docker Walkthrough
- Install AWS CLI
- Laravel Deployment Prep
- AWS RDS Create MySQL Database
- Docker Base Images
- AWS ECR Repository for Base Image
- Create Build Trigger for AWS ECR using CodeBuild
- AWS CodeBuild buildspec.yml
- AWS CodeBuild IAM Policy
- Prod Image
- AWS ECS Cluster
- AWS Task Definition
- Running AWS Task Definition
- AWS ECS Injecting .env Variables
- What is AWS Load Balancer
- What is AWS Target Group
- Creating AWS Target Groups
- Creating AWS Load Balancer
- Configure AWS ECS to use Load Balancer
- Purchase Domain using AWS Route53
- Setting Domain with AWS Load Balancer
- SSL Certificate using AWS Certificate Manager ACM
- Enabling HTTPS
- Redirect HTTP to HTTPS using AWS Load Balancer
Github Setup
Chúng ta sẽ có 2 private repo, một repo cho base image và một repo chứa ứng dụng laravel của chúng ta.
Các bạn hãy xem video để xem cách tạo 2 repo này cũng như là nội dụng bên trong của từng repo nhé!
Dưới đây là từng repo:
- laravel-docker-api-aws-deployment
- laravel-api-base-image
Laravel Project Setup
Link github Laravel project: Link
.
Trong phần này mình sẽ giải thích về docker trong dự án Laravel, cũng như run Laravel app của chúng ta dưới local, các bạn xem video để hiểu chi tiết hơn nhé!
Install AWS CLI
Để chúng ta có thể push image của chúng ta tên ECR chúng ta cần phải cài đặt AWS CLI dưới máy host của chúng ta các bạn vào link sau để cài đặt nhé: Link. Tuỳ thuộc vào OS của các bạn đang sử dụng hãy cài đặt AWS CLI phù hợp.
Laravel Deployment Prep
Các bạn hãy copy .env ra một file có tên là .env.prod sau đó thì sửa các config sau:
APP_ENV=prod
APP_DEBUG=false
DB_CONNECTION=mysql
DB_HOST= // RDS host
DB_PORT=3306
DB_DATABASE= // RDS database name
# DB_USERNAME= // sử dụng trong aws Secret manager
# DB_PASSWORD= // sử dụng trong aws Secret manager
AWS RDS Create MySQL Database
Trong phần này chúng ta sẽ tạo ra một RDS mysql cho production và sau đó chúng ta cấu hình những thông tin của DB này trong .env.prod
DB_CONNECTION=mysql
DB_HOST=prod-laravel-api.c4aoqn5ai7fd.ap-southeast-1.rds.amazonaws.com
DB_PORT=3306
DB_DATABASE=prod_laravel_api
# DB_USERNAME= // Sử dụng trong aws Secret manager
# DB_PASSWORD= // Sử dụng trong aws Secret manager
Docker Base Images
Hãy copy Dockerfile và Makefile từ project sang bên thư mục của base image.
Dockerfile
# Used for prod build.
FROM php:8.1-fpm as php
# Set environment variables
ENV PHP_OPCACHE_ENABLE=1
ENV PHP_OPCACHE_ENABLE_CLI=0
ENV PHP_OPCACHE_VALIDATE_TIMESTAMPS=0
ENV PHP_OPCACHE_REVALIDATE_FREQ=0
# Install dependencies.
RUN apt-get update && apt-get install -y unzip libpq-dev libcurl4-gnutls-dev nginx libonig-dev
# Install PHP extensions.
RUN docker-php-ext-install mysqli pdo pdo_mysql bcmath curl opcache mbstring
# Copy composer executable.
COPY --from=composer:2.3.5 /usr/bin/composer /usr/bin/composer
Makefile
.PHONY: help
help: ## Print help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n\nTargets:\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-10s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
Sau đó thì có thể build và chạy thử.
AWS ECR Repository For Base Image
Bước này các bạn hãy vào trong ECR của aws và tạo một repository
Các bạn nhớ chọn private và nhập tên repository và sau đó click create
Sau đó các bạn click vào repository mà các bạn vừa tạo vào click vào Push commands for laravel-api-base-image
Để chúng ta có thể push image từ local lên ECR chúng ta cần phải đăng nhập vào AWS CLI hãy sử dụng user credentials với permission phù hợp.
aws configure
Sau khi đăng nhập thành công các bạn có thể chạy từng cmd bên trên hoặc hãy viết nó nó vào file Makefile như dưới đây để có thể thực thi một cách dễ dàng:
Makefile:
.PHONY: help
help: ## Print help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n\nTargets:\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-10s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
auth: ## auth with aws
aws ecr get-login-password --region ap-southeast-1 | docker login --username AWS --password-stdin 178108053885.dkr.ecr.ap-southeast-1.amazonaws.com
build-push:
make build
make push
build: ## build image
docker build -t laravel-api-base-image .
push: ## push image to ECR
docker tag laravel-api-base-image:latest 178108053885.dkr.ecr.ap-southeast-1.amazonaws.com/laravel-api-base-image:latest
docker push 178108053885.dkr.ecr.ap-southeast-1.amazonaws.com/laravel-api-base-image:latest
Sau đó thì thử thay thế base image này vào trong base laravel image của các bạn để test thử như bên dưới:
FROM 178108053885.dkr.ecr.ap-southeast-1.amazonaws.com/laravel-api-base-image:latest as php
Sau đó thì build thử nhé!
Create Build Trigger for AWS ECR using CodeBuild
AWS CodeBuild: Là một dịch vụ xây dựng liên tục (Continuous Integration) tập trung vào biên dịch mã nguồn, chạy kiểm thử, và tạo các gói phần mềm sẵn sàng triển khai. Nó cung cấp môi trường để chạy các tác vụ xây dựng và kiểm thử phần mềm.
- AWS CodeBuild:
- Biên dịch mã nguồn: Chạy các tác vụ biên dịch cho các ngôn ngữ lập trình khác nhau.
- Chạy kiểm thử: Thực hiện các bài kiểm thử đơn vị, kiểm thử tích hợp.
- Tạo gói phần mềm: Tạo ra các gói phần mềm để sẵn sàng triển khai.
- Môi trường xây dựng: Cung cấp môi trường xây dựng tùy chỉnh hoặc có sẵn.
Sau khi làm xong tất cả các bước bên trên các bạn hãy click vào button Create build project.
AWS CodeBuild buildspec.yml
Sau khi đã tạo xong codebuild trên aws thì chúng ta cần phải tạo file buildspec.yml trong thư mục root của base image của chúng ta như sau: Các bạn có thể tìm cấu trúc viết file buildspec.yml ở trên doc của aws Buildspec syntax.
version: 0.2
phases:
install:
on-failure: ABORT
runtime-versions:
php: 8.2
commands:
- echo 'install'
# steps:
pre_build:
on-failure: ABORT | CONTINUE
commands:
- echo 'pre_build'
# steps:
build:
on-failure: ABORT
commands:
- echo 'build'
# steps:
post_build:
on-failure: ABORT
commands:
- echo 'post_build'
# steps:
Chỉ cần giữ lại phases là đủ và chỉnh lại theo cấu hình sau để nó có thể tự động build một base image cho chúng ta.
version: 0.2
phases:
install:
on-failure: ABORT
runtime-versions:
php: 8.2
# steps:
pre_build:
on-failure: ABORT
commands:
- echo 'Login to AWS ECR'
- aws ecr get-login-password --region ap-southeast-1 | docker login --username AWS --password-stdin 178108053885.dkr.ecr.ap-southeast-1.amazonaws.com
# steps:
build:
on-failure: ABORT
commands:
- docker build -t laravel-api-base-image .
- docker tag laravel-api-base-image:latest 178108053885.dkr.ecr.ap-southeast-1.amazonaws.com/laravel-api-base-image:latest
# steps:
post_build:
on-failure: ABORT
commands:
- echo 'post_build'
- docker push 178108053885.dkr.ecr.ap-southeast-1.amazonaws.com/laravel-api-base-image:latest
Cơ bản thì chúng ta có thể viết như trên nhưng để dễ dàng quản lý tag và bảo mật, dễ tái sử dụng hơn chúng ta nên viết vào trong codebuild env.
Chúng ta hãy chỉnh sửa lại codebuild project và thêm các env như hình dưới đây:
Sau khi chúng ta thiết lập xong ENV trên codebuild hãy thay thế nó vào trong file buildspec.yml như dưới đây:
version: 0.2
phases:
install:
on-failure: ABORT
runtime-versions:
php: 8.2
# steps:
pre_build:
on-failure: ABORT
commands:
- echo 'Login to AWS ECR'
- aws ecr get-login-password --region ap-southeast-1 | docker login --username AWS --password-stdin $REPONSITORY
# steps:
build:
on-failure: ABORT
commands:
- docker build -t $IMAGE .
- docker tag $IMAGE:$IMAGE_TAG $REPONSITORY/$IMAGE:$IMAGE_TAG
# steps:
post_build:
on-failure: ABORT
commands:
- echo 'post_build'
- docker image ls -a
- docker push $REPONSITORY/$IMAGE:$IMAGE_TAG
Sau khi sửa và các bạn push code lên github, codebuild sẽ trigger và build image cho các bạn, nhưng các bạn sẽ gặp lỗi sau:
AWS CodeBuild IAM Policy
Để fix được lỗi như mình có show ở trên thì các bạn cần phải thêm policy cho role mà lần đầu khi chúng ta tạo codebuild nó đã tạo cho chúng ta, các bạn xem các bước dưới đây:
Sau khi thêm xong thì các bạn rebuild lại codebuild như hình dưới nếu trường hợp sau khi mà rebuild mà vẫn bị lỗi thì hãy thay đổi code ở dưới local và push lại lên github để codebuild nó trigger lại nhé.
Prod Image
Tạo ECR:
Tạo Codebuild: Tương tự như tạo codebuild base image, có một lưu ý khi chọn Service role thì hãy chọn role giống với base image để có quyền sử dụng CLI, sau đó thì các bạn nhớ chỉnh sửa các ENV trong codebuild của prod laravel api như hình dưới nhé với các value lấy từ ECR.
Cấu hình buildspec.yml cho prod laravel api:
version: 0.2
phases:
install:
on-failure: ABORT
runtime-versions:
php: 8.2
# steps:
pre_build:
on-failure: ABORT
commands:
- echo 'Login to AWS ECR'
- aws ecr get-login-password --region ap-southeast-1 | docker login --username AWS --password-stdin $REPONSITORY
# steps:
build:
on-failure: ABORT
commands:
- docker build -t $IMAGE .
- docker tag $IMAGE:$IMAGE_TAG $REPONSITORY/$IMAGE:$IMAGE_TAG
# steps:
post_build:
on-failure: ABORT
commands:
- echo 'post_build'
- docker image ls -a
- docker push $REPONSITORY/$IMAGE:$IMAGE_TAG
AWS ECS Cluster
AWS Elastic Container Service (ECS) Cluster là một nhóm logic của các tài nguyên điện toán mà AWS ECS quản lý để chạy các ứng dụng container. Dưới đây là một số điểm quan trọng về ECS Cluster:
1. Định Nghĩa
- ECS Cluster: Là một tập hợp các tài nguyên (EC2 instances hoặc AWS Fargate) được nhóm lại để chạy các container. Cluster có thể chứa nhiều loại tài nguyên khác nhau và có thể mở rộng hoặc thu hẹp linh hoạt dựa trên nhu cầu.
2. Thành Phần
- Container Instances: Là các EC2 instances đã được đăng ký vào ECS Cluster. Trên các instances này, Docker daemon chạy và có thể thực hiện các tác vụ container.
- Tasks: Một task là một đơn vị triển khai của một hoặc nhiều container. Task được định nghĩa bởi Task Definition.
- Services: Một service quản lý và duy trì một số lượng task cụ thể, đảm bảo rằng số lượng task này luôn chạy, dù có lỗi xảy ra.
3. Chế Độ Hoạt Động
- EC2 Launch Type: Các container chạy trên các EC2 instances do bạn quản lý. Bạn có toàn quyền kiểm soát cấu hình và quản lý các instances này.
- Fargate Launch Type: Một dịch vụ serverless mà AWS quản lý hoàn toàn các tài nguyên điện toán cho bạn. Bạn chỉ cần xác định cấu hình tài nguyên và AWS sẽ lo phần còn lại.
4. Lợi Ích
- Quản lý dễ dàng: ECS Cluster giúp bạn dễ dàng quản lý và điều phối các container. Bạn có thể thêm hoặc bớt các tài nguyên điện toán mà không ảnh hưởng đến các container đang chạy.
- Tự động hóa: ECS tích hợp với các dịch vụ khác của AWS như Elastic Load Balancing, IAM, và CloudWatch để tự động hóa việc triển khai, quản lý và giám sát các container.
- Tính mở rộng: ECS Cluster có thể tự động mở rộng hoặc thu hẹp tài nguyên dựa trên nhu cầu, giúp bạn tối ưu hóa chi phí và hiệu suất.
5. Tích Hợp Với Các Dịch Vụ Khác
- Elastic Load Balancing: Tích hợp với Application Load Balancer (ALB) và Network Load Balancer (NLB) để phân phối lưu lượng tới các container.
- IAM Roles: Sử dụng IAM roles để quản lý quyền truy cập và bảo mật cho các container.
- CloudWatch: Giám sát và thu thập logs, metrics để theo dõi hiệu suất và tình trạng của các container.
6. Sử Dụng ECS Cluster
- Tạo Cluster: Bạn có thể tạo ECS Cluster thông qua AWS Management Console, AWS CLI, hoặc AWS SDKs.
- Đăng ký EC2 Instances: Nếu bạn sử dụng EC2 Launch Type, bạn cần đăng ký các EC2 instances vào cluster.
- Triển khai Tasks và Services: Bạn định nghĩa các task và service để triển khai và quản lý các container trên cluster.
Tổng Kết
AWS ECS Cluster là một thành phần cốt lõi trong AWS ECS, cung cấp một cách tổ chức và quản lý các tài nguyên điện toán để chạy các ứng dụng container. Với khả năng quản lý linh hoạt, tích hợp tốt với các dịch vụ AWS khác và hỗ trợ cả chế độ EC2 và Fargate, ECS Cluster giúp đơn giản hóa việc triển khai, quản lý và mở rộng các ứng dụng container.
Các bạn hãy tạo cho mình một ECS cluster như hình dưới:
AWS Task Definition
AWS Task Definition là một thành phần quan trọng của AWS Elastic Container Service (ECS). Nó xác định cách chạy một task (tác vụ) cụ thể, bao gồm cấu hình chi tiết về container, tài nguyên và cài đặt mạng. Dưới đây là các khía cạnh chính của AWS Task Definition:
1. Định Nghĩa
- Task Definition: Là một tập hợp các hướng dẫn định nghĩa cách chạy một hoặc nhiều container Docker trong ECS. Nó là một đối tượng JSON mô tả thông tin về các container, thông số tài nguyên, và các thiết lập khác cần thiết để chạy một task.
2. Các Thành Phần Chính
-
Container Definitions: Xác định các container sẽ chạy trong một task. Bao gồm:
- Image: Định rõ hình ảnh Docker sẽ được sử dụng.
- CPU and Memory: Thiết lập lượng tài nguyên CPU và bộ nhớ mà container cần.
- Port Mappings: Xác định cổng mạng để container sử dụng.
- Environment Variables: Thiết lập các biến môi trường cho container.
- Volumes: Gắn kết các volume cho container.
-
Volumes: Xác định các volume được chia sẻ giữa các container trong cùng một task. Volume có thể là host volumes hoặc Docker volumes.
-
Networking Configuration: Thiết lập cấu hình mạng cho task, bao gồm VPC, subnets, và security groups nếu sử dụng chế độ mạng awsvpc.
-
Task Role: Xác định IAM role mà task sẽ sử dụng để cấp quyền truy cập vào các dịch vụ AWS khác.
-
Execution Role: Xác định IAM role mà ECS agent sử dụng để kéo images từ ECR và ghi logs vào CloudWatch.
3. Chế Độ Hoạt Động
- EC2 Launch Type: Các container chạy trên các EC2 instances do bạn quản lý.
- Fargate Launch Type: Một dịch vụ serverless mà AWS quản lý hoàn toàn các tài nguyên điện toán cho bạn. Bạn chỉ cần xác định cấu hình tài nguyên và AWS sẽ lo phần còn lại.
4. Lợi Ích
- Tái Sử Dụng: Task Definitions có thể được tái sử dụng nhiều lần để chạy các task giống nhau hoặc tương tự, giảm thiểu công sức cấu hình.
- Quản Lý Dễ Dàng: Tất cả các cấu hình cần thiết cho một task được lưu trữ ở một nơi duy nhất, giúp việc quản lý và cập nhật dễ dàng hơn.
- Tích Hợp Tốt Với Dịch Vụ Khác: Task Definitions có thể tích hợp với các dịch vụ khác như IAM, CloudWatch, và ELB, giúp tăng cường bảo mật và giám sát.
5. Sử Dụng Task Definition
- Tạo Task Definition: Bạn có thể tạo Task Definition thông qua AWS Management Console, AWS CLI, hoặc AWS SDKs.
- Cập Nhật Task Definition: Khi cần thay đổi, bạn tạo một phiên bản mới của Task Definition với các thay đổi mong muốn.
- Triển Khai Task: Task Definition được sử dụng để khởi chạy tasks trong ECS Cluster. Các service hoặc các tác vụ chạy độc lập đều dựa trên Task Definition này.
Tổng Kết
AWS Task Definition là một thành phần quan trọng trong ECS, cung cấp cấu hình chi tiết cho cách chạy các container. Nó giúp đảm bảo rằng tất cả các thông số cần thiết để chạy một task được định nghĩa rõ ràng và có thể tái sử dụng, giúp đơn giản hóa việc triển khai và quản lý các container trong môi trường ECS.
Sau khi chúng ta định nghĩa tên task definition, infastructure, chúng ta cần phải định nghĩa Container các bạn hãy sang ECR để copy image URI của image laravel prod api, như hình bên dưới!
Còn các thông số còn lại các bạn có thể custom thêm như là tăng CPU, MEM... Sau đó thì các bạn click vào Create để tạo task nhé!
Bước tiếp theo các bạn click vào create service như hình dưới:
Các bạn hãy truy cập bằng public IP:
NẾU các bạn muốn test khi thay đổi code xem cách nó hoạt động thì các các bạn làm theo hướng dẫn sau:
Bước 1: Thay đổi code
Bước 2: Kiểm tra codebuild
Bước 3: Update ECS task:
Các bạn click vào update
và chọn Force new deployment và click vào button update
Sau khi các bạn update nó sẽ tạo ra cho chúng ta 2 phiên bản, phiên bản mới là phiên bản (in progress) như các bạn thấy như hình dưới -> sau khi nó chuyển sang (completed) nó sẽ chuyển traffic sang phiên bản mới, còn phiên bản cũ sẽ được xoá đi.
Các bạn lưu ý rằng mỗi phiên bản được sinh ra nó sẽ có một IP public mới, để giải quyết vấn đề này chúng ta sẽ sử dụng ELB ở phần sau nhé!
Và đây là kết quả:
AWS ECS Injecting .env Variables
Các bạn còn nhớ khi chúng ta tạo .env.prod cho production.
DB_CONNECTION=mysql
DB_HOST=prod-laravel-api.c4aoqn5ai7fd.ap-southeast-1.rds.amazonaws.com
DB_PORT=3306
DB_DATABASE=prod_laravel_api
# DB_USERNAME= // Sử dụng trong aws Secret manager
# DB_PASSWORD= // Sử dụng trong aws Secret manager
Để cho bảo mật hơn cũng như là dễ quản lý chúng ta sẽ thiết lập DB_USERNAME VÀ DB_PASSWORD trong Secret manager, bây giờ chúng ta sẽ sử dụng nó trong ECS task Definitions, các bạn hãy vào trong Secret manager sẽ thấy những thông tin sau:
Tiếp theo hãy tạo mới một revision task Definitions từ task definition name là prod-laravel-api-task như hình dưới đây:
Sau đó hãy thêm những ENV sau:
//Secret ARN
arn:aws:secretsmanager:ap-southeast-1:178108053885:secret:rds!db-c11a0205-e891-47d5-8e9a-419fc7d4150c-JQ8kML
DB_USERNAME = Secret ARN:username::
//DB_USERNAME
arn:aws:secretsmanager:ap-southeast-1:178108053885:secret:rds!db-c11a0205-e891-47d5-8e9a-419fc7d4150c-JQ8kML:username::
//DB_PASSWORD
arn:aws:secretsmanager:ap-southeast-1:178108053885:secret:rds!db-c11a0205-e891-47d5-8e9a-419fc7d4150c-JQ8kML:password::
Tuỳ vào các bạn muốn chọn loại Value type: Value hoặc ValueFrom.
Sau khi hoàn thành việc setup ENVs chúng ta sẽ có một revision mới được sinh ra như hình bên dưới.
Chúng ta cần phải update new revision này trong service cluster như hình dưới:
Kết quả các bạn có thể thấy task:3 của chúng ta đã provisioning
Kết quả cuối cùng các bạn vào trong database và có thể nhìn thấy các tables đã được migrate.
AWS Load Balancer
Để chúng ta có thể áp dụng load balancer cho ECS của chúng ta , việc đầu tiên chúng ta cần làm là tạo target group để chuyển traffic lên ứng dụng của chúng ta
Creating AWS Target Groups
Các bạn hãy vào EC2 > Target group > IP addresses
Ở bước nhập IP này các bạn thêm .20 rồi click vào Include as pending below
Kết quả sẽ hiện ra như thế này cho các bạn:
Creating AWS Load Balancer
Các bạn hãy tạo ra một Application Load Balancer như hướng dẫn sau đây:
Configure AWS ECS To Use Load Balancer
Hãy xoá cluster Services và tạo lại theo cách dưới đây:
Các bạn phải chắc chắn nhập port mà các bạn đang expose từ container ví dụ của mình đang là 8080 nên mình để như bên dưới:
Bước tiếp theo dựa vào task definetion với revision vừa tạo các bạn tạo ra một service mới:
Sau đó thì cấu hình load balancer như hình dưới:
Và dưới đây là kết quả của chúng ta:
Setting Domain With AWS Load Balancer
Bước 1: Các bạn hãy copy DNS của AWS Load Balancer như hình dưới đây:
Bước 2: Tạo Record trong Route 53 service như hình dưới đây:
Sau đó chúng ta có thể tạo thêm một record cho www như hình dưới:
Và kết qủa của chúng ta:
SSL Certificate AWS Certificate Manager ACM
Các bạn hãy vào service ACM và thực hiện theo các bước sau:
Các bạn click vào xem detail certificate và click vào create records in Route 53
Enabling HTTPS
Sau khi AWS Certificate Manager (ACM) đã được active như hình thì chúng ta có thẻ enable HTTPS
Sau đó hãy vào load balancer vào thêm listener để Enable HTTPS như hình nhé :
Và đây là kết quả của chúng ta:
Redirect HTTP to HTTPs Using AWS Load Balancer
Các bạn hãy chọn listener HTTP:80 như hình dưới và chỉnh sửa
Bình luận (1)
Nhờ anh support. Em đã làm theo bài hướng dẫn của anh rồi. Tạo Clusters, Task và build version 2 dc rồi. Nhưng em lấy cái ip public ở task mới nhất dc build chạy thì ko dc http://13.250.64.111:8080. Nhờ anh support dùm em nha. Cảm ơn anh nhiều