Sử dụng một bộ cân bằng tải ALB duy nhất cho nhiều ứng dụng trên Amazon EKS

Giới thiệu

Kiến trúc microservices là lựa chọn ưu tiên khi xây dựng các ứng dụng tối ưu cho đám mây (cloud-native applications). Nó giúp cải thiện khả năng mở rộng, sự độc lập giữa các tính năng, và cho phép chúng ta thiết lập nhiều nhóm khác nhau làm việc trên những tính năng khác nhau của ứng dụng. Vì thế, kiến trúc này ngày càng được sử dụng rộng rãi hơn.

Cùng với đó là việc ngày càng nhiều ứng dụng được sinh ra trên đám mây, bằng việc tận dụng các công nghệ cung cấp bởi đám mây, việc triển khai, vận hành và quản lý các ứng dụng microservices với tính sẵn sàng cao trở nên dễ dàng hơn.

Một kiến trúc, nhiều cách xây dựng

Với AWS, có tối thiểu hai cách phổ biến để xây dựng kiến trúc microservices:

  1. Đóng gói các cấu phần của ứng dụng vào trong một container hoặc một tập các container (việc này được gọi là containerisation) và uỷ nhiệm việc quản lý các container này cho một công cụ quản trị container (container orchestrator), như là Amazon Elastic Kubernetes Service (Amazon EKS) hay Amazon Elastic Container Service (Amazon ECS).
  2. Dịch chuyển các cấu phần của hứng dụng vào các serverless function (hàm phi máy chủ), và uỷ nhiệm việc quản trị cho AWS Lambda.

Bước tiếp theo là mở các microservice thông qua một điểm kết nối để các bên sử dụng có thể gửi các yêu cầu đến và nhận phản hồi. Thông thường, ta sẽ muốn mỗi microservice có một điểm kết nối riêng. Ví dụ, mỗi hậu tố trên địa chỉ web (phần phía sau dấu “/”) sẽ trỏ tới một microservice khác nhau: 

www.example.com/service1 > microservice1

www.example.com/service2 > microservice2

www.example.com/service3 > microservice3

Cách cân bằng tải hay định tuyến này được gọi là path-based routing (định tuyến dựa trên URL path). Lợi thế của cách làm này là chi phí thấp và dễ dàng thực hiện trên nhiều ứng dụng khác nhau. Application Load Balancer (ALB)Amazon API Gateway đều hỗ trợ tính năng này. Bởi vậy, chỉ cần một ALB hoặc API Gateway duy nhất, ta có thể mở cùng lúc nhiều microservice khác nhau chạy trên các container quản lý bởi Amazon EKS, Amazon ECS, hoặc các serverless function chạy trên AWS Lambda.

Trong blog này, chúng ta sẽ tìm hiểu cách mở cùng lúc các ứng dụng Container được quản trị bởi Amazon EKS thông qua Application Load Balancer. 

Kiến trúc và các cấu phần

Amazon EKS Cluster là cụm Kubernetes mà ứng dụng được triển khai trên đó. Amazon EKS là một dịch vụ của AWS giúp đơn giản hoá việc quản trị các cấu phần bên trong control plane của Kubernetes, bao gồm các máy chủ API và etcd, cho phép nhà phát triển tập trung vào data plane, là các máy chủ dùng để triển khai ứng dụng.

Auto Scaling Group là một tính năng cho phép tự động thay đổi số lượng máy ảo Amazon EC2 theo dựa trên các thông số giám sát. Để sử dụng tính năng này, ta cần tạo ra Cluster Autoscaler bên trong Kubernetes cluster, nó sẽ tự động tương tác với Auto Scaling group thay cho chúng ta. Trong blog này, ta chỉ dùng tính năng này để giữ một số lượng máy ảo EC2 tối thiểu để đảm bảo tính sẵn sàng cao. AWS Fargate là một lựa chọn khác để chạy ứng dụng trên EKS bên cạnh EC2. Với Fargate, data plane sẽ được quản trị hoàn toàn bởi AWS, vì thế ta không cần cấu hình Cluster Autoscaler.

Application Load Balancer (ALB) tự động chia tải cho các máy chủ EC2, container, hay các địa chỉ IP ở trên một hoặc nhiều vùng sẵn sàng (Availability Zone) khác nhau. Các ALB hoạt động ở lớp 7 (lớp ứng dụng) của mô hình OSI (HTTP/HTTPS) và hỗ trợ nhiều tính năng khác nhau, trong đó có path conditions, cho phép ta định nghĩa các quy tắc để định tuyến các yêu cầu dựa trên URL path (hay còn gọi là path-based routing). ALB là một trong các tuỳ chọn cân bằng tải cung cấp bởi Elastic Load Balancing.

