Chuyển đến nội dung chính

第14課:NGINXのマイクロサービスとサービスメッシュ

Nginxによるマイクロサービスとサービスメッシュのレッスン——サービスディスカバリ、APIゲートウェイパターン、サービスごとのレート制限、サーキットブレーカー、リトライポリシー、分散トレーシング、サービスメッシュ統合(Consul、Istio)、サイドカーパターン、カナリアデプロイ、ブルーグリーンデプロイ。

🔒 DevSecOps — 第14課 Bài 14: マイクロサービスとサービスメッシュ trong NGINX

NGINXの基礎から応用まで

第5部:本番環境&高度なトピック

xdev.asia

1. Microservices Architecture Overview

1.1. Monolith vs Microservices

Monolithic Architecture:

┌────────────────────────────────┐
│      Single Application        │
│  ┌──────────────────────────┐  │
│  │   Web Layer              │  │
│  ├──────────────────────────┤  │
│  │   Business Logic         │  │
│  ├──────────────────────────┤  │
│  │   Data Access            │  │
│  └──────────────────────────┘  │
│              │                 │
│              ▼                 │
│      ┌──────────────┐          │
│      │   Database   │          │
│      └──────────────┘          │
└────────────────────────────────┘

Microservices Architecture:

                    ┌──────────────┐
                    │ API Gateway  │
                    │   (Nginx)    │
                    └──────┬───────┘
                           │
        ┌──────────────────┼──────────────────┐
        │                  │                  │
    ┌───▼───┐         ┌───▼───┐         ┌───▼───┐
    │ User  │         │Order  │         │Product│
    │Service│         │Service│         │Service│
    └───┬───┘         └───┬───┘         └───┬───┘
        │                 │                  │
    ┌───▼───┐         ┌───▼───┐         ┌───▼───┐
    │User DB│         │Order  │         │Product│
    │       │         │  DB   │         │  DB   │
    └───────┘         └───────┘         └───────┘

1.2. API Gateway Pattern

# API Gateway configuration
http {
    # Service discovery (will be populated dynamically)
    upstream user_service {
        server user-service-1:8081;
        server user-service-2:8081;
        server user-service-3:8081;
        keepalive 32;
    }
upstream order_service {
    server order-service-1:8082;
    server order-service-2:8082;
    keepalive 32;
}

upstream product_service {
    server product-service-1:8083;
    server product-service-2:8083;
    keepalive 32;
}

upstream payment_service {
    server payment-service-1:8084;
    keepalive 16;
}

# Rate limiting per service
limit_req_zone $binary_remote_addr zone=user_limit:10m rate=100r/s;
limit_req_zone $binary_remote_addr zone=order_limit:10m rate=50r/s;
limit_req_zone $binary_remote_addr zone=product_limit:10m rate=200r/s;
limit_req_zone $binary_remote_addr zone=payment_limit:10m rate=10r/s;

server {
    listen 80;
    server_name api.example.com;
    
    # Global headers
    add_header X-Gateway "nginx-api-gateway" always;
    
    # User service
    location /api/users {
        limit_req zone=user_limit burst=20 nodelay;
        
        proxy_pass http://user_service;
        include includes/proxy_params.conf;
        include includes/timeout_params.conf;
    }
    
    # Order service
    location /api/orders {
        limit_req zone=order_limit burst=10 nodelay;
        
        proxy_pass http://order_service;
        include includes/proxy_params.conf;
        include includes/timeout_params.conf;
    }
    
    # Product service
    location /api/products {
        limit_req zone=product_limit burst=50 nodelay;
        
        proxy_pass http://product_service;
        include includes/proxy_params.conf;
        include includes/timeout_params.conf;
    }
    
    # Payment service (stricter limits)
    location /api/payments {
        limit_req zone=payment_limit burst=5;
        limit_conn addr 5;
        
        proxy_pass http://payment_service;
        include includes/proxy_params.conf;
        
        # Longer timeout for payments
        proxy_read_timeout 60s;
    }
    
    # Health check aggregation
    location /health {
        access_log off;
        
        content_by_lua_block {
            local http = require "resty.http"
            local cjson = require "cjson"
            
            local services = {
                {name = "user", url = "http://user-service-1:8081/health"},
                {name = "order", url = "http://order-service-1:8082/health"},
                {name = "product", url = "http://product-service-1:8083/health"},
                {name = "payment", url = "http://payment-service-1:8084/health"}
            }
            
            local results = {}
            local all_healthy = true
            
            for _, service in ipairs(services) do
                local httpc = http.new()
                local res, err = httpc:request_uri(service.url, {
                    method = "GET",
                    keepalive_timeout = 60000,
                    keepalive_pool = 10
                })
                
                if res and res.status == 200 then
                    results[service.name] = "healthy"
                else
                    results[service.name] = "unhealthy"
                    all_healthy = false
                end
            end
            
            ngx.status = all_healthy and 200 or 503
            ngx.say(cjson.encode(results))
        }
    }
}

}

