Nếu bạn từng scale một node group GPU trên EKS và nhìn một pod nằm ở trạng thái ContainerCreating suốt ba bốn phút trong lúc image CUDA/PyTorch đang pull, chắc bạn từng nghĩ đó là vấn đề bandwidth và chuyển sang instance có tier network lớn hơn. Đội ngũ EKS của AWS công bố nguyên nhân gốc rễ thực sự vào ngày 10 tháng 8 năm 2026, và hóa ra không hề liên quan đến bandwidth — mà là serialization. Hai bản fix containerd, giờ đã mặc định trong EKS Auto Mode, rút thời gian chờ đó từ vài phút xuống vài giây mà không cần đụng vào cấu hình network.

Các instance GPU hiện đại (dòng p5, p5e, trn2) đi kèm network interface 100-400 Gbps. Về lý thuyết, một image CUDA/PyTorch/vLLM 20-30GB nên pull xong trong vài giây ở băng thông đó. Trên thực tế, các team thường xuyên thấy pull mất 2-5 phút trên chính những instance đó. Bản năng là đổ lỗi cho registry, VPC endpoint, hoặc NIC — nhưng nút thắt thực sự nằm ở cách containerd xử lý một lượt pull image, không phải tốc độ byte di chuyển.

Nguyên Nhân Gốc Rễ: Tuần Tự, Không Song Song

Đường pull image OCI mặc định trong containerd phiên bản cũ làm hai việc không scale theo kích thước image, bất kể tốc độ link:

  1. Layer được download bằng một HTTP GET tuần tự duy nhất cho mỗi layer, buffer toàn bộ layer trước khi sang bước tiếp theo — không có range request, không chia chunk. Một layer CUDA base 8GB là một kết nối HTTP sống lâu duy nhất, chịu ảnh hưởng của TCP slow-start và giới hạn throughput ở cấp kết nối mà một NIC 400Gbps chẳng bao giờ có cơ hội bão hòa.
  2. Việc unpack layer (giải nén tar + xác minh checksum) diễn ra từng layer một, tuần tự. Ngay cả khi một layer đã download xong, containerd không bắt đầu unpack layer N+1 cho đến khi layer N được extract hoàn toàn ra đĩa. Với một image nhiều gigabyte có hơn 10 layer, điều này biến một thao tác vốn dĩ song song hoàn hảo thành tuần tự.

Cả hai đều không phải vấn đề bandwidth. Bạn có thể có một link 400Gbps rảnh rỗi mà vẫn bị nghẽn bởi TCP slow-start trên một stream duy nhất và quá trình giải nén tar đơn luồng.

Bản Fix: Hai Thay Đổi Containerd Đã Được Upstream

containerd 2.1 giới thiệu download dựa trên range-request theo chunk, stream trực tiếp nội dung layer ra đĩa thay vì buffer toàn bộ layer trong bộ nhớ trước khi ghi:

# về mặt khái niệm, thay đổi là gì — một GET tuần tự lớn cho mỗi layer:
curl -o layer.tar.gz https://registry/v2/blobs/sha256:abc123...

# trở thành các request theo range chạy song song, stream ra đĩa:
curl -r 0-99999999    -o layer.tar.gz.part1 https://registry/... &
curl -r 100000000-199999999 -o layer.tar.gz.part2 https://registry/... &
curl -r 200000000-299999999 -o layer.tar.gz.part3 https://registry/... &
wait

Điều này cho phép một layer lớn duy nhất bão hòa nhiều TCP stream cùng lúc thay vì sống chết theo giới hạn throughput của một kết nối — đúng loại vấn đề quan trọng khi bạn dùng NIC 100+ Gbps, nơi một TCP stream đơn lẻ thực tế không bao giờ đạt được line rate.

containerd 2.2 giải quyết nút thắt thứ hai: unpack layer song song thay vì tuần tự nghiêm ngặt. Các layer không có xung đột thứ tự phụ thuộc (phần lớn layer trong một image CUDA base thông thường) giờ được unpack song song ngay khi byte của chúng có mặt trên đĩa, thay vì phải chờ mọi layer trước đó extract xong.

Cả hai giờ là hành vi mặc định trong EKS Auto Mode, và đội ngũ EKS đã công bố hướng dẫn cấu hình cụ thể cho cả AMI AL2023 và Bottlerocket nếu bạn tự quản lý node group ngoài Auto Mode:

