Containers, Kubernetes & CI/CD

Interview Context

當面試官問「你的 model 是怎麼上線的?」,他想聽的不是 notebook 分數,而是你能不能講出 Docker image → registry → Kubernetes deployment → CI/CD → canary rollout 這條完整的路。DS/MLE 面試不會要你背 kubectl 指令,但會考你懂不懂 container 為什麼解決 environment 問題、K8s 怎麼做 scaling、以及新 model 怎麼安全地 deploy。

What You Should Understand

  • 能解釋 bare metal → VM → container 的演進,以及 container 為什麼適合 ML workloads
  • 知道 Docker image 和 container 的差異、layers 如何運作、Dockerfile 的最佳實踐
  • 能畫出 Kubernetes 的 control plane / worker node 架構,說明 pod、deployment、service 的角色
  • 理解 HPA、VPA、Cluster Autoscaler 的差異,以及 resource requests/limits 對 scheduling 的影響
  • 能比較 rolling、blue-green、canary、shadow 等 deployment strategies 的 risk 和 rollback tradeoffs
  • 知道 CI/CD pipeline 的 stages,以及 continuous delivery 和 continuous deployment 的差別

Virtualization vs Containerization

From Bare Metal to Containers

計算資源的隔離方式經歷三個世代:

Bare metal: Application 直接跑在實體機器的 OS 上。效能最好(沒有任何 virtualization overhead),但一台機器只能服務一組應用 — 資源利用率低,environment 衝突(兩個 app 需要不同版本的 library)無解。

Virtual Machines: Hypervisor 在硬體之上切出多台虛擬機,每台 VM 有自己完整的 guest OS。Hypervisor 分兩種:

TypeRuns OnExamplesUse Case
Type 1 (bare-metal)Directly on hardwareVMware ESXi, Xen, KVM, Hyper-VData centers, cloud providers (EC2 底層)
Type 2 (hosted)On top of a host OSVirtualBox, VMware Workstation, Parallels開發者本機測試不同 OS

Containers: 不虛擬化硬體,而是由 container engine(如 Docker)利用 Linux kernel 的 namespaces(隔離 process、network、filesystem 視圖)和 cgroups(限制 CPU/memory 用量)在同一個 OS kernel 上隔離出多個使用者空間。沒有 guest OS → 啟動快、體積小、密度高。

Comparison

DimensionBare MetalVirtual MachineContainer
IsolationNone (shared OS)Strong (hardware-level, separate kernel)Medium (process-level, shared kernel)
Startup timeMinutes (boot)Minutes (boot guest OS)Seconds or less
Image sizeN/AGBs (full OS)MBs to hundreds of MBs
Density1 workload per machineTens per hostHundreds per host
OverheadNoneHigh (guest OS + hypervisor)Near-zero
Security boundaryPhysicalVery strongWeaker (kernel exploits cross containers)

Why Containers Won for ML

  • Reproducibility: ML 最痛的問題是「在我電腦上可以跑」— CUDA 版本、Python 版本、library 相依全部打包進 image,training 和 serving 用同一份環境,消除 environment skew。
  • Fast iteration: Container 秒級啟動,適合 autoscaling(流量來了馬上開新 replica)和 batch training job(跑完就回收資源)。
  • Density and cost: 一台 GPU 機器可以同時跑多個 serving containers,資源利用率遠高於一 VM 一 app。
  • Ecosystem: Kubernetes、Kubeflow、Ray、SageMaker 等 ML infra 全部以 container 為部署單位。

VM 和 Container 不是二選一

實務上是疊起來用的:cloud provider 先用 Type 1 hypervisor 切出 VM(安全隔離不同客戶),你再在 VM 上跑 container(打包和調度你自己的 workloads)。EKS/GKE 的 worker node 就是 VM。

How Docker Works

Images vs Containers

