Nginx

Upstream Configuration ใน Nginx — จัดการ Backend Servers อย่างมืออาชีพ

Nginx upstream configuration เป็นหนึ่งในฟีเจอร์ที่มีประสิทธิภาพสูงสุดของ Nginx สำหรับการจัดการและกระจายการร้องขอ (load balancing) ไปยังเซิร์ฟเวอร์ backend หลายเครื่อง ในบทความนี้ เราจะสำรวจทุกแง่มุมของ upstream configuration ตั้งแต่พื้นฐานไปจนถึงการกำหนดค่าแบบเพิ่มเติมสำหรับการจัดการ backend servers อย่างมืออาชีพ

เมื่อคุณมี backend application servers หลายเครื่องและต้องการให้ Nginx กระจายการไหลของ traffic อย่างสมดุล upstream configuration คือเครื่องมือหลักของคุณ ไม่ว่าจะเป็น Node.js, Python Flask, Django, Java Tomcat, หรือ PHP-FPM servers Nginx upstream สามารถจัดการได้อย่างมีประสิทธิภาพ

Upstream Block พื้นฐาน – อธิบายโครงสร้าง

upstream block ใน Nginx กำหนดกลุ่มของ backend servers ที่ Nginx จะส่งการร้องขอไปยัง โครงสร้างพื้นฐานของ upstream block มีดังนี้:

upstream backend_pool {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend_pool;
    }
}

ตัวอย่างข้างต้นแสดงการกำหนด upstream ที่มีเซิร์ฟเวอร์ backend 3 เครื่องและใช้วิธี round-robin (ค่าเริ่มต้น) ในการกระจาย traffic เมื่อมีคำขอเข้ามา Nginx จะส่งไปยังแต่ละเซิร์ฟเวอร์ตามลำดับ

Server Directive Parameters – ตัวเลือกการกำหนดค่าเซิร์ฟเวอร์

Directive “server” ใน upstream block มีพารามิเตอร์ต่างๆ ที่ให้คุณปรับแต่งพฤติกรรมของแต่ละ backend server ได้อย่างละเอียด

Weight – น้ำหนักการกระจาย Traffic

พารามิเตอร์ weight ใช้เพื่อกำหนดสัดส่วนการกระจาย traffic ให้กับแต่ละเซิร์ฟเวอร์ ค่าเริ่มต้นคือ 1 ถ้าคุณมีเซิร์ฟเวอร์ที่มีประสิทธิภาพสูงกว่า สามารถตั้ง weight สูงกว่าได้:

upstream backend_pool {
    server 192.168.1.10:8080 weight=3;
    server 192.168.1.11:8080 weight=2;
    server 192.168.1.12:8080 weight=1;
}

ในตัวอย่างนี้ เซิร์ฟเวอร์แรกจะได้รับคำขอ 3 ใน 6 ส่วน เซิร์ฟเวอร์ที่สองได้รับ 2 ส่วน และเซิร์ฟเวอร์ที่สามได้รับ 1 ส่วน

Max_fails และ Fail_timeout – การจัดการข้อผิดพลาด

เมื่อเซิร์ฟเวอร์ backend ตอบสนองด้วยข้อผิดพลาด Nginx สามารถทำเครื่องหมายว่าเซิร์ฟเวอร์ “down” และหยุดส่งคำขอไปยังมันชั่วคราว

upstream backend_pool {
    server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:8080 max_fails=3 fail_timeout=30s;
}

ที่นี่ถ้าเซิร์ฟเวอร์ backend มีข้อผิดพลาด 3 ครั้งติดต่อกัน Nginx จะทำเครื่องหมายว่า “down” และไม่ส่งคำขอไปยังมันเป็นเวลา 30 วินาที หลังจาก 30 วินาที Nginx จะลองส่งคำขอไปใหม่

Backup – เซิร์ฟเวอร์ส ำรอง

สามารถตั้งให้เซิร์ฟเวอร์บางตัวเป็นสำรองและใช้เฉพาะเมื่อเซิร์ฟเวอร์หลักลงไป:

upstream backend_pool {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080 backup;
}

ในตัวอย่างนี้ เซิร์ฟเวอร์ที่สามจะทำหน้าที่เป็นสำรองและจะใช้เฉพาะเมื่อเซิร์ฟเวอร์แรกและที่สองหมดหนทาง

Down – ปิดเซิร์ฟเวอร์

Flag “down” ใช้เพื่อปิดเซิร์ฟเวอร์ชั่วคราวโดยไม่ต้องลบออกจากค่าการตั้งค่า ซึ่งมีประโยชน์มากในการบำรุงรักษา:

upstream backend_pool {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080 down;
    server 192.168.1.12:8080;
}

Load Balancing Methods – วิธีการกระจาย Traffic

Nginx มีวิธีการกระจาย traffic หลายแบบ ให้เลือกใช้ตามความเหมาะสมกับ application ของคุณ

Round-Robin (ค่าเริ่มต้น)

Round-robin คือวิธีการกระจาย traffic แบบหมุนเวียน โดยส่งคำขอไปยังแต่ละเซิร์ฟเวอร์ตามลำดับ:

upstream backend_pool {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

Least Connections – การเชื่อมต่อน้อยที่สุด

วิธีนี้ส่งคำขอไปยังเซิร์ฟเวอร์ที่มีจำนวนการเชื่อมต่อด่วนน้อยที่สุด เหมาะสำหรับ long-lived connections:

upstream backend_pool {
    least_conn;

    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

IP Hash – แฮชพื้นฐานบน IP ผู้ส่ง

IP hash ใช้ IP address ของผู้ส่งเพื่อเลือกเซิร์ฟเวอร์ backend วิธีนี้รับประกันว่าผู้ส่งจากนั้น IP เดียวกันจะไปยังเซิร์ฟเวอร์เดียวกันเสมอ ซึ่งมีประโยชน์สำหรับ session-based applications:

upstream backend_pool {
    ip_hash;

    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

Hash – แฮชแบบกำหนดเอง

Hash method ให้ความยืดหยุ่นมากขึ้น สามารถแฮชตามตัวแปรเซิร์ฟเวอร์ใดก็ได้ เช่น request URI หรือ cookie:

upstream backend_pool {
    hash $request_uri consistent;

    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

Flag “consistent” ใช้ consistent hashing ซึ่งหมายความว่าเมื่อเพิ่ม/ลบเซิร์ฟเวอร์ ก็จะมีเพียงสัดส่วนเล็กของ requests ที่จำเป็นต้องไป remap ไป server ใหม่

Random – การเลือกแบบสุ่ม

Random method เลือกเซิร์ฟเวอร์แบบสุ่ม มี two_choices option ที่เลือก 2 เซิร์ฟเวอร์แบบสุ่มแล้วเลือกอันที่มีการเชื่อมต่อน้อยกว่า:

upstream backend_pool {
    random two_choices;

    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

Health Checks – การตรวจสอบสภาพเซิร์ฟเวอร์

Health checks ช่วยให้ Nginx ทราบว่าเซิร์ฟเวอร์ backend ไม่พร้อมใช้งาน Nginx มี 2 ประเภท: passive (ค่าเริ่มต้น) และ active (ต้องใช้ Nginx Plus)

Passive Health Checks

Passive health checks ตรวจสอบสถานะของเซิร์ฟเวอร์โดยยึดตามปฏิกิริยาต่อคำขอเสก็จ Nginx ทำเครื่องหมายเซิร์ฟเวอร์ว่า “down” หากมีข้อผิดพลาดจำนวนหนึ่ง:

upstream backend_pool {
    server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
}

Active Health Checks (Nginx Plus)

Active health checks ส่งคำขอแยก (probe requests) ไปยังเซิร์ฟเวอร์ backend เพื่อตรวจสอบสถานะ ซึ่งต้องใช้ Nginx Plus แต่ให้ข้อมูลที่แม่นยำมากขึ้น

Keepalive Connections – การรักษาการเชื่อมต่อ

Keepalive connections ช่วยลดโอเวอร์เฮดของการเปิดและปิดการเชื่อมต่อ TCP ใหม่ ซึ่งปรับปรุงประสิทธิภาพ:

upstream backend_pool {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;

    keepalive 32;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend_pool;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

ตัวเลขหลังจาก keepalive กำหนดจำนวนสูงสุดของการเชื่อมต่อ idle ที่จะเก็บไว้ สำหรับ HTTP/1.1 จำเป็นต้องตั้ง Connection header เป็น empty string

Upstream กับ HTTPS Backends

หากเซิร์ฟเวอร์ backend ใช้ HTTPS คุณจำเป็นต้องปรับแต่งการตั้งค่า proxy:

upstream backend_pool {
    server backend1.example.com:443;
    server backend2.example.com:443;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass https://backend_pool;
        proxy_ssl_verify off;
        proxy_ssl_session_reuse on;
        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_ssl_verify on และใช้ certificate ที่สอดคล้อง เพื่อความปลอดภัย

Upstream Variables – ตัวแปรที่ส่งออก

Nginx มีตัวแปรชุดหนึ่งที่สามารถใช้เพื่อติดตามและบันทึกข้อมูล upstream:

ตัวแปรคำอธิบาย
$upstream_addrIP address:port ของเซิร์ฟเวอร์ที่ส่งคำขอไป
$upstream_statusHTTP status code จากเซิร์ฟเวอร์ upstream
$upstream_response_timeเวลาที่ใช้เพื่อรับการตอบสนองจาก upstream
$upstream_bytes_receivedจำนวนไบต์ที่ได้รับจาก upstream
$upstream_bytes_sentจำนวนไบต์ที่ส่งไปยัง upstream
$upstream_connect_timeเวลาในการเชื่อมต่อ upstream

สามารถใช้ตัวแปรเหล่านี้ใน log_format เพื่อบันทึกข้อมูล upstream:

log_format upstream_log '$remote_addr - [$time_local] '
                        '"$request" $status '
                        'upstream: $upstream_addr, status: $upstream_status, '
                        'time: $upstream_response_time, bytes: $upstream_bytes_received';

access_log /var/log/nginx/upstream.log upstream_log;

Upstream Zones – การจัดการหน่วยความจำแบบแชร์

Upstream zones ใช้ shared memory เพื่อเก็บข้อมูล upstream state ซึ่งสำคัญเมื่อใช้ dynamic upstream (Nginx Plus feature) หรือต้องการให้ worker processes แชร์ข้อมูล:

upstream backend_pool {
    zone backend_pool 64k;

    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

Zone directive กำหนด zone name และขนาดของ shared memory ขนาด 64k มักจะเพียงพอสำหรับ 128-256 connections

Upstream กับ Resolve Flag สำหรับ DNS

เมื่อใช้ domain names แทน IP addresses ในเซิร์ฟเวอร์ upstream คุณสามารถใช้ flag “resolve” เพื่อให้ Nginx refresh DNS queries ตามช่วงเวลาที่กำหนด:

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

ต้อง resolver directive เพื่อระบุ DNS servers:

resolver 8.8.8.8 8.8.4.4 valid=30s;
resolver_timeout 5s;

upstream backend_pool {
    server backend1.example.com:8080 resolve;
    server backend2.example.com:8080 resolve;
}

Slow Start – การเริ่มต้นแบบช้า

Slow start feature ให้ newborn servers ได้รับ traffic ค่อยเป็นค่อยไปแทนที่จะเต็มทีเลย ซึ่งช่วยให้เซิร์ฟเวอร์มีเวลาเพื่อทำความพร้อม:

upstream backend_pool {
    server 192.168.1.10:8080 slow_start=30s;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

ในตัวอย่างนี้ เซิร์ฟเวอร์ 192.168.1.10 จะได้รับ traffic ค่อยเป็นค่อยไปในระยะ 30 วินาที จนกว่าจะถึง weight level ปกติ

Drain Mode – โหมดระบายน้ำ

Drain mode ใช้เพื่อส่งต่อเซิร์ฟเวอร์ไปยังสถานะที่ไม่ยอมรับการเชื่อมต่อใหม่ แต่ยังคงประมวลผลการเชื่อมต่อที่มีอยู่เดิม:

upstream backend_pool {
    server 192.168.1.10:8080 drain;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

ตัวอย่างจริงของ Upstream Configuration

ตัวอย่าง 1: Load Balancing สำหรับ Microservices

upstream api_service {
    least_conn;

    server api1.internal:3000 weight=2;
    server api2.internal:3000 weight=2;
    server api3.internal:3000 weight=1 slow_start=30s;

    keepalive 32;
}

upstream web_service {
    ip_hash;

    server web1.internal:8080;
    server web2.internal:8080;
    server web3.internal:8080 backup;

    keepalive 16;
}

server {
    listen 80;
    server_name api.example.com;

    location /api {
        proxy_pass http://api_service;
        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;
    }

    location / {
        proxy_pass http://web_service;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
    }
}

ตัวอย่าง 2: Configuration กับ Health Checks และการจัดการข้อผิดพลาด

upstream db_backends {
    zone db_zone 128k;

    server db1.example.com:5432 weight=3 max_fails=5 fail_timeout=60s;
    server db2.example.com:5432 weight=2 max_fails=5 fail_timeout=60s;
    server db3.example.com:5432 weight=1 max_fails=5 fail_timeout=60s backup;

    keepalive 64;
}

server {
    listen 80;
    server_name db-proxy.example.com;

    location / {
        proxy_pass http://db_backends;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_read_timeout 30s;
        proxy_connect_timeout 5s;
        proxy_send_timeout 10s;
    }

    # Monitoring endpoint
    location /status {
        access_log off;
        return 200 "Healthy\n";
        add_header Content-Type text/plain;
    }
}

ตัวอย่าง 3: Upstream กับ HTTPS Backends

upstream secure_backends {
    server secure1.example.com:443;
    server secure2.example.com:443;
    server secure3.example.com:443;

    keepalive 32;
}

server {
    listen 443 ssl http2;
    server_name secure-api.example.com;

    ssl_certificate /etc/nginx/certs/cert.pem;
    ssl_certificate_key /etc/nginx/certs/key.pem;

    location / {
        proxy_pass https://secure_backends;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_ssl_verify off;
        proxy_ssl_session_reuse on;
        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 https;
    }

    location /health {
        access_log off;
        return 200 "OK\n";
        add_header Content-Type text/plain;
    }
}

Best Practices สำหรับ Upstream Configuration

  • ใช้ upstream zones เพื่อเก็บข้อมูล state ร่วมกันระหว่าง worker processes
  • เปิดใช้งาน keepalive connections เพื่อลดโอเวอร์เฮดของการเชื่อมต่อ
  • ตั้ง proxy timeouts อย่างเหมาะสม (proxy_connect_timeout, proxy_read_timeout, proxy_send_timeout)
  • ใช้ max_fails และ fail_timeout เพื่อระบุเซิร์ฟเวอร์ที่ลงพื้นดิน
  • พิจารณาใช้ slow_start สำหรับเซิร์ฟเวอร์ใหม่
  • ตั้งค่า headers ที่เหมาะสม (X-Real-IP, X-Forwarded-For, Host)
  • บันทึก upstream information ใน logs เพื่อ debug
  • ทดสอบ load balancing setup อย่างถี่ถ้วนก่อนใช้งานจริง

สรุป

Upstream configuration ใน Nginx เป็นฟีเจอร์ที่มีประสิทธิภาพสูงสำหรับการจัดการและกระจาย traffic ไปยังเซิร์ฟเวอร์ backend หลายเครื่อง ด้วยการผสมผสาน weight, health checks, keepalive connections, และวิธีการ load balancing ต่างๆ คุณสามารถสร้างระบบที่มีความเสถียรและประสิทธิภาพสูง

กำลังสำคัญของ upstream configuration อยู่ที่ความยืดหยุ่น ซึ่งช่วยให้คุณปรับแต่งการทำงานตามความเหมาะสมกับ application ของคุณ ไม่ว่าจะเป็น microservices, traditional web applications, หรือ real-time applications

แนะนำบริการ DE

หากคุณกำลังมองหาโซลูชันสำหรับการจัดการ upstream servers และ load balancing ให้พิจารณา DE Cloud VPS ซึ่งมอบประสิทธิภาพและความเสถียรที่สูง พร้อมด้วยเครื่องมือสำหรับการจัดการเซิร์ฟเวอร์ได้อย่างมืออาชีพ

นอกจากนี้ DE Cloud Hosting ยังมีการรองรับ Nginx upstream pre-configured ซึ่งช่วยให้คุณสามารถเริ่มต้นการ load balancing ได้อย่างรวดเร็ว บริการของ DE มีการสนับสนุนที่ดี ดูแลสูง และการอัปเดตความปลอดภัยอย่างต่อเนื่อง

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการตั้งค่า Nginx upstream บนโปรแกรม DE ติดต่อทีมสนับสนุนของเรา