Ingress là một loại tài nguyên của Kubernetes cho phép quản lý các truy cập từ bên ngoài vào các ứng dụng trong Kubernetes cluster, ví dụ như các giao tiếp HTTP. Một Ingress thường đính kèm với bộ cân bằng tải, chứng chỉ SSL và cấu hình virtual hosting. Để sử dụng tính năng Ingress, ta cần cài đặt Ingress Controller.

AWS Load Balancer Controller là một bộ điều khiển dùng để quản lý các Elastic Load Balancers bên trong Kubernetes clusters. Trong blog này, chúng ta sử dụng Ingress để tự động tạo ra một ALB và cấu hình các routing rules cần thiết cho ALB thông qua Kubernetes manifests (là một file YAML hoặc JSON dùng để mô tả các đội tượng bên trong Kubernetes).

Mục tiêu cuối cùng là mở nhiều ứng dụng khác nhau trên những đường dẫn khác nhau, thông qua cùng một ALB.

 Yêu cầu

Để hiện thực giải pháp này, ta phải chuẩn bị một số tiền điều kiện sau:

  • Một Amazon EKS cluster được tạo với node groups nếu bạn chọn chạy ứng dụng trên các máy ảo EC2 instances, hoặc một Fargate profile nếu bạn chọn dùng AWS Fargate.
  • Cài đặt AWS Load Balancer Controller cho cluster, để Application Load Balancer có thể được tạo tự động khi cấu hình Ingress.
  • Ảnh container (container image) cho ứng dụng được đẩy lên kho lưu trữ Amazon ECR repository hoặc một kho lưu trữ khác mà bạn chọn. Trong blog này, chúng tôi sử dụng container image lưu trữ trong Amazon ECR, chúng tôi cũng sẽ hướng dẫn các bước để tạo và đẩy container image lên kho lưu trữ này. 

 Khởi tạo môi trường

1. Application and Docker image creation process

Đầu tiên, hãy xây dựng các ứng dụng cùng với các Dockerfile tương ứng. Ở đây, chúng tôi dùnghai ứng dụng HTML đơn giản để thử nghiệm, một ứng dụng sẽ hiển thị màn hình vàng và ứng dụng còn lại hiển thị màn hình xanh lá, chúng đóng vai trò như hai microservice. 

A) Tạo ra hai thư mục có tên là green và yellow.

mkdir green/ yellow/

B) Sao chép mã nguồn của mỗi ứng dụng và lưu lại với tên index.html ở trong thư mục tương ứng.

Ứng dụng xanh lá: 

echo '<html style="background-color: green;"></html>' > green/index.html

Ứng dụng vàng: 

echo '<html style="background-color: yellow;"></html>' > yellow/index.html

C) Tạo ra các dockerfile tương ứng trong mỗi thư mục chứa file index.html.

Nội dung Dockerfile: Chúng ta sử dụng docker image có chứa Nginx trên hệ điều hành Alpine, sau đó tạo ra thư mục để chứa ứng dụng và sao chép file index.html vào thư mục đó. Cuối cùng chúng ta mở ứng dụng ở port 80 (HTTP). 

Ứng dụng xanh lá: 

cat <<EOF > green/Dockerfile
FROM public.ecr.aws/nginx/nginx:1.20-alpine
RUN mkdir -p /usr/share/nginx/html/green
COPY ./index.html /usr/share/nginx/html/green/index.html 
EXPOSE 80
EOF

Ứng dụng vàng: 

cat <<EOF > yellow/Dockerfile
FROM nginx:alpine
RUN mkdir -p /usr/share/nginx/html/yellow
COPY ./index.html /usr/share/nginx/html/yellow/index.html 
EXPOSE 80
EOF

D) Tạo ra các kho lưu trữ ECR repository và tải lên docker image tương ứng. 

Làm theo tài liệu này để tạo ra các kho lưu trữ green và yellow cho các ứng dụng.

aws ecr create-repository --repository-name green

aws ecr create-repository --repository-name yellow

E) Sau khi tạo ra các kho lưu trữ, truy cập vào chúng và chọn View push commands.

Làm theo hướng dẫn tương ứng với hệ điều hành bạn sử dụng để tạo ra Docker image và tải lên kho lưu trữ. Trong ví dụ này, chúng tôi sử dụng khu vực us-east-1. Thay đổi mã khu vực nếu bạn không sử dụng N. Virginia. 

