SSD Nodes Learn 🎉 VPS เริ่ม $5.50/เดือน
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-13

วิธีติดตั้ง LiteLLM เป็น LLM Gateway บนเซิร์ฟเวอร์ตัวเอง

เรียนรู้วิธีติดตั้ง LiteLLM เพื่อจัดการ API จากหลายผู้ให้บริการผ่านจุดเดียว รองรับการตั้งค่า Virtual keys การกำหนดงบประมาณรายแอป และการทำ Fallback โมเดลอัตโนมัติบน VPS ของคุณ

หน้าที่ของ LLM gateway แบบ self-hosted

LiteLLM คือ LLM gateway แบบ open source ที่คุณโฮสต์ด้วยตนเอง โดยทำหน้าที่เป็น HTTP endpoint เดียวที่แอปพลิเคชันทั้งหมดของคุณเรียกใช้งาน จากนั้น gateway จะส่งต่อคำขอแต่ละรายการไปยังผู้ให้บริการที่เหมาะสม LLM ย่อมาจาก large language model ตัว gateway นี้รองรับ OpenAI chat completions API (application programming interface) ดังนั้น client library ใดก็ตามที่สื่อสารกับ OpenAI อยู่แล้วจะสามารถใช้งานร่วมกับ gateway นี้ได้ทันทีหลังจากปรับเปลี่ยน 2 จุด คือ base URL และ key

การเพิ่มชั้นการทำงานนี้ขึ้นมาคือหัวใจสำคัญ แอปพลิเคชันของคุณไม่จำเป็นต้องเก็บข้อมูลรับรอง (credentials) ของผู้ให้บริการอีกต่อไป การเปลี่ยนโมเดลสามารถทำได้โดยแก้ไขบรรทัดเดียวในไฟล์ config บนเซิร์ฟเวอร์ แทนที่จะต้องแก้ไขโค้ดใน 5 บริการ และเนื่องจากทุกการเรียกใช้งานผ่านกระบวนการเดียว คุณจึงมีจุดศูนย์กลางสำหรับกำหนดงบประมาณและบันทึกค่าใช้จ่ายที่เกิดขึ้น

นี่คือสิ่งที่คุณจะได้รับเมื่อระบบเริ่มทำงาน:

  • Endpoint เดียว: แอปพลิเคชันจะชี้เป้าไปที่ https://gateway.example.com/v1 และระบุชื่อโมเดลที่คุณกำหนดขึ้นเอง เช่น bulk หรือ strong
  • Virtual keys: แต่ละแอปพลิเคชันจะได้รับ key ของตนเอง พร้อมรายการโมเดลที่อนุญาตให้ใช้และเพดานงบประมาณเฉพาะตัว คุณสามารถเพิกถอนสิทธิ์ของแอปหนึ่งได้โดยไม่กระทบแอปอื่น
  • Fallbacks: หากการเรียกใช้งานล้มเหลวหรือ prompt มีขนาดใหญ่เกินไป ระบบจะลองเรียกใช้โมเดลอื่นโดยอัตโนมัติ
  • บันทึกการใช้งาน: ทุกคำขอจะถูกบันทึกพร้อมระบุต้นทุน ทำให้สามารถตรวจสอบได้ว่า "แอปใดเป็นผู้ใช้จ่ายงบประมาณนั้น"

เหตุผลที่ควรจัดการเกตเวย์ด้วยตนเอง

Managed router มีลักษณะการทำงานเหมือนกัน แต่มีกระบวนการของผู้อื่นแทรกอยู่ตรงกลางในทุกคำขอ การจัดการด้วยตนเองช่วยให้กุญแจของผู้ให้บริการและข้อความ prompt ของคุณอยู่บนเครื่องที่คุณควบคุมได้จริง อย่างไรก็ตาม ต้นทุนที่ต้องแลกมานั้นมีอยู่จริง คือคุณต้องเป็นผู้ดูแลส่วนประกอบที่ทุกแอปพลิเคชันต้องพึ่งพา ส่วนสุดท้ายของคู่มือนี้จะกล่าวถึงต้นทุนดังกล่าว เนื่องจากเป็นส่วนที่บทความส่วนใหญ่มักละเลยไป

สิ่งที่ต้องเตรียม

  • VPS (virtual private server) ที่รัน Ubuntu 24.04 พร้อมติดตั้ง Docker และ Compose plugin
  • โดเมนเนมที่ชี้มายังเซิร์ฟเวอร์ หากต้องการให้เครื่องภายนอกเข้าถึง gateway ผ่าน TLS (transport layer security)
  • API key ของผู้ให้บริการอย่างน้อยหนึ่งรายการ