ConceptWhat It IsAnalogy
ImageRead-only template: filesystem + metadata (entrypoint, env vars)Class(定義)
ContainerA running instance of an image with a writable layer on topObject(實例)
RegistryCentral store for images (Docker Hub, ECR, GCR, Artifact Registry)GitHub for images

一個 image 可以同時跑出多個 containers;container 刪掉後 writable layer 消失,image 不變 — 這就是 immutable infrastructure 的基礎。

Layers and Union Filesystem

Docker image 由一疊 read-only layers 組成,每個 Dockerfile 指令(RUN、COPY 等)產生一層。Union filesystem(如 overlay2)把這些 layers「疊」成一個統一的檔案系統視圖;container 啟動時在最上面加一層 thin writable layer。

好處:

  • Layer caching: rebuild 時只有變動的層(和它之後的層)需要重建 → build 快。
  • Layer sharing: 十個 containers 共用同一個 base image,磁碟上只存一份。
  • Fast distribution: pull image 時只下載本機沒有的 layers。

Dockerfile Anatomy

FROM python:3.11-slim        # base image (choose small!)
WORKDIR /app                 # working directory inside the image
COPY requirements.txt .      # copy deps file FIRST (cache-friendly)
RUN pip install --no-cache-dir -r requirements.txt   # one layer
COPY . .                     # copy source code (changes often → last)
EXPOSE 8000                  # documentation of the listening port
CMD ["uvicorn", "app:app", "--host", "0.0.0.0"]      # default command

關鍵直覺:變動頻率低的指令放前面,變動頻率高的放後面 — 這樣改 code 不會 invalidate 安裝 dependencies 的 cache layer。

Registry Flow: Build → Push → Pull → Run

Dockerfiledocker buildImagedocker pushRegistrydocker pullNodedocker runContainer\text{Dockerfile} \xrightarrow{\text{docker build}} \text{Image} \xrightarrow{\text{docker push}} \text{Registry} \xrightarrow{\text{docker pull}} \text{Node} \xrightarrow{\text{docker run}} \text{Container}

CI pipeline build 出 image、tag 上 version(例如 git commit SHA)、push 到 registry;production cluster 從 registry pull 指定 tag 來跑。永遠不要用 latest tag deploy production — 無法追蹤現在跑的是哪個版本,也無法精準 rollback。

Docker Best Practices

PracticeWhyHow
Small base image更快 pull、更小 attack surfacepython:3.11-slim 或 distroless,不要用完整 ubuntu
Multi-stage buildsBuild tools 不進最終 imageStage 1 編譯/安裝,Stage 2 只 copy 產物
Layer cachingBuild 快Dependencies 先 COPY + install,source code 最後 COPY
Non-root userContainer 被攻破時降低權限USER appuser(建立專用 user)
.dockerignore避免把 data、.git、venv 打進 image排除 .git, pycache, data/, *.ipynb
Pin versionsReproducible buildsrequirements.txt 鎖版本,base image 用 digest 或明確 tag

ML Image 常見錯誤

最常見的錯誤是把 training data、notebook、甚至 credentials COPY 進 image。Image 會被 push 到 registry、被很多人 pull — secrets 進了 layer 就算後面 RUN rm 掉也還在 layer history 裡。Data 用 volume mount 或 object storage,secrets 用 runtime 注入(K8s Secret、env vars)。

Kubernetes Architecture

Docker 解決「一台機器上跑 container」;Kubernetes(K8s)解決「幾百台機器上跑幾千個 containers」— 調度、自癒、擴縮、服務發現。

Control Plane vs Worker Nodes

ComponentRole直覺
API Server所有操作的唯一入口(kubectl、controllers 都打它)集群的前台櫃檯
etcdDistributed key-value store,存整個 cluster 的 desired + actual state集群的資料庫(唯一的 source of truth)
Scheduler決定新 pod 要放到哪個 node(看 resources、affinity、taints)排班經理
Controller Manager跑 reconciliation loops:actual state 不等於 desired state 就修正自動糾錯的管家
Kubelet (worker)每個 node 上的 agent,跟 API server 溝通、實際啟動/監控 containers工頭
Kube-proxy (worker)維護 node 上的 network rules,讓 Service 的流量能導到 pods交通警察

核心設計哲學是 declarative + reconciliation:你在 YAML 宣告 desired state(「我要 3 個 replicas」),controllers 不斷比對現況並修正 — pod 掛了就補一個,node 掛了就把 pods 搬走。這就是 self-healing 的來源。

Core Objects

ObjectWhat It IsWhy You Need It
Pod最小部署單位:一個或多個共享 network/storage 的 containersContainer 的執行單位(通常一個 pod 一個主 container)
Deployment管理一組相同 pods 的 replicas + rolling update你幾乎不直接建 pod,都透過 Deployment
Service一組 pods 的穩定虛擬 IP + load balancingPod IP 會變(重啟就換),Service 提供穩定入口
IngressHTTP(S) 層的路由:domain/path → Service對外暴露 API、TLS termination
ConfigMap / Secret設定與機密的注入Image 不變,環境設定可換
Job / CronJob跑完就結束的 workloadTraining jobs、batch inference、定時 retraining

Pod Lifecycle

PendingRunningSucceeded  /  Failed\text{Pending} \to \text{Running} \to \text{Succeeded} \;/\; \text{Failed}
  • Pending: Pod 已建立但還沒被排到 node(等 scheduler)或還在 pull image。卡在 Pending 最常見原因:沒有 node 有足夠的 CPU/memory/GPU 滿足 requests。
  • Running: 至少一個 container 正在跑。
  • Succeeded / Failed: 所有 containers 結束(Job 類 workload);serving pods 正常情況下永遠 Running。

三種 probes 決定 K8s 怎麼對待一個 running pod:

ProbeQuestion It AnswersOn Failure
LivenessProcess 還活著嗎(沒有 deadlock)?Restart container
Readiness現在可以接流量嗎(model 載入好了嗎)?從 Service endpoints 移除(不送流量,不重啟)
Startup慢啟動的 app 開好了沒?在 startup 完成前不跑 liveness(避免大 model 載入中被誤殺)

ML Serving 必懂:Readiness Probe

Model server 啟動要先載入幾 GB 的 model weights,可能要 30-120 秒。沒有設 readiness probe 的話,K8s 一看 container 起來就把流量導過去 → 大量 500 errors。正確做法:readiness endpoint 回傳 model 是否載入完成;rolling update 時新 pod ready 之前舊 pod 不會被砍。

Kubernetes Scaling

Three Autoscalers

AutoscalerWhat It ScalesTriggerBest For
HPA (Horizontal Pod Autoscaler)Pod replicas 數量CPU/memory utilization 或 custom metrics(QPS、queue length)Stateless serving — 流量漲就加 replicas
VPA (Vertical Pod Autoscaler)單一 pod 的 CPU/memory requests歷史用量不易水平擴展的 workload、right-sizing requests
Cluster AutoscalerNode(VM)數量Pods 因資源不足卡在 Pending配合 HPA — pods 加了沒地方放時開新機器

三者常一起用:流量上升 → HPA 加 pods → node 塞不下、pods Pending → Cluster Autoscaler 開新 node。注意 HPA 和 VPA 不要同時對 CPU/memory 作用在同一個 workload(會互相打架)。

Resource Requests and Limits

resources:
  requests:        # what the scheduler reserves for you
    cpu: "1"
    memory: 2Gi
  limits:          # hard cap at runtime
    cpu: "2"       # exceed → throttled
    memory: 4Gi    # exceed → OOMKilled
  • Requests 是 scheduler 排程的依據 — node 的可分配資源夠不夠放這個 pod。設太低 → pods 擠在一起互搶資源;設太高 → 資源閒置、cluster 成本爆炸。
  • Limits 是 runtime 上限 — CPU 超過會被 throttle(變慢),memory 超過會被 OOMKilled(直接砍掉重啟)。
  • Requests 等於 limits 的 pod 是 Guaranteed QoS,最不容易在資源壓力下被驅逐 — production serving 建議這樣設。