# containerd config.toml — các knob liên quan (self-managed node AL2023)
[plugins."io.containerd.grpc.v1.cri".containerd]
  discard_unpacked_layers = false

[plugins."io.containerd.grpc.v1.cri".registry]
  config_path = "/etc/containerd/certs.d"

# bật pull/unpack song song (containerd >= 2.1/2.2)
[plugins."io.containerd.snapshotter.v1.overlayfs"]
  sync_remove = false

Ý Nghĩa Thực Sự Cho Độ Trễ Scale-Up Node

Nếu bạn chạy workload inference trên EKS — autoscale một node group GPU để phản ứng với traffic tăng đột biến, hoặc tạo node ephemeral cho batch training job — thời gian pull image nằm thẳng trên critical path của quá trình scale-up. Một lượt pull 3-4 phút cộng với thời gian bootstrap node và kubelet readiness dễ dàng đẩy cold-start latency của một node GPU mới vượt quá 5-6 phút. Rút ngắn chính lượt pull xuống còn vài chục giây là khác biệt giữa “autoscaling phản ứng với tải trong SLA hợp lý” và “on-call của bạn bị page vì scale-up không kịp trước khi traffic tăng đột biến trôi qua.”

Cụ thể, với một team chạy vLLM hoặc Triton inference server trên EKS: nếu bạn đang dùng self-managed node group (không phải Auto Mode), hãy kiểm tra phiên bản containerd và cấu hình trước khi mặc định cho rằng pull chậm là vấn đề network hay ECR. Đây là một lần kiểm tra cấu hình mất năm phút có thể giúp bạn tránh over-provision các node warm standby chỉ để né tránh độ trễ pull.

# kiểm tra nhanh trên một node EKS self-managed
containerd --version
# nếu < 2.1, chưa có tối ưu download range-request
# nếu < 2.2, việc unpack layer vẫn còn tuần tự

# xác minh hành vi pull hiện tại qua metrics của chính containerd
ctr --namespace k8s.io images pull --local  registry/image:tag

Hạng Mục Roadmap Đáng Theo Dõi

Bài viết của đội ngũ EKS cũng nêu hai thay đổi sắp tới đáng theo dõi: rapidgzip (giải nén gzip có thể song song hóa, vì giải nén gzip tiêu chuẩn vốn đơn luồng và có thể trở thành nút thắt ngay cả khi download và unpack đã được song song hóa) và BLAKE3 tree-hashing cho việc xác minh layer (thay thế chuỗi hash tuần tự của SHA-256 bằng cấu trúc cây có thể xác minh song song). Cả hai chưa được triển khai tại thời điểm bài viết này, nhưng đều nhắm vào cùng một lớp vấn đề — các bước trong pipeline pull được thiết kế cho image nhỏ và chưa từng được xem xét lại khi image GPU/ML tăng lên hàng chục gigabyte.

Bài Học Cho Platform Team

Đây là một case study tốt cho một mẫu hình tôi liên tục gặp phải: khi hạ tầng được thiết kế cho một quy mô khác bắt đầu hoạt động kém, bản năng gần như luôn là “ném thêm bandwidth/compute vào đó”, và bản năng đó thường sai. Việc pull image container được thiết kế trong thời đại image ứng dụng vài trăm MB; nó chưa bao giờ được thiết kế lại cho image ML 20-30GB, và không nâng cấp NIC nào sửa được một pipeline vốn dĩ tuần tự về bản chất. Bản fix thực sự đòi hỏi ai đó phải profile xem thời gian wall-clock thực sự đi đâu — TCP slow-start trên stream đơn, giải nén tar đơn luồng — thay vì giả định nút thắt nằm ở nơi nó “nên” nằm.

Nếu bạn đang chạy workload GPU trên Kubernetes ở bất cứ đâu (EKS hay nơi khác) và chưa kiểm tra phiên bản containerd theo hướng này, năm phút bỏ ra là xứng đáng. Đây là kiểu fix không tốn gì mà chỉ làm scale-up của bạn nhanh hơn.


Thuận Lương là Technical Lead với hơn 15 năm kinh nghiệm .NET, cloud architecture, và AI systems. Anh viết về những bài học thực tế từ việc xây dựng hệ thống production.

Xuất nội dung

Bình luận