Gateway ไม่ได้ทำหน้าที่ประมวลผล inference โดยตรง แต่ทำหน้าที่ส่งต่อคำขอและสตรีมคำตอบกลับมา ดังนั้นภาระงานของ CPU จะขึ้นอยู่กับปริมาณคำขอมากกว่าขนาดของโมเดล เซิร์ฟเวอร์ขนาด 1 vCPU สามารถรองรับแอปพลิเคชันภายในจำนวนหนึ่งได้โดยไม่มีปัญหา สิ่งที่จะเพิ่มขึ้นตามการใช้งานคือฐานข้อมูล เนื่องจาก gateway จะบันทึกแถวข้อมูลค่าใช้จ่ายต่อหนึ่งคำขอ

เขียนไฟล์ config.yaml ก่อน

ไฟล์ config เป็นตัวกำหนดว่าไคลเอนต์สามารถเรียกใช้โมเดลใดได้บ้าง โดยมีส่วนประกอบหลัก 4 ส่วนที่สำคัญ ได้แก่ model_list, litellm_settings, router_settings และ general_settings

model_list:
  - model_name: bulk
    litellm_params:
      model: anthropic/claude-haiku-4-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: strong
    litellm_params:
      model: anthropic/claude-sonnet-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: strong
    litellm_params:
      model: openai/gpt-5.5
      api_key: os.environ/OPENAI_API_KEY

litellm_settings:
  num_retries: 2
  request_timeout: 120
  allowed_fails: 3
  cooldown_time: 30
  json_logs: true
  set_verbose: false

router_settings:
  fallbacks: [{"bulk": ["strong"]}]
  context_window_fallbacks: [{"bulk": ["strong"]}]

general_settings:
  background_health_checks: true
  health_check_interval: 300

model_name คือชื่อที่ไคลเอนต์ส่งเข้ามา ส่วน litellm_params.model คือโมเดลจริงที่เขียนในรูปแบบ provider/model ควรตั้งชื่อโมเดลตามลักษณะงานแทนที่จะตั้งตามชื่อผู้ให้บริการ เพื่อให้แอปพลิเคชันที่เรียกใช้ bulk ยังคงทำงานได้ตามปกติแม้ในเดือนถัดไปคุณจะตัดสินใจเปลี่ยน bulk ให้เป็นโมเดลอื่นก็ตาม

api_key: os.environ/ANTHROPIC_API_KEY เป็นคำสั่งให้ LiteLLM อ่านค่าตัวแปรนั้นในขณะรันไทม์ โดยไม่ต้องระบุคีย์จริงลงในไฟล์ ซึ่งเป็นเรื่องสำคัญเนื่องจาก config.yaml เป็นไฟล์ที่คุณต้อง commit ลงในระบบควบคุมเวอร์ชัน

มีการตั้งชื่อรายการสองรายการเป็น strong โดยเจตนา เมื่อมี deployment มากกว่าหนึ่งรายการที่ใช้ model_name เดียวกัน ตัว router จะมองว่ารายการเหล่านั้นสามารถใช้แทนกันได้ และจะพยายามเรียกใช้รายการอื่นหากรายการแรกเกิดความล้มเหลว นี่คือวิธีที่ strong ช่วยให้ระบบยังคงทำงานได้แม้ผู้ให้บริการรายหนึ่งจะประสบปัญหาชั่วคราว

num_retries: 2 จะทำการลองใหม่ (retry) กับ deployment เดิมเมื่อเกิดข้อผิดพลาดที่สามารถลองใหม่ได้ ส่วน fallback จะทำงานก็ต่อเมื่อการลองใหม่ทั้งหมดล้มเหลวแล้วเท่านั้น การตั้งค่า allowed_fails: 3 ร่วมกับ cooldown_time: 30 จะนำ deployment นั้นออกจากระบบหมุนเวียนเป็นเวลา 30 วินาทีหลังจากล้มเหลวครบ 3 ครั้ง เพื่อป้องกันไม่ให้ระบบพยายามเรียกใช้ผู้ให้บริการที่ตอบกลับเป็น 500 ในทุกคำขอ

fallbacks และ context_window_fallbacks มีเงื่อนไขการทำงานที่แตกต่างกัน โดยแบบที่สองเป็นฟังก์ชันที่มีประโยชน์ซึ่งคนมักมองข้าม

  • fallbacks จะทำงานเมื่อการเรียกหลักล้มเหลว
  • context_window_fallbacks จะทำงานเมื่อผู้ให้บริการปฏิเสธคำขอเนื่องจากมีความยาวเกิน context window ของโมเดลนั้นๆ ทำให้ prompt ที่มีขนาดใหญ่เกินไปถูกส่งไปยังโมเดลที่มีพื้นที่เพียงพอแทนที่จะส่งข้อความแจ้งเตือนข้อผิดพลาดกลับไปยังผู้เรียก