export AWS_REGION=$(aws ec2 describe-availability-zones --output text --query 'AvailabilityZones[0].[RegionName]')
export AWS_REGISTRY_ID=$(aws ecr describe-registry --query registryId --output text)
export AWS_ECR_REPO=${AWS_REGISTRY_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com

aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ECR_REPO

cd yellow/
docker build . -t yellow
docker tag yellow:latest $AWS_ECR_REPO/yellow:latest
docker push $AWS_ECR_REPO/yellow:latest
cd ..

cd green/
docker build . -t green
docker tag green:latest $AWS_ECR_REPO/green:latest
docker push $AWS_ECR_REPO/green:latest
cd ..

Lưu ý rằng bạn cần thực hiện tác vụ này cho cả hai ứng dụng. 

2. Xây dựng các file cấu hình cho kubernetes

Tiếp theo, ta sẽ tạo ra các file cấu hình để hướng dẫn kubernetes thiết lập các cấu phần cần thiết.

A) Sử dụng trình soạn thảo văn bản bất kỳ để tạo file tên là color-app.yaml. Sau đó, ta thêm phần nội dung sau vào file (Lưu ý bạn có thể chỉnh sửa phần nội dung này nếu cần thiết, ví dụ nếu bạn không sử dụng ECR, bạn có thể thay thế trường image thành địa chỉ kho lưu trữ image của bạn):

cat <<EOF > color-app.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: color-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: green-app
  namespace: color-app
  labels:
    app: green-app
spec:
  selector:
    matchLabels:
      app: green-app
  replicas: 2
  template:
    metadata:
      labels:
        app: green-app
    spec:
      containers:
      - name: green-container
        image: $AWS_ECR_REPO/green:latest
        ports:
            - containerPort: 80
        resources:
          limits:
            memory: "100Mi"
            cpu: "200m"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: yellow-app
  namespace: color-app
  labels:
    app: yellow-app
spec:
  selector:
    matchLabels:
      app: yellow-app
  replicas: 2
  template:
    metadata:
      labels:
        app: yellow-app
    spec:
      containers:
      - name: yellow-container
        image: $AWS_ECR_REPO/yellow:latest
        ports:
            - containerPort: 80
        resources:
          limits:
            memory: "100Mi"
            cpu: "200m"
EOF

File cấu hình này sẽ hướng dẫn kubenetes tạo một số tài nguyên, bao gồm: một Namespace tên là color-app và hai Deployment cho hai ứng dụng. Mỗi Deployment bao gồm hai Replica, chúng ta sẽ gắn một số nhãn (label) cho mỗi ứng dụng, chúng ta cũng chỉ định image và giới hạn tài nguyên memory và CPU mà mỗi ứng dụng có thể sử dụng. 

B) Bây giờ ta sẽ thêm định nghĩa để tạo ra hai Service với loại là NodePort bằng cách thêm đoạn nội dung sau vào file trên, mỗi Service sẽ tương ứng với một Deployment. NodePort là loại Service cho phép mở ứng dụng bên trong cluster để có thể truy cập thông qua Ingress, mà cụ thể trong bài này là Application Load Balancer được tạo ra tự động bởi AWS Load Balancer Controller mà chúng tôi đã nhắc đến trong phần tiền điều kiện. Lưu ý rằng đối với Service, ta sẽ trỏ nó tới ứng dụng thông qua các label và selector:

cat <<EOF >> color-app.yaml 
---
apiVersion: v1
kind: Service
metadata:
  namespace: color-app
  name: green-service
  labels:
    app: green-app
  annotations:
    alb.ingress.kubernetes.io/healthcheck-path: /green/index.html
spec:
  type: NodePort
  selector:
    app: green-app
  ports:
    - port: 80
      targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
  namespace: color-app
  name: yellow-service
  labels:
    app: yellow-app
  annotations:
    alb.ingress.kubernetes.io/healthcheck-path: /yellow/index.html
spec:
  type: NodePort
  selector:
    app: yellow-app
  ports:
    - port: 80
      targetPort: 80
EOF

C) Cuối cùng, chúng ta thêm vào file định nghĩa Ingress. Như đã mô tả ở trên, về bản chất Ingress hoạt động dựa trên ALB. Vì thế, chúng ta không cần quản lý Ingress thông qua các Pod bên trong cluster, cũng như không tiêu tốn tài nguyên của cluster. Ngoài ra, chúng ta cũng tận dụng được những tính năng mạnh mẽ của ALB, như khả năng mở rộng tự động, tính bảo mật năng cao, hay khả năng điều hướng dựa trên path. 

