Terraform EKS CI/CD: Cloud-Native với AWS CodeBuild & ECR
Bài trước chúng ta đã xây dựng Hệ Thống AWS EKS kết hợp RDS, S3, Lambda và CI/CD với Jenkins .
Trong bài lab này, chúng ta sẽ thực hiện một cuộc "đại tu" kiến trúc: Loại bỏ Jenkins server truyền thống và thay thế bằng hệ sinh thái CI/CD Cloud-Native của AWS (CodeBuild, ECR). Toàn bộ hạ tầng sẽ được quản lý bằng Terraform, đảm bảo tính tự động hóa 100%, bảo mật tuyệt đối và tiết kiệm chi phí (Pay-as-you-go).
Dự án mẫu được sử dụng là OriShop (Spring Boot), triển khai trên Amazon EKS, sử dụng Amazon RDS (MySQL) và Amazon S3 (lưu trữ ảnh) thông qua cơ chế IRSA.
Phần 1: Khai báo Hạ tầng bằng Terraform (Infrastructure as Code)
Để hệ thống hoạt động đồng bộ, chúng ta cần định nghĩa kho chứa Docker Image (ECR), Quyền truy cập (IAM) và Dây chuyền Build (CodeBuild).
1. Tạo kho lưu trữ ECR (ecr.tf)
Thay vì dùng Docker Hub, chúng ta dùng ECR để tối ưu tốc độ và bảo mật trong nội mạng AWS.
resource "aws_ecr_repository" "orishop_repo" {
name = "orishop-repo"
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
tags = {
Environment = "Production"
Project = "com.quyenlt"
}
}
output "ecr_repository_url" {
value = aws_ecr_repository.orishop_repo.repository_url
}

2. Cấp "Lệnh bài" IAM cho CodeBuild (iam.tf)
CodeBuild cần quyền đẩy Image lên ECR và quyền gõ lệnh kubectl vào cụm EKS.
resource "aws_iam_role" "codebuild_role" {
name = "quyenlt-codebuild-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "codebuild.amazonaws.com" }
}]
})
}
resource "aws_iam_role_policy" "codebuild_policy" {
role = aws_iam_role.codebuild_role.name
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["ecr:*", "eks:DescribeCluster", "eks:ListClusters", "logs:*", "s3:*"]
Resource = "*"
}
]
})
}

3. Khai báo CodeBuild và Webhook (pipeline.tf)
Đoạn code này định nghĩa bộ máy CodeBuild và cấu hình Webhook để tự động trigger mỗi khi có code push lên nhánh main.
resource "aws_codebuild_project" "orishop_build" {
name = "orishop-build-project"
service_role = aws_iam_role.codebuild_role.arn
artifacts { type = "NO_ARTIFACTS" }
environment {
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
type = "LINUX_CONTAINER"
privileged_mode = true # Bắt buộc để build Docker
}
source {
type = "GITHUB"
location = "https://github.com/tobi1008/orishop.git"
buildspec = "buildspec.yml"
}
}
# Tự động kích hoạt khi Push vào nhánh main
resource "aws_codebuild_webhook" "orishop_webhook" {
project_name = aws_codebuild_project.orishop_build.name
build_type = "BUILD"
filter_group {
filter { type = "EVENT", pattern = "PUSH" }
filter { type = "HEAD_REF", pattern = "^refs/heads/main$" }
}
}

4. Nối quyền Admin EKS cho CodeBuild
Trong module EKS của Terraform, bổ sung access_entries để cho phép IAM Role của CodeBuild trở thành Admin của cluster.
access_entries = {
codebuild_admin = {
kubernetes_groups = []
principal_arn = aws_iam_role.codebuild_role.arn
policy_associations = {
admin_policy = {
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy"
access_scope = { type = "cluster" }
}
}
}
}

Phần 2: Cấu hình Source Code và Docker
1. Vượt Rate Limit của Docker Hub bằng AWS Public ECR
Khi build trên CodeBuild, IP của AWS dễ bị Docker Hub chặn do vượt quá 100 request/6 giờ (Lỗi 429). Giải pháp là dùng bản sao lưu trên kho AWS Public ECR.
Sửa file Dockerfile:
# Sử dụng kho Public ECR của AWS thay vì Docker Hub
FROM public.ecr.aws/docker/library/maven:3.9.4-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# Image siêu nhẹ cho production
FROM public.ecr.aws/docker/library/eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8091
ENTRYPOINT ["java", "-jar", "app.jar"]

