Skip to content
DevRecall
← Roadmap
25

Containers, CI/CD & IaC

drafted

Docker cho Node (multi-stage, non-root, PID 1 & signal), Kubernetes basics, thiết kế pipeline, rolling/blue-green/canary, feature flags, migration trong pipeline, Terraform/CDK, GitOps.

0/45 rated · mastery 0%

Bài học

2 bài · ~25 phút đọc. Học theo thứ tự, rồi làm câu hỏi.
  1. 01~13 phút· 4 câuverify

    Image, layer và build cache cho Node

    Image là gì, layer và cache key, thứ tự Dockerfile, .dockerignore, npm ci vs npm install, BuildKit cache mount và cache trong CI, 12-factor build/release/run.

  2. 02~12 phút· 4 câuverify

    Multi-stage build, base image và non-root

    Multi-stage cho TypeScript/Next.js standalone, chọn alpine/slim/distroless, non-root và securityContext, secret lúc build (ARG/ENV vs secret mount), review một Dockerfile production.

Tóm tắt & cheat sheet của track

TL;DR

  • Interviewer muốn biết bạn ship code an toàn và lặp lại được: image nhỏ và an toàn, pipeline nhanh và đáng tin, deploy có đường lui, hạ tầng mô tả bằng code.
  • Với Node, 80% câu hỏi Docker xoay quanh: multi-stage build, layer caching (package*.json trước, npm ci), non-root user, PID 1 & SIGTERM (graceful shutdown), .dockerignore.
  • Kubernetes basics cần nói chắc: Pod/Deployment/Service/Ingress, readiness vs liveness vs startup probe, requests/limits và OOMKilled, HPA, ConfigMap/Secret.
  • Deploy strategy là trade-off giữa tốc độ rollback, chi phí, và blast radius; DB migration là thứ quyết định bạn có rollback được hay không (expand/contract).
  • IaC: Terraform state là trái tim (remote backend + locking + drift detection); GitOps = Git là source of truth, cluster tự pull trạng thái mong muốn.

Khái niệm cốt lõi

Khái niệmMột câuVí dụ ngắn
Multi-stage buildBuild ở stage có devDependencies, copy chỉ artifact sang stage runtimeImage từ ~1 GB xuống ~150 MB
Layer cachingMỗi instruction là một layer; layer đổi → mọi layer sau build lạiCOPY package*.json + npm ci trước COPY . .
PID 1Process đầu tiên trong container nhận signal, và kernel không áp default handler cho nóCMD npm start → SIGTERM không tới node → bị SIGKILL sau grace period
Readiness probe"Có nhận traffic được không?" — fail thì bị rút khỏi Service endpointsĐang warm cache → chưa ready
Liveness probe"Có cần restart không?" — fail thì kubelet restart containerEvent loop treo hẳn
requests / limitsrequests dùng để schedule; limits là trần (memory vượt → OOMKilled, CPU vượt → throttle)memory: 512Mi limit, Node heap vượt → exit 137
Rolling updateThay dần pod cũ bằng pod mớimaxSurge: 25%, maxUnavailable: 0
Blue/greenHai môi trường đầy đủ, đổi traffic một lầnRollback = chuyển router về blue
CanaryĐẩy % traffic nhỏ sang bản mới, đo, rồi tăng dần5% → 25% → 100% nếu error rate ổn
Feature flagTách deploy (code lên prod) khỏi release (bật cho user)Bật checkout mới cho 10% tenant
Terraform stateFile ánh xạ resource trong code ↔ resource thậtMất state = Terraform muốn tạo lại mọi thứ
GitOpsAgent trong cluster (Argo CD/Flux) reconcile theo GitMerge PR = deploy; revert commit = rollback

Cơ chế hoạt động

Pipeline chuẩn cho một Node/Next.js service:

Graceful shutdown của một pod:

Trade-offs

StrategyRollbackChi phí hạ tầngBlast radiusYêu cầu
RecreateChậm (deploy lại)Thấp100% + downtimeChấp nhận downtime
RollingVừa (rollout undo)Thấp (+surge)Tăng dần, khó dừng giữa chừng có kiểm soátCode cũ & mới chạy song song, schema tương thích
Blue/greenTức thì (đổi router)x2 trong lúc deploy100% khi switchStateless hoặc DB tương thích cả hai
CanaryNhanh (dồn traffic về bản cũ)ThấpNhỏ, có kiểm soátMetric tốt theo version, tự động phân tích
Feature flagTức thì (tắt flag)KhôngTheo segmentKỷ luật dọn flag, test cả hai nhánh
IaCMạnhYếu
Terraform/OpenTofuMulti-cloud + SaaS provider, plan rõ ràng, ecosystem lớnTự quản state, HCL hạn chế logic
AWS CDKTypeScript thật, construct tái sử dụng, sinh CloudFormationChỉ AWS (chủ yếu), CloudFormation chậm/giới hạn
CloudFormationNative, state do AWS quản, rollback stackYAML dài, ít trừu tượng
PulumiNgôn ngữ thật, multi-cloudEcosystem nhỏ hơn Terraform

Pitfalls

  • FROM node:latest → build không tái lập; dùng tag cụ thể (hoặc digest) và cập nhật có chủ đích.
  • COPY . . trước npm install → mỗi lần sửa code đều cài lại dependency; thiếu .dockerignore → copy cả node_modules, .env, .git vào image.
  • Chạy container bằng root; secret trong ENV/ARG (lộ qua docker history) — dùng BuildKit secret mount.
  • CMD npm start / shell form → signal không tới Node; không xử lý SIGTERM → cắt ngang request mỗi lần deploy.
  • Liveness probe gọi DB → DB chậm làm restart toàn bộ pod (cascading failure).
  • Không đặt memory limit hoặc đặt heap Node lớn hơn limit → OOMKilled.
  • Build image riêng cho từng môi trường thay vì build once, promote cùng một digest.
  • Migration phá vỡ tương thích (rename/drop cột) chạy cùng release code mới → không rollback được.
  • Terraform state local / trong git; không có locking → hai người apply cùng lúc hỏng state.
  • Feature flag không bao giờ được dọn → nợ kỹ thuật, tổ hợp nhánh không ai test.

Cheat sheet

  • Dockerfile Node: multi-stage · npm ci · --omit=dev · USER node · CMD ["node","server.js"] (exec form) · --init/tini nếu cần reap zombie · .dockerignore.
  • Next.js: output: "standalone" để image runtime chỉ chứa server tối thiểu.
  • Probes: startup (khởi động chậm) → readiness (dependency cần thiết cho traffic) → liveness (chỉ process tự thân, rẻ).
  • Exit code 137 = SIGKILL (thường OOMKilled); 143 = SIGTERM.
  • HPA scale theo % requests → không có requests thì HPA CPU không hoạt động đúng.
  • K8s Secret chỉ là base64 → bật encryption at rest, hoặc External Secrets / CSI driver.
  • Pipeline: fail fast (lint/typecheck trước), cache dependency, song song hoá, artifact bất biến theo SHA.
  • Rollback code phải luôn khả thi → migration expand trước, contract sau.
  • Terraform: remote state + lock, plan trong PR, apply từ CI, drift detection định kỳ, module có version.
  • 12-factor: config trong env, stateless process, logs ra stdout, dev/prod parity, disposability (start nhanh, shutdown êm).

Personal notes (Notion)

Ghi chú cá nhân — có thể sai hoặc lỗi thời, dùng để tham khảo.

Interview Questions

Showing 45 / 45

FROM node:22-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev

FROM node:22-slim
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build --chown=node:node /app/dist ./dist
COPY --from=build --chown=node:node /app/node_modules ./node_modules
USER node
CMD ["node", "dist/server.js"]
FROM node:latest
WORKDIR /app
COPY . .
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc && npm install
ENV DATABASE_URL=postgres://admin:secret@db:5432/app
EXPOSE 3000
CMD npm run start
const server = app.listen(port);
const shutdown = () => {
  server.close(async () => { await db.end(); process.exit(0); });
  setTimeout(() => process.exit(1), 25_000).unref(); // safety net
};
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);
containers:
  - name: api
    image: acme/api:1.4.2
    env:
      - name: NODE_OPTIONS
        value: "--max-old-space-size=1024"
    resources:
      requests: { cpu: "250m", memory: "256Mi" }
      limits:   { cpu: "500m", memory: "512Mi" }
on:
  pull_request_target:
    types: [opened, synchronize]
jobs:
  deploy-preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - uses: some-org/deploy-action@main
      - run: npm ci && npm run build && ./deploy.sh
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
lifecycle:
  preStop:
    exec: { command: ["sh", "-c", "sleep 10"] }
terminationGracePeriodSeconds: 45
livenessProbe:
  httpGet: { path: /health, port: 3000 }   # /health runs SELECT 1 and pings Redis
  periodSeconds: 5
  timeoutSeconds: 1
  failureThreshold: 2
readinessProbe:
  httpGet: { path: /health, port: 3000 }
moved {
  from = aws_db_instance.main
  to   = module.database.aws_db_instance.this
}
resource "aws_db_instance" "this" {
  deletion_protection = true
  lifecycle { prevent_destroy = true }
}