Abdulaziz Akyol

Docker ve Kubernetes ile GPU görüntü analitiği servislerini ölçeklemek

Yazılım geliştirme · Görüntü işleme
25 Eylül 2026 · 7 dk okuma · Abdulaziz Akyol

GPU görüntü analitiği servislerini ölçeklemek, kamera başına çalışan çıkarım süreçlerini konteynere koyup GPU erişimlerini, sağlıklarını ve güncellemelerini tekrarlanabilir biçimde yönetmek demektir. Tek sunucuda Docker Compose, çok sunuculu ya da çok lokasyonlu kurulumda Kubernetes bu işin iki ölçeği.

Bu yazıda Python ve YOLO ile gerçek zamanlı tespit yazısındaki betiği servis haline getiriyorum. Modeli TensorRT motoruna derleme tarafı ONNX, TensorRT ve INT8 yazısında; burada o motoru sahaya dağıtmakla ilgileniyoruz.

Mimari karar: kamera başına bir süreç

Her kamera için ayrı bir konteyner (Kubernetes’te ayrı bir pod) çalıştırıyorum. Takip durumu kameraya özgü olduğu için süreçleri ayırmak kod tarafını sadeleştiriyor; bir kameranın akış sorunu ötekileri etkilemiyor; güncelleme de kamera kamera yapılabiliyor. Bedeli GPU belleği: her süreç modeli ayrıca yüklüyor.

YaklaşımArtıEksi
Kamera başına süreçHata yalıtımı, basit kod, kamera bazında güncellemeHer süreç modeli ayrıca yükler
Çok kameralı tek süreçToplu (batch) çıkarım, daha az bellekBir hata tüm kameraları durdurur, kod karmaşıklaşır

Kamera sayısı GPU belleğini zorlamaya başlayınca çok kameralı sürece geçmek mantıklı; o noktaya kadar kamera başına süreç işletmesi kolay olan tercih.

Adım 1: Docker’da GPU: NVIDIA Container Toolkit

Hostta NVIDIA sürücüsü kurulu olmalı; CUDA kütüphaneleri imajın içinden gelir. NVIDIA Container Toolkit, sürücüyü konteynere bağlayan katman. Paket deposunu NVIDIA’nın kurulum rehberine göre ekledikten sonra:

sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
sudo docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi   # GPU görünüyor mu?

Son komut nvidia-smi tablosunu basıyorsa konteynerler GPU’yu görüyor demektir.

Adım 2: Çok aşamalı Dockerfile

İlk aşama bağımlılıkları bir sanal ortama kuruyor, ikinci aşama yalnızca bu ortamı ve uygulama dosyalarını alıyor. pip önbelleği ve derleme artıkları son imaja girmiyor.

# Sürümleri sabitleyin; motoru üreten ve çalıştıran TensorRT sürümü aynı olmalı.
ultralytics==8.4.163
tensorrt-cu13==11.3.0.99      # PyPI'daki torch 2.14 tekerleği CUDA 13 kütüphanelerini çekiyor
nvidia-modelopt[onnx]==0.47.0 # TensorRT 11'de FP16/INT8 dışa aktarımı ModelOpt ile yapılıyor
paho-mqtt==2.1.0
prometheus-client==0.26.0
# syntax=docker/dockerfile:1

# --- 1. aşama: bağımlılıkları sanal ortama kur ---------------------------------
FROM python:3.12-slim-bookworm AS build
ENV PIP_NO_CACHE_DIR=1 PIP_DISABLE_PIP_VERSION_CHECK=1
RUN python -m venv /opt/venv
ENV PATH=/opt/venv/bin:$PATH
COPY requirements.txt /tmp/requirements.txt
RUN pip install -r /tmp/requirements.txt