2. Trái tim của Pipeline: buildspec.yml
File này đặt ở thư mục gốc của project, chỉ thị cho CodeBuild các bước đóng gói và triển khai. Đặc biệt chú ý bước kubectl apply để khởi tạo App trước khi set image nhằm tránh lỗi Deployment NotFound.
version: 0.2
phases:
install:
commands:
- curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
- chmod +x kubectl && mv kubectl /usr/local/bin/
pre_build:
commands:
- aws ecr get-login-password --region ap-southeast-1 | docker login --username AWS --password-stdin 808999395759.dkr.ecr.ap-southeast-1.amazonaws.com
- REPOSITORY_URI=808999395759.dkr.ecr.ap-southeast-1.amazonaws.com/orishop-repo
- IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
build:
commands:
- mvn clean package -DskipTests
- docker build -t $REPOSITORY_URI:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
- aws eks update-kubeconfig --name quyenlt-eks-cluster --region ap-southeast-1
# Khởi tạo App nếu chưa có
- kubectl apply -f orishop-eks.yaml
# Ghi đè phiên bản Image mới
- kubectl set image deployment/orishop-app orishop=$REPOSITORY_URI:$IMAGE_TAG

Phần 3: Triển Khai Thực Tế
1. Nạp Token GitHub cho AWS
Nếu bạn chưa tạo Token trên GitHub, hãy vào GitHub > Settings > Developer settings > Personal access tokens (classic) > Tạo 1 cái Token có tick chọn quyền repo và admin:repo_hook (quyền để AWS tự động gắn Webhook).
Sau khi có cái chuỗi Token (ghp_xxxxxxxx...)
Để Terraform có thể tạo Webhook và CodeBuild có thể kéo code, bạn cần cấp Token của GitHub cho tài khoản AWS thông qua CLI:
aws codebuild import-source-credentials \
--server-type GITHUB \
--auth-type PERSONAL_ACCESS_TOKEN \
--token <GITHUB_PAT_TOKEN> \
--region ap-southeast-1

Tới đây sau này bạn push code sẽ áp dụng cấu hình trên terraform và tạo ra webhook tới codebuild của AWS luôn rồi :

2. Apply Hạ Tầng
Mở Terminal ở thư mục Terraform và chạy:
terraform init
terraform apply
3. Cấu trúc Database (RDS)
Vì RDS khởi tạo trống trơn, ta cần dùng một Pod tạm trên EKS để bơm cấu trúc database ban đầu (datalast1.sql).
🛠️ Bước 1: Cập nhật lại Kubeconfig
Bro mở Terminal trên Mac và gõ lệnh này để AWS nạp lại địa chỉ cụm EKS mới vào máy:
aws eks update-kubeconfig --region ap-southeast-1 --name quyenlt-eks-cluster
🛠️ Bước 2: Tạo lại con Pod "Đặc vụ"
Vì cụm EKS hiện tại là cụm mới tinh tình tình, chưa có cái Pod nào bên trong cả, nên bro phải thả con tmp-mysql-client vào lại:
kubectl run tmp-mysql-client --image=mysql:8.0 --command -- sleep 3600
Import dữ liệu :
kubectl exec -i tmp-mysql-client -- mysql -h orishop-db.ch8mw2wuqs14.ap-southeast-1.rds.amazonaws.com -u admin -pOriShop12345! orishop < datalast1.sql
Bước 3 : Xóa giải phóng pod
kubectl delete pod tmp-mysql-client
Đi xem "Nhà máy" hoạt động
Bạn khoan vội sửa code, hãy tận hưởng thành quả setup đã.
-
Mở trình duyệt, đăng nhập vào AWS Console.
-
Gõ vào thanh tìm kiếm: CodeBuild và chọn nó.
-
Bạn sẽ thấy cái pipeline
orishop-build-projectđang ở trạng thái In Progress (Đang chạy). -
Bạn bấm vào nút Details (Chi tiết) ở giai đoạn Build để xem tận mắt anh thợ CodeBuild đang gõ lệnh Maven và build Docker Image trực tiếp trên Cloud như thế nào nhé. Phê hơn xem log Jenkins nhiều!

Nếu thành công sẽ có trạng thái như này :

Phần 4: Trải nghiệm GitOps hoàn chỉnh
Mọi thiết lập đã hoàn tất. Giờ đây, quy trình vận hành chỉ còn lại 1 thao tác duy nhất đối với Developer:
git add .
git commit -m "Feature: Awesome new updates"
git push origin main
Ngay lập tức, GitHub Webhook sẽ gọi sang AWS CodeBuild. Quá trình lấy code ➔ Maven Build ➔ Docker Build ➔ ECR Push ➔ EKS Deploy sẽ diễn ra hoàn toàn tự động và bảo mật thông qua IAM Roles.
Kết quả: Hệ thống CI/CD ổn định, không tốn phí duy trì Server (như Jenkins), dễ dàng tái tạo thông qua Terraform và loại bỏ hoàn toàn việc lưu trữ lộ liễu file kubeconfig.

Chạy lệnh kubectl get svc để lấy link truy cập kiểm tra thử nhé.

