VirtoMir: Bir Hobi Projesinin Dürüst Teknik Dökümü
KVM/libvirt için yazdığım web paneli VirtoMir'in nasıl çalıştığı, neyi yapıp neyi yapmadığı, AI ajanlarıyla nasıl yazıldığı ve neden hâlâ başka gözlere ihtiyaç duyduğu.
English version: VirtoMir: An Honest Technical Teardown of a Hobby Project
1. Giriş
VirtoMir, KVM / QEMU / libvirt için yazdığım bir web kontrol paneli. Mevcut bir Linux sunucusuna tek bir binary olarak kuruluyor. Ayrı bir dağıtım, veritabanı ya da sanal makinelerin içinde ajan istemiyor.
Bu yazının amacı projeyi pazarlamak değil. Böyle projelerden çok var ve bazıları VirtoMir’den çok daha olgun. Burada şunları anlatmak istiyorum:
- VirtoMir’in içeride gerçekte nasıl çalıştığı,
- neleri yaptığı ve neleri yapmadığı,
- nasıl yazıldığı (kısa cevap: tek kişi ve AI kodlama ajanları),
- ve neden başka insanların test etmesine, eleştirmesine ve koduna bakmasına ihtiyaç duyduğu.
Başta söylemem gereken birkaç şey var:
- Sürüm 0.9, erken bir sürüm. Ubuntu 26.04 üzerinde tek host’larda ve küçük kümelerde test edildi. Birçok özellik gerçek donanımda hiç denenmedi. Hangileri olduğunu aşağıda tek tek yazdım.
- Proje çok hızlı büyüdü. GitHub’daki ilk commit 12 Ekim 2026, v0.9.0 ise 19 Ekim 2026 tarihli. Bu sürede yaklaşık 96 bin satır Go (35 bini test) ve 46 bin satır Svelte/TypeScript yazıldı. Bu hız ancak AI ajanlarıyla mümkün oldu. Bu da kodun bir insanın satır satır yazıp yıllarca olgunlaştırdığı bir kod gibi değerlendirilmemesi gerektiği anlamına geliyor.
- Aktif geliştirmeye bir süre ara verdim. Issue’lar ve pull request’ler açık, ama cevaplar gecikebilir.
Production’da kullanmadan önce test edin. Bu cümleyi ciddi anlamda yazıyorum.

2. Neden bir tane daha?
Açık konuşayım: KVM yönetmek için çoğu kişinin VirtoMir’e ihtiyacı yok. Ekosistem kalabalık ve sağlam seçenekler var.
| Araç | Ne | Güçlü yanı | VirtoMir’den farkı |
|---|---|---|---|
| VMware vSphere (ESXi + vCenter) | Kurumsal sanallaştırma platformu | Sektör standardı: vMotion, DRS, HA, vSAN, geniş ekosistem ve destek | Ticari ve kapalı kaynak. Broadcom satın alması sonrası lisans modeli değişti, birçok kurum alternatif arıyor |
| Microsoft Hyper-V / Azure Local | Windows tabanlı hipervizör | Windows ve Active Directory ile derin entegrasyon | Windows ekosistemine bağlı, KVM değil |
| Nutanix AHV | KVM tabanlı hiper-yakınsak platform | Olgun HCI, Prism ile merkezi yönetim, ticari destek | Kendi yazılım yığını olarak kuruluyor, ticari |
| Proxmox VE | Debian tabanlı sanallaştırma dağıtımı (KVM + LXC) | Yıllardır production’da, corosync tabanlı küme, HA, Ceph entegrasyonu, Proxmox Backup Server, SDN, ticari destek | İşletim sisteminin kendisi. Mevcut bir sunucuya “eklenmez”, sunucu Proxmox olur |
| Cockpit + cockpit-machines | Linux sunucu paneline libvirt eklentisi | Hafif, dağıtımlarla birlikte geliyor, Red Hat destekli | Temel ayarlara odaklanıyor. İleri ayarlar için virsh / XML gerekiyor. Çoklu host, HA yok |
| virt-manager | Masaüstü libvirt arayüzü | Çok detaylı, olgun | Masaüstü uygulaması, web erişimi yok |
| Incus (ve LXD) | Sistem container’ları ve VM’ler için yönetici | Olgun kümeleme, iyi bir API, web arayüzü | libvirt kullanmıyor, QEMU’yu kendisi yönetiyor. Mevcut libvirt makinelerinizi görmez |
| oVirt | RHV’nin upstream’i, kurumsal KVM yönetimi | Kapsamlı kurumsal özellikler | Red Hat büyük ölçüde çekildi, geliştirme temposu düşük, kurulumu ağır |
| Harvester | Kubernetes, KubeVirt ve Longhorn üzerine HCI | Bulut-yerel, SUSE destekli | Kendi işletim sistemi olarak kuruluyor, Kubernetes bilgisi istiyor |
| XCP-ng + Xen Orchestra | Xen tabanlı platform | Olgun, ticari destek var | KVM değil, Xen |
| OpenStack, CloudStack, OpenNebula | Bulut orkestrasyonu | Büyük ölçek, çok kiracılı yapı | Küçük kurulumlar için ağır |
| WebVirtCloud | Django tabanlı libvirt web paneli | Basit, kullanıcılara VM devredebiliyor | Daha dar özellik seti |
Proxmox’a ihtiyacınız varsa Proxmox kullanın. Yıllardır production’da çalışan bir küme ve HA altyapısını bir haftalık bir projeyle değiştirmek mantıklı değil.
VirtoMir’in denediği boşluk dar:
- Zaten çalışan bir Debian, Ubuntu ya da Fedora sunucunuz var ve onu yeniden kurmak istemiyorsunuz.
- Cockpit’ten daha detaylı, virt-manager gibi ama tarayıcıdan erişilen bir arayüz istiyorsunuz.
- libvirt’in her ayarına ulaşmak, ama yeni başlayan biri için de basit bir yol istiyorsunuz.
Hedef “Cockpit kadar kolay, ham libvirt XML’i kadar detaylı”. Bu hedefe ne kadar ulaştığını kullananlar söyleyecek.
3. Nasıl yazıldı: tek kişi ve AI ajanları
VirtoMir’i AI kodlama ajanlarıyla birlikte yazdım. Bunu README’de de açıkça söylüyorum ve burada biraz daha detaylı anlatmak istiyorum, çünkü kodu değerlendirmek isteyen biri için en önemli bilgi bu.
Çalışma şekli:
- Repo kökündeki
AGENTS.md, ajanlar için giriş noktası. Kod haritası, çalışma kuralları, build ve test komutları orada. İnsanlar için de kodun en iyi haritası o dosya. - Mimari kararlar ADR (Architecture Decision Record) olarak tutuluyor. Kod yorumları bunlara
ADR-00xxdiye atıf yapıyor. Kodda 34 farklı ADR numarası geçiyor. - Ancak ADR’ler, vizyon ve oturum notları yerel, gitignore’lu bir
.ai/klasöründe duruyor ve yayımlanmadı. Bu bir eksiklik: koddaki birADR-0020atfının gerekçesini dışarıdan okuyamıyorsunuz.
Bu yöntemin sonuçları iki yönlü:
İyi tarafı: Tek bir kişinin aylarca uğraşacağı bir kapsamı bir haftada denemek mümkün oldu. Testler de aynı hızla yazıldı: 137 test dosyasında yaklaşık 748 Go test fonksiyonu var. CI her push’ta go test -race, go vet, Svelte tip kontrolü, vitest ve shellcheck çalıştırıyor.
Dikkat edilmesi gereken tarafı:
- Kodun tamamını bir insan satır satır incelemedi. Ben tasarımı yönlendirdim, kararları verdim ve test ettim, ama 140 bin satırın her birini okuduğumu iddia etmem dürüst olmaz.
- Kod tabanında neredeyse hiç
TODOya daFIXMEyok. İnsan elinden çıkmış bu büyüklükte bir projede bu alışılmadık bir durum ve bence kodun bitmiş olduğunu değil, nasıl üretildiğini gösteriyor. Eksikler yorumlarda değil, bu yazıda ve issue’larda. - Testlerin hepsi birim testi. Gerçek bir libvirtd, QEMU, OVN, ZFS, BMC ya da vCenter’a karşı çalışan entegrasyon testi yok (Bölüm 13).
Bu yüzden kodu incelemek isteyen birine önerim: README’deki özellik listesini değil, bu yazıdaki “test edilmedi” ve “eksik” notlarını esas alın.
4. Mimari
VirtoMir, root olarak çalışan tek bir Go süreci (virtomird). Svelte 5 ile yazılmış arayüz derleme sırasında binary’nin içine gömülüyor (web/embed.go, //go:embed all:dist).
flowchart LR
B[Tarayıcı<br/>Svelte 5 SPA] -- "HTTPS :8420<br/>REST + SSE + WebSocket" --> D
subgraph H[Host]
D[virtomird<br/>tek Go binary, root] -- "libvirt RPC<br/>unix socket" --> L[libvirtd]
L --> Q[QEMU/KVM süreçleri]
D -- "okur/yazar" --> S[(/var/lib/virtomir<br/>JSON, JSONL, gob)]
D -- "harici araçlar" --> T[qemu-img, restic,<br/>tcpdump, virt-v2v, zfs ...]
end
D <-- "sabitlenmiş TLS<br/>peer anahtarı" --> P[Diğer küme üyeleri<br/>virtomird]
Temel tercihler:
- libvirt’e saf Go ile bağlanıyor. cgo libvirt bağlamaları yerine
digitalocean/go-libvirtRPC kütüphanesi kullanılıyor. Bağlantı/var/run/libvirt/libvirt-socküzerinden açılıyor ve her çağrıdan önce kopup kopmadığı kontrol ediliyor. Koptuysa yeniden bağlanıyor. - Veritabanı yok. Her alt sistem kendi durumunu
-state-dir(varsayılan/var/lib/virtomir) altında bir JSON dosyasında tutuyor. Aktivite kaydı JSONL, metrik geçmişi gob formatında. - Makineye ait bilgiler makinenin kendisinde. Bir VM’in template olup olmadığı, hangi template’ten türediği, sahibi, projesi gibi bilgiler domain XML’inin
<metadata>bölümünde, VirtoMir’e ait bir namespace altında saklanıyor. Bu sayede bu bilgivirsh dumpxmlile birlikte taşınıyor ve ayrı bir veritabanıyla tutarlı tutulması gerekmiyor. - API, OpenAPI ile tanımlı. Yaklaşık 326 route var (126 GET, 113 POST, 51 PUT, 36 DELETE). OpenAPI 3.1 dokümanı kayıtlı route’lardan çalışma anında üretiliyor ve
/api/v1/openapi.jsonadresinden sunuluyor.
Açılışta yaklaşık 25 alt sistem aynı süreç içinde başlıyor: canlı metrik hub’ı, alarm motoru, zamanlayıcı, küme senkronizasyonu, HA yöneticisi, DRS, konfigürasyon geçmişi vb. Çoğu alt sistemin durum dosyası okunamazsa daemon yalnızca uyarı yazıp o özellik olmadan çalışmaya devam ediyor. Sadece kimlik doğrulama ve TLS kurulumu hataları ölümcül.
Bu tasarımın bedeli de var:
- Bütün özellikler aynı root süreçte. Birindeki bir hata hepsini etkileyebilir.
- Oturumlar, giriş denemesi sayaçları ve arka plan işlerinin (clone, backup) durumu bellekte tutuluyor. Daemon yeniden başlarsa herkes oturumdan çıkar ve çalışan bir işin durumu kaybolur.
- Durum dosyalarının çoğu atomik yazılıyor (geçici dosya +
rename), ama yalnızca bir kısmıfsyncçağırıyor. Durum dizini için bir kilit dosyası da yok, yani iki daemon’ın aynı dizini kullanmasını engelleyen bir şey yok.
5. libvirt ile konuşmak: “her ayar erişilebilir” ne demek?
VirtoMir’deki her düzenleme aynı döngüyü izliyor:
DomainGetXMLDescile domain’in inactive XML’ini al.libvirt.org/go/libvirtxmlile Go struct’ına çöz.- Struct’ı değiştir.
- Tekrar XML’e çevir.
DomainDefineXMLFlags(doc, DomainDefineValidate)ile tanımla. libvirt XML’i kendi RNG şemasına göre doğrular.
Ayarlar formu bu döngünün üzerine kurulu. Kaydetmeden önce “değişiklikleri gözden geçir” adımı, eski ve yeni XML’i aynı libvirtxml yolundan geçirip satır satır diff gösteriyor. Böylece diff’te yalnızca gerçek değişiklikler görünüyor, biçimlendirme farkları görünmüyor.

Makine çalışıyorsa vCPU sayısı (DomainSetVcpusFlags), balloon belleği (DomainSetMemoryFlags) ve scheduler parametreleri canlı uygulanıyor. Geri kalan her şey bir sonraki açılışta geçerli oluyor ve arayüz hangisinin hangisi olduğunu söylüyor.
Burada abartmamam gereken bir nokta var: formlar libvirt’in her ayarını kapsamıyor. CPU topolojisi, bellek, firmware, boot sırası, pinning, huge pages, NUMA, cihazlar gibi sık kullanılan ayarların formu var. Geri kalan her şey için gerçek kaçış yolu XML editörü. Editör UUID ya da isim değişikliğini reddediyor, gerisini libvirt’in doğrulamasına bırakıyor. “Her ayar erişilebilir” iddiası, form + XML editörü birlikte düşünüldüğünde doğru.
Template ve linked clone. Linked clone için template’in diski backing store olarak kullanılan bir qcow2 overlay volume oluşturuluyor (StorageVolCreateXML). Template’in çalıştırılması engelleniyor, ona bağlı clone’lar varken silinmesi de engelleniyor.
6. Canlı izleme ve metrik geçmişi
Canlı veriler internal/live içindeki bir hub’dan geliyor:
- Hub saniyede bir host istatistiklerini, domain listesini ve toplu domain istatistiklerini (bulk stats) okuyor. libvirt’ten bir lifecycle olayı geldiğinde de (makine başladı, durdu, çöktü) hemen bir örnek alıyor.
- CPU yüzdesi, sayaç farklarından hesaplanıyor, vCPU başına da. Host disk ve ağ trafiği
/proc/diskstatsve/proc/net/devdosyalarından okunuyor. - Tarayıcıya Server-Sent Events (SSE) ile gidiyor. Yeni bağlanan bir istemci önce son 60 anlık görüntüyü (bir dakika) alıyor. Yavaş bir istemci eski kareleri atlıyor ve her zaman en yenisini görüyor.
Geçmiş için Prometheus gerekmiyor. Her seri için dört halka tampon (ring buffer) tutuluyor:
| Adım | Nokta | Kapsadığı süre |
|---|---|---|
| 10 sn | 2160 | 6 saat |
| 1 dk | 1440 | 24 saat |
| 10 dk | 1008 | 7 gün |
| 1 saat | 720 | 30 gün |
Değerler float32 olarak tutuluyor, seri başına yaklaşık 21 KB yer kaplıyor. Eksik noktalar NaN, yani durdurulmuş bir makinenin grafiğinde sıfır değil boşluk görünüyor. Geçmiş beş dakikada bir ve kapanışta history.gob dosyasına yazılıyor. Bu yazma işlemi fsync yapmıyor, yani ani bir elektrik kesintisinde son birkaç dakika kaybolabilir.
Prometheus kullananlar için /api/v1/metrics endpoint’i de var.
7. Konfigürasyon geçmişi
VirtoMir, bir makinenin tanımının her sürümünü saklıyor. virsh edit ile yapılan değişiklikler de dahil. Her sürümün diff’i görülebiliyor ve tek tıkla geri alınabiliyor.
Nasıl çalıştığı ve sınırları:
- VirtoMir üzerinden yapılan değişiklikler API’den doğrudan kaydediliyor ve kimin yaptığı biliniyor.
- Dışarıdan yapılan değişiklikleri yakalamak için libvirt olayları değil polling kullanılıyor. Her 20 saniyede bir bütün domain’lerin inactive XML’i okunuyor ve son kaydedilen sürümle karşılaştırılıyor.
- Bunun sonuçları:
- 20 saniye içinde yapılan birden fazla dış değişiklik tek bir sürüm olarak görünür.
virshile yapılan değişikliğin kim tarafından yapıldığı bilinmez.- Yalnızca canlı (live) tanımda yapılan değişiklikler görülmez.
- Çok sayıda VM olan bir host’ta 20 saniyede bir her VM’in XML’ini okumak gereksiz bir yük.
- Makine başına son 100 sürüm tutuluyor.
8. Yedekleme
Çalışan makineler için libvirt’in push modu backup API’si (DomainBackupBegin) kullanılıyor. Blockcopy ya da harici snapshot değil.
- Başlamadan önce guest agent varsa dosya sistemi donduruluyor (
DomainFsfreeze) ve iş başladıktan hemen sonra çözülüyor. - Bütün diskler qcow2 ise libvirt checkpoint’i (dirty bitmap) oluşturuluyor. Bir sonraki yedek yalnızca değişen blokları alıyor ve
qemu-img rebase -uile bir önceki yedeğe bağlanıyor. Belirli aralıklarla tam yedek alınıyor. - Kapalı makineler
qemu-img convertile kopyalanıyor. - Off-site kopyalar restic ile gönderiliyor: S3 uyumlu depolar, Backblaze B2, Azure, GCS, SFTP, REST sunucu ya da bir disk. Gönderilmeden önce zincir düzleştiriliyor.
Güvenlik açısından önemli bulduğum bir detay: root olarak çalışan bir süreçte qemu-img‘in disk formatını kendisinin tahmin etmesine izin vermek tehlikeli. Kötü niyetli bir imaj, backing file olarak /etc/shadow gibi bir dosyayı gösterebilir. VirtoMir formatı hiçbir zaman qemu-img’e tahmin ettirmiyor ve formatı kayıtlı olmayan ya da yedek klasörünün dışını gösteren backing zincirlerini reddediyor.
9. Tarayıcıda konsol
- VNC: Daemon, domain’in canlı XML’inden VNC adresini ya da socket’ini buluyor. Tarayıcıdan gelen WebSocket’i bu socket’e bağlıyor. İstemci tarafında noVNC var.
- SPICE: spice-html5 için her SPICE kanalına ayrı bir WebSocket açılıyor. Çözünürlük pencereye göre değişiyor, guest agent varsa pano paylaşılıyor.
- Seri konsol:
DomainOpenConsoleBidirectionalile açılıyor, xterm.js ile gösteriliyor. Açık birvirsh consoleoturumu varsa onu devralıyor.
Konsol en az operator rolü istiyor. Oturum süresi dolduğunda açık konsol 30 saniye içinde kapatılıyor.
10. Birden fazla host
Kümeleme, ayrı bir veritabanı ya da corosync gibi bir servis olmadan çalışıyor. Bu tasarımın hem kolaylığı hem de zayıf noktaları var.
Katılma
- Mevcut bir üye bir katılma kodu üretiyor:
vmj1-+ base64url(adres, TLS sertifika parmak izi, 16 baytlık tek kullanımlık sır). Kod 15 dakika geçerli ve yalnızca bellekte tutuluyor. - Yeni host, koddaki parmak izini sabitleyerek (pinning) üyeye bağlanıyor ve kendi bilgilerini gönderiyor.
- Davet eden üye yeni host’u kendi beyan ettiği adres ve parmak iziyle geri arıyor, sırrı tüketiyor, üye listesinin sürümünü bir artırıyor ve listeyi herkese gönderiyor.
Üyeler arası kimlik doğrulama
- Üyeler birbirinin TLS sertifikasını SHA-256 parmak iziyle sabitliyor. CA doğrulaması kullanılmıyor.
- Her istek
Authorization: VirtoMirPeer <nodeID>.<anahtar>başlığı taşıyor. Karşı taraf yalnızca anahtarın hash’ini saklıyor ve sabit zamanlı karşılaştırma yapıyor.
Diğer host’ları yönetmek
Arayüz başka bir host’a /api/v1/hosts/<id>/… yolu üzerinden erişiyor. Kullanıcının bağlı olduğu host isteği önce kendisi doğrulayıp yetkilendiriyor, sonra hedef üyeye reverse proxy ile iletiyor. REST, SSE ve konsol WebSocket’leri bu yoldan geçiyor. Kullanıcı adı ve rol, başlıklarla iletiliyor.
Burada önemli bir güven varsayımı var: hedef üye, ileten üyenin söylediği role güveniyor. Kullanıcıyı kendisi yeniden doğrulamıyor. Yani kümedeki üyeler birbirine tam güveniyor. Ele geçirilmiş tek bir üye, bütün kümede admin yetkisi demek.
“Her host’ta aynı ayar”
Her özelliğin (DRS, yerleşim kuralları, GPU havuzları, SDN, HA) kendi JSON dosyası ve bir tamsayı sürüm numarası var. Değişiklikler bazı durumlarda anında gönderiliyor. Ayrıca her üye 60 saniyede bir diğer üyelerden durumu çekiyor ve sürüm numarası daha yüksekse gelen durumu alıyor.
Bu yaklaşımın bilinen bir zayıflığı var: çakışma çözümü sadece “yüksek sürüm kazanır”. Vector clock, birleştirme ya da eşitlik bozucu yok. Bir ağ bölünmesi sırasında iki host aynı ayarı değiştirip ikisi de N+1 sürümüne ulaşırsa, hiçbiri diğerinin durumunu almaz ve bir sonraki değişikliğe kadar sessizce farklı kalırlar. Küçük ve kararlı bir ağda bu nadiren olur, ama bilinmesi gerekiyor.
11. Yüksek erişilebilirlik (HA)
HA, VirtoMir’in en riskli parçası. Gerçek BMC donanımında hiç denenmedi. Aşağıdaki açıklama kodun ne yapmaya çalıştığını anlatıyor, sahada nasıl davrandığını değil.

Bir host’un öldüğüne nasıl karar veriliyor?
- Her üye varsayılan olarak 2 saniyede bir diğer üyelere heartbeat gönderiyor.
- Varsayılan 30 saniye boyunca cevap vermeyen host ölü sayılıyor. Arayüz 3 kaçırılmış heartbeat’ten sonra “şüpheli” gösteriyor.
Quorum
oylar = bu host + zaman aşımı içinde cevap veren üyeler (+1 witness erişilebilirse)
toplam = üye sayısı (+1 witness tanımlıysa)
gerekli = toplam / 2 + 1
- Witness, TCP bağlantısı kabul eden herhangi bir
host:portolabilir (örneğin SSH için 22). - Quorum’u kaybeden host, korumalı makinelerini hemen kendisi kapatıyor (self-fence).
- Koordinatör, çevrimiçi üyeler içinde en küçük ID’ye sahip olan.
Fencing
Ölü sayılan bir host’un makineleri, o host’un gerçekten kapalı olduğundan emin olunmadan başka bir yerde başlatılmıyor. Sırasıyla şunlar deneniyor:
- Bir admin arayüzden host’un kapalı olduğunu onayladıysa (host hâlâ heartbeat gönderiyorsa reddediliyor).
- IPMI:
ipmitool -I lanplus … chassis power off. Parola ortam değişkeniyle geçiyor,psçıktısında görünmüyor. - Redfish:
ComputerSystem.ResetileForceOff. - IPMI ve Redfish için fence, ancak BMC gücün kapandığını bildirdiğinde başarılı sayılıyor.
- Ölü host’ta donanım watchdog’u (
/dev/watchdog) kuruluysa: koordinatör “son görülme + failover süresi + watchdog süresi + 15 sn” kadar (varsayılanlarla yaklaşık 105 saniye) bekliyor. Watchdog, host yalnızca quorum’dayken besleniyor, yani quorum’u kaybeden host’u kendisi yeniden başlatıyor. - Hiçbiri yoksa admin onayı bekleniyor.
Fence başarılı olduktan sonra koordinatör, korumalı makineleri öncelik sırasına göre (critical, high, normal, low) boş belleği yeterli olan bir üyede başlatıyor. Her üye korumalı makinelerinin XML’ini diğer üyelere düzenli olarak kopyalıyor ve her yeniden başlatmada bir epoch numarası artırılıyor. Geri dönen bir host, kendisinden daha yüksek epoch’lu bir kopyanın başka yerde çalıştığını görürse kendi eski kopyasını kapatıyor.
Bilinen zayıflıklar
- Depolama seviyesinde kilit yok. sanlock, virtlockd ya da SCSI reservation kullanılmıyor. Aynı NFS ya da RBD diskine iki kopyanın yazmasını engelleyen tek şey fencing’in doğru çalışması. Proxmox gibi olgun sistemlerle arasındaki en büyük fark bence bu.
- Witness olmayan iki node’lu bir küme bir arızayı atlatamaz. Toplam 2, gerekli 2 oy. Kalan host’un 1 oyu var, quorum’u kaybediyor ve kendi korumalı makinelerini de kapatıyor. İki node kullanacaksanız witness şart.
- Witness olan iki node’lu bir kümede iki taraf da hâlâ witness’a ulaşabiliyorsa ikisi de kendini quorum’da sanabilir. İki tarafta da IPMI/Redfish tanımlıysa birbirlerini kapatmaya çalışabilirler. Watchdog yolu bu duruma karşı korumalı, BMC yolu değil.
- BMC parolaları her üyede
ha.jsoniçinde düz metin olarak duruyor (dosya izni 0600). - Hedef seçiminde yalnızca boş bellek kontrol ediliyor, CPU yükü ya da overcommit değil.
12. Canlı göç (live migration)
VirtoMir, libvirt’in istemci yönetimli v3 göç protokolünü kullanıyor. Her daemon yalnızca kendi host’undaki libvirtd ile konuşuyor ve libvirt’in cookie’lerini üyeler arasında sabitlenmiş HTTPS üzerinden taşıyor:
sequenceDiagram
participant S as Kaynak virtomird
participant LS as Kaynak libvirtd
participant T as Hedef virtomird
participant LT as Hedef libvirtd
S->>LS: Begin3
S->>T: POST /api/v1/migrate/prepare
T->>LT: Prepare3
S->>LS: Perform3 (QEMU → QEMU veri akışı)
S->>T: POST /api/v1/migrate/finish
T->>LT: Finish3
S->>LS: Confirm3
- Kullanılan bayraklar:
Live,PersistDest,UndefineSource,ChangeProtection,AutoConverge. Disklerin kopyalanması gerekiyorsaNonSharedDisk. - Paylaşımlı NFS’te diskler kopyalanmıyor. Paylaşımlı değilse diskler göç sırasında canlı kopyalanıyor.
- Farklı CPU’lar arasında göç için kümedeki bütün host’ların desteklediği bir CPU modeli öneriliyor (
ConnectBaselineHypervisorCPU).
Önemli bir eksik: bellek ve disk verisi QEMU’dan QEMU’ya şifresiz TCP üzerinden gidiyor. libvirt’in TLS göçü ya da tünelleme kullanılmıyor. Kontrol trafiği şifreli, veri trafiği değil. Göç trafiğinin ayrı ve güvenilir bir yönetim ağında olması gerekiyor. Bu, ilk düzeltilmesi gereken şeylerden biri.
13. Diğer küme özellikleri
Bunları kısa tutuyorum, çünkü çoğu deneysel:
- DRS: Host’ların CPU ve bellek yüzdelerinin standart sapmasına bakıyor. Eşik aşılırsa en yüklü host’tan en az yüklü host’a bir makine taşınmasını öneriyor. Tam otomatik modda bunu kendisi yapıyor (5 dakikada bir, makine başına 30 dakika bekleme). Basit bir sezgisel yöntem: önerilen taşımanın dengesizliği gerçekten azaltıp azaltmayacağını simüle etmiyor.
- Rolling upgrade: Host’lar sırayla bakıma alınıyor, makineler canlı taşınıyor, VirtoMir (ve isteğe bağlı olarak işletim sistemi paketleri) güncelleniyor, host geri geliyor. Bilinen bir eksik: “bakım modu” yalnızca upgrade planının içinde biliniyor. DRS ve HA bunu dikkate almıyor, yani güncellenen host’a makine geri yerleştirilebilir.
- VXLAN overlay: Birden fazla host’a yayılan tek bir L2 ağ, mevcut ağ üzerinden UDP 4789 ile. Subnet, DHCP ve NAT yalnızca ağı oluşturan host’ta. Bu host tek hata noktası ve yerine geçecek bir mekanizma yok. Underlay şifresiz.
- OVN üzerinde SDN: VPC, subnet, floating IP, security group, load balancer, IPsec. Gerçek bir OVN kümesinde hiç çalıştırılmadı. OVN’in kendisini sizin kurmanız gerekiyor.
- ZFS replikasyonu: Zamanlanmış
zfs send -i | ssh … zfs receive. Düz SSH kullanıyor, host anahtarını ilk bağlantıda kabul ediyor. - Felaket kurtarma (DR): Varsayılan olarak kapalı (
-dr-experimental). Kod bunu açıkça söylüyor: üretim host’una henüz erişilmiyor, gerçek failover her zaman “zorla” yapılıyor. Kullanılabilir durumda değil. - VMware’den içe aktarma: vCenter/ESXi envanterini okuyup
virt-v2vile dönüştürüyor. Gerçek VMware donanımında denenmedi.
14. Güvenlik modeli
VirtoMir root olarak çalışıyor, çünkü sistem libvirt socket’ine erişmesi ve PAM ile parola kontrol etmesi gerekiyor. Ağ ayarlarını değiştiren, paket güncelleyen ve tcpdump çalıştıran bir root süreç. Bunu ciddiye almak gerekiyor.
Bence doğru yapılanlar:
- Varsayılan olarak HTTPS (ilk açılışta self-signed ECDSA sertifika, minimum TLS 1.2).
- PAM ile Linux hesaplarıyla giriş. Roller Linux gruplarından geliyor: admin (
libvirt), operator, viewer. - LDAP / Active Directory ve OpenID Connect (state, PKCE, nonce ile). LDAP filtreleri escape ediliyor, şifresiz
ldap://reddediliyor. - TOTP ile iki adımlı giriş ve kurtarma kodları.
- Oturum çerezi
HttpOnly,SameSite=Strict. Değişiklik yapan isteklerdeOrigin/Sec-Fetch-Sitekontrolü. - API token’ları ve küme anahtarları yalnızca hash olarak saklanıyor.
- Aktivite kaydı SHA-256 ile zincirleniyor ve doğrulanabiliyor.
- Güncellemeler GitHub Releases’tan indiriliyor, imzalı SHA-256 dosyası gömülü OpenPGP anahtarıyla doğrulanıyor. apt/dnf depoları da imzalı.
- Her libvirt tanımı libvirt şemasına göre doğrulanıyor.
Eksik ya da zayıf olanlar:
- Arayüzde genel bir CSP,
X-Frame-Optionsya da HSTS başlığı yok. - Yetkilendirme, istek yolunun string eşleştirmesiyle yapılıyor. Listede olmayan her şey varsayılan olarak admin istiyor, bu iyi bir varsayılan, ama elle doğru tutulması kırılgan.
- Giriş kilitleme IP başına. Reverse proxy arkasında herkes aynı IP’den geldiği için bu, herkesi kilitlemenin bir yoluna dönüşebilir.
- Rate limiting yalnızca giriş ve 2FA endpoint’lerinde.
- TOTP sırları diskte düz metin, aynı kodun tekrar kullanılmasına karşı koruma yok.
- Aktivite kaydının hash zinciri anahtarsız. Root yetkisi olan biri dosyayı değiştirip zinciri yeniden hesaplayabilir.
- İmza anahtarı GitHub Actions secret’larında. GitHub hesabını ya da CI’ı ele geçiren biri geçerli imzalı bir güncelleme yayımlayabilir. Otomatik kurulum varsayılan olarak kapalı, ama güncelleme kontrolü 6 saatte bir açık.
- systemd sandbox’ı hafif (
NoNewPrivileges,PrivateTmp,ProtectHome=read-only).ProtectSystemyok, çünkü daemon netplan ve NetworkManager ayarlarını yazıyor.
Önerim: VirtoMir’i yalnızca bir yönetim ağında çalıştırın ya da 127.0.0.1 üzerinde dinletip kendi reverse proxy’nizin arkasına koyun. İki adımlı girişi açın ve kullanıcılara gereken en düşük rolü verin.
15. Testler ve CI
- 137 test dosyasında yaklaşık 748 Go testi var.
go test ./...libvirt olmadan her yerde çalışıyor. internal/virttestleri, libvirtxml struct’ları ve XML fixture’ları üzerinde çalışan saf fonksiyonları test ediyor.- API testleri sahte bir backend kullanıyor. Harici araçlar (ipmitool, zfs, ovn-nbctl, ip, bridge) bir arayüz arkasında. Testler hangi komutun çalışacağını kontrol ediyor ama komutu çalıştırmıyor.
- Bir OpenAPI sözleşme testi, parametresiz bütün GET endpoint’lerini çağırıp cevapların şemaya uyduğunu kontrol ediyor.
- CI:
go vet,go test -race, PAM ile cgo build, Svelte tip kontrolü, vitest, shellcheck ve Terraform provider testleri.
Eksik olanlar:
- Gerçek libvirt’e karşı entegrasyon testi yok. Bu, en büyük test açığı.
golangci-lint,govulncheck, CodeQL ya danpm auditCI’da çalışmıyor.- OpenAPI sözleşme testi POST/PUT isteklerini ve parametreli yolları kapsamıyor.
16. Ne destekliyor, ne desteklemiyor?
| Alan | Durum |
|---|---|
| Tek host’ta VM oluşturma, konsol, snapshot, yedek | Ubuntu 26.04 ve Debian 13’te test edildi |
| Ubuntu 24.04, Fedora / RHEL 10, arm64 | Paketler var, test eden yok |
| Küçük kümede canlı göç, cold move | Test edildi. Göç verisi şifresiz |
| HA (IPMI, Redfish, watchdog) | Gerçek BMC’de denenmedi, depolama kilidi yok |
| Ceph RBD, iSCSI, LVM, ZFS havuzları | Test eden yok |
| PCI/USB passthrough, vGPU, GPU havuzları | Gerçek GPU ile test eden yok |
| VXLAN overlay | Var, tek gateway, şifresiz |
| OVN SDN, IPsec | Deneysel, gerçek OVN’de çalıştırılmadı |
| DR (felaket kurtarma) | Deneysel, varsayılan kapalı, kullanılabilir değil |
| VMware içe aktarma | Deneysel, gerçek VMware’de denenmedi |
| Confidential computing (SEV/SEV-SNP/TDX) | Host destekliyorsa sunuluyor, sınırlı test |
| LXC sistem container’ları | Var, temel işlemler |
| Hiper-yakınsak Ceph (Proxmox gibi) | Yok, roadmap’te |
| SR-IOV, BGP/EVPN, IPAM | Yok, roadmap’te |
| Nesne bazında ince yetkiler | Yok (yalnızca roller, projeler ve kotalar) |
| Ticari destek | Yok |
| Terraform provider, Ansible koleksiyonu | Var, erken aşamada |
| Arayüz dili | İngilizce ve Türkçe |
Lisans AGPL-3.0. Proxmox VE de AGPL-3.0 ile lisanslı. Katkılar bir CLA ile kabul ediliyor, bu da ileride AGPL yükümlülükleri olmayan ticari bir lisansı mümkün kılıyor. Bunu gizli bir madde gibi değil, açıkça belirtmek istiyorum.
17. Kim denemeli, kim denememeli?
Denemek için mantıklı:
- Homelab’ında KVM çalıştıran ve virt-manager’ın detayını tarayıcıda isteyenler.
- libvirt’i bilen ve bir panelin libvirt üzerinde nasıl kurulabileceğini merak edenler.
- Kodu eleştirmek, özellikle güvenlik açısından incelemek isteyenler.
Şu an mantıklı değil:
- Production’da HA’ya güvenmesi gereken herkes.
- Ceph’li hiper-yakınsak bir küme kurmak isteyenler. Proxmox ya da Harvester’a bakın.
- Ticari destek ve uzun vadeli bakım garantisi isteyenler.
En güvenli deneme yolu çevrimiçi demo. Tamamen tarayıcıda, sahte bir üç host’lu kümeyle çalışıyor. Bir adım ötesi, tek komutla atılabilir bir VM’de kurmak:
git clone https://github.com/onurbaydas/VirtoMir && cd VirtoMir
sh dev/try/try.sh # Multipass ile atılabilir bir Ubuntu VM'i kurar
sh dev/try/try.sh --delete # işiniz bitince siler
18. Katkı vermek isterseniz
Proje GitHub’da açık kaynak olarak geliştiriliyor. Sanallaştırmaya, Go’ya ya da Svelte’e ilgi duyan biri katkı vermek isterse çok mutlu olurum. Tek bir kişinin ulaşamadığı yerler var. En çok işe yarayacak alanlar:
- Çalıştırın ve ne bozulduğunu söyleyin. Debian, Fedora, eski Ubuntu sürümleri, arm64. “Sorunsuz çalıştı” da faydalı bir rapor. Issue #1 bunun için açık.
- Güvenlik açısından kritik kodu inceleyin.
internal/authveinternal/api. Sorunları lütfen herkese açık issue yerine özel olarak bildirin. - Benim ulaşamadığım yolları test edin. Gerçek BMC’lerle HA, Ceph, GPU passthrough, VMware.
- Eleştirin. Kodu da fikri de. Bu yazıda yazdığım eksiklerin bir kısmını zaten biliyorum, bilmediklerimi duymak istiyorum.
- Kod katkısı. Good first issue etiketli işlerle başlayabilirsiniz. Bu yazıdaki eksikler de iyi başlangıç noktaları: TLS ile göç, depolama kilidi (virtlockd/sanlock), libvirt olaylarıyla konfigürasyon geçmişi, CSP başlıkları, entegrasyon testleri.
Fikirler ve sorular için: GitHub Discussions.
19. Sonuç
VirtoMir, “bir kişi AI ajanlarıyla bir haftada ne kadar ileri gidebilir?” sorusunun bir cevabı. Kapsam olarak çok ileri gidebiliyor. Olgunluk olarak gidemiyor. Olgunluk, gerçek donanımda, gerçek kullanıcılarla ve zamanla geliyor.
O yüzden bu yazıyı bir tanıtım değil, bir davet olarak yazdım. Kodun nasıl çalıştığını, nerede zayıf olduğunu ve neyin test edilmediğini olabildiğince açık yazmaya çalıştım. Bir şeyi yanlış ya da eksik anlattıysam, onu da duymak isterim.
Kaynaklar
- VirtoMir kaynak kodu: https://github.com/onurbaydas/VirtoMir
- Çevrimiçi demo: https://onurbaydas.github.io/VirtoMir/demo/
- Atılabilir bir VM’de deneme: https://github.com/onurbaydas/VirtoMir/blob/main/docs/try-in-a-vm.md
- Güvenlik politikası: https://github.com/onurbaydas/VirtoMir/blob/main/SECURITY.md
- libvirt domain XML formatı: https://libvirt.org/formatdomain.html
- libvirt backup API’si ve checkpoint’ler: https://libvirt.org/kbase/incrementalbackupinternals.html
- libvirt göç dokümantasyonu: https://libvirt.org/migration.html
- go-libvirt: https://github.com/digitalocean/go-libvirt
- libvirtxml: https://pkg.go.dev/libvirt.org/go/libvirtxml
- Proxmox VE: https://www.proxmox.com/en/products/proxmox-virtual-environment/overview
- Cockpit: https://cockpit-project.org/
- Incus: https://linuxcontainers.org/incus/
- oVirt: https://www.ovirt.org/
- Harvester: https://harvesterhci.io/
- XCP-ng: https://xcp-ng.org/
- WebVirtCloud: https://github.com/retspen/webvirtcloud