Include files:

# /etc/nginx/includes/proxy_params.conf
proxy_http_version 1.1;
proxy_set_header Connection "";

proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Request-ID $request_id;

Add tracing headers

proxy_set_header X-B3-TraceId $request_id; proxy_set_header X-B3-SpanId $request_id;

# /etc/nginx/includes/timeout_params.conf
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 30s;

proxy_next_upstream error timeout http_502 http_503 http_504; proxy_next_upstream_tries 2; proxy_next_upstream_timeout 10s;

1.3. Service Mesh Benefits

Without Service Mesh:

Service A → Service B (manual retries, timeouts in code)
Service A → Service C (manual circuit breaker)
Service B → Service D (manual tracing)

With Service Mesh:

Service A → Sidecar → Sidecar → Service B
↓          ↓         ↓          ↓
Logic    Retries   Tracing   Circuit Breaker

2. Service Discovery

2.1. Static Configuration

# Simple static upstream
upstream user_service {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}

Problems:

- Manual updates required

- No dynamic scaling

- Hard to manage in cloud environments

2.2. DNS-based Service Discovery

# DNS-based discovery
upstream user_service {
# Resolve DNS on each request
server user-service.internal:8080 resolve;

# Or with resolver directive
# resolver 10.0.0.2 valid=10s;

}

http { resolver 10.0.0.2 valid=10s; resolver_timeout 5s;

upstream dynamic_backend {
    zone backend 64k;
    server backend.internal:8080 resolve;
}

}

2.3. Consul Service Discovery

Install Consul:

# Download Consul
wget https://releases.hashicorp.com/consul/1.17.0/consul_1.17.0_linux_amd64.zip
unzip consul_1.17.0_linux_amd64.zip
sudo mv consul /usr/local/bin/

Start Consul agent

consul agent -dev

Register services with Consul:

// user-service.json
{
"service": {
"name": "user-service",
"tags": ["v1", "production"],
"port": 8080,
"check": {
"http": "http://localhost:8080/health",
"interval": "10s",
"timeout": "1s"
}
}
}
# Register service
consul services register user-service.json

Nginx with Consul Template:

# Install Consul Template
wget https://releases.hashicorp.com/consul-template/0.34.0/consul-template_0.34.0_linux_amd64.zip
unzip consul-template_0.34.0_linux_amd64.zip
sudo mv consul-template /usr/local/bin/

Template file:

# /etc/nginx/templates/upstream.conf.ctmpl
{{range services}}
upstream {{.Name}} {
zone {{.Name}} 64k;
{{range service .Name}}
server {{.Address}}:{{.Port}} max_fails=3 fail_timeout=30s;
{{end}}
keepalive 32;
}
{{end}}

server { listen 80;

{{range services}}
location /api/{{.Name}} {
    proxy_pass http://{{.Name}};
    include /etc/nginx/includes/proxy_params.conf;
}
{{end}}

}

Run Consul Template:

consul-template 
-template "/etc/nginx/templates/upstream.conf.ctmpl:/etc/nginx/conf.d/upstream.conf:nginx -s reload"
-consul-addr "localhost:8500"

Systemd service:

# /etc/systemd/system/consul-template.service
[Unit]
Description=Consul Template
Requires=network-online.target
After=network-online.target

[Service] Type=simple User=nginx Group=nginx ExecStart=/usr/local/bin/consul-template
-template "/etc/nginx/templates/upstream.conf.ctmpl:/etc/nginx/conf.d/upstream.conf:nginx -s reload"
-consul-addr "localhost:8500" ExecReload=/bin/kill -HUP $MAINPID KillMode=process Restart=on-failure RestartSec=10s

[Install] WantedBy=multi-user.target

2.4. Kubernetes Service Discovery

# kubernetes-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-gateway
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:

  • host: api.example.com http: paths:
    • path: /api/users pathType: Prefix backend: service: name: user-service port: number: 8080
    • path: /api/orders pathType: Prefix backend: service: name: order-service port: number: 8080
    • path: /api/products pathType: Prefix backend: service: name: product-service port: number: 8080

3. Circuit Breaker Pattern

3.1. Basic Circuit Breaker