นอกจากนี้ยังมี content_policy_fallbacks สำหรับกรณีที่ผู้ให้บริการปฏิเสธคำขอเนื่องจากนโยบายเนื้อหา (content policy) ควรตั้งค่านี้ก็ต่อเมื่อคุณมีปลายทางที่เหมาะสมสำหรับส่งคำขอเหล่านั้นไปจัดการต่อ

การติดตั้ง LiteLLM บน VPS ด้วย Docker Compose

สร้างไดเรกทอรีเพื่อเก็บไฟล์สามไฟล์ ได้แก่ config.yaml, docker-compose.yml และ .env โดยปกติแล้วคู่มือเริ่มต้นใช้งานจะดึงแท็ก latest มาใช้ แต่แนะนำให้ระบุเวอร์ชัน (pin) ให้ชัดเจน เพื่อให้การรัน docker compose up -d ในเดือนถัดไปได้เกตเวย์เวอร์ชันเดิมที่คุณใช้งานในวันนี้ และทำให้การย้อนกลับเวอร์ชัน (rollback) ทำได้ง่ายเพียงบรรทัดเดียว

services:
  litellm:
    image: ghcr.io/berriai/litellm:v1.95.0
    restart: unless-stopped
    command: ["--config", "/app/config.yaml", "--num_workers", "1"]
    ports:
      - "127.0.0.1:4000:4000"
    volumes:
      - ./config.yaml:/app/config.yaml:ro
    env_file: .env
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_USER: litellm
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
      POSTGRES_DB: litellm
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U litellm"]
      interval: 5s
      timeout: 5s
      retries: 10
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Compose จะอ่านไฟล์ .env สองครั้งในกรณีนี้ ครั้งแรกเพื่อแทนที่ค่า ${POSTGRES_PASSWORD} ภายในไฟล์ compose เอง และครั้งที่สองผ่าน env_file เพื่อส่งตัวแปรทั้งหมดเข้าไปในคอนเทนเนอร์

v1.95.0 คือเวอร์ชันล่าสุด ณ เดือนสิงหาคม 2026 ให้ตรวจสอบหน้า releases ของโปรเจกต์และระบุเวอร์ชันที่เป็นปัจจุบันที่สุดในขณะที่คุณติดตั้ง แต่ละเวอร์ชันจะมีการเผยแพร่ลายเซ็นกำกับไว้ คุณจึงสามารถตรวจสอบอิมเมจก่อนที่จะเริ่มใช้งานได้:

cosign verify --key https://raw.githubusercontent.com/BerriAI/litellm/v1.95.0/cosign.pub ghcr.io/berriai/litellm:v1.95.0

บรรทัดกำหนดพอร์ตคือ 127.0.0.1:4000:4000 ซึ่งเป็นการเปิดพอร์ตบนอินเทอร์เฟซ loopback เท่านั้น หากคุณเปลี่ยนไปเขียน 4000:4000 แทน เกตเวย์ของคุณจะสามารถเข้าถึงได้จากอินเทอร์เน็ตทั้งหมด เนื่องจาก Docker จะเพิ่มกฎของตัวเองลงใน iptables ใน chain FORWARD ซึ่งจะถูกประมวลผลก่อนกฎของ ufw ดังนั้น ufw deny 4000 จึงไม่สามารถป้องกันการเข้าถึงนี้ได้ นี่เป็นสาเหตุที่พบบ่อยที่สุดที่ทำให้เกตเวย์ที่โฮสต์เองถูกเปิดเผยต่อสาธารณะ: ดูรายละเอียดเพิ่มเติมที่ วิธีที่ Docker เปิดพอร์ตคอนเทนเนอร์โดยข้าม ufw โดยควรให้ทราฟฟิกจากภายนอกเข้ามาผ่าน reverse proxy แทน

เก็บคีย์ของผู้ให้บริการไว้นอกอิมเมจ

ไฟล์ .env เก็บความลับทั้งหมดไว้ โดยจะถูกส่งผ่านเป็น environment variable ในขณะรันไทม์ ดังนั้นจึงไม่มีการฝังไว้ในอิมเมจและไม่มีการ commit ลงใน source control

LITELLM_MASTER_KEY=sk-REPLACE_ME
LITELLM_SALT_KEY=sk-REPLACE_ME_TOO
POSTGRES_PASSWORD=REPLACE_ME_AS_WELL
DATABASE_URL=postgresql://litellm:REPLACE_ME_AS_WELL@db:5432/litellm
STORE_MODEL_IN_DB=True
LITELLM_MODE=PRODUCTION
LITELLM_LOG=ERROR
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-proj-...

สร้างคีย์ LiteLLM สองชุดด้วยค่าสุ่มที่แท้จริง จากนั้นจำกัดสิทธิ์การเข้าถึงไฟล์:

printf 'sk-%s\n' "$(openssl rand -hex 32)"
chmod 600 .env

LITELLM_MASTER_KEY คือข้อมูลประจำตัวของผู้ดูแลระบบ ใช้สำหรับยืนยันตัวตนกับ management API และเป็นรหัสผ่านสำหรับ Admin UI ที่ /ui ห้ามไม่ให้แอปพลิเคชันใดๆ ถือครองคีย์นี้ไว้

LITELLM_SALT_KEY ใช้สำหรับเข้ารหัสข้อมูลประจำตัวของผู้ให้บริการที่จัดเก็บอยู่ในฐานข้อมูล ให้ตั้งค่าเพียงครั้งเดียวแล้วไม่ต้องแก้ไขอีก หากเปลี่ยนในภายหลัง ข้อมูลประจำตัวที่จัดเก็บไว้เดิมจะไม่สามารถถอดรหัสได้ ส่งผลให้ gateway เริ่มทำงานได้ตามปกติ แต่การเรียกใช้งานผู้ให้บริการเหล่านั้นทั้งหมดจะล้มเหลวเนื่องจากปัญหาการยืนยันตัวตน

STORE_MODEL_IN_DB=True ช่วยให้คุณเพิ่มและแก้ไขโมเดลผ่าน Admin UI ได้โดยไม่ต้องแก้ไขไฟล์ config.yaml แม้จะสะดวก แต่ก็ทำให้แหล่งข้อมูลหลัก (source of truth) ถูกแยกออกเป็นสองส่วน ให้ตัดสินใจว่าแหล่งข้อมูลใดจะเป็นหลักและบันทึกการตัดสินใจนั้นไว้ข้างไฟล์ config

เหตุผลที่ต้องเก็บคีย์ไว้นอกไฟล์ config เป็นเหตุผลเดียวกับที่ต้องเก็บคีย์ไว้นอกเครื่องมือที่คุณมอบให้กับ AI agent เนื้อหา การเก็บความลับของผู้ให้บริการไว้นอก AI agents อธิบายถึงรูปแบบดังกล่าว และ ไฟล์ env และความลับใน Docker Compose อธิบายถึงวิธีการดำเนินการ

เริ่มการทำงานและตรวจสอบการบูตครั้งแรก:

docker compose up -d
docker compose logs -f litellm

ตรวจสอบว่าระบบทำงานได้จริง

มีการตรวจสอบแบบไม่ระบุตัวตนสองรายการและการตรวจสอบแบบระบุตัวตนหนึ่งรายการ ซึ่งทั้งหมดล้มเหลวด้วยสาเหตุที่แตกต่างกัน

curl -s http://127.0.0.1:4000/health/liveliness
curl -s http://127.0.0.1:4000/health/readiness

/health/liveliness ไม่จำเป็นต้องมีการยืนยันตัวตนและจะตอบกลับเป็น "I'm alive!" ในขณะที่กระบวนการทำงานอยู่ ส่วน /health/readiness ก็ไม่จำเป็นต้องมีการยืนยันตัวตนเช่นกัน โดยจะส่งคืนออบเจกต์ JSON ที่มีฟิลด์ "status": "healthy" และ db หรือส่งคืนสถานะ 503 เมื่อไม่สามารถเข้าถึงฐานข้อมูลได้ ให้ชี้ระบบตรวจสอบของคุณไปที่ readiness เนื่องจาก liveliness จะแสดงสถานะเป็นสีเขียวเสมอในเกตเวย์ที่ไม่สามารถค้นหา virtual key ได้

การตรวจสอบแบบระบุตัวตนคือส่วนที่สื่อสารกับผู้ให้บริการ:

curl -s http://127.0.0.1:4000/health \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

ระบบจะตอบกลับด้วยอาร์เรย์ healthy_endpoints และ unhealthy_endpoints หากโมเดลที่อยู่ใน unhealthy_endpoints แสดงข้อผิดพลาดด้านการยืนยันตัวตน หมายความว่าคีย์ของผู้ให้บริการใน .env ไม่ถูกต้องหรือหายไป ซึ่งเป็นความล้มเหลวที่คุณต้องการตรวจพบในขณะนี้ เนื่องจากมีการตั้งค่า background_health_checks: true ไว้ พร็อกซีจะเรียกใช้การตรวจสอบเหล่านี้ทุกๆ health_check_interval วินาทีด้วยตัวเอง และ /health จะส่งคืนผลลัพธ์ล่าสุด ดังนั้นการ polling จึงไม่เป็นการส่งคำขอทดสอบไปยังผู้ให้บริการของคุณในทุกครั้งที่เรียกใช้

คีย์เสมือนและงบประมาณต่อคีย์

แอปพลิเคชันแต่ละตัวจะได้รับคีย์ของตนเอง ซึ่งถูกสร้างขึ้นโดยอ้างอิงจากคีย์หลัก (master key)

curl -s http://127.0.0.1:4000/key/generate \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "key_alias": "nightly-summariser",
    "models": ["bulk"],
    "max_budget": 5,
    "budget_duration": "30d",
    "rpm_limit": 60,
    "tpm_limit": 200000
  }'

การตอบกลับจะมีฟิลด์ key ซึ่งขึ้นต้นด้วย sk- สตริงดังกล่าวคือสิ่งที่แอปพลิเคชันได้รับ และเป็นสิ่งเดียวที่แอปพลิเคชันจะได้รับตลอดการใช้งาน

  • models คือรายการอนุญาต (allowlist) สำหรับสิ่งที่คีย์นี้สามารถร้องขอได้ คีย์ข้างต้นสามารถร้องขอได้เพียง bulk เท่านั้น
  • max_budget: 5 พร้อมด้วย budget_duration: "30d" คือการกำหนดงบประมาณ 5 ดอลลาร์สหรัฐต่อรอบ 30 วัน หลังจากนั้นคีย์จะหยุดทำงาน
  • rpm_limit และ tpm_limit คือการจำกัดจำนวนคำขอต่อนาทีและจำนวนโทเค็นต่อนาทีสำหรับคีย์นี้โดยเฉพาะ
  • key_alias คือสิ่งที่คุณจะใช้ระบุตัวตนในบันทึกการใช้จ่าย (spend log) ในอีก 6 สัปดาห์ถัดไป ควรตั้งค่านี้ไว้เสมอ

เมื่องบประมาณหมดลง การเรียกใช้งานจะล้มเหลวด้วย HTTP 401 และมีเนื้อหาในรูปแบบนี้:

ExceededBudget: Current spend for token: 7.2e-05; Max Budget for Token: 2e-07

รหัสสถานะ (status code) คือสิ่งที่ทำให้เกิดความสับสน เนื่องจากไลบรารีของไคลเอ็นต์มักรายงาน 401 ว่าเป็นปัญหาด้านการยืนยันตัวตน นักพัฒนาที่อ่าน stack trace จึงมักเริ่มตรวจสอบว่าคีย์ยังใช้งานได้อยู่หรือไม่ ควรบันทึกเนื้อหาการตอบกลับ (response body) ควบคู่ไปกับรหัสสถานะ มิฉะนั้นการที่งบประมาณหมดจะดูเหมือนข้อมูลประจำตัวเสียหายอยู่ตลอดเวลา

คุณสามารถตรวจสอบและปรับแต่งคีย์ผ่าน API การจัดการตัวเดียวกันได้:

curl -s "http://127.0.0.1:4000/key/info?key=sk-..." \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

curl -s -X POST http://127.0.0.1:4000/key/update \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"key": "sk-...", "max_budget": 25}'

งบประมาณที่ถูกบังคับใช้ที่เกตเวย์จะยังคงมีผลแม้ในกรณีที่ตัวแทน (agent) เองเกิดข้อผิดพลาด ซึ่งเป็นเหตุผลว่าทำไมมันจึงเป็นหัวใจสำคัญของ การควบคุมค่าใช้จ่ายสำหรับ AI agents บน VPS

ส่งงานจำนวนมากไปยังโมเดลราคาประหยัด

กำหนดให้ไคลเอนต์ชี้ไปยังเกตเวย์ โดยระบุ Base URL, คีย์ และชื่อโมเดลดังนี้:

curl -s http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-<the virtual key>" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "bulk",
    "messages": [{"role": "user", "content": "Say hello in five words."}]
  }'

ไลบรารีไคลเอนต์ของ OpenAI ทุกตัวทำงานในลักษณะเดียวกัน: ตั้งค่า base_url เป็น https://gateway.example.com/v1 และ api_key เป็นคีย์เสมือน

นโยบายการจัดเส้นทาง (routing policy) จาก config.yaml จะถูกนำมาใช้โดยที่ผู้เรียกใช้งานไม่จำเป็นต้องทราบ คำขอสำหรับ bulk จะถูกส่งไปยังโมเดลราคาประหยัด หากการเรียกนั้นล้มเหลวหลังจากพยายามซ้ำแล้ว คำขอจะถูกส่งไปลองใหม่ที่ strong หาก prompt มีความยาวเกินกว่าที่ bulk จะรับได้ context_window_fallbacks จะส่งคำขอนั้นไปยัง strong แทนที่จะส่งคืนข้อผิดพลาด งานจำนวนมาก เช่น การจัดหมวดหมู่หรือการสรุปข้อมูลค้างสะสม จะทำงานด้วยต้นทุนที่ต่ำโดยอัตโนมัติ และจะมีเพียงคำขอที่ซับซ้อนเท่านั้นที่ต้องเสียค่าใช้จ่ายสูงขึ้น

