ข้ามไปเนื้อหาหลัก
คอนเทนเนอร์· ~30 นาที

เสิร์ฟ VOD ผ่าน Ingress + หน้าเว็บ player

ติดตั้ง ingress-nginx เปิด bucket ให้อ่านได้ และสร้างหน้าเว็บอัปโหลด+ดูวิดีโอครบวงจร

ชิ้นสุดท้าย: ให้คนดูวิดีโอผ่านเบราว์เซอร์ได้จริง — ติดตั้ง Ingress controller เป็นประตูเดียวของระบบ แล้ว route ตาม path: / ไปหน้าเว็บ+API และ /vod ตรงไปที่ MinIO (เพราะ HLS เป็นไฟล์ static ไม่จำเป็นต้องผ่าน API ให้เปลืองแรง)

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

# รอจน controller พร้อม
kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=120s
💻 ติดตั้ง ingress-nginx สำหรับ kind (ใช้พอร์ต 8080 ที่ map ไว้ตั้งแต่บท 8.5)

เปิด bucket vod ให้อ่านแบบ public

ผู้ชมต้องโหลด .m3u8/.ts ได้โดยไม่ต้อง login — ใช้ Pod ชั่วคราว (kubectl run) รัน mc เข้าไปตั้ง bucket policy:

kubectl run mc --rm -it --image=minio/mc --restart=Never --command -- /bin/sh
# ได้ shell ข้างใน Pod แล้วพิมพ์:
mc alias set store http://minio:9000 admin password123
mc anonymous set download store/vod
exit   # Pod ลบตัวเองอัตโนมัติเพราะ --rm
💻 ตั้ง anonymous download เฉพาะ bucket vod (uploads ยังปิดเหมือนเดิม)

เพิ่ม endpoint รายการวิดีโอใน API

// คืนรายการ videoId ที่แปลงเสร็จแล้ว (มี index.m3u8 ใน bucket vod)
app.get('/api/videos', (_req, res) => {
  const ids = new Set()
  const stream = minio.listObjectsV2('vod', '', true)
  stream.on('data', (obj) => {
    if (obj.name.endsWith('index.m3u8')) ids.add(obj.name.split('/')[0])
  })
  stream.on('end', () => res.json([...ids]))
  stream.on('error', (err) => res.status(500).json({ error: err.message }))
})
📄 api/server.js — เวอร์ชันสุดท้าย: เพิ่ม GET /api/videos

หน้าเว็บครบวงจร: อัปโหลด + รายการ + player

<!doctype html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mini YouTube บน K8s</title>
<style>
  body { font-family: sans-serif; max-width: 760px; margin: 2rem auto; padding: 0 1rem; }
  li { margin: .3rem 0; } #msg { color: #059669; }
</style>

<h1>🎬 Mini YouTube บน K8s</h1>

<form id="up">
  <input type="file" name="video" accept="video/mp4" required>
  <button>อัปโหลด</button>
</form>
<p id="msg"></p>

<h2>วิดีโอทั้งหมด <button onclick="refresh()">รีเฟรช</button></h2>
<ul id="list"></ul>

<video id="player" controls width="720"></video>

<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<script>
  async function refresh() {
    const videos = await (await fetch('/api/videos')).json()
    list.innerHTML = videos
      .map((v) => '<li><a href="#" onclick="play(\'' + v + '\');return false">▶ ' + v + '</a></li>')
      .join('') || '<li>ยังไม่มีวิดีโอ — อัปโหลดเลย!</li>'
  }

  function play(id) {
    const src = '/vod/' + id + '/index.m3u8'
    if (player.canPlayType('application/vnd.apple.mpegurl')) {
      player.src = src
    } else if (Hls.isSupported()) {
      const hls = new Hls()
      hls.loadSource(src)
      hls.attachMedia(player)
    }
    player.play()
  }

  up.onsubmit = async (e) => {
    e.preventDefault()
    msg.textContent = '⏳ กำลังอัปโหลด...'
    const r = await fetch('/api/upload', { method: 'POST', body: new FormData(up) })
    const d = await r.json()
    msg.textContent = '✅ videoId=' + d.videoId + ' กำลังแปลงไฟล์อยู่ — รอครู่นึงแล้วกดรีเฟรช'
  }

  refresh()
</script>
📄 api/public/index.html

Ingress: ประตูเดียว สอง path

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: video
  namespace: video
spec:
  ingressClassName: nginx
  rules:
    - http:
        paths:
          - path: /vod
            pathType: Prefix
            backend:
              service:
                name: minio
                port: { number: 9000 }
          - path: /
            pathType: Prefix
            backend:
              service:
                name: video-api
                port: { number: 80 }
📄 k8s/ingress.yaml — /vod ตรงเข้า MinIO (path ตรงกับชื่อ bucket พอดี เลยไม่ต้อง rewrite)
docker build -t video-api:v1 ./api
kind load docker-image video-api:v1 --name video
kubectl rollout restart deployment/video-api
kubectl apply -f k8s/ingress.yaml

# เปิดเบราว์เซอร์ → http://localhost:8080
# อัปโหลด sample.mp4 → รอ Job วิ่งจบ (kubectl get jobs -w) → รีเฟรช → กดเล่น 🎉
💻 deploy รอบสุดท้าย แล้วเปิดใช้งานจริง!

สรุป Key Takeaways

  • Ingress = ประตูเดียว route ตาม path: / → API, /vod → MinIO ตรง ๆ
  • ไฟล์ static ไม่ควรไหลผ่าน API — pattern เดียวกับ CDN หน้า S3 ใน production
  • bucket ผลลัพธ์เปิด anonymous download ได้ แต่ bucket ต้นฉบับยังปิดไว้
อ่านจบแล้วอย่าลืมทำเครื่องหมาย