# Circuit breaker with passive health checks
upstream backend {
server backend1.example.com:8080 max_fails=3 fail_timeout=30s;
server backend2.example.com:8080 max_fails=3 fail_timeout=30s;
server backend3.example.com:8080 max_fails=3 fail_timeout=30s;
}



server { location /api/ { proxy_pass http://backend;

    # Circuit breaker behavior
    proxy_next_upstream error timeout http_502 http_503 http_504;
    proxy_next_upstream_tries 2;
    proxy_next_upstream_timeout 5s;
    
    # If all backends fail, return cached response
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    
    # Timeouts
    proxy_connect_timeout 2s;
    proxy_read_timeout 5s;
}

}

3.2. Advanced Circuit Breaker with Lua

http {
lua_shared_dict circuit_breaker 10m;

init_by_lua_block {
    -- Circuit breaker configuration
    circuit_breaker = {
        threshold = 5,           -- Open after 5 failures
        timeout = 30,            -- Try again after 30s
        half_open_requests = 1   -- Allow 1 request when half-open
    }
}

server {
    location /api/payment {
        access_by_lua_block {
            local cb = ngx.shared.circuit_breaker
            local key = "payment_service"
            
            -- Get circuit state
            local failures = cb:get(key .. ":failures") or 0
            local state = cb:get(key .. ":state") or "closed"
            local last_failure = cb:get(key .. ":last_failure") or 0
            
            -- Check if circuit is open
            if state == "open" then
                local now = ngx.now()
                if now - last_failure < circuit_breaker.timeout then
                    ngx.status = 503
                    ngx.say("Circuit breaker is open")
                    return ngx.exit(503)
                else
                    -- Move to half-open
                    cb:set(key .. ":state", "half-open")
                    state = "half-open"
                end
            end
            
            -- Half-open: allow limited requests
            if state == "half-open" then
                local half_open_count = cb:incr(key .. ":half_open_count", 1, 0)
                if half_open_count > circuit_breaker.half_open_requests then
                    ngx.status = 503
                    ngx.say("Circuit breaker is half-open, try again")
                    return ngx.exit(503)
                end
            end
        }
        
        proxy_pass http://payment_service;
        
        log_by_lua_block {
            local cb = ngx.shared.circuit_breaker
            local key = "payment_service"
            local status = ngx.status
            
            if status >= 500 then
                -- Increment failure count
                local failures = cb:incr(key .. ":failures", 1, 0)
                cb:set(key .. ":last_failure", ngx.now())
                
                -- Open circuit if threshold reached
                if failures >= circuit_breaker.threshold then
                    cb:set(key .. ":state", "open")
                    ngx.log(ngx.ERR, "Circuit breaker opened for " .. key)
                end
            else
                -- Success - reset or close circuit
                local state = cb:get(key .. ":state")
                if state == "half-open" then
                    cb:set(key .. ":state", "closed")
                    cb:set(key .. ":failures", 0)
                    cb:set(key .. ":half_open_count", 0)
                    ngx.log(ngx.INFO, "Circuit breaker closed for " .. key)
                elseif state == "closed" then
                    cb:set(key .. ":failures", 0)
                end
            end
        }
    }
}

}

3.3. Circuit Breaker Monitoring

server {
listen 8080;

location /circuit-breaker/status {
    access_log off;
    
    content_by_lua_block {
        local cb = ngx.shared.circuit_breaker
        local cjson = require "cjson"
        
        local services = {"payment_service", "order_service", "user_service"}
        local status = {}
        
        for _, service in ipairs(services) do
            status[service] = {
                state = cb:get(service .. ":state") or "closed",
                failures = cb:get(service .. ":failures") or 0,
                last_failure = cb:get(service .. ":last_failure") or 0
            }
        end
        
        ngx.say(cjson.encode(status))
    }
}

}


4. Retry Policies

4.1. Basic Retry Configuration

upstream backend {
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}

server { location /api/ { proxy_pass http://backend;

    # Retry on these conditions
    proxy_next_upstream error timeout http_502 http_503 http_504;
    
    # Maximum retry attempts
    proxy_next_upstream_tries 3;
    
    # Total timeout for all retries
    proxy_next_upstream_timeout 10s;
    
    # Connection timeout
    proxy_connect_timeout 2s;
    proxy_send_timeout 5s;
    proxy_read_timeout 5s;
}

}

4.2. Exponential Backoff

