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şım | Artı | Eksi |
|---|---|---|
| Kamera başına süreç | Hata yalıtımı, basit kod, kamera bazında güncelleme | Her süreç modeli ayrıca yükler |
| Çok kameralı tek süreç | Toplu (batch) çıkarım, daha az bellek | Bir 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/modelsaltına bağlanıyor. - Sürümler sabit. Motoru üreten ve çalıştıran TensorRT sürümü aynı olmalı;
latestetiketi ve sabitlenmemiş paketler bu eşleşmeyi bozar. NVIDIA_DRIVER_CAPABILITIESiçindekivideo, konteynerin donanım video kod çözücüsüne erişmesi için gerekli;computeCUDA,utilityisenvidia-smiiç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 podPendingkalı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ğunvidia.com/gpu.presentetiketini 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.readOnlyRootFilesystemile 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
nvidiaruntime’ını varsayılan yapmıyor; bu yüzden örnekteruntimeClassNameyorum 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öntem | Bellek yalıtımı | Hata yalıtımı | Nerede |
|---|---|---|---|
| Time-slicing | Yok | Yok | Her NVIDIA GPU |
| MPS (deneysel) | Eşit pay sınırı | Sınırlı | MIG kapalı GPU’lar |
| MIG | Var | Var | MIG 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.2gibi sürüm etiketleri kullanın,latestkullanmayı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_PATHile 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
.enviçinde, sürüm kontrolünde değil. - GPU yalnızca
limitsaltında isteniyor; bellek sınırı ve CPU isteği tanımlı. - Kamera podlarında
Recreatestratejisi; ç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
- NVIDIA Container Toolkit — Installation Guide docs.nvidia.com
- Docker Docs — GPU support in Docker Compose docs.docker.com
- Kubernetes — Schedule GPUs kubernetes.io
- NVIDIA GPU Operator — Time-Slicing GPUs in Kubernetes docs.nvidia.com
- K3s — Advanced Options (NVIDIA Container Runtime) docs.k3s.io
- NVIDIA DCGM — Install DCGM Exporter docs.nvidia.com