Why GPU Scheduling Matters for ML

GPU 和 CPU 在 K8s 的行為根本不同:

  • GPU 是整數資源、不可超賣:nvidia.com/gpu 只能 request 整顆(除非用 MIG/time-slicing),沒有「requests 和 limits 不同」這回事。一個 inference pod 只用 30% GPU 也佔掉整顆 → 利用率問題。
  • 貴且稀缺:GPU node 一小時數美元起跳,Cluster Autoscaler 開 GPU node 又慢(幾分鐘)→ 常用「保留 baseline + queue 排隊」而不是純 reactive autoscaling。
  • Taints and tolerations:GPU nodes 通常設 taint,避免普通 CPU workload 被排上去浪費昂貴機器;ML pods 加 toleration + nodeSelector 指定 GPU 型號(A100 vs T4 差很多)。
  • Bin packing:training 要整顆或多顆 GPU、serving 想共享 GPU — 混排時 fragmentation 會讓大 job 排不進去,所以常把 training pool 和 serving pool 分開。

CI/CD Pipeline

Stages

CI/CD 把「寫完 code 到跑在 production」自動化成一條 pipeline:

CommitBuildTestPackageDeploy (staging)Deploy (production)\text{Commit} \to \text{Build} \to \text{Test} \to \text{Package} \to \text{Deploy (staging)} \to \text{Deploy (production)}
StageWhat HappensTypical Tools
BuildCompile / install deps / lint / type checkGitHub Actions, GitLab CI, Jenkins
TestUnit + integration tests, coverage gatepytest, coverage
PackageBuild Docker image, tag with commit SHA, push to registryDocker, ECR/GCR
Deploy staging部署到 staging 環境,跑 e2e / smoke testsHelm, kubectl, ArgoCD
Deploy production漸進式 rollout + 監控 + 自動 rollbackArgo Rollouts, Spinnaker

Continuous Delivery vs Continuous Deployment

TermMeaningHuman Involved?
Continuous Integration每次 commit 自動 build + test,快速發現整合問題No
Continuous Delivery每個通過測試的版本隨時可以部署,但按下 deploy 鈕是人Yes(最後一步手動核准)
Continuous Deployment通過所有 gates 就自動部署到 production,全程無人No

大部分公司做到 continuous delivery;continuous deployment 需要極高的測試覆蓋和監控成熟度。

Testing Pyramid

LayerScopeSpeedQuantity
Unit tests單一 function/class,mock 掉外部依賴毫秒級最多(底層)
Integration tests多個 components 一起(app + DB、feature pipeline)秒到分鐘中等
End-to-end tests整條 user flow 打真實(staging)環境分鐘級、易 flaky最少(頂層)

直覺:越往上越貴越慢越脆 — 所以數量呈金字塔。ML pipeline 還會多兩類:data validation tests(schema、分布)和 model validation gates(新 model 的 offline metrics 必須不輸 baseline 才准進 deploy stage)。

How Companies Ship Safely

安全上線的公式是:small changes(一次改一點,錯了好找)+ automated gates(tests、canary metrics 擋住壞版本)+ fast rollback(一鍵回滾比「不出錯」更務實)。面試講 deployment 時主動提 rollback plan,是 senior signal。

Deployment Strategies

Comparison