cat <<EOF >> color-app.yaml 
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: color-app-ingress
  namespace: color-app
  labels:
    app: color-app
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
    alb.ingress.kubernetes.io/healthcheck-port: traffic-port
    alb.ingress.kubernetes.io/healthcheck-interval-seconds: '15'
    alb.ingress.kubernetes.io/healthcheck-timeout-seconds: '5'
    alb.ingress.kubernetes.io/success-codes: '200'
    alb.ingress.kubernetes.io/healthy-threshold-count: '2'
    alb.ingress.kubernetes.io/unhealthy-threshold-count: '2'
spec:
  rules:
    - http:
        paths:
          - path: /yellow
            pathType: Prefix
            backend:
              service:
                name: yellow-service
                port:
                  number: 80                        
          - path: /green
            pathType: Prefix
            backend:
              service:
                name: green-service
                port:
                  number: 80
EOF

Trong file này, chúng ta định nghĩa các Ingress annotation để chỉ định Ingress sử dụng ALB, chỉ định các gói tin sẽ được điều hướng trực tiếp đến các Pod, và cấu hình cách ALB thực hiện health check. Trong phần “spec”, ta chỉ định các URL path cho mỗi ứng dụng và Service tương ứng của ứng dụng đó để điều hướng các gói tin đến.

3. Chạy file cấu hình để tạo các tài nguyên bên trong Kubernetes cluster

Sau khi tạo xong file yaml, ta sử dụng file này để triển khai ứng dụng lên EKS cluster, thông qua kubectl. 

A) Từ máy tỉnh có kết nối tới EKS cluster, mở cửa sổ lệnh ở thư mục chưa file color-app.yaml và chạy lệnh sau:

kubectl apply -f color-app.yaml

Nếu bạn chưa biết cách kết nối kubectl đến eks cluster, tham khảo hướng dẫn cách cấu hình RBAC ở tài liệu này và các cấu hình môi trường máy trạm của bạn ở tài liệu này.

Đợi vài phút để việc cài đặt hoàn tất.

B) Sau khi ALB khởi tạo xong, ta cần kiểm tra các cầu hình được cài đặt tự động cho ALB bằng cách truy cập vào AWS Management Console và chọn dịch vụ Amazon EC2 > Load Balancers > chọn the ALB > chọn tab Listeners > sau đó chọn View/edit rules.

C) Chạy câu lệnh sau để lấy địa chỉ URL của ALB: 

kubectl get ingress color-app-ingress -n color-app -o=jsonpath="{'http://'}{.status.loadBalancer.ingress[].hostname}{'\n'}"

Copy đường dẫn trả về, dán lên trình duyệt và lần lượt thêm /green và /yellow vào cuối như trong hình sau:

/green 

/yellow 

Bước tiếp theo 

Dựa trên bài viết này, bạn có thể tuỳ chỉnh lại file YAML để sử dụng cho các ứng dụng của mình. Bạn có thể đổi tên các label và các cấu phần như namespace, Deployment, Service, Ingress tương ứng với môi trường của bạn và sử dụng các Docker image của chính bạn cho các Deployment. Ngoài ra, bạn cũng có thể sử dụng cách thức trong bài viết này cho cụm Kubernetes chạy hoàn toàn trên Amazon EC2 thay vì Amazon EKS, nếu cần. 

Tổng kết 

Trong bài viết này, ta biết rằng có nhiều cách đây xây dựng kiến trúc microservice trên AWS. Chúng ta cũng tìm hiểu một vài khái niệm quan trọng cần biết khi sử dụng container cho microservice. Cuối cùng, chúng ta đã thực hành các hiện thực hoá kiến trúc này một cách đơn giản và tiết kiệm chi phí với Amazon EKS và ALB. 

Nếu muốn, bạn cũng có thể đạt được kết quả tương tự bằng cách trỏ nhiều đối tượng Ingress khác nhau vào cùng một ALB thay vì một Ingress bằng cách sử dụng annotation “alb.ingress.kubernetes.io/group.name”. Trong trường hợp này, bạn sẽ cần tạo ra từng Ingress đơn lẻ và đặt cho cho chúng một tên chung thông qua annotation này. 

Bằng cách này, các đội khác nhau có thể triển khai và quản trị các Service và Ingress của riêng mình một cách hoàn toàn độc lập mà vẫn chia sẻ cùng một ALB.


Bài được dịch từ bài viết trên AWS Blogs, bạn có thể xem bài viết gốc tại đây.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: