Nginx static server, reverse proxy và container operations
Nginx dùng master/worker event-driven model. Trong container, mental model quan trọng là: một foreground master process, cấu hình bất biến theo image/deployment, log đi ra stdout/stderr, request routing phải được hiểu rõ trước khi debug proxy, và mọi thay đổi production cần có validation + rollback path.
1. Nginx & Ops: mental model
Nginx thường xuất hiện ở hai vai trò trong hệ thống container: phục vụ static content như SPA và làm reverse proxy/gateway mỏng tới upstream application. Master process quản lý cấu hình và worker lifecycle; worker xử lý connection theo event-driven model, vì vậy một container Nginx không nên được vận hành như một VM có daemon manager riêng.
Khi đọc một request, hãy theo chuỗi: listen socket → server selection → location matching → phase/content handler → upstream/static response → filters → access/error log. Chuỗi này giúp phân biệt lỗi routing của Nginx với lỗi application phía sau.
2. Request processing
Nginx trước tiên chọn server dựa trên địa chỉ/port đang listen và Host/server_name; nếu không có match thì request đi vào default server của listen socket đó. Sau đó Nginx chọn location. Prefix location và regex location có precedence riêng, nên “cấu hình nhìn đúng” vẫn có thể route sai nếu một location khác thắng.
Location precedence cần nhớ
- Exact match như
location = /healthzlà trường hợp rõ nhất và kết thúc tìm kiếm. - Prefix location được chọn theo prefix dài nhất; modifier như
^~có thể ngăn bước regex tiếp theo. - Regex locations được đánh giá theo thứ tự cấu hình khi bước regex được thực hiện; regex match đầu tiên thắng.
- URI matching không dựa trên query string, nên debug
/api?id=1phải tập trung vào path/api.
Sau khi location được chọn, request đi qua các phase/modules như rewrite, access, content, filters và log. Không cần nhớ mọi internal phase để vận hành, nhưng phải biết module nào thực sự tạo response: static file, proxy_pass, FastCGI, return, v.v.
3. Static SPA image
Mô hình điển hình là multi-stage build: stage Node/npm tạo static artifacts, stage runtime chỉ chứa Nginx + artifacts. Cách này giữ runtime nhỏ hơn và không mang build toolchain vào production image.
# Build stage
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Runtime stage
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
Client-side routing thường cần fallback về index.html, nhưng fallback phải được đặt ở phạm vi SPA routes, không che giấu asset/API bị sai.
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://app:8080/;
}
Với hashed assets như app.8f31c2.js, có thể cache lâu vì URL đổi khi content đổi. Với index.html, dùng TTL ngắn hoặc revalidation/no-cache phù hợp deployment để client nhanh nhìn thấy manifest/build mới.
index.html mới tham chiếu asset mới nhưng CDN/pod cũ chưa có asset đó, user có thể nhận HTML mới + 404 asset. Immutable asset naming, upload/copy asset trước khi chuyển HTML và giữ một khoảng overlap giữa release giúp giảm lỗi này.4. Proxy caveats
Reverse proxy không chỉ là proxy_pass. Bạn cần quyết định rõ trust boundary cho forwarded headers, timeout budget, buffering, request body limits, retry policy và protocol upgrade.
| Area | Điều cần quyết định | Failure nếu bỏ qua |
|---|---|---|
| Headers | Host, X-Forwarded-For, X-Forwarded-Proto; chỉ trust proxy chain đã biết. | Sai redirect/scheme, spoof client IP, audit không đáng tin. |
| Timeout | Connect/read/send timeout phải nằm trong end-to-end latency budget. | Worker giữ connection quá lâu hoặc cắt request hợp lệ quá sớm. |
| Buffering | Chọn request/response buffering theo payload, streaming và disk/temp behavior. | Memory/disk pressure, latency bất ngờ hoặc streaming bị phá. |
| Body size | Đặt giới hạn upload phù hợp use case. | 413 cho request hợp lệ hoặc mở cửa cho payload quá lớn. |
| Retry | Retry chỉ khi method/outcome an toàn và có giới hạn tries/time. | Double side effect hoặc retry storm. |
| WebSocket | Forward upgrade headers và đặt timeout phù hợp connection dài. | Handshake thất bại hoặc connection rơi sớm. |
proxy_next_upstream có thể chuyển request sang upstream khác trong một số lỗi/timeout. Với non-idempotent request, retry có thể tạo tác dụng phụ lặp lại nếu upstream cũ đã xử lý nhưng response bị mất. Vì vậy retry policy phải gắn với semantics của method và idempotency của application, không chỉ với HTTP status.
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 2s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
}
X-Forwarded-* nào là sự thật nếu client có thể đi thẳng tới Nginx. Chuỗi proxy tin cậy, real-IP configuration và network exposure phải thống nhất.5. Container operations
Foreground, config test và graceful reload
Container cần foreground process để lifecycle signal đi tới process chính. Trước khi reload, chạy nginx -t. Khi reload, master process kiểm tra syntax và cố mở các file/listen socket cần thiết; nếu áp dụng thành công thì worker mới nhận config mới và worker cũ shutdown graceful.
nginx -t
nginx -s reload
# graceful shutdown khi cần
nginx -s quit
Trong orchestration thực tế, immutable deployment thường an toàn hơn việc mutate cấu hình trong container đang chạy: build/config version mới → canary/rolling update → health/readiness → rollback về artifact trước nếu metric xấu. Reload vẫn hữu ích cho một số mô hình config mount, nhưng cần audit rõ ai thay config và rollback file nào.
Logging và health
Access/error logs nên đi ra stdout/stderr hoặc được runtime forward tới hệ thống log tập trung. Health endpoint không nên chỉ trả static 200 nếu Nginx “sống” nhưng upstream critical đã chết. Tách liveness (process còn vận hành) khỏi readiness (instance có thể nhận traffic) nếu orchestrator hỗ trợ.
- Access log: status, request time, upstream status, upstream response/connect time, request ID/correlation ID.
- Error log: config/runtime errors, upstream connection failures, permission/path issues.
- Runtime signals: connection count/rate, 4xx/5xx rate, 499, 502/504, p95/p99 latency, upstream saturation và restart count.
Least privilege và filesystem
Ưu tiên non-root khi image/deployment cho phép; dùng high port hoặc capability tối thiểu nếu cần bind privileged port. Read-only root filesystem là mục tiêu tốt, nhưng phải cấp writable path có chủ đích cho PID/cache/temp nếu cấu hình/module cần chúng. Secret/TLS private key không nên bake vào image public/shared.
Rollback / recovery checklist
- Lưu image digest/config version của release trước và có lệnh rollback đã test.
- Canary một phần traffic; theo dõi 5xx, 499, latency, upstream error và saturation trước khi mở rộng.
- Nếu config reload fail, không tiếp tục rollout; giữ worker/config cũ đang phục vụ và sửa artifact mới.
- Nếu release mới gây lỗi route/cache, rollback cả Nginx config và static assets theo cùng release boundary.
- Sau incident, lưu effective config bằng
nginx -T(cẩn thận secret có thể xuất hiện trong config).
6. Debug
| Triệu chứng | Khả năng thường gặp | Kiểm tra đầu tiên |
|---|---|---|
| 403 | Filesystem permission, directory index, deny/access rule, root path. | Xác nhận effective root/alias, UID/GID và error log. |
| 404 | Sai server/location, try_files, SPA fallback hoặc URI mapping. | Xác nhận location thắng và file/path thực tế. |
| 502 | Upstream DNS, listener/port, connection refused/reset, protocol mismatch. | Resolve/connect từ network namespace/container Nginx tới upstream. |
| 504 | Upstream chậm/treo hoặc timeout budget quá ngắn. | So request time với upstream connect/header/response timing. |
| Redirect loop | Sai forwarded proto/host, TLS termination awareness hoặc app canonical redirect. | Trace Location, scheme và forwarded headers qua từng hop. |
| SPA refresh 404 | Thiếu/sai try_files fallback. | Phân biệt route client với static asset/API path. |
Debug flow thực dụng
- Reproduce bằng request tối thiểu và giữ request/correlation ID.
- Chạy
nginx -t; khi cần, lấy effective config bằngnginx -T. - Xác nhận listen/server/location đang xử lý request.
- Nếu static: kiểm tra path, permission, cache/fallback. Nếu proxy: kiểm tra DNS → TCP connect → TLS/protocol → upstream response.
- Correlate access log, error log, upstream timings và container/network metrics trong cùng time window.
- Nếu thay đổi production gần đây trùng với onset, ưu tiên rollback/canary isolation trước khi tiếp tục thử nghiệm trực tiếp trên traffic thật.
Version scope: nội dung bám semantics trong tài liệu nginx.org hiện hành tại thời điểm biên soạn; distribution/container image có thể pin Nginx khác nhau, vì vậy kiểm tra nginx -v/nginx -V trước khi dùng directive/version-specific behavior.