Skip to content

Image Pulling — Windows Image Registry Optimization

Vì Sao Quan Trọng

Nếu Linux engineer chuyển sang vận hành Windows container lần đầu, điều gây shock nhất thường là kích thước image. Một base image Linux tối giản (Alpine) chỉ vài MB. Base image Windows tối giản nhất (Nano Server) đã hàng trăm MB compressed; Server Core-based image thường vài GB. Ở scale — node autoscaling liên tục pull image mới, CI/CD build image mới mỗi lần deploy — sự khác biệt này ảnh hưởng trực tiếp tới pod startup latency, autoscaling responsiveness, và network egress cost.

Ngoài vấn đề kích thước, có một constraint quan trọng hơn về correctness: base image OS version phải tương thích với host node OS version — mismatch không phải "chậm hơn", mà là container không start được.

Vì Sao Windows Image Lớn Hơn Linux Rất Nhiều

Linux container image tối giản có thể chỉ chứa vài binary + shared library cần thiết, vì Linux kernel + userspace tách biệt rõ ràng, và container chỉ cần userspace layer tương thích ABI với host kernel qua syscall interface (ổn định, tương thích ngược tốt).

Windows container không có sự tách biệt sạch như vậy — nhiều Windows API (Win32 API surface) được implement ở user-mode DLL nằm ngay trong base image, không phải "syscall trực tiếp tới kernel" như phần lớn Linux operation. Vì vậy base image Windows phải đóng gói một phần lớn OS userspace để app chạy được:

Base ImageKích thước (compressed)API Surface
mcr.microsoft.com/windows/nanoserver~250–350 MBTối giản, .NET Core/5+ compatible, không hỗ trợ .NET Framework
mcr.microsoft.com/windows/servercore~2–3 GBĐầy đủ hơn, hỗ trợ .NET Framework, IIS, MSMQ, COM
mcr.microsoft.com/windows (full)~4+ GBWindows API surface đầy đủ nhất, hiếm dùng cho container

So sánh: Linux alpine ~5MB, debian-slim ~30MB. Chênh lệch 2-3 order of magnitude.

Version-Matching Constraint: Điểm Dễ Gây Lỗi Nhất

GKE chỉ hỗ trợ process isolation cho Windows container (không hỗ trợ Hyper-V isolation). Với process isolation, container chia sẻ kernel trực tiếp với host — tương tự Linux container. Hệ quả: base image OS build phải tương thích chặt với host node OS build.

Host Node OS: Windows Server 2022 LTSC (build 10.0.20348.x)
Container base image: windowsservercore-ltsc2022 (build 10.0.20348.y)
→ Tương thích NẾU major build number khớp (20348), dù minor patch (x, y) khác nhau

Nếu mismatch major build (ví dụ node chạy 2022 nhưng image build cho 2019):

Error: container <id>: hcs::System::CreateProcess: 
The container operating system does not match the host operating system.

Đây không phải lỗi network hay registry — pod sẽ vào trạng thái CrashLoopBackOff hoặc fail ngay ở create container step, dễ nhầm với lỗi image pull thông thường.

Nguyên Tắc Vận Hành

  • Pin base image tag theo channel node pool đang chạy — ví dụ node pool dùng LTSC 2022 thì mọi Dockerfile FROM phải dùng tag ltsc2022, không dùng tag generic (latest có thể trỏ version khác)
  • Khi upgrade node pool sang Windows Server version mới, phải rebuild lại toàn bộ image trước, không thể chạy image cũ trên node mới (và ngược lại)
  • CI/CD pipeline nên có step validate build compatibility trước khi cho phép deploy, tránh incident do version drift giữa base image và node pool

Chiến Lược Tối Ưu Image Pull

1. Chọn Base Image Nhỏ Nhất Khả Dụng

Nếu app chỉ cần .NET Core/.NET 5+ (không dùng .NET Framework, IIS, COM), luôn chọn Nano Server thay vì Server Core — giảm size 5-10 lần, giảm surface area vulnerability đáng kể.

2. Layer Caching & Registry Locality

Artifact Registry ở cùng region với GKE cluster giảm latency pull đáng kể do egress trong cùng region rẻ hơn và nhanh hơn. Layer caching hoạt động giống Linux — layer chung (base OS layer) chỉ pull một lần nếu đã có trên node từ image trước, nhưng vì Windows base layer đã lớn, cache hit rate ảnh hưởng tới thời gian pull nhiều hơn Linux đáng kể.

Image A: FROM servercore-ltsc2022 + app layer 1
Image B: FROM servercore-ltsc2022 + app layer 2 (khác)

Node đã pull Image A → pull Image B chỉ cần fetch "app layer 2"
(servercore base layer đã cache, tiết kiệm ~2-3GB transfer)

Hệ quả thiết kế: giữ base image tag consistent across toàn bộ team/service để tối đa hóa cache hit — tránh mỗi team tự chọn base image tag khác nhau (dù cùng OS version) gây cache miss không cần thiết.

3. Pre-pull Qua DaemonSet Hoặc Node Image Bake

Với image lớn và ổn định (base image ít đổi), có thể pre-pull vào node ngay khi node join cluster, tránh cold-start pull latency khi pod đầu tiên schedule:

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: windows-image-prepull
spec:
  selector:
    matchLabels: {app: prepull}
  template:
    metadata:
      labels: {app: prepull}
    spec:
      nodeSelector:
        kubernetes.io/os: windows
      containers:
      - name: prepull
        image: mcr.microsoft.com/windows/servercore:ltsc2022
        command: ["cmd", "/c", "ping -t localhost"]

DaemonSet buộc containerd pull image lên mọi Windows node, kể cả node mới join do autoscaling — tránh cold pull latency khi traffic spike cần scale-out nhanh.

4. Cân Nhắc Impact Lên Node Autoscaling

Vì Windows node image + container image pull tốn nhiều phút (so với vài giây trên Linux với image nhỏ), Cluster Autoscaler scale-out event cho Windows node pool có latency cao hơn đáng kể. Nếu workload cần scale nhanh theo traffic spike, cần tính buffer capacity (giữ sẵn một số node dư) thay vì hoàn toàn dựa vào reactive autoscaling — tương tự pattern "warm pool" quen thuộc ở AWS nhưng áp dụng thủ công qua min node count.

5. Image Vulnerability Scanning

Do base image lớn và chứa nhiều Windows component, surface area cho CVE lớn hơn đáng kể so với Linux minimal image. Artifact Registry vulnerability scanning nên chạy định kỳ, và nên có policy patch base image thường xuyên (Microsoft release Windows container base image update hàng tháng theo Patch Tuesday cycle) — image cũ vài tháng thường tích lũy nhiều CVE chưa patch.

Tham Khảo