Câu "pod là đơn vị nhỏ nhất, không phải container" nghe như một chi tiết thuật ngữ. Bài này đo xem nó thật sự có nghĩa gì, và tìm ra một cái bẫy về thời gian xoá.
Một pod, một IP
Dựng một pod có hai container: web chạy nginx, ban chạy busybox.
podIP: 10.244.0.13
container trong pod: ban web
Một IP cho cả pod, không phải một IP mỗi container.
Cho ban gọi http://localhost/:
<!DOCTYPE html>
Hai container khác nhau nói chuyện với nhau qua localhost. Chúng chung một không gian mạng, một địa chỉ IP, một dải cổng.
Đây là toàn bộ lý do pod tồn tại. Nếu container đã đủ, Kubernetes đã dùng container làm đơn vị. Pod là cách gói vài container phải chạy cùng máy và chia sẻ tài nguyên với nhau.
Hệ quả trực tiếp: hai container trong cùng pod không được nghe cùng một cổng. Chúng chung dải cổng, nên hai nginx trong một pod là xung đột.
Khi nào cần nhiều container trong một pod
Ba mẫu, và cả ba đều dựa vào việc chung không gian mạng hoặc chung thư mục:
Sidecar — container phụ chạy cùng suốt vòng đời: thu log, proxy mạng (Envoy trong lưới dịch vụ), làm mới chứng chỉ.
Adapter — đổi định dạng đầu ra của ứng dụng chính thành thứ hệ thống theo dõi hiểu được.
Ambassador — đứng giữa ứng dụng và thế giới bên ngoài, ví dụ một proxy tới cơ sở dữ liệu để ứng dụng chỉ cần nói localhost:5432.
Ngoài ba mẫu này, gần như luôn nên tách thành hai pod. Nhiều container trong một pod nghĩa là chúng cùng sống cùng chết, cùng được xếp lên một node, và không co giãn độc lập được.
Bốn giai đoạn khởi động
Scheduled Successfully assigned default/t2 to kt-control-plane
Pulled Container image "nginx:alpine" already present on machine
Created Created container: t2
Started Started container t2
Bốn dòng, bốn thành phần khác nhau ghi ra — đúng chuỗi ở phần trước.
Thời gian tới Ready:
| ảnh đã có sẵn trên node | 2,4 giây |
| ảnh phải kéo về | 6,7 giây |
| 2 initContainer × 3 giây + 2 container | 9,0 giây |
Chênh lệch 2,4 so với 6,7 giây là thời gian kéo ảnh, và đó thường là phần lớn nhất trong thời gian khởi động pod. Ảnh nhỏ khởi động nhanh hơn, và đó là lý do alpine phổ biến đến vậy.
initContainer chạy tuần tự
Hai initContainer, mỗi cái sleep 3, cộng hai container chính: 9,0 giây.
Sáu giây trong đó là sàn bắt buộc — initContainer chạy lần lượt, cái sau đợi cái trước kết thúc thành công. Không có cách nào chạy song song.
Chúng dùng để làm việc phải xong trước khi ứng dụng khởi động: chạy migration cơ sở dữ liệu, đợi một dịch vụ khác sẵn sàng, tải cấu hình về thư mục chung.
Ba initContainer mỗi cái mất 10 giây là 30 giây thêm vào mọi lần khởi động pod — nhân với số bản, nhân với số lần triển khai. Đây là chỗ đáng đo trước khi thêm cái thứ tư.
Xoá pod: thời gian không phụ thuộc ứng dụng
Đây là phát hiện phản trực giác nhất của bài.
Bốn phép đo, hai giá trị terminationGracePeriodSeconds, hai hành vi container trái ngược:
| grace | Container | kubectl delete mất |
|---|---|---|
| 5 s | sleep — chết ngay khi nhận SIGTERM |
7,0 giây |
| 30 s | sleep — chết ngay khi nhận SIGTERM |
31,1 giây |
| 5 s | trap '' TERM — bỏ qua SIGTERM hoàn toàn |
7,0 giây |
| 30 s | trap '' TERM — bỏ qua SIGTERM hoàn toàn |
31,5 giây |
Hai hàng đầu và hai hàng cuối giống hệt nhau. Container chết ngay hay bướng bỉnh không chịu chết — kết quả như nhau.
Kết quả chỉ phụ thuộc terminationGracePeriodSeconds:
thời gian xoá = grace period + khoảng 1-2 giây
Nghĩa là:
Thời gian
kubectl delete podkhông đo được tốc độ tắt của ứng dụng.
Rất nhiều người dùng con số này để đánh giá "ứng dụng của tôi tắt có sạch không". Nó không nói gì về điều đó.
Tôi không tách được từ bên ngoài là kubelet giữ pod đủ grace period, hay chính kubectl chờ. Cái đo được chắc chắn là: hành vi container không thay đổi kết quả.
Muốn biết ứng dụng tắt nhanh hay chậm thì phải nhìn log của chính nó, hoặc đo thời gian giữa lúc nhận SIGTERM và lúc tiến trình thoát.
Vì sao vẫn nên đặt grace period đúng
Dù thời gian delete không đổi, grace period vẫn quan trọng — nó là trần thời gian ứng dụng được phép dọn dẹp:
terminationGracePeriodSeconds: 30 # mặc định
Hết thời gian đó là SIGKILL, không thương lượng. Ứng dụng đang xử lý một yêu cầu HTTP dài, hoặc đang ghi nốt một lô vào cơ sở dữ liệu, sẽ bị cắt ngang.
Đặt quá ngắn: mất công việc đang dở. Đặt quá dài: mọi lần triển khai cuốn chiếu chậm đi đúng chừng đó, nhân với số pod.
Con số đúng là thời gian tắt sạch tệ nhất của ứng dụng, cộng biên. Đo nó, đừng đoán.
Bốn pha của pod
Pending đã tạo trong etcd, chưa chạy — đang đợi xếp chỗ hoặc đang kéo ảnh
Running ít nhất một container đang chạy
Succeeded mọi container đã kết thúc với mã 0
Failed ít nhất một container kết thúc với mã khác 0
Pending là pha đáng chú ý nhất khi gỡ lỗi, vì nó gộp hai nguyên nhân rất khác nhau: chưa được xếp chỗ (thiếu tài nguyên, không node nào hợp) và đang kéo ảnh. Phân biệt bằng:
kubectl describe pod <ten> | tail -20
Có dòng Scheduled nghĩa là đã xếp chỗ xong, vấn đề nằm ở ảnh. Không có nghĩa là bộ lập lịch chưa tìm được node — và Events sẽ nói vì sao.
Thử ba mươi giây
Tự kiểm cái bẫy grace period:
kubectl run nhanh --image=busybox:1.36 --restart=Never \
--overrides='{"spec":{"terminationGracePeriodSeconds":30}}' -- sleep 3000
kubectl wait --for=condition=ready pod/nhanh
time kubectl delete pod nhanh
sleep chết ngay khi nhận SIGTERM, nhưng lệnh trên vẫn mất hơn ba mươi giây. Nếu bạn từng nghĩ triển khai chậm là do ứng dụng tắt chậm, đây là chỗ nên kiểm lại trước.
Phần sau đo Deployment và ReplicaSet: cơ chế đứng sau việc cập nhật không gián đoạn.