นี่คือจุดที่เกตเวย์แสดงคุณค่าเมื่อใช้งานร่วมกับเอเจนต์ที่ใช้เครื่องมือ (tool-using agents) โดยที่ MCP (model context protocol) server บน VPS เดียวกัน และเอเจนต์ที่ควบคุมมัน สามารถชี้ไปยัง endpoint เดียวกันได้ ทำให้สามารถเปลี่ยนโมเดลที่อยู่เบื้องหลังได้โดยไม่ต้อง deploy ใหม่ทั้งสองส่วน

คุณจะทราบได้อย่างไรว่ามีการทำ fallback เกิดขึ้น?

นี่คือรูปแบบความล้มเหลวที่ทำให้เสียค่าใช้จ่าย เพราะทุกอย่างดูเหมือนปกติ การทำ fallback ที่สำเร็จจะส่งสถานะ HTTP 200 พร้อมเนื้อหาการตอบกลับตามปกติ โมเดลราคาประหยัดของคุณอาจหยุดทำงานไปทั้งวัน โดยที่ทุกคำขอถูกส่งไปยังโมเดลราคาแพงโดยที่คุณไม่รู้ตัว และหลักฐานแรกที่คุณจะพบคือใบแจ้งหนี้

หลักฐานนั้นมีอยู่จริงในส่วนของ response headers คุณสามารถตรวจสอบได้ดังนี้:

curl -s -D - -o /dev/null http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-<the virtual key>" \
  -H 'Content-Type: application/json' \
  -d '{"model":"bulk","messages":[{"role":"user","content":"ping"}]}' \
  | grep -i '^x-litellm'
  • x-litellm-model-group คือสิ่งที่ไคลเอนต์ร้องขอ ส่วน x-litellm-model-id คือ deployment ที่เป็นผู้ตอบกลับ เมื่อค่าทั้งสองไม่ตรงกัน แสดงว่ามีการทำ fallback เกิดขึ้น
  • x-litellm-attempted-fallbacks และ x-litellm-attempted-retries ใช้สำหรับนับจำนวนครั้ง หากการเรียกใช้งานเป็นปกติ ทั้งสองค่าจะเป็น 0
  • x-litellm-response-cost คือต้นทุนของคำขอนั้นๆ ในหน่วยดอลลาร์สหรัฐ
  • x-litellm-call-id คือตัวระบุที่คุณใช้สำหรับค้นหาคำขอเดียวกันใน log ของคุณ

ให้บันทึกค่า x-litellm-attempted-fallbacks ในทุกคำขอและตั้งค่าการแจ้งเตือนเมื่อค่าดังกล่าวไม่ใช่ 0 ตัวเลขนี้คือความแตกต่างระหว่างนโยบายการจัดเส้นทางที่ทำงานได้จริง กับนโยบายที่กลายเป็น "ใช้โมเดลราคาแพงเสมอ" โดยที่คุณไม่รู้ตัว

เวอร์ชันเต็มของเรื่องนี้คือการทำ tracing ซึ่งควรมีการตั้งค่าแยกต่างหาก: การติดตั้ง Langfuse บนเซิร์ฟเวอร์ตนเองเพื่อติดตามการเรียกใช้งาน agent LiteLLM มี callback มาให้พร้อม ดังนั้นการเชื่อมต่อจึงใช้เพียงสองบรรทัดบวกกับข้อมูลรับรอง (credentials)

litellm_settings:
  success_callback: ["langfuse"]
  failure_callback: ["langfuse"]
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_HOST=https://langfuse.example.com

ตั้งค่า failure_callback และ success_callback หากคุณข้ามขั้นตอนนี้ไป Trace ที่คุณเก็บไว้จะมีเฉพาะกรณีที่ไม่มีอะไรผิดพลาดเท่านั้น นอกจากนี้ LiteLLM จะเขียนแถวข้อมูลค่าใช้จ่ายต่อคำขอลงใน Postgres และ Admin UI ที่ /ui จะอ่านข้อมูลจากตารางนั้น ข้อมูลนี้จะเพิ่มขึ้นตามปริมาณ traffic ดังนั้นควรหมั่นตรวจสอบหากใช้ดิสก์ขนาดเล็ก

การวาง gateway ไว้หลัง reverse proxy

ไม่มีสิ่งใดจากภายนอกควรเข้าถึงพอร์ต 4000 ได้โดยตรง ให้ทำ TLS termination ที่ nginx หรือ Caddy แล้วส่งต่อคำขอไปยัง loopback address

location / {
    proxy_pass http://127.0.0.1:4000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_buffering off;
    proxy_read_timeout 600s;
}