http {
lua_shared_dict retry_state 10m;

server {
    location /api/with-backoff {
        rewrite_by_lua_block {
            local retry_state = ngx.shared.retry_state
            local key = ngx.var.request_id
            
            local attempt = retry_state:incr(key, 1, 0)
            
            if attempt > 1 then
                -- Exponential backoff: 2^(attempt-1) * 100ms
                local delay = math.pow(2, attempt - 1) * 0.1
                ngx.sleep(delay)
            end
            
            -- Max 3 attempts
            if attempt > 3 then
                ngx.status = 503
                ngx.say("Max retries exceeded")
                return ngx.exit(503)
            end
        }
        
        proxy_pass http://backend;
        
        log_by_lua_block {
            local retry_state = ngx.shared.retry_state
            local key = ngx.var.request_id
            
            -- Clean up on success
            if ngx.status < 500 then
                retry_state:delete(key)
            end
        }
    }
}

}

4.3. Retry Budget

http {
lua_shared_dict retry_budget 10m;

init_by_lua_block {
    -- Allow 10% of requests to be retries
    retry_budget_limit = 0.1
}

server {
    location /api/ {
        access_by_lua_block {
            local budget = ngx.shared.retry_budget
            
            -- Get counters
            local total = budget:get("total_requests") or 0
            local retries = budget:get("total_retries") or 0
            
            -- Check if this is a retry
            local is_retry = ngx.var.arg_retry == "1"
            
            if is_retry then
                local retry_ratio = retries / (total + 1)
                if retry_ratio >= retry_budget_limit then
                    ngx.status = 503
                    ngx.say("Retry budget exceeded")
                    return ngx.exit(503)
                end
                budget:incr("total_retries", 1, 0)
            end
            
            budget:incr("total_requests", 1, 0)
        }
        
        proxy_pass http://backend;
    }
}

}


5. Rate Limiting per Service

5.1. Service-specific Rate Limits

http {
# Different limits for different services
limit_req_zone $binary_remote_addr zone=user_svc:10m rate=100r/s;
limit_req_zone $binary_remote_addr zone=order_svc:10m rate=50r/s;
limit_req_zone $binary_remote_addr zone=product_svc:10m rate=200r/s;
limit_req_zone $binary_remote_addr zone=payment_svc:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=admin_svc:10m rate=5r/s;

server {
    listen 80;
    
    # User service - high limit
    location /api/users {
        limit_req zone=user_svc burst=200 nodelay;
        proxy_pass http://user_service;
    }
    
    # Order service - medium limit
    location /api/orders {
        limit_req zone=order_svc burst=100 nodelay;
        proxy_pass http://order_service;
    }
    
    # Product service - highest limit (read-heavy)
    location /api/products {
        limit_req zone=product_svc burst=400 nodelay;
        proxy_pass http://product_service;
    }
    
    # Payment service - strict limit (critical)
    location /api/payments {
        limit_req zone=payment_svc burst=5;
        limit_conn addr 3;
        proxy_pass http://payment_service;
    }
    
    # Admin service - very strict
    location /api/admin {
        limit_req zone=admin_svc burst=2;
        limit_conn addr 1;
        
        # Also require auth
        auth_basic "Admin Area";
        auth_basic_user_file /etc/nginx/.htpasswd;
        
        proxy_pass http://admin_service;
    }
}

}

5.2. Per-user Rate Limiting

http {
# Extract user ID from JWT or cookie
map $http_authorization $user_id {
default "anonymous";
~*Bearer\s+([A-Za-z0-9-_.]+) $1;
}

# Rate limit by user ID
limit_req_zone $user_id zone=per_user:10m rate=50r/s;

# Different limits for different user tiers
map $user_id $user_tier {
    default "free";
    ~*premium_ "premium";
    ~*enterprise_ "enterprise";
}

map $user_tier $rate_limit_zone {
    free per_user_free;
    premium per_user_premium;
    enterprise per_user_enterprise;
}

limit_req_zone $user_id zone=per_user_free:10m rate=10r/s;
limit_req_zone $user_id zone=per_user_premium:10m rate=50r/s;
limit_req_zone $user_id zone=per_user_enterprise:10m rate=200r/s;

server {
    location /api/ {
        # Apply rate limit based on user tier
        limit_req zone=$rate_limit_zone burst=20 nodelay;
        
        proxy_pass http://backend;
        
        # Add rate limit info to response
        add_header X-RateLimit-Tier $user_tier;
    }
}

}

5.3. Token Bucket Algorithm

http {
lua_shared_dict token_bucket 10m;

server {
    location /api/ {
        access_by_lua_block {
            local tb = ngx.shared.token_bucket
            local user_id = ngx.var.http_x_user_id or ngx.var.remote_addr
            
            -- Token bucket configuration
            local capacity = 100      -- Max tokens
            local refill_rate = 10    -- Tokens per second
            
            -- Get current state
            local key = "tb:" .. user_id
            local tokens = tb:get(key .. ":tokens") or capacity
            local last_refill = tb:get(key .. ":last_refill") or ngx.now()
            
            -- Refill tokens
            local now = ngx.now()
            local time_passed = now - last_refill
            local new_tokens = math.min(capacity, tokens + (time_passed * refill_rate))
            
            -- Check if request allowed
            if new_tokens >= 1 then
                -- Allow request
                tb:set(key .. ":tokens", new_tokens - 1)
                tb:set(key .. ":last_refill", now)
                
                -- Add headers
                ngx.header["X-RateLimit-Remaining"] = math.floor(new_tokens - 1)
                ngx.header["X-RateLimit-Limit"] = capacity
            else
                -- Rate limited
                ngx.status = 429
                ngx.header["Retry-After"] = math.ceil((1 - new_tokens) / refill_rate)
                ngx.say("Rate limit exceeded")
                return ngx.exit(429)
            end
        }
        
        proxy_pass http://backend;
    }
}

}


6. Distributed Tracing

6.1. OpenTelemetry Integration

http {
# Generate unique request ID
map $request_id $trace_id {
default $request_id;
}

log_format trace '$remote_addr - [$time_local] "$request" '
                '$status $body_bytes_sent '
                'trace_id=$trace_id '
                'span_id=$request_id '
                'parent_span=$http_x_parent_span '
                'service=api-gateway '
                'duration=$request_time';

access_log /var/log/nginx/trace.log trace;

server {
    listen 80;
    
    location /api/ {
        # Add tracing headers
        proxy_set_header X-Trace-ID $trace_id;
        proxy_set_header X-Span-ID $request_id;
        proxy_set_header X-Parent-Span $request_id;
        
        # Service name
        proxy_set_header X-Service-Name "api-gateway";
        
        proxy_pass http://backend;
    }
}

}

6.2. Jaeger Integration

http {
lua_shared_dict tracing 10m;

init_by_lua_block {
    local jaeger = require "jaeger"
    tracer = jaeger.new_tracer("nginx-gateway", {
        sampler = {
            type = "const",
            param = 1  -- Sample all requests
        },
        reporter = {
            endpoint = "http://jaeger:14268/api/traces"
        }
    })
}

server {
    location /api/ {
        access_by_lua_block {
            local span = tracer:start_span("nginx-gateway")
            span:set_tag("http.method", ngx.var.request_method)
            span:set_tag("http.url", ngx.var.request_uri)
            span:set_tag("http.host", ngx.var.host)
            
            -- Store span for later
            ngx.ctx.span = span
        }
        
        proxy_pass http://backend;
        
        # Forward trace context
        proxy_set_header uber-trace-id $http_uber_trace_id;
        
        log_by_lua_block {
            local span = ngx.ctx.span
            if span then
                span:set_tag("http.status_code", ngx.status)
                span:finish()
            end
        }
    }
}

}

6.3. Zipkin Integration

http {
# Zipkin headers
map $http_x_b3_traceid $trace_id {
default $request_id;
~.+ $http_x_b3_traceid;
}

map $http_x_b3_spanid $span_id {
    default $request_id;
    ~.+ $http_x_b3_spanid;
}

server {
    location /api/ {
        # B3 propagation headers
        proxy_set_header X-B3-TraceId $trace_id;
        proxy_set_header X-B3-SpanId $span_id;
        proxy_set_header X-B3-ParentSpanId $span_id;
        proxy_set_header X-B3-Sampled "1";
        
        proxy_pass http://backend;
        
        # Log tracing info
        access_log /var/log/nginx/trace.log;
    }
}

}


7. Service Mesh Integration

7.1. Istio Sidecar Pattern

Kubernetes deployment with Istio:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
version: v1
annotations:
sidecar.istio.io/inject: "true"
spec:
containers:
- name: user-service
image: user-service:v1
ports:
- containerPort: 8080

Istio VirtualService:

# virtualservice.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: user-service
spec:
hosts:

  • user-service http:
  • match:
    • headers: x-user-tier: exact: premium route:
    • destination: host: user-service subset: v2 weight: 100
  • route:
    • destination: host: user-service subset: v1 weight: 90
    • destination: host: user-service subset: v2 weight: 10

Istio DestinationRule:

# destinationrule.yaml
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: user-service
spec:
host: user-service
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
http1MaxPendingRequests: 50
http2MaxRequests: 100
maxRequestsPerConnection: 2
loadBalancer:
simple: LEAST_REQUEST
outlierDetection:
consecutiveErrors: 5
interval: 30s
baseEjectionTime: 30s
maxEjectionPercent: 50
subsets:


  • name: v1 labels: version: v1
  • name: v2 labels: version: v2
  • 7.2. Consul Connect

    Service definition with Connect:

    {
    "service": {
    "name": "user-service",
    "port": 8080,
    "connect": {
    "sidecar_service": {
    "port": 20000,
    "proxy": {
    "upstreams": [
    {
    "destination_name": "order-service",
    "local_bind_port": 8081
    },
    {
    "destination_name": "product-service",
    "local_bind_port": 8082
    }
    ],
    "config": {
    "protocol": "http",
    "local_request_timeout_ms": 5000
    }
    }
    }
    }
    }
    }
    

    Nginx with Consul Connect:

    upstream order_service {
    

    Connect to Consul Connect sidecar

    server 127.0.0.1:8081; keepalive 32; }

    upstream product_service { server 127.0.0.1:8082; keepalive 32; }

    server { listen 8080;

    location /api/orders {
        proxy_pass http://order_service;
    }
    
    location /api/products {
        proxy_pass http://product_service;
    }
    

    }

    7.3. Linkerd Integration

    Linkerd proxy injection:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: nginx-gateway
    annotations:
    linkerd.io/inject: enabled
    spec:
    replicas: 2
    selector:
    matchLabels:
    app: nginx-gateway
    template:
    metadata:
    labels:
    app: nginx-gateway
    spec:
    containers:
    - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80
    

    Service profile for retry budget:

    apiVersion: linkerd.io/v1alpha2
    kind: ServiceProfile
    metadata:
    name: user-service
    spec:
    routes:

    • name: GET /api/users condition: method: GET pathRegex: /api/users/.* responseClasses:
      • condition: status: min: 500 max: 599 isFailure: true retries: limit: 3 budget: minRetriesPerSecond: 10 ttl: 10s retryRatio: 0.2

    8. Canary Deployments

    8.1. Basic Canary with Weight

    upstream user_service_v1 {
    server user-v1-1:8080;
    server user-v1-2:8080;
    }
    
    
    
    

    upstream user_service_v2 { server user-v2-1:8080; server user-v2-2:8080; }

    Split clients based on IP

    split_clients "${remote_addr}" $version { 90% "v1"; 10% "v2"; }

    server { location /api/users { if ($version = "v1") { proxy_pass http://user_service_v1; }

        if ($version = "v2") {
            proxy_pass http://user_service_v2;
        }
        
        add_header X-Version $version;
    }
    

    }

    8.2. Header-based Canary

    # Route by header
    map $http_x_canary $backend {
    default user_service_v1;
    "true" user_service_v2;
    }

    upstream user_service_v1 { server user-v1:8080; }

    upstream user_service_v2 { server user-v2:8080; }

    server { location /api/users { proxy_pass http://$backend; add_header X-Backend $backend; } }

    Usage:

    Normal: curl http://api.example.com/api/users

    Canary: curl -H "X-Canary: true" http://api.example.com/api/users

    8.3. Progressive Canary Rollout

    # Gradually increase canary traffic
    map $time_iso8601 $canary_percentage {
    ~2024-01-01 10;
    ~2024-01-02 25;
    ~2024-01-03 50;
    ~2024-01-04 75;
    default 100;
    }

    split_clients "${remote_addr}${http_user_agent}" $version { ${canary_percentage}% "v2"; * "v1"; }

    server { location /api/users { if ($version = "v1") { proxy_pass http://user_service_v1; }

        if ($version = "v2") {
            proxy_pass http://user_service_v2;
        }
    }
    

    }

    8.4. Feature Flag-based Canary

    http {
    lua_shared_dict feature_flags 10m;

    init_by_lua_block {
        local ff = ngx.shared.feature_flags
        ff:set("new_ui_enabled", "false")
        ff:set("new_ui_percentage", 10)
    }
    
    server {
        location /api/users {
            access_by_lua_block {
                local ff = ngx.shared.feature_flags
                local enabled = ff:get("new_ui_enabled")
                local percentage = ff:get("new_ui_percentage") or 0
                
                if enabled == "true" then
                    -- Random selection based on percentage
                    local random = math.random(100)
                    if random <= percentage then
                        ngx.var.backend = "user_service_v2"
                    else
                        ngx.var.backend = "user_service_v1"
                    end
                else
                    ngx.var.backend = "user_service_v1"
                end
            }
            
            proxy_pass http://$backend;
        }
        
        # Feature flag management endpoint
        location /admin/feature-flags {
            content_by_lua_block {
                local ff = ngx.shared.feature_flags
                local cjson = require "cjson"
                
                if ngx.req.get_method() == "GET" then
                    local flags = {
                        new_ui_enabled = ff:get("new_ui_enabled"),
                        new_ui_percentage = ff:get("new_ui_percentage")
                    }
                    ngx.say(cjson.encode(flags))
                elseif ngx.req.get_method() == "POST" then
                    ngx.req.read_body()
                    local data = cjson.decode(ngx.req.get_body_data())
                    
                    if data.new_ui_enabled then
                        ff:set("new_ui_enabled", data.new_ui_enabled)
                    end
                    if data.new_ui_percentage then
                        ff:set("new_ui_percentage", data.new_ui_percentage)
                    end
                    
                    ngx.say("Feature flags updated")
                end
            }
        }
    }
    

    }


    9. Blue-Green Deployments

    9.1. Basic Blue-Green Setup

    # Blue environment (current production)
    upstream blue_env {
    server blue-1:8080;
    server blue-2:8080;
    server blue-3:8080;
    }

    Green environment (new version)

    upstream green_env { server green-1:8080; server green-2:8080; server green-3:8080; }

    Active environment

    map $http_host $active_env { default blue_env; }

    server { listen 80;

    location / {
        proxy_pass http://$active_env;
        add_header X-Environment $active_env;
    }
    

    }

    Switch script:

    #!/bin/bash

    switch_environment.sh

    CURRENT=$(curl -s http://localhost/env)

    if [ "$CURRENT" == "blue" ]; then NEW="green" else NEW="blue" fi

    Update configuration

    sed -i "s/default blue_env/default ${NEW}_env/" /etc/nginx/nginx.conf

    Test configuration

    nginx -t

    if [ $? -eq 0 ]; then # Reload Nginx nginx -s reload echo "Switched to $NEW environment" else echo "Configuration test failed, rollback" sed -i "s/default ${NEW}_env/default ${CURRENT}_env/" /etc/nginx/nginx.conf fi

    9.2. Blue-Green with Traffic Shaping

    # Gradual cutover
    split_clients "${remote_addr}" $environment {
    75% "blue_env";    # Old version gets 75%
    25% "green_env";   # New version gets 25%
    }

    server { location / { proxy_pass http://$environment; add_header X-Environment $environment; } }

    After validation, switch to 100% green

    Then update:

    split_clients "${remote_addr}" $environment {

    0% "blue_env";

    100% "green_env";

    }

    9.3. Blue-Green with Smoke Tests

    server {
    # Production traffic
    location / {
    proxy_pass http://$active_env;
    }

    # Smoke test endpoint for green
    location /test/green {
        proxy_pass http://green_env;
        
        # Only allow from internal network
        allow 10.0.0.0/8;
        deny all;
        
        add_header X-Test-Environment "green";
    }
    

    }

    Automated smoke test:

    #!/bin/bash

    smoke_test.sh

    GREEN_URL="http://localhost/test/green" TESTS=( "/health" "/api/users" "/api/products" )

    echo "Running smoke tests on green environment..."

    FAILED=0 for test in "${TESTS[@]}"; do URL="${GREEN_URL}${test}" STATUS=$(curl -s -o /dev/null -w "%{http_code}" $URL)

    if [ $STATUS -eq 200 ]; then
        echo "✓ PASS: $test"
    else
        echo "✗ FAIL: $test (Status: $STATUS)"
        FAILED=1
    fi
    

    done

    if [ $FAILED -eq 0 ]; then echo "All smoke tests passed. Ready to switch." exit 0 else echo "Smoke tests failed. Do not switch." exit 1 fi


    10. Complete Microservices Example

    10.1. Full API Gateway Configuration

    # /etc/nginx/nginx.conf

    user nginx; worker_processes auto; worker_rlimit_nofile 65535;

    error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid;

    events { worker_connections 4096; use epoll; multi_accept on; }

    http { include /etc/nginx/mime.types; default_type application/octet-stream;

    # Logging
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    
    log_format trace '$remote_addr - [$time_local] "$request" '
                    '$status $body_bytes_sent '
                    'trace_id=$request_id '
                    'upstream=$upstream_addr '
                    'upstream_time=$upstream_response_time '
                    'request_time=$request_time';
    
    access_log /var/log/nginx/access.log main;
    access_log /var/log/nginx/trace.log trace;
    
    # Performance settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off;
    
    # Gzip
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml text/javascript 
               application/json application/javascript application/xml+rss;
    
    # Rate limiting zones
    limit_req_zone $binary_remote_addr zone=global:10m rate=1000r/s;
    limit_req_zone $binary_remote_addr zone=user_svc:10m rate=100r/s;
    limit_req_zone $binary_remote_addr zone=order_svc:10m rate=50r/s;
    limit_req_zone $binary_remote_addr zone=payment_svc:10m rate=10r/s;
    
    # Connection limiting
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    
    # Upstreams
    include /etc/nginx/conf.d/upstreams/*.conf;
    
    # Servers
    include /etc/nginx/conf.d/servers/*.conf;
    

    }

    Upstreams configuration:

    # /etc/nginx/conf.d/upstreams/services.conf

    upstream user_service { least_conn; server user-1.internal:8080 max_fails=3 fail_timeout=30s; server user-2.internal:8080 max_fails=3 fail_timeout=30s; server user-3.internal:8080 max_fails=3 fail_timeout=30s; keepalive 64; }

    upstream order_service { least_conn; server order-1.internal:8080 max_fails=3 fail_timeout=30s; server order-2.internal:8080 max_fails=3 fail_timeout=30s; keepalive 32; }

    upstream product_service { least_conn; server product-1.internal:8080 max_fails=3 fail_timeout=30s; server product-2.internal:8080 max_fails=3 fail_timeout=30s; server product-3.internal:8080 max_fails=3 fail_timeout=30s; keepalive 64; }

    upstream payment_service { server payment-1.internal:8080 max_fails=2 fail_timeout=10s; server payment-2.internal:8080 max_fails=2 fail_timeout=10s backup; keepalive 16; }

    Server configuration:

    # /etc/nginx/conf.d/servers/api-gateway.conf

    server { listen 80; listen [::]:80; server_name api.example.com; return 301 https://$server_name$request_uri; }

    server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name api.example.com;

    # SSL
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    
    # Security headers
    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    
    # Global rate limit
    limit_req zone=global burst=2000 nodelay;
    limit_conn addr 50;
    
    # Health check
    location /health {
        access_log off;
        return 200 "OK\n";
        add_header Content-Type text/plain;
    }
    
    # User service
    location /api/v1/users {
        limit_req zone=user_svc burst=200 nodelay;
        
        proxy_pass http://user_service/users;
        include /etc/nginx/includes/proxy.conf;
        include /etc/nginx/includes/tracing.conf;
    }
    
    # Order service
    location /api/v1/orders {
        limit_req zone=order_svc burst=100 nodelay;
        
        proxy_pass http://order_service/orders;
        include /etc/nginx/includes/proxy.conf;
        include /etc/nginx/includes/tracing.conf;
    }
    
    # Product service
    location /api/v1/products {
        proxy_pass http://product_service/products;
        include /etc/nginx/includes/proxy.conf;
        include /etc/nginx/includes/tracing.conf;
        
        # Cache product listings
        proxy_cache product_cache;
        proxy_cache_valid 200 5m;
        proxy_cache_use_stale error timeout updating;
        add_header X-Cache-Status $upstream_cache_status;
    }
    
    # Payment service (critical)
    location /api/v1/payments {
        limit_req zone=payment_svc burst=5;
        limit_conn addr 3;
        
        proxy_pass http://payment_service/payments;
        include /etc/nginx/includes/proxy.conf;
        include /etc/nginx/includes/tracing.conf;
        
        # Longer timeout for payments
        proxy_read_timeout 60s;
        
        # No caching for payments
        proxy_no_cache 1;
        proxy_cache_bypass 1;
    }
    

    }


    まとめ

    このレッスンで学んだこと:

    • ✅ Microservices architecture patterns
    • ✅ API Gateway implementation
    • ✅ Service discovery (static, DNS, Consul)
    • ✅ Circuit breaker patterns
    • ✅ Retry policies với exponential backoff
    • ✅ Per-service rate limiting
    • ✅ Distributed tracing (OpenTelemetry, Jaeger, Zipkin)
    • ✅ Service mesh integration (Istio, Consul Connect, Linkerd)
    • ✅ Canary deployments
    • ✅ Blue-green deployments

    重要なポイント:

    • API Gateway là central point for routing
    • Service discovery enables dynamic scaling
    • Circuit breakers prevent cascading failures
    • Distributed tracing essential for debugging
    • Rate limiting protects services
    • Service mesh adds observability và resilience
    • Canary và blue-green enable safe deployments

    Microservices Checklist:

    • ✅ API Gateway configured
    • ✅ Service discovery implemented
    • ✅ Health checks on all services
    • ✅ Circuit breakers configured
    • ✅ Retry policies defined
    • ✅ Rate limiting per service
    • ✅ Distributed tracing enabled
    • ✅ Monitoring và alerting
    • ✅ Deployment strategies tested
    • ✅ Documentation complete

    これら14の包括的なレッスンで、基礎から応用まで、マイクロサービスアーキテクチャを含む本番環境でNginxをデプロイおよび管理するための十分な知識が身につきました!🎉