StrategyHow It WorksRiskRollback SpeedExtra CostBest For
Big bang全部流量一次切到新版最高慢(要重新 deploy 舊版)內部工具、不得已的 breaking change
Rolling逐一替換 replicas(K8s Deployment 預設)中(re-roll 回舊 image)一般 stateless services
Blue-green兩套完整環境,流量在 load balancer 一次切換秒級(切回舊環境)高(雙倍資源)需要瞬間切換 + 瞬間回滾
Canary先給 1-5% 流量,看 metrics 再逐步放大最低快(把流量切回去)低-中高風險變更、model deployment
Feature flagsCode 先上線但功能用開關控制、可按 user 分群開即時(關 flag,不用 deploy)低(技術債:flag 清理)產品功能、A/B testing
Shadow (mirroring)複製真實流量給新版,但 response 不回給 user零(user 無感)N/A(本來就沒接流量)中(雙倍 inference 成本)驗證新 model/系統的行為

Tie to Model Deployment

Model 是「行為由 data 決定」的 artifact,offline test 永遠不能完全保證 online 行為,所以 model deployment 幾乎都用 shadow → canary 的組合:

  1. Shadow mode: 新 model 對 production traffic 做推論但不影響 user → 比較新舊 predictions 的分布差異、latency、error rate。抓 engineering bugs(feature 拿錯、serialization 錯)。
  2. Canary: 1% → 5% → 25% → 100%,每一步看 online metrics(CTR、fraud loss、latency p99)。抓 offline 看不到的 model quality 問題。
  3. Rollback trigger 事先定義:例如 canary 群的 conversion 掉超過 X% 或 error rate 超過閾值就自動切回 — 不要臨場拍腦袋。

Feature flag 對 ML 的對應物是 model registry 的 stage 切換:serving code 不變,flag/config 決定載入哪個 model version。

Infrastructure as Code

What: 用宣告式的 code(而不是手動點 console)定義 infrastructure — servers、clusters、networks、IAM。Why: 可以 code review、版控、重現環境(dev/staging/prod 一致)、災難後快速重建;手動配置的環境是「snowflake」,沒人敢動也沒人能重建。

ApproachRepresentative ToolWhat It Manages一句話
Provisioning (declarative IaC)Terraform, CloudFormation, PulumiCloud resources(VPC、EKS cluster、S3、IAM)宣告 desired state,工具算 diff 去 apply
Configuration managementAnsible, Chef, Puppet機器裡面的設定(套件、config files)對已存在的機器做設定(container 時代重要性下降)
GitOpsArgoCD, FluxK8s 上的 application manifestsGit 是唯一 source of truth,controller 自動把 cluster 同步到 repo 狀態

GitOps 的關鍵反轉:不是 CI pipeline push 變更到 cluster,而是 cluster 裡的 agent(ArgoCD)pull Git repo 並持續 reconcile — deploy 等於 merge 一個 PR,rollback 等於 revert 一個 commit,全部有 audit trail。這和 K8s 本身的 reconciliation 哲學一脈相承。

Real-World Use Cases

Case 1: 把 Notebook 模型包成 Docker Image 上 K8s Serving

你在 notebook 訓練好一個詐欺偵測模型(LightGBM + 前處理),要變成一個 p99 latency 100ms 以下的 API。步驟:

  1. 把 notebook 重構成 module:train.py 產出 model artifact,app.py(FastAPI)載入 artifact 提供 /predict/healthz
  2. Multi-stage Dockerfile:builder stage 裝 dependencies,final stage 用 slim base + non-root user。Model artifact 不烘進 image,啟動時從 model registry / object storage 下載(image 和 model version 解耦)。
  3. K8s Deployment:3 replicas、readiness probe 等 model 載入完成、requests=limits 拿 Guaranteed QoS、HPA 以 QPS 為 metric。
  4. Service + Ingress 對內部系統暴露 API。

面試 follow-up:

  • 為什麼 model artifact 不直接 COPY 進 image?(Model 每天 retrain,image 不用重 build;rollback model 不用 rollback code;image 保持小。)
  • 一個 pod 可以扛多少 QPS,怎麼知道?(Load test 找出單 pod 容量 → 決定 replicas 和 HPA 上限。)
  • Feature 從哪裡來?(Online feature store lookup — 注意這常是 latency bottleneck,不是 model inference。)