# --- 2. aşama: yalnızca çalışma zamanı ------------------------------------------
FROM python:3.12-slim-bookworm AS runtime
# OpenCV'nin (opencv-python) ihtiyaç duyduğu sistem kütüphaneleri
RUN apt-get update \
 && apt-get install -y --no-install-recommends libgl1 libglib2.0-0 \
 && rm -rf /var/lib/apt/lists/*
COPY --from=build /opt/venv /opt/venv
ENV PATH=/opt/venv/bin:$PATH \
    PYTHONUNBUFFERED=1 \
    YOLO_CONFIG_DIR=/tmp/ultralytics \
    NVIDIA_DRIVER_CAPABILITIES=compute,utility,video
RUN useradd --uid 10001 --no-create-home app
WORKDIR /app
COPY --chown=app:app rtsp_yolo_events.py observability.py ./
# Model imaja gömülmez: TensorRT motoru GPU'ya ve TensorRT sürümüne özgüdür, /app/models'e bağlanır.
USER 10001
EXPOSE 9108
# Ana döngü kalp atışı dosyasını 30 sn'dir güncellemediyse konteyner sağlıksız sayılır
HEALTHCHECK --interval=15s --timeout=3s --start-period=120s --retries=3 \
  CMD ["python", "-c", "import os,sys,time; sys.exit(0 if time.time()-os.path.getmtime('/tmp/heartbeat')<30 else 1)"]
ENTRYPOINT ["python", "rtsp_yolo_events.py"]

Birkaç karar:

  • Model imaja gömülmüyor. TensorRT motoru derlendiği GPU’ya ve TensorRT sürümüne özgü; aynı imaj farklı GPU’lu sahalarda çalışacaksa motor her sahada üretilip /app/models altına bağlanıyor.
  • Sürümler sabit. Motoru üreten ve çalıştıran TensorRT sürümü aynı olmalı; latest etiketi ve sabitlenmemiş paketler bu eşleşmeyi bozar.
  • NVIDIA_DRIVER_CAPABILITIES içindeki video, konteynerin donanım video kod çözücüsüne erişmesi için gerekli; compute CUDA, utility ise nvidia-smi içindir.
  • ModelOpt imajın içinde. Ultralytics, TensorRT 11’de ModelOpt’u ilk kullanımda kendisi kurmaya çalışıyor; kök olmayan kullanıcıyla çalışan bir konteynerde bu kurulum yapılamayacağı için paketi baştan ekliyorum.
  • Kök olmayan kullanıcı ve Ultralytics’in ayar dosyası için yazılabilir YOLO_CONFIG_DIR.

Sağlık kontrolü ve Prometheus metrikleri için servise küçük bir modül ekliyorum:

"""Analitik servisine Prometheus metrikleri ve kalp atışı dosyası ekler (prometheus-client)."""
from __future__ import annotations

import os
import time
from pathlib import Path

from prometheus_client import Counter, Gauge, Histogram, start_http_server

CAMERA = os.getenv("CAMERA_ID", "cam-07")
HEARTBEAT = Path(os.getenv("HEARTBEAT_FILE", "/tmp/heartbeat"))

FRAMES = Counter("va_frames_processed_total", "İşlenen kare sayısı", ["camera"])
EVENTS = Counter("va_events_total", "Üretilen olay sayısı", ["camera", "type"])
RECONNECTS = Counter("va_rtsp_reconnects_total", "RTSP yeniden bağlanma sayısı", ["camera"])
INFER = Histogram("va_inference_seconds", "Kare başına tespit+takip süresi", ["camera"],
                  buckets=(0.005, 0.01, 0.02, 0.04, 0.08, 0.16, 0.32))
LAST_FRAME = Gauge("va_last_frame_timestamp_seconds", "Son işlenen karenin zamanı", ["camera"])

_last_beat = 0.0


def start(port: int = 9108) -> None:
    start_http_server(port)  # /metrics


def heartbeat() -> None:
    """Ana döngünün her turunda çağırın; dosyaya en fazla saniyede bir yazar.
    Kamera kapalıyken de döngü dönüyorsa süreç sağlıklıdır: yeniden başlatmak kamerayı düzeltmez."""
    global _last_beat
    now = time.time()
    if now - _last_beat >= 1.0:
        HEARTBEAT.write_text(str(int(now)))
        _last_beat = now


def frame_done(seconds: float) -> None:
    """Her işlenen karede çağırın."""
    FRAMES.labels(CAMERA).inc()
    INFER.labels(CAMERA).observe(seconds)
    LAST_FRAME.labels(CAMERA).set(time.time())

Betiğe entegrasyonu birkaç satır: main() başında obs.start(9108), ana döngünün her turunda obs.heartbeat(), model.track çağrısının süresini ölçüp obs.frame_done(süre), olay üretilince obs.EVENTS.labels(CAMERA_ID, tip).inc(), okuyucu yeniden bağlandığında obs.RECONNECTS.labels(CAMERA_ID).inc().

Kalp atışının neden kare işlendiğinde değil döngünün her turunda yazıldığı önemli. Kamera kapandığında süreç sağlıklıdır, sorun kameradadır; kalp atışı kareye bağlı olsaydı konteyner her kamera arızasında yeniden başlatılır, bu da kamerayı düzeltmezdi. Kubernetes belgesi de liveness kontrolünün dış bağımlılıklara bağlanmasının zincirleme yeniden başlatmalara yol açabileceği konusunda uyarıyor. Kamera kesintisi metrikten alarm olarak izleniyor.

Adım 3: docker compose ile kamera başına servis

# Kamera başına bir servis; ortak ayarlar YAML çapası (x-analytics) ile tek yerde.
x-analytics: &analytics
  image: registry.example.com/cx/video-analytics:1.4.2
  restart: unless-stopped
  env_file: common.env        # SITE, MODEL_PATH, MQTT_HOST, MQTT_CA
  volumes:
    - ./certs/ca.crt:/etc/cx/ca.crt:ro
    - ./models:/app/models:ro   # bu makinede üretilmiş best.engine
  deploy:
    resources:
      reservations:
        devices:
          - driver: nvidia
            device_ids: ["0"]
            capabilities: [gpu]
  logging:
    driver: json-file
    options: { max-size: "10m", max-file: "3" }

services:
  cam-01:
    <<: *analytics
    environment:
      CAMERA_ID: cam-01
      RTSP_URL: ${CAM01_RTSP_URL}
      MQTT_USER: cam-01
      MQTT_PASS: ${CAM01_MQTT_PASS}
      PROCESS_FPS: "8"
  cam-02:
    <<: *analytics
    environment:
      CAMERA_ID: cam-02
      RTSP_URL: ${CAM02_RTSP_URL}
      MQTT_USER: cam-02
      MQTT_PASS: ${CAM02_MQTT_PASS}
      PROCESS_FPS: "15"

Ortak ayarlar YAML çapasıyla (x-analytics) tek yerde; her kamera yalnızca kendi kimliğini, akış adresini ve MQTT bilgisini ekliyor. Parolalar common.env’e değil, Compose’un değişken yerine koyma için okuduğu .env dosyasına giriyor; böylece bir kameranın akış adresi öteki konteynerin ortamına düşmüyor. .env sürüm kontrolüne girmemeli.

Docker belgesine göre capabilities alanı zorunlu, count ile device_ids ise birlikte kullanılamıyor. Docker, aynı GPU’yu paylaşan konteynerler arasında bellek sınırı koymaz; her konteyner GPU belleğinin tamamını kullanabilir, bu yüzden kamera sayısını bellek ölçümüyle belirleyin.

Motoru sahadaki makinede aynı imajla bir kez üretip başlatmak:

docker run --rm --gpus all --user "$(id -u):$(id -g)" -v "$PWD/models:/app/models" \
  --entrypoint yolo registry.example.com/cx/video-analytics:1.4.2 \
  export model=/app/models/best.pt format=engine imgsz=640 quantize=16 nms=False
docker compose up -d
docker compose ps        # STATUS sütununda (healthy) görünmeli

Adım 4: Kubernetes’te GPU: device plugin ve GPU Operator

Kubernetes GPU’yu tanımaz; düğümdeki GPU’ları nvidia.com/gpu kaynağı olarak duyuran NVIDIA device plugin gerekir. NVIDIA GPU Operator bunu ve sürücü, container toolkit, GPU Feature Discovery, DCGM Exporter ve MIG Manager bileşenlerini tek Helm kurulumuyla getiriyor:

helm repo add nvidia https://helm.ngc.nvidia.com/nvidia && helm repo update
helm install --wait --generate-name -n gpu-operator --create-namespace \
  nvidia/gpu-operator --version=v26.7.1
# Sürücü ve toolkit hostta zaten kuruluysa: --set driver.enabled=false --set toolkit.enabled=false

Kubernetes belgesindeki kurallar: GPU yalnızca limits altında istenir, requests verilirse limits’e eşit olmalıdır ve kesirli GPU istenemez. Kamera başına Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: va-cam-07
  namespace: video-analytics
  labels: { app: video-analytics, camera: cam-07 }
spec:
  replicas: 1
  strategy:
    type: Recreate            # aynı kamerayı iki pod aynı anda saymasın
  selector:
    matchLabels: { app: video-analytics, camera: cam-07 }
  template:
    metadata:
      labels: { app: video-analytics, camera: cam-07 }
    spec:
      # runtimeClassName: nvidia   # k3s + ayrı kurulan device plugin'de gerekli (aşağıya bakın)
      nodeSelector:
        nvidia.com/gpu.present: "true"
      terminationGracePeriodSeconds: 30
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
      containers:
        - name: analytics
          image: registry.example.com/cx/video-analytics:1.4.2
          env:
            - { name: CAMERA_ID, value: cam-07 }
            - { name: SITE, value: gebze-01 }
            - { name: PROCESS_FPS, value: "8" }
            - { name: MODEL_PATH, value: /app/models/best.engine }
            - { name: MQTT_HOST, value: mosquitto.iot.svc }
            - { name: MQTT_CA, value: /etc/cx/ca.crt }
            - { name: MQTT_USER, value: cam-07 }
            - name: RTSP_URL
              valueFrom: { secretKeyRef: { name: cam-07-rtsp, key: url } }
            - name: MQTT_PASS
              valueFrom: { secretKeyRef: { name: mqtt-cam-07, key: password } }
          ports:
            - { name: metrics, containerPort: 9108 }
          resources:
            requests: { cpu: "1", memory: 1Gi }
            limits:
              memory: 3Gi
              nvidia.com/gpu: 1       # GPU yalnızca limits'te; tam sayı olmalı
          startupProbe:               # model yükleme ve TensorRT hazırlığı için 5 dakikaya kadar süre
            exec:
              command: ["python", "-c", "import os,sys,time; sys.exit(0 if time.time()-os.path.getmtime('/tmp/heartbeat')<30 else 1)"]
            periodSeconds: 10
            failureThreshold: 30
          livenessProbe:              # ana döngü 30 sn'dir dönmüyorsa (takıldıysa) yeniden başlat
            exec:
              command: ["python", "-c", "import os,sys,time; sys.exit(0 if time.time()-os.path.getmtime('/tmp/heartbeat')<30 else 1)"]
            periodSeconds: 15
            failureThreshold: 4
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities: { drop: ["ALL"] }
          volumeMounts:
            - { name: models, mountPath: /app/models, readOnly: true }
            - { name: ca, mountPath: /etc/cx, readOnly: true }
            - { name: tmp, mountPath: /tmp }
      volumes:
        - name: models              # motor bu düğümde, bu GPU için üretildi
          hostPath: { path: /opt/cx/models, type: Directory }
        - name: ca
          secret:
            secretName: mqtt-ca
            items: [{ key: ca.crt, path: ca.crt }]
        - name: tmp
          emptyDir: {}

Gizli değerler manifeste yazılmıyor:

kubectl create namespace video-analytics
kubectl -n video-analytics create secret generic cam-07-rtsp --from-literal=url='rtsp://<kullanici>:<parola>@10.0.0.21:554/stream2'
kubectl -n video-analytics create secret generic mqtt-cam-07 --from-literal=password='<parola>'
kubectl -n video-analytics create secret generic mqtt-ca --from-file=ca.crt=./certs/ca.crt

Manifestteki kararlar:

  • strategy: Recreate. Varsayılan RollingUpdate, yeni pod hazır olana kadar eskisini çalıştırır: aynı kamerayı iki pod sayar ve çift olay üretir. Üstelik boşta GPU yoksa yeni pod Pending kalır ve güncelleme takılır. Recreate birkaç saniyelik boşluğu kabul edip bu iki sorunu ortadan kaldırıyor.
  • CPU limiti yok, bellek limiti var. CPU limiti, kod çözme iş parçacıklarını kısıp gecikme yaratabilir; benim tercihim CPU’da yalnızca istek (request), bellekte ise sınır.
  • nodeSelector, GPU Feature Discovery’nin koyduğu nvidia.com/gpu.present etiketini kullanıyor.
  • startupProbe, modelin yüklenmesine ve TensorRT hazırlığına 5 dakikaya kadar süre tanıyor; başarılı olana kadar liveness devreye girmiyor.
  • readOnlyRootFilesystem ile imaj salt okunur; yazılabilir tek yer /tmp.
  • GPU Operator’ün güncel sürümlerinde CDI (Container Device Interface) varsayılan olarak açık ve Operator artık nvidia runtime’ını varsayılan yapmıyor; bu yüzden örnekte runtimeClassName yorum satırında. k3s’te durum farklı (aşağıda).

Adım 5: Tek GPU’yu paylaşmak: time-slicing, MPS, MIG

Varsayılan durumda bir pod bir GPU’yu tek başına alır. On iki kamera podu için on iki GPU gerekmemesi için paylaşım gerekiyor.

Time-slicing, GPU’yu belirli sayıda kopya olarak duyurur:

apiVersion: v1
kind: ConfigMap
metadata:
  name: time-slicing-config-all
  namespace: gpu-operator
data:
  any: |-
    version: v1
    flags:
      migStrategy: none
    sharing:
      timeSlicing:
        resources:
          - name: nvidia.com/gpu
            replicas: 4
kubectl create -n gpu-operator -f time-slicing-config.yaml
kubectl patch clusterpolicies.nvidia.com/cluster-policy -n gpu-operator --type merge \
  -p '{"spec": {"devicePlugin": {"config": {"name": "time-slicing-config-all", "default": "any"}}}}'

Bu yapılandırmayla her fiziksel GPU dört nvidia.com/gpu olarak görünür. NVIDIA belgesinin açık uyarıları: kopyalar arasında bellek ve hata yalıtımı yok; birden fazla kopya istemek orantılı işlem gücü garanti etmez; time-slicing açıkken DCGM Exporter metrikleri konteynerlerle ilişkilendiremiyor. Bir pod GPU belleğini doldurursa aynı GPU’daki diğerleri de etkilenir.

MPS, device plugin’de deneysel olarak destekleniyor; her istemcinin belleğini eşit bir payla ve işlem kapasitesini de belirli bir sınırla kısıtlıyor, MIG etkin cihazlarda çalışmıyor.

MIG (Multi-Instance GPU), destekleyen GPU’ları donanım düzeyinde bellek ve hata yalıtımı olan bölümlere ayırıyor. GPU Operator’de mig.strategy (single ya da mixed) seçilip düğüme nvidia.com/mig.config etiketiyle profil veriliyor; podlar nvidia.com/mig-1g.10gb gibi kaynaklar istiyor. Yalnızca MIG destekleyen GPU’larda var.

YöntemBellek yalıtımıHata yalıtımıNerede
Time-slicingYokYokHer NVIDIA GPU
MPS (deneysel)Eşit pay sınırıSınırlıMIG kapalı GPU’lar
MIGVarVarMIG destekleyen GPU’lar

Yeni bir seçenek: GPU Operator 26.7.1 sürüm notlarına göre R615 ve üstü sürücüyle NVIDIA_GPU_MEMORY_REQUEST ve NVIDIA_GPU_MEMORY_LIMIT ortam değişkenleri (MiB) konteyner başına yumuşak ve sert CUDA bellek sınırı koyabiliyor. Time-slicing’in bellek yalıtımı eksiğini kapatabilecek bir özellik; henüz yeni, kendi sürücü ve sürüm kombinasyonunuzda test etmeden güvenmeyin.

Adım 6: Sahada hafif Kubernetes: k3s

Mağaza ya da fabrikadaki tek GPU sunucusunda tam bir Kubernetes kümesi ağır kalır. k3s, tek ikili dosya halinde gelen, tam uyumlu bir Kubernetes dağıtımı; çok lokasyonda aynı manifestleri ve aynı geri alma yöntemini kullanmayı sağlıyor. Tek sunuculu, tek lokasyonlu bir kurulumda ise Compose çoğu zaman yeterli.

k3s belgesine göre sıra şöyle: hosta NVIDIA sürücüsü ve container runtime kurulur, k3s kurulur (ya da yeniden başlatılır); k3s runtime’ı kendisi bulup containerd yapılandırmasına ekler ve nvidia RuntimeClass’ını hazır getirir.

curl -sfL https://get.k3s.io | sh -
sudo grep nvidia /var/lib/rancher/k3s/agent/etc/containerd/config.toml   # runtime bulundu mu?
helm repo add nvdp https://nvidia.github.io/k8s-device-plugin && helm repo update
helm upgrade -i nvdp nvdp/nvidia-device-plugin -n nvidia-device-plugin --create-namespace \
  --set runtimeClassName=nvidia --set-file config.map.config=dp-time-slicing.yaml

dp-time-slicing.yaml, yukarıdaki ConfigMap’in any anahtarındaki içerikle aynı. Varsayılan runtime değiştirilmediyse podların runtimeClassName: nvidia taşıması gerekiyor; Deployment’taki yorum satırını açın. Dar hatlı sahalarda imaj çekmek de bir konu: k3s, /etc/rancher/k3s/registries.yaml ile yerel bir ayna (mirror) kayıt defterinden çekecek şekilde yapılandırılabiliyor.

Adım 7: İzleme: Prometheus ve DCGM Exporter

İzleme iki katmanlı. GPU katmanını DCGM Exporter veriyor; GPU Operator ile varsayılan olarak geliyor, tek başına kurulduğunda 9400 portunda /metrics yayınlıyor. Uygulama katmanını servisin kendi metrikleri veriyor. Prometheus Operator kullanıyorsanız servis metrikleri için:

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: video-analytics
  namespace: video-analytics
spec:
  selector:
    matchLabels: { app: video-analytics }
  podMetricsEndpoints:
    - port: metrics
      interval: 30s

Alarm ve panel için kullandığım sorgular:

# Kameradan 60 sn'dir kare işlenmiyor (kamera, ağ ya da akış sorunu)
time() - va_last_frame_timestamp_seconds > 60
# Kamera başına işlenen kare hızı
rate(va_frames_processed_total[5m])
# Kamera başına p95 çıkarım süresi
histogram_quantile(0.95, sum by (le, camera) (rate(va_inference_seconds_bucket[5m])))
# GPU bellek doluluğu (yaklaşık)
DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE)
# Donanım kod çözücü kullanımı ve son XID hatası
DCGM_FI_DEV_DEC_UTIL
DCGM_FI_DEV_XID_ERRORS > 0

DCGM Exporter’ın varsayılan sayaç listesinde GPU kullanımı, bellek, sıcaklık, güç, kodlayıcı/kod çözücü kullanımı ve XID hataları var. Kamera analitiğinde kod çözücü kullanımını mutlaka izleyin: benim deneyimimde kamera sayısı arttığında darboğaz sık sık çıkarımda değil, kod çözmede çıkıyor.

Adım 8: Güncelleme stratejisi

  • Değişmez etiketler. 1.4.2 gibi sürüm etiketleri kullanın, latest kullanmayın; bir etiketi başka bir imaja taşımayın.
  • İmaj ve model ayrı sürümlenir. Motor dosyasını tarihle adlandırın (best-2026-09-25.engine), MODEL_PATH ile seçin. Geri almak eski dosyayı göstermek kadar kolay olur.
  • Kamera kamera kanarya. Önce tek bir kameranın Deployment’ını güncelleyin, işlenen kare hızını, p95 süreyi ve olay sayılarını bir süre izleyin, sonra ötekilere geçin.
  • Motoru hedefte yeniden üretin. Sürücü, TensorRT ya da GPU değiştiğinde motoru o donanımda yeniden derlemek sürüm hattının parçası olmalı.
  • Geri alma komutu hazır olsun:
kubectl -n video-analytics set image deployment/va-cam-07 analytics=registry.example.com/cx/video-analytics:1.4.3
kubectl -n video-analytics rollout status deployment/va-cam-07
kubectl -n video-analytics rollout undo deployment/va-cam-07     # sorun çıkarsa

Sürücü ve GPU Operator güncellemelerini düğümü boşaltarak (drain) ve bakım penceresinde yapın; kameralar düğümde tek olduğu için bu, o lokasyonda analitiğin kısa süre durması demek. MQTT tarafında olayların güvenli taşınması için MQTT ile güvenli IoT yazısına bakabilirsiniz.

Kontrol listesi

  • Hostta sürücü ve NVIDIA Container Toolkit kurulu; docker run --gpus all ... nvidia-smi çalışıyor.
  • İmaj çok aşamalı, kök olmayan kullanıcıyla çalışıyor, sürümler sabit; model imajın dışında.
  • TensorRT motoru hedef makinede, imajdaki TensorRT sürümüyle üretildi.
  • Parolalar ve akış adresleri Secret ya da .env içinde, sürüm kontrolünde değil.
  • GPU yalnızca limits altında isteniyor; bellek sınırı ve CPU isteği tanımlı.
  • Kamera podlarında Recreate stratejisi; çift sayım yok.
  • GPU paylaşım yöntemi (time-slicing, MPS, MIG) yalıtım ihtiyacına göre seçildi; bellek ölçüldü.
  • Liveness kontrolü ana döngüye bağlı, kamera kesintisi alarmla izleniyor.
  • DCGM Exporter ve servis metrikleri Prometheus’ta; kod çözücü kullanımı panelde.
  • Güncelleme önce tek kamerada deneniyor; geri alma komutu ve model dosyası geçmişi hazır.

Sık sorulan sorular

Docker konteynerinde GPU nasıl kullanılır?

Hosta NVIDIA sürücüsü ve NVIDIA Container Toolkit kurulur, sudo nvidia-ctk runtime configure --runtime=docker ile Docker yapılandırılıp yeniden başlatılır. Ardından docker run --gpus all ile ya da Compose'da deploy.resources.reservations.devices altında driver: nvidia ve capabilities: [gpu] tanımlanarak konteynere GPU verilir.

Kubernetes'te GPU nasıl istenir?

Düğümde NVIDIA device plugin (tek başına ya da GPU Operator ile) çalışmalıdır. Pod, resources.limits altında nvidia.com/gpu: 1 ister; GPU yalnızca limits'te belirtilir, requests verilirse limits'e eşit olmalıdır ve kesirli değer kabul edilmez.

Tek GPU birden fazla pod arasında nasıl paylaşılır?

Üç yol var: time-slicing ile GPU'yu belirli sayıda kopya olarak duyurmak (bellek ve hata yalıtımı yok), deneysel MPS desteğiyle belleği eşit paylara bölmek ya da MIG destekleyen GPU'ları donanım düzeyinde yalıtılmış bölümlere ayırmak.

Sahadaki tek sunucuda Kubernetes kullanmak mantıklı mı?

Tek sunucuda Docker Compose çoğu zaman yeterlidir. Çok lokasyonda aynı dağıtımı merkezden yönetmek, sağlık kontrolü ve geri almayı standartlaştırmak gerekiyorsa k3s gibi hafif bir dağıtım tek düğümde de işe yarar.

GPU kullanımı Prometheus ile nasıl izlenir?

NVIDIA DCGM Exporter, GPU kullanımı, bellek, sıcaklık, güç, kod çözücü kullanımı ve XID hataları gibi metrikleri varsayılan olarak 9400 portunda /metrics adresinden yayınlar. GPU Operator kurulumlarında DCGM Exporter varsayılan olarak gelir.

Kaynaklar

  1. NVIDIA Container Toolkit — Installation Guide docs.nvidia.com
  2. Docker Docs — GPU support in Docker Compose docs.docker.com
  3. Kubernetes — Schedule GPUs kubernetes.io
  4. NVIDIA GPU Operator — Time-Slicing GPUs in Kubernetes docs.nvidia.com
  5. K3s — Advanced Options (NVIDIA Container Runtime) docs.k3s.io
  6. NVIDIA DCGM — Install DCGM Exporter docs.nvidia.com

DockerKubernetesNVIDIA GPU Operatork3sPrometheusDCGMGPU paylaşımı Markdown sürümü

İletişim

Konuşalım.