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
- 2. Elastic Container Service (ECS)
- 3. Elastic Container Registry (ECR)
- Tài liệu tham khảo
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.
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:
CpuvàMemory: 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.
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.
Đ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.