Case 2: Canary Release 一個新版推薦模型

推薦系統要上 v2 model(offline NDCG +2%)。你不敢直接全量切,設計如下:

  1. Shadow 一週:v2 對 100% 流量做推論但不 serve,比較 v1/v2 的 prediction 分布、latency p99、error rate — 抓 feature pipeline bug。
  2. Canary 開始:5% users 由 v2 serve(用 user id hash 分流,確保同一 user 體驗一致 — 這其實就是一個 A/B test)。
  3. 事先定義 guardrails:CTR 掉超過 1%、latency p99 超過 150ms、或 error rate 超過 0.1% → 自動把流量切回 v1。
  4. Metrics 顯著正向 → 5% → 25% → 50% → 100%,每一步觀察至少一個 business cycle(例如一整天,避開 day-of-week effect)。

面試 follow-up:

  • Canary 和 A/B test 差在哪?(機制幾乎相同;canary 目的是 safety(找 regression、樣本小、看 guardrail metrics),A/B test 目的是 inference(統計顯著性、固定樣本數)。)
  • 5% canary 的 metrics 要看多久才能下結論?(取決於 metric 的 variance 和 traffic 量 — 連到 power analysis;guardrail 用寬鬆閾值快速擋災難,精細比較留給正式 A/B。)
  • 如果 v2 的 improvement 只在冷門 items 上,canary metrics 可能看不出來,怎麼辦?(Segment 分析、拉長觀察期、或用 interleaving。)

Case 3: Training Job 的資源配置與 Autoscaling

你的團隊每天在 K8s 上跑幾十個 training jobs(K8s Job 物件),常見兩個問題:有人的 job 卡 Pending 排不進去,有人的 job 跑到一半被 OOMKilled。你的設計:

  1. Right-size requests/limits:用 VPA recommendation mode 或歷史 metrics 找出每類 job 的真實用量。Memory limit 設太低 → OOMKilled(training 跑三小時後在第 80 epoch 被砍最痛);requests 設太高 → 卡 Pending 而且浪費配額。
  2. GPU pool 隔離:GPU nodes 設 taint,training jobs 加 toleration;training pool 和 serving pool 分開,避免 training 把 serving 的資源吃光。
  3. Cluster Autoscaler + spot instances:training 可容忍中斷(有 checkpointing)→ 用 spot GPU nodes 省 60-70% 成本;job 排進來時 autoscaler 開機器,跑完縮回去。
  4. Queue + priority:用 priority classes 或 job queue(Kueue、Volcano)讓 production retraining 優先於 ad-hoc 實驗。

面試 follow-up:

  • Training job 被 spot instance 回收怎麼辦?(Checkpoint 到 object storage,job restart 後從 checkpoint 恢復。)
  • 為什麼 GPU 利用率只有 30%?(常見原因是 data loading bottleneck — CPU requests 太低餵不飽 GPU;先 profile 再加 GPU。)
  • 一個 8-GPU 的 job 一直 Pending 但 cluster 明明有 8 顆空 GPU?(Fragmentation — 8 顆散在 4 台機器上,job 要求同一台的 8 顆。需要 bin packing / gang scheduling。)

Hands-on: Dockerfile, K8s Manifests, and CI

這個主題的 hands-on 不是 Python,而是三個 production 必備的 artifacts。

Multi-stage Dockerfile for an ML Serving App

# ---------- Stage 1: builder ----------
FROM python:3.11-slim AS builder
WORKDIR /app

# Install deps into a virtualenv (easy to copy as one unit)
COPY requirements.txt .
RUN python -m venv /opt/venv && \
    /opt/venv/bin/pip install --no-cache-dir -r requirements.txt

# ---------- Stage 2: runtime ----------
FROM python:3.11-slim
WORKDIR /app

# Copy only the installed packages, not build tools/caches
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# Copy source code last (changes most often → best layer caching)
COPY app/ ./app/

# Run as non-root for security
RUN useradd --create-home appuser
USER appuser

EXPOSE 8000
# Model weights are downloaded at startup from the registry,
# so the image stays small and model versions are decoupled
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

Kubernetes Deployment + Service with Probes and Resources

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fraud-model
spec:
  replicas: 3                      # HA: survive single pod/node failure
  selector:
    matchLabels: { app: fraud-model }
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0            # never drop below desired capacity
      maxSurge: 1                  # add one new pod at a time
  template:
    metadata:
      labels: { app: fraud-model }
    spec:
      containers:
        - name: server
          image: registry.example.com/fraud-model:git-3fa2b1c  # never :latest
          ports:
            - containerPort: 8000
          resources:
            requests: { cpu: "1", memory: 2Gi }   # scheduler reserves this
            limits:   { cpu: "1", memory: 2Gi }   # equal → Guaranteed QoS
          startupProbe:                 # allow slow model loading
            httpGet: { path: /healthz, port: 8000 }
            failureThreshold: 30        # up to 30 * 5s = 150s to start
            periodSeconds: 5
          readinessProbe:               # gate traffic until model is loaded
            httpGet: { path: /ready, port: 8000 }
            periodSeconds: 10
          livenessProbe:                # restart if process deadlocks
            httpGet: { path: /healthz, port: 8000 }
            periodSeconds: 15
---
apiVersion: v1
kind: Service
metadata:
  name: fraud-model
spec:
  selector: { app: fraud-model }     # routes to ready pods only
  ports:
    - port: 80
      targetPort: 8000

GitHub Actions CI Sketch

name: ci-cd
on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.11" }
      - run: pip install -r requirements.txt -r requirements-dev.txt
      - run: ruff check .            # lint
      - run: pytest --cov=app        # unit + integration tests

  build-and-push:
    needs: test                      # only runs if tests pass
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build and push image tagged with commit SHA
        run: |
          docker build -t $REGISTRY/fraud-model:${{ github.sha }} .
          docker push $REGISTRY/fraud-model:${{ github.sha }}

  deploy-staging:
    needs: build-and-push
    runs-on: ubuntu-latest
    steps:
      # GitOps style: update the image tag in the manifests repo;
      # ArgoCD detects the change and syncs the cluster
      - name: Bump image tag in k8s manifests
        run: |
          ./scripts/update-manifest.sh staging ${{ github.sha }}
          git commit -am "deploy ${{ github.sha }} to staging" && git push
      # Production deploy = promote via PR + canary rollout (manual gate)

Interview Signals

What interviewers listen for:

  • 你能講清楚 VM 和 container 的隔離層級差異(guest OS vs shared kernel),而不是背「container 比較輕」
  • 你知道 image layer caching 怎麼影響 Dockerfile 的指令順序,以及為什麼不能用 latest tag deploy
  • 你能解釋 K8s 的 declarative + reconciliation 模型,並用 readiness probe 說明 model server 的啟動流程
  • 講 deployment 時主動提 rollback plan 和事先定義的 guardrail metrics,而不是只講 happy path
  • 你能把 shadow / canary 連回 ML:offline metrics 不能保證 online 行為,所以 model deploy 需要漸進驗證

Practice

Flashcards

Flashcards (1/10)

VM 和 container 的核心差異是什麼?

VM 由 hypervisor 虛擬化硬體,每台 VM 有自己完整的 guest OS → 隔離強但重(GB 級、分鐘級啟動)。Container 共享 host OS kernel,用 namespaces + cgroups 做 process 層隔離 → 輕(MB 級、秒級啟動、高密度)但隔離較弱。實務上疊著用:cloud 用 VM 隔離客戶,客戶在 VM 上跑 containers。

Click card to flip

Quiz

Question 1/10

Container 比 VM 啟動快、體積小的根本原因是?

Mark as Complete

3/5 — Okay