มีสองบรรทัดที่ผู้คนมักลืมใส่ proxy_buffering off มีความสำคัญเนื่องจากการทำ streaming completion คือชุดของ server-sent events หากเปิดใช้งาน buffering ไว้ nginx จะกักเก็บข้อมูลส่วนต่างๆ ไว้จนกว่าการตอบสนองจะสิ้นสุด ส่งผลให้ไคลเอนต์ไม่ได้รับข้อมูลใดๆ จนกว่าจะได้รับข้อมูลทั้งหมดพร้อมกันในคราวเดียว proxy_read_timeout 600s มีความสำคัญเนื่องจากการสร้างข้อมูลที่ใช้เวลานานอาจเกินค่า default 60 วินาทีของ nginx ซึ่งจะทำให้ไคลเอนต์ได้รับข้อผิดพลาด 504 ในขณะที่ log บันทึกข้อผิดพลาด upstream timed out (110: Connection timed out) while reading response header from upstream ไว้

สำหรับใบรับรอง Certbot กับ Let's Encrypt บน nginx เป็นวิธีที่รวดเร็วที่สุด หากเซิร์ฟเวอร์ให้บริการคอนเทนเนอร์หลายตัวอยู่แล้ว Traefik หน้าแอป Compose หลายตัว จะช่วยจัดการการกำหนดเส้นทางและใบรับรองไว้ในจุดเดียว

เกตเวย์กลายเป็นจุดที่หากล้มเหลวจะส่งผลกระทบต่อทั้งระบบ

จงยอมรับความจริงเกี่ยวกับสิ่งที่คุณได้สร้างขึ้น แอปพลิเคชันทุกตัวที่คุณมีในขณะนี้ขึ้นอยู่กับคอนเทนเนอร์เดียวบน VPS หนึ่งเครื่อง ในขณะที่มันหยุดทำงาน จะไม่มีบริการใดสามารถเรียกใช้โมเดลได้เลย แม้แต่ผู้ให้บริการที่ยังทำงานได้ตามปกติก็ตาม ซึ่งนำไปสู่ข้อควรระวัง 4 ประการดังนี้

  • การตั้งค่าที่ผิดพลาดจะทำให้ทุกอย่างหยุดทำงานพร้อมกัน restart: unless-stopped จะรีสตาร์ทคอนเทนเนอร์ที่ทำงานล้มเหลว และมันจะรีสตาร์ทคอนเทนเนอร์ที่ไม่สามารถอ่านไฟล์ config.yaml ซ้ำแล้วซ้ำเล่า ให้อ่าน docker compose logs litellm ทุกครั้งหลังแก้ไขการตั้งค่า และควรทำการเปลี่ยนแปลงการตั้งค่าในเวลาที่คุณมีโอกาสเฝ้าสังเกตผลลัพธ์
  • Postgres อยู่ในเส้นทางการร้องขอ ทั้งการตรวจสอบคีย์เสมือนและการบันทึกค่าใช้จ่ายล้วนต้องใช้ฐานข้อมูลนี้ การที่ /health/readiness ส่งค่า 503 กลับมาเป็นสัญญาณเตือนว่าเกตเวย์ยังทำงานอยู่แต่ไม่สามารถทำหน้าที่ทั้งสองอย่างนั้นได้
  • ขยายระบบด้วยการเพิ่มอินสแตนซ์ ไม่ใช่การเพิ่มขนาดของอินสแตนซ์เดียว คำแนะนำของโปรเจกต์คือให้ใช้หนึ่งเวิร์กเกอร์ต่อหนึ่งอินสแตนซ์ (--num_workers 1) โดยให้หลายอินสแตนซ์ใช้ฐานข้อมูลร่วมกัน การมีเกตเวย์ขนาดเล็กสองตัวอยู่หลังโหลดบาลานเซอร์จะช่วยขจัดปัญหาการมีคอนเทนเนอร์เดียว แต่ไม่ได้ขจัดปัญหาเรื่องฐานข้อมูล
  • สำรองข้อมูลที่คุณไม่สามารถสร้างใหม่ได้ นั่นคือ config.yaml และ .env รวมถึง pg_dump ของฐานข้อมูล การสูญเสีย LITELLM_SALT_KEY จะทำให้ข้อมูลประจำตัวของผู้ให้บริการที่ถูกเข้ารหัสไว้ในไฟล์ดัมพ์นั้นใช้งานไม่ได้ ดังนั้นไฟล์ env และไฟล์ดัมพ์จึงควรอยู่ในงานสำรองข้อมูลเดียวกัน: การสำรองข้อมูลด้วย restic ไปยังพื้นที่จัดเก็บภายนอก

การอัปเกรดทำได้โดยการแก้ไขแท็กของอิมเมจแล้วรัน docker compose up -d โดยปกติแล้ว LiteLLM จะรัน prisma migrate deploy ในขณะเริ่มต้นระบบ ดังนั้นคอนเทนเนอร์ใหม่จะทำการย้ายสคีมาฐานข้อมูล (migrate) ในการบูตครั้งแรก ให้ทำการดัมพ์ข้อมูลก่อนที่คุณจะเปลี่ยนแท็ก เพราะการนำอิมเมจเก่ากลับมาใช้ใหม่ไม่สามารถย้อนการย้ายสคีมาที่ทำงานไปแล้วได้

FAQ

LiteLLM เพิ่ม latency ที่สังเกตได้ในการเรียกใช้งานแต่ละครั้งหรือไม่?

โครงการระบุตัวเลข latency ไว้ที่ 8 ms ณ เปอร์เซ็นไทล์ที่ 95 ที่อัตรา 1000 คำขอต่อวินาที ตามที่ระบุไว้ใน README เมื่อเดือนสิงหาคม 2026 ให้ถือว่าตัวเลขนี้เป็นข้อมูลจากผู้ผลิต ตัวเลขที่จะส่งผลต่อ latency ของคุณจริงๆ คือระยะทางเครือข่ายระหว่างแอปพลิเคชันของคุณกับ gateway เนื่องจากคุณได้เพิ่มการรับส่งข้อมูลไป-กลับ (round trip) เข้าไปในการเรียกใช้งานทุกครั้ง ควรติดตั้ง gateway ไว้ในภูมิภาคเดียวกับแอปพลิเคชันที่เรียกใช้งาน จากนั้นจึงวัด overhead ของคุณเองด้วย header x-litellm-overhead-duration-ms จากการตอบกลับจริง

ทำไมการสตรีมถึงหยุดทำงานหลังจากวาง nginx ไว้ด้านหน้า?

เนื่องจาก nginx จะทำ buffering การตอบกลับจาก upstream เป็นค่าเริ่มต้น ในขณะที่การสตรีม completion คือชุดของ server-sent events เมื่อเปิดใช้งาน proxy_buffering ตัว nginx จะรวบรวมข้อมูลเป็นส่วนๆ และปล่อยออกมาเมื่อการตอบกลับเสร็จสิ้นเท่านั้น ทำให้ไคลเอนต์รอโดยไม่มีการตอบสนองและได้รับคำตอบทั้งหมดพร้อมกันในคราวเดียว ให้ตั้งค่า proxy_buffering off; ในบล็อก location และเพิ่มค่า proxy_read_timeout ในบล็อกเดียวกัน เพราะหากการสร้างข้อความใช้เวลานานจะเกินค่าเริ่มต้น 60 วินาทีของ nginx และทำให้ไคลเอนต์ได้รับข้อผิดพลาด 504

จะเกิดอะไรขึ้นเมื่อ virtual key ใช้งานงบประมาณจนหมด?

การเรียกใช้งานจะล้มเหลวด้วย HTTP 401 และมีเนื้อหาในรูปแบบ ExceededBudget: Current spend for token: 7.2e-05; Max Budget for Token: 2e-07 ค่า 401 นี้เป็นกับดัก: ไลบรารีของไคลเอนต์จะรายงานว่าเป็นความล้มเหลวในการยืนยันตัวตน ทำให้ผู้ใช้เริ่มตรวจสอบว่าคีย์ยังใช้งานได้หรือไม่ แทนที่จะอ่านข้อความที่แจ้งเตือน ให้บันทึกเนื้อหาการตอบกลับควบคู่ไปกับ status code ตรวจสอบสถานะจริงของคีย์ด้วย /key/info?key=sk-... เทียบกับ master key และเพิ่มเพดานงบประมาณด้วย /key/update หากตั้งค่างบประมาณไว้ต่ำเกินไป

gateway สามารถกำหนดเส้นทางไปยังโมเดลในเครื่อง (local model) เช่นเดียวกับโมเดลที่โฮสต์ไว้ได้หรือไม่?

ได้ และเป็นอีกหนึ่งรายการใน model_list ให้ใช้ prefix ollama_chat/ ร่วมกับ api_base เช่น model: ollama_chat/llama3.1 ควบคู่ไปกับ api_base: http://ollama:11434 จากภายในคอนเทนเนอร์ localhost หมายถึงคอนเทนเนอร์นั้นเอง ดังนั้นให้ใช้ชื่อบริการใน Compose หรือที่อยู่ของโฮสต์บนเครือข่าย Docker ห้ามใช้ 127.0.0.1 การติดตั้งโมเดลในเครื่องเป็นงานแยกต่างหาก: ดูที่ การโฮสต์ LLM ด้วยตนเองโดยใช้ Ollama บน VPS