48. Elastic Container Service (ECS)



Ngày nay, việc triển khai ứng dụng bằng container đã trở thành một phần không thể thiếu. Dịch vụ hỗ trợ triển khai container trên AWS là Elastic Container Service (ECS), sẽ được trình bày trong bài này. Nhưng trước hết, hãy quan sát bức tranh toàn cảnh để hiểu tại sao container lại phổ biến như vậy.

Trong bài này:

1. Container

1.1. Tổng quan

Trước khi có container, Máy Ảo (Virtual Machine) là cách phổ biến để tận dụng tối đa tài nguyên phần cứng. Cơ chế của VM là ảo hóa phần cứng, cho phép chạy nhiều hệ điều hành trên cùng một máy chủ vật lý, thông qua một phần mềm đặc biệt gọi là Hypervisor, như đã đề cập trong bài EC2.

Tuy nhiên, VM có 2 hạn chế:

  • Tốn tài nguyên: mỗi VM cần một hệ điều hành đầy đủ, tiêu tốn nhiều CPU, RAM và dung lượng lưu trữ, ngay cả khi ứng dụng bên trong không chạy hết công suất.
  • Khởi động chậm: do cần khởi động hệ điều hành riêng.

containerization

Container là một giải pháp thay thế nhẹ hơn. Thay vì ảo hóa phần cứng, container ảo hóa hệ điều hành. Tất cả container chia sẻ chung nhân hệ điều hành (OS Kernel) của máy chủ, nhưng vẫn đảm bảo việc cách ly tuyệt đối giữa các ứng dụng.

Mỗi container lúc này sẽ chỉ chứa ứng dụng và các thư viện cần thiết để chạy ứng dụng đó, không cần hệ điều hành riêng. Điều này giúp container nhẹ hơn nhiều so với VM, sử dụng tài nguyên hiệu quả hơn và khởi động nhanh hơn.

1.2. Các Khái niệm

  • Container: là một bản sao của ứng dụng và tất cả thư viện cần thiết để chạy ứng dụng đó. Chạy như một phần mềm độc lập trên máy chủ, cách ly với các ứng dụng khác ở cấp độ tiến trình (process-level).
  • Image: là dạng tĩnh (không hoạt động) của Container. Chạy Image sẽ tạo ra một Container. Có thể lưu trữ Image trong registry (như Docker Hub hoặc ECR), và đương nhiên có thể tái sử dụng để tạo Container chạy trên các máy chủ khác.
  • Dockerfile: định nghĩa cách thức tạo ra Image, gồm việc cài đặt phần mềm, sao chép mã nguồn, và thiết lập cấu hình cho ứng dụng. Bản chất là một tập hợp các lệnh CLI.

2. Elastic Container Service (ECS)

ECS giúp triển khai, quản lý và mở rộng các tác vụ chạy trên container một cách dễ dàng trên AWS qua hai chế độ triển khai: EC2-Based (sử dụng EC2 Instance để host container) và Fargate-Based (serverless, tự động mở rộng theo tải). Ngoài ra, cũng có thể dùng ECS để chạy container trên hạ tầng nội bộ (on-premise) thông qua ECS Anywhere.

Cùng xét một ví dụ cấu hình ECS qua CloudFormation để hiểu các thuật ngữ cơ bản. Ở đây mình chỉ trình bày các phần liên quan đến ECS, bỏ qua các phần khác như VPC, Subnet, Security Group. Bạn đọc có thể tham khảo toàn bộ Template tại đây.

Docker Image được sử dụng ở đây được lấy từ ECR Public, các container sẽ được triển khai dựa trên Image này:

Parameters:
  ContainerImage:
    Type: String
    Default: 'public.ecr.aws/ecs-sample-image/amazon-ecs-sample:latest'
    Description: Container Image to use in task definition
  ContainerPort:
    Type: Number
    Default: 80
    Description: Port on which the container listens
  DesiredCount:
    Type: Number
    Default: 2
    Description: Desired number of tasks

2.1. Cluster

Là đơn vị quản lý và phân phối tài nguyên cho các tác vụ container trên ECS. Gọi là Cluster vì có thể chứa nhiều máy chủ chạy container (EC2 Instance).

  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Sub '${AWS::StackName}-cluster'
      CapacityProviders:
        - FARGATE
        - FARGATE_SPOT
      DefaultCapacityProviderStrategy:
        - CapacityProvider: FARGATE
          Weight: 1
        - CapacityProvider: FARGATE_SPOT
          Weight: 4
      ClusterSettings:
        - Name: containerInsights
          Value: enabled

Trong cluster, ta cấu hình CapacityProviders để chỉ định loại tài nguyên sẽ sử dụng cho container. ở đây là Fargate và Fargate Spot. Trọng số phân phối tài nguyên là 1 cho Fargate và 4 cho Fargate Spot, tức chạy 80% trên Fargate Spot (giá rẻ hơn nhưng không đảm bảo tài nguyên luôn có sẵn, tương tự như EC2 Spot). Ngoài ra, ContainerInsights được bật để thu thập dữ liệu hiệu suất từng container (CPU, RAM, v.v.) và gửi về CloudWatch.

2.2. Task Definition

Định nghĩa cách thức chạy container, bao gồm:

  • CpuMemory: tài nguyên cần thiết cho container. Ở đây là 256 CPU unit (tương đương 0.25 vCPU) và 512 MiB RAM.
  • Execution Role: tương tự như Lambda Execution Role, ECS cần Role này để thay người dùng thực thi các tác vụ liên quan đến container, như pull Docker Image từ ECR, ghi log vào CloudWatch Logs, v.v. Chi tiết JSON của Role này tại đây.
  • Task Role: chỉ định quyền hạn dành cho ứng dụng bên trong container, được phép truy cập vào đâu (ví dụ, S3, DynamoDB, v.v.).
  • ContainerDefinitions: cấu hình cho container sẽ chạy trong tác vụ, gồm tên, Image, cổng, nơi ghi log và health check.
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub '${AWS::StackName}-task'
      Cpu: '256'
      Memory: '512'
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
      TaskRoleArn: !GetAtt ECSTaskRole.Arn
      ContainerDefinitions:
        - Name: !Ref ServiceName
          Image: !Ref ContainerImage
          PortMappings:
            - ContainerPort: !Ref ContainerPort
              Protocol: tcp
          Essential: true
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref LogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: ecs
          HealthCheck:
            Command:
              - CMD-SHELL
              - curl -f http://localhost/ || exit 1
            Interval: 30
            Timeout: 5
            Retries: 3
            StartPeriod: 60

Task Definition được dùng để tạo ra Task, chạy container và ứng dụng bên trong. Hiểu đơn giản, Task Definition giống như định nghĩa hàm, còn Task là một lời gọi hàm đó.

2.3. Service

Người dùng có thể tự chạy Task đơn lẻ, ví dụ như dùng lệnh CLI aws ecs run-task. Tuy nhiên, để đảm bảo tính sẵn sàng, nên tạo Service. Service sẽ đảm bảo số lượng Task đang chạy luôn đạt mức mong muốn (DesiredCount), tự động khởi động lại Task nếu có lỗi, và tích hợp với Load Balancer để phân phối lưu lượng.

  ECSService:
    Type: AWS::ECS::Service
    DependsOn: ALBListener
    Properties:
      ServiceName: !Sub '${AWS::StackName}-service'
      Cluster: !Ref ECSCluster
      TaskDefinition: !Ref TaskDefinition
      DesiredCount: !Ref DesiredCount
      LaunchType: FARGATE
      PlatformVersion: LATEST
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED 
          SecurityGroups:
            - !Ref ECSSecurityGroup
          Subnets:
            - !Ref PrivateSubnet1
            - !Ref PrivateSubnet2
      LoadBalancers:
        - ContainerName: !Ref ServiceName
          ContainerPort: !Ref ContainerPort
          TargetGroupArn: !Ref ALBTargetGroup
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 50
        DeploymentCircuitBreaker:
          Enable: true
          Rollback: true

2.4. EC2-Based

Với chế độ này, container được chạy trên EC2 Instance. Người dùng tự quản lý, lựa chọn cấu hình, cài đặt ECS Agent.

Khi tạo ECS Cluster ở chế độ này, CapacityProvider thường sẽ là một Auto Scaling Group. Thông số của ASG này được cấu hình theo Service, DesiredCapacity của ASG sẽ bằng DesiredCount của Service. ECS Task sẽ được triển khai trên các EC2 Instance trong ASG này.

ECS EC2-Based

Cách triển khai này phù hợp khi cần kiểm soát môi trường chạy container, hoặc khi có sẵn hạ tầng EC2 và muốn tận dụng để tối ưu chi phí, như khi muốn tận dụng Spot Instance hoặc Reserved Instance.

2.5. Fargate-Based

Triển khai trên EC2 đòi hỏi người dùng phải quản lý hạ tầng, như cập nhật hệ điều hành, phần mềm, vá lỗi bảo mật, và đảm bảo tính sẵn sàng. Nếu chỉ muốn tập trung vào phát triển ứng dụng, có thể dùng Fargate.

Đây là một dịch vụ serverless cho container, tự động mở rộng theo tải, tính phí dựa trên tài nguyên sử dụng, cơ chế tương tự như Lambda. Người dùng chỉ cần cung cấp Docker Image và các cấu hình Task, Service.

ECS Fargate-Based

Điểm khác biệt mấu chốt là hạ tầng Fargate được sử dụng chung, những Container nào của người dùng sẽ được “gắn” vào VPC của người dùng thông qua Elastic Network Interface (ENI). Trong khi với EC2-Based, container chạy trực tiếp trên VPC của người dùng.

Nên dùng Fargate khi muốn đơn giản hóa việc quản lý hạ tầng, hoặc khi ứng dụng thường có tải đột ngột (burst workload), cần tự động mở rộng nhanh chóng. Tất nhiên, chi phí thường cao hơn so với EC2-Based.

3. Elastic Container Registry (ECR)

ECR là một dịch vụ lưu trữ Image (một dạng Docker Registry) được quản lý bởi AWS. Nó tương tự như Docker Hub, nhưng được thiết kế cho hệ sinh thái AWS.

Mỗi tài khoản AWS có 1 public và 1 private registry (kho chứa Image). Public registry cho phép đọc không cần xác thực, còn private registry yêu cầu xác thực cho cả đọc và ghi. ECR tích hợp với IAM để kiểm soát quyền truy cập.

Mỗi registry có thể chứa nhiều repository (tương tự như thư mục), và mỗi repository có thể chứa nhiều image (bản sao của ứng dụng). Mỗi image có thể được gắn nhiều tag để phân biệt các phiên bản khác nhau.

Image trong ECR được tự động quét để phát hiện lỗ hổng bảo mật (qua Inspector), cũng như hỗ trợ mã hoá bằng KMS.

Tài liệu tham khảo

  1. Tạo Docker Image và Lưu lên ECR
  2. Giới thiệu về ECS
  3. Cấu hình ECS bằng CloudFormation
  4. Khái niệm và Thành phần của ECR
Nếu có câu hỏi, bạn có thể nhắn mình trên fanpage hoặc group. Cảm ơn bạn.