<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hakan İSMAİL</title>
    <description>The latest articles on DEV Community by Hakan İSMAİL (@hakanbaban53).</description>
    <link>https://dev.to/hakanbaban53</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3593379%2F63770104-9eda-408d-bbe7-f9d3072ffe6e.jpeg</url>
      <title>DEV Community: Hakan İSMAİL</title>
      <link>https://dev.to/hakanbaban53</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hakanbaban53"/>
    <language>en</language>
    <item>
      <title>Bir Hafta Sonu Projesi: AWS EC2 Üzerinde V2Ray (3X-UI) ve Cloudflare WARP ile Datacenter IP Engellerini Aşmak</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Mon, 14 Sep 2026 06:30:46 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/bir-hafta-sonu-projesi-aws-ec2-uzerinde-v2ray-3x-ui-ve-cloudflare-warp-ile-datacenter-ip-e0</link>
      <guid>https://dev.to/hakanbaban53/bir-hafta-sonu-projesi-aws-ec2-uzerinde-v2ray-3x-ui-ve-cloudflare-warp-ile-datacenter-ip-e0</guid>
      <description>&lt;p&gt;Bu hafta sonu, elimde AWS Free Tier'ın verdiği 6 aylık (181 gün), 200 dolara kadar kredi ve basit bir sorunla oturdum: kendi proxy sunucumu kurmak istiyordum ama datacenter IP'lerin ChatGPT veya Netflix gibi servislerde nasıl engellendiğini de biliyordum. Bu yazı, o sorunu çözene kadar attığım adımları, karşılaştığım gerçek hataları ve onları nasıl çözdüğümü anlatıyor. Her komut, kendi terminalimden aldığım gerçek çıktılarla birlikte.&lt;/p&gt;

&lt;p&gt;Kurulum, CachyOS (Arch Linux tabanlı) bir istemci üzerinden AWS EC2'ye SSH ile bağlanılarak gerçekleştirildi.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adım 1: AWS EC2 ve SSH Kurulumu
&lt;/h2&gt;

&lt;p&gt;İlk adım AWS konsolundan uygun bir sunucu ayağa kaldırmaktı. AWS konsoluna girip EC2 (Elastic Compute Cloud) altında launch instance dedim.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F080nmvyp92i8oa6t4thj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F080nmvyp92i8oa6t4thj.png" alt="AWS EC2 konsolu, Launch instance butonuna basılmadan önceki ekran" width="799" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Burada direkt olarak bana "getting started walkthroughs" önerdi. Fakat ne yaptığımı bildiğim için "Launch without a walkthrough" seçeneğini seçtim.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzd4awie8mm1eov79hzpc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzd4awie8mm1eov79hzpc.png" alt="AWS'nin önerdiği " width="799" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Evet, burada bizi AWS'nin "Launch an instance" konfigürasyon arayüzü karşıladı. Ben burada bütçe dostu bir çözüm aradığım için, kredi havuzumu az tüketen &lt;code&gt;t3.micro&lt;/code&gt; instance tipini seçili bıraktım. İşletim sistemi olarak da Ubuntu 26.04 LTS'i seçtim. İkisi de Free Tier eligible olarak işaretliydi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxae92esj3c26hpaxn8mc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxae92esj3c26hpaxn8mc.png" alt="Launch an instance ekranında t3.micro instance tipinin seçilmesi" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmipr0lc2uir1aafa1sm2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmipr0lc2uir1aafa1sm2.png" alt="İşletim sistemi olarak Ubuntu 26.04 LTS seçimi, Free Tier eligible etiketi" width="799" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Daha sonrasında ise login için Key Pair istedi. Burada "yeni bir key pair oluştur" dedim:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0hr45np8htezlc3fn2js.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0hr45np8htezlc3fn2js.png" alt="Key pair oluşturma ekranı, yeni bir anahtar çifti oluşturma adımı" width="594" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fozdk1lizfo1127amqvuo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fozdk1lizfo1127amqvuo.png" alt="Key pair formatı seçimi, .pem uzantısının işaretlenmesi" width="799" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Burada anahtarımın .pem uzantılı olduğuna dikkat ettim, çünkü SSH bağlantısında bu formatı kullanacaktım. Sonrasında anahtarı bilgisayarıma indirdi.&lt;/p&gt;

&lt;p&gt;Daha sonrasında ise AWS Network ayarlarında şu Firewall (security groups) kurallarını tanımladım:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH (Port 22) - My IP&lt;/li&gt;
&lt;li&gt;HTTP (Port 80) - Anywhere (panel erişimi için, sonradan kapatılacak)&lt;/li&gt;
&lt;li&gt;HTTPS (Port 443) - Anywhere (V2Ray bağlantısı için)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb7420ksjpk5kphksk4dp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb7420ksjpk5kphksk4dp.png" alt="Network settings kısmında tanımlanan SSH/HTTP/HTTPS inbound kuralları" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kısaca konfigürasyonum bu şekildeydi; advanced ayarlara falan gerek yoktu. Sadece AWS'de çalışan, internete açık bir IP'si olan (CGNAT arkasında değil) ve fazla kaynak gerektirmeyen basit bir makine yeterliydi. Bu arada "Launch instance"a basmadan önce gözüme takılan çok güzel bir özellik vardı: Preview Code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo26q8xlcte1hgw9drsnu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo26q8xlcte1hgw9drsnu.png" alt="Launch instance öncesi " width="529" height="880"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Burada, instance hazırlanırken hangi komutların CLI üzerinden çalıştırılacağını gösteriyor. Daha da güzeli, formatı değiştirerek diğer dillerde nasıl kullanılacağını da gösteriyor, örneğin Python SDK için:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwrkgmsdmq38u80122mx3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwrkgmsdmq38u80122mx3.png" alt="Preview code panelinde dil seçeneği Python SDK'ya çevrilmiş hali" width="529" height="880"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Otomasyon script'leri yazarken cidden işe yarayacak bir özellik.&lt;/p&gt;

&lt;p&gt;Şimdi ise launch instance diyelim ve makinemizi AWS tarafından oluşturmaya bırakalım (kısa bir süre):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsdq2joyqf0hhy0amequ8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsdq2joyqf0hhy0amequ8.png" alt="Instance'ın oluşturulma sürecini gösteren ekran" width="800" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5 sn falan sürdü.&lt;/p&gt;

&lt;p&gt;İlk dashboard izlenimi:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdasj6z64tsxqrmazuzv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdasj6z64tsxqrmazuzv7.png" alt="EC2 instance dashboard'unun ilk görünümü (IPv4, instance state ve diğer sekmeler)" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Benim aşırı fazla bir AWS kullanmışlığım veya bununla ilgili detaylı çalışmalarım yok ama aradığım şeylerin (örneğin IPv4 ve instance state) ve diğer monitoring, security gibi sekmelerin direkt olarak beni karşılaması işimi kolaylaştırdı.&lt;/p&gt;

&lt;p&gt;İstemciden sunucuya ilk bağlantı denemesi:&lt;/p&gt;

&lt;p&gt;İndirdiğim anahtarla (Downloads klasöründen) giriş yaptım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/Downloads/aws-key.pem ubuntu@54.93.233.182
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İlk bağlantıda bilinen hostlar listesine ekleme onayı istendi, giriş sorunsuz sağlandı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The authenticity of host '54.93.233.182 (54.93.233.182)' can't be established.
ED25519 key fingerprint is: SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Welcome to Ubuntu 26.04 LTS (GNU/Linux 7.0.0-1006-aws x86_64)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Evet. Uzak EC2 makinemizin içindeyiz. Artık kuruluma geçebiliriz.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adım 2: 3X-UI Panel Kurulumu ve İlk Engeller
&lt;/h2&gt;

&lt;p&gt;Sunucuya girdikten sonra root yetkisi aldım ve 3X-UI (V2Ray/Xray yönetim paneli) kurulum script'ini çalıştırdım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su -
bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-Ls&lt;/span&gt; https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kurulum sırasında veritabanı olarak SQLite'ı seçtim. Büyük bir veritabanı yöneticisine ihtiyacım yoktu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;═══════════════════════════════════════════
     Database Selection
═══════════════════════════════════════════
  1) SQLite     (default — recommended for &amp;lt; 500 clients)
  2) PostgreSQL (recommended for high client counts / many nodes)
Choose [1]: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Panel portu özelleştirme sorusuna &lt;code&gt;y&lt;/code&gt; yanıtı verip port &lt;code&gt;80&lt;/code&gt; olarak ayarladım.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Would you like to customize the Panel Port settings? (If not, a random port will be applied) [y/n]: y
Please set up the panel port: 80
Your Panel Port is: 80
Port set successfully: 80
Username and password updated successfully
Base URI path set successfully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kurulumun son aşamasında SSL kurulumu soruldu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;═══════════════════════════════════════════
     SSL Certificate Setup (RECOMMENDED)
═══════════════════════════════════════════
SSL is strongly recommended. Skip only if a reverse proxy
or SSH tunnel handles TLS for you.
Let's Encrypt now supports both domains and IP addresses!

Choose SSL certificate setup method:
1. Let's Encrypt for Domain (90-day validity, auto-renews)
2. Let's Encrypt for IP Address (6-day validity, auto-renews)
3. Custom SSL Certificate (Path to existing files)
4. Skip SSL (advanced — behind reverse proxy / SSH tunnel only)
Note: Options 1 &amp;amp; 2 require port 80 open. Option 3 requires manual paths.
Note: Option 4 serves the panel over plain HTTP — only safe behind nginx/Caddy or an SSH tunnel.
Choose an option (default 2 for IP): 4

⚠ Panel will be installed WITHOUT SSL/TLS.
Login credentials and cookies will travel as plain HTTP.
Only safe when:
  • A reverse proxy (nginx, Caddy, Traefik) terminates TLS for you, or
  • You access the panel exclusively via SSH tunnel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SSL'i atladım (4). Ama script, panelin &lt;code&gt;127.0.0.1&lt;/code&gt;'e mi bağlanacağını da sordu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bind the panel to 127.0.0.1 only? (recommended — forces SSH tunnel / reverse-proxy access) [y/N]: N
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Burada &lt;code&gt;N&lt;/code&gt; seçtim. Nedeni, ilk aşamada paneli tarayıcıdan test etmek istememdi; &lt;code&gt;y&lt;/code&gt; deseydim panele sadece SSH tüneli ile erişilebilecekti, bu da ilk hatayı tespit etmeyi zorlaştıracaktı.&lt;/p&gt;

&lt;h3&gt;
  
  
  Karşılaşılan Hata: 404 Not Found
&lt;/h3&gt;

&lt;p&gt;Tarayıcıdan &lt;code&gt;http://54.93.233.182&lt;/code&gt; adresine gittiğimde karşılama ekranı yerine &lt;code&gt;404 Not Found&lt;/code&gt; hatası aldım.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Gözlem ve Tespit:&lt;/strong&gt; 3X-UI'nin son sürümleri, güvenlik gereği paneli ana dizine (&lt;code&gt;/&lt;/code&gt;) değil, rastgele oluşturulmuş gizli bir yola (&lt;code&gt;webBasePath&lt;/code&gt;) kuruyor. Ana IP'ye girince 404 vermesi bir hata değil, bir güvenlik özelliği.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Gizli yolu bulmak için terminalden panel ayarlarını sorguladım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;x-ui settings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Çıktı şu şekilde geldi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INF] current panel settings as follows:
port: 80
webBasePath: /mubjbN3SXSEBOdEhtd/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tarayıcı adresini &lt;code&gt;http://54.93.233.182/mubjbN3SXSEBOdEhtd/&lt;/code&gt; olarak güncelledim ve panele başarıyla girdim.&lt;/p&gt;

&lt;p&gt;Ama giriş ekranı geldikten sonra bir sorun vardı. Kurulum yaparken bana kullanıcı adı ve şifre için sorular sormamıştı!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffqulkv6obizt2lo27hpp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffqulkv6obizt2lo27hpp.png" alt="Kullanıcı adı/parola bilgisi bilinmeyen 3X-UI giriş ekranı" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bunun için panel ayarlarını açtım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@ip-172-31-38-106:~# x-ui
The OS release is: ubuntu

╔────────────────────────────────────────────────╗
│  3X-UI Panel Management Script                │
│  0. Exit Script                               │
│────────────────────────────────────────────────│
│  1. Install                                   │
│  2. Update                                    │
│  3. Update to Dev Channel (latest commit)     │
│  4. Update Menu                               │
│  5. Legacy Version                            │
│  6. Uninstall                                 │
│────────────────────────────────────────────────│
│  7. Reset Username &amp;amp; Password                 │
│  8. Reset Web Base Path                       │
│  9. Reset Settings                            │
│  10. Change Port                              │
│  11. View Current Settings                    │
│────────────────────────────────────────────────│
│  12. Start                                    │
│  13. Stop                                     │
│  14. Restart                                  │
|  15. Restart Xray                             │
│  16. Check Status                             │
│  17. Logs Management                          │
│────────────────────────────────────────────────│
│  18. Enable Autostart                         │
│  19. Disable Autostart                        │
│────────────────────────────────────────────────│
│  20. SSL Certificate Management               │
│  21. Cloudflare SSL Certificate               │
│  22. IP Limit Management                      │
│  23. Firewall Management                      │
│  24. SSH Port Forwarding Management           │
│  25. PostgreSQL Management                    │
│────────────────────────────────────────────────│
│  26. Enable BBR                               │
│  27. Update Geo Files                         │
│  28. Speedtest by Ookla                       │
╚────────────────────────────────────────────────╝

Panel state: Running
Start automatically: Yes
xray state: Running

Please enter your selection [0-28]: 7

Are you sure to reset the username and password of the panel? [Default n]: y
Please set the login username [default is a random username]: ********
Please set the login password [default is a random password]: ********
Do you want to disable currently configured two-factor authentication? (y/n): n
Panel login username has been reset to:  ******** 
Panel login password has been reset to:  ******** 
 Please use the new login username and password to access the X-UI panel. Also remember them! 

Restart the panel, Attention: Restarting the panel will also restart xray [Default y]: 
[INF] x-ui and xray Restarted successfully 

Press enter to return to the main menu: 

╔────────────────────────────────────────────────╗
│  3X-UI Panel Management Script                │
│  0. Exit Script                               │
│────────────────────────────────────────────────│
│  1. Install                                   │
│  2. Update                                    │
│  3. Update to Dev Channel (latest commit)     │
│  4. Update Menu                               │
│  5. Legacy Version                            │
│  6. Uninstall                                 │
│────────────────────────────────────────────────│
│  7. Reset Username &amp;amp; Password                 │
│  8. Reset Web Base Path                       │
│  9. Reset Settings                            │
│  10. Change Port                              │
│  11. View Current Settings                    │
│────────────────────────────────────────────────│
│  12. Start                                    │
│  13. Stop                                     │
│  14. Restart                                  │
|  15. Restart Xray                             │
│  16. Check Status                             │
│  17. Logs Management                          │
│────────────────────────────────────────────────│
│  18. Enable Autostart                         │
│  19. Disable Autostart                        │
│────────────────────────────────────────────────│
│  20. SSL Certificate Management               │
│  21. Cloudflare SSL Certificate               │
│  22. IP Limit Management                      │
│  23. Firewall Management                      │
│  24. SSH Port Forwarding Management           │
│  25. PostgreSQL Management                    │
│────────────────────────────────────────────────│
│  26. Enable BBR                               │
│  27. Update Geo Files                         │
│  28. Speedtest by Ookla                       │
╚────────────────────────────────────────────────╝

Panel state: Running
Start automatically: Yes
xray state: Running

Please enter your selection [0-28]: 0
root@ip-172-31-38-106:~#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ve evet, kullanıcı adı ve parolayı sıfırlayarak kendim yeni kullanıcı adı ve parola tanımladım! Ve web arayüzüne giriş yaptığımda beni çok güzel bir arayüz karşıladı:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcrf7np1xrabhmvt5jkfp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcrf7np1xrabhmvt5jkfp.png" alt="Yeni kullanıcı adı ve parola ile giriş yapılan 3X-UI panel arayüzü" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ve artık VLESS + WebSocket Inbound kurulumuna geçebiliriz!&lt;/p&gt;

&lt;h2&gt;
  
  
  Adım 3: VLESS + WebSocket Inbound Oluşturma
&lt;/h2&gt;

&lt;p&gt;Panele girdikten sonra bağlantıları karşılayacak Inbound kuralını oluşturmamız gerekti. GUI'deki sol menüden Inbounds'u açtım:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpm2obc9pn7y86kfreohi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpm2obc9pn7y86kfreohi.png" alt="3X-UI panelinde sol menüden Inbounds sekmesinin açılması" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Add Inbound" ekranında "Stream" sekmesine geçtiğimde "Transmission" kısmı varsayılan olarak &lt;code&gt;RAW&lt;/code&gt; (TCP) geliyordu. Arayüzden bunu &lt;code&gt;ws&lt;/code&gt; (WebSocket) yapmak ve Host eklemek bazen karmaşık olabiliyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fngjllzhfdzs2g0277hc6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fngjllzhfdzs2g0277hc6.png" alt="Add Inbound ekranında Stream sekmesi, Transmission alanının varsayılan RAW (TCP) değeri" width="776" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Gerçek Hata ve Çözüm:&lt;/strong&gt; GUI üzerinden ayarları değiştirmeye çalıştığımda arayüz bazen değişiklikleri tam yansıtmadı. En kesin ve hatasız yöntemin "Advanced" sekmesinden JSON config'i doğrudan düzenlemek olduğuna karar verdim.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Advanced (JSON) Config:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"listen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"protocol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vless"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"in-443-ws"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"clients"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"decryption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"encryption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sniffing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"destOverride"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"tls"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"streamSettings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ws"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"security"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"wsSettings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hakanismail.info"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not: &lt;code&gt;security: none&lt;/code&gt; seçmemin sebebi, sunucuda henüz SSL sertifikası olmaması. Trafik şifrelenmiş (TLS) bir port üzerinden değil, ham WebSocket (HTTP upgrade) üzerinden taşınacak.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9few6q62n5cpfxwj26hx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9few6q62n5cpfxwj26hx.png" alt="Advanced sekmesinden JSON config düzenlendikten sonra kaydedilen Inbound" width="798" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inbound'u kaydettikten sonra "Clients" kısmından bir kullanıcı oluşturdum ve sağlanan bağlantı QR'ı ile Arch Linux üzerindeki v2rayN istemcisinden bağlantı sağladım. &lt;code&gt;vless://&lt;/code&gt; linki ile bağlantı sağlanabilir fakat QR kod daha kolay oldu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe2nmnk1b7xc6as65l080.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe2nmnk1b7xc6as65l080.png" alt="Oluşturulan client için görüntülenen bağlantı QR kodları" width="718" height="865"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;QR kodda önemli bir detay var: iki tane QR kodu var. "Subscription Info" QR'ı bağlantı için kullanılmaz. Bağlantı için kullanmamız gereken, "Vless WS (Arch-Linux:443)" etiketli QR.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsizwyh7pbeidm6bifzyj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsizwyh7pbeidm6bifzyj.png" alt="Bağlantı için kullanılması gereken " width="523" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bağlantıyı ekledikten sonra alttaki anahtarı "Clear system proxy" konumundan "Set system proxy" konumuna çekiyoruz ve bağlantı sağlanıyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffwxduytoo33coymcp6i3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffwxduytoo33coymcp6i3.png" alt="v2rayN istemcisinde " width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;whatismyip.com&lt;/code&gt; üzerinden IP'nin Almanya (AWS) olduğunu teyit ettim.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F10o91ouz7hk99s7icac3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F10o91ouz7hk99s7icac3.png" alt="whatismyip.com sonucu, IP'nin Almanya/Frankfurt olarak görünmesi" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adım 4: Güvenlik (Panelin Dış Dünyaya Kapatılması)
&lt;/h2&gt;

&lt;p&gt;Sistem çalışır durumdaydı ama 3X-UI paneli (&lt;code&gt;80&lt;/code&gt; portu) tüm dünyaya açıktı. İnternetteki botlar IP'yi tarayıp paneli kötüye kullanmaya çalışabilir. Çözüm, AWS güvenlik duvarından 80 portunu kapatmak ve panele sadece SSH tüneli ile girmekti.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. AWS Security Group Kuralını Sildim&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS konsolundan EC2 → seçili sanal makine → Security sekmesi → Security Groups altındaki tek security grup → Inbound rules kısmına gittim. HTTP (Port 80) kuralını listeden sildim. Artık &lt;code&gt;http://54.93.233.182&lt;/code&gt; adresine dışarıdan kimse erişemiyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fytlyhkueuev9bqp5w67m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fytlyhkueuev9bqp5w67m.png" alt="Security group Inbound rules listesinden HTTP (80) kuralının silinmesi" width="799" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. SSH Tüneli (Local Port Forwarding) Kurdum&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Arch Linux terminalinden şu komutu çalıştırdım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; aws-key.pem &lt;span class="nt"&gt;-L&lt;/span&gt; 8080:127.0.0.1:80 ubuntu@54.93.233.182
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu komut, yerel makinenin &lt;code&gt;8080&lt;/code&gt; portunu, SSH bağlantısı üzerinden sunucunun iç ağındaki &lt;code&gt;127.0.0.1:80&lt;/code&gt; adresine gizlice yönlendirdi.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Test&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SSH tüneli açıkken tarayıcıya şu adresi girdim: &lt;code&gt;http://127.0.0.1:8080/mubjbN3SXSEBOdEhtd/panel/&lt;/code&gt;. Panel sorunsuz açıldı.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frlgoidlngi67gxxfhbq4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frlgoidlngi67gxxfhbq4.png" alt="SSH tüneli üzerinden localhost:8080 adresinden açılan panel" width="799" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dış IP üzerinden denediğimde ise bağlantı zaman aşımına uğradı (Timeout). Sunucu güvenliği sağlandı.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adım 5: Cloudflare WARP Entegrasyonu
&lt;/h2&gt;

&lt;p&gt;Şu anda IP'm &lt;code&gt;54.93.233.182&lt;/code&gt; (Amazon). ChatGPT veya Netflix gibi servisler, trafiğin bir veri merkezinden geldiğini anlayıp erişimi reddedebilir. Bu, film izlerken veya agentik iş yaparken can sıkıcı olabiliyor. Bunu aşmak için AWS sunucusunun içine Cloudflare WARP kurdum. WARP, trafiği Cloudflare'in residential IP havuzundan, yani son kullanıcı IP'lerinden çıkarıyor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. WARP Kurulumu&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS sunucusuna SSH ile bağlanıp root oldum. Cloudflare deposunu ekledim ve paketi kurdum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://pkg.cloudflareclient.com/pubkey.gpg | &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--yes&lt;/span&gt; &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lsb_release &lt;span class="nt"&gt;-cs&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; main"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/cloudflare-client.list
apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install &lt;/span&gt;cloudflare-warp &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Kayıt ve Proxy Modu&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WARP'ı tüm sunucu trafiğini kesmeyecek, sadece yerel bir SOCKS5 proxy (port 40000) olarak çalışacak şekilde ayarladım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;warp-cli registration new
&lt;span class="c"&gt;# (Kullanım şartları kabul edildi: y)&lt;/span&gt;

warp-cli mode proxy
&lt;span class="c"&gt;# Success&lt;/span&gt;

warp-cli connect
&lt;span class="c"&gt;# Success&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Test ve Karşılaşılan Garip Çıktı&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WARP'ın çalışıp çalışmadığını test etmek için standart trace komutunu kullandım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://www.cloudflare.com/cdn-cgi/trace/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İlginç bir şekilde ekrana HTML olarak &lt;code&gt;404 Not Found&lt;/code&gt; düştü. Bu adresin kendisi bozuk değildi; muhtemelen cloudflare.com'un kendi sitesine özgü bir bot koruması ya da redirect davranışıydı, WARP veya AWS IP'siyle doğrudan ilgisi yoktu.&lt;/p&gt;

&lt;p&gt;Bunun yerine Cloudflare'in tam olarak bu iş için var olan tanılama adresini denedim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://1.1.1.1/cdn-cgi/trace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fl=928f31
h=1.1.1.1
ip=54.93.233.182
ts=1788933479.000
visit_scheme=https
uag=curl/8.18.0
colo=FRA
sliver=050-tier1
http=http/2
loc=DE
tls=TLSv1.3
sni=off
warp=off
gateway=off
rbi=off
kex=X25519MLKEM768
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;warp=off&lt;/code&gt; görünce şüphelendim, çünkü WARP zaten bağlıydı. Emin olmak için bağlantıyı kesip tekrar bağladım ve her seferinde aynı komutu tekrarladım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@ip-172-31-38-106:~# warp-cli disconnect
Success
root@ip-172-31-38-106:~# curl https://1.1.1.1/cdn-cgi/trace
...
warp=off

root@ip-172-31-38-106:~# warp-cli connect
Success
root@ip-172-31-38-106:~# warp-cli status
Status update: Connected
Network: healthy
root@ip-172-31-38-106:~# curl https://1.1.1.1/cdn-cgi/trace
...
warp=off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;warp-cli status&lt;/code&gt; "Connected" ve "healthy" dese bile çıktı hep &lt;code&gt;warp=off&lt;/code&gt; kaldı.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Gerçek Durum:&lt;/strong&gt; WARP'ı &lt;code&gt;proxy&lt;/code&gt; moduna aldığım için varsayılan ağ yönlendirmesine hiç müdahale etmiyor. &lt;code&gt;curl&lt;/code&gt; gibi düz bir istek WARP'a hiç girmeden direkt AWS ağından çıkıyor; WARP yalnızca açıkça kendisine yönlendirilen (SOCKS5 üzerinden gelen) trafiği görüyor. Bu artık hipotez değil, connect/disconnect döngüsüyle doğrulanmış bir gözlem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bunu kanıtlamak için, az önceki testle birebir aynı komutu bu kez WARP'ın SOCKS5 portu üzerinden çalıştırdım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-x&lt;/span&gt; socks5h://127.0.0.1:40000 https://1.1.1.1/cdn-cgi/trace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not: &lt;code&gt;socks5h&lt;/code&gt; kullanmak önemli (düz &lt;code&gt;socks5&lt;/code&gt; değil), çünkü &lt;code&gt;h&lt;/code&gt; DNS çözümlemesini de proxy üzerinden yaptırıyor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fl=470f261
h=1.1.1.1
ip=104.28.197.9
ts=1788933795.000
visit_scheme=https
uag=curl/8.18.0
colo=FRA
sliver=010-tier1
http=http/2
loc=DE
tls=TLSv1.3
sni=off
warp=on
gateway=off
rbi=off
kex=X25519MLKEM768
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fark net: &lt;code&gt;ip&lt;/code&gt; artık AWS'e ait &lt;code&gt;54.93.233.182&lt;/code&gt; değil, Cloudflare'e ait &lt;code&gt;104.28.197.9&lt;/code&gt;; &lt;code&gt;warp&lt;/code&gt; alanı da &lt;code&gt;off&lt;/code&gt;'tan &lt;code&gt;on&lt;/code&gt;'a döndü. WARP'ın çalıştığı, sadece varsayılan yönlendirmeye dahil olmadığı bu şekilde kanıtlandı.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. 3X-UI Panelinden Yönlendirme (Routing)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WARP kuruldu ama V2Ray trafiği hâlâ AWS'in normal internetinden çıkıyordu. 3X-UI panelinden (SSH tüneli ile girdim), &lt;code&gt;Panel Settings → Xray Configuration&lt;/code&gt; kısmındaki JSON config'i güncelledim.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;outbounds&lt;/code&gt; dizisine WARP için bir SOCKS çıkışı ekledim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"protocol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"socks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40000&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqo2gnhkk9vlsq1kfu4tz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqo2gnhkk9vlsq1kfu4tz.png" alt="3X-UI panelinin Xray Configuration ekranında, outbounds dizisine eklenen WARP SOCKS çıkışı" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;routing&lt;/code&gt; → &lt;code&gt;rules&lt;/code&gt; kısmına ise tüm trafiği bu WARP'a yönlendirecek kuralı yazdım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"outboundTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tcp,udp"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft05qh9y2do87ilrlf33c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft05qh9y2do87ilrlf33c.png" alt="3X-UI panelinin Xray Configuration ekranında, routing kurallarına eklenen WARP yönlendirme kuralı" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ayarları kaydedip Xray'i yeniden başlattım.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Final Testi&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;v2rayN istemcisinden bağlantıyı kesip tekrar bağlandım. &lt;code&gt;whatismyip.com&lt;/code&gt; adresine girdim:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yeni IPv4: &lt;code&gt;104.28.197.14&lt;/code&gt; (Cloudflare)&lt;/li&gt;
&lt;li&gt;Yeni IPv6: &lt;code&gt;2a09:bac1:1e20:8::3a2:d&lt;/code&gt; (WARP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftbrgrbhdgex0wm3x9gnl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftbrgrbhdgex0wm3x9gnl.png" alt="whatismyip.com üzerinde WARP sonrası görünen yeni IP (Cloudflare)" width="799" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Merakımdan, WARP'ı devre dışı bırakıp &lt;code&gt;chat.openai.com&lt;/code&gt; adresine direkt AWS IP'si üzerinden de gittim. Beklediğimin aksine herhangi bir hata almadan, sorunsuz açıldı; yani ChatGPT bu spesifik AWS IP'sini o an engellemiyordu. Bu da datacenter IP engellerinin ne kadar tutarsız olduğunu gösteriyor: hangi IP'nin hangi serviste ne zaman engelleneceği önceden kestirilemiyor (Netflix gibi bölge kilidine çok daha sıkı yaklaşan servislerde durum farklı olabilir, ama bunu ayrıca test etmedim). WARP'ın buradaki asıl faydası "aktif bir engeli aşmak" değil, IP'nin görünümünü baştan residential seviyeye çekip bu riski azaltmaktı.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kapanış ve Gözlem
&lt;/h2&gt;

&lt;p&gt;Bir hafta sonuna sığan bu lab çalışmasında, standart bir VPS'in nasıl güvenli bir proxy sunucusuna dönüştürüleceğini test ettim. Çıkardığım en önemli dersler:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GUI bazen istenen network ayarlarını tam yansıtmayabilir; JSON config'i doğrudan düzenlemek çok daha güven verici.&lt;/li&gt;
&lt;li&gt;Bir servisi (WARP gibi) sistem geneline yaymak yerine local proxy modunda çalıştırıp ana proxy yazılımının (Xray) routing kurallarıyla yönetmek, hata ayıklamayı inanılmaz kolaylaştırıyor.&lt;/li&gt;
&lt;li&gt;Yönetim panellerini (3X-UI gibi) dış dünyaya açmamak, SSH local forwarding ile erişmek en sağlam sunucu güvenliği yöntemi.&lt;/li&gt;
&lt;li&gt;Datacenter IP engelleri servise göre çok değişken ve garantili değil; bu testte ChatGPT AWS IP'mi hiç engellemedi. WARP'ın buradaki kazancı "aktif bir engeli aşmak" değil, IP'nin görünümünü residential seviyeye çekip bu riski önceden azaltmaktı.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>aws</category>
      <category>v2ray</category>
      <category>3xui</category>
    </item>
    <item>
      <title>Proxmox Otomasyon Serisi Faz 3: Zabbix Agent 2 (7.0 LTS), Rsyslog (QRadar TCP ve Disk Destekli Kuyruk) ve Güvenlik Ajanları</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Thu, 10 Sep 2026 12:08:11 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/proxmox-otomasyon-serisi-faz-3-zabbix-agent-2-70-lts-rsyslog-qradar-tcp-ve-disk-destekli-52jd</link>
      <guid>https://dev.to/hakanbaban53/proxmox-otomasyon-serisi-faz-3-zabbix-agent-2-70-lts-rsyslog-qradar-tcp-ve-disk-destekli-52jd</guid>
      <description>&lt;p&gt;Bu rehber, &lt;strong&gt;Proxmox Otomasyon Serisi&lt;/strong&gt;'nin dördüncü modülüdür ve tamamlanmış bir altyapı fazının üzerine inşa edilen ilk katmandır. Faz 2; seri numaralarıyla etiketlenmiş disklerin LVM ve XFS katmanlarından geçirilip bağlanması, çalışan makinede kesintisiz disk genişletme, VM açılışında kalıcılığını kanıtlayan reboot testleri ve aygıt harfleri (&lt;code&gt;sdb&lt;/code&gt;, &lt;code&gt;sde&lt;/code&gt;...) tamamen yer değiştirdiğinde dahi sistemin ayakta kaldığını gösteren saha oturumlarıyla kapatılmıştı.&lt;/p&gt;

&lt;p&gt;Makineler mevcut, veriyi tutuyor ve reboot sonrasında sorunsuz ayağa kalkıyor. Bu noktada ana soru değişir: Artık soru &lt;em&gt;"makine ayakta mı?"&lt;/em&gt; değil, &lt;em&gt;"makinenin içinde neler oluyor?"&lt;/em&gt; sorusudur. Bu sorunun yanıtı konuk (guest) makinenin dışından verilir; bu yanıtın makine içindeki ayağı ise her zaman bir ajandır (agent).&lt;/p&gt;

&lt;p&gt;Faz 3, provizyon edilen her sanal makineye üç temel ajan rolü kazandırır:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;İzleme Ajanı (Monitoring):&lt;/strong&gt; Merkezi bir sunucunun makineyi kontrol edebilmesi için resmi 7.0 LTS deposuna sabitlenmiş &lt;strong&gt;Zabbix Agent 2&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log Yönlendirme (Logging):&lt;/strong&gt; Makinenin tüm sistem loglarını kurumsal QRadar mimarisine uygun şekilde TCP üzerinden ve disk destekli bir kuyruk mekanizmasıyla dışarı aktaran &lt;strong&gt;rsyslog&lt;/strong&gt; yapılandırması.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Güvenlik Ajanları (Security):&lt;/strong&gt; Homelab ortamında lisanslanması mümkün olmayan kurumsal güvenlik yazılımlarını (Trend Micro Deep Security ve ManageEngine) simüle eden, bir belirteç sözleşmesi (&lt;strong&gt;marker contract&lt;/strong&gt;) arkasında çalışan &lt;strong&gt;stub yükleyiciler&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Her üç bileşen de serinin felsefesine sadık kalınarak; numaralandırılmış, bağımsız, idempotent, tek bir değişken dosyasından beslenen ve yeşil recap çıktısı vermeden önce kendi yaptıklarını bizzat doğrulayan Ansible playbook'ları olarak dağıtılır.&lt;/p&gt;




&lt;h2&gt;
  
  
  Faz 3'de ne var?
&lt;/h2&gt;

&lt;p&gt;Planlama aşamasında community rolleri, bir Graylog konteyneri ve basit &lt;code&gt;echo&lt;/code&gt; komutlarıyla çalışan sahte betikler düşünülmüştü. Canlıya alınan mimarideki sapmalar birer taviz değil; saha testlerinin getirdiği teknik zorunluluklardır:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;İlk Planlanan&lt;/th&gt;
&lt;th&gt;Canlıya Alınan&lt;/th&gt;
&lt;th&gt;Teknik Gerekçe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;community.zabbix.zabbix_agent&lt;/code&gt; rolü&lt;/td&gt;
&lt;td&gt;Özel &lt;code&gt;04_zabbix_agent.yml&lt;/code&gt; playbook'u&lt;/td&gt;
&lt;td&gt;Ubuntu 26.04 için resmi 7.0 LTS deposu zorunluluğu; kontrol düğümüne koleksiyon kurmadan &lt;code&gt;ansible.builtin&lt;/code&gt; içinde kalma hedefi.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ayrı Zabbix sunucusu VM'i (Docker, 4 GB)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ctrl-01&lt;/code&gt; üzerinden &lt;code&gt;zabbix-get&lt;/code&gt; doğrulaması&lt;/td&gt;
&lt;td&gt;Bu fazın teslimatı sunucu değil ajandır. Soket sorgusu tüm yolu kanıtlar; sunucu isteğe bağlı hafif LXC'ye bırakıldı.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graylog konteyneri / tam rsyslog sunucusu&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ctrl-01&lt;/code&gt; üzerinde tek satırlık &lt;code&gt;socat&lt;/code&gt; dinleyicisi&lt;/td&gt;
&lt;td&gt;QRadar TCP desenini test eden en hafif uç nokta. Altyapıyı şişirmeden kuyruk akışını tam doğrular.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;*.* @@receiver:514&lt;/code&gt; (tek satır yönlendirme)&lt;/td&gt;
&lt;td&gt;Disk destekli kuyruğa sahip &lt;code&gt;omfwd&lt;/code&gt; aksiyonu&lt;/td&gt;
&lt;td&gt;Tek satırlık kural hedef kapandığında log düşürür. Kuyruk mekanizması hedef gelene kadar logları diske depolar.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Basit kabuk betikleri (&lt;code&gt;touch /opt/...&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Sürüm sapma alarmlı marker sözleşmesi&lt;/td&gt;
&lt;td&gt;Yükleyici iskeleti prodüksiyon standardındadır; yarın lisans geldiğinde yalnızca yükleme gövdesi güncellenir.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Dizin Ağacı ve Dosya Rolleri
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible/
├── inventory/group_vars/
│   └── provisioned.yml             # Ajan değişkenleri ayrıştırıldı, sadece depolama şeması kaldı
├── playbooks/
│   ├── vars/
│   │   └── agent-layer.yml         # Ajan katmanının tek gerçek kaynağı (Single Source of Truth)
│   ├── tasks/
│   │   └── wait_for_apt.yml        # İlk açılış dpkg/apt kilit yarışını çözen geçit görevi
│   ├── templates/
│   │   ├── zabbix_agent2.conf.j2   # Dinamik ajan yapılandırma şablonu
│   │   ├── 50-qradar-forward.conf.j2 # Disk kuyruklu rsyslog yönlendirme şablonu
│   │   └── install.sh.j2           # Marker sözleşmesine bağlı stub yükleyici
│   ├── 04_zabbix_agent.yml         # Zabbix kurulum ve 5 adımlı doğrulama playbook'u
│   ├── 05_rsyslog_forward.yml      # Log yönlendirme ve sözdizimi doğrulama playbook'u
│   └── 06_security_agents.yml      # Güvenlik ajanları stub dağıtımı ve drift denetimi

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Kapsam Sınırları
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kapsam İçi:&lt;/strong&gt; Zabbix Agent 2'nin resmi 7.0 LTS reposundan pini, disk destekli TCP rsyslog kuyruğu, lisanslı ajanlar için marker sözleşmeli stub altyapısı, &lt;code&gt;ctrl-01&lt;/code&gt; üzerinde oturum bağımsız dinleyici ve saha doğrulamaları.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kapsam Dışı:&lt;/strong&gt; Prodüksiyon QRadar / Zabbix sunucu kurulumları, TLS sertifika yönetimi, log ayrıştırma kuralları ve alarm dashboard'ları (bu katmanın tek amacı misafir makinedeki ajan omurgasını ayağa kaldırmaktır).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Zabbix Agent 2: Sürüm Sabitleme ve Doğrulama
&lt;/h2&gt;

&lt;p&gt;İzleme tarafında klasik Zabbix ajanı yerine Go ile yeniden yazılan Zabbix Agent 2 tercih edildi. Klasik ajan her metrik için yeni bir alt süreç (&lt;code&gt;fork&lt;/code&gt;) açarak kaynak tüketirken, Agent 2 eklenti mimarisi ve kalıcı tek bir TCP bağlantısı üzerinden aktif kontrolleri yürütür.&lt;/p&gt;

&lt;h3&gt;
  
  
  Neden Community Değil?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Resmi Depo Sabitlemesi:&lt;/strong&gt; Ubuntu 26.04 (&lt;code&gt;resolute&lt;/code&gt;) için kararlı paket sağlayan güncel ana dal 7.0 LTS serisidir. Üçüncü taraf bir rolün varsayılan değişkenlerine güvenmek yerine bu bağımlılık doğrudan kod içinde sabitlendi.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sıfır Dış Bağımlılık:&lt;/strong&gt; Kontrol düğümüne harici Ansible Galaxy koleksiyonları eklenmedi, tüm akış &lt;code&gt;ansible.builtin&lt;/code&gt; modülleriyle çözüldü.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operasyonel Şeffaflık:&lt;/strong&gt; Arka planda onlarca task çalıştıran soyut roller yerine, her komutun ve doğrulamayı yapan assertion'ların net izlenebildiği yalın bir yapı kuruldu.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Canlı Depo Kontrolü ve Apt Seçimi (Election)
&lt;/h3&gt;

&lt;p&gt;Resmi repo doğrudan sorgulandığında 7.0 LTS paketinin erişilebilir olduğu, 8.0 paketinin ise henüz Ubuntu 26.04 için derlenmediği teyit edildi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://repo.zabbix.com/zabbix/7.0/ubuntu/dists/resolute/Release | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
HTTP/2 200 &lt;span class="c"&gt;# 7.0 LTS paketi yayında&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://repo.zabbix.com/zabbix/8.0/ubuntu/dists/resolute/Release | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
HTTP/2 404 &lt;span class="c"&gt;# 8.0 ana sürümü henüz bu dağıtım için yok&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ubuntu Universe deposunda &lt;code&gt;7.0.22&lt;/code&gt; sürümü yer alırken, resmi &lt;code&gt;repo.zabbix.com&lt;/code&gt; üzerinde güncel &lt;code&gt;7.0.30&lt;/code&gt; paketi bulunur. Paket yöneticisi (&lt;code&gt;apt&lt;/code&gt;) yüksek sürüm numarasına sahip resmi depoyu seçer; ancak playbook bu varsayımla yetinmeyip &lt;code&gt;apt-cache policy zabbix-agent2&lt;/code&gt; çıktısını kontrol ederek paketin resmi repodan geldiğini ve &lt;code&gt;7.0&lt;/code&gt; ailesinde kaldığını doğrular.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kurulum Görevleri ve &lt;code&gt;lock_timeout&lt;/code&gt; Ayrımı
&lt;/h3&gt;

&lt;p&gt;İşlem sırası kritiktir: GPG anahtarı repo tanımından önce diske yazılmalıdır; aksi halde &lt;code&gt;apt_repository&lt;/code&gt; anında tetiklediği güncellemede imzasız repo hatasıyla süreci kırar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create the apt keyrings directory&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/apt/keyrings&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;directory&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0755"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install the Zabbix official repo signing key&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.get_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://repo.zabbix.com/zabbix-official-repo.key&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/apt/keyrings/zabbix-official-repo.asc&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0644"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wait out the first-boot apt race before touching packages&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.include_tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tasks/wait_for_apt.yml&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Add the pinned Zabbix apt repository&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.apt_repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
      &lt;span class="s"&gt;deb [signed-by=/etc/apt/keyrings/zabbix-official-repo.asc]&lt;/span&gt;
      &lt;span class="s"&gt;https://repo.zabbix.com/zabbix/{{ zabbix_agent_version }}/ubuntu&lt;/span&gt;
      &lt;span class="s"&gt;{{ ansible_facts['distribution_release'] }} main&lt;/span&gt;
    &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zabbix-official-repo&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Zabbix Agent &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zabbix-agent2&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&lt;/span&gt;
    &lt;span class="na"&gt;lock_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1800&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Saha Dersi (Bulgu #19):&lt;/strong&gt; İlk pipeline denemesinde &lt;code&gt;apt_repository&lt;/code&gt; adımına eklenen &lt;code&gt;lock_timeout: 1800&lt;/code&gt; parametresi görevi çökertti (&lt;code&gt;Unsupported parameters for apt_repository module&lt;/code&gt;). Bu parametre yalnızca &lt;code&gt;apt&lt;/code&gt; modülüne aittir. Kilit koruması &lt;code&gt;wait_for_apt.yml&lt;/code&gt; geçidiyle çözüldüğü için parametre repo görevinden kaldırıldı, sadece paket kurulum adımında bırakıldı.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Yapılandırma ve Değişken Sözleşmesi
&lt;/h3&gt;

&lt;p&gt;Yapılandırma Jinja2 şablonu üzerinden basılır:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;# &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ansible_managed&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
PidFile=/run/zabbix/zabbix_agent2.pid
LogFile=/var/log/zabbix/zabbix_agent2.log
Server=&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;zabbix_agent_server&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
ListenPort=&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;zabbix_agent_listenport&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
ServerActive=&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;zabbix_agent_serveractive&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
Hostname=&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;zabbix_agent_hostname&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
Include=/etc/zabbix/zabbix_agent2.d/plugins.d/*.conf

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu şablonu besleyen &lt;code&gt;vars/agent-layer.yml&lt;/code&gt; dosyası:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Zabbix Agent 2 Parametreleri&lt;/span&gt;
&lt;span class="na"&gt;zabbix_agent_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;7.0"&lt;/span&gt;                &lt;span class="c1"&gt;# Resmi LTS deposu sürüm sabitlemesi&lt;/span&gt;
&lt;span class="na"&gt;zabbix_agent_server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;192.168.122.10"&lt;/span&gt;      &lt;span class="c1"&gt;# Pasif kontrolleri sorgulayacak uç (ctrl-01)&lt;/span&gt;
&lt;span class="na"&gt;zabbix_agent_serveractive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;192.168.122.10"&lt;/span&gt;&lt;span class="c1"&gt;# Aktif kontrollerin iletileceği uç&lt;/span&gt;
&lt;span class="na"&gt;zabbix_agent_listenport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10050&lt;/span&gt;             &lt;span class="c1"&gt;# Standart ajan dinleme portu&lt;/span&gt;
&lt;span class="na"&gt;zabbix_agent_hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;inventory_hostname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt; &lt;span class="c1"&gt;# Zabbix üzerindeki konak kimliği&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Şablon güncellendiğinde &lt;code&gt;Restart Zabbix Agent 2&lt;/code&gt; handler'ı devreye girer. Playbook içindeki &lt;code&gt;ansible.builtin.flush_handlers&lt;/code&gt; çağrısı, doğrulama testlerine geçmeden önce servisin yeniden başlatılmasını zorunlu kılar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Beş Basamaklı Doğrulama ve &lt;code&gt;zabbix-get&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;04_zabbix_agent.yml&lt;/code&gt;, sunucuya ihtiyaç duymadan ajanı 5 aşamalı bir test zincirinden geçirir:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Apt Seçim Sağlaması:&lt;/strong&gt; &lt;code&gt;apt policy&lt;/code&gt; çıktısından paketin &lt;code&gt;repo.zabbix.com&lt;/code&gt; adresinden ve &lt;code&gt;7.0&lt;/code&gt; ana sürümünden geldiği denetlenir.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Servis Durumu:&lt;/strong&gt; &lt;code&gt;service_facts&lt;/code&gt; ile ajanın &lt;code&gt;running&lt;/code&gt; durumunda olduğu doğrulanır.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Soket Kontrolü:&lt;/strong&gt; &lt;code&gt;wait_for&lt;/code&gt; ile &lt;code&gt;127.0.0.1:10050&lt;/code&gt; portunun açık olduğu görülür.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yerel Metrik Testi:&lt;/strong&gt; &lt;code&gt;zabbix_agent2 -t agent.version&lt;/code&gt; komutu çalıştırılarak eklentilerin yüklendiği ve ajanın dahili metrik üretebildiği teyit edilir (&lt;code&gt;rc=0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Konfigürasyon Denetimi:&lt;/strong&gt; Dağıtılan dosya &lt;code&gt;slurp&lt;/code&gt; ile okunarak IP ve konak adı tanımları doğrulanır.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Uçtan uca son test kontrol makinesinden (&lt;code&gt;ctrl-01&lt;/code&gt;) misafir ajana doğru tetiklenir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ctrl-01 üzerinden misafir ajanı sorgula:&lt;/span&gt;
zabbix_get &lt;span class="nt"&gt;-s&lt;/span&gt; 192.168.122.50 &lt;span class="nt"&gt;-k&lt;/span&gt; agent.version
&lt;span class="c"&gt;# Çıktı: 7.0.30&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Rsyslog Yönlendirme: QRadar Deseni
&lt;/h2&gt;

&lt;p&gt;Sistem loglarının yalnızca yerel diskte tutulması felaket anında adli kanıtların yok olmasına neden olur. Ancak yaygın kullanılan &lt;code&gt;*.* @@192.168.122.10:5514&lt;/code&gt; gibi tek satırlık yönlendirme kuralları alıcı çöktüğü veya ağ kesildiği anda ya log akışını kilitler ya da veriyi sessizce düşürür.&lt;/p&gt;

&lt;p&gt;Bu riski bertaraf etmek için disk destekli bir &lt;code&gt;omfwd&lt;/code&gt; kuyruğu kurulmuştur:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;action(type="omfwd"
  target="192.168.122.10"
  port="5514"
  protocol="tcp"
  queue.type="LinkedList"
  queue.filename="qradar_fwd"
  queue.maxDiskSpace="100m"
  queue.saveOnShutdown="on"
  action.resumeRetryCount="-1")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu yapılandırmanın getirdiği davranış modeli:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loglar normal koşullarda bellekte (&lt;code&gt;LinkedList&lt;/code&gt;) işlenir.&lt;/li&gt;
&lt;li&gt;Hedefe ulaşılamazsa bellek taşmasını engellemek için &lt;code&gt;/var/spool/rsyslog&lt;/code&gt; altına geçici disk kuyruğu açılır.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;saveOnShutdown="on"&lt;/code&gt; sayesinde makine yeniden başlasa dahi iletilmemiş kuyruk silinmez.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resumeRetryCount="-1"&lt;/code&gt; alıcı ayağa kalkana kadar iletim denemelerinin sonsuza dek sürmesini sağlar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Parametreler yine &lt;code&gt;vars/agent-layer.yml&lt;/code&gt; üzerinden yönetilir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Rsyslog Yönlendirme Parametreleri&lt;/span&gt;
&lt;span class="na"&gt;rsyslog_receiver_host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;192.168.122.10"&lt;/span&gt; &lt;span class="c1"&gt;# SIEM / Alıcı IP (Laboratuvarda ctrl-01)&lt;/span&gt;
&lt;span class="na"&gt;rsyslog_receiver_port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5514&lt;/span&gt;             &lt;span class="c1"&gt;# Root yetkisi istemeyen socat test portu (Prodüksiyonda 514)&lt;/span&gt;
&lt;span class="na"&gt;rsyslog_forward_protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tcp"&lt;/span&gt;         &lt;span class="c1"&gt;# Paket kaybını önleyen TCP aktarımı&lt;/span&gt;
&lt;span class="na"&gt;rsyslog_queue_maxdisk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100m"&lt;/span&gt;           &lt;span class="c1"&gt;# Kuyruk diske taştığında ayrılacak üst sınır&lt;/span&gt;
&lt;span class="na"&gt;rsyslog_test_tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;phase3-test"&lt;/span&gt;         &lt;span class="c1"&gt;# Doğrulama aşamasında basılacak test log etiketi&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  12 Dakikalık Saha Kesinti Testi
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ctrl-01&lt;/code&gt; üzerindeki &lt;code&gt;socat&lt;/code&gt; dinleyicisi SSH oturumu kapandığı için devre dışı kaldı. Bu esnada &lt;code&gt;05_rsyslog_forward.yml&lt;/code&gt; çalıştırıldı, ardından alıcı kapalıyken test logu üretildi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Alıcı kapalı, port dinlenmiyor:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;5514
&lt;span class="c"&gt;# (çıktı yok)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Misafir makinede log üretildi ve rsyslog servisi restart edilerek saveOnShutdown test edildi:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ssh ubuntu@192.168.122.50 &lt;span class="s1"&gt;'sudo systemctl restart rsyslog &amp;amp;&amp;amp; ls -la /var/spool/rsyslog/'&lt;/span&gt;
&lt;span class="nt"&gt;-rw-------&lt;/span&gt; 1 syslog syslog 193147 Aug 28 09:45 qradar_fwd.00000001
&lt;span class="nt"&gt;-rw-------&lt;/span&gt; 1 syslog syslog    567 Aug 28 09:45 qradar_fwd.qi

&lt;span class="c"&gt;# 3. Alıcı 12 dakika sonra arka planda ayağa kaldırıldı:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;nohup &lt;/span&gt;socat &lt;span class="nt"&gt;-u&lt;/span&gt; TCP-LISTEN:5514,reuseaddr,fork OPEN:/tmp/qradar-stub.log,creat,append &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;

&lt;span class="c"&gt;# 4. Yaklaşık 30 saniye sonra biriken tüm kuyruk hedefe ulaştı:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /tmp/qradar-stub.log
942 /tmp/qradar-stub.log

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;grep &lt;/span&gt;kuyruk /tmp/qradar-stub.log
&amp;lt;13&amp;gt;Aug 28 09:34:21 vm-test-01 phase3-test: kuyruk testi: alici kapaliyken gonderildi

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log satırındaki &lt;code&gt;09:34:21&lt;/code&gt; zaman damgası mimarinin başarısını kanıtlar: Mesaj alıcı kapalıyken üretilmiş, iletim 12 dakika sonra gerçekleştiği halde satır &lt;strong&gt;orijinal üretim zamanıyla&lt;/strong&gt; hedefe ulaşmıştır.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stub Yükleyici Deseni ve Marker Sözleşmesi
&lt;/h2&gt;

&lt;p&gt;Homelab ortamında Trend Micro Deep Security veya ManageEngine gibi lisanslı yazılımların ikili paketleri bulunmaz; ancak bu ajanları kuracak dağıtım hattının kendisi gerçektir. Bu ayrımı yönetmek için &lt;strong&gt;Marker Sözleşmesi (Marker Contract)&lt;/strong&gt; kurgulanmıştır.&lt;/p&gt;

&lt;p&gt;Ansible doğrudan ikili dosyanın içeriğiyle değil, kurulum tamamlandığında dosya sistemine bırakılan &lt;code&gt;.installed&lt;/code&gt; belirteciyle el sıkışır:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-eu&lt;/span&gt;
&lt;span class="nv"&gt;INSTALL_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{{ item.install_dir }}"&lt;/span&gt;
&lt;span class="nv"&gt;MARKER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{{ item.install_dir }}/.installed"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[{{ item.name }}] yukleyici basliyor: surum {{ item.version }}..."&lt;/span&gt;

&lt;span class="c"&gt;# -------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# YUKLEME BLOKU (STUB)&lt;/span&gt;
&lt;span class="c"&gt;# Lisans geldiginde YALNIZCA bu blok degistirilecek&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[{{ item.name }}] stub modu: ikili dosya yok, lisans kullanilmadi."&lt;/span&gt;
&lt;span class="c"&gt;# -------------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INSTALL_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"agent={{ item.name }}"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"version={{ item.version }}"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"mode={{ item.mode }}"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"installed_at=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-Is&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"host=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MARKER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[{{ item.name }}] marker yazildi: &lt;/span&gt;&lt;span class="nv"&gt;$MARKER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Belirteç dosyası içindeki her alan belirli bir amaca hizmet eder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;agent&lt;/code&gt; ve &lt;code&gt;version&lt;/code&gt;: &lt;code&gt;vars/agent-layer.yml&lt;/code&gt; içindeki tanımla eşleşmek zorundadır. Uyuşmazlık durumunda Ansible &lt;strong&gt;drift alarmı&lt;/strong&gt; üretir.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mode&lt;/code&gt;: Şu an &lt;code&gt;stub&lt;/code&gt; değerine kilitlidir; prodüksiyona geçildiğinde &lt;code&gt;real&lt;/code&gt; moduna çevrilir.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;installed_at&lt;/code&gt;: Adli bilişim açısından kurulum zamanını kanıtlar.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;host&lt;/code&gt;: Belirtecin başka bir sanal makineden kopyalanmadığını belgeler.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drift Alarmı ve Lisans Geçişi
&lt;/h3&gt;

&lt;p&gt;Kurulum adımı &lt;code&gt;creates: "{{ item.install_dir }}/.installed"&lt;/code&gt; korumasıyla çalışır. Bu sayede belirteç mevcutsa görev işletilmez ve temiz ikinci çalıştırmada özet &lt;code&gt;changed=0&lt;/code&gt; döner.&lt;/p&gt;

&lt;p&gt;Eğer değişken dosyasında sürüm &lt;code&gt;1.0.0-stub&lt;/code&gt; iken &lt;code&gt;1.1.0-stub&lt;/code&gt; yapılırsa:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Yükleyici betik şablonu yeni sürümle güncellenir.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;creates&lt;/code&gt; koruması nedeniyle betik tekrar çalıştırılmaz, diskteki marker eski kalır.&lt;/li&gt;
&lt;li&gt;Sonraki adımda marker içeriği okunup assertion çalıştırıldığında playbook &lt;strong&gt;hata verir ve süreci durdurur&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bu yapı kontrolsüz güncellemeyi engelleyen bir güvenlik kilididir. Yeniden kurulum için marker'ın bilinçli olarak silinmesi gerekir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ansible provisioned &lt;span class="nt"&gt;-m&lt;/span&gt; file &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"path=/opt/TrendMicro/.installed state=absent"&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt;
ansible-playbook playbooks/06_security_agents.yml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lisanslar temin edildiğinde &lt;code&gt;install.sh.j2&lt;/code&gt; dosyasındaki indirme/kurulum bloğu güncellenecek, &lt;code&gt;vars/agent-layer.yml&lt;/code&gt; içinde &lt;code&gt;mode: real&lt;/code&gt; değerine geçilecek ve dağıtım hattı mimarisi bozulmadan gerçek ajanlar devreye alınacaktır.&lt;/p&gt;




&lt;h2&gt;
  
  
  Saha Doğrulama Adımları ve Terminal Sonuçları
&lt;/h2&gt;

&lt;p&gt;Ajan katmanı sahada önce bağımsız adımlarla, ardından Semaphore UI dağıtım hattı üzerinden iki aşamada doğrulandı.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Zabbix Agent 2 Kurulumu (&lt;code&gt;04_zabbix_agent.yml&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Ubuntu 26.04 cloud imajı &lt;code&gt;/etc/apt/keyrings&lt;/code&gt; diziniyle geldiği ve &lt;code&gt;apt install&lt;/code&gt; sonrası servis paket tetikleyicisi tarafından otomatik başlatıldığı için ilk çalıştırmada 5 değişiklik gerçekleşti:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ansible-playbook playbooks/04_zabbix_agent.yml
&lt;span class="c"&gt;# ...&lt;/span&gt;
TASK &lt;span class="o"&gt;[&lt;/span&gt;Receipt - agent on duty] &lt;span class="k"&gt;***************************************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"zabbix-agent2 (repo 7.0) listening on port 10050; passive checks accepted from 192.168.122.10, active checks sent to 192.168.122.10 as host &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;vm-test-01&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;. Self-test: agent.version [s|7.0.30]"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

PLAY RECAP &lt;span class="k"&gt;***********************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;19 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aynı komut tekrar çalıştırıldığında handler tetiklenmedi ve idempotency kanıtlandı: &lt;code&gt;ok=18 changed=0&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Rsyslog İletimi (&lt;code&gt;05_rsyslog_forward.yml&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Paket sistemde kurulu geldiği için paket adımı &lt;code&gt;ok&lt;/code&gt; döndü; spool dizin izin düzeltmesi, şablon kopyalama ve servis restart adımları değişimi oluşturdu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ansible-playbook playbooks/05_rsyslog_forward.yml
&lt;span class="c"&gt;# ...&lt;/span&gt;
PLAY RECAP &lt;span class="k"&gt;***********************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;13 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İkinci çalıştırmada sistem tam kararlılığa ulaştı: &lt;code&gt;ok=12 changed=0&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Güvenlik Ajanları Dağıtımı (&lt;code&gt;06_security_agents.yml&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;İki stub ajan sisteme dağıtıldı, marker dosyaları oluşturuldu ve doğrulandı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ansible-playbook playbooks/06_security_agents.yml
&lt;span class="c"&gt;# ...&lt;/span&gt;
PLAY RECAP &lt;span class="k"&gt;***********************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;span class="c"&gt;# Sanal makinedeki marker çıktısı:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ssh ubuntu@192.168.122.50 &lt;span class="s1"&gt;'cat /opt/TrendMicro/.installed /opt/ManageEngine/.installed'&lt;/span&gt;
&lt;span class="nv"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;trendmicro
&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.0.0-stub
&lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stub
&lt;span class="nv"&gt;installed_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2026-08-28T09:12:01+00:00
&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vm-test-01
&lt;span class="nv"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;manageengine
&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.0.0-stub
&lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stub
&lt;span class="nv"&gt;installed_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2026-08-28T09:12:02+00:00
&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vm-test-01

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tekrarlanan çalıştırmada &lt;code&gt;creates&lt;/code&gt; muhafazası devreye girdi: &lt;code&gt;ok=8 changed=0&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dağıtım Hattı Entegrasyonu (Pipeline)
&lt;/h2&gt;

&lt;p&gt;Faz 4 kapsamında Semaphore UI üzerinden provizyon edilen &lt;code&gt;vm-faz4-01&lt;/code&gt; makinesinde tüm süreç otomatik tetiklendi. Süreç sırasında iki temel entegrasyon bulgusu çözüldü:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bulgu #18 (Run #31):&lt;/strong&gt; Semaphore dinamik envanter kullandığından reponun &lt;code&gt;group_vars&lt;/code&gt; tanımlarını doğrudan görmedi ve değişkenler tanımsız kaldı. Çözüm olarak tüm ajan yapılandırması &lt;code&gt;vars/agent-layer.yml&lt;/code&gt; dosyasına toplandı ve playbook'lara &lt;code&gt;vars_files&lt;/code&gt; ile bağlandı.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bulgu #19 (Run #36):&lt;/strong&gt; &lt;code&gt;apt_repository&lt;/code&gt; adımına verilen &lt;code&gt;lock_timeout&lt;/code&gt; parametresi görevi çökertti. Hatalı parametre kaldırılarak dpkg kilitleri &lt;code&gt;tasks/wait_for_apt.yml&lt;/code&gt; geçidine devredildi.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tam Başarı (Run #37):&lt;/strong&gt; Hataların ardından pipeline baştan sona eksiksiz çalıştı:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run #37 pipeline özeti (vm-faz4-01)&lt;/span&gt;
PLAY RECAP &lt;span class="k"&gt;*********************************************************************&lt;/span&gt;
localhost  : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;16 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;skipped&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;rescued&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;ignored&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
vm-faz4-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;88 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;11 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;skipped&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;rescued&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;ignored&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Makine üzerindeki &lt;code&gt;changed=11&lt;/code&gt; değeri, bağımsız çalıştırmalardaki değişimlerin toplamıdır ($5 + 3 + 3$). Otomasyonun terminalden manuel işletilmesi ile pipeline üzerinden tetiklenmesi arasında hiçbir davranış sapması yaşanmamıştır.&lt;/p&gt;

&lt;h3&gt;
  
  
  İlk Açılış Apt Kilitlenmesinin Çözümü (&lt;code&gt;tasks/wait_for_apt.yml&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Yeni klonlanan bir sanal makine ilk açıldığında cloud-init paket güncellemelerini çalıştırırken &lt;code&gt;unattended-upgrades&lt;/code&gt; de devreye girer. Bu aralıkta tetiklenen apt görevleri &lt;code&gt;dpkg lock&lt;/code&gt; hatasıyla çöker.&lt;/p&gt;

&lt;p&gt;Süreç adlarını (&lt;code&gt;pgrep unattended-upgr&lt;/code&gt;) izlemek kalıcı servisler nedeniyle sonsuz döngüye yol açtığından, nihai çözüm doğrudan kilit dosyalarını izlemek oldu:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;cloud-init status --wait&lt;/code&gt; komutuyla bulut başlatması beklenir.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/var/lib/dpkg/lock-frontend&lt;/code&gt; ve &lt;code&gt;/var/lib/apt/lists/lock&lt;/code&gt; kilit dosyalarının boşa çıkması dinlenir.&lt;/li&gt;
&lt;li&gt;Paket görevlerinde &lt;code&gt;lock_timeout: 1800&lt;/code&gt; kullanılarak yarış koşulu tamamen engellenir.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Faz 3 Kapanışı ve Sıradaki Adım
&lt;/h2&gt;

&lt;p&gt;Ajan katmanı; harici koleksiyon bağımlılığı olmadan kurulan Zabbix Agent 2 omurgası, kesintiye dayanıklı disk destekli rsyslog kuyruğu ve lisans geçişine hazır drift korumalı marker mimarisiyle tamamlandı.&lt;/p&gt;

&lt;p&gt;Süreç geride iki aktif mekanizma bıraktı:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Makine içindeki logları merkezi kuyrukla toplayan oturum bağımsız dinleyici,&lt;/li&gt;
&lt;li&gt;Hem yerel hem de uzaktan tetiklenebilen 5 basamaklı servis sağlık denetimi.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Faz 3 tamamen kapanmıştır. Sıradaki çalışma &lt;strong&gt;Faz 4: Semaphore UI ile Uçtan Uca Otomatik Dağıtım&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Form üzerinden parametrik VM provizyonu,&lt;/li&gt;
&lt;li&gt;Disklerin, LVM katmanının ve ajanların tek tıkla canlıya alınması,&lt;/li&gt;
&lt;li&gt;Dağıtım kapısı (&lt;strong&gt;Delivery Gate&lt;/strong&gt;) ile provizyon doğrulama testleri.&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.google.com/search?q=https://github.com/Hakanbaban53/proxmox-automation" rel="noopener noreferrer"&gt;GitHub Deposu&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>proxmox</category>
      <category>zabbix</category>
      <category>devops</category>
    </item>
    <item>
      <title>Proxmox Otomasyonu Faz 2.5: Depolamayı Güçlendirme, Doğrulama, fstab Kalıcılığı ve 5. Disk</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Mon, 07 Sep 2026 12:26:44 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/proxmox-otomasyonu-faz-25-depolamayi-guclendirme-dogrulama-fstab-kaliciligi-ve-5-disk-21b0</link>
      <guid>https://dev.to/hakanbaban53/proxmox-otomasyonu-faz-25-depolamayi-guclendirme-dogrulama-fstab-kaliciligi-ve-5-disk-21b0</guid>
      <description>&lt;p&gt;Bu rehber, Proxmox Otomasyon Serisi'nin üçüncü modülüdür. Faz 2 saha oturumu, depolama mimarisinin canlı ortamda kanıtlanmasıyla noktalanmıştı: Terraform üzerinde seri numaralarıyla (&lt;code&gt;serial&lt;/code&gt;) tanımlanan dört disk, idempotent bir Ansible playbook aracılığıyla LVM ve XFS katmanlarından geçirilip &lt;code&gt;/srv/*&lt;/code&gt; altına bağlanmış, çalışan sanal makinede (VM) disk boyutu 20 GB'den 30 GB'ye kesintisiz büyütülmüş ve her iddianın arkasına somut terminal çıktıları konmuştu.&lt;/p&gt;

&lt;p&gt;Bu sağlam bir zemin olsa da Faz 2 tamamlandığında arkasında henüz test edilmemiş iki büyük varsayım ve bir mimari açık bırakmıştı:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reboot Kalıcılık Testi:&lt;/strong&gt; Dosya sistemlerinin reboot sonrasında geri geleceği varsayılıyordu. &lt;code&gt;fstab&lt;/code&gt; satırları yazılmış ve LVM'in açılışta volume group'ları otomatik etkinleştirmesi bekleniyordu; fakat birisi makineyi gerçekten yeniden başlatıp kontrol edene kadar bu bir kanıt değil, bir temenniden ibarettir. Üretim ortamlarındaki veritabanı sunucularında &lt;em&gt;"makine reboot oldu ve veri diskleri geri gelmedi"&lt;/em&gt; durumu klasik bir kriz senaryosudur ve neredeyse her zaman tek satırlık bir hatadan kaynaklanır: &lt;code&gt;fstab&lt;/code&gt;'da bir yazım yanlışı, aktivasyon kapsamı dışında kalan bir VG veya açılış sürecinin bağlamayı reddettiği bir dosya sistemi.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yeni Disk İhtiyacı:&lt;/strong&gt; Faz 2'deki büyüme senaryosu mevcut bir diski büyütmeyi kapsıyordu. Oysa sahada depolama büyümesi sıklıkla ortama yeni bir diskin eklenmesi şeklinde gelir: yeni bir iş yükü için depolama ekibinden gelen taze bir LUN.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yapılandırma Kayması Riski (Configuration Drift):&lt;/strong&gt; Depolama düzeni &lt;code&gt;02_storage.yml&lt;/code&gt; playbook'unun içindeki &lt;code&gt;vars:&lt;/code&gt; bloğunda tanımlanmıştı. Tek bir playbook varken bu durum sorunsuzdu; ancak aynı listeyi doğrulayacak ikinci bir playbook geldiği anda iki dosya arasında kopyala-yapıştır yapmak kaçınılmaz bir drift riskine davetiye çıkaracaktı.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bu sağlamlaştırma adımı, çalışan parçaları bozmadan bu üç açığı kapatıyor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disk düzeni tek gerçek kaynak (&lt;strong&gt;Single Source of Truth&lt;/strong&gt;) olarak &lt;code&gt;group_vars/provisioned.yml&lt;/code&gt; dosyasına taşınıyor.&lt;/li&gt;
&lt;li&gt;Yeni yazılan &lt;code&gt;03_storage_verify.yml&lt;/code&gt; playbook'u makineyi yeniden başlatarak reboot sonrası kalıcılığını doğrulama token'ları, güncel fact'ler ve assertion'lar ile kanıtlıyor.&lt;/li&gt;
&lt;li&gt;Opsiyonel bir beşinci disk ise uçtan uca yeni disk ekleme iş akışını sınıyor.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dosya&lt;/th&gt;
&lt;th&gt;Değişiklik&lt;/th&gt;
&lt;th&gt;Amaç&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible/inventory/group_vars/provisioned.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yeni&lt;/td&gt;
&lt;td&gt;Depolama düzenini tek bir liste olarak tanımlar: &lt;code&gt;serial&lt;/code&gt;, &lt;code&gt;vg&lt;/code&gt;, &lt;code&gt;lv&lt;/code&gt;, &lt;code&gt;mount&lt;/code&gt;. Hem kurulum hem doğrulama playbook'ları buradan okur.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible/playbooks/02_storage.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Güncellendi (v3)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;data_disks&lt;/code&gt; listesi &lt;code&gt;group_vars&lt;/code&gt;'a taşındı; dosya eksikse açık bir mesajla süreci durduran koruyucu &lt;code&gt;assert&lt;/code&gt; eklendi. Görevler v2 ile aynı kaldı.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible/playbooks/03_storage_verify.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yeni (v1.1)&lt;/td&gt;
&lt;td&gt;Reboot sonrası kalıcılık doğrulaması: doğrulama token'ları, reboot, taze fact'ler, assertion'lar ve doğrulama logları. İlk saha testinde tespit edilen &lt;code&gt;follow: true&lt;/code&gt; düzeltmesini içerir.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform/variables.tf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Güncellendi&lt;/td&gt;
&lt;td&gt;Yeni değişken: &lt;code&gt;scratch_disk_size_gb&lt;/code&gt; (varsayılan: &lt;code&gt;0&lt;/code&gt; = 5. disk kapalı).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform/vm.tf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Güncellendi&lt;/td&gt;
&lt;td&gt;Değişken sıfırdan büyük olduğunda &lt;code&gt;scsi5&lt;/code&gt; arayüzünde &lt;code&gt;scratch01&lt;/code&gt; serial'li diski üreten dinamik blok (&lt;code&gt;dynamic "disk"&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform/terraform.tfvars.example&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Güncellendi&lt;/td&gt;
&lt;td&gt;5. diskin büyüme iş akışıyla birlikte belgelenen yeni parametresi.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Kapsam sınırları yine bilinçli olarak dar tutuldu:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Kapsam Dahilinde&lt;/th&gt;
&lt;th&gt;Kapsam Dışında&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kurulum ve doğrulama playbook'larının paylaştığı tek &lt;code&gt;group_vars&lt;/code&gt; listesi&lt;/td&gt;
&lt;td&gt;Terraform state'ten beslenen dinamik envanter (Faz 4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senaryolaştırılmış reboot ile boot kalıcılığının kanıtlanması&lt;/td&gt;
&lt;td&gt;Dosya sistemi kullanımının sürekli izlenmesi (Zabbix Faz 3'te geliyor)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;tfvars&lt;/code&gt; parametresiyle kontrol edilen opsiyonel beşinci disk&lt;/td&gt;
&lt;td&gt;Disk silme veya küçültme&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;02, 03 ve büyüme çalıştırmalarının gerçek saha logları (&lt;code&gt;ok=15 changed=1&lt;/code&gt;, &lt;code&gt;ok=28 changed=2 failed=0&lt;/code&gt;, büyümede &lt;code&gt;ok=15 changed=5&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Rebootsuz hızlı test modunun logları (&lt;code&gt;verify_reboot=false&lt;/code&gt; belgelendi, henüz sahada loglanmadı)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Depolama Düzeni İçin Tek Gerçek Kaynak
&lt;/h2&gt;

&lt;p&gt;Faz 2 playbook'u disk listesini play başındaki bir &lt;code&gt;vars:&lt;/code&gt; bloğunda tanımlamıştı. İkinci bir playbook aynı bilgiye ihtiyaç duyduğu anda sorun başlar: doğrulama playbook'unun da aynı serial'lere, aynı VG/LV adlarına ve aynı bağlama noktalarına ihtiyacı vardır; çünkü altyapıyı kanıtlamak, kurulum playbook'unun kurduğunu iddia ettiği şeyi birebir denetlemek demektir. İki dosyada tek bir listenin kopyasını tutmak drift'in doğduğu yerdir: beşinci bir disk eklersiniz, bir playbook'u günceller, diğerini unutursunuz ve doğrulama adımı fark ettirmeden o diski denetlemeyi bırakır.&lt;/p&gt;

&lt;p&gt;Çözüm standart Ansible yöntemidir: grup değişkenleri (&lt;code&gt;group_vars&lt;/code&gt;). Envanter zaten &lt;code&gt;provisioned&lt;/code&gt; adında bir grup tanımlıyor; bu nedenle &lt;code&gt;inventory/group_vars/provisioned.yml&lt;/code&gt; konumundaki bir dosya, hiçbir playbook kodu çalışmadan önce o gruptaki her host için otomatik olarak yüklenir. İki playbook da artık doğrudan &lt;code&gt;data_disks&lt;/code&gt; değişkenini okur ve mimari bir garanti elde edilir: depoda tek bir liste vardır.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ansible/inventory/group_vars/provisioned.yml&lt;/span&gt;
&lt;span class="c1"&gt;# Faz 2 depolama düzeni için tek gerçek kaynak (Single Source of Truth).&lt;/span&gt;
&lt;span class="c1"&gt;# playbooks/02_storage.yml (kurulum) ve&lt;/span&gt;
&lt;span class="c1"&gt;# playbooks/03_storage_verify.yml (reboot kalıcılığı kanıtı) tarafından okunur.&lt;/span&gt;

&lt;span class="na"&gt;data_disks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serial&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;var01&lt;/span&gt;      &lt;span class="c1"&gt;# scsi1, boyut: var_disk_size_gb&lt;/span&gt;
    &lt;span class="na"&gt;vg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vg_var&lt;/span&gt;
    &lt;span class="na"&gt;lv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lv_var&lt;/span&gt;
    &lt;span class="na"&gt;mount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/srv/var&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serial&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;log01&lt;/span&gt;      &lt;span class="c1"&gt;# scsi2, boyut: log_disk_size_gb&lt;/span&gt;
    &lt;span class="na"&gt;vg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vg_log&lt;/span&gt;
    &lt;span class="na"&gt;lv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lv_log&lt;/span&gt;
    &lt;span class="na"&gt;mount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/srv/log&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serial&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data01&lt;/span&gt;     &lt;span class="c1"&gt;# scsi3, boyut: data_disk_size_gb&lt;/span&gt;
    &lt;span class="na"&gt;vg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vg_data&lt;/span&gt;
    &lt;span class="na"&gt;lv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lv_data&lt;/span&gt;
    &lt;span class="na"&gt;mount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/srv/data&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serial&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backup01&lt;/span&gt;   &lt;span class="c1"&gt;# scsi4, boyut: backup_disk_size_gb&lt;/span&gt;
    &lt;span class="na"&gt;vg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vg_backup&lt;/span&gt;
    &lt;span class="na"&gt;lv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lv_backup&lt;/span&gt;
    &lt;span class="na"&gt;mount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/srv/backup&lt;/span&gt;

&lt;span class="c1"&gt;# 03_storage_verify.yml varsayılan olarak makineyi reboot eder.&lt;/span&gt;
&lt;span class="c1"&gt;# Kesintisiz hızlı kontrol (smoke test):&lt;/span&gt;
&lt;span class="c1"&gt;# ansible-playbook playbooks/03_storage_verify.yml -e verify_reboot=false&lt;/span&gt;
&lt;span class="na"&gt;verify_reboot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu dosyadaki yorum satırları SCSI arayüzünü ve &lt;code&gt;tfvars&lt;/code&gt; değişkenini her serial'in yanında tutar; çünkü bu dosya Terraform tarafı ile Ansible tarafı arasındaki bir sözleşmedir. &lt;code&gt;verify_reboot&lt;/code&gt; değişkeni de playbook'un derinliklerine gömülmek yerine buraya kondu; böylece operatör ayarları aradığı yerde bulur.&lt;/p&gt;

&lt;p&gt;Listeyi playbook dışına taşımak dosya bulunamadığında ne olacağını değiştirir; bu yüzden her iki playbook da en başta bir koruyucu &lt;code&gt;assert&lt;/code&gt; ile açılır. Yanlış dizinden ya da bu grubu içermeyen bir envanterle çalıştırma durumunda süreç ortalarda tanımsız değişken hatasıyla patlamak yerine, henüz hiçbir şeye dokunmadan neden durduğunu açıkça bildirir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Require the storage layout (group_vars/provisioned.yml)&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.assert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;that&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data_disks is defined and data_disks | length &amp;gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="na"&gt;fail_msg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
      &lt;span class="s"&gt;data_disks tanımlanmamış. Depolama düzeni&lt;/span&gt;
      &lt;span class="s"&gt;ansible/inventory/group_vars/provisioned.yml dosyasında yaşar;&lt;/span&gt;
      &lt;span class="s"&gt;bu dosyanın var olduğunu ve playbook'un reponun kendi ansible.cfg'siyle,&lt;/span&gt;
      &lt;span class="s"&gt;provisioned grubuna karşı çalıştırıldığını kontrol edin.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ansible öncelik sıralaması (precedence) burada kritik bir tasarım kararı içerir:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Öncelik (Düşükten Yükseğe)&lt;/th&gt;
&lt;th&gt;Bu Tasarımdaki Anlamı&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;group_vars/provisioned.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tek gerçek kaynak; her iki playbook da olduğu gibi okur.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Playbook içindeki &lt;code&gt;vars:&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;group_vars&lt;/code&gt;'ı override eder; &lt;code&gt;02_storage.yml&lt;/code&gt; içinden özellikle kaldırıldı ki dosyadaki listeyle yarışamasın.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Komut satırındaki &lt;code&gt;--extra-vars&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Her şeyi override eder; tek seferlik geçici denemeler için belgelenmiş kaçış kapısı (&lt;code&gt;-e verify_reboot=false&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Reboot Testi: &lt;code&gt;03_storage_verify.yml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Faz 2 saha oturumu depolama katmanını bırakıldığı anki haliyle doğrulamıştı: bağlı, boyutlandırılmış ve &lt;code&gt;df&lt;/code&gt; üzerinden okunabilir durumda. Ancak makinenin bir reboot sonrasında ayağa kalkıp kalkmayacağı test edilmemişti.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fstab&lt;/code&gt; girdileri yalnızca açılışta okunur. Açılıştaki LVM aktivasyonu, kurulum playbook'unun tetiklediği açık çalıştırmadan tamamen farklı bir mekanizmadır. Elle &lt;code&gt;mount&lt;/code&gt; komutu çalıştırıldığı için ayakta duran bir dosya sistemi ile kalıcı olan bir dosya sistemi, ilk yeniden başlatmaya kadar birbirinden ayırt edilemez.&lt;/p&gt;

&lt;p&gt;Doğrulama playbook'u, dikkatli bir sistem mühendisinin elle yapacağı adımları tekrarlanabilir çıktılara döker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reboot Öncesi Belirteçler (Token &amp;amp; Marker)
&lt;/h3&gt;

&lt;p&gt;Kalıcılığı dizin adıyla değil içerikle denetlemek gerekir; çünkü yeni formatlanmış boş bir disk de aynı mount point'e bağlanabilir ama içindeki veriyi kaybetmiştir. Playbook, reboot öncesinde her dosya sistemine benzersiz bir token yazar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mint a one-run verification token&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.set_fact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;verify_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;phase2-{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_facts.date_time.epoch&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}-{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;100000&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;random&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Write a marker file into every data filesystem&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.copy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.mount&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;verify_token&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.mount&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/phase2-verify.txt"&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0644"&lt;/span&gt;
  &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;data_disks&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Token, her çalıştırmanın yalnızca kendi yazdığını doğrulaması için üretilir. Sabit bir string'e karşı kontrol yapmak önceki çalıştırmadan kalan bayat bir dosyada bile geçer; oysa reboot'tan saniyeler önce üretilen bir token'ı aramak, okunan verinin tam o oturumda diske yazıldığını garantiler. Dosya içeriği mount yolu ile token'ın birleşimidir; yani yanlışlıkla başka diske yazılan bir belirteç de doğrulamadan geçemez.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reboot ve Fact Yenileme Mantığı
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Reboot the VM (the moment of truth for fstab and LVM)&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.reboot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;msg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Phase&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;verification&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;reboot:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;proving&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;fstab&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;+&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;LVM&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;boot&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;persistence"&lt;/span&gt;
    &lt;span class="na"&gt;pre_reboot_delay&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;post_reboot_delay&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;reboot_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;
    &lt;span class="na"&gt;test_command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uptime"&lt;/span&gt;
  &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;verify_reboot | default(true) | bool&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Refresh facts after the reboot (pre-reboot facts are stale)&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.setup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;verify_reboot | default(true) | bool&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu iki görevde iki hayati teknik detay bulunur:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;ansible.builtin.setup&lt;/code&gt; görevi gereksiz görünür ama zorunludur. &lt;code&gt;gather_facts&lt;/code&gt; play'in en başında tek bir kez çalışır; dolayısıyla reboot sonrasında &lt;code&gt;ansible_facts.mounts&lt;/code&gt; listesi hâlâ makinenin açılış öncesi dünyasını tarif eder. Reboot sonrası &lt;code&gt;setup&lt;/code&gt; modülünü çağırmak, mount kontrollerinin güncel gerçeği okumasını sağlar.&lt;/li&gt;
&lt;li&gt;Koşulda yer alan &lt;code&gt;| bool&lt;/code&gt; filtresi zorunludur. Komut satırından &lt;code&gt;--extra-vars&lt;/code&gt; ile geçilen değerler Ansible'a string olarak ulaşır. Jinja motorunda &lt;code&gt;"false"&lt;/code&gt; string'i truthy (doğru kabul edilen) bir değerdir; bu filtre konmazsa &lt;code&gt;-e verify_reboot=false&lt;/code&gt; parametresi verildiğinde playbook tam da reboot etmemesini istediğiniz anda makineyi yeniden başlatır.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  LVM Kontrolü: Neden &lt;code&gt;lv_attr&lt;/code&gt; Karakterleri Değil?
&lt;/h3&gt;

&lt;p&gt;LVM, volüm durumunu &lt;code&gt;lv_attr&lt;/code&gt; öznitelik dizgisinin 5. karakterinde kodlar (&lt;code&gt;a&lt;/code&gt; = active). Ancak pozisyona dayalı karakter okumak, araçlar raporlama düzenini değiştirdiğinde sessizce patlayan kırılgan bir yöntemdir.&lt;/p&gt;

&lt;p&gt;Playbook bunun yerine &lt;code&gt;lvs&lt;/code&gt; komutundan JSON raporu alarak volüm sayısını doğrular, ardından sistemin doğrudan kendisine sorar: aktif bir mantıksal volüm aygıt düğümünü &lt;code&gt;/dev/vg/lv&lt;/code&gt; altında sunar, aktif olmayan bir volümün düğümü oluşmaz. Bu yüzden her volüm için &lt;code&gt;stat&lt;/code&gt; çalıştırmak hem daha sade hem daha güvenilir bir testtir.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Read the LVM logical volume report (JSON)&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cmd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lvs --reportformat json --noheadings -o vg_name,lv_name,lv_attr&lt;/span&gt;
  &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lvs_state&lt;/span&gt;
  &lt;span class="na"&gt;changed_when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Parse the LVM report&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.set_fact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;lv_rows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(lvs_state.stdout&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from_json).report[0].lv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Exactly the expected logical volumes must exist&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.assert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;that&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lv_rows | length == data_disks | length&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check every logical volume's device node&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.stat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/dev/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.vg&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.lv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;follow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# /dev/vg/lv -&amp;gt; /dev/dm-X symlink'ini takip etmek şart&lt;/span&gt;
  &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;data_disks&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
  &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lv_nodes&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Every logical volume must be active after the reboot&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.assert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;that&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lv_nodes.results[idx].stat.exists&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lv_nodes.results[idx].stat.isblk&lt;/span&gt;
    &lt;span class="na"&gt;fail_msg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
      &lt;span class="s"&gt;The volume group {{ item.vg }} was not activated after the reboot.&lt;/span&gt;
      &lt;span class="s"&gt;Diagnose inside the VM with: systemctl status lvm2-activation.service,&lt;/span&gt;
      &lt;span class="s"&gt;then check vgchange -ay.&lt;/span&gt;
    &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;data_disks&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;loop_control&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;index_var&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;idx&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İlk saha oturumunun öğrettiği &lt;code&gt;follow: true&lt;/code&gt; dersi tam buraya aittir. &lt;code&gt;/dev/vg/lv&lt;/code&gt; bir blok aygıtı değil, udev tarafından yönetilen bir sembolik bağdır (&lt;code&gt;symlink&lt;/code&gt;). &lt;code&gt;stat&lt;/code&gt; modülüne symlink'i takip etmesi söylenmediğinde bağın kendisini inceler; sonuç olarak &lt;code&gt;islnk: true&lt;/code&gt; dönerken &lt;code&gt;isblk: false&lt;/code&gt; kalır ve çalışan sistemde sahte bir alarm (false positive) üretir.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fstab&lt;/code&gt; kontrolü ise mount modülünün yazdığı satırı doğrudan denetler: &lt;code&gt;/dev/mapper&lt;/code&gt; alias'ı yerine verilen tam aygıt yolu (&lt;code&gt;/dev/vg_data/lv_data&lt;/code&gt;) ve bağlama noktası. Hem aygıtı hem hedefi aramak iki arıza modunu birden yakalar: satırın hiç olmaması veya olup yanlış yere işaret etmesi.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kesintisiz Hızlı Kontrol Modu (&lt;code&gt;verify_reboot=false&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Reboot istenmeyen anlarda playbook &lt;code&gt;-e verify_reboot=false&lt;/code&gt; ile çalıştırılarak kesintisiz bir denetim aracına (smoke test) dönüşür. Bu modda hangi kontrolün neyi kanıtladığı net biçimde bilinmelidir:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Denetim&lt;/th&gt;
&lt;th&gt;Reboot İle (Varsayılan)&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;verify_reboot=false&lt;/code&gt; İle (Smoke Test)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Belirteç dosyaları (byte-to-byte)&lt;/td&gt;
&lt;td&gt;Veri açılışı atlattı&lt;/td&gt;
&lt;td&gt;Yalnızca oturum içi doğrulama&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mount durumu (&lt;code&gt;fstype: xfs&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fstab&lt;/code&gt; açılış aktivasyonu çalıştı&lt;/td&gt;
&lt;td&gt;Mevcut bağlı durum doğru&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LV aygıt düğümleri mevcut&lt;/td&gt;
&lt;td&gt;Açılış zamanı LVM aktivasyonu çalıştı&lt;/td&gt;
&lt;td&gt;LV'ler şu anda aktif&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;fstab&lt;/code&gt; satırları mevcut&lt;/td&gt;
&lt;td&gt;Kalıcılık beyan edilmiş, sadece canlıda kalmamış&lt;/td&gt;
&lt;td&gt;Aynı&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serial'ler tek diske çözülüyor&lt;/td&gt;
&lt;td&gt;Kimlik şeması açılışı atlattı&lt;/td&gt;
&lt;td&gt;Kimlik şeması şu an geçerli&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Dört Diskten Fazlası: &lt;code&gt;scratch01&lt;/code&gt; Genişletme İş Akışı
&lt;/h2&gt;

&lt;p&gt;Faz 2'deki büyütme senaryosu en yaygın talebi yanıtlamıştı: aynı diski büyütmek. İkinci yaygın talep ise yeni bir iş yükü için yeni bir disk eklemektir.&lt;/p&gt;

&lt;p&gt;Manuel dünyada bu işlem hipervizörde ayrı, guest içinde ayrı konsol adımları demektir ve aygıt harfi tahmin oyununun en çok can sıktığı yerdir; çünkü yeni bir disk kendisinden sonraki disklerin sıralamasını kaydırabilir.&lt;/p&gt;

&lt;p&gt;Terraform tarafı bunu dinamik bir blokla çözer. HCL blok seviyesinde doğrudan &lt;code&gt;if&lt;/code&gt; anahtarı sunmaz; bu yüzden parametre verildiğinde tek elemanlı, verilmediğinde boş liste dönen bir &lt;code&gt;dynamic "disk"&lt;/code&gt; bloğu kullanılır. Varsayılan sıfırken plan içinde &lt;code&gt;scsi5&lt;/code&gt; diski hiç yer almaz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# terraform/vm.tf (Opsiyonel 5. veri diski)&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"scratch_disk_size_gb"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Opsiyonel 5. veri diski (serial: scratch01), GB. 0 = kapali."&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# proxmox_virtual_environment_vm.test_vm kaynagi icinde:&lt;/span&gt;
&lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="s2"&gt;"disk"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scratch_disk_size_gb&lt;/span&gt; &lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scratch_disk_size_gb&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;datastore_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_id&lt;/span&gt;
    &lt;span class="nx"&gt;interface&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scsi5"&lt;/span&gt;
    &lt;span class="nx"&gt;size&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;disk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
    &lt;span class="nx"&gt;serial&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scratch01"&lt;/span&gt;
    &lt;span class="nx"&gt;ssd&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;discard&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ansible tarafında yeni kod yazılmaz, sadece sözleşmeye yeni bir satır eklenir. Sorumluluk dağılımı bellidir:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Taraf&lt;/th&gt;
&lt;th&gt;Sorumluluk Alanı&lt;/th&gt;
&lt;th&gt;Tanımlandığı Yer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Terraform&lt;/td&gt;
&lt;td&gt;Hangi disklerin var olduğu, arayüzler, boyutlar, serial'ler&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;terraform/vm.tf&lt;/code&gt; + &lt;code&gt;tfvars&lt;/code&gt; ayarları&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ansible&lt;/td&gt;
&lt;td&gt;Her serial'in guest içinde neye dönüştüğü: &lt;code&gt;vg&lt;/code&gt;, &lt;code&gt;lv&lt;/code&gt;, mount noktası&lt;/td&gt;
&lt;td&gt;&lt;code&gt;inventory/group_vars/provisioned.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kernel&lt;/td&gt;
&lt;td&gt;Her serial'in o anda hangi disk harfi olduğu&lt;/td&gt;
&lt;td&gt;Her çalıştırmada sıfırdan çözülür, asla kalıcı yazılmaz&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Büyütme (Resize) ile Yeni Disk Ekleme (Attach) Arasındaki Asimetri
&lt;/h3&gt;

&lt;p&gt;Saha oturumu bu iki eylem arasındaki farkı netleştirdi:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operasyon&lt;/th&gt;
&lt;th&gt;VM Durumu&lt;/th&gt;
&lt;th&gt;Serial Guest'te Görünür mü?&lt;/th&gt;
&lt;th&gt;Görünmezse Yapılacak Eylem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mevcut diski büyütmek (&lt;code&gt;data&lt;/code&gt; 20 -&amp;gt; 30 GB)&lt;/td&gt;
&lt;td&gt;Çalışıyor (Online)&lt;/td&gt;
&lt;td&gt;Anında (Kernel görünümü anında yenilenir)&lt;/td&gt;
&lt;td&gt;Gerek yok; sahada kanıtlanmış online işlem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yeni disk takmak (&lt;code&gt;scsi5&lt;/code&gt; &lt;code&gt;scratch01&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Çalışıyor (Online)&lt;/td&gt;
&lt;td&gt;Tam bir VM başlatması gerektirebilir (saha oturumunda: anında görüldü)&lt;/td&gt;
&lt;td&gt;PVE host üzerinde: &lt;code&gt;qm shutdown 200 &amp;amp;&amp;amp; qm start 200&lt;/code&gt;, sonra 02'yi tekrar çalıştır&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tam bir VM başlatmasından sonraki durum&lt;/td&gt;
&lt;td&gt;Taze başladı&lt;/td&gt;
&lt;td&gt;Her zaman (konfigürasyon ve QEMU hemfikir)&lt;/td&gt;
&lt;td&gt;Teşhis: `qm config 200&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Büyütme online, yeni takma da (genellikle) online:&lt;/strong&gt;&lt;br&gt;
Mevcut diski büyütmek tamamen kesintisiz kalır. Yeni disk takmak da saha oturumunda makine çalışırken online tamamlandı; ancak serial'in konfigürasyonun gerisinde kaldığı durumlar için belgelenmiş tam kapat-aç adımı el altında beklemelidir.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Uçtan uca büyüme iş akışı:&lt;br&gt;
{% raw %}&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. ctrl-01'de: diski aktif et (terraform/terraform.tfvars)&lt;/span&gt;
scratch_disk_size_gb &lt;span class="o"&gt;=&lt;/span&gt; 5

&lt;span class="c"&gt;# 2. apply: disk makine calisirken takilir&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/proxmox-automation/terraform &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; terraform apply

&lt;span class="c"&gt;# 3. duzen girdisini group_vars/provisioned.yml dosyasina ekle&lt;/span&gt;
&lt;span class="c"&gt;# - serial: scratch01&lt;/span&gt;
&lt;span class="c"&gt;#   vg: vg_scratch&lt;/span&gt;
&lt;span class="c"&gt;#   lv: lv_scratch&lt;/span&gt;
&lt;span class="c"&gt;#   mount: /srv/scratch&lt;/span&gt;

&lt;span class="c"&gt;# 4. baglamayi insa et (ayni playbook, fazladan bir dongu iterasyonu)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../ansible &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ansible-playbook playbooks/02_storage.yml

&lt;span class="c"&gt;# 5. Eger 4. adim scratch01 NOT FOUND derse: PVE node'unda tam durdur/baslat&lt;/span&gt;
&lt;span class="c"&gt;# qm shutdown 200 &amp;amp;&amp;amp; qm start 200&lt;/span&gt;
&lt;span class="c"&gt;# ardindan 4. adimi tekrar calistir&lt;/span&gt;

&lt;span class="c"&gt;# 6. reboot dahil kanitla&lt;/span&gt;
ansible-playbook playbooks/03_storage_verify.yml
ssh ubuntu@192.168.122.50 &lt;span class="s1"&gt;'df -h /srv/scratch'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Upstream İmaj Yenilenmesi Durumu
&lt;/h3&gt;

&lt;p&gt;Beşinci diski takarken çalıştırılan apply, plan çıktısında beklenmedik bir satır getirdi: &lt;code&gt;Plan: 1 to add, 2 to change, 1 to destroy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Yok edilen nesne VM değildi; &lt;code&gt;proxmox_download_file.ubuntu_image&lt;/code&gt; kaynağıydı. Canonical, Ubuntu cloud imajını upstream depoda güncellemiş, dosya boyutu 861.837.824 bayttan 864.818.688 bayta çıkmıştı. Provider boyut uyuşmazlığını görünce eski dosyayı silip yenisini indirdi. Apply süresi bu indirme nedeniyle 5 dakika sürdü; fakat VM yerinde güncellendi, kesinti yaşanmadı. Bu tazelik yerine tam tekrarlanabilirlik isteyenler &lt;code&gt;template.tf&lt;/code&gt; içinde &lt;code&gt;overwrite = false&lt;/code&gt; parametresini kullanabilir.&lt;/p&gt;


&lt;h2&gt;
  
  
  Saha Oturumu ve Terminal Çıktıları
&lt;/h2&gt;

&lt;p&gt;Aşağıdaki runbook tablosu Faz 2 Addendum'un sahadaki tam icra sırasıdır:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Makine&lt;/th&gt;
&lt;th&gt;Komut&lt;/th&gt;
&lt;th&gt;Geri Alınan Çıktı&lt;/th&gt;
&lt;th&gt;Durum&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ctrl-01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cd ~/proxmox-automation &amp;amp;&amp;amp; unzip -o proxmox-automation.zip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dosyalar güncellendi&lt;/td&gt;
&lt;td&gt;Tamamlandı&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ctrl-01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cd ansible &amp;amp;&amp;amp; ansible-playbook playbooks/02_storage.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PLAY RECAP&lt;/td&gt;
&lt;td&gt;Saha: &lt;code&gt;ok=15 changed=1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ctrl-01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook playbooks/03_storage_verify.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Debug satırları, assert satırları, PLAY RECAP&lt;/td&gt;
&lt;td&gt;Saha: &lt;code&gt;ok=28 changed=2 failed=0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ctrl-01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook playbooks/03_storage_verify.yml -e verify_reboot=false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rebootsuz mod recap çıktısı&lt;/td&gt;
&lt;td&gt;Opsiyonel, henüz çalıştırılmadı&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ctrl-01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tfvars düzenle: &lt;code&gt;scratch_disk_size_gb = 5&lt;/code&gt;, sonra &lt;code&gt;terraform apply&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Apply çıktısı (imaj tazeleme + scsi5, VM yerinde)&lt;/td&gt;
&lt;td&gt;Saha: tamamlandı (5 dk: imaj indirme)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ctrl-01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;scratch01&lt;/code&gt;'i group_vars'a ekle, &lt;code&gt;02_storage.yml&lt;/code&gt; tekrar çalıştır&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;scratch01&lt;/code&gt; mapping satırı, PLAY RECAP&lt;/td&gt;
&lt;td&gt;Saha: &lt;code&gt;ok=15 changed=5&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;ok=14&lt;/code&gt;'ten &lt;code&gt;ok=15&lt;/code&gt;'e Geçişin Mantığı
&lt;/h3&gt;

&lt;p&gt;Faz 2 rehberinde &lt;code&gt;02_storage.yml&lt;/code&gt; için recap &lt;code&gt;ok=14 changed=1&lt;/code&gt; olarak kaydedilmişti. v3 refactor'ünde liste &lt;code&gt;group_vars&lt;/code&gt;'a taşındı ve play başına koruyucu assert görevi eklendi. Geçen bir assertion recap'te fazladan bir &lt;code&gt;ok&lt;/code&gt; demektir: 14 görev artı koruyucu assert eşittir 15. Depolama işinin kendisinde hiçbir şey değişmedi; recap'ler arasındaki bir görevlik fark, &lt;code&gt;group_vars&lt;/code&gt; hamlesinin sisteme gizli bir yük bindirmediğinin somut kanıtıdır.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible-playbook playbooks/02_storage.yml
&lt;span class="c"&gt;# ... eslestirme ve kontroller gecti ...&lt;/span&gt;
TASK &lt;span class="o"&gt;[&lt;/span&gt;Create/resize physical volumes] &lt;span class="k"&gt;******************************************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;var01&lt;span class="o"&gt;)&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;log01&lt;span class="o"&gt;)&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;data01&lt;span class="o"&gt;)&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;backup01&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# pvresize gecisi&lt;/span&gt;

PLAY RECAP &lt;span class="k"&gt;*********************************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;15 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  İlk Doğrulama Denemesi ve Düzeltme Adımı
&lt;/h3&gt;

&lt;p&gt;İlk doğrulama denemesi reboot'a kadar yeşil aktı: token'lar yazıldı, eşleşme alındı, makine yeniden başladı, token'lar byte'ı byte'ına geri okundu. Ancak mantıksal volüm kontrolünde sistem sahte bir alarmla (false positive) durdu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible-playbook playbooks/03_storage_verify.yml
TASK &lt;span class="o"&gt;[&lt;/span&gt;Every logical volume must be active after the reboot] &lt;span class="k"&gt;********************&lt;/span&gt;
fatal: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01]: FAILED! &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vg_var/lv_var&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"assertion"&lt;/span&gt;: &lt;span class="s2"&gt;"lv_nodes.results[0].stat.isblk"&lt;/span&gt;,
    &lt;span class="s2"&gt;"evaluated_to"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"The volume group vg_var was not activated after the reboot ..."&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# ... (vg_log, vg_data, vg_backup: dördü birden ayni anda basarisiz)&lt;/span&gt;

PLAY RECAP &lt;span class="k"&gt;*********************************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;18 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;follow: true&lt;/code&gt; eklenip düzeltme doğrulandıktan sonra ikinci deneme başarıyla sonuçlandı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"follow:"&lt;/span&gt; playbooks/03_storage_verify.yml
    follow: &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible-playbook playbooks/03_storage_verify.yml
TASK &lt;span class="o"&gt;[&lt;/span&gt;Show the mapping before the reboot] &lt;span class="k"&gt;**************************************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;var01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"var01 = /dev/sde"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# ... (log01 = /dev/sdd, data01 = /dev/sdc, backup01 = /dev/sdb)&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Reboot the VM &lt;span class="o"&gt;(&lt;/span&gt;the moment of truth ...&lt;span class="o"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;*********************************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01]

TASK &lt;span class="o"&gt;[&lt;/span&gt;Every logical volume must be active after the reboot] &lt;span class="k"&gt;********************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vg_var/lv_var&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"All assertions passed"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# ... (vg_log/lv_log, vg_data/lv_data, vg_backup/lv_backup)&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Show the mapping before vs after ...] &lt;span class="k"&gt;************************************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;var01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"var01: /dev/sde before -&amp;gt; /dev/sde after (vg_var/lv_var on /srv/var)"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;backup01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"backup01: /dev/sdb before -&amp;gt; /dev/sdb after (vg_backup/lv_backup on /srv/backup)"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# ... (log01, data01)&lt;/span&gt;

PLAY RECAP &lt;span class="k"&gt;*********************************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;28 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Buradaki sayılar net bir hesaba dayanır:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;changed=2&lt;/code&gt;: Doğrulama çalıştırması için beklenen minimumdur; token dosyaları her çalıştırmada taze yazılır ve &lt;code&gt;reboot&lt;/code&gt; modülü gerçek bir eylemdir.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ok=28&lt;/code&gt;: İlk denemedeki &lt;code&gt;ok=18&lt;/code&gt;'in üzerine 10 yeni kontrol eklenmedi; ilk deneme assert'te durduğu için çalışamayan boot sonrası eşleşme, &lt;code&gt;fstab&lt;/code&gt; çıktıları, &lt;code&gt;findmnt&lt;/code&gt;, &lt;code&gt;lsblk&lt;/code&gt; ve &lt;code&gt;df&lt;/code&gt; görevleri nihayet çalışabildi.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bu ikinci denemede harfler de değişmedi (&lt;code&gt;var01&lt;/code&gt; hem öncesinde hem sonrasında &lt;code&gt;sde&lt;/code&gt;); ama bu bir garanti değil, o boot'a özgü bir şans. Şema harflerin sabit kalmasına değil, sadece serial'lerin sabit kalmasına güveniyor; birkaç bölüm sonraki büyüme çalıştırması, harfler gerçekten döndüğünde de aynı yeşil sonucu veriyor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Genişletme Çalıştırması: Aygıt Harflerinin Kayması Testi
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;scratch_disk_size_gb = 5&lt;/code&gt; ile disk eklendikten sonra &lt;code&gt;02_storage.yml&lt;/code&gt; çalıştırıldı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible-playbook playbooks/02_storage.yml
TASK &lt;span class="o"&gt;[&lt;/span&gt;Show the serial to device mapping ...] &lt;span class="k"&gt;***********************************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;scratch01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"scratch01 (vg_scratch/lv_scratch -&amp;gt; /srv/scratch) = /dev/sdf"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Create volume &lt;span class="nb"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;one per disk&lt;span class="o"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;*************************************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vg_scratch&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# (vg_var, vg_log, vg_data, vg_backup: ok, dokunulmadi)&lt;/span&gt;

PLAY RECAP &lt;span class="k"&gt;*********************************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;15 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;changed=5&lt;/code&gt; tam olarak yeni bir diskin ilk çalıştırma işini yapan beş görevdir: PV görevi (&lt;code&gt;pvresize&lt;/code&gt; geçişi), VG, LV, XFS ve mount. Mevcut dört disk hiçbir değişiklik bildirmedi; VG, LV, XFS ve mount maddeleri &lt;code&gt;ok&lt;/code&gt; olarak döndü.&lt;/p&gt;

&lt;p&gt;Ardından çalıştırılan doğrulama playbook'u, bu tasarımın en büyük sınavını verdi. Beşinci disk takıldıktan sonra yapılan reboot sırasında kernel aygıt harflerini tamamen döndürdü:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible-playbook playbooks/03_storage_verify.yml
TASK &lt;span class="o"&gt;[&lt;/span&gt;Show the mapping before vs after ...] &lt;span class="k"&gt;************************************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;var01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"var01: /dev/sde before -&amp;gt; /dev/sdf after (vg_var/lv_var on /srv/var)"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;scratch01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"scratch01: /dev/sdf before -&amp;gt; /dev/sdb after (vg_scratch/lv_scratch on /srv/scratch)"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# (log01: sdd -&amp;gt; sde, data01: sdc -&amp;gt; sdd, backup01: sdb -&amp;gt; sdc)&lt;/span&gt;

PLAY RECAP &lt;span class="k"&gt;*********************************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;28 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Açılışta &lt;code&gt;var01&lt;/code&gt; diski &lt;code&gt;sde&lt;/code&gt;'den &lt;code&gt;sdf&lt;/code&gt;'ye kaydı; yeni eklenen &lt;code&gt;scratch01&lt;/code&gt; ise &lt;code&gt;sdf&lt;/code&gt; iken açılışta &lt;code&gt;sdb&lt;/code&gt; oldu. Diğer tüm komşular birer koltuk ötelendi.&lt;/p&gt;

&lt;p&gt;Buna rağmen çalıştırma tamamen yeşil kaldı; çünkü yığındaki hiçbir şey bir harfi adreslemiyor: &lt;code&gt;fstab&lt;/code&gt; &lt;code&gt;/dev/vg/lv&lt;/code&gt; yollarını arıyor, LVM disklerin üzerindeki meta veriyi okuyor ve playbook'lar her çalıştırmada haritalamayı kernel görüşünden taze kuruyor. Recap yine &lt;code&gt;ok=28 changed=2&lt;/code&gt; kaldı; çünkü listeye beşinci bir girdi eklemek görev sayısını değil, döngü iterasyonlarını artırır: playbook makineyle birlikte büyüyor, hareketli parça sayısı ise hiç artmıyor.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Operasyonel Not:&lt;/strong&gt; Kontrol düğümünde &lt;code&gt;df -h /srv/scratch&lt;/code&gt; çalıştırmak "No such file or directory" der; çünkü mount kontrol düğümünde değil, misafir makinededir. Doğrulama çıktısı SSH üzerinden misafirden okunmalıdır:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~# ssh ubuntu@192.168.122.50 &lt;span class="s1"&gt;'df -h /srv/scratch'&lt;/span&gt;
Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/vg_scratch-lv_scratch  5.0G  130M  4.9G   3% /srv/scratch

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Depolama Katmanı Kapandı: Sırada Ne Var?
&lt;/h2&gt;

&lt;p&gt;Depolama katmanı serinin taahhüt ettiği her iki boyutta da tamamlandı: golden template ve dört serial ile deklaratif olarak inşa edildi, tek bir &lt;code&gt;tfvars&lt;/code&gt; düzenlemesiyle online büyütüldü ve bu addendum ile birlikte tek bir komutla reboot sınavından geçirildi.&lt;/p&gt;

&lt;p&gt;Tek gerçek kaynaklı &lt;code&gt;group_vars&lt;/code&gt; mimarisi, doğrulama playbook'u ve büyüme iş akışı sahada bizzat çalıştı. Alınan dersler; &lt;code&gt;stat&lt;/code&gt;'ın sembolik bağları yalnızca açıkça istendiğinde takip ettiği, serial hotplug'ın şanslı bir günde online tamamlandığı ve upstream bir imajın pipeline ortasında kendini yenileyebileceği, gelecekteki çalıştırmaların bulacağı yerlere not edildi.&lt;/p&gt;

&lt;p&gt;Her depolama değişikliğinden (büyütme ya da yeni disk ekleme) sonra operasyonel alışkanlık aynıdır:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Önce kurulum playbook'unu çalıştır (&lt;code&gt;02_storage.yml&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;Ardından doğrulama playbook'unu çalıştır (&lt;code&gt;03_storage_verify.yml&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;İlki durumu yakınsar; ikincisi makinenin gece saat 3'te tek başına ayağa kalkabileceğini somut terminal çıktılarıyla kanıtlar.&lt;/p&gt;

&lt;p&gt;Faz 2 tamamen kapandı. Sıradaki durak Faz 3:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Template içindeki eski &lt;code&gt;/install&lt;/code&gt; dizinini gerçek Ansible rollerine dönüştürmek,&lt;/li&gt;
&lt;li&gt;Mount noktalarına anlık denetim yerine kalıcı bir gözlemci atayacak &lt;strong&gt;Zabbix Agent 2&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;Merkezi güvenlik ajanları ve log diskini salt bir depolama alanından pipeline parçasına dönüştürecek &lt;strong&gt;QRadar rsyslog&lt;/strong&gt; yönlendirme kuralları.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://github.com/Hakanbaban53/proxmox-automation" rel="noopener noreferrer"&gt;GitHub Deposu&lt;/a&gt;&lt;/p&gt;

</description>
      <category>proxmox</category>
      <category>terraform</category>
      <category>ansible</category>
      <category>devops</category>
    </item>
    <item>
      <title>Proxmox Otomasyonu Faz 2: Serial ile Kimliklendirilmiş Depolama, Kesintisiz LVM + XFS</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Fri, 04 Sep 2026 06:15:59 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/proxmox-otomasyonu-faz-2-serial-ile-kimliklendirilmis-depolama-kesintisiz-lvm-xfs-4acb</link>
      <guid>https://dev.to/hakanbaban53/proxmox-otomasyonu-faz-2-serial-ile-kimliklendirilmis-depolama-kesintisiz-lvm-xfs-4acb</guid>
      <description>&lt;p&gt;Bir önceki fazda pipeline'ın temelini attık: tek bir &lt;code&gt;terraform apply&lt;/code&gt; Ubuntu 26.04 imajını indirip golden template'i kuruyor, cloud-init'li klonu (vm-test-01, VMID 200) ayağa kaldırıyor, ilk temas playbook'u Ansible'la yönetim kanalını açıyordu. VM hâlâ ayakta, guest agent çalışıyor, repo git'te. Ama o makine tek diskli ve production'da hiçbir DB sunucusu tek diskle çalışmıyor: &lt;code&gt;/var&lt;/code&gt;, log, data, backup ayrı LUN'larda, her biri kendi büyüme takvimiyle.&lt;/p&gt;

&lt;p&gt;Şeffaflık adına küçük bir not, inşa bölümlerine girmeden önce: bu rehberin arkasındaki saha oturumu, Faz 2'nin başında lab'ı sıfırdan kurdu. Yani buradaki transkriptleri taşıyan makine birebir Faz 1'in bitirdiği VM değildi; aynı pipeline'ın, aynı koddan ürettiği taze bir klondu. Ama rebuild'in kendisi bu rehberin en değerli iki sorun giderme satırını hediye etti: temizlik sırasında silinmeyip kalan bir template'e çarpan ilk apply ve ilk girişte karşılayan taze SSH host anahtarları. İkisi de aşağıda, gerçek transkriptleriyle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hedef: dört disk, kalıcı kimlik
&lt;/h2&gt;

&lt;p&gt;Faz 2'nin tamamı yine tek cümleye sığıyor: DB sınıfı bir iş yükünün dört diski Terraform'da tanımlanmalı, her diskin kalıcı bir kimliği olmalı, guest tarafı bu kimliklerden LVM üstünden XFS'e uzanan bir zincirle bağlanmalı ve büyüme isteği tek değişkenle, makine çalışırken çözülmeli.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bileşen&lt;/th&gt;
&lt;th&gt;Sürüm/Not&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Proxmox VE&lt;/td&gt;
&lt;td&gt;9.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform&lt;/td&gt;
&lt;td&gt;1.15.x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bpg/proxmox provider&lt;/td&gt;
&lt;td&gt;0.111.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansible-core&lt;/td&gt;
&lt;td&gt;2.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yeni bağımlılık&lt;/td&gt;
&lt;td&gt;community.general, ansible.posix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dosya sistemi&lt;/td&gt;
&lt;td&gt;XFS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Kapsam, Faz 1'deki gibi bilinçli olarak dar tutuluyor; her hareketli parça tek başına doğrulanabilsin diye:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Faz 2'de var&lt;/th&gt;
&lt;th&gt;Sonraki fazlara ertelendi&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Workload VM'de dört serial etiketli veri diski&lt;/td&gt;
&lt;td&gt;Golden template'in kendisine disk eklemek (kalıp sade kalıyor)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PV, VG, LV, XFS ve fstab'ı tek idempotent playbook'un yönetmesi&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/var&lt;/code&gt; gibi canlı dizinleri yeni disklere taşımak (canlı veri taşıma)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Online büyütme: tfvars düzenle, apply, playbook'u tekrar çalıştır&lt;/td&gt;
&lt;td&gt;Küçültme (tasarım gereği imkânsız: provider sert hata, XFS'in kendi sınırı)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Doğrulama çıktıları: lsblk, df, serial eşleşmesi&lt;/td&gt;
&lt;td&gt;Kullanım izleme (Zabbix Faz 3'te geliyor)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Community collection'ları yönetilen bir bağımlılık olarak tutmak&lt;/td&gt;
&lt;td&gt;Terraform state'ten beslenen dinamik envanter (Faz 4)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Problem: disk tahmini ve elle büyütme
&lt;/h2&gt;

&lt;p&gt;Bu fazı şekillendiren gereksinimler sahadan geldi ve tekrar etmeye değer, çünkü başarının neye benzediğini tanımlıyorlar. O ortamdaki DB sunucuları OS diski dışında dört beş disk taşıyor: &lt;code&gt;/var&lt;/code&gt;, log, data, backup, her biri kendi LUN'unda, kendi büyüme takviminde. Bir müşteri &lt;code&gt;/data&lt;/code&gt;'ya yer istediğinde önce birinin hangi hipervizör diskinin gerçekten o olduğunu bulması, sonra doğru olanı büyütmesi, sonra guest içinde dosya sisteminin yeni alanı görmesi için bir komut zincirini adım adım işletmesi gerekiyordu.&lt;/p&gt;

&lt;p&gt;Sorunun özü, Linux'ta disk harflerinin bir kimlik değil, bir numaralandırma kazası olması. Kernel, SCSI aygıtlarını bus'ın tarama sırasına göre numaralandırıyor; bu sıra, sürücü probing'i ve aygıt kaydının zamanlamasının bir yan etkisi. Genelde konfigürasyon sırasıyla eşleşiyor, bu yüzden &lt;code&gt;/dev/sdb&lt;/code&gt; sinsi ve yanıltıcı bir güvenceye dönüşüyor: eşleşme, tam da bir gün artık geçerli olmayana kadar (tipik olarak bir reboot'tan, bir hotplug olayından ya da bir disk daha eklendiğinde) istikrarlı görünüyor. Sistemde &lt;code&gt;scsi3&lt;/code&gt;'ün, yapılandırdığınız üçüncü disk olduğuna dair hiçbir söz yok.&lt;/p&gt;

&lt;p&gt;Bunun sonucu, deneme-yanılmayla büyütmeydi: hipervizörde bir diski tanınabilir bir miktar büyüt, guest içinde hangi disk harfinin boyutunun değiştiğini gözle, ancak o zaman hangi diske dokunduğunu öğren. Bu deneme sürecini bir grup içindeki on sunucuyla çarpınca hata payı ciddileşiyor: yanlış diski büyütmek zararsız ama bir bakım penceresini boşa harcıyor; yanlış diski formatlamak ise bir veri kaybı olayı. Çözüm daha fazla dikkat değil, numaralandırmayı atlatan bir kimlik.&lt;/p&gt;

&lt;p&gt;Doğru disk bulunduktan sonra ritüelin kendisi beş komutluk bir silsileden ibaretti: partition'ı genişletmek için &lt;code&gt;growpart&lt;/code&gt; ya da &lt;code&gt;fdisk&lt;/code&gt;, LVM'e yeni boyutu öğretmek için &lt;code&gt;pvresize&lt;/code&gt;, mantıksal volümü büyütmek için &lt;code&gt;lvextend&lt;/code&gt;, dosya sistemini büyütmek için &lt;code&gt;xfs_growfs&lt;/code&gt;. Bu adımların hiçbiri zor değil; hepsi unutulması kolay, sıraya bağlı ve on terminal aynı anda açıkken yanlış aygıta karşı çalıştırılması kolay. Faz 2 bu ritüeli, beş komutu scriptleştirerek değil, "bu disk hangisi" sorusunu baştan belirsiz olmaktan çıkararak siliyor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tasarım: kimlik serial'da
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Disk harfi, kernel'in diski nerede bulduğudur. Serial, diskin ne olduğudur. Diskleri ne olduklarıyla adresle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bu tasarımdaki her disk, Terraform'da set edilen, Proxmox konfigürasyonundan geçen, QEMU tarafından guest'e sunulan ve kernel tarafından &lt;code&gt;lsblk&lt;/code&gt;'nin SERIAL kolonunda raporlanan bir serial string taşıyor. Serial, bütün yığını değişmeden dolaşıyor; onu bir etiket değil bir kimlik yapan da bu: tfvars dosyasında, PVE konfigürasyonunda ve guest'in kendi disklerine dair kernel görüşünde aynı değer.&lt;/p&gt;

&lt;p&gt;Saha testi bu zincire bir ders ekledi ve aşağıdaki tablo bu durumu bütün çıplaklığıyla gösteriyor:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Katman&lt;/th&gt;
&lt;th&gt;Değer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;terraform.tfvars&lt;/td&gt;
&lt;td&gt;&lt;code&gt;data_disk_size_gb = 20&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;terraform (vm.tf)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;disk { interface = "scsi3" serial = "data01" size = 20 }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pve-a (&lt;code&gt;qm config 200&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;scsi3: nfs-shared:vm-200-disk-2,size=20,serial=data01&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pve-a (&lt;code&gt;qm showcmd 200&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;scsi-hd aygıt satırında &lt;code&gt;serial=data01&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;guest (&lt;code&gt;lsblk -o SERIAL&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sdc disk 20G data01&lt;/code&gt; ← kernel'in gördüğü&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansible (02_storage.yml)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;dev: "/dev/sdc"&lt;/code&gt; (lsblk'den taze çözülmüş)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sonuç&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;vg_data/lv_data&lt;/code&gt;, &lt;code&gt;/srv/data&lt;/code&gt;'ya bağlı&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Ders, son iki halkada yatıyor. Bu playbook'un ilk sürümü serial'leri klasik udev yolu &lt;code&gt;/dev/disk/by-id&lt;/code&gt; üzerinden çözüyordu; bu yığında o dizin serial-adlı girdileri hiç oluşturmadı. Kernel &lt;code&gt;lsblk&lt;/code&gt;'de dört serial'i de raporluyordu, by-id ise hiçbirini sunmuyordu; yani Ubuntu 26.04, virtio-scsi üzerinde bu symlink'leri garanti etmiyor. Playbook bu yüzden kernel görüşünü doğrudan okuyor; çözdüğü disk harfleri de tam bu yüzden güvenle kullanılabiliyor: LVM fiziksel volümleri UUID'yle takip ediyor, fstab &lt;code&gt;/dev/vg/lv&lt;/code&gt; device-mapper yollarını bağlıyor, eşleşme her çalıştırmada sıfırdan yeniden kuruluyor. Harf, playbook'un bir çalıştırma döngüsü süresince geçerli bir tutamaç; serial, VG ve LV ise kalıcı kimlikler.&lt;/p&gt;

&lt;p&gt;Tasarımın üç dayanağı, kod okunmadan varsayılmadı; bpg/proxmox 0.111.1'in kaynağından doğrudan alındı:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;İddia&lt;/th&gt;
&lt;th&gt;Kaynak kanıtı&lt;/th&gt;
&lt;th&gt;Tasarıma yansıması&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mevcut bir VM'e disk eklemek yeniden oluşturmaya zorlamıyor&lt;/td&gt;
&lt;td&gt;disk.go'daki disk bloğunda hiç ForceNew bayrağı yok&lt;/td&gt;
&lt;td&gt;Dört disk, çalışan VM 200'e yerinde ekleniyor; destroy yok, re-clone yok&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diski büyütmek online bir API çağrısı&lt;/td&gt;
&lt;td&gt;vm.go, boyut artışları için resize API'yi çağırıyor; yalnızca datastore taşımaları shutdownForDisksRequired işaretliyor&lt;/td&gt;
&lt;td&gt;Büyütme demosu reboot'suz çalışıyor; datastore'lar arası taşıma hâlâ kesinti isteyen tek depolama değişikliği&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Küçültme yüksek sesle reddediliyor&lt;/td&gt;
&lt;td&gt;vm.go "Cannot shrink ... it is not supported" hatasını döndürüyor&lt;/td&gt;
&lt;td&gt;Sadece-büyüme kuralı provider katmanında zaten dayatılıyor; playbook bununla savaşmak yerine uyum sağlıyor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Bu garantilerin üstüne beş yerleşim kararı geliyor:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Karar&lt;/th&gt;
&lt;th&gt;Gerekçe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Disk başına bir VG ve bir LV&lt;/td&gt;
&lt;td&gt;Disk büyür, VG'si büyür, LV'si büyür: net ve bire bir bir zincir. Paylaşımlı bir VG tahsiste daha esnek ama hangi fiziksel diskin hangi LV'yi beslediğini bulanıklaştırıyor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LV, VG'nin yüzde yüzü&lt;/td&gt;
&lt;td&gt;LV her zaman diskine eşit; VG büyüyünce &lt;code&gt;lvol&lt;/code&gt; onu otomatik uzatıyor, hiçbir şey değişmediğinde no-op olarak boş dönüyor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XFS, ext4 değil&lt;/td&gt;
&lt;td&gt;Hedef ortamdaki DB-sunucu pratiği; &lt;code&gt;xfs_growfs&lt;/code&gt;'le online büyüyor; küçültülememesi provider'ın sadece-büyüme kuralıyla zaten örtüşüyor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lab, &lt;code&gt;/srv/*&lt;/code&gt; altına bağlanıyor&lt;/td&gt;
&lt;td&gt;Canlı &lt;code&gt;/var&lt;/code&gt;'ın üstüne taze bir dosya sistemi bağlamak önce bir veri taşıma ister; lab, makineyi güvenli bağlama noktalarında kanıtlıyor, production veri taşımasıyla uyguluyor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boyutlar tfvars parametresi&lt;/td&gt;
&lt;td&gt;Golden template her VM için sade ve aynı kalıyor; VM başına boyutlar iş yükü parametresi; tam olarak Faz 4'ün istek formunun dolduracağı şey&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Bu yerleşimin bir özelliği erkenden içselleştirilmeye değer, çünkü bütün bir endişe sınıfını ortadan kaldırıyor: tasarım sadece büyümeyi destekliyor ve bu bir eksiklik değil, bilinçli bir tasarım tercihi. Provider küçültmeyi reddediyor, XFS bir dosya sistemini küçültemiyor, playbook &lt;code&gt;lvol&lt;/code&gt;'ü &lt;code&gt;shrink: false&lt;/code&gt; ile çalıştırıyor ki hiç denemesin. Bir volüm gerçekten küçülmek zorundaysa doğru ve sağlıklı yöntem doğru boyutta yeni bir disk artı bir veri taşıma; yerinde yapılan bir göz boyama değil, planlı bir göç.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform: dört diski tanımlamak
&lt;/h2&gt;

&lt;p&gt;Faz 2'nin Terraform tarafı tek bir kavramın dört kez uygulanması: serial'li bir disk bloğu. Aşağıdaki ek, vm.tf'e Faz 1'in mevcut scsi0 OS disk bloğunun hemen ardına giriyor. Neyin eksik olduğuna dikkat: provider'a özel geçici çözüm yok, ignore yok, lifecycle bloğu yok. Kimlik şeması, hipervizöre olduğu gibi geçen bir string özniteliğinden fazlasını istemiyor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;datastore_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_id&lt;/span&gt;
  &lt;span class="nx"&gt;interface&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scsi1"&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;var_disk_size_gb&lt;/span&gt;
  &lt;span class="nx"&gt;serial&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"var01"&lt;/span&gt;
  &lt;span class="nx"&gt;ssd&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;discard&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;datastore_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_id&lt;/span&gt;
  &lt;span class="nx"&gt;interface&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scsi2"&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log_disk_size_gb&lt;/span&gt;
  &lt;span class="nx"&gt;serial&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"log01"&lt;/span&gt;
  &lt;span class="nx"&gt;ssd&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;discard&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;datastore_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_id&lt;/span&gt;
  &lt;span class="nx"&gt;interface&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scsi3"&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data_disk_size_gb&lt;/span&gt;
  &lt;span class="nx"&gt;serial&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"data01"&lt;/span&gt;
  &lt;span class="nx"&gt;ssd&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;discard&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;datastore_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_id&lt;/span&gt;
  &lt;span class="nx"&gt;interface&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scsi4"&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backup_disk_size_gb&lt;/span&gt;
  &lt;span class="nx"&gt;serial&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"backup01"&lt;/span&gt;
  &lt;span class="nx"&gt;ssd&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;discard&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ssd&lt;/code&gt; ve &lt;code&gt;discard&lt;/code&gt; bayrakları OS diskiyle birebir aynı, ki beş disk NFS backend'inde aynı şekilde davransın: SSD emülasyonu guest'in TRIM göndermesine izin veriyor, &lt;code&gt;discard on&lt;/code&gt; paylaşımda thin provisioning'in doğru ve sağlıklı çalışmasını sağlıyor. Arayüzler açıkça sabitlenmiş, scsi1'den scsi4'e; çünkü provider, state ile gerçeği eşleştirirken diskleri tam olarak arayüzden okuyor; sabit bir pin, diff'i temiz tutuyor.&lt;/p&gt;

&lt;p&gt;Boyutlar, hepsi varsayılanlı dört yeni değişkenden geliyor; mevcut bir tfvars dosyasını kırmayan da tam bu: hiçbir satır değişmeden çalışmaya devam ediyor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# terraform.tfvars.example (Faz 2 eki)&lt;/span&gt;
var_disk_size_gb    &lt;span class="o"&gt;=&lt;/span&gt; 10  &lt;span class="c"&gt;# scsi1 serial=var01    -&amp;gt; /srv/var&lt;/span&gt;
log_disk_size_gb    &lt;span class="o"&gt;=&lt;/span&gt; 10  &lt;span class="c"&gt;# scsi2 serial=log01    -&amp;gt; /srv/log&lt;/span&gt;
data_disk_size_gb   &lt;span class="o"&gt;=&lt;/span&gt; 20  &lt;span class="c"&gt;# scsi3 serial=data01   -&amp;gt; /srv/data (büyütme demosu)&lt;/span&gt;
backup_disk_size_gb &lt;span class="o"&gt;=&lt;/span&gt; 15  &lt;span class="c"&gt;# scsi4 serial=backup01 -&amp;gt; /srv/backup&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Değişken&lt;/th&gt;
&lt;th&gt;Varsayılan&lt;/th&gt;
&lt;th&gt;Serial&lt;/th&gt;
&lt;th&gt;Bağlama noktası&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;var_disk_size_gb&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;var01&lt;/td&gt;
&lt;td&gt;/srv/var&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;log_disk_size_gb&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;log01&lt;/td&gt;
&lt;td&gt;/srv/log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;data_disk_size_gb&lt;/td&gt;
&lt;td&gt;20 (demoda 30'a çıkıyor)&lt;/td&gt;
&lt;td&gt;data01&lt;/td&gt;
&lt;td&gt;/srv/data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;backup_disk_size_gb&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;backup01&lt;/td&gt;
&lt;td&gt;/srv/backup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Repo güncellenince, mevcut Faz 1 state'ine karşı çekilen plan bir yerinde güncelleme gösteriyor, bir yer değiştirme değil. Bu, üçüncü bölümdeki ForceNew-yok kanıtının gerçek çıktıda görünür olduğu an ve dikkatle okunmaya değer: plan tam olarak bir kaynağın değiştiğini, sıfır kaynağın yok edildiğini göstermeli.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~ resource "proxmox_virtual_environment_vm" "test_vm" {
    ~ agent { enabled = true }
    ~ disk {
        ~ serial = "data01" -&amp;gt; (known after apply)
        # (5 değişmeyen öznitelik gizlendi)
      }
    + disk {
        + datastore_id = "nfs-shared"
        + interface    = "scsi1"
        + serial       = "var01"
        + size         = 10
      }
    + disk { ... scsi2, serial "log01", size 10 ... }
    + disk { ... scsi3, serial "data01", size 20 ... }
    + disk { ... scsi4, serial "backup01", size 15 ... }
  }

Plan: 0 to add, 1 to change, 0 to destroy.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Diff'in tam görünümü provider sürümüne göre değişiyor ama özet satır sözleşmenin ta kendisi: 0 to add, 1 to change, 0 to destroy. Bir plan bir boyut düzenlemesinden sonra VM'in yeniden oluşturulacağını gösterirse dur ve &lt;code&gt;yes&lt;/code&gt; yazmadan önce diff'i oku; bu tasarımda yeniden oluşturmanın tek meşru sebepleri VMID ya da klon kaynağı gibi kimlik özniteliklerinin değişmesi, asla bir disk boyutu değil.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apply: disk, VM çalışırken beliriyor
&lt;/h2&gt;

&lt;p&gt;Disk eklemesini apply etmek tasarım gereği sıradan: provider, VM konfigürasyonunu API üzerinden güncelliyor, NFS datastore'da dört disk dosyası oluşturuyor, çalışan VM de hotplug ile dört yeni virtio-scsi aygıtı görüyor. Zamanlanacak bir kesinti penceresi yok, açılacak bir konsol yok; tüm operasyon NFS'te disk başına saniyeler sürüyor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/terraform# terraform apply
proxmox_virtual_environment_vm.test_vm: Modifying... &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;200]
proxmox_virtual_environment_vm.test_vm: Modifications &lt;span class="nb"&gt;complete &lt;/span&gt;after 12s

Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 0 added, 1 changed, 0 destroyed.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu akıştaki tek kritik nokta saha testinde ortaya çıktı ve baş ağrıtmadan önce bilinmesinde fayda var. API diskleri takılı olarak raporluyor, &lt;code&gt;qm config&lt;/code&gt; serial'leri gösteriyor; ama serial hotplug edilebilen bir özellik değil: disk'ler VM zaten çalışırken eklendiğinde, saklanan konfigürasyon serial'leri taşıyor ama çalışan QEMU süreci taşımıyor, bu yüzden guest kernel'i boş serial'ler raporluyor. Düzeltme node üzerinden tam bir durdur-başlat: &lt;code&gt;qm shutdown 200 &amp;amp;&amp;amp; qm start 200&lt;/code&gt;. Guest içindeki bir reboot QEMU'yu yeniden başlatmıyor. Sonraki bölümdeki playbook'un assert'i tam bu durumu iki ihtimalli net bir teşhis mesajıyla yakalıyor; hata bu yüzden gizemli değil, gayet anlaşılır.&lt;/p&gt;

&lt;h3&gt;
  
  
  İki taraftan doğrulama
&lt;/h3&gt;

&lt;p&gt;Bu seride her yerde olduğu gibi bir iddia, ancak arkasındaki kanıt ve somut çıktı kadar geçerlidir. Diskler artık hipervizör sınırının iki tarafında da var ve üç komut zincirin tamamını kapsıyor; hepsi de Ansible adımı bir şeye dokunmadan önce aynı dört serial'de hemfikir olmalı. İlk ikisi bir PVE node'unda çalışıp hipervizörün gördüğü durumu, hem saklanan konfigürasyonda hem gerçekten çalışan QEMU sürecinde gösteriyor; sonuncusu guest içinde çalışıp kernel'in gerçekten ne gördüğünü gösteriyor. Bu, saha hatasını dakikalar içinde izole eden komut seti.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fazv18pk1ay942q1npwd5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fazv18pk1ay942q1npwd5.png" alt="WebUI Üzerinden Görünümü" width="800" height="234"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# pve-a'da: saklanan konfigürasyon serial'leri taşıyor...&lt;/span&gt;
root@pve-a:~# qm config 200 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"scsi[1-4]:"&lt;/span&gt;
scsi1: nfs-shared:vm-200-disk-0,size&lt;span class="o"&gt;=&lt;/span&gt;10,serial&lt;span class="o"&gt;=&lt;/span&gt;var01,ssd&lt;span class="o"&gt;=&lt;/span&gt;1,discard&lt;span class="o"&gt;=&lt;/span&gt;on
scsi2: nfs-shared:vm-200-disk-1,size&lt;span class="o"&gt;=&lt;/span&gt;10,serial&lt;span class="o"&gt;=&lt;/span&gt;log01,ssd&lt;span class="o"&gt;=&lt;/span&gt;1,discard&lt;span class="o"&gt;=&lt;/span&gt;on
scsi3: nfs-shared:vm-200-disk-2,size&lt;span class="o"&gt;=&lt;/span&gt;20,serial&lt;span class="o"&gt;=&lt;/span&gt;data01,ssd&lt;span class="o"&gt;=&lt;/span&gt;1,discard&lt;span class="o"&gt;=&lt;/span&gt;on
scsi4: nfs-shared:vm-200-disk-3,size&lt;span class="o"&gt;=&lt;/span&gt;15,serial&lt;span class="o"&gt;=&lt;/span&gt;backup01,ssd&lt;span class="o"&gt;=&lt;/span&gt;1,discard&lt;span class="o"&gt;=&lt;/span&gt;on

&lt;span class="c"&gt;# ...çalışan QEMU süreci de öyle:&lt;/span&gt;
root@pve-a:~# qm showcmd 200 &lt;span class="nt"&gt;--pretty&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"serial=[a-z0-9]*"&lt;/span&gt;
&lt;span class="nv"&gt;serial&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;var01
&lt;span class="nv"&gt;serial&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;log01
&lt;span class="nv"&gt;serial&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;data01
&lt;span class="nv"&gt;serial&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;backup01

&lt;span class="c"&gt;# guest içinde: kernel her serial'i kendi görüşünde raporluyor&lt;/span&gt;
ubuntu@vm-test-01:~&lt;span class="nv"&gt;$ &lt;/span&gt;lsblk &lt;span class="nt"&gt;-o&lt;/span&gt; NAME,SIZE,TYPE,SERIAL
NAME SIZE TYPE SERIAL
sda  10G  disk drive-scsi0   &amp;lt;- OS diski: QEMU&lt;span class="s1"&gt;'nun varsayılan kimliği, serial yok
sdb  15G  disk backup01
sdc  20G  disk data01
sdd  10G  disk log01
sde  10G  disk var01

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu çıktıda üç ayrıntı dikkati hak ediyor. OS diski &lt;code&gt;drive-scsi0&lt;/code&gt; gösteriyor, QEMU'nun serial'siz yapılandırılmış bir disk için varsayılan tanımlayıcısı; zararsız çünkü bu tasarımda hiçbir şey OS diskini serial'le adreslemiyor. Disk dosyası numaralandırması kasıtlı biçimde tedirgin edici görünüyor: OS diski &lt;code&gt;vm-200-disk-0.qcow2&lt;/code&gt; iken dört ham veri diski &lt;code&gt;vm-200-disk-0&lt;/code&gt;'dan &lt;code&gt;vm-200-disk-3&lt;/code&gt;'e uzanıyor. Yani depolama tahsis mekanizması (storage allocator), farklı dosya uzantıları arasında indeksleri yeniden kullanmış; bir hata gibi görünüyor, aslında bir uygulama detayı, disk sayısı da boyutlar da serial'ler de doğru. Ve &lt;code&gt;/dev/disk/by-id&lt;/code&gt;'nin &lt;code&gt;lsblk&lt;/code&gt; çıktısını yansıtmasını beklemeyin: bu Ubuntu 26.04 guest'inde o dizin serial-adlı hiçbir girdi hiç oluşturmadı; playbook'un kernel görüşünü okumasının sebebi tam da bu.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebuild dolambacı: önce başarısız olan apply
&lt;/h3&gt;

&lt;p&gt;Saha oturumu çalışan bir Faz 1 VM'inden başlamadı. Lab'ı sıfıra indirerek başladı: eski repo dizini silindi, eski VM'ler yok edildi, repo ctrl-01'de yeniden açıldı, &lt;code&gt;terraform init&lt;/code&gt; boş bir state'ten başladı. Temizlikten bir nesne kaçtı: Faz 1'in golden template'i, VMID 9000, hâlâ pve-a'da duruyordu ve oturumun ilk apply'ı ona çarpıp başarısız oldu. Hata bir dönüştürme sorunu gibi görünüyor; asıl sorun iki adım önce, oluşturma anında yaşandı ve sıra dikkatle okunmaya değer, çünkü geride kalan her nesne senaryosunda benzer durum yaşanır.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/terraform# terraform apply
proxmox_virtual_environment_vm.ubuntu_template: Creating...
proxmox_virtual_environment_vm.ubuntu_template: Still creating...
╷
│ Error: VM convert to template failed
│ ... can&lt;span class="s1"&gt;'t convert a template to a template ...
╵
# apply #1 burada durdu; template kaynağı state'&lt;/span&gt;te kaldı, tainted işaretlendi

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mekanizma, sonradan provider kaynağından doğrulandı: create çağrısı PVE'nin "VM 9000 already exists" hatasına çarptı; provider'ın retry yardımcısı bu spesifik hatayı zaten-tamamlanmış-başarı olarak ele alıyor (vms.go, &lt;code&gt;ErrorContains("already exists")&lt;/code&gt; etrafındaki &lt;code&gt;WithAlreadyDoneCheck&lt;/code&gt;). Oluşturma bu yüzden "başarılı" oldu, imaj içe aktarma ve disk boyutlandırma geride kalan template'e karşı çalıştı, uyuşmazlık ancak son dönüştürme adımında ortaya çıktı: PVE bir template'i template'e dönüştürmeyi reddetti. Durumu olduğundan daha vahim gösteren beklenmedik bir ayrıntı daha var: provider, VM oluşturulduktan hemen sonra kaynak ID'sini yazıyor, yani başarısız apply template kaynağını state'te mevcut ve tainted bırakıyor; bunu hiç kurtarırken görmemiş birine tamamen kurtarılamaz görünen bir durum.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/terraform# terraform apply
Note: Objects have changed outside of Terraform since the last
&lt;span class="s2"&gt;"terraform apply"&lt;/span&gt; ... the following changes ... has been deleted
- ...
proxmox_virtual_environment_vm.ubuntu_template: Creating...
proxmox_virtual_environment_vm.ubuntu_template: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 10s
proxmox_virtual_environment_vm.test_vm: Creating...
proxmox_virtual_environment_vm.test_vm: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 5s

Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 2 added ...
&lt;span class="c"&gt;# template: ~10 sn; test_vm: ~5 sn; ikinci denemede ikisi de temiz kuruldu&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kurtarma hiçbir elle state ameliyatı istemedi ve asıl ders bu: apply'ı yeniden çalıştırmak geçerli bir ilk tepki. Refresh, state ile gerçeğin anlaşmadığını fark etti, Terraform dışında nesnelerin değiştiğine dair tanıdık notu bastı, geride kalan girdiyi temizledi, iki kaynağı da temiz yeniden kurdu: template'i on saniyede, VM'i beş saniyede. Bu hikâyeden iki alışkanlık çıkıyor: bir lab'ı söküyorsan template'i de yok et, bu temizliğin unuttuğu tek nesne o, node üzerinde &lt;code&gt;qm destroy 9000&lt;/code&gt; onu kaldırıyor. Ve bir apply geride kalmış bir şeye çarpıp düşerse, hatayı oku, sonra &lt;code&gt;terraform state&lt;/code&gt; komutlarına uzanmadan önce tekrar apply et; provider'ın okuma yolu tam bu durumu yakınsatmak için inşa edilmiş.&lt;/p&gt;

&lt;p&gt;Rebuild'in bir sonucu daha vardı, tam bir sonraki komutu karşılıyor: yeni VM taze host anahtarları üretti, ctrl-01'deki known_hosts hâlâ eski makineyi hatırlıyor ve SSH bağlantıyı yüksek sesle reddediyor. Bu her klonlanan VM'in her rebuild'inde beklenen bir şey, uyarı özelliğin ta kendisi, düzeltmesi tek komut:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~# ssh ubuntu@192.168.122.50
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
...
root@ctrl-01:~# ssh-keygen &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/known_hosts &lt;span class="nt"&gt;-R&lt;/span&gt; 192.168.122.50
&lt;span class="c"&gt;# Host 192.168.122.50 found: line 2&lt;/span&gt;
192.168.122.50 Host key removed.
root@ctrl-01:~# ssh ubuntu@192.168.122.50  &lt;span class="c"&gt;# yeni anahtarı kabul et&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Faz 1'i yazarken bu sahneye "bunu sonradan öğrendim" diye not düşmüştüm; söz burada tutuldu.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ansible storage playbook'u
&lt;/h2&gt;

&lt;p&gt;Faz 1 tamamen builtin modüllerle çalışmıştı; Faz 2, pipeline'ın ilk gerçek bağımlılığını kazandığı yer. LVM ve mount yönetimi community collection'larında yaşıyor, repo artık bunları bir requirements dosyasında sabitliyor ki kontrol düğümü repodan tek başına yeniden üretilebilsin. Kurulum ctrl-01'de tek komut, bu fazın getirdiği tek yeni ön koşul da bu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ansible/requirements.yml&lt;/span&gt;
&lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;community.general&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://galaxy.ansible.com&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ansible.posix&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://galaxy.ansible.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible-galaxy collection &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.yml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Playbook'un kendisi bir ekran değişken, bir ekran görev ve şekli üçüncü bölümdeki kimlik zincirini izliyor: önce çöz, sonra kur, sonra bağla. Değişkenler dört diskin tüm istenen durumunu tek yerde bildiriyor; lab'a ileride beşinci bir disk eklemek bu yüzden bir tfvars satırı, bir vm.tf bloğu ve bu listeye bir girdi kadar basit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# playbooks/02_storage.yml (data_disks sözleşmesi)&lt;/span&gt;
&lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;data_disks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serial&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;var01&lt;/span&gt; &lt;span class="c1"&gt;# scsi1, boyut: var_disk_size_gb&lt;/span&gt;
      &lt;span class="na"&gt;vg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vg_var&lt;/span&gt;
      &lt;span class="na"&gt;lv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lv_var&lt;/span&gt;
      &lt;span class="na"&gt;mount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/srv/var&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serial&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;log01&lt;/span&gt; &lt;span class="c1"&gt;# scsi2, boyut: log_disk_size_gb&lt;/span&gt;
      &lt;span class="na"&gt;vg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vg_log&lt;/span&gt;
      &lt;span class="na"&gt;lv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lv_log&lt;/span&gt;
      &lt;span class="na"&gt;mount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/srv/log&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serial&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data01&lt;/span&gt; &lt;span class="c1"&gt;# scsi3, boyut: data_disk_size_gb&lt;/span&gt;
      &lt;span class="na"&gt;vg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vg_data&lt;/span&gt;
      &lt;span class="na"&gt;lv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lv_data&lt;/span&gt;
      &lt;span class="na"&gt;mount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/srv/data&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serial&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backup01&lt;/span&gt; &lt;span class="c1"&gt;# scsi4, boyut: backup_disk_size_gb&lt;/span&gt;
      &lt;span class="na"&gt;vg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vg_backup&lt;/span&gt;
      &lt;span class="na"&gt;lv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lv_backup&lt;/span&gt;
      &lt;span class="na"&gt;mount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/srv/backup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Çözümleme önce geliyor, çünkü sonraki her görev bir diski kendi çözülmüş yolundan adresliyor ve saha testi bu çözümlemenin nereden okuyacağına karar verdi. İlk sürüm bir &lt;code&gt;find&lt;/code&gt; görevi ile &lt;code&gt;/dev/disk/by-id&lt;/code&gt;'yi dolaşıyordu; Ubuntu 26.04 guest'i serial symlink'lerini orada hiç oluşturmadı, bu yüzden v2 doğrudan kernel'e soruyor: tek bir &lt;code&gt;lsblk -J&lt;/code&gt; çağrısı her blok aygıt için NAME, TYPE ve SERIAL'i yakalıyor, bir &lt;code&gt;set_fact&lt;/code&gt; bu JSON'dan serial-den-diske haritayı kuruyor, bir debug görevi haritayı yazdırıyor ki çalıştırma log'u her serial'in hangi aygıta dönüştüğünü göstersin, ve bir assert bir serial tam olarak bir diskten başka bir şeye çözülürse açıkça hata fırlatıp işlemi durduruyor. Bu assert hem beşinci bölümdeki taze-QEMU durumuna hem klasik bir yazım hatasına karşı bir bekçi; hata mesajı ise karmaşık bir stack trace yerine iki ihtimalli net bir teşhis taşıyor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Read the kernel serial view of all disks&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cmd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lsblk -J -o NAME,TYPE,SERIAL&lt;/span&gt;
  &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lsblk_serials&lt;/span&gt;
  &lt;span class="na"&gt;changed_when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Map every serial to its disk(s) (kernel view)&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.set_fact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;serial_map&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
      &lt;span class="s"&gt;{{ serial_map | default({}) | combine({&lt;/span&gt;
        &lt;span class="s"&gt;item: (lsblk_serials.stdout | from_json).blockdevices&lt;/span&gt;
          &lt;span class="s"&gt;| selectattr('type', 'equalto', 'disk')&lt;/span&gt;
          &lt;span class="s"&gt;| selectattr('serial', 'equalto', item)&lt;/span&gt;
          &lt;span class="s"&gt;| map(attribute='name') | list&lt;/span&gt;
      &lt;span class="s"&gt;}) }}&lt;/span&gt;
  &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;data_disks&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;map(attribute='serial')&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;list&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Every serial must resolve to exactly one disk&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.assert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;that&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;serial_map[item.serial] | default([]) | length == &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;fail_msg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
      &lt;span class="s"&gt;Expected exactly one disk with serial {{ item.serial }}, found&lt;/span&gt;
      &lt;span class="s"&gt;{{ serial_map[item.serial] | default([]) | length }}. Diagnose in&lt;/span&gt;
      &lt;span class="s"&gt;the guest with lsblk -o NAME,SIZE,SERIAL. (a) Empty serials there&lt;/span&gt;
      &lt;span class="s"&gt;mean the running QEMU predates the config: check qm config, then&lt;/span&gt;
      &lt;span class="s"&gt;fully stop and start the VM; a guest reboot does NOT restart&lt;/span&gt;
      &lt;span class="s"&gt;QEMU. (b) If lsblk shows the serial, compare against vm.tf.&lt;/span&gt;
  &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;data_disks&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu yeniden yazım teorik değildi: ilk sürüm sahada tam olarak by-id'nin boş olduğu yerde başarısız oldu ve o başarısızlığın şekli, onu izole eden komutlarla birlikte saklamaya değer, çünkü "playbook, hipervizörün orada olduğuna yemin ettiği şeyi göremiyor" durumunun genel reçetesi bu: saklanan konfigürasyonu kontrol et, çalışan süreci kontrol et, kernel'i kontrol et, ilk anlaşmayan görüş sorunu adlandırsın.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible-playbook playbooks/02_storage.yml
TASK &lt;span class="o"&gt;[&lt;/span&gt;Every serial must resolve to exactly one disk] &lt;span class="k"&gt;**************&lt;/span&gt;
fatal: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01]: FAILED! &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;var01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"Expected exactly one disk with serial var01, found 0. ..."&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
... &lt;span class="o"&gt;(&lt;/span&gt;log01, data01, backup01: aynı şekil, found 0&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# izolasyon, dakikalar sonra (VM'in tam durdur/başlat'ından sonra):&lt;/span&gt;
&lt;span class="c"&gt;# pve-a'da: qm config 200 | grep serial -&amp;gt; dört serial de mevcut&lt;/span&gt;
&lt;span class="c"&gt;# pve-a'da: qm showcmd 200 | grep serial -&amp;gt; çalışan QEMU'da da var&lt;/span&gt;
&lt;span class="c"&gt;# guest'te: lsblk -o NAME,SIZE,SERIAL -&amp;gt; dört serial de mevcut&lt;/span&gt;
&lt;span class="c"&gt;# guest'te: ls /dev/disk/by-id -&amp;gt; 10 girdi, hiçbiri serial-adlı değil&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eşleşme kurulduktan sonraki inşa zinciri dört görevden oluşuyor ve modül işleyişi varsayımlarla değil, doğrudan community.general kaynak kodundan incelenerek kurgulandı. &lt;code&gt;fstype: lvm&lt;/code&gt; ile &lt;code&gt;filesystem&lt;/code&gt; modülü oluşturmayı &lt;code&gt;pvcreate&lt;/code&gt;'e, büyütmeyi &lt;code&gt;pvresize&lt;/code&gt;'a eşliyor; &lt;code&gt;lvg&lt;/code&gt; modülü gerekirse &lt;code&gt;pvcreate&lt;/code&gt;'i kendisi çalıştırıp volume group'u kuruyor; &lt;code&gt;size: 100%VG&lt;/code&gt; ile &lt;code&gt;lvol&lt;/code&gt; modülü LV hedefini VG'nin tamamı olarak hesaplıyor, ilk çalıştırmada LV'yi kuruyor, VG her büyüdüğünde &lt;code&gt;lvextend --resizefs&lt;/code&gt; çağırıyor; bu da XFS için online &lt;code&gt;xfs_growfs&lt;/code&gt;'i tetikliyor. &lt;code&gt;shrink&lt;/code&gt; bayrağı sadece-büyüme mantığıyla eşleşsin diye &lt;code&gt;false&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create/resize physical volumes&lt;/span&gt;
  &lt;span class="na"&gt;community.general.filesystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;fstype&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lvm&lt;/span&gt;
    &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;disk_map[item.serial]&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;resizefs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# ilk çalıştırmada pvcreate, her çalıştırmada pvresize&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create volume groups (one per disk)&lt;/span&gt;
  &lt;span class="na"&gt;community.general.lvg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;vg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.vg&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;pvs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;disk_map[item.serial]&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create/extend logical volumes at 100 percent of their VG&lt;/span&gt;
  &lt;span class="na"&gt;community.general.lvol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;vg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.vg&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;lv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.lv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100%VG&lt;/span&gt; &lt;span class="c1"&gt;# LV her zaman VG'sine eşit&lt;/span&gt;
    &lt;span class="na"&gt;resizefs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# lvextend --resizefs -&amp;gt; xfs_growfs&lt;/span&gt;
    &lt;span class="na"&gt;shrink&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;# sadece büyüme, tasarım gereği&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create XFS filesystems&lt;/span&gt;
  &lt;span class="na"&gt;community.general.filesystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;fstype&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xfs&lt;/span&gt;
    &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/dev/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.vg&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.lv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mount filesystems and persist in fstab&lt;/span&gt;
  &lt;span class="na"&gt;ansible.posix.mount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.mount&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/dev/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.vg&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item.lv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;fstype&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xfs&lt;/span&gt;
    &lt;span class="na"&gt;opts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;defaults,noatime&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mounted&lt;/span&gt; &lt;span class="c1"&gt;# hem şimdi bağla HEM fstab'a yaz&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu zincirde iki ayrıntı dikkati hak ediyor. Mount görevi &lt;code&gt;state: mounted&lt;/code&gt; kullanıyor, bu hem hemen bağlıyor hem fstab girdisini yazıyor; playbook, düzen bir reboot'u atlatana kadar bitmiş sayılmıyor. Ve disk'in tamamı, partition tablosu olmadan LVM'e bir PV olarak veriliyor; bu modern disk-başına-tek-LV kalıbı ve resize zincirinden &lt;code&gt;growpart&lt;/code&gt; adımını tamamen kaldırıyor. Genişletilecek bir partition yok, çünkü partition yok.&lt;/p&gt;

&lt;p&gt;Son olarak, &lt;code&gt;pre_tasks&lt;/code&gt; bloğu her şeyden önce &lt;code&gt;lvm2&lt;/code&gt; ve &lt;code&gt;xfsprogs&lt;/code&gt;'u kuruyor, çünkü Ubuntu cloud imajları ikisiyle de gelmiyor. Bu, Faz 1'deki eksik guest agent'la aynı sınıftan bir ders: cloud imajları bilinçli olarak minimal, iş yükünün taban sistemin ötesinde ihtiyaç duyduğu her şey pipeline'ın işi oluyor. Bu adımı unutmak, filesystem modülünün &lt;code&gt;pvcreate&lt;/code&gt;'i bulamadığı kafa karıştırıcı bir erken hatayla sonuçlanıyor; pre_task bağımlılığı açık ve idempotent hale getiriyor.&lt;/p&gt;

&lt;h2&gt;
  
  
  İlk çalıştırma ve doğrulama
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sıfırıncı adım: taze VM'de guest agent yok
&lt;/h3&gt;

&lt;p&gt;Sıralama kuralları neyin önce çalışacağını belirliyor ve saha oturumu bunu bir başarısızlıktan değil bir uyarıdan öğrendi. Taze klonlanmış bir VM'de QEMU guest agent yok; Faz 1'in &lt;code&gt;01_first_contact.yml&lt;/code&gt;'i onu kuran adım. Agent bu fazda özellikle önemli, çünkü beşinci bölümdeki teşhis durdur-başlat'ı temiz bir kapanış istiyor ve PVE, agent'ı olan bir VM'i onun üzerinden kapatıyor. Aşağıdaki uyarı, aynı komutun agent yokken neye benzediği; zararsız ama sıralamayı adlandırıyor: önce ilk temas, sonra depolama.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@pve-a:~# qm shutdown 200
warning: QEMU Guest Agent is not running
&lt;span class="c"&gt;# kapatma yine de başarılı; agent olmadan guest-farkında değil, düz bir ACPI kapatma&lt;/span&gt;

root@pve-a:~# qm start 200

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rebuild edilmiş VM'de ilk temas playbook'unun tekrar çalıştırılması, Faz 1'in taze bir makine için belgelediği doğrulama çıktısının aynısıyla sonuçlandı, aynı sebeple: agent paketi kuruluyor (changed), postinst'i servisi zaten başlatmış oluyor (bu yüzden service görevi &lt;code&gt;ok&lt;/code&gt; okuyor), yönetilen damga yazılıyor (changed). Aynı sayılar, yeni makine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible-playbook playbooks/01_first_contact.yml
TASK &lt;span class="o"&gt;[&lt;/span&gt;Install QEMU guest agent] &lt;span class="k"&gt;*********************************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01]

TASK &lt;span class="o"&gt;[&lt;/span&gt;Enable and start the guest agent] &lt;span class="k"&gt;*************************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01]

TASK &lt;span class="o"&gt;[&lt;/span&gt;Mark the VM as managed by this pipeline] &lt;span class="k"&gt;*******************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01]

PLAY RECAP &lt;span class="k"&gt;******************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="c"&gt;# taze klon doğrulandı: Ubuntu 26.04, kernel 7.0.0-30-generic&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  İlk çalıştırma
&lt;/h3&gt;

&lt;p&gt;Agent yerindeyken storage playbook'u ilk kez çalıştırılıyor ve değişiklik yapan tek çalıştırma bu: sonrasında playbook'taki her görev ya no-op, ya kozmetik bir kontrol ya da bir büyütme adımı. Saha testinde recap beş changed görev gösterdi; dört tam dosya sistemi yığınını çıplak disklerden inşa etmenin görünüşü bu. Araç pre_task'ı &lt;code&gt;ok&lt;/code&gt;'a indi çünkü daha önce yarım kalmış bir deneme &lt;code&gt;lvm2&lt;/code&gt; ve &lt;code&gt;xfsprogs&lt;/code&gt;'u zaten kurmuştu; bakir bir VM'de o görev de değişiyor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible-playbook playbooks/02_storage.yml
PLAY &lt;span class="o"&gt;[&lt;/span&gt;Storage - LVM and XFS on serial-tagged data disks] &lt;span class="k"&gt;**********&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Show the serial to device mapping &lt;span class="o"&gt;(&lt;/span&gt;the guessing game this replaces&lt;span class="o"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;***&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;var01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"var01 (vg_var/lv_var -&amp;gt; /srv/var) = /dev/sde"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;log01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"log01 (vg_log/lv_log -&amp;gt; /srv/log) = /dev/sdd"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;data01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"data01 (vg_data/lv_data -&amp;gt; /srv/data) = /dev/sdc"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;backup01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"backup01 (vg_backup/lv_backup -&amp;gt; /srv/backup) = /dev/sdb"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Every serial must resolve to exactly one disk] &lt;span class="k"&gt;**************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;var01&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"All assertions passed"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
... &lt;span class="o"&gt;(&lt;/span&gt;3 tane daha, hepsi geçti&lt;span class="o"&gt;)&lt;/span&gt; ...

TASK &lt;span class="o"&gt;[&lt;/span&gt;Create/resize physical volumes] &lt;span class="k"&gt;****************************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;var01&lt;span class="o"&gt;)&lt;/span&gt; ... &lt;span class="o"&gt;(&lt;/span&gt;3 tane daha&lt;span class="o"&gt;)&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Create volume &lt;span class="nb"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;one per disk&lt;span class="o"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;***********************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vg_var&lt;span class="o"&gt;)&lt;/span&gt; ... &lt;span class="o"&gt;(&lt;/span&gt;3 tane daha&lt;span class="o"&gt;)&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Create/extend logical volumes at 100 percent of their VG] &lt;span class="k"&gt;***&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vg_var/lv_var&lt;span class="o"&gt;)&lt;/span&gt; ... &lt;span class="o"&gt;(&lt;/span&gt;3 tane daha&lt;span class="o"&gt;)&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Create XFS filesystems] &lt;span class="k"&gt;************************************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/vg_var/lv_var&lt;span class="o"&gt;)&lt;/span&gt; ... &lt;span class="o"&gt;(&lt;/span&gt;3 tane daha&lt;span class="o"&gt;)&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Mount filesystems and persist &lt;span class="k"&gt;in &lt;/span&gt;fstab] &lt;span class="k"&gt;********************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/srv/var&lt;span class="o"&gt;)&lt;/span&gt; ... &lt;span class="o"&gt;(&lt;/span&gt;3 tane daha&lt;span class="o"&gt;)&lt;/span&gt;

PLAY RECAP &lt;span class="k"&gt;******************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;14 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eşleştirme bloğunu her şeyden önce okuyun: o dört debug satırı, ikinci bölümdeki tahmin oyununun düz metinde çözülmesi. Her serial, kendi VG/LV çifti, bağlama noktası ve çözülmüş aygıtıyla tek satırda duruyor; bütün bu tasarımın bize kazandırdığı asıl değer bu satır. Bir serial çözülemeseydi, assert karmaşık bir stack trace yerine yönlendirici bir hata mesajıyla süreci tam orada durdururdu.&lt;/p&gt;

&lt;p&gt;Playbook, çalıştırma log'unun bir iddia değil kanıtla bitmesi için iki doğrulama çıktısıyla kapanıyor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TASK [Show block devices with serials and mounts] *************
NAME                   SIZE TYPE SERIAL      MOUNTPOINT
sda                    10G disk              / &amp;lt;- OS diski (serial gerekmiyor)
sdb                    15G disk backup01
└─vg_backup-lv_backup  15G lvm               /srv/backup
sdc                    20G disk data01
└─vg_data-lv_data      20G lvm               /srv/data
sdd                    10G disk log01
└─vg_log-lv_log        10G lvm               /srv/log
sde                    10G disk var01
└─vg_var-lv_var        10G lvm               /srv/var

TASK [Show filesystem usage] ************************************
Filesystem                        Size  Used Avail Use% Mounted on
/dev/mapper/vg_var-lv_var          10G  104M   10G   2% /srv/var
/dev/mapper/vg_log-lv_log          10G  104M   10G   2% /srv/log
/dev/mapper/vg_data-lv_data        20G  424M   20G   3% /srv/data
/dev/mapper/vg_backup-lv_backup    15G  145M   15G   1% /srv/backup

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  İdempotans: tekrar çalıştırma
&lt;/h3&gt;

&lt;p&gt;Bu playbook'un sözleşmesi, hiçbir şey değişmediğinde ikinci çalıştırmanın olaysız (sıkıcı) geçmesiydi ve saha testi bu taahhüdü kesin sayılarla doğruladı. PV'ler zaten var, VG'ler zaten onları içeriyor, LV'ler zaten VG'lerine eşit, dosya sistemleri zaten var, bağlamalar zaten fstab'la eşleşiyor; bu yüzden her görev bir tanesi dışında &lt;code&gt;ok&lt;/code&gt;'a iniyor: PV görevi saha testinde yine &lt;code&gt;changed&lt;/code&gt; raporladı, çünkü &lt;code&gt;resizefs: true&lt;/code&gt; ile community.general'in filesystem modülü her çağrıda &lt;code&gt;pvresize&lt;/code&gt;'ı çalıştırıyor ve hiçbir boyut kımıldamasa bile görevi changed işaretliyor. Etki yine idempotent, hiçbir şey büyümüyor ya da yeniden yazılmıyor, ancak recap çıktısındaki gerçekçi beklenti sıfır değil, bir changed görev.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible-playbook playbooks/02_storage.yml
TASK &lt;span class="o"&gt;[&lt;/span&gt;Create/resize physical volumes] &lt;span class="k"&gt;****************************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;var01&lt;span class="o"&gt;)&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;log01&lt;span class="o"&gt;)&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;data01&lt;span class="o"&gt;)&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;backup01&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# pvresize geçişi&lt;/span&gt;
&lt;span class="c"&gt;# diğer her görev: ok&lt;/span&gt;

PLAY RECAP &lt;span class="k"&gt;******************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;14 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tek hayatta kalan değişiklik &lt;code&gt;pvresize&lt;/code&gt; geçişi: &lt;code&gt;resizefs: true&lt;/code&gt; ile modül onu koşulsuz çalıştırıyor ve hiçbir disk büyümese bile changed raporluyor. Gürültü olarak görün; asıl sinyal hiçbir VG, LV, dosya sistemi ya da mount görevinin değişiklik raporlamaması.&lt;/p&gt;

&lt;h2&gt;
  
  
  Büyütme denemesi: tek düzenleme, sıfır kesinti
&lt;/h2&gt;

&lt;p&gt;Senaryo: &lt;code&gt;/srv/data&lt;/code&gt;, aşağıya inmemesi gereken bir sunucuda dolmaya yaklaşıyor. Eski dünyada bu bir bakım penceresi, doğru hipervizör diskini deneme-yanılmayla bulmak ve beş komutluk ritüel demekti. Burada tek bir değişken düzenlemesi, bir apply ve playbook'un bir kez daha çalıştırılması; VM hiçbir şey fark etmiyor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ctrl-01'de: data_disk_size_gb 20 -&amp;gt; 30, sonra apply&lt;/span&gt;
root@ctrl-01:~/proxmox-automation/terraform# terraform apply
~ resource &lt;span class="s2"&gt;"proxmox_virtual_environment_vm"&lt;/span&gt; &lt;span class="s2"&gt;"test_vm"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    ~ disk &lt;span class="o"&gt;{&lt;/span&gt;
        ~ size &lt;span class="o"&gt;=&lt;/span&gt; 20 -&amp;gt; 30   &lt;span class="c"&gt;# yalnızca scsi3 değişiyor&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

Plan: 0 to add, 2 to change, 0 to destroy.   &lt;span class="c"&gt;# 2. değişiklik: template VM&lt;/span&gt;
...
proxmox_virtual_environment_vm.test_vm: Modifications &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s

Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 0 added, 2 changed, 0 destroyed.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply, VM çalışırken diski API üzerinden büyüttü; guest'teki virtio-scsi katmanı yeni aygıt boyutunu anında aldı. Ama guest içinde henüz hiçbir şey değişmedi: PV hâlâ eski boyutu, LV ve dosya sistemi de onunla birlikte kayıtlı. Disk büyüdü; üstündeki yığın bilmiyor. Playbook'un tekrar çalıştırılması tam olarak bu yüzden gerekiyor.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Saha oturumunda bir denemede playbook apply'dan önce çalıştırıldı, &lt;code&gt;df&lt;/code&gt; yine 20G raporladı: playbook yalnızca hipervizörün zaten büyüttüğü boyutu içeriye yansıtabiliyor. Sıra: düzenle, &lt;code&gt;terraform apply&lt;/code&gt;, playbook, doğrulama.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible-playbook playbooks/02_storage.yml
TASK &lt;span class="o"&gt;[&lt;/span&gt;Create/resize physical volumes] &lt;span class="k"&gt;****************************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;data01&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# pvresize büyümeyi gördü&lt;/span&gt;
&lt;span class="c"&gt;# (+ diğerlerinde her çalıştırmada olan rutin kontrol)&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Create/extend logical volumes at 100 percent of their VG] &lt;span class="k"&gt;***&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vg_data/lv_data&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# lvextend --resizefs&lt;/span&gt;

PLAY RECAP &lt;span class="k"&gt;******************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;14 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~# ssh ubuntu@192.168.122.50 &lt;span class="s1"&gt;'df -h /srv/data'&lt;/span&gt;
Filesystem                     Size  Used Avail Use% Mounted on
/dev/mapper/vg_data-lv_data    30G  620M   30G   3% /srv/data

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İki görev değişti, yalnızca biri büyüme anlamına geliyor: LV görevi tam olarak büyüyen tek disk için değişti: &lt;code&gt;lvol&lt;/code&gt;, VG'yi LV'den büyük görünce &lt;code&gt;lvextend&lt;/code&gt;'i &lt;code&gt;--resizefs&lt;/code&gt; ile çağırdı, bu da dosya sistemi bağlıyken online &lt;code&gt;xfs_growfs&lt;/code&gt;'i tetikledi. PV görevi dört diskin hepsinde her çalıştırmadaki alışılmış rutin tetiklemeyi işaretledi. Bu hassasiyet, disk-başına-bir-VG yerleşiminin ödülü: büyüme, serial üzerinden tam olarak doğru yığına yönlendiriliyor, komşusuna hiç dokunmadan. Uptime tüm dizi boyunca hiç değişmedi; disk, bağlı ve kullanımdaki bir XFS dosya sistemi altında 20G'den 30G'ye büyüdü.&lt;/p&gt;

&lt;p&gt;Ters yönü denemek de öğretici, çünkü biri er ya da geç bir diski bu yolla küçültmeyi deneyecek. &lt;code&gt;data_disk_size_gb&lt;/code&gt;'yi 20'ye geri çekip apply etmek, planı provider'ın kendi sözleriyle duvara çarptırıyor: &lt;code&gt;Cannot shrink, it is not supported&lt;/code&gt;. Bu hata bir eksiklik değil, tasarımın kendi işini yapması. Daha küçük bir volume'e giden doğru ve sağlıklı yol; doğru boyutta yeni bir disk, bir veri taşıma ve eskisinin kaldırılmasıdır; yerinde yapılan bir göz boyama değil, kendi prosedürü olan planlı bir göç.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hızlı başvuru: belirti, neden, çözüm
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Belirti&lt;/th&gt;
&lt;th&gt;Neden&lt;/th&gt;
&lt;th&gt;Çözüm&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Plan, VM'in yok edilip yeniden oluşturulacağını gösteriyor&lt;/td&gt;
&lt;td&gt;Bir kimlik özniteliği değişmiş (VMID, klon kaynağı), disk boyutu değil&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;yes&lt;/code&gt; yazmadan dur, diff'i oku; kimlik özniteliğini geri al, bir boyut düzenlemesi için asla replace'i kabul etme&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apply &lt;code&gt;VM convert to template failed ... can't convert a template to a template&lt;/code&gt; hatasıyla sonlanıyor&lt;/td&gt;
&lt;td&gt;Geride kalmış bir nesne zaten VMID 9000'i tutuyor; provider "already exists" içeren bir create hatasını zaten-tamamlanmış sayıyor&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;terraform apply&lt;/code&gt;'ı tekrar çalıştır: refresh geride kalanı temizler ve temiz yeniden kurar; ya da önce node'da &lt;code&gt;qm destroy 9000&lt;/code&gt; ile elle kaldır&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH rebuild sonrası &lt;code&gt;REMOTE HOST IDENTIFICATION HAS CHANGED&lt;/code&gt; uyarısı veriyor&lt;/td&gt;
&lt;td&gt;Rebuild edilen VM taze host anahtarları üretti; ctrl-01 eskisini hatırlıyor&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ssh-keygen -f ~/.ssh/known_hosts -R &amp;lt;ip&amp;gt;&lt;/code&gt;, yeniden bağlan, yeni anahtarı kabul et&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assert başarısız: serial 0 diske çözülüyor, lsblk boş serial gösteriyor&lt;/td&gt;
&lt;td&gt;Çalışan QEMU konfigürasyondan eski: serial'ler çalışan bir sürece hotplug edilemiyor&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;qm config&lt;/code&gt; serial'leri gösteriyorsa, VM'i node'da tam durdur-başlat (&lt;code&gt;qm shutdown&lt;/code&gt; sonra &lt;code&gt;qm start&lt;/code&gt;); guest içi reboot QEMU'yu yeniden başlatmaz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serial'ler lsblk'de görünüyor ama &lt;code&gt;/dev/disk/by-id&lt;/code&gt;'de serial girdisi yok&lt;/td&gt;
&lt;td&gt;Bu Ubuntu 26.04 virtio-scsi guest'i by-id symlink'lerini hiç oluşturmuyor&lt;/td&gt;
&lt;td&gt;Düzeltilecek bir şey yok: playbook kernel görüşünü okuyor; by-id ilk sürümün bağımlılığıydı, artık yok&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;qm shutdown&lt;/code&gt;, &lt;code&gt;QEMU Guest Agent is not running&lt;/code&gt; uyarısı veriyor&lt;/td&gt;
&lt;td&gt;Klon taze, &lt;code&gt;01_first_contact.yml&lt;/code&gt; henüz tekrar çalıştırılmadı, agent yok&lt;/td&gt;
&lt;td&gt;Önce &lt;code&gt;01_first_contact.yml&lt;/code&gt;'i çalıştır; kapatma agent olmadan da çalışır, düz bir ACPI kapatmaya düşer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bir boyut parametresini düzenledikten sonra &lt;code&gt;df&lt;/code&gt; hâlâ eski boyutu gösteriyor&lt;/td&gt;
&lt;td&gt;Playbook, &lt;code&gt;terraform apply&lt;/code&gt; diski büyütmeden önce çalıştı&lt;/td&gt;
&lt;td&gt;Sıra önemli: düzenle, &lt;code&gt;terraform apply&lt;/code&gt;, sonra playbook; yalnızca hipervizörün zaten büyüttüğü boyutu yansıtır&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;lvol&lt;/code&gt; ya da &lt;code&gt;lvg&lt;/code&gt; modülü bulunamıyor&lt;/td&gt;
&lt;td&gt;Collection'lar kontrol düğümüne kurulmamış&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible-galaxy collection install -r requirements.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filesystem modülü &lt;code&gt;pvcreate&lt;/code&gt;'i arayıp bulamıyor&lt;/td&gt;
&lt;td&gt;Cloud imajında lvm2 yok (pre_task atlanmış ya da kaldırılmış)&lt;/td&gt;
&lt;td&gt;Playbook'u değiştirmeden tekrar çalıştır; pre_task önce lvm2 ve xfsprogs'u kuruyor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apply, &lt;code&gt;Cannot shrink ... not supported&lt;/code&gt; hatası veriyor&lt;/td&gt;
&lt;td&gt;Bir boyut parametresi küçültülmüş&lt;/td&gt;
&lt;td&gt;Tasarım gereği yalnızca büyümeye izin var; volume gerçekten küçülmeli ise yeni disk oluştur ve taşı&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Sırada ne var?
&lt;/h2&gt;

&lt;p&gt;Faz 2 kapanınca, taze sağlanmış bir VM işletim sistemiyle, kimliğiyle, ağıyla ve eksiksiz DB sınıfı bir depolama düzeniyle geliyor; hepsi tek bir apply ve tek bir playbook'tan. Hâlâ eksik olan, orijinal manuel sürecin depolama adımından sonra yavaşlatan her şey: ajanlar. Faz 3, eski template'in &lt;code&gt;/install&lt;/code&gt; dizinini gerçek Ansible rollerine çeviriyor, ajan başına bir tane, her biri idempotent ve konfigürasyon güdümlü: sunucu adresi &lt;code&gt;zabbix_agent2.conf&lt;/code&gt;'a template'lenmiş Zabbix Agent 2, kontrollü kaynaklardan kurulan Trend Micro ve ManageEngine, QRadar'a işaret eden rsyslog forward kuralları.&lt;/p&gt;

&lt;p&gt;Faz 4 sonra son elle dokunma noktasını kaldırıyor: istek formu. Bu rehberde tanıtılan boyutlar zaten sade tfvars parametreleri, yani birer form alanı adayı; bir Semaphore UI pipeline'ı bir form gönderiminden bir workspace tfvars yazabilir, &lt;code&gt;terraform apply&lt;/code&gt;'ı ve iki playbook'u çalıştırabilir, dakikalar içinde tamamen kurulmuş bir sunucu teslim edebilir. Faz 5 pipeline'ı ölçek için sertleştiriyor: IP adreslerinin elle atanmayı bırakması için IPAM, paralellik sınırları ve gerçekliğin bildirilen durumdan saptığını fark etmek için drift tespiti.&lt;/p&gt;

&lt;p&gt;Kat edilen mesafe kendi paragrafını hak ediyor, çünkü tüm seriyi haklı çıkaran ölçüt bu. Bu serinin yerini aldığı süreç günde yirmi ila doksan kez işletiliyordu: konsol-güdümlü klonlama, elle IP konfigürasyonu, ajan başına bir SSH oturumu, elle düzenlenmiş konfigürasyonlar ve bitirmek için bir reboot; bir DB profili söz konusu olduğunda üstüne bir de depolama işi. İki fazdan sonra aynı sonuç, bir kontrol konteynerinden iki komut, her adımı loglanmış, her hata modu belgelenmiş ve her disk kernel'in onu nerede bulduğuyla değil ne olduğuyla adreslenmiş. Kalan fazlar kalıbın tekrarı, yeni bir fikir değil: durumu bildir, o duruma yakınsa (durumu eşitle), doğrulama çıktısıyla teyit et.&lt;/p&gt;

&lt;p&gt;Companion repo, seri boyunca her fazda biraz daha büyüyor:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;a href="https://github.com/Hakanbaban53/proxmox-automation" rel="noopener noreferrer"&gt;Link: GitHub repo; fazlarla eşleşen dizin yapısı, playbook'lar ve Terraform dosyaları&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bu fazı kendi lab'ınızda denerseniz, by-id'nin sizin ortamınızda çalışıp çalışmadığını ve rebuild sonrası geride kalan nesneleri kontrol etmeye değer; ikisi de bu yazıdaki komutlarla dakikalar içinde çözülüyor. Deneyimleriniz ve kendi disk desenleriniz için yorumlarda bekliyorum.&lt;/p&gt;

</description>
      <category>proxmox</category>
      <category>terraform</category>
      <category>ansible</category>
      <category>devops</category>
    </item>
    <item>
      <title>Proxmox'da Sıfır Dokunuşla VM Sağlama: Terraform, cloud-init ve Ansible (Bölüm 1)</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Tue, 01 Sep 2026 06:00:52 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/proxmoxda-sifir-dokunusla-vm-saglama-terraform-cloud-init-ve-ansible-bolum-1-1aof</link>
      <guid>https://dev.to/hakanbaban53/proxmoxda-sifir-dokunusla-vm-saglama-terraform-cloud-init-ve-ansible-bolum-1-1aof</guid>
      <description>&lt;p&gt;Bir önceki seride Proxmox VE cluster ve Corosync'i kurmuştuk; üç node (pve-a, pve-b, pve-c), bir QDevice, quorum dersleri, split-brain denemesi ve NFS shared storage ile bitirmiştik.&lt;/p&gt;

&lt;p&gt;O lab hâlâ ayakta ve bu yeni seri tam olarak o lab'ın üzerine kuruluyor. Cluster kısmı bitti; şimdi asıl konu, o cluster'dan verimli VM üretmek.&lt;/p&gt;

&lt;p&gt;Başlangıç noktası hepimizin bildiği o manuel rutin: yeni bir sunucu lazım olur, konsolu açarsın, template'den klonlarsın, IP'yi, maskeyi, ağ geçidini elle yazarsın, her agent kurulumu için ayrı ayrı SSH girersin, bir dosyayı elinle düzenlersin, sonunda reboot atarsın. Günde yirmi kurulumda bu sıkıcı; doksan kurulumda yönetilemez. Bu serinin ilk fazı, o işin en can yakıcı katmanını kaldırıyor: işletim sistemi kurulumunun kendisi. Faz 1'in sonunda elimizde tek komutla çalışan bir pipeline olacak; altın kalıbı (golden template) Terraform kuracak, workload VM'i cloud-init ile klonlayacak ve makineyi Ansible'a devredecek.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hedef: tek komut, üç kaynak
&lt;/h2&gt;

&lt;p&gt;Faz 1'in tamamı aslında şu cümleye indirgenebilir: &lt;code&gt;terraform apply&lt;/code&gt; yazdığımda Proxmox üç kaynak oluşturmalı ve Ansible kalanını halletmeli. Üç kaynak, sırayla şunlar:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ubuntu 26.04 LTS cloud imajının NFS storage'a indirilmesi&lt;/li&gt;
&lt;li&gt;Bu imajdan VMID 9000 numaralı, Terraform tarafından yönetilen altın kalıp&lt;/li&gt;
&lt;li&gt;VMID 200 numaralı, statik IP'li, cloud-init'li tam klon (full clone)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Klon ayağa kalkıp ağa girdikten sonra sıra Ansible'a geçiyor; ilk temas (first contact) playbook'u qemu-guest-agent'ı kuruyor ve VM'e "buradan yönetiliyorsun" damgasını basıyor. Sonrası, ileriki fazların konusu: veri diskleri (Faz 2), monitoring ve güvenlik agent'ları (Faz 3), form tabanlı self-servis akış (Faz 4).&lt;/p&gt;

&lt;p&gt;Kullanılan araçların doğrulanmış sürümleri şöyle; hepsi yazım anında (Ağustos 2026) upstream kaynaklardan kontrol edildi. Güncelliğini yitirmiş tutorial'lar, lab'da boşa geçen saatlerin bir numaralı sebebi:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bileşen&lt;/th&gt;
&lt;th&gt;Sürüm&lt;/th&gt;
&lt;th&gt;Not&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Proxmox VE&lt;/td&gt;
&lt;td&gt;9.2&lt;/td&gt;
&lt;td&gt;8.4 dalı 2026-08-31'te EOL; hard minimum 8.4 (Import içerik türü 8.4'le geldi)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform&lt;/td&gt;
&lt;td&gt;1.15.9&lt;/td&gt;
&lt;td&gt;1.16 hâlâ release-candidate aşamasında&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bpg/proxmox provider&lt;/td&gt;
&lt;td&gt;0.111.1&lt;/td&gt;
&lt;td&gt;Eski &lt;code&gt;proxmox_virtual_environment_download_file&lt;/code&gt; adı deprecated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu imajı&lt;/td&gt;
&lt;td&gt;26.04 LTS (Resolute)&lt;/td&gt;
&lt;td&gt;İki varyant: amd64 ve amd64v3 (x86-64-v3 CPU ister)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ansible-core&lt;/td&gt;
&lt;td&gt;2.20.1&lt;/td&gt;
&lt;td&gt;Ubuntu deposundan; Faz 1 için builtin modüller yeterli&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Ön kontrol: dört kapı, on dakika
&lt;/h2&gt;

&lt;p&gt;İlk &lt;code&gt;apply&lt;/code&gt;'dan önce dört kapıyı geçmek gerekiyor: cluster sürümü, CPU komut seti, NFS storage ve ağ planı. Cluster serisindeki disiplin burada da geçerli: her iddia, komut çıktısıyla kanıtlanır.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@pve-a:~# pveversion
pve-manager/9.2.2/b9984c6d90a4bd80 &lt;span class="o"&gt;(&lt;/span&gt;running kernel: 7.0.2-6-pve&lt;span class="o"&gt;)&lt;/span&gt;

root@pve-a:~# &lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt;
192.168.122.11

root@pve-a:~# pvesm status
Name        Type    Status  Total&lt;span class="o"&gt;(&lt;/span&gt;KiB&lt;span class="o"&gt;)&lt;/span&gt;   Used&lt;span class="o"&gt;(&lt;/span&gt;KiB&lt;span class="o"&gt;)&lt;/span&gt;  Available&lt;span class="o"&gt;(&lt;/span&gt;KiB&lt;span class="o"&gt;)&lt;/span&gt;  %
&lt;span class="nb"&gt;local       dir     &lt;/span&gt;active  17687844     4986852    11777140        28.19%
local-lvm   lvmthin active  10940416     0          10940416        0.00%
nfs-shared  nfs     active  486256640    197097472  286114816       40.53%

root@pve-a:~# ping &lt;span class="nt"&gt;-c2&lt;/span&gt; 192.168.122.1
PING 192.168.122.1 &lt;span class="o"&gt;(&lt;/span&gt;192.168.122.1&lt;span class="o"&gt;)&lt;/span&gt; 56&lt;span class="o"&gt;(&lt;/span&gt;84&lt;span class="o"&gt;)&lt;/span&gt; bytes of data.
64 bytes from 192.168.122.1: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.312 ms
64 bytes from 192.168.122.1: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.298 ms

&lt;span class="nt"&gt;---&lt;/span&gt; 192.168.122.1 ping statistics &lt;span class="nt"&gt;---&lt;/span&gt;
2 packets transmitted, 2 received, 0% packet loss, &lt;span class="nb"&gt;time &lt;/span&gt;1001ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;hostname -I&lt;/code&gt; çıktısındaki adres, &lt;code&gt;https://&lt;/code&gt; öneki ve &lt;code&gt;:8006&lt;/code&gt; portuyla birlikte sonradan &lt;code&gt;pve_endpoint&lt;/code&gt; değişkeninin değerini oluşturacak. NFS tarafında 486 GB'lık paylaşımda yer bol; imaj, kalıp ve ilk VM için en az 25 GB gerekiyor. Paket kaybı sıfır olan ping de ağ tarafını kanıtlıyor.&lt;/p&gt;

&lt;p&gt;Çoğu tutorial'ın atladığı bir kapı daha var: CPU komut seti. Ubuntu 26.04'ün iki cloud imajı var; baseline &lt;code&gt;amd64&lt;/code&gt; her x86-64 CPU'da çalışır, &lt;code&gt;amd64v3&lt;/code&gt; ise x86-64-v3 mikro mimari seviyesini (AVX2, BMI2, FMA, MOVBE) ister ve modern donanımda belirgin şekilde daha hızlıdır. v3 imajını bu özelliklerden yoksun bir CPU'da açarsanız boot'ta kernel panic alırsınız; fiziksel CPU yeni olsa bile Proxmox'un VM'e gösterdiği sanal CPU tipi eskiyse aynı sonuç çıkar. Benim lab'ım nested-virt olduğu için bu kontrolü fiziksel makinede, yani laptop üzerinde çalıştırdım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# laptop üzerinde, scripts/check-cpu-v3.sh&lt;/span&gt;
OK: CPU exposes all x86-64-v3 features &lt;span class="o"&gt;(&lt;/span&gt;avx2 bmi2 fma movbe&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
You can use the amd64v3 cloud image.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Storage: content type'ların sessiz tuzağı
&lt;/h2&gt;

&lt;p&gt;PVE node'larının yerel diskleri küçük olduğu için her şey NFS'te duruyor: indirilen cloud imajı, altın kalıp, VM diskleri. Proxmox storage'ları hangi içerik türlerini kabul ettiklerini bildirir; yeni oluşturulmuş bir NFS storage genelde sadece disk imajlarını kabul eder. Faz 1 için kritik olan tür &lt;code&gt;Import&lt;/code&gt;; bu tür kapalıysa Terraform'un indirme kaynağı HTTP 500 ile ölüyor ve hata mesajı storage ayarına hiç değinmiyor. &lt;code&gt;Snippets&lt;/code&gt; de şimdiden açılırsa Faz 4'te custom user-data dosyaları için bu ayara geri dönmek zorunda kalmayız.&lt;/p&gt;

&lt;p&gt;Web arayüzünde &lt;strong&gt;Datacenter &amp;gt; Storage &amp;gt; nfs-shared &amp;gt; Edit&lt;/strong&gt; yolunu izleyip Content listesindeki her kutuyu tiklemek yeterli.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6yyfks9232pwj59l6h7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6yyfks9232pwj59l6h7.png" alt="NFS depolama düzenleme" width="608" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CLI'deki karşılığı tek satır; lab günlüğüne yazdığım da bu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@pve-a:~# pvesm &lt;span class="nb"&gt;set &lt;/span&gt;nfs-shared &lt;span class="nt"&gt;-content&lt;/span&gt; images,rootdir,vztmpl,snippets,import
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Import&lt;/code&gt; türü çoğu tutorial'dan yeni olduğu için bir cümlelik açıklamayı hak ediyor. Proxmox'un &lt;code&gt;download-url&lt;/code&gt; adında bir API ucu var; bir node bu uç sayesinde dosyayı HTTPS üzerinden doğrudan storage'a çekiyor. bpg provider bu ucu &lt;code&gt;proxmox_download_file&lt;/code&gt; kaynağıyla kullanıyor; bu yüzden kontrol düğümünde &lt;code&gt;wget&lt;/code&gt; de, &lt;code&gt;qm importdisk&lt;/code&gt; da yok. Uç, &lt;code&gt;import&lt;/code&gt; içerik türünü bildirmeyen bir storage'a yazmayı reddediyor ve bu ret Terraform tarafına jenerik bir 500 olarak yansıyor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform için API token'ı
&lt;/h2&gt;

&lt;p&gt;Provider'ın cluster'a erişmesi için üç yol var: kullanıcı adı ve şifre, auth ticket ya da API token'ı. Otomasyon için doğru cevap token: tek tek iptal edilebilir, kendi izin sınırını taşır ve bir insanın şifresini CI değişkenine gömmenize gerek kalmaz. Token'ı üç komutla, idempotent şekilde oluşturuyorum; kullanıcı ve izin nesneleri cluster genelindeki yapılandırma veritabanında tutulduğu için komutu tek bir node'da çalıştırmak yeterli, diğer node'lara kendiliğinden replike oluyor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@pve-a:~# pveum user add terraform@pve &lt;span class="nt"&gt;--comment&lt;/span&gt; &lt;span class="s2"&gt;"Terraform automation"&lt;/span&gt;
root@pve-a:~# pveum acl modify / &lt;span class="nt"&gt;--users&lt;/span&gt; terraform@pve &lt;span class="nt"&gt;--roles&lt;/span&gt; Administrator
root@pve-a:~# pveum user token add terraform@pve provider &lt;span class="nt"&gt;--privsep&lt;/span&gt; 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────┬──────────────────────┬────────┬─────────┐
│ token-name │ value                │ expire │ privsep │
├────────────┼──────────────────────┼────────┼─────────┤
│ provider   │ 1a2b3c4d-...         │ never  │       1 │
└────────────┴──────────────────────┴────────┴─────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Web arayüzünde &lt;strong&gt;Datacenter &amp;gt; Users &amp;gt; add&lt;/strong&gt; yolunu izleyip yeni kullanıcı eklenebilir. Önemli olan kısım, Linux PAM standard authentication yerine Proxmox VE authentication server seçilmesi:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqns3iph70tn4qvfzalse.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqns3iph70tn4qvfzalse.png" alt="Yeni Kullanıcı Ekleme" width="630" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ardından Web arayüzünde &lt;strong&gt;Datacenter &amp;gt; API Tokens &amp;gt; add&lt;/strong&gt; yolunu izleyip yeni bir token eklenir:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faoz2cg57j43d5n2pch4j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faoz2cg57j43d5n2pch4j.png" alt="Yeni API Token Ekleme" width="608" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Token değeri, tam token kimliği ile UUID'nin eşittirle birleşimi: &lt;code&gt;terraform@pve!provider=1a2b3c4d-...&lt;/code&gt;. Kopyalarken dizgenin başına ya da sonuna karışan boşluklar, yeni üretilmiş bir token'ın reddedilmesinin en yaygın sebebi; o yüzden tamını tamına alın, ne eksiği ne fazlası. Açık konuşmak gerekirse: Administrator rolü ve &lt;code&gt;privsep 0&lt;/code&gt; bir lab kısayolu. Production'da custom bir role geçilip minimum yetkiyle yetinilmeli (indirme kaynağı için &lt;code&gt;Sys.Audit&lt;/code&gt;, &lt;code&gt;Sys.Modify&lt;/code&gt;, &lt;code&gt;Datastore.AllocateTemplate&lt;/code&gt;; VM kurulumu için &lt;code&gt;VM.Allocate&lt;/code&gt;, &lt;code&gt;VM.Clone&lt;/code&gt; ve benzerleri) ve &lt;code&gt;privsep&lt;/code&gt; 1 yapılmalı. Bu, serinin sonundaki hardening maddelerinden biri.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kontrol düğümü: küçük bir LXC
&lt;/h2&gt;

&lt;p&gt;Terraform ve Ansible'ın çalışacağı kontrol düğümü, NFS üzerinde küçük bir LXC konteyneri: &lt;code&gt;ctrl-01&lt;/code&gt;. Konteyner olması bilinçli bir tercih; saniyeler içinde açılıyor, boştayken kabaca 100 MB RAM yiyor ve root filesystem'i zaten Faz 1'de açtığımız &lt;code&gt;rootdir&lt;/code&gt; içerik türünün kapsamına giriyor. Faz 1'de kernel düzeyinde yetki gerektiren hiçbir şey yok, o yüzden unprivileged konteyner sorunsuz.&lt;/p&gt;

&lt;p&gt;Önce Ubuntu LXC template'ini NFS'e indirip konteyneri oluşturuyorum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@pve-a:~# pveam available &lt;span class="nt"&gt;--section&lt;/span&gt; system | &lt;span class="nb"&gt;grep &lt;/span&gt;ubuntu
root@pve-a:~# pveam download nfs-shared ubuntu-26.04-standard_26.04-1_amd64.tar.zst
downloading ubuntu-26.04-standard_26.04-1_amd64.tar.zst from
download finished

root@pve-a:~# pct create 900 nfs-shared:vztmpl/ubuntu-26.04-standard_26.04-1_amd64.tar.zst &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; ctrl-01 &lt;span class="nt"&gt;--memory&lt;/span&gt; 2048 &lt;span class="nt"&gt;--cores&lt;/span&gt; 2 &lt;span class="nt"&gt;--rootfs&lt;/span&gt; nfs-shared:8 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--net0&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eth0,bridge&lt;span class="o"&gt;=&lt;/span&gt;vmbr0,ip&lt;span class="o"&gt;=&lt;/span&gt;192.168.122.10/24,gw&lt;span class="o"&gt;=&lt;/span&gt;192.168.122.1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--unprivileged&lt;/span&gt; 1 &lt;span class="nt"&gt;--features&lt;/span&gt; &lt;span class="nv"&gt;nesting&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nt"&gt;--start&lt;/span&gt; 1

root@pve-a:~# pct list
VMID Status    Lock Name
900  running       ctrl-01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Template'in tam sürüm dizgesi zamanla değişir; önemli olan üç şey: template'in NFS'e indirilmesi (böylece migration sonrası her node konteyneri başlatabilir), konteynere gelecekteki VM'lerle aynı ağdan statik adres verilmesi ve ağ tarafının aynı vmbr0 köprüsüne takılması. 2 GB RAM, Terraform state işlemlerine rahat rahat yetiyor.&lt;/p&gt;

&lt;h3&gt;
  
  
  İlk giriş: konsolda şifre yok
&lt;/h3&gt;

&lt;p&gt;Konteyner ayağa kalktı, noVNC konsolunu açtım, &lt;code&gt;root&lt;/code&gt; yazdım ve şunu aldım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Login incorrect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ubuntu LXC template'inde root hesabı kilitli geliyor ve hiçbir varsayılan şifresi yok; template, yapılandırmanın konsoldan değil host'tan yapılacağını varsayıyor. Kapının kendisi &lt;code&gt;pct enter&lt;/code&gt;; kimlik doğrulaması olmadan, kernel namespace'i üzerinden konteynerin içine root shell açıyor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@pve-a:~# pct enter 900
root@ctrl-01:~#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Konsoldan tty1'e doğrudan girmek isterseniz içeride &lt;code&gt;passwd root&lt;/code&gt; çalıştırmak yeterli. Node tarafından tek satırlık, etkileşimsiz alternatif de var:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@pve-a:~# pct &lt;span class="nb"&gt;exec &lt;/span&gt;900 &lt;span class="nt"&gt;--&lt;/span&gt; bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo "root:changeme" | chpasswd'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu yazıda "ctrl-01 içinde" etiketli komutların tamamı bu kapıdan girilerek çalıştırıldı.&lt;/p&gt;

&lt;p&gt;Aynı shell'e web arayüzünden de girilebilir:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2c5pf30y7d9n0nile4mj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2c5pf30y7d9n0nile4mj.png" alt="Web arayüzünden konteyner shell'ine giriş" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Minimal konteynerde gpg yok
&lt;/h3&gt;

&lt;p&gt;Terraform'u HashiCorp deposundan kurmak istedim; çünkü Ubuntu'nun kendi deposundaki sürüm geride kalıyor. Elinizdeki tutorial'ların hepsi şöyle bir blok gösterir: anahtarı indir, &lt;code&gt;gpg --dearmor&lt;/code&gt;'dan geçir, keyring'e yaz. Ben de öyle yaptım ve şunu aldım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo: 'gpg': command not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu sadece ilk halka. Minimal Ubuntu LXC template'inde gnupg yok, o yüzden dearmor adımı keyring'e hiçbir şey yazmıyor ve &lt;code&gt;apt-get update&lt;/code&gt; bir sonraki adımda HashiCorp deposunu imzasız reddediyor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NO_PUBKEY AA16FCBCA621E701
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;çünkü &lt;code&gt;signed-by&lt;/code&gt; referansı, hiç var olmamış bir &lt;code&gt;.gpg&lt;/code&gt; dosyasına bakıyor. Çözüm, gpg'yi tamamen atlamak: HashiCorp'ın sunduğu anahtar ASCII-armored ve modern apt (2.4 ve üzeri; Ubuntu 26.04'te apt 3.x var) armored anahtarları &lt;code&gt;.asc&lt;/code&gt; uzantısıyla doğrudan okuyor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~# wget &lt;span class="nt"&gt;-qO&lt;/span&gt; /usr/share/keyrings/hashicorp.asc &lt;span class="se"&gt;\&lt;/span&gt;
  https://apt.releases.hashicorp.com/gpg
root@ctrl-01:~# &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [signed-by=/usr/share/keyrings/hashicorp.asc] &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
  https://apt.releases.hashicorp.com &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lsb_release &lt;span class="nt"&gt;-cs&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; main"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/apt/sources.list.d/hashicorp.list
root@ctrl-01:~# apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; terraform
root@ctrl-01:~# apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ansible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ayrıca doğruladım: Ubuntu 26.04'ün kod adı &lt;code&gt;resolute&lt;/code&gt; ve HashiCorp mirror'ında kendi dağıtımı var; &lt;code&gt;lsb_release -cs&lt;/code&gt; hiçbir özel işlem gerektirmiyor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Üretilmemiş locale
&lt;/h3&gt;

&lt;p&gt;Terraform kuruldu, sıra Ansible'da. &lt;code&gt;ansible --version&lt;/code&gt; ise version banner'ı yerine bunu bastı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: Ansible could not initialize the preferred locale: unsupported locale setting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ubuntu template'i &lt;code&gt;LANG=en_US.UTF-8&lt;/code&gt; set ediyor ama o locale'i hiç üretmiyor; aynı eksiklik, paket kurulumları sırasında perl'den gelen uyarıların da sebebiydi. Ansible, perl kadar hoşgörülü değil; başlamayı tamamen reddediyor. Locale'i bir kez üretmek ikisini de tek hamlede çözüyor ve &lt;code&gt;update-locale&lt;/code&gt; bu ayarı &lt;code&gt;/etc/default/locale&lt;/code&gt;'a taşıdığı için reboot sonrası da kalıcı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~# apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; locales
root@ctrl-01:~# locale-gen en_US.UTF-8
root@ctrl-01:~# update-locale &lt;span class="nv"&gt;LANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en_US.UTF-8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Araç kapıları da artık kanıtla kapandı; her iki banner'ı da gerçek çalıştırmadaki haliyle buraya bırakıyorum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~# terraform &lt;span class="nt"&gt;-version&lt;/span&gt;
Terraform v1.15.9
on linux_amd64

root@ctrl-01:~# ansible &lt;span class="nt"&gt;--version&lt;/span&gt;
ansible-core 2.20.1
  config file &lt;span class="o"&gt;=&lt;/span&gt; /etc/ansible/ansible.cfg
  executable location &lt;span class="o"&gt;=&lt;/span&gt; /usr/bin/ansible
  python version &lt;span class="o"&gt;=&lt;/span&gt; 3.13.2 &lt;span class="o"&gt;(&lt;/span&gt;/usr/bin/python3&lt;span class="o"&gt;)&lt;/span&gt;
  jinja version &lt;span class="o"&gt;=&lt;/span&gt; 3.1.6
  libyaml &lt;span class="o"&gt;=&lt;/span&gt; True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bir küçük sürüm gerçeğini de buraya not edeyim: araştırmam sırasında "Ansible core 2.21" yazmıştım ama Ubuntu deposu &lt;code&gt;ansible-core 2.20.1&lt;/code&gt; + &lt;code&gt;ansible 13.1.0&lt;/code&gt; meta paketini veriyor. Plan 2.21'di, saha 2.20.1'di; saha kazanır.&lt;/p&gt;

&lt;h2&gt;
  
  
  SSH anahtarı ve repo'nun üç aktarmalı yolculuğu
&lt;/h2&gt;

&lt;p&gt;Kontrol düğümü, kurduğu her VM'e anahtar enjekte edecek; önce o anahtar çifti:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~# ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/id_ed25519 &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
Generating public/private ed25519 key pair.
Your identification has been saved &lt;span class="k"&gt;in&lt;/span&gt; /root/.ssh/id_ed25519
Your public key has been saved &lt;span class="k"&gt;in&lt;/span&gt; /root/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:1Zc5k... root@ctrl-01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;cat ~/.ssh/id_ed25519.pub&lt;/code&gt; ile okunan satır, Terraform tarafında her klonlanan VM'e cloud-init &lt;code&gt;user_account&lt;/code&gt; bloğuyla enjekte edilecek olan dizgi. Aynı anahtar malzemesini üç yerde görmek (kontrol düğümü, hypervisor'da &lt;code&gt;qm cloudinit&lt;/code&gt; çıktısı, guest içinde &lt;code&gt;authorized_keys&lt;/code&gt;), Faz 1'in en hızlı uçtan uca sağlaması.&lt;/p&gt;

&lt;p&gt;Repo zip'i laptop'ta duruyor. Konteynere taşımak üç aktarmada tamamlanıyor ve her aktarma, o ayağın elinde zaten olan yolu kullanıyor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# laptop üzerinde: cluster'a (laptop aynı zamanda lab'ın NAT ağ geçidi, 192.168.122.1)&lt;/span&gt;
laptop&lt;span class="nv"&gt;$ &lt;/span&gt;scp ~/Downloads/proxmox-automation.zip root@192.168.122.11:/root/
proxmox-automation.zip  100%  452KB   9.1MB/s   00:00

&lt;span class="c"&gt;# pve-a üzerinde: dosyayı konteynerin filesystem'ine doğrudan yaz; SSH'a gerek yok&lt;/span&gt;
root@pve-a:~# pct push 900 /root/proxmox-automation.zip /root/proxmox-automation.zip

&lt;span class="c"&gt;# ctrl-01 içinde&lt;/span&gt;
root@ctrl-01:~# apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; unzip
root@ctrl-01:~# unzip ~/proxmox-automation.zip &lt;span class="nt"&gt;-d&lt;/span&gt; ~/
root@ctrl-01:~# &lt;span class="nb"&gt;ls&lt;/span&gt; ~/proxmox-automation
ansible  docs  README.md  scripts  terraform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Laptop'tan konteynere doğrudan &lt;code&gt;scp&lt;/code&gt; neden çalışmaz? Çünkü yeni kurulmuş Ubuntu konteynerinde kilitli bir root var, şifre yok, &lt;code&gt;authorized_keys&lt;/code&gt; yok; SSH'ın doğrulayacağı hiçbir şey yok. &lt;code&gt;pct push&lt;/code&gt; bu engeli tasarım gereği bypass ediyor, dosyayı konteyner filesystem'ine node üzerinden yazıyor. Her aktarmanın başarı işareti farklı: &lt;code&gt;scp&lt;/code&gt; ilerleme satırı basıp sessizce çıkıyor, &lt;code&gt;pct push&lt;/code&gt; hiçbir şey basmıyor (Proxmox'un "başarı sessizdir" geleneği) ve &lt;code&gt;ls&lt;/code&gt; nihai makbuz. Bu zincir, repo bir git remote'a taşındığı anda tek bir &lt;code&gt;git clone&lt;/code&gt;'a indirgeniyor; o zaman bu bölümün tamamı tarih oluyor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform tarafı: kritik kararlar
&lt;/h2&gt;

&lt;p&gt;Repo'daki Terraform konfigürasyonunun tamamına girmeyeceğim; sadece beklenmedik bir davranışla karşılaştığınızda açıklaması elinizin altında olsun diye kritik kararları ve iki dosyanın can alıcı kısımlarını buraya bırakıyorum.&lt;/p&gt;

&lt;p&gt;İlki, imaj indirme ve altın kalıp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"proxmox_download_file"&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu_image"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;content_type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"import"&lt;/span&gt;     &lt;span class="c1"&gt;# PVE 8.4+ ve storage'da Import açık olmalı&lt;/span&gt;
  &lt;span class="nx"&gt;datastore_id&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_id&lt;/span&gt;
  &lt;span class="nx"&gt;node_name&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;template_node&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloud_image_url&lt;/span&gt;
  &lt;span class="nx"&gt;file_name&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu-2604-cloudimg.qcow2"&lt;/span&gt;
  &lt;span class="nx"&gt;upload_timeout&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;         &lt;span class="c1"&gt;# provider varsayılanı 600 s, dar geliyor&lt;/span&gt;
  &lt;span class="nx"&gt;overwrite_unmanaged&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"proxmox_virtual_environment_vm"&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu_template"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;template_name&lt;/span&gt;
  &lt;span class="nx"&gt;node_name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;template_node&lt;/span&gt;
  &lt;span class="nx"&gt;vm_id&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;template_vmid&lt;/span&gt;  &lt;span class="c1"&gt;# sabit 9000&lt;/span&gt;
  &lt;span class="nx"&gt;template&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;cpu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cores&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cpu_type&lt;/span&gt;  &lt;span class="c1"&gt;# x86-64-v3, aşağıda hikayesi var&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;scsi_hardware&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"virtio-scsi-pci"&lt;/span&gt;
  &lt;span class="nx"&gt;disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;datastore_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_id&lt;/span&gt;
    &lt;span class="nx"&gt;import_from&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;proxmox_download_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ubuntu_image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;interface&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scsi0"&lt;/span&gt;
    &lt;span class="nx"&gt;size&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;template_disk_size_gb&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;serial_device&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="nx"&gt;vga&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"serial0"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;boot_order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"scsi0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Buradaki en önemli satır &lt;code&gt;import_from&lt;/code&gt;. &lt;code&gt;file_id&lt;/code&gt; kullanırsanız provider sessizce PVE node'una SSH açıp &lt;code&gt;qm disk import&lt;/code&gt; çalıştırıyor; API token'ıyla giden yolda ise SSH yok ve apply template adımında düşüyor. &lt;code&gt;import_from&lt;/code&gt; ise PVE'nin yerel &lt;code&gt;import-from=&lt;/code&gt; disk seçeneğini API üzerinden kullanıyor; kimse hiçbir node'a SSH yapmıyor. İkinci kritik karar: template'te &lt;code&gt;initialization&lt;/code&gt; bloğu olmamalı; cloud-init ayarları klonun işidir, öyle ki tek kalıp, birbirinden tamamen farklı ağ kimlikleriyle workload'lara hizmet verebilsin. Kalıp bir kalıp, makine değil.&lt;/p&gt;

&lt;p&gt;İkinci dosya, workload klonu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"proxmox_virtual_environment_vm"&lt;/span&gt; &lt;span class="s2"&gt;"test_vm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vm_name&lt;/span&gt;
  &lt;span class="nx"&gt;node_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vm_node&lt;/span&gt;
  &lt;span class="nx"&gt;vm_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workload_vmid&lt;/span&gt;  &lt;span class="c1"&gt;# sabit 200&lt;/span&gt;

  &lt;span class="nx"&gt;clone&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;vm_id&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;proxmox_virtual_environment_vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ubuntu_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vm_id&lt;/span&gt;
    &lt;span class="nx"&gt;full&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;retries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;wait_for_ip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# bu satırın 15 dakikalık hikayesi aşağıda&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;# klonun root diskini büyütür&lt;/span&gt;
    &lt;span class="nx"&gt;datastore_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_id&lt;/span&gt;
    &lt;span class="nx"&gt;interface&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scsi0"&lt;/span&gt;
    &lt;span class="nx"&gt;size&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vm_disk_size_gb&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;initialization&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;datastore_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_id&lt;/span&gt;
    &lt;span class="nx"&gt;dns&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;domain&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search_domain&lt;/span&gt;
      &lt;span class="nx"&gt;servers&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dns_servers&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;ip_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;ipv4&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.vm_ip_address}/${var.vm_ip_cidr}"&lt;/span&gt;
        &lt;span class="nx"&gt;gateway&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vm_ip_gateway&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;user_account&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;keys&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;trimspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathexpand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ssh_public_key_path&lt;/span&gt;&lt;span class="p"&gt;)))]&lt;/span&gt;
      &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vm_username&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kararların gerekçeleri, kısaca:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Karar&lt;/th&gt;
&lt;th&gt;Gerekçe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;proxmox_download_file&lt;/code&gt; (yeni ad)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;proxmox_virtual_environment_download_file&lt;/code&gt; deprecated; v1.0'da kalkacak&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scsi_hardware = virtio-scsi-pci&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ubuntu cloud imajlarının istediği kontrolcü; online disk resize'ı yalnızca bu destekliyor, Faz 2 bunun üzerine kurulu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU tipi &lt;code&gt;x86-64-v3&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;PVE varsayılanı &lt;code&gt;x86-64-v2-Auto&lt;/code&gt; AVX2 taşımıyor; amd64v3 guest boot'ta kernel panic yiyor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seri konsol (&lt;code&gt;serial_device&lt;/code&gt; + &lt;code&gt;vga serial0&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Cloud imaj konvansiyonu; standart VGA'da noVNC'de görülen boş ekran belirtisini önler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full clone&lt;/td&gt;
&lt;td&gt;NFS üzerinde bağımsız diskler; production DB sunucusu pratiğinin aynısı&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guest agent cloud-init yerine Ansible'dan&lt;/td&gt;
&lt;td&gt;user-data sade kalıyor, paket yapılandırması yönetim altına giriyor; Faz 3'te drift tespiti de yanında geliyor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  init, validate ve plan: iki schema hatası üst üste
&lt;/h2&gt;

&lt;p&gt;Değişkenleri doldurup (&lt;code&gt;pve_endpoint&lt;/code&gt;, token, &lt;code&gt;storage_id = nfs-shared&lt;/code&gt;, VM adı ve IP'si) provider'ı çekiyorum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~# terraform init
Initializing the backend...
Initializing provider plugins...
- Finding bpg/proxmox versions matching &lt;span class="s2"&gt;"~&amp;gt; 0.111.1"&lt;/span&gt;...
- Installing bpg/proxmox v0.111.1...
- Installed bpg/proxmox v0.111.1 &lt;span class="o"&gt;(&lt;/span&gt;signed, key ID F0582AD6AE97C188&lt;span class="o"&gt;)&lt;/span&gt;

Terraform has been successfully initialized!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  vlan_tag artık yok
&lt;/h3&gt;

&lt;p&gt;İlk &lt;code&gt;terraform plan&lt;/code&gt; tam da burada, schema hatasıyla düştü:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;╷
│ Error: Unsupported argument
│
│   on template.tf line 65, in resource "proxmox_virtual_environment_vm" "ubuntu_template":
│   65:     vlan_tag = var.vlan_tag
│
│ An argument named "vlan_tag" is not expected here.
╵
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Provider 0.111 hattında &lt;code&gt;network_device.vlan_tag&lt;/code&gt; özniteliğini &lt;code&gt;vlan_id&lt;/code&gt; olarak yeniden adlandırmış. Eski tutorial'lar &lt;code&gt;vlan_tag&lt;/code&gt; kullanıyor. Düzeltme tek satır; değişken adını koruyup değeri &lt;code&gt;vlan_id&lt;/code&gt;'ye bağlıyorum, böylece tfvars dosyası değişmiyor&lt;/p&gt;

&lt;p&gt;Bu arada, VLAN-aware köprülerdeki trunk özelliği ayrı bir öznitelik (&lt;code&gt;trunks = "10;20;30"&lt;/code&gt;); &lt;code&gt;vlan_id&lt;/code&gt; ile karıştırmamak gerek.&lt;/p&gt;

&lt;h3&gt;
  
  
  hostname kalkmış, CPU tipinde -Auto yok
&lt;/h3&gt;

&lt;p&gt;Plan bu kez başka iki hatayla düştü. İlki &lt;code&gt;initialization.hostname&lt;/code&gt; özniteliği; provider 0.111'de kaldırılmış. cloud-init hostname'i artık VM'in adından geliyor, o yüzden VM adı geçerli bir DNS ismi olmalı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;│ Error: Unsupported argument
│
│   on vm.tf line 90, in resource "proxmox_virtual_environment_vm" "test_vm":
│   90:     hostname = var.vm_name
│
│ An argument named "hostname" is not expected here.
╵
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İkincisi daha gizliydi: &lt;code&gt;x86-64-v3-Auto&lt;/code&gt; diye bir CPU tipi provider validator'ının listesinde yok. PVE arayüzünde gördüğünüz &lt;code&gt;-Auto&lt;/code&gt; varyantları, provider tarafında geçersiz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;│ Error: expected cpu.0.type to be one of [x86-64-v2 x86-64-v3 ... host ...],
│ got x86-64-v3-Auto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Çözüm: &lt;code&gt;hostname&lt;/code&gt; satırını silmek ve &lt;code&gt;cpu_type&lt;/code&gt;'ı düz &lt;code&gt;x86-64-v3&lt;/code&gt; yapmak. Buradan çıkan ders, &lt;code&gt;terraform validate&lt;/code&gt;'ı alışkanlık haline getirmek oldu; init ile plan arasına sıkışan bu tek komut, tüm öznitelik ve blokları gerçek provider şemasına karşı, cluster'a tek istek atmadan kontrol ediyor. İki schema hatasının ikisini de, kimlik bilgilerine hiç dokunmadan, birer saniyenin altında yakalayabilirdi.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~# terraform validate
Success! The configuration is valid.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ve nihayet sağlıklı bir plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plan: 3 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + ssh_command    = "ssh ubuntu@192.168.122.50"
  + template_vm_id = 9000
  + test_vm_id     = 200
  + test_vm_ip     = "192.168.122.50"
  + test_vm_name   = "vm-test-01"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İlk çalıştırmada üç ekleme, sıfır değişiklik, sıfır silme; olması gereken tablo bu. Outputs bloğu da anahtarların devrini gösteriyor.&lt;/p&gt;

&lt;h2&gt;
  
  
  İlk apply: 4 dakika 25 saniye, 11 saniye, 15 dakika 5 saniye
&lt;/h2&gt;

&lt;p&gt;Konfigürasyon düzeltmeleri sonrası apply. Süreler ölçüldü; işte gerçek değerler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;proxmox_download_file.ubuntu_image: Creating...
proxmox_download_file.ubuntu_image: Still creating... [4m25s elapsed]
proxmox_download_file.ubuntu_image: Creation complete after 4m25s
proxmox_virtual_environment_vm.ubuntu_template: Creating...
proxmox_virtual_environment_vm.ubuntu_template: Creation complete after 11s
proxmox_virtual_environment_vm.test_vm: Creating...
proxmox_virtual_environment_vm.test_vm: Still creating... [15m00s elapsed]
proxmox_virtual_environment_vm.test_vm: Creation complete after 15m5s

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

ssh_command = "ssh ubuntu@192.168.122.50"
template_vm_id = 9000
test_vm_id = 200
test_vm_ip = "192.168.122.50"
test_vm_name = "vm-test-01"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmjtajto784seenqznsvq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmjtajto784seenqznsvq.png" alt="Terraform Apply Sonucunda Yeni Template ve Yeni VM" width="307" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Süreyi belirleyen indirme (4,5 dakika) ve saniyeler içinde ayaklanan kalıp (11 saniye) normal; VM'e bağlanan 15 dakikalık kısım ise asıl sorun işareti.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hata olmayan on beş dakikalık uyarı
&lt;/h3&gt;

&lt;p&gt;Apply'ın kuyruğunda şu uyarı vardı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;╷
│ Warning: error waiting for network interfaces from QEMU agent
│
│ with proxmox_virtual_environment_vm.test_vm,
│ on vm.tf line 7, in resource "proxmox_virtual_environment_vm" "test_vm":
│ 7: resource "proxmox_virtual_environment_vm" "test_vm" {
│
│ timeout while waiting for the QEMU agent on VM "200" to publish the
│ network interfaces
╵
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;15 dakika 5 saniyelik sürenin kendisi ipucu. Klon saniyeler içinde bitti, VM neredeyse anında açıldı; beklemin tamamı, provider'ın guest agent'ı yoklamasından ibaretti. Mekanizmayı sonradan provider'ın kaynak kodundan doğruladım: VM konfigürasyonunda agent açık olduğu sürece provider, VM'i başlattıktan sonra ve sonraki her plan/apply yenilemesinde agent'a ağ arayüzlerini soruyor ve "henüz IP yok" cevabını &lt;code&gt;agent.timeout&lt;/code&gt; (varsayılan 15 dakika) bitene kadar tekrarlıyor. Ubuntu cloud imajları qemu-guest-agent ile gelmiyor; o yüzden her yoklama boşa çıkıyor, 15 dakika dolunca provider bir &lt;code&gt;Warning&lt;/code&gt; ile pes ediyor. Warning, Error değil: VM çalışıyor, state temiz, tek kayıp state'te &lt;code&gt;ipv4_addresses&lt;/code&gt;'ın boş kalması; statik IP tasarımında zaten ihtiyacım yok.&lt;/p&gt;

&lt;p&gt;Üç makul tepki var; repoya işlenen, birincisi:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;agent { wait_for_ip { disabled = true } }&lt;/code&gt; ile sorguyu tamamen atlamak; statik IP'li bu pipeline için doğru varsayılan.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;agent.timeout&lt;/code&gt;'ı kısaltmak (örneğin 2m); bekleme küçülür ama kalkmaz. Agent tabanlı IP keşfini ileride isterseniz makul orta yol.&lt;/li&gt;
&lt;li&gt;Hiçbir şey yapmamak; guest agent kurulunca yoklamalar saniyeler içinde başarıya döner ve uyarı kendiliğinden kaybolur.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Asla doğru olmayan dördüncü yol ise apply'ı Ctrl-C ile kesmek; oluşturulması yarıda kesilen kaynak tainted olur ve arkasında yarım kalmış bir temizlik bırakır. Ben kesmedim, bekledim; 15 dakikanın bedeli bu yazı oldu.&lt;/p&gt;

&lt;p&gt;Düzeltmeyi uygulayıp tekrar plan çektiğimde fark tek bir bloğa iniyordu (agent), apply "0 to add, 2 changed" ile bir saniyede tamamlandı ve sonraki tüm yenilemeler saniye sürdü. Yani &lt;code&gt;wait_for_ip&lt;/code&gt; satırı, bu yazıyı okuyan herkesin konfigürasyonuna eklemesi gereken tek satırlık sigorta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doğrulama: üç bağımsız katman hemfikir olana kadar
&lt;/h2&gt;

&lt;p&gt;Terraform "tamam" dedikten sonra güven meselesi başlıyor. Hipervizör, guest ve ağ katmanlarının üçü de aynı şeyi söyleyene kadar hiçbir şeye inanmıyorum.&lt;/p&gt;

&lt;p&gt;Hipervizör tarafı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@pve-a:~# qm list
VMID  Name             Status   MEM&lt;span class="o"&gt;(&lt;/span&gt;MB&lt;span class="o"&gt;)&lt;/span&gt;  BOOTDISK&lt;span class="o"&gt;(&lt;/span&gt;GB&lt;span class="o"&gt;)&lt;/span&gt;  PID
9000  ubuntu-2604-tpl  stopped  2048     10.00         0
200   vm-test-01       running  2048     10.00         318234

root@pve-a:~# qm config 200 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"scsi0|net0|agent|serial"&lt;/span&gt;
agent: &lt;span class="nv"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
net0: &lt;span class="nv"&gt;virtio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;BC:24:11:2E:AA:F7,bridge&lt;span class="o"&gt;=&lt;/span&gt;vmbr0
scsi0: nfs-shared:vm-200-disk-0,size&lt;span class="o"&gt;=&lt;/span&gt;10G
serial0: socket
vga: serial0

root@ctrl-01:~# terraform state list
proxmox_download_file.ubuntu_image
proxmox_virtual_environment_vm.ubuntu_template
proxmox_virtual_environment_vm.test_vm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kalıbın &lt;code&gt;stopped&lt;/code&gt; ve PID 0 görünmesi tam olması gerektiği gibi; bir kalıbın çalışıyor görünmesi, birilerinin yanlışlıkla kalıbı başlattığı anlamına gelir ve asla olmamalıdır. &lt;code&gt;qm config&lt;/code&gt; çıktısı ise altıncı bölümdeki dört donanım kararını tek bakışta doğruluyor: agent açık, virtio ağ, NFS'te root diski, seri konsol çifti.&lt;/p&gt;

&lt;p&gt;Guest ve ağ tarafı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~# ssh ubuntu@192.168.122.50
The authenticity of host &lt;span class="s1"&gt;'192.168.122.50'&lt;/span&gt; can&lt;span class="s1"&gt;'t be established.
ED25519 key fingerprint is SHA256:dG9w...DzKw.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Welcome to Ubuntu 26.04 LTS (Resolute) (GNU/Linux 6.17.0-25-generic...)

ubuntu@vm-test-01:~$ cloud-init status --wait
status: done

ubuntu@vm-test-01:~$ grep Datasource /var/log/cloud-init-output.log
Cloud-init v. 25.1 finished at ... Datasource DataSourceNoCloud
seeddev=/dev/sr0. Up 14.32 seconds
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prompt'taki &lt;code&gt;vm-test-01&lt;/code&gt;, kaldırılan &lt;code&gt;initialization.hostname&lt;/code&gt; özniteliğine rağmen hostname'in cloud-init tarafından VM adından alındığının kanıtı. &lt;code&gt;status: done&lt;/code&gt; ve &lt;code&gt;DataSourceNoCloud seeddev=/dev/sr0&lt;/code&gt; ise tohum (seed) diskının bulunduğunun ve ilk boot'ta tamamen tüketildiğinin makbuzu. Bu ikisi olmadan, VM göründüğü gibi çalışsa bile cloud-init payload'ı hiç almamış olabilir; belirtisi de şu olur: statik adres yerine imajın DHCP kimliğiyle ağa çıkar. Bir not: VM'i destroy/apply döngüsüne sokarsanız yeni makine yeni host anahtarıyla gelir; yeniden bağlanmadan önce &lt;code&gt;ssh-keygen -R 192.168.122.50&lt;/code&gt; şart. Bunu sonradan, Faz 2 rebuild'i sırasında öğrendim.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ansible ilk temas: pipeline'ın kapanışı
&lt;/h2&gt;

&lt;p&gt;Terraform, açılmış ve ağa bağlanmış bir VM'e kadar götürüyor. İşletim sisteminin içindeki her şey artık Ansible'ın sahası ve Faz 3'te bu ilk playbook'un üzerine tüm agent yığını (Zabbix Agent 2, Trend Micro, ManageEngine, QRadar'a rsyslog forward'u) gelecek. Faz 1 playbook'u bilinçli olarak minik; görevi yönetim kanalını kanıtlamak ve hypervisor'ın tek umursadığı paketi, qemu-guest-agent'ı kurmak.&lt;/p&gt;

&lt;p&gt;Envanter tek VM'lik:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;all&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;provisioned&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;vm-test-01&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ansible_host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.122.50&lt;/span&gt;
          &lt;span class="na"&gt;ansible_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~/proxmox-automation/ansible# ansible provisioned &lt;span class="nt"&gt;-m&lt;/span&gt; ping
vm-test-01 | SUCCESS &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ansible_facts"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"discovered_interpreter_python"&lt;/span&gt;: &lt;span class="s2"&gt;"/usr/bin/python3"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"changed"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
    &lt;span class="s2"&gt;"ping"&lt;/span&gt;: &lt;span class="s2"&gt;"pong"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Playbook dört görevden ibaret: kimlik göster, agent'ı kur, servisi başlat, &lt;code&gt;/etc/provisioned-by&lt;/code&gt; damgasını yaz. (Playbook'un tamamı repoda; burada çalıştırmanın çıktısı daha değerli.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ctrl-01:~# ansible-playbook playbooks/01_first_contact.yml

PLAY &lt;span class="o"&gt;[&lt;/span&gt;First contact - verify a freshly provisioned VM] &lt;span class="k"&gt;***********&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Gathering Facts] &lt;span class="k"&gt;******************************************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01]

TASK &lt;span class="o"&gt;[&lt;/span&gt;Show system identity] &lt;span class="k"&gt;*************************************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"vm-test-01 is up, Ubuntu 26.04"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Install QEMU guest agent] &lt;span class="k"&gt;*********************************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01]

TASK &lt;span class="o"&gt;[&lt;/span&gt;Enable and start the guest agent] &lt;span class="k"&gt;*************************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01]

TASK &lt;span class="o"&gt;[&lt;/span&gt;Mark the VM as managed by this pipeline] &lt;span class="k"&gt;******************&lt;/span&gt;
changed: &lt;span class="o"&gt;[&lt;/span&gt;vm-test-01]

PLAY RECAP &lt;span class="k"&gt;*******************************************************&lt;/span&gt;
vm-test-01 : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;skipped&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;rescued&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;ignored&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ok=5 changed=2&lt;/code&gt; ilk bakışta yanlış görünür; aritmetiği şöyle: &lt;code&gt;ok&lt;/code&gt; sayacı changed olan görevleri de içerir. Beş görevin beş sonucu var; üçü normalde changed raporlardı ama service görevi fresh VM'de &lt;code&gt;ok&lt;/code&gt; rapor etti, çünkü paketin postinst'i, apt görevinin hemen ardından agent'ı zaten başlatıp enable etmişti. İki changed; paket kurulumu ve damga dosyası. Bir dakika sonra ikinci çalıştırma, idempotans makbuzu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vm-test-01 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;changed&lt;/code&gt;'in sıfıra düşmesi bu playbook'u tek seferlik kurulumdan güvenli, tekrar çalıştırılabilir bir operasyona çevirir; Faz 3'te aynı örüntü her provision edilmiş VM'e karşı çalışacak ve sıfırın üstündeki changed, rutin bir yan etki değil, incelenmeye değer bir drift sinyali olacak.&lt;/p&gt;

&lt;h3&gt;
  
  
  Facts enjeksiyonu 2.24'te kalkıyor
&lt;/h3&gt;

&lt;p&gt;İlk çalıştırmada iki görevden şu uyarı düştü:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[DEPRECATION WARNING]: The INJECT_FACTS_AS_VARS setting is scheduled to be
removed in version 2.24. ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Playbook, &lt;code&gt;ansible_distribution&lt;/code&gt; tarzı çıplak fact değişkenleri kullanıyordu. ansible-core 2.20 otomatik fact enjeksiyonunu deprecate etmiş; 2.24'te tamamen kalkıyor. Düzeltme, parantez sözdizimine geçmek:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# eski:&lt;/span&gt;
&lt;span class="na"&gt;msg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;inventory_hostname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;up,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_distribution&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_distribution_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;span class="c1"&gt;# yeni:&lt;/span&gt;
&lt;span class="na"&gt;msg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;inventory_hostname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;up,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_facts['distribution']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_facts['distribution_version']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.24 çıktığında bu uyarı, kırılan playbook'ların başındaki hata mesajına dönüşecek; düzeltmesi de gösterildiği kadar küçük.&lt;/p&gt;

&lt;p&gt;Agent kurulduktan sonra hypervisor tarafındaki son kapı da sessizce açılıyor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@pve-a:~# qm guest cmd 200 ping
root@pve-a:~# &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;qm guest cmd ping&lt;/code&gt; başarılı olunca hiçbir şey basmaz; sessizlik ve sıfır çıkış kodu, testin geçtiğinin işareti. Bu tek satır, 15 dakikalık uyarıyla başlayan döngüyü kapatıyor: agent artık var, yoklamalar saniyeler içinde cevap buluyor ve Proxmox arayüzü VM'in IP adresini kendisi gösteriyor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab aniden dondu
&lt;/h2&gt;

&lt;p&gt;Faz 1'i kapatırken bir de gizemli olay yaşadım; kaydetmemek olmaz. Oturum ortasında pve-a'ya açık SSH bağlantım aniden &lt;code&gt;Connection reset by peer&lt;/code&gt; ile gitti. Yeniden girdiğimde CT 900 önce &lt;code&gt;not running&lt;/code&gt; gösterdi, saniyeler sonra yeniden running oldu; VM 200 ise durmuştu ve sonraki plan &lt;code&gt;started: false -&amp;gt; true&lt;/code&gt; değişikliği gösterdi. Ansible, guest'in uptime'ını 2.2 dakika raporladı; yani makine kısa süre önce yeniden başlamıştı. Üç belirti de aynı anda bir pve-a reboot'una işaret ediyordu ama hiçbir crash kanıtı yoktu.&lt;/p&gt;

&lt;p&gt;Cevap, utanılacak kadar basitti: laptop öğle yemeğinde uykuya girmiş. Bu lab nested-virt; pve-a ve arkadaşları o laptop'un libvirt guest'leri. Host uykuya girince tüm lab bir anda askıya alınıyor; SSH kopuyor, konteyner duruyor, VM kapanıyor. Hiçbir şey çökmedi; &lt;code&gt;onboot=1&lt;/code&gt; ayarları her şeyi kendisi toparladı. Son ders donanımda değil, lab tasarımında: nested-virt lab'ın tek bir fiziksel host'u var ve onun güç yönetimi, cluster'ın beşinci düğümüdür. Lab saatlerinde host'un otomatik uykuya dalmasını kapatmak ve bunu &lt;code&gt;uptime&lt;/code&gt; ile &lt;code&gt;journalctl --list-boots&lt;/code&gt; üzerinden doğrulamak yeterli.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hızlı başvuru: belirti, neden, çözüm
&lt;/h2&gt;

&lt;p&gt;Yazı boyunca geçen belirti ve çözümler, hepsini baştan okumak istemeyenler için tek tabloda:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Belirti&lt;/th&gt;
&lt;th&gt;Neden&lt;/th&gt;
&lt;th&gt;Çözüm&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Konsolda &lt;code&gt;Login incorrect&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Ubuntu LXC template'i şifresiz ve root kilitli&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pct enter 900&lt;/code&gt;, gerekiyorsa &lt;code&gt;passwd&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;gpg: command not found&lt;/code&gt; + &lt;code&gt;NO_PUBKEY&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Minimal template'te gnupg yok; keyring hiç yazılmadı&lt;/td&gt;
&lt;td&gt;Anahtarı &lt;code&gt;.asc&lt;/code&gt; olarak sakla; gpg'ye hiç ihtiyaç yok&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ansible could not initialize the preferred locale&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LANG&lt;/code&gt; set edilmiş ama locale üretilmemiş&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;locale-gen en_US.UTF-8&lt;/code&gt; + &lt;code&gt;update-locale&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan: &lt;code&gt;vlan_tag is not expected here&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Provider 0.111 özniteliği &lt;code&gt;vlan_id&lt;/code&gt; yaptı&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;network_device&lt;/code&gt; içinde &lt;code&gt;vlan_id = N&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan: &lt;code&gt;hostname&lt;/code&gt; yok + &lt;code&gt;x86-64-v3-Auto&lt;/code&gt; geçersiz&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;initialization.hostname&lt;/code&gt; kaldırıldı; &lt;code&gt;-Auto&lt;/code&gt; CPU tipleri validatörde yok&lt;/td&gt;
&lt;td&gt;hostname satırını sil (VM adı DNS ismi), &lt;code&gt;cpu_type = x86-64-v3&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VM 15 dk &lt;code&gt;Still creating&lt;/code&gt; + Warning&lt;/td&gt;
&lt;td&gt;Agent açık ama imajda qemu-guest-agent yok; &lt;code&gt;agent.timeout&lt;/code&gt; 15m&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;wait_for_ip { disabled = true }&lt;/code&gt;; asla Ctrl-C değil&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;INJECT_FACTS_AS_VARS&lt;/code&gt; deprecation uyarısı&lt;/td&gt;
&lt;td&gt;ansible-core 2.24'te çıplak fact değişkenleri kalkıyor&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ansible_facts['...']&lt;/code&gt; parantez sözdizimi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH reset, CT durdu, VM kapandı; kanıt yok&lt;/td&gt;
&lt;td&gt;Laptop uykusu; nested-virt lab'ın tamamı host'a bağlı&lt;/td&gt;
&lt;td&gt;Lab saatlerinde host uykusunu kapat; &lt;code&gt;journalctl --list-boots&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Sırada ne var?
&lt;/h2&gt;

&lt;p&gt;Faz 1 tek diskli bir VM üretiyor; web sunucusu ve jump host için yeterli, veritabanı sunucusu için değil. Faz 2'nin konusu çoklu disk örüntüsü: &lt;code&gt;/var&lt;/code&gt;, &lt;code&gt;/log&lt;/code&gt;, &lt;code&gt;/data&lt;/code&gt; ve &lt;code&gt;/backup&lt;/code&gt; için ayrı SCSI cihazlar, her diske Terraform &lt;code&gt;serial&lt;/code&gt; özniteliğiyle kalıcı kimlik ve konuk içinde LVM + XFS zinciri. Buraya çekilen temeller tam olarak Faz 2'nin ihtiyacı olanlar; &lt;code&gt;virtio-scsi-pci&lt;/code&gt; kontrolcüsü online disk resize'ı destekleyen tek kontrolcü ve serial etiketi, &lt;code&gt;/dev/sdb&lt;/code&gt; harflerinin konuk içinde kayması problemine kökten çözüm. Müşteri bir terabayt ek data isterse cevap bir satırlık değişken düzenlemesi ve bir re-apply olacak.&lt;/p&gt;

&lt;p&gt;Companion repo, seri boyunca her fazda biraz daha büyüyor:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;a href="https://github.com/Hakanbaban53/proxmox-automation" rel="noopener noreferrer"&gt;Link: GitHub repo; fazlarla eşleşen dizin yapısı, playbook'lar ve Terraform dosyaları&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bu fazı siz de kendi lab'ınızda denerseniz, 15 dakikalık uyarı sizin de karşınıza çıkacaktır; çözümü, yazının yukarısındaki o tek satırlık sigorta. Sorularınız, düzeltmeleriniz ve kendi karşılaştığınız tuzaklar için yorumlarda bekliyorum.&lt;/p&gt;

</description>
      <category>proxmox</category>
      <category>terraform</category>
      <category>ansible</category>
      <category>devops</category>
    </item>
    <item>
      <title># Redundant Links, İzleme Araçları ve Bir Affinity Kilitlenmesi (Modül 5)</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Wed, 26 Aug 2026 06:32:30 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/-redundant-links-izleme-araclari-ve-bir-affinity-kilitlenmesi-modul-5-479j</link>
      <guid>https://dev.to/hakanbaban53/-redundant-links-izleme-araclari-ve-bir-affinity-kilitlenmesi-modul-5-479j</guid>
      <description>&lt;p&gt;&lt;strong&gt;Seri:&lt;/strong&gt; Proxmox VE Cluster ve Corosync | Hafta 5&lt;/p&gt;




&lt;p&gt;Serinin adı "Cluster ve Corosync"; ama dört modüldür ağırlık HA Manager, resource affinity ve CRS'teydi, Corosync'in kendisine (redundant link'ler, izleme araçları) hiç dönmemiştim. Bu modülde iki konuyu birleştirip derinlemesine işledim: birden fazla corosync link'i tanımlayıp gerçekten birini kesip diğerinin devralmasını kanıtlamak, ve günlük operasyonda kullanılacak izleme araçlarını tek tek denemek. İkisi de planladığımdan çok daha fazla soru açtı; biri yanlış bir config anahtarı yüzünden saatler süren bir araştırmaya dönüştü, diğeri ise hiç beklemediğim bir kilitlenme keşfiyle bitti.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bölüm 1: Redundant Corosync Links
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Kurulum: İkinci Link'i Eklemek
&lt;/h3&gt;

&lt;p&gt;Şu ana kadar cluster'ımızda tek bir corosync link'i vardı (&lt;code&gt;link1&lt;/code&gt;, izole &lt;code&gt;corosync-net&lt;/code&gt; ağı). Management ağını (&lt;code&gt;192.168.122.x&lt;/code&gt;) &lt;code&gt;link0&lt;/code&gt; olarak ekleyip gerçek bir yedeklilik kurdum; &lt;code&gt;/etc/pve/corosync.conf&lt;/code&gt;'u kopyalayıp düzenleyip atomik olarak yerine taşıdım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; /etc/pve/corosync.conf /etc/pve/corosync.conf.new
&lt;span class="c"&gt;# nodelist'teki her node'a ring0_addr ekledim, totem'e ikinci bir interface bloğu ekledim&lt;/span&gt;
&lt;span class="nb"&gt;mv&lt;/span&gt; /etc/pve/corosync.conf.new /etc/pve/corosync.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doğrulama:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;corosync-cfgtool &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LINK ID 0 udp
    addr    = 192.168.122.11
    status: ... connected ... connected
LINK ID 1 udp
    addr    = 10.10.10.11
    status: ... connected ... connected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Teknik olarak başarılı; iki link de bağlı. Ama log'a dikkatlice bakınca, mimarimizin niyetini tersine çeviren bir şey oldu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[KNET  ] rx: host: 3 link: 0 is up
[KNET  ] host: host: 3 (passive) best link: 0 (pri: 1)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;link_mode: passive&lt;/code&gt; modunda, öncelik eşitken &lt;strong&gt;düşük numaralı link kazanıyor&lt;/strong&gt;. &lt;code&gt;link0&lt;/code&gt;'ı sonradan eklediğim için, o Corosync'in asıl trafiğini üstlenmiş; Modül 0'da özellikle izole ettiğimiz &lt;code&gt;corosync-net&lt;/code&gt; (&lt;code&gt;link1&lt;/code&gt;) sessizce yedek konuma düşmüştü.&lt;/p&gt;

&lt;h3&gt;
  
  
  Yanlış Anahtar, Saatler Süren Bir Araştırma
&lt;/h3&gt;

&lt;p&gt;Bunu düzeltmek için &lt;code&gt;link1&lt;/code&gt;'e daha yüksek öncelik vermeye çalıştım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;linknumber&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;linknumber&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İşe yaramadı. &lt;code&gt;corosync-cmapctl | grep priority&lt;/code&gt; sadece &lt;strong&gt;tek, indekslenmemiş&lt;/strong&gt; bir satır gösterdi: &lt;code&gt;totem.interface.priority (str) = 10&lt;/code&gt;. Diğer parametreler (&lt;code&gt;knet_ping_interval&lt;/code&gt;, &lt;code&gt;knet_ping_timeout&lt;/code&gt;) düzgün şekilde &lt;code&gt;interface.0.*&lt;/code&gt; / &lt;code&gt;interface.1.*&lt;/code&gt; diye ayrılmışken, &lt;code&gt;priority&lt;/code&gt; öyle değildi. Log'da "best link" seçimi hâlâ &lt;code&gt;(pri: 1)&lt;/code&gt; gösteriyordu; bizim verdiğimiz değer hiç okunmamıştı.&lt;/p&gt;

&lt;p&gt;Tam bir servis restart'ının (&lt;code&gt;systemctl restart corosync&lt;/code&gt;) sorunu çözebileceğini düşündüm; denedim, çözmedi. Restart, &lt;code&gt;pve-a&lt;/code&gt;'yı ~13 saniyeliğine cluster'dan düşürdü (&lt;code&gt;Nodes: 1, Quorate: No&lt;/code&gt;), ama &lt;code&gt;priority&lt;/code&gt; yine tek, indekslenmemiş kaldı.&lt;/p&gt;

&lt;p&gt;Sebep, resmi man sayfasında (&lt;code&gt;corosync.conf(5)&lt;/code&gt;) çıktı: doğru anahtar &lt;code&gt;priority&lt;/code&gt; değil, &lt;strong&gt;&lt;code&gt;knet_link_priority&lt;/code&gt;&lt;/strong&gt;. Bizim yazdığımız &lt;code&gt;priority&lt;/code&gt;, geçerli bir knet parametresi olmadığı için corosync tarafından sessizce, gevşek bir yere yazılmış; hiçbir zaman gerçek link seçim mantığına girmemiş.&lt;/p&gt;

&lt;h3&gt;
  
  
  Doğru Syntax, Kesin Kanıt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;linknumber&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;knet_link_priority&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;linknumber&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nx"&gt;knet_link_priority&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;corosync-cmapctl | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"interface.*priority"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;totem.interface.0.knet_link_priority&lt;/span&gt; &lt;span class="err"&gt;(u8)&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;5&lt;/span&gt;
&lt;span class="err"&gt;totem.interface.1.knet_link_priority&lt;/span&gt; &lt;span class="err"&gt;(u8)&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu sefer düzgün indekslenmiş. Log de anında değişti:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[KNET  ] host: host: 3 (passive) best link: 0 (pri: 5)
[KNET  ] host: host: 3 (passive) best link: 1 (pri: 10)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Corosync her iki linki de değerlendirip yüksek öncelikli olanı (&lt;code&gt;link1&lt;/code&gt;, corosync-net) seçmiş.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kesme ve Geri Getirme Testi
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;link1&lt;/code&gt;'i fiziksel host'tan kestim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh domif-setlink pve-a vnet0 down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[KNET  ] link: host: 3 link: 1 is down
[KNET  ] host: host: 3 (passive) best link: 0 (pri: 5)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anında &lt;code&gt;link0&lt;/code&gt;'a geçiş; &lt;code&gt;pvecm status&lt;/code&gt; kesintisiz &lt;code&gt;Quorate: Yes&lt;/code&gt; kaldı, hâlâ tam oy sayısıyla. Sonra geri açtım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh domif-setlink pve-a vnet0 up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[KNET  ] rx: host: 3 link: 1 is up
[KNET  ] host: host: 3 (passive) best link: 1 (pri: 10)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Saniyeler içinde&lt;/strong&gt; &lt;code&gt;link1&lt;/code&gt;'e geri dönüş. Bu, hem failover'ı hem failback'i (yüksek öncelikli link geri gelince otomatik ona dönme) doğru anahtarla kesin olarak kanıtladı. İlk yanlış denemede ("sticky" davranış sandığım şey) aslında sadece önceliğin hiç uygulanmamış olmasıydı; gerçek mekanizma tam beklendiği gibi çalışıyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bölüm 2: İzleme Araçları
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;corosync-quorumtool&lt;/code&gt;: Daha Okunabilir Bir Alternatif
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;corosync-quorumtool &lt;span class="nt"&gt;-s&lt;/span&gt;
corosync-quorumtool &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İçerik &lt;code&gt;pvecm status&lt;/code&gt; ile aynı, ama membership tablosu hex ID (&lt;code&gt;0x00000001&lt;/code&gt;) yerine düz ondalık ID + doğrudan hostname gösteriyor. Günlük kullanımda daha okunabilir.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token/Consensus Formülünü Canlı Doğrulamak
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;corosync-cmapctl | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'totem.token|totem.consensus'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;runtime.config.totem.token&lt;/span&gt; &lt;span class="err"&gt;(u32)&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;3125&lt;/span&gt;
&lt;span class="err"&gt;runtime.config.totem.consensus&lt;/span&gt; &lt;span class="err"&gt;(u32)&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;3750&lt;/span&gt;
&lt;span class="err"&gt;totem.token_coefficient&lt;/span&gt; &lt;span class="err"&gt;(u32)&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;125&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modül 0'da bulduğumuz formül: &lt;code&gt;token = 3000 + (node_sayısı - 2) × token_coefficient&lt;/code&gt;. 3 node ile: &lt;code&gt;3000 + 1×125 = 3125&lt;/code&gt;. Tam uyuyor. &lt;code&gt;consensus = 1.2 × token = 3750&lt;/code&gt;. O da uyuyor. Aylar önce bir dokümandan öğrendiğimiz bir formülü, kendi cluster'ımızın gerçek sayılarıyla doğrulamış olduk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Beklenmedik Bulgu: İki Ayrı &lt;code&gt;corosync.conf&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pve-cluster&lt;/code&gt; (pmxcfs) loglarına bakınca, hiç bilmediğimiz bir mekanizma ortaya çıktı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; pve-cluster &lt;span class="nt"&gt;-n&lt;/span&gt; 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[dcdb] notice: wrote new corosync config '/etc/corosync/corosync.conf' (version = 7)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bir saniye sonra, corosync log'unda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[CFG] Config reload requested by node 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Şimdiye kadar hep &lt;code&gt;/etc/pve/corosync.conf&lt;/code&gt;'u düzenledik; bu, pmxcfs üzerinden cluster geneline otomatik yayılan sanal bir dosya. Ama corosync daemon'ının kendisi aslında &lt;strong&gt;&lt;code&gt;/etc/corosync/corosync.conf&lt;/code&gt;&lt;/strong&gt;'u okuyor, farklı bir yol. pmxcfs bizim düzenlememizi algılayıp gerçek dosyayı arkada kendisi yazıyor, corosync de bunu saniyeler içinde fark edip reload istiyor. İki ayrı dosya, tek görünen arayüz.&lt;/p&gt;

&lt;h3&gt;
  
  
  Yan Etki: Corosync Restart'ı HA'ya da Yayılıyor
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;systemctl restart corosync&lt;/code&gt; denememizin ardından, &lt;code&gt;pve-ha-crm&lt;/code&gt;/&lt;code&gt;pve-ha-lrm&lt;/code&gt; logları da bir "Boot" işareti ve yeniden başlatma döngüsü gösterdi; VM'ler yeniden başlatılmış, lock'lar yeniden alınmış. "Sadece corosync'i restart ettim" sanısı, HA katmanına da dalga dalga yayılabiliyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bölüm 3: Bir Node'u Gerçekten Çıkarmak
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prosedür
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pve-c&lt;/code&gt;'yi resmi prosedürle çıkardım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# pve-c'de&lt;/span&gt;
systemctl stop pve-cluster corosync

&lt;span class="c"&gt;# pve-a'da&lt;/span&gt;
pvecm delnode pve-c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes: 2
Expected votes: 3
Total votes: 3
Quorum: 2
    0x00000000    1    Qdevice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beklenmedik detay: &lt;strong&gt;QDevice oyu 2'den 1'e düşmüş.&lt;/strong&gt; &lt;code&gt;lms&lt;/code&gt; algoritmasının formülü &lt;code&gt;N-1&lt;/code&gt; (node sayısı eksi bir); önceden 3-1=2'ydi, şimdi 2-1=1. &lt;code&gt;pvecm delnode&lt;/code&gt;, node sayısını düşürse de QDevice algoritmasına (&lt;code&gt;lms&lt;/code&gt; olarak kalmaya devam ediyor) hiç dokunmuyor; bunu elle yönetmek bize düşüyor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ortamın Kararsızlığı: Beklenmedik Bir HA Kilitlenmesi
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pve-c&lt;/code&gt; çıkarma işleminden sonra, &lt;code&gt;ha-manager status&lt;/code&gt; saatlerce şunu gösterdi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fencing standby (CRM watchdog standby)
lrm pve-a (wait_for_agent_lock, ...)
lrm pve-b (wait_for_agent_lock, ...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Corosync/quorum seviyesi tamamen sağlıklıyken (&lt;code&gt;Quorate: Yes&lt;/code&gt;), HA CRM/LRM katmanı ayrı bir kilitte takılı kalmıştı. Loglardaki &lt;code&gt;Boot&lt;/code&gt; işaretleri, host laptop'un bu süre zarfında birkaç kez uyku/uyanma döngüsünden geçtiğini gösteriyordu; nested VM'ler bu geçişlerde bazen kendini resetliyor. Bu, kontrollü bir test değildi, dürüstçe belirtmem gerekiyor. Ama şunu net gösterdi: &lt;strong&gt;corosync seviyesinde quorum sağlıklı olması, HA katmanının da sağlıklı olduğu anlamına gelmiyor&lt;/strong&gt;; ikisi ayrı ayrı bozulabiliyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bölüm 4: &lt;code&gt;lms&lt;/code&gt;'ten &lt;code&gt;ffsplit&lt;/code&gt;'e Geçiş
&lt;/h2&gt;

&lt;p&gt;Artık gerçekten 2 node'dayız; Modül 2'de "resmi olarak desteklenmiyor" dediğimiz &lt;code&gt;lms&lt;/code&gt; yerine, resmi olarak önerilen &lt;code&gt;ffsplit&lt;/code&gt;'i test edebileceğimiz ilk an bu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/algorithm: lms/algorithm: ffsplit/'&lt;/span&gt; corosync.conf.new
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total votes: 3
    0x00000000    1    Qdevice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;QDevice oyu &lt;strong&gt;1&lt;/strong&gt;'de kaldı; &lt;code&gt;ffsplit&lt;/code&gt; her zaman sabit 1 oy veriyor, &lt;code&gt;lms&lt;/code&gt;'in değişken &lt;code&gt;N-1&lt;/code&gt; formülü değil. 2-node özelinde rakamsal sonuç aynı görünüyor (zaten &lt;code&gt;N-1=1&lt;/code&gt; burada), ama mekanizma temelden farklı; asıl fark bir node düştüğünde ortaya çıkacak.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gerçek Bir 2-Node + QDevice Testi
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pve-b&lt;/code&gt;'yi çökerttim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh destroy pve-b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes: 1
Total votes: 2
Quorum: 2
Quorate: Yes
    0x00000001    1    pve-a
    0x00000000    1    Qdevice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pve-a&lt;/code&gt; (1 oy) + QDevice (&lt;code&gt;ffsplit&lt;/code&gt;'in sabit 1 oyu) = 2, eşik de 2. &lt;strong&gt;Quorate: Yes.&lt;/strong&gt; Bu, Modül 2'de simüle ettiğimiz senaryonun, artık gerçekten resmi olarak desteklenen 2-node yapılandırmasında, doğru algoritmayla çalıştığının kanıtı.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bölüm 5: Beklenmedik Kapanış, Affinity Kilitlenmesi
&lt;/h2&gt;

&lt;p&gt;Asıl beklemediğim şey burada oldu. VM 102 (o an &lt;code&gt;pve-b&lt;/code&gt;'deydi) için &lt;code&gt;ha-manager status&lt;/code&gt;, &lt;code&gt;fence&lt;/code&gt; durumundan sonra sonsuza kadar &lt;strong&gt;&lt;code&gt;recovery&lt;/code&gt;&lt;/strong&gt;'de takılı kaldı. Loglara baktım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; pve-ha-crm &lt;span class="nt"&gt;-n&lt;/span&gt; 40 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"102"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;08:16:50  recovering service 'vm:102' from fenced node 'pve-b' failed, no recovery node found
08:17:00  recovering service 'vm:102' from fenced node 'pve-b' failed, no recovery node found
08:17:10  recovering service 'vm:102' from fenced node 'pve-b' failed, no recovery node found
... (her 10 saniyede bir, kesintisiz tekrarlanıyor)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sebep: Modül 4'te tanımladığımız negatif resource affinity kuralı (VM 100 ve 102 asla aynı node'da olamaz) hâlâ aktifti. Tek ayakta kalan node (&lt;code&gt;pve-a&lt;/code&gt;) zaten VM 100'ü barındırıyordu; VM 102 için &lt;strong&gt;yasal hiçbir hedef yoktu&lt;/strong&gt;. Sistem bu imkansız durumu, Modül 4'teki gerçek migration hatasından (&lt;code&gt;exit code 255&lt;/code&gt;, birkaç denemeden sonra &lt;code&gt;error&lt;/code&gt; state'ine düşme) tamamen farklı bir şekilde ele aldı: &lt;strong&gt;hiç &lt;code&gt;error&lt;/code&gt;'a düşmedi&lt;/strong&gt;, sadece sessizce, sabırla, her 10 saniyede bir yeniden denemeye devam etti.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pve-b&lt;/code&gt;'yi geri getirdim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh start pve-b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Birkaç dakika sonra:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lrm pve-b (active, ...)
service vm:102 (pve-b, started)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pve-b&lt;/code&gt; geri gelir gelmez, VM 102 için nihayet yasal bir hedef oluştu (VM 100'den uzak), ve kilitlenme kendiliğinden çözüldü.&lt;/p&gt;

&lt;p&gt;Bu, Modül 3'teki "yanlış çalışmaya devam etmektense hiç çalışmamak" fail-safe felsefesinin bir üst seviyesi: burada sistem "hiç çalışmamayı" bile seçmedi, "kuralı çiğnemektense sonsuza kadar beklemeyi" seçti. HA'nın kendi kurtarma mantığı, kendi affinity kurallarıyla çelişkiye düşebiliyor, ve sistem bunu görmezden gelmiyor; askıda bırakıyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Genel Değerlendirme
&lt;/h2&gt;

&lt;p&gt;Bu modül, planladığımdan çok daha derin bir yere gitti, ve muhtemelen serinin en yoğun modülü oldu.&lt;/p&gt;

&lt;p&gt;Birincisi, &lt;code&gt;priority&lt;/code&gt; yerine &lt;code&gt;knet_link_priority&lt;/code&gt; gerektiği yanlış anahtar hatası. Saatler süren bir araştırmaya (config reload mı restart mı, cmapctl'in tuhaf tek satırı, "sticky" yanlış hipotezi) yol açtı, ama sonunda man sayfasını okumanın (tahmin etmek yerine) değerini bir kez daha kanıtladı.&lt;/p&gt;

&lt;p&gt;İkincisi, pmxcfs'in arkada iki ayrı corosync.conf dosyası yönetmesi. Beş modüldür &lt;code&gt;/etc/pve/corosync.conf&lt;/code&gt;'u düzenliyorduk, corosync'in gerçekte &lt;code&gt;/etc/corosync/corosync.conf&lt;/code&gt;'u okuduğunu hiç fark etmemiştik.&lt;/p&gt;

&lt;p&gt;Üçüncüsü, ve en değerlisi: affinity kilitlenmesi. Bunu planlamamıştık; gerçek bir node çıkarma operasyonunun, gerçek bir node çökmesinin, ve Modül 4'te tanımladığımız bir kuralın kesişiminde kendiliğinden ortaya çıktı. HA'nın "kuralı çiğnemektense sonsuza kadar bekle" tercihi, sistemin ne kadar temkinli tasarlandığını gösteren en net kanıt oldu.&lt;/p&gt;

&lt;p&gt;Bu seride Corosync'in temellerinden (Modül 1) quorum'a (Modül 2), HA Manager'a (Modül 3), resource affinity ve CRS'e (Modül 4), ve şimdi redundant link'lere ve izleme araçlarına kadar geniş bir yüzeyi gezdim. Her modülde en az bir varsayımım yanlış çıktı, ve neredeyse her seferinde yanlış çıkan varsayım, doğru olandan daha çok şey öğretti. Bu, sanırım bu serinin özeti: hazır bir HOL yokken, doğru cevabı bilmiyor olmak bir eksiklik değil, öğrenmenin kendisiydi.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bu seri, resmi Proxmox VE dokümantasyonundan (pve.proxmox.com), corosync man sayfalarından, ve topluluk kaynaklarından derleniyor. Buradaki gözlemler bir homelab ortamına dayanıyor; production kararları için resmi dokümantasyon ve deneyimli sistem yöneticileri referans alınmalıdır.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>infrastructure</category>
      <category>linux</category>
      <category>monitoring</category>
      <category>networking</category>
    </item>
    <item>
      <title># Resource Affinity, CRS ve Bir Gece Boyu Salınım (Modül 4)</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Fri, 21 Aug 2026 05:50:06 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/-resource-affinity-crs-ve-bir-gece-boyu-salinim-modul-4-ji9</link>
      <guid>https://dev.to/hakanbaban53/-resource-affinity-crs-ve-bir-gece-boyu-salinim-modul-4-ji9</guid>
      <description>&lt;p&gt;&lt;strong&gt;Seri:&lt;/strong&gt; Proxmox VE Cluster ve Corosync&lt;/p&gt;




&lt;p&gt;Modül 3'ün sonunda söz vermiştim: birden fazla VM ile resource affinity kurallarını (aynı node'da tutma/ayrı tutma) ve CRS'in (Cluster Resource Scheduler) yük dengeleme davranışını test edecektim. Planım basitti: birkaç kural ekle, birkaç migration dene, sonuçları yaz. Gerçekte olan şu oldu: bir gece boyu süren beklenmedik bir salınım, gerçek bir HA arızası, ve kendi verdiğim yanlış bir talimatın beni yanlış bir teşhise sürüklemesi. Hepsini olduğu gibi anlatıyorum.&lt;/p&gt;




&lt;h2&gt;
  
  
  Kavramlar: Resource Affinity ve CRS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk65rrq1zr0o2edy2gyjz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk65rrq1zr0o2edy2gyjz.png" alt="Resource Affinity &amp;amp; CRS" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource affinity&lt;/strong&gt;, node-affinity'den farklı bir şey; node-affinity bir VM'i belirli node'lara bağlarken, resource affinity iki veya daha fazla VM'i &lt;strong&gt;birbirine göre&lt;/strong&gt; konumlandırıyor. İki tipi var: &lt;strong&gt;pozitif&lt;/strong&gt; (belirtilen kaynaklar hep aynı node'da olmalı) ve &lt;strong&gt;negatif&lt;/strong&gt; (hep farklı node'larda olmalı). Resmi dokümantasyona göre negatif bir kuralda cluster'daki node sayısından fazla kaynak olamıyor (bizim 3 node'umuzda en fazla 3 kaynak), ve aynı VM çifti aynı anda hem pozitif hem negatif bir kuralda olamıyor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CRS (Cluster Resource Scheduler)&lt;/strong&gt;, HA kaynaklarının hangi node'a yerleştirileceğine karar veren mekanizma. Üç mod var: &lt;code&gt;basic&lt;/code&gt; (sadece guest sayısına bakar), &lt;code&gt;static&lt;/code&gt; (yapılandırılmış CPU/RAM kotalarını da hesaba katar), &lt;code&gt;dynamic&lt;/code&gt; (gerçek zamanlı kullanımı da hesaba katar). Dynamic mod, isteğe bağlı bir &lt;strong&gt;otomatik yeniden dengeleme&lt;/strong&gt; (auto-rebalance) özelliği de sunuyor; cluster dengesizliği belirli bir eşiği aşarsa, otomatik migration'lar tetikliyor. Config, &lt;code&gt;/etc/pve/datacenter.cfg&lt;/code&gt; içinde &lt;code&gt;crs: ha=dynamic,ha-auto-rebalance=1,ha-auto-rebalance-threshold=&amp;lt;yüzde&amp;gt;,ha-auto-rebalance-hold-duration=&amp;lt;tur&amp;gt;&lt;/code&gt; şeklinde tutuluyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 1: Test VM'lerini Hazırlamak
&lt;/h2&gt;

&lt;p&gt;VM 100'ü iki kez klonladım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm clone 100 101 &lt;span class="nt"&gt;--name&lt;/span&gt; alpine-b &lt;span class="nt"&gt;--full&lt;/span&gt;
qm clone 100 102 &lt;span class="nt"&gt;--name&lt;/span&gt; alpine-c &lt;span class="nt"&gt;--full&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İkisini de HA'ya ekledim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager add vm:101 &lt;span class="nt"&gt;--state&lt;/span&gt; started
ha-manager add vm:102 &lt;span class="nt"&gt;--state&lt;/span&gt; started
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İlk &lt;code&gt;ha-manager status&lt;/code&gt; çıktısında üçü de &lt;code&gt;pve-a&lt;/code&gt;'da görünüyordu. İlk bakışta "pozitif affinity zaten çalışıyor" diye düşünebilirdim, ama bu yanlış bir sonuç olurdu: &lt;code&gt;qm clone&lt;/code&gt;'u &lt;code&gt;pve-a&lt;/code&gt;'dan çalıştırdığım için, yeni VM'lerin config dosyaları zaten oraya oluşmuştu; hiçbir kural henüz devrede değildi. Tesadüfi yerleşimi kanıt sanmak, bu modülün ilk dersi oldu.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 2: Pozitif Resource Affinity, Gerçek Bir Test
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager rules add resource-affinity keep-together &lt;span class="nt"&gt;--affinity&lt;/span&gt; positive &lt;span class="nt"&gt;--resources&lt;/span&gt; vm:100,vm:101
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gerçek kanıt için, kuralı ihlal etmeye çalıştım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm migrate 101 pve-c &lt;span class="nt"&gt;--online&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cannot migrate resource 'vm:101' to node 'pve-c':
- resource 'vm:101' not allowed on target node 'pve-c'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reddedildi. Kural gerçekten çalışıyor; VM 100 &lt;code&gt;pve-a&lt;/code&gt;'ya bağlı olduğu (Modül 3'ten kalma node-affinity kuralı) için, 101'in oradan ayrılmasına izin verilmiyor. Manuel bir taşımayı bile engellemesi, bunun tesadüfi değil aktif bir kısıtlama olduğunu kanıtlıyor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Beklenmedik Bir İsimlendirme Gizemi
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ha-manager rules list&lt;/code&gt; çalıştırdığımda, Modül 3'te &lt;code&gt;prefer-pve-a&lt;/code&gt; adıyla oluşturduğum node-affinity kuralının artık &lt;code&gt;ha-rule-ae59af1e-b08f&lt;/code&gt; gibi bir hash olarak göründüğünü fark ettim. Kendiliğinden değişmemişti; ben bir ara bu kuralı web arayüzünden silip yine web arayüzünden yeniden eklemiştim. Yeni eklediğim &lt;code&gt;keep-together&lt;/code&gt; (CLI'dan) ise ismini koruyordu. &lt;code&gt;/etc/pve/ha/rules.cfg&lt;/code&gt;'ye bakınca ikisi de doğrulandı. Sebebi araştırınca netleşti: GUI'nin "Add" formunda isim alanı yok; boş bırakılınca backend otomatik bir hash üretiyor. Bunu doğrulamak için CLI'dan ayrı bir isim testi yaptım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager rules add node-affinity test-isim-korunuyor-mu &lt;span class="nt"&gt;--resources&lt;/span&gt; vm:102 &lt;span class="nt"&gt;--nodes&lt;/span&gt; pve-c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu isim korundu. Sonuç: &lt;strong&gt;CLI'dan eklenen kurallar verdiğin ismi koruyor, GUI'den eklenenler otomatik hash alıyor.&lt;/strong&gt; Küçük ama gerçek bir GUI/CLI farkı; benim başıma gelme sebebi kuralı GUI üzerinden silip tekrar eklemem oldu, sistemin kendi kendine isim değiştirmesi değil.&lt;/p&gt;

&lt;p&gt;Aynı ekleme işlemi web arayüzünden Datacenter → HA → Affinity Rules → HA Node Affinity Rules altında "Add" butonuyla da yapılabiliyor; iddiamı bu ekranı görünce doğruladım, gerçekten bir isim alanı yok:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsw9udgiys2m6djplyb94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsw9udgiys2m6djplyb94.png" alt="Web UI dan Affinity Rule Ekleme" width="600" height="494"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 3: Negatif Resource Affinity, Proaktiflik ve Geçişli Çıkarım
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager rules add resource-affinity keep-separate &lt;span class="nt"&gt;--affinity&lt;/span&gt; negative &lt;span class="nt"&gt;--resources&lt;/span&gt; vm:100,vm:102
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hiçbir manuel migration yapmadan, &lt;code&gt;ha-manager status&lt;/code&gt;'a tekrar baktığımda VM 102'nin &lt;strong&gt;kendiliğinden&lt;/strong&gt; &lt;code&gt;pve-c&lt;/code&gt;'ye taşınmış olduğunu gördüm; Modül 3'teki proaktif node-affinity bulgusunun resource-affinity için de geçerli olduğu kanıtlandı.&lt;/p&gt;

&lt;p&gt;Sonra kuralı ihlal etmeye çalıştım: VM 102'yi (&lt;code&gt;pve-c&lt;/code&gt;'de), VM 100'ün olduğu &lt;code&gt;pve-a&lt;/code&gt;'ya geri çekmeye:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm migrate 102 pve-a &lt;span class="nt"&gt;--online&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cannot migrate resource 'vm:102' to node 'pve-a':
- resource 'vm:100' on target node 'pve-a' in negative affinity with resource 'vm:102'
- resource 'vm:101' on target node 'pve-a' in negative affinity with resource 'vm:102'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İkinci satır beni şaşırttı: &lt;code&gt;vm:101&lt;/code&gt; için hiç negatif kural tanımlamamıştım. Sistem bunu kendisi türetmiş: &lt;code&gt;vm:101&lt;/code&gt;, &lt;code&gt;vm:100&lt;/code&gt;'le pozitif kuralda (birlikte kalmalı); &lt;code&gt;vm:100&lt;/code&gt; da &lt;code&gt;vm:102&lt;/code&gt;'yle negatif kuralda (asla birlikte olmamalı). Motor bu iki kuralı birleştirip, açıkça yazılmamış üçüncü bir kısıtlamayı &lt;strong&gt;geçişli olarak&lt;/strong&gt; çıkarmış: &lt;code&gt;vm:101&lt;/code&gt; de &lt;code&gt;vm:102&lt;/code&gt;'den uzak durmalı. Kural motoru sadece her kuralı tek tek kontrol etmiyor, aralarında mantıksal çıkarım yapıyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 4: CRS'i Etkinleştirmek, Yanlış Bir İzin Peşinde
&lt;/h2&gt;

&lt;p&gt;Dynamic CRS'i, testi hızlandırmak için agresif ayarlarla açtım: threshold %10, hold-duration 1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0xdx8ywvgtbkig5sz47z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0xdx8ywvgtbkig5sz47z.png" alt="Datacenter → Options → Cluster Resource Scheduling: Dynamic Load, Automatic Rebalance, Threshold %10, Hold Duration 1" width="447" height="329"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;master pve-b (active, ...) - dynamic load CRS (load imbalance: 8.1%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;%8.1 &amp;lt; %10&lt;/code&gt; olduğu için hiçbir şey taşınmadı; beklenen davranış. Eşiği &lt;code&gt;%1&lt;/code&gt;'e düşürdüm, ama yine hiçbir taşıma olmadı, dengesizlik &lt;code&gt;%9.3&lt;/code&gt; göstermesine rağmen. Bunun sebebini GUI'den migration denemesiyle araştırdım:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6kfcy0le1bpnfs0dbx6x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6kfcy0le1bpnfs0dbx6x.png" alt="Migrate VM 102, pve-a hedefi, " width="599" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbdolir7bml9mavg8wo7x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbdolir7bml9mavg8wo7x.png" alt="Migrate VM 102, pve-b hedefi, " width="599" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pve-b&lt;/code&gt; hedefindeki hata daha az detaylıydı, ve gerçek sebep CRS ile hiç ilgili değildi: Uygulama 2'de test amaçlı eklediğim &lt;code&gt;test-isim-korunuyor-mu&lt;/code&gt; kuralı (VM 102'yi &lt;code&gt;pve-c&lt;/code&gt;'ye sabitleyen) hâlâ duruyordu, hiç silmemiştim. Kalıntıyı temizleyince:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager rules remove test-isim-korunuyor-mu
ha-manager migrate vm:102 pve-b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu sefer kabul edildi. Ders: kendi test kalıntılarını temizlemeden bir sonraki testi yorumlamaya çalışmak, yanlış sonuçlara götürebiliyor. "Kesin CRS'in suçu" diye düşünüp geçebilirdim; gerçek sebep çok daha sıradandı.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 5: 12 Dakikalık Salınım
&lt;/h2&gt;

&lt;p&gt;Temiz zeminde CRS'i test etmeye devam ettim. Saatler sonra loglara dönünce, &lt;code&gt;journalctl -u pve-ha-crm&lt;/code&gt; çıktısında beklemediğim bir şey buldum: 21:38 ile 21:50 arası, &lt;strong&gt;tam 12 dakika boyunca&lt;/strong&gt;, VM 102 her ~20 saniyede bir &lt;code&gt;pve-b&lt;/code&gt; ile &lt;code&gt;pve-c&lt;/code&gt; arasında ileri geri taşınmış:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;21:38:27  auto rebalance - migrate vm:102 to pve-c (imbalance from 6.0% to 5.0%)
21:38:47  auto rebalance - migrate vm:102 to pve-b (imbalance from 12.0% to 9.8%)
21:39:07  auto rebalance - migrate vm:102 to pve-c (imbalance from 10.9% to 8.8%)
21:39:27  auto rebalance - migrate vm:102 to pve-b (imbalance from 9.4% to 7.2%)
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Bu desen, yaklaşık 20 saniyede bir tekrarlanarak devam ediyor.)&lt;/p&gt;

&lt;p&gt;Her taşıma "dengesizliği azaltıyor" gibi görünüyor, ama taşıdıktan hemen sonra tekrar dengesiz hale geliyor, sistem tekrar taşıyor; klasik bir salınım. Sebebi büyük ihtimalle bizim test için verdiğimiz aşırı agresif ayarlar (&lt;code&gt;threshold=1%&lt;/code&gt;, &lt;code&gt;hold-duration=1&lt;/code&gt;). Gerçek bir ortamda bu ayarlarla çalışmak, sürekli gereksiz migration'a ve VM'ler için mikro kesintilere yol açardı. Bu, dynamic CRS'in varsayılan ayarlarının (yaklaşık %30 eşik, daha uzun hold-duration) neden öyle seçildiğini somut olarak gösterdi.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 6: Gerçek Bir Arıza
&lt;/h2&gt;

&lt;p&gt;Salınım 21:50'de durmuş, ama ondan 9 saat sonra, sabaha karşı, gerçek bir arıza oluşmuş:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;07:46:58  service 'vm:102' - migration failed (exit code 255)
07:47:18  starting service vm:102 on node 'pve-b' failed, relocating service.
07:47:28  service 'vm:102' - migration failed (exit code 255)
07:47:48  recovery policy for service vm:102 failed, entering error state. Failed nodes: pve-b, pve-b
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;VM &lt;code&gt;error&lt;/code&gt; state'ine düşmüştü. &lt;code&gt;qm status 102&lt;/code&gt;, &lt;code&gt;pve-b&lt;/code&gt;'de "stopped", &lt;code&gt;pve-c&lt;/code&gt;'de "config dosyası yok" diyordu; disk ve NFS bağlantısı sağlıklıydı. Asıl ipucu &lt;code&gt;/var/lock/qemu-server/lock-102.conf&lt;/code&gt; dosyasıydı; başarısız migration'ın arkasında bıraktığı bir kilit.&lt;/p&gt;

&lt;p&gt;Proxmox'un resmi hata kurtarma prosedürünü uyguladım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager &lt;span class="nb"&gt;set &lt;/span&gt;vm:102 &lt;span class="nt"&gt;--state&lt;/span&gt; disabled
qm unlock 102
qm start 102
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu üçüncü komut başarılı oldu; VM gerçekten çalışmaya başladı (&lt;code&gt;VM 102 started with PID 3490&lt;/code&gt;). Kilit, sorunun gerçek sebebiydi.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kendi Verdiğim Yanlış Bir Talimat
&lt;/h3&gt;

&lt;p&gt;Buradan sonra "temiz bir devir" için VM'i durdurup HA'ya tekrar devretmeyi önermiştim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm stop 102
ha-manager &lt;span class="nb"&gt;set &lt;/span&gt;vm:102 &lt;span class="nt"&gt;--state&lt;/span&gt; started
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;VM birkaç saniye sonra durmuş görününce bir an paniğe kapıldım: "neden durdu?" Log'a bakınca gerçek sebep ortaya çıktı: VM 07:57:42'de başarıyla başlamıştı, ama benim önerdiğim &lt;code&gt;qm stop&lt;/code&gt; komutu tam 9 saniye sonra (07:57:51) onu durdurmuştu. Gizemli bir sistem davranışı değildi; benim gereksiz bir talimatımdı. Asıl düzeltme (&lt;code&gt;qm unlock&lt;/code&gt;) tek başına yeterliydi.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 7: İkinci Bir Salınım, Ve Kalıcı Çözüm
&lt;/h2&gt;

&lt;p&gt;VM'i tekrar &lt;code&gt;--state started&lt;/code&gt; yapınca, HA onu &lt;code&gt;pve-b&lt;/code&gt;'de başlattı, ama 20 saniye sonra CRS onu tekrar &lt;code&gt;pve-c&lt;/code&gt;'ye taşıdı, 30 saniye sonra &lt;code&gt;pve-b&lt;/code&gt;'ye geri getirdi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;08:02:59  service 'vm:102': state changed to 'started' (node = pve-b)
08:03:19  auto rebalance - migrate vm:102 to pve-c (imbalance from 18.4% to 15.4%)
08:03:49  auto rebalance - migrate vm:102 to pve-b (imbalance from 7.0% to 6.3%)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uygulama 5'teki salınım hâlâ aktifti; recovery'nin üstüne binmişti. &lt;code&gt;ha-auto-rebalance&lt;/code&gt;'ı hiç kapatmamıştık. Kapattım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/ha-auto-rebalance=1/ha-auto-rebalance=0/'&lt;/span&gt; /etc/pve/datacenter.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sed&lt;/code&gt; ile metin üzerinde arama-değiştirme yapmak, teknik olarak çalışsa da bir risk taşıyor: yanlış bir eşleşme ya da beklenmedik bir format farkı, dosyayı sessizce bozabilir. Aynı değişikliği web arayüzünden yapmak daha güvenli; &lt;code&gt;Datacenter → Options → Cluster Resource Scheduling&lt;/code&gt; ekranına dönüp "Automatic Rebalance" kutusunu kaldırmak yeterli:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9dww18hdmhm05d5ptt79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9dww18hdmhm05d5ptt79.png" alt="Cluster Resource Scheduling - Automatic Rebalance kapatma" width="451" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Birkaç dakika sonra:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;master pve-c (active, ...) - dynamic load CRS
service vm:102 (pve-c, started)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;VM artık sabit. Küçük bir bonus gözlem: &lt;code&gt;master&lt;/code&gt; satırındaki &lt;code&gt;(load imbalance: X%)&lt;/code&gt; yüzdesi de kaybolmuş; muhtemelen bu hesaplama sadece auto-rebalance aktifken yapılıp gösteriliyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Genel Değerlendirme
&lt;/h2&gt;

&lt;p&gt;Bu modül, planladığımdan tamamen farklı bir yere gitti, ve sanırım bu yüzden serinin en zengin modüllerinden biri oldu.&lt;/p&gt;

&lt;p&gt;Birincisi, resource affinity kurallarının geçişli çıkarım yapması. İki kuralı ayrı ayrı yazdım (100-101 pozitif, 100-102 negatif), ama sistem bunları birleştirip yazmadığım üçüncü bir kısıtlamayı (101-102 negatif) kendisi türetti. Bu, kural motorunun basit bir liste kontrolünden çok daha fazlası olduğunu gösterdi.&lt;/p&gt;

&lt;p&gt;İkincisi, agresif CRS ayarlarının gerçek bir tehlikeyi somutlaştırması. 12 dakikalık salınımı görmeden önce "düşük eşik = daha duyarlı sistem" gibi soyut bir fikrim vardı. Gördükten sonra bunun "sürekli gereksiz migration" anlamına geldiğini anladım; varsayılan ayarların neden temkinli seçildiğini artık deneyimledim.&lt;/p&gt;

&lt;p&gt;Üçüncüsü, ve belki en önemlisi: kendi verdiğim bir talimatın (gereksiz &lt;code&gt;qm stop&lt;/code&gt;) beni yanlış bir teşhise sürüklemesi. Bir rehber, bir dokümantasyon, hatta bir AI asistanı bile, doğrulanmamış bir öneri verdiğinde yanlış ize sürükleyebiliyor. Log'a dönüp gerçek zaman damgalarını karşılaştırmak, "sistem tuhaf davranıyor" sanısını "ben tuhaf bir şey yaptım" gerçeğine çevirdi. Bu ders, teknik bir bulgudan çok, bir çalışma disiplini: iddia etmeden önce logu aç.&lt;/p&gt;

&lt;p&gt;Bir sonraki modülde seriyi toparlayacağım: bu dört modülde öğrendiklerimi (Corosync temelleri, quorum, QDevice, HA Manager, resource affinity, CRS) bir araya getiren bir kapanış, ve muhtemelen bir sonraki öğrenme hedefine (Ceph ya da başka bir konu) işaret eden bir bakış.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bu seri, resmi Proxmox VE dokümantasyonundan (pve.proxmox.com) derleniyor. Buradaki gözlemler bir homelab ortamına dayanıyor; production kararları için resmi dokümantasyon ve deneyimli sistem yöneticileri referans alınmalıdır.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title># HA Manager: Watchdog, Fencing ve Node Affinity (Modül 3)</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Mon, 17 Aug 2026 05:59:07 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/-ha-manager-watchdog-fencing-ve-node-affinity-modul-3-3fng</link>
      <guid>https://dev.to/hakanbaban53/-ha-manager-watchdog-fencing-ve-node-affinity-modul-3-3fng</guid>
      <description>&lt;p&gt;&lt;strong&gt;Seri:&lt;/strong&gt; Proxmox VE Cluster ve Corosync&lt;/p&gt;




&lt;p&gt;Modül 1'de bir node çökünce VM'in kendiliğinden hiçbir yerde açılmadığını göstermiştim; cluster kurmak otomatik yüksek erişilebilirlik demek değildi. Bu modülde o boşluğu dolduran parçaya geçiyorum: HA Manager. Watchdog tabanlı fencing'i, gerçek bir node çökmesinde recovery'nin saniye saniye nasıl işlediğini, ve bir node affinity kuralının beklediğimden çok daha proaktif davrandığını, hepsini gerçekten test ederek öğrendim.&lt;/p&gt;

&lt;p&gt;Bu modülde her adımı hem terminalden hem web arayüzünden gösteriyorum; ikisi aynı API'yi kullanıyor, ama bazen birini görmek diğerini daha iyi anlamana yardımcı oluyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  HA Manager Nedir?
&lt;/h2&gt;

&lt;p&gt;Proxmox VE'nin HA stack'i, Pacemaker gibi harici bir araç gerektirmiyor; kendi başına çalışan bir çözüm. İki daemon var: &lt;strong&gt;pve-ha-lrm&lt;/strong&gt; (Local Resource Manager), her node'da çalışıp o node'daki servisleri kontrol ediyor; &lt;strong&gt;pve-ha-crm&lt;/strong&gt; (Cluster Resource Manager), cluster genelinde kararlar alıyor, sadece bir node'da aktif "master" olabiliyor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fencing&lt;/strong&gt;, bir node arızalandığında onun gerçekten "offline" olduğundan emin olma süreci. Bu kritik, çünkü fence edilmemiş bir node hâlâ shared storage'a erişebiliyor olabilir; aynı VM'i başka bir node'da başlatmak, iki node'un aynı diske aynı anda yazmasına yol açabilir. Proxmox, harici fencing donanımı gerektirmeyen bir yöntem kullanıyor: &lt;strong&gt;watchdog timer tabanlı self-fencing&lt;/strong&gt;. &lt;code&gt;ha-manager&lt;/code&gt;, watchdog timer'ı düzenli olarak resetliyor; bir arıza nedeniyle bu reset gerçekleşmezse, timer node'u otomatik olarak reboot ediyor.&lt;/p&gt;

&lt;p&gt;Burada altta yatan tasarım felsefesi ilginç: node'un "belki çalışıyordur, belki bozuktur" gibi belirsiz bir durumda kalmasına izin verilmiyor, doğrudan tam bir reboot'a zorlanıyor. Mantığı şu: bir node yarım yamalak, tutarsız bir durumda çalışmaya devam ederse (örneğin ağı kopmuş ama diski hâlâ yazabiliyorsa), bu durum veri bozulmasına yol açabilir; oysa node tamamen durursa, en azından zarar vermeye devam etmiyor. Yanlış çalışmaya devam etmektense hiç çalışmamak, burada bilinçli bir güvenlik tercihi.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 1: Watchdog'u Doğrulamak
&lt;/h2&gt;

&lt;p&gt;Nested lab'ımda donanım watchdog yok, Proxmox varsayılan olarak &lt;code&gt;softdog&lt;/code&gt; kernel modülüne düşüyor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/default/pve-ha-manager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# select watchdog module (default is softdog)
#WATCHDOG_MODULE=ipmi_watchdog
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Satır yorumlu, yani varsayılan (&lt;code&gt;softdog&lt;/code&gt;) kullanılıyor. Doğrulamak için ilk denediğim komut boş döndü:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsmod | &lt;span class="nb"&gt;grep &lt;/span&gt;watchdog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(boş çıktı)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bir an modülün yüklü olmadığını düşündüm. Ama hata bendeydi: "softdog" kelimesi harfi harfine "watchdog" içermiyor. Doğrusu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsmod | &lt;span class="nb"&gt;grep &lt;/span&gt;dog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;softdog                12288  2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modül yüklü. &lt;code&gt;/dev/watchdog&lt;/code&gt; ve &lt;code&gt;/dev/watchdog0&lt;/code&gt; cihazları da mevcuttu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /dev/watchdog&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;crw------- 1 root root  10, 130 Aug 15 07:41 /dev/watchdog
crw------- 1 root root 243,   0 Aug 15 07:41 /dev/watchdog0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Uygulama 2: VM'i HA'ya Eklemek
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager add vm:100 &lt;span class="nt"&gt;--state&lt;/span&gt; started
ha-manager status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;quorum OK
master pve-a (active, ...)
fencing armed (CRM watchdog active)
lrm pve-a (active, watchdog active, ...)
lrm pve-b (idle, watchdog standby, ...)
lrm pve-c (idle, watchdog standby, ...)
service vm:100 (pve-a, started)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aynı işlem web arayüzünden de yapılabiliyor: &lt;code&gt;Datacenter → HA → Resources&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwhczpptwnowhb95w27sn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwhczpptwnowhb95w27sn.png" alt="Datacenter → HA → Resources ekranı, boş kaynak listesi" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Add" butonuna basıp VM'i ve başlangıç durumunu (&lt;code&gt;started&lt;/code&gt;) seçiyorsun:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxdghrlgru3ielpprdl0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxdghrlgru3ielpprdl0m.png" alt="Add HA Resource diyaloğu - VM 100 ve state seçimi" width="599" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dikkat çeken bir detay: &lt;code&gt;pve-a&lt;/code&gt; hem VM'i barındırıyor hem de CRM master'ı. Bu, sıradaki testi daha ilginç kılıyor; &lt;code&gt;pve-a&lt;/code&gt;'yı çökertince sadece VM'in taşınıp taşınmayacağını değil, cluster'ın yeni bir master seçip seçemeyeceğini de gözlemleyeceğim.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 3: Gerçek Bir Çökme, Saniye Saniye
&lt;/h2&gt;

&lt;p&gt;Host'tan ping'i başlattım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping 192.168.122.251
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5nvdcyorvxynjaxshda2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5nvdcyorvxynjaxshda2.png" alt="Ping başlatma - terminal görüntüsü" width="641" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sonra &lt;code&gt;pve-a&lt;/code&gt;'yı fiziksel host'tan çökerttim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh destroy pve-a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ping çıktısı Modül 1/2'deki tanıdık deseni tekrarladı: önce sessiz paket kaybı, sonra gateway'in aktif "Destination Host Unreachable" demesi.&lt;/p&gt;

&lt;p&gt;İlk kontrolüm (çökmeden ~10 saniye sonra), &lt;code&gt;pve-b&lt;/code&gt;'den:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;quorum OK
master pve-c (active, ...)
fencing armed (CRM watchdog active)
lrm pve-a (active, watchdog active, ...)
lrm pve-b (idle, watchdog standby, ...)
lrm pve-c (idle, watchdog standby, ...)
service vm:100 (pve-a, started)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeni master seçimi hızlı gerçekleşmişti (&lt;code&gt;master pve-c&lt;/code&gt; olmuştu), ama VM henüz taşınmamıştı; bu, sürecin ortasında bir "anlık fotoğraf". Birkaç dakika sonra aynı komutu tekrar çalıştırdım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;quorum OK
master pve-c (active, ...)
fencing armed (CRM watchdog active)
lrm pve-a (old timestamp - dead?, watchdog standby, ...)
lrm pve-b (active, watchdog active, ...)
lrm pve-c (idle, watchdog standby, ...)
service vm:100 (pve-b, started)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9fe627x7bmb9r5usln5a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9fe627x7bmb9r5usln5a.png" alt="HA Status ekranı - vm:100 pve-b'de started, lrm pve-a " width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VM gerçekten &lt;code&gt;pve-b&lt;/code&gt;'ye taşınmış. &lt;code&gt;lrm pve-a&lt;/code&gt;'nın durumu da ilginçti: sistem bile kesin konuşmuyor, "old timestamp - dead?" diye soru işaretiyle yazıyor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tam Zaman Çizelgesi
&lt;/h3&gt;

&lt;p&gt;Tahmin etmek yerine &lt;code&gt;pve-c&lt;/code&gt;'nin (master) CRM loglarına baktım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; pve-ha-crm &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"19:46:00"&lt;/span&gt; &lt;span class="nt"&gt;--until&lt;/span&gt; &lt;span class="s2"&gt;"19:50:00"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;19:46:53  node 'pve-a': state changed from 'online' =&amp;gt; 'unknown'
19:47:43  service 'vm:100': state changed from 'started' to 'fence'
19:47:43  node 'pve-a': state changed from 'unknown' =&amp;gt; 'fence'
19:48:43  successfully acquired lock 'ha_agent_pve-a_lock'
19:48:43  fencing: acknowledged - got agent lock for node 'pve-a'
19:48:43  service 'vm:100': state changed from 'fence' to 'recovery'
19:48:43  recover service 'vm:100' from fenced node 'pve-a' to node 'pve-b'
19:48:43  service 'vm:100': state changed from 'recovery' to 'started'  (node = pve-b)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pve-a&lt;/code&gt;'yı çökerttiğim an ~19:46:33'tü. Üç net faz ortaya çıktı:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Algılama (~20 saniye):&lt;/strong&gt; çökme → &lt;code&gt;online =&amp;gt; unknown&lt;/code&gt; (19:46:53). Corosync'in kaybı fark edip CRM'e bildirmesi.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fence kararı (tam 50 saniye):&lt;/strong&gt; &lt;code&gt;unknown&lt;/code&gt; → &lt;code&gt;fence&lt;/code&gt; (19:47:43). Muhtemelen ani bir yanlış pozitiften kaçınmak için bekletilen bir politika süresi.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watchdog güvenlik beklemesi (tam 60 saniye):&lt;/strong&gt; &lt;code&gt;fence&lt;/code&gt; → lock alındı, VM &lt;code&gt;pve-b&lt;/code&gt;'de başladı (19:48:43). Bu rakam tesadüf değil; watchdog'un kendi kendini resetleyeceği tam süreyi (tipik 60 saniye) bekliyor, "belki node hâlâ hayatta ve VM'i çalıştırıyordur" ihtimalini sıfırlıyor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Toplam: ~130 saniye&lt;/strong&gt; (&lt;code&gt;virsh destroy&lt;/code&gt;'dan VM'in yeni node'da çalışmasına kadar).&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 4: Double-Start'ın Yapısal Olarak İmkansız Olduğunu Kanıtlamak
&lt;/h2&gt;

&lt;p&gt;En kritik soru: &lt;code&gt;pve-a&lt;/code&gt; geri gelince VM'i ikinci kez başlatmaya çalışacak mı? İki node'un aynı diske aynı anda yazması gerçek bir felaket senaryosu olurdu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh start pve-a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boot tamamlandıktan sonra, &lt;code&gt;pve-a&lt;/code&gt;'da:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm status 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Configuration file 'nodes/pve-a/qemu-server/100.conf' does not exist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu, Modül 1'de gördüğüm birebir aynı hata mesajı, ama tamamen farklı bir anlamda. Modül 1'de bu, "kimse bu VM'i başlatmayacak" demekti. Burada ise: fencing + recovery süreci, VM'in config dosyasını pmxcfs üzerinden &lt;strong&gt;gerçekten &lt;code&gt;pve-b&lt;/code&gt;'nin dizinine taşımış&lt;/strong&gt;. &lt;code&gt;pve-a&lt;/code&gt;'da böyle bir dosya artık hiç yok. Bu, double-start'ı sadece "önlenmiş" değil, yapısal olarak &lt;strong&gt;imkansız&lt;/strong&gt; kılıyor: &lt;code&gt;pve-a&lt;/code&gt; geri gelse bile, elinde VM'i başlatacak bir config bile bulamıyor.&lt;/p&gt;

&lt;p&gt;Bunu iki node'dan bağımsız olarak doğruladım. &lt;code&gt;pve-b&lt;/code&gt;'de:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm status
ha-manager status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes:            3
Quorate:          Yes
Total votes:      5
Quorum:           3
Flags:            Quorate Qdevice
...
quorum OK
master pve-c (active, ...)
fencing armed (CRM watchdog active)
lrm pve-a (idle, watchdog standby, ...)
lrm pve-b (active, watchdog active, ...)
lrm pve-c (idle, watchdog standby, ...)
service vm:100 (pve-b, started)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pve-a&lt;/code&gt;'da, aynı anda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm status
ha-manager status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes:            3
Quorate:          Yes
Total votes:      5
Quorum:           3
Flags:            Quorate Qdevice
...
quorum OK
master pve-c (active, ...)
fencing armed (CRM watchdog active)
lrm pve-a (idle, watchdog standby, ...)
lrm pve-b (active, watchdog active, ...)
lrm pve-c (idle, watchdog standby, ...)
service vm:100 (pve-b, started)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Birebir tutarlı: 5 oy, 3 node, &lt;code&gt;vm:100 (pve-b, started)&lt;/code&gt;, fencing armed. &lt;code&gt;lrm pve-a&lt;/code&gt;, hiçbir manuel müdahale gerekmeden, kendiliğinden "old timestamp - dead?" durumundan "idle"a döndü.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 5: Node Affinity, Beklediğimden Daha Proaktif
&lt;/h2&gt;

&lt;p&gt;Her şey sağlıklıyken, VM'in tercihen &lt;code&gt;pve-a&lt;/code&gt;'da kalmasını söyleyen bir kural tanımladım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager rules add node-affinity prefer-pve-a &lt;span class="nt"&gt;--resources&lt;/span&gt; vm:100 &lt;span class="nt"&gt;--nodes&lt;/span&gt; pve-a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aynı işlem web arayüzünden: &lt;code&gt;Datacenter → HA → Rules&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fulv3bjhimy5k6u23opl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fulv3bjhimy5k6u23opl9.png" alt="Datacenter → HA → Rules ekranı" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Add" → "Node Affinity Rule" diyip kaynağı ve tercih edilen node'u seçiyorsun:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhjabul0g2kl0o7wup8ht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhjabul0g2kl0o7wup8ht.png" alt="Add Node Affinity Rule diyaloğu - vm:100, pve-a" width="598" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Beklentim şuydu: bu kural sadece bir sonraki arıza/recovery anında devreye girer, halihazırda sağlıklı çalışan bir servisi kendiliğinden taşımaz. Yanılmışım.&lt;/p&gt;

&lt;p&gt;Kuralı ekledikten saniyeler sonra, tekrar baktığımda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service vm:100 (pve-b, migrate)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0xpf0zurvfdo19crnj7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0xpf0zurvfdo19crnj7j.png" alt="HA Status - vm:100 migrate durumunda" width="796" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VM zaten taşınmaya başlamıştı; hem de &lt;code&gt;fence&lt;/code&gt; ya da &lt;code&gt;relocate&lt;/code&gt; değil, &lt;strong&gt;&lt;code&gt;migrate&lt;/code&gt;&lt;/strong&gt; durumunda; yani VM durdurulup yeniden başlatılmadı, gerçek bir canlı migration tetiklendi. ~25-30 saniye sonra, tekrar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ha-manager status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service vm:100 (pve-a, started)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fagkajtw8ogryf1cp2qm6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fagkajtw8ogryf1cp2qm6.png" alt="HA Status - vm:100 pve-a'da started" width="796" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tamamlanmıştı. Hiçbir arıza olmadan, ben sadece bir tercih tanımladığım için, HA Manager kendiliğinden mevcut durumu istenen duruma yakınsatmış. Bu, HA Manager'ın sadece &lt;strong&gt;reaktif&lt;/strong&gt; (arızaya tepki veren) değil, aynı zamanda &lt;strong&gt;proaktif&lt;/strong&gt; (deklare edilen tercihe kendiliğinden yaklaşan) bir sistem olduğunu gösterdi; bunu önceden bilmiyordum, test etmeden öğrenemezdim.&lt;/p&gt;

&lt;p&gt;Son doğrulama:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm status 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;running&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm status
ha-manager status
ha-manager rules list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes:            3
Quorate:          Yes
Total votes:      5
Quorum:           3
...
service vm:100 (pve-a, started)
...
┌──────────────┐
│ rule         │
╞══════════════╡
│ prefer-pve-a │
└──────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tamamen tutarlı: VM çalışıyor, cluster quorate, kural hâlâ kayıtlı.&lt;/p&gt;




&lt;h2&gt;
  
  
  Genel Değerlendirme
&lt;/h2&gt;

&lt;p&gt;Bu modülde üç şey beni özellikle şaşırttı.&lt;/p&gt;

&lt;p&gt;Birincisi, fencing'in üç net fazlı, toplamda ~130 saniyelik bir süreç olması. "HA var, arıza olunca hemen düzelir" gibi belirsiz bir beklentim vardı; gerçekte sistem, her biri ayrı bir amaca hizmet eden (algılama, yanlış pozitiften kaçınma, watchdog güvenlik payı) üç ayrı bekleme süresinden geçiyor. Bu, hız değil, güvenlik önceliğiyle tasarlanmış bir sistem.&lt;/p&gt;

&lt;p&gt;İkincisi, &lt;code&gt;pve-a&lt;/code&gt;'nın config dosyasının gerçekten &lt;code&gt;pve-b&lt;/code&gt;'ye taşınmış olması. Double-start korumasını "kilitli bir mekanizma" gibi hayal etmiştim; gerçekte çok daha basit ve zarif: VM'i başlatacak bilgi artık orada yok, o yüzden başlatılamıyor.&lt;/p&gt;

&lt;p&gt;Üçüncüsü, node affinity kuralının proaktif davranışı. Bu, testin planımı tersine çevirdiği bir andı; "muhtemelen böyle çalışır" diye düşünüp geçebilirdim, ama gerçekten test edince tam tersini gördüm. Modül 2'de de benzer bir şey olmuştu (split-brain'i tetiklemeye çalışıp başaramamak); bu serinin tekrar eden bir teması gibi görünüyor: varsayımlarım, gerçek testlerden daha az güvenilir çıkıyor.&lt;/p&gt;

&lt;p&gt;Bir sonraki modülde, HA Manager'ı biraz daha zorlayacağım: birden fazla VM ile resource affinity kurallarını (aynı node'da tutma/ayrı tutma), ve CRS'in (Cluster Resource Scheduler) yük dengeleme davranışını test edeceğim.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bu seri, resmi Proxmox VE dokümantasyonundan (pve.proxmox.com) derleniyor. Buradaki gözlemler bir homelab ortamına dayanıyor; production kararları için resmi dokümantasyon ve deneyimli sistem yöneticileri referans alınmalıdır.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Quorum Kaybını Tetiklemek, QDevice Kurmak ve Shared Storage'a Geçmek (Modül 2)</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Fri, 14 Aug 2026 07:03:26 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/quorum-kaybini-tetiklemek-qdevice-kurmak-ve-shared-storagea-gecmek-modul-2-pma</link>
      <guid>https://dev.to/hakanbaban53/quorum-kaybini-tetiklemek-qdevice-kurmak-ve-shared-storagea-gecmek-modul-2-pma</guid>
      <description>&lt;p&gt;&lt;strong&gt;Seri:&lt;/strong&gt; Proxmox VE Cluster ve Corosync&lt;/p&gt;




&lt;p&gt;Modül 1'de cluster'ı kurdum, canlı migration'ın çalıştığını kanıtladım, ve bir node çökünce VM'in kendiliğinden başka bir yerde açılmadığını gösterdim. Bu modülde asıl arkasındaki mekanizmaya iniyorum: quorum. Ne zaman kaybediliyor, pmxcfs'i nasıl etkiliyor, QDevice bu denklemi nasıl değiştiriyor, ve "quorum'u zorla" demenin (&lt;code&gt;pvecm expected&lt;/code&gt;) gerçekten ne kadar tehlikeli olduğunu, kendim tetiklemeye çalışarak öğrendim. Sona da, Modül 1'de yarım kalan bir şeyi tamamladım: gerçek bir shared storage kurup, migration'ın &lt;code&gt;--with-local-disks&lt;/code&gt; olmadan nasıl çalıştığını gösterdim.&lt;/p&gt;

&lt;p&gt;Bu modülde bir hipotezimin yanlış çıktığını da saklamayacağım; yanlış çıkması, doğru sonuçtan daha fazla şey öğretti.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quorum Nedir, Neden Var?
&lt;/h2&gt;

&lt;p&gt;Quorum, dağıtık bir sistemde bir işlemi (özellikle konfigürasyon değişikliği) gerçekleştirmek için gereken minimum oy sayısı. Proxmox VE'de her node varsayılan olarak bir oy taşıyor. Formül basit: &lt;code&gt;quorum = floor(toplam_oy / 2) + 1&lt;/code&gt;. Ağ bölünmesi (partition) olduğunda, sadece çoğunluğa sahip taraf işlemeye devam edebiliyor; bu, split-brain'i (iki tarafın da "ben doğruyum" deyip aynı kaynağı bağımsız yönetmeye çalışmasını) önlüyor.&lt;/p&gt;

&lt;p&gt;3 node'lu cluster'ımızda quorum eşiği 2 (3'ün çoğunluğu). Bir node izole olursa, kalan iki node çoğunluğu oluşturduğu için çalışmaya devam ediyor; izole olan tek node ise azınlıkta kalıp durabiliyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 1: Quorum Kaybını Gerçekten Tetiklemek
&lt;/h2&gt;

&lt;p&gt;Bunu Modül 1'deki node çökertme testinden farklı yapmak istedim: orada tüm node'u (&lt;code&gt;virsh destroy&lt;/code&gt;) öldürmüştüm, burada sadece Corosync ağını kesip node'u canlı tutuyorum; management ağı hâlâ açık, SSH ile erişebiliyorum.&lt;/p&gt;

&lt;p&gt;Fiziksel host'tan &lt;code&gt;pve-c&lt;/code&gt;'nin corosync-net arayüzünü düşürdüm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh domif-setlink pve-c vnet15 down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pve-c&lt;/code&gt;'de:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes:            1
Quorate:          No
Total votes:      1
Quorum:           3 Activity blocked
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beklendiği gibi: tek başına kalan &lt;code&gt;pve-c&lt;/code&gt;, quorum eşiğini (3, bu noktada QDevice henüz yoktu) karşılayamıyor.&lt;/p&gt;

&lt;p&gt;pmxcfs'e yazmayı denedim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; /etc/pve/test-readonly.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;touch: cannot touch '/etc/pve/test-readonly.txt': Permission denied
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modül 1'de gördüğüm "read-only file system" davranışının bir başka görünümü; burada hata mesajı farklı (&lt;code&gt;Permission denied&lt;/code&gt; vs &lt;code&gt;Read-only file system&lt;/code&gt;) ama sonuç aynı: quorum'suz bir node, konfigürasyona yazamıyor.&lt;/p&gt;

&lt;p&gt;Çoğunluk tarafında (&lt;code&gt;pve-a&lt;/code&gt;, iki node) durum farklıydı: &lt;code&gt;Quorate: Yes&lt;/code&gt; olarak kaldı, hiç kesinti yaşamadı. Bu, quorum mekanizmasının tam olarak tasarlandığı gibi çalıştığının kanıtı: azınlık durur, çoğunluk devam eder.&lt;/p&gt;

&lt;p&gt;Ağı geri açtım (&lt;code&gt;up&lt;/code&gt;), birkaç saniye içinde cluster kendiliğinden 3/3 quorate'e döndü.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 2: QDevice'i Devreye Almak
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;qdevice-1&lt;/code&gt; VM'ine Modül 0'da zaten &lt;code&gt;corosync-qnetd&lt;/code&gt; paketini kurmuştum, ama hiç yapılandırmamıştım. Şimdi gerçekten devreye alma zamanı.&lt;/p&gt;

&lt;h3&gt;
  
  
  Beklenmedik Uyarı: Odd Node Count
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm qdevice setup 192.168.122.20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clusters with an odd node count are not officially supported!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu beni şaşırttı. Araştırınca öğrendim: &lt;code&gt;pvecm&lt;/code&gt;, node sayısı tek olduğunda varsayılan olarak duruyor. Sebep, QDevice'in iki farklı algoritması var: çift node sayısı için varsayılan &lt;code&gt;ffsplit&lt;/code&gt; (her node'a eşit davranıp QDevice'e her zaman tam 1 oy veren), tek node sayısı için ise &lt;code&gt;--force&lt;/code&gt; ile devreye giren &lt;code&gt;lms&lt;/code&gt; (Last Man Standing). 3 node'lu cluster'ım tek sayı olduğu için bu uyarıyı tetikledi.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm qdevice setup 192.168.122.20 &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu sefer kurulum tamamlandı: sertifikalar üç node'a da dağıtıldı, &lt;code&gt;corosync-qdevice.service&lt;/code&gt; her node'da otomatik enable + start edildi.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;lms&lt;/code&gt; Algoritmasının Etkisini Doğrulamak
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected votes:   5
Total votes:      5
Quorum:           3
Membership information
    Nodeid      Votes    Qdevice Name
0x00000001          1    A,V,NMW 10.10.10.11
0x00000002          1    A,V,NMW 10.10.10.12
0x00000003          1    A,V,NMW 10.10.10.13 (local)
0x00000000          2            Qdevice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Burada dikkatimi çeken şey: Qdevice satırında &lt;strong&gt;2 oy&lt;/strong&gt; var, 1 değil. &lt;code&gt;ffsplit&lt;/code&gt;'te QDevice her zaman 1 oy verirken, &lt;code&gt;lms&lt;/code&gt;'de &lt;strong&gt;N-1 oy&lt;/strong&gt; veriyor (N = node sayısı). Bizim durumumuzda 3-1=2. &lt;code&gt;corosync.conf&lt;/code&gt;'a bakınca bunu doğruladım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hocon"&gt;&lt;code&gt;&lt;span class="nl"&gt;quorum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;device&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;net&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;lms&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;192.168.122.20&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;tls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;on&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Toplam matematik: 3 node × 1 + QDevice × 2 = &lt;strong&gt;5 oy&lt;/strong&gt;, quorum eşiği &lt;strong&gt;3&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 3: QDevice'in Değerini Kanıtlamak
&lt;/h2&gt;

&lt;p&gt;Modül 1'de, tek bir node çoğunluğu asla oluşturamıyordu. QDevice'in asıl var oluş sebebi, bunu belirli senaryolarda değiştirmek. Test ettim: iki node'u &lt;strong&gt;aynı anda&lt;/strong&gt; çökerttim.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh destroy pve-b
&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh destroy pve-c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tek kalan &lt;code&gt;pve-a&lt;/code&gt;'da:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes:            1
Total votes:      3
Quorum:           3
Quorate:          Yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Matematik: &lt;code&gt;pve-a&lt;/code&gt; (1 oy) + QDevice (2 oy) = 3, eşik de 3. &lt;strong&gt;Quorate: Yes.&lt;/strong&gt; QDevice olmadan bu imkansız olurdu; tek bir node kendi başına asla çoğunluk oluşturamaz.&lt;/p&gt;

&lt;p&gt;Kanıtı tamamlamak için:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; /etc/pve/qdevice-test.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Başarılı oldu; dosya &lt;code&gt;/etc/pve&lt;/code&gt; altında göründü. Modül 1'deki "tek node = read-only" bulgusunun tam tersi; burada tek node ama QDevice sayesinde quorate, pmxcfs yazılabilir.&lt;/p&gt;

&lt;p&gt;İki node'u da geri getirdim, birkaç dakika içinde cluster tam olarak (5 oy, 3 node) toparlandı.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 4: Split-Brain'i Tetiklemeye Çalışmak
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pvecm expected&lt;/code&gt; komutu, bir node'u zorla "quorate" göstermenin bir yolu; teoride tehlikeli, çünkü izole bir node kendini haklı sanıp bağımsız kararlar almaya başlayabilir. Bunu bilerek tetiklemeye çalıştım.&lt;/p&gt;

&lt;h3&gt;
  
  
  İlk Deneme: Yanlış Anlaşılan Bir Hata
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pve-c&lt;/code&gt;'yi izole ettim, ama &lt;code&gt;down&lt;/code&gt; komutundan hemen sonra yanlışlıkla &lt;code&gt;up&lt;/code&gt;'ı da çalıştırdım; izolasyon çok kısa sürdü. &lt;code&gt;pve-c&lt;/code&gt;'de:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm expected 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hiçbir hata almadım, ama sonraki denemede (hem &lt;code&gt;pve-c&lt;/code&gt;'de hem &lt;code&gt;pve-a&lt;/code&gt;'da):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unable to set expected votes: CS_ERR_INVALID_PARAM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İlk başta bunun QDevice'e özgü bir kısıtlama olduğunu düşündüm. Yanılmışım. Gerçek sebep daha basit: &lt;code&gt;votequorum_setexpected&lt;/code&gt; API'si, &lt;strong&gt;sadece zaten quorum'suz olan bir partition'ı kurtarmak için&lt;/strong&gt; çalışıyor; zaten quorate olan bir partition'ın oy eşiğini manuel düşürmene izin vermiyor. Bizim denemelerimiz, node zaten (kısa izolasyondan sonra ya da QDevice'in sağladığı fazladan oyla) quorate durumdayken yapıldığı için reddedildi. Bu, QDevice'in özel bir davranışı değil, corosync'in her zaman var olan bir güvenlik kuralı.&lt;/p&gt;

&lt;h3&gt;
  
  
  İkinci Deneme: Doğru İzolasyon
&lt;/h3&gt;

&lt;p&gt;Bu sefer &lt;code&gt;pve-c&lt;/code&gt;'yi gerçekten izole tuttum, geri açmadım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh domif-setlink pve-c vnet15 down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pve-c&lt;/code&gt;'de doğruladım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes:            1
Quorate:          No
Total votes:      1
Quorum:           3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gerçekten quorum'suz. Şimdi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm expected 1
pvecm status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beklenmedik bir şey oldu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected votes:   3
Quorum:           2
Quorate:          No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;1 istedim, sistem 3 verdi.&lt;/strong&gt; Ve &lt;code&gt;pve-c&lt;/code&gt; (1 oy) bu yeni eşiği (2) bile karşılayamadığı için &lt;code&gt;Quorate: No&lt;/code&gt; kaldı, &lt;code&gt;touch&lt;/code&gt; yine &lt;code&gt;Permission denied&lt;/code&gt; verdi.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bu Neden Oldu?
&lt;/h3&gt;

&lt;p&gt;Bunu araştırdım. Proxmox'un kurucu geliştiricilerinden Thomas Lamprecht, 2018 tarihli bir mailing list yazışmasında Corosync'in &lt;code&gt;expected_votes&lt;/code&gt; davranışını şöyle açıklıyor:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Highest expected can not be smaller then the actual online quorate nodes."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yani Corosync, &lt;code&gt;expected_votes&lt;/code&gt; değerini &lt;strong&gt;online ve quorate node sayısının altında tutmuyor&lt;/strong&gt;. Thomas'ın verdiği örneğe göre, manuel olarak &lt;code&gt;expected_votes: 16&lt;/code&gt; ayarlanmışsa ve 28 node online/quorate durumdaysa, Corosync 16 yerine 28'i kullanıyor. Bunu kabaca şu şekilde ifade ediyor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;expected = max(user_set_expected, #nodes_quorate_online)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ancak online/quorate node sayısı manuel olarak belirlenen değere eşit veya daha düşükse, Corosync'in bu değeri koruması mümkün. Thomas ayrıca &lt;code&gt;last_man_standing&lt;/code&gt; etkinleştirildiğinde, yeterli node kaldığı sürece belirli bir süre sonunda &lt;code&gt;highest expected&lt;/code&gt; değerinin aşağı doğru yeniden hesaplanabileceğini belirtiyor. &lt;a href="https://lists.proxmox.com/pipermail/pve-user/2018-October/013641.html?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Proxmox Lists&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bunun QDevice'in &lt;code&gt;lms&lt;/code&gt; algoritmasından kaynaklandığını düşünüyorum; muhtemelen QDevice'in oy hesaplaması sağlıklı kalabilsin diye &lt;code&gt;expected_votes&lt;/code&gt;'un cluster'da tanımlı gerçek node sayısının (3) altına hiç düşürülmesine izin verilmiyor, o an kaç node'un online olduğundan bağımsız olarak. Ama bunu kaynak koda bakmadan kesin olarak iddia edemem; bu, dokümante edilmiş bir davranıştan çok, gözlemlenip mantıklı görünen bir çıkarım.&lt;/p&gt;

&lt;p&gt;Sonuç ne olursa olsun, pratik etkisi net: &lt;strong&gt;split-brain'i kasıtlı olarak tetiklemeye çalıştım, QDevice'li ortamda başaramadım.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Toparlamak
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm expected 5
&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh domif-setlink pve-c vnet15 up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Birkaç saniye sonra cluster tam olarak (5 oy, 3 node, Quorate: Yes) geri döndü.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 5: Shared Storage ile Migration'ı Yeniden Denemek
&lt;/h2&gt;

&lt;p&gt;Modül 1'de canlı migration'ı test ederken &lt;code&gt;--with-local-disks&lt;/code&gt; flag'ini kullanmak zorunda kalmıştım; çünkü VM'in diski shared storage'da değil, sadece kaynak node'un yerel LVM'sindeydi. Bir arkadaşımın önerisiyle bunu düzeltmeye karar verdim: gerçek bir SAN/NAS kurmak yerine, kendi laptop'umu (CachyOS host) NFS sunucusu olarak kullanıp bunu üç node'a paylaşımlı storage olarak eklemek.&lt;/p&gt;

&lt;p&gt;Baştan söylemek isterim: bu, gerçek bir shared storage değil, homelab için taklit edilmiş bir tanesi. Kullandığım &lt;code&gt;no_root_squash&lt;/code&gt; ve &lt;code&gt;insecure&lt;/code&gt; export seçenekleri, gerçek bir ortamda asla böyle bırakılmaz; root'un uzaktan tam yetkiyle yazmasına izin veriyor, güvenlik açısından savunmasız. Burada sadece lab kolaylığı için var.&lt;/p&gt;

&lt;h3&gt;
  
  
  NFS Sunucusunu Host'ta Kurmak
&lt;/h3&gt;

&lt;p&gt;CachyOS (Arch tabanlı) olduğu için paket ismi Debian'dan farklı; tek paket hem sunucu hem client araçlarını içeriyor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; nfs-utils
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /srv/nfs/proxmox-shared
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;nobody:nobody /srv/nfs/proxmox-shared
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;777 /srv/nfs/proxmox-shared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/etc/exports&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;/&lt;span class="n"&gt;srv&lt;/span&gt;/&lt;span class="n"&gt;nfs&lt;/span&gt;/&lt;span class="n"&gt;proxmox&lt;/span&gt;-&lt;span class="n"&gt;shared&lt;/span&gt; &lt;span class="m"&gt;192&lt;/span&gt;.&lt;span class="m"&gt;168&lt;/span&gt;.&lt;span class="m"&gt;122&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;/&lt;span class="m"&gt;24&lt;/span&gt;(&lt;span class="n"&gt;rw&lt;/span&gt;,&lt;span class="n"&gt;sync&lt;/span&gt;,&lt;span class="n"&gt;no_subtree_check&lt;/span&gt;,&lt;span class="n"&gt;no_root_squash&lt;/span&gt;,&lt;span class="n"&gt;insecure&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; rpcbind nfs-server
&lt;span class="nb"&gt;sudo &lt;/span&gt;exportfs &lt;span class="nt"&gt;-arv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Beklenmedik Engel: Firewall
&lt;/h3&gt;

&lt;p&gt;İlk mount denemesi &lt;code&gt;pve-a&lt;/code&gt;'dan başarısız oldu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;mount.nfs: Connection refused for 192.168.122.1:/srv/nfs/proxmox-shared on /mnt/test-nfs
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Servisler çalışıyordu, export tanımlıydı, portlar (&lt;code&gt;2049&lt;/code&gt;, &lt;code&gt;111&lt;/code&gt;) dinliyordu; sorun &lt;code&gt;firewalld&lt;/code&gt;'ın aktif olmasıydı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--get-active-zones&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;libvirt
  interfaces: virbr-coro virbr0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;virbr0&lt;/code&gt; (management) ve &lt;code&gt;virbr-coro&lt;/code&gt; (corosync-net) ikisi de &lt;code&gt;libvirt&lt;/code&gt; zone'undaydı, ama bu zone'a NFS servisleri hiç eklenmemişti:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;libvirt &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nfs
&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;libvirt &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rpc-bind
&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;libvirt &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mountd
&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu düzeltmeden sonra manuel mount çalıştı, &lt;code&gt;touch&lt;/code&gt; ve &lt;code&gt;ls&lt;/code&gt; başarılı oldu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8bfgm5kntz3xmi0k7e8z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8bfgm5kntz3xmi0k7e8z.png" alt="nfs-shared Depolamasının Web UI da Görünümü" width="301" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Proxmox'a Eklemek ve Disk Taşımak
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvesm add nfs nfs-shared &lt;span class="nt"&gt;--server&lt;/span&gt; 192.168.122.1 &lt;span class="nt"&gt;--export&lt;/span&gt; /srv/nfs/proxmox-shared &lt;span class="nt"&gt;--content&lt;/span&gt; images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name              Type     Status     Total (KiB)      Used (KiB) Available (KiB)        %
nfs-shared         nfs     active       486256640        72509440       412091392   14.91%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;VM 100'ün diskini taşıdım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm move-disk 100 scsi0 nfs-shared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr15peaiom5fqhuam3yk4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr15peaiom5fqhuam3yk4.png" alt="nfs-shared Depolamasına Taşıdığımız Diskin Görünümü" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Beklenmedik Bir Detay: Hayalet Disk Referansı
&lt;/h3&gt;

&lt;p&gt;İlk migration denemesinde (&lt;code&gt;pve-b&lt;/code&gt;'den &lt;code&gt;pve-c&lt;/code&gt;'ye) log'da hâlâ "copying local disk images" adımı çıktı, 8GB'lık bir kopyalama yaptı; disk zaten NFS'te olması gerekirken. &lt;code&gt;qm config 100&lt;/code&gt;'e baktığımda sebebi gördüm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scsi0: nfs-shared:100/vm-100-disk-0.raw,...
unused0: local-lvm:vm-100-disk-0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;move-disk&lt;/code&gt; diski gerçekten NFS'e taşımıştı, ama eski local-lvm referansını &lt;code&gt;unused0&lt;/code&gt; olarak arkada bırakmıştı. Migration da bağlı her diski (kullanılan/kullanılmayan farketmeksizin) taşımaya çalıştığı için bu hayalet referansı da kopyalamıştı. &lt;code&gt;lvremove&lt;/code&gt; ile temizlemeye çalışınca üç node'da da "Failed to find logical volume" hatası aldım; migration'ın kendisi log'un sonunda zaten &lt;code&gt;Logical volume "vm-100-disk-0" successfully removed&lt;/code&gt; demişti, yani gerçek disk çoktan silinmişti, sadece config'te unutulmuş bir referans kalmıştı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm &lt;span class="nb"&gt;unlink &lt;/span&gt;100 &lt;span class="nt"&gt;--idlist&lt;/span&gt; unused0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Temiz Migration: Öncesi/Sonrası
&lt;/h3&gt;

&lt;p&gt;Hayalet referans temizlendikten sonra tekrar denedim (&lt;code&gt;pve-c&lt;/code&gt;'den &lt;code&gt;pve-a&lt;/code&gt;'ya):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-08-14 09:06:34 starting migration of VM 100 to node 'pve-a'
2026-08-14 09:06:34 starting VM 100 on remote node 'pve-a'
2026-08-14 09:06:36 starting online/live migration
2026-08-14 09:06:37 average migration speed: 1.0 GiB/s - downtime 15 ms
2026-08-14 09:06:37 migration completed, transferred 158.5 MiB VM-state
2026-08-14 09:06:39 migration finished successfully (duration 00:00:05)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Copying local disk images" adımı &lt;strong&gt;tamamen yok&lt;/strong&gt;; disk hiç dokunulmadı, sadece VM'in bellek durumu (158.5 MiB) taşındı.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Modül 1 (local disk, &lt;code&gt;--with-local-disks&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;Bu test (NFS shared storage)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Disk kopyalama&lt;/td&gt;
&lt;td&gt;Var, ~8 GB, ~13 saniye&lt;/td&gt;
&lt;td&gt;Yok&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toplam süre&lt;/td&gt;
&lt;td&gt;~20 saniye&lt;/td&gt;
&lt;td&gt;~5 saniye&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Downtime&lt;/td&gt;
&lt;td&gt;43 ms&lt;/td&gt;
&lt;td&gt;15 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Taşınan veri&lt;/td&gt;
&lt;td&gt;Disk + VM state&lt;/td&gt;
&lt;td&gt;Sadece VM state (158.5 MiB)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Ping testi bu sırada da kesintisizdi; 40'tan fazla paket, hiç kayıp yok, gecikme 0.3-1.3ms aralığında sabit kaldı.&lt;/p&gt;

&lt;p&gt;Bu karşılaştırma, &lt;code&gt;--with-local-disks&lt;/code&gt;'in neden var olduğunu somutlaştırdı: o flag olmadan disk paylaşımlı değilse migration mümkün değil; flag'in kendisi de aslında bir kısayol değil, "diski de taşımak zorundayım çünkü paylaşımlı değil" itirafı. Gerçek shared storage'da bu ihtiyaç tamamen ortadan kalkıyor, migration da doğal olarak hızlanıyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Genel Değerlendirme
&lt;/h2&gt;

&lt;p&gt;Bu modülde planladığımdan farklı bir sona vardım. Amacım split-brain riskini göstermekti; onun yerine, QDevice'in bu riski nasıl engellediğini gösterdim. Sanırım bu, orijinal planımdan daha iyi bir sonuç.&lt;/p&gt;

&lt;p&gt;En çok üzerinde durduğum üç nokta:&lt;/p&gt;

&lt;p&gt;Birincisi, &lt;code&gt;ffsplit&lt;/code&gt; ve &lt;code&gt;lms&lt;/code&gt; arasındaki oy farkı (1 vs N-1). Bu küçük bir detay gibi görünüyor ama gerçek bir kararı etkiliyor: tek sayılı bir cluster'da QDevice'in matematiği, çift sayılı bir cluster'dakinden temelden farklı çalışıyor.&lt;/p&gt;

&lt;p&gt;İkincisi, &lt;code&gt;pvecm expected&lt;/code&gt;'in "zaten quorate olan bir partition'ı düşüremezsin" kuralı. İlk denemem bu yüzden başarısız oldu, ve bunu QDevice'e yanlış bağladım; doğrusunu bulmak için araştırmam gerekti. Yanlış bir hipotezle başlayıp onu düzeltmek, doğru hipotezle başlamaktan daha çok şey öğretti.&lt;/p&gt;

&lt;p&gt;Üçüncüsü, ikinci (doğru) denemede gördüğüm 1'den 3'e yuvarlanma. Bunun tam mekanizmasını hâlâ %100 kesinlikle açıklayamıyorum; genel corosync davranışı bunu tam karşılamıyor, QDevice'e özgü bir şey olduğunu düşünüyorum ama kaynak koda inmeden kesin konuşamam. Bu belirsizliği olduğu gibi bırakmayı, uydurma bir kesinlik sunmaya tercih ettim.&lt;/p&gt;

&lt;p&gt;Sonuç olarak: quorum kaybını gerçekten tetikledim, pmxcfs'in read-only davranışını iki farklı şekilde (host çökmesi, ağ izolasyonu) doğruladım, QDevice'i kurdum ve onun olmadan imkansız olacak bir senaryoyu ayakta tuttum, split-brain'i kasıtlı olarak tetiklemeye çalışıp başaramadım, ve son olarak Modül 1'deki &lt;code&gt;--with-local-disks&lt;/code&gt; zorunluluğunu shared storage kurarak ortadan kaldırdım. Bu son kısım kendi başına küçük bir hikaye oldu: NFS kurulumu, firewall'ın gerçek engel olduğunu bulmam, ve migration'ın arkada bıraktığı hayalet disk referansını çözmem; hiçbiri planladığım gibi sorunsuz gitmedi, ama her biri gerçek bir öğrenme anıydı.&lt;/p&gt;

&lt;p&gt;Bir sonraki modülde HA Manager'a geçiyorum: watchdog tabanlı fencing, node/resource affinity kuralları, ve Modül 1'de gördüğümüz "VM kendiliğinden açılmadı" sonucunu bu sefer tersine çevirmek.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bu seri, resmi Proxmox VE dokümantasyonundan (pve.proxmox.com) ve topluluk kaynaklarından (pve-user mailing list, corosync/votequorum man sayfaları) derleniyor. Buradaki gözlemler bir homelab ortamına dayanıyor; production kararları için resmi dokümantasyon ve deneyimli sistem yöneticileri referans alınmalıdır.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title># Proxmox Cluster Kurmak: Kavramlar, GUI/CLI ve Gerçek Hatalar (Modül 1)</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Tue, 11 Aug 2026 06:06:19 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/-proxmox-cluster-kurmak-kavramlar-guicli-ve-gercek-hatalar-modul-1-1f6k</link>
      <guid>https://dev.to/hakanbaban53/-proxmox-cluster-kurmak-kavramlar-guicli-ve-gercek-hatalar-modul-1-1f6k</guid>
      <description>&lt;p&gt;&lt;strong&gt;Seri:&lt;/strong&gt; Proxmox VE Cluster ve Corosync&lt;/p&gt;




&lt;p&gt;Modül 0'da dört sanal makineyi (&lt;code&gt;pve-a&lt;/code&gt;, &lt;code&gt;pve-b&lt;/code&gt;, &lt;code&gt;pve-c&lt;/code&gt;, &lt;code&gt;qdevice-1&lt;/code&gt;) kurdum, ağlarını yapılandırdım ve birbirlerine ulaşabildiklerini doğruladım. Lab artık hazır. Bu modüld asıl kavramlara ve gerçek cluster kurulumuna geçiyorum: &lt;code&gt;pvecm&lt;/code&gt;, Corosync ve pmxcfs nedir, ne işe yarar, ve cluster'ı hem GUI'den hem CLI'dan kurunca neler yaşadım.&lt;/p&gt;

&lt;p&gt;Bu modülde işleri bilerek iki farklı yoldan yaptım: cluster'ı web arayüzünden oluşturdum, bir node'u terminalden, bir node'u web arayüzünden kattım. Amaç sadece "iki yöntem de mümkün" demek değildi; iki yöntemi karıştırınca ortaya çıkan gerçek bir hatayı da görmek istedim, ve gördüm.&lt;/p&gt;




&lt;h2&gt;
  
  
  Neden Cluster?
&lt;/h2&gt;

&lt;p&gt;Tek bir Proxmox node'u zaten kendi başına çalışan bir hipervizör; web arayüzü var, VM/container yönetimi var. Cluster, birden fazla node'u bir araya getirip merkezi bir yönetim deneyimi sunuyor: tek bir web arayüzünden tüm node'ları yönetmek, VM'leri node'lar arası taşımak, ortak firewall ve HA politikaları tanımlamak.&lt;/p&gt;

&lt;p&gt;Mimari olarak dikkat çeken bir nokta var: Proxmox cluster'ı &lt;strong&gt;multi-master&lt;/strong&gt;. Yani tek bir "merkezi yönetim sunucusu" yok; her node, cluster'ın tamamını yönetebilecek yetkiye sahip. Bu, VMware'in vCenter'ı gibi ayrı bir yönetim katmanına ihtiyaç duymuyor; yönetim mantığı doğrudan node'ların kendisine gömülü.&lt;/p&gt;




&lt;h2&gt;
  
  
  Üç Parça: pvecm, Corosync, pmxcfs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;pvecm&lt;/code&gt;&lt;/strong&gt;, cluster'ı yönetmek için kullandığım komut satırı aracı. Node oluşturma, ekleme, çıkarma, durum sorgulama gibi işlemler buradan geçiyor. Web arayüzünün "Cluster" sekmesi de aslında aynı işlemleri, aynı API üzerinden yapıyor; ikisi farklı arayüz, aynı motor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Corosync Cluster Engine&lt;/strong&gt;, node'lar arası güvenilir grup iletişimini sağlayan alt katman. Hem &lt;code&gt;pvecm&lt;/code&gt; hem web arayüzü bunun üzerine kurulu. Node sayısında sert bir teknik sınır yok; pratikte ağ ve donanım performansı sınırlıyor, üst düzey donanımla 50'den fazla node'lu production cluster'lar bile rapor edilmiş.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pmxcfs (Proxmox Cluster File System)&lt;/strong&gt;, &lt;code&gt;/etc/pve&lt;/code&gt; altında mount edilen, FUSE tabanlı bir dosya sistemi. Cluster'daki tüm konfigürasyon dosyaları burada tutuluyor ve Corosync üzerinden tüm node'lara gerçek zamanlı olarak replike ediliyor. En kritik davranışı şu: node quorum'u kaybettiğinde bu dosya sistemi read-only'e geçiyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 1: pmxcfs'i Kümelenmeden Önce Gözlemlemek
&lt;/h2&gt;

&lt;p&gt;Cluster'ı kurmadan önce, &lt;code&gt;pve-a&lt;/code&gt; üzerinde henüz tek başınayken pmxcfs'e baktım.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl status pve-cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Servis &lt;code&gt;active (running)&lt;/code&gt;, &lt;code&gt;pmxcfs&lt;/code&gt; process'i çalışıyor. Log satırlarında ilginç bir detay vardı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pmxcfs[806]: [main] notice: resolved node name 'pve-a' to '192.168.122.11' for default node IP address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9eyzb5icckbyx015t38t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9eyzb5icckbyx015t38t.png" alt="pmxcfs log satırı - node adının management IP'sine çözümlenmesi" width="786" height="18"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;pmxcfs, node adını çözümlerken &lt;strong&gt;management ağının IP'sini&lt;/strong&gt; (&lt;code&gt;192.168.122.11&lt;/code&gt;) varsayılan olarak almış. Bu gözlem, ileride başıma geleceklerin habercisi oldu; birazdan neden önemli olduğunu göreceğiz.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /etc/pve/nodes/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/etc/pve/nodes/&lt;/code&gt; dizininde sadece &lt;code&gt;pve-a&lt;/code&gt;'yı gördüm; bu, "öncesi" fotoğrafıydı.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff0ig0mow8gxf0n4wm6nn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff0ig0mow8gxf0n4wm6nn.png" alt="/etc/pve/nodes/ dizini - cluster öncesi, sadece pve-a" width="636" height="63"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Uygulama 2: Cluster'ı Kurmak
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cluster'ı GUI'den Oluşturmak
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdizd2e4aqvhxfjl2ldi6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdizd2e4aqvhxfjl2ldi6.png" alt="Create Cluster ekranı - cluster ismi ve Link seçimi" width="607" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0s2z6d8cxw19mkllay4m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0s2z6d8cxw19mkllay4m.png" alt="Create Cluster - Link 1 için corosync-net arayüzü (10.10.10.11) seçili" width="602" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pve-a&lt;/code&gt;'nın web arayüzünden &lt;code&gt;Datacenter → Cluster → Create Cluster&lt;/code&gt; dedim, isim olarak &lt;code&gt;lab-cluster&lt;/code&gt; verdim, Link 1 için &lt;code&gt;corosync-net&lt;/code&gt; arayüzünü (&lt;code&gt;10.10.10.11&lt;/code&gt;) seçtim. İşlem birkaç saniyede tamamlandı.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fno2gooane4zf0x548hhm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fno2gooane4zf0x548hhm.png" alt="Datacenter → Cluster - lab-cluster oluşturuldu, tek node (pve-a)" width="799" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Hata: TLS Sertifika Doğrulaması
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pve-b&lt;/code&gt;'yi katılırken ilk aklıma gelen, doğrudan &lt;code&gt;corosync-net&lt;/code&gt; IP'sini (&lt;code&gt;10.10.10.11&lt;/code&gt;) hedef göstermekti. Mantığım şuydu: bu ağı zaten Corosync trafiği için ayırmıştım, cluster'a katılma işlemi de bir "cluster" işlemiydi, o yüzden bu IP'yi kullanmak bana doğru geldi.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm add 10.10.10.11 &lt;span class="nt"&gt;--link1&lt;/span&gt; 10.10.10.12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Establishing API connection with host '10.10.10.11'
The authenticity of host '10.10.10.11' can't be established.
X509 SHA256 key fingerprint is FF:59:FA:...
Are you sure you want to continue connecting (yes/no)? yes
500 Can't connect to 10.10.10.11:8006 (hostname verification failed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meğerse yanılmışım. &lt;code&gt;pvecm add&lt;/code&gt;'in ilk parametresi, Corosync trafiği değil; &lt;code&gt;pve-a&lt;/code&gt;'nın API'sine (&lt;code&gt;:8006&lt;/code&gt; portu, yani web arayüzünün kendisi) HTTPS üzerinden bağlanıp "beni bu cluster'a ekle" demek. Bu, Uygulama 1'de gördüğüm pmxcfs gözlemiyle doğrudan bağlantılıydı: &lt;code&gt;pve-a&lt;/code&gt;'nın self-signed sertifikası, management IP/hostname için üretilmiş. &lt;code&gt;pve-b&lt;/code&gt;, &lt;code&gt;10.10.10.11&lt;/code&gt; üzerinden bu API'ye bağlanmaya çalışınca sertifika o adresi tanımıyor, hostname doğrulaması çöküyor.&lt;/p&gt;

&lt;p&gt;Yani karıştırdığım şey şuydu: &lt;code&gt;corosync-net&lt;/code&gt;'in var olma amacı (Corosync'in kendi trafiği) ile join işleminin gerçekte ne olduğu (bir API çağrısı) aynı şey değil. &lt;code&gt;--link1&lt;/code&gt; parametresi zaten Corosync trafiğinin hangi arayüzden gideceğini ayrıca belirtiyor; ilk parametrenin de aynı ağdan olması gerekmiyor, çünkü o bambaşka bir iletişim katmanı.&lt;/p&gt;

&lt;p&gt;Doğrusu, join/API bağlantısı için management IP'yi, Corosync trafiği için &lt;code&gt;--link1&lt;/code&gt;'i ayrı ayrı kullanmaktı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm add 192.168.122.11 &lt;span class="nt"&gt;--link1&lt;/span&gt; 10.10.10.12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Join request OK, finishing setup locally
stopping pve-cluster service
backup old database to '/var/lib/pve-cluster/backup/config-1786335821.sql.gz'
waiting for quorum...OK
(re)generate node files
generate new node certificate
merge authorized SSH keys
generated new node certificate, restart pveproxy and pvedaemon services
successfully added node 'pve-b' to cluster.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Çalıştı. &lt;code&gt;pve-b&lt;/code&gt; cluster'a katıldı.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;pve-c&lt;/code&gt;'yi GUI'den Katılmak
&lt;/h3&gt;

&lt;p&gt;Bu sefer terminale hiç dokunmadan, tamamen web arayüzünden gitmek istedim. &lt;code&gt;pve-a&lt;/code&gt;'nın arayüzünde &lt;code&gt;Datacenter → Cluster → Join Information&lt;/code&gt;, kopyala butonuna bastım; encode edilmiş bir bilgi bloğu (peer address, fingerprint, cluster network bilgisi hepsi içinde) verdi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8a6kgnfhw3ry2h50b56k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8a6kgnfhw3ry2h50b56k.png" alt="Join Information ekranı - pve-a, encode edilmiş bilgi bloğu" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8frb6eqdtl3khbi940jg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8frb6eqdtl3khbi940jg.png" alt="Join Information - kopyala butonu" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pve-c&lt;/code&gt;'nin kendi arayüzüne geçip &lt;code&gt;Join Cluster&lt;/code&gt; dedim, kopyaladığım bilgiyi yapıştırdım, root parolasını girdim. Ekran otomatik olarak Peer Address, Fingerprint alanlarını doldurdu, ve &lt;strong&gt;Cluster Network&lt;/strong&gt; kısmında "Link: 1" yazıp &lt;code&gt;10.10.10.13&lt;/code&gt;'ü önerdi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqxpz2qucqfg4xg323ilb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqxpz2qucqfg4xg323ilb.png" alt="Cluster Join ekranı - pve-c, Link 1 otomatik önerisi (10.10.10.13)" width="799" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3n3kp7ttm6hpr6j5apf7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3n3kp7ttm6hpr6j5apf7.png" alt="Cluster Join ekranı - Peer Address ve Fingerprint otomatik dolu" width="799" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Join 'lab-cluster'" dedim.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fazvpu0hw09x2egcc0nzt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fazvpu0hw09x2egcc0nzt.png" alt="Task viewer - Join Cluster, " width="799" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Not:&lt;/strong&gt; Join Information bloğu cluster ismini, node adreslerini (management ve corosync IP'leri) ve sertifika fingerprint'ini kodluyor; root parolası bunun dışında, ayrıca giriliyor. Yani bu string'i tek başına ele geçirmek cluster'a katılmaya yetmiyor. Yine de gerçek bir ortamda bu blok ağ topolojisini açığa çıkarıyor, bu yüzden production sistemlerde bu tür ekran görüntülerini herkese açık paylaşmadan önce IP'leri kırpmak/bulanıklaştırmak makul bir alışkanlık. Burada gösterdiğim IP'ler (&lt;code&gt;192.168.122.x&lt;/code&gt;, &lt;code&gt;10.10.10.x&lt;/code&gt;) private adresler ve sadece kendi laptopumdaki izole lab ağında anlamlı olduğu için olduğu gibi bıraktım.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Task viewer ekranı "stopping pve-cluster service" satırında donmuş gibi görünüyordu, "Download" butonu bile aktif olmamıştı. İlk anda başarısız olduğunu düşündüm. Ama bu, bilinen bir kozmetik davranış: &lt;code&gt;pve-cluster.service&lt;/code&gt; yeniden başlarken &lt;code&gt;pveproxy&lt;/code&gt;/&lt;code&gt;pvedaemon&lt;/code&gt; de kendini yeniden başlatıyor, bu da tarayıcının o anki task log bağlantısını kesiyor. Arka planda işlem gerçekte tamamlanmış oluyor; sadece tarayıcı bunu görmüyor.&lt;/p&gt;

&lt;p&gt;Bunu terminalden doğruladım.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sonucu Doğrulamak
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pve-a&lt;/code&gt;'da, &lt;code&gt;pve-b&lt;/code&gt; katıldıktan sonra:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes:            2
Quorate:          Yes
Expected votes:   2
Total votes:      2
Quorum:           2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pve-c&lt;/code&gt; katıldıktan sonra:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes:            3
Quorate:          Yes
Expected votes:   3
Total votes:      3
Quorum:           2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pvecm nodes&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    Nodeid      Votes Name
         1          1 pve-a (local)
         2          1 pve-b
         3          1 pve-c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Web arayüzünde de &lt;code&gt;Datacenter → Cluster&lt;/code&gt; altında aynı bilgi: üç node, her biri 1 oy, &lt;code&gt;Link 1&lt;/code&gt; sütununda üçünün de doğru corosync-net IP'sini gösterdiğini gördüm. GUI ve CLI, aynı gerçekliği iki farklı pencereden gösteriyordu; bu benim için iyi bir doğrulama oldu, tek bir kaynağa güvenmek yerine ikisini çapraz kontrol etmiş oldum.&lt;/p&gt;

&lt;h3&gt;
  
  
  pmxcfs'in Canlı Senkronizasyonu
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pve-a&lt;/code&gt;'da bir dosya oluşturdum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"cluster calisiyor"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/pve/test-sync.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pve-b&lt;/code&gt; ve &lt;code&gt;pve-c&lt;/code&gt;'de, saniyeler içinde:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/pve/test-sync.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cluster calisiyor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pve-c&lt;/code&gt;'nin &lt;code&gt;pve-cluster.service&lt;/code&gt; loglarına baktığımda senkronizasyonun nasıl gerçekleştiğini de gördüm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[status] notice: received sync request (epoch 1/1632/00000003)
[dcdb] notice: received all states
[dcdb] notice: leader is 1/1632
[dcdb] notice: synced members: 1/1632, 2/2612
[dcdb] notice: waiting for updates from leader
[status] notice: all data is up to date
[dcdb] notice: update complete - trying to commit (got 47 inode updates)
[dcdb] notice: all data is up to date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pve-c&lt;/code&gt;'nin katıldığı anda, &lt;code&gt;pve-a&lt;/code&gt;'nın (leader'ın) tarafında ne olduğunu da görmek istedim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[status] notice: received sync request (epoch 1/1632/00000003)
[dcdb] notice: received all states
[dcdb] notice: leader is 1/1632
[dcdb] notice: synced members: 1/1632, 2/2612
[dcdb] notice: start sending inode updates
[dcdb] notice: sent all (47) updates
[dcdb] notice: all data is up to date
[status] notice: received all states
[status] notice: all data is up to date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;leader is 1/1632&lt;/code&gt; satırı özellikle dikkatimi çekti: pmxcfs'in kendi içinde bir "leader" kavramı var, senkronizasyon bu leader üzerinden koordine ediliyor. İki log da aynı sayıyı, 47 update'i, konuşuyor; biri "gönderdim" diyor, diğeri "aldım" diyor. Dağıtık bir sistemde iki bağımsız node'un aynı olayı aynı sayılarla anlatması, bana pmxcfs'in gerçekten tutarlı çalıştığına dair küçük ama tatmin edici bir kanıt oldu.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/etc/pve/nodes/&lt;/code&gt; Dizininin Son Hali
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /etc/pve/nodes/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modül 1'in başında &lt;code&gt;pve-a&lt;/code&gt;'da sadece kendi node'unu görmüştüm. Şimdi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;drwxr-xr-x 2 root www-data 0 Aug  5 17:57 pve-a
drwxr-xr-x 2 root www-data 0 Aug 10 07:23 pve-b
drwxr-xr-x 2 root www-data 0 Aug 10 07:26 pve-c
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu dizin listesi, hem &lt;code&gt;pve-a&lt;/code&gt;'da hem &lt;code&gt;pve-b&lt;/code&gt;'de hem &lt;code&gt;pve-c&lt;/code&gt;'de birebir aynı; üçü de aynı gerçekliği paylaşıyor. Bu, "öncesi/sonrası" karşılaştırmasının en net hali: bir dizin, tek bir node'a özgü değil, artık cluster'ın ortak hafızasının bir parçası.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test: Migration ve Node Arızası Simülasyonu
&lt;/h2&gt;

&lt;p&gt;Buraya kadar cluster'ı kurdum, quorate olduğunu doğruladım, pmxcfs'in senkronize çalıştığını gördüm. Ama bir arkadaşım haklı bir eksiği fark etti: cluster'a özgü hiçbir şey (bir VM'i node'lar arası taşımak) hiç denenmemişti. "Cluster kurdum" demek, "kurdum ve gerçekten işe yarıyor" demenin garantisi değil.&lt;/p&gt;

&lt;h3&gt;
  
  
  Küçük Bir VM ile Migration Testi
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pve-a&lt;/code&gt; üzerinde minimal bir Alpine Linux VM'i (VMID 100) oluşturdum, &lt;code&gt;vmbr0&lt;/code&gt; (management ağı) üzerine bağladım; &lt;code&gt;corosync-net&lt;/code&gt;'e değil, çünkü o ağ sadece Corosync trafiği için izole tutulmuş durumda, guest trafiğini oraya karıştırmak istemedim.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2For0rac4s4u8v6bxlrxve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2For0rac4s4u8v6bxlrxve.png" alt="Alpine Linux Sanal Makine Detayları" width="759" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;İlk migration denemem başarısız oldu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm migrate 100 pve-b &lt;span class="nt"&gt;--online&lt;/span&gt; &lt;span class="nt"&gt;--with-local-disks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;found local disk 'local:iso/alpine-standard-3.24.1-x86_64.iso' (attached)
can't migrate local disk 'local:iso/alpine-standard-3.24.1-x86_64.iso': local cdrom image
ERROR: migration aborted (duration 00:00:01): Problem found while scanning volumes - can't migrate VM - check log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kurulumdan kalan ISO hâlâ sanal CD-ROM olarak bağlıydı; bu, ne shared storage'da ne de replike ediliyor, sadece &lt;code&gt;pve-a&lt;/code&gt;'nın diskinde duruyor, migration bunu taşıyamıyor. CD-ROM'u çıkarıp tekrar denedim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm &lt;span class="nb"&gt;set &lt;/span&gt;100 &lt;span class="nt"&gt;--ide2&lt;/span&gt; none
qm migrate 100 pve-b &lt;span class="nt"&gt;--online&lt;/span&gt; &lt;span class="nt"&gt;--with-local-disks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu sefer çalıştı. &lt;code&gt;pve-b&lt;/code&gt;'de doğruladım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm status 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;running&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cluster genelinde de aynı bilgiyi &lt;code&gt;pve-a&lt;/code&gt;'dan görebiliyordum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvesh get /cluster/resources &lt;span class="nt"&gt;--type&lt;/span&gt; vm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;│ id       │ type │ ... │ name   │
│ qemu/100 │ qemu │ ... │ alpine │
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;VM &lt;code&gt;pve-b&lt;/code&gt; üzerinde, çalışır durumda görünüyordu.&lt;/p&gt;

&lt;h3&gt;
  
  
  Node'u Çökertmek
&lt;/h3&gt;

&lt;p&gt;Asıl test buradaydı. Önce ping'i başlattım (host laptop'umdan):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping 192.168.122.251
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;64 bytes from 192.168.122.251: icmp_seq=1 ttl=64 time=0.842 ms
64 bytes from 192.168.122.251: icmp_seq=2 ttl=64 time=0.969 ms
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;64 bytes from 192.168.122.251: icmp_seq=19 ttl=64 time=1.08 ms
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sonra &lt;code&gt;pve-b&lt;/code&gt;'yi, temiz bir kapanış değil, gerçek bir çökme gibi simüle ettim; fiziksel host'tan (nested VM olduğu için mümkün):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh destroy pve-b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Domain 'pve-b' destroyed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pve-a&lt;/code&gt;'dan cluster durumuna baktım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes:            2
Quorate:          Yes
Expected votes:   3
Total votes:      2
Quorum:           2
Membership information
----------------------
    Nodeid      Votes Name
0x00000001          1 10.10.10.11 (local)
0x00000003          1 10.10.10.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tam beklendiği gibi: &lt;code&gt;pve-b&lt;/code&gt; (nodeid 2) membership listesinden düştü, ama kalan iki node (&lt;code&gt;pve-a&lt;/code&gt;, &lt;code&gt;pve-c&lt;/code&gt;) hâlâ çoğunluğu oluşturduğu için &lt;code&gt;Quorate: Yes&lt;/code&gt; kaldı.&lt;/p&gt;

&lt;p&gt;Ping çıktısında ilginç bir detay vardı: &lt;code&gt;pve-b&lt;/code&gt; öldükten hemen sonra paketler önce sessizce kayboldu (ne cevap ne hata, muhtemelen ARP kaydı hâlâ geçerliydi), birkaç düzine paket sonra gateway (&lt;code&gt;192.168.122.1&lt;/code&gt;, libvirt'ün NAT'ı) aktif olarak "Destination Host Unreachable" demeye başladı:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;64 bytes from 192.168.122.251: icmp_seq=19 ttl=64 time=1.08 ms
From 192.168.122.1 icmp_seq=52 Destination Host Unreachable
From 192.168.122.1 icmp_seq=53 Destination Host Unreachable
ping: sendmsg: No route to host
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yani arıza algılama anlık değil, kademeli: önce sessizlik, sonra açık ret.&lt;/p&gt;

&lt;h3&gt;
  
  
  VM'i Başka Bir Yerde Başlatmayı Denemek
&lt;/h3&gt;

&lt;p&gt;Şimdi asıl soru: VM 100, &lt;code&gt;pve-b&lt;/code&gt; çökmüşken başka bir node'da kendiliğinden açılır mı?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qm start 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Configuration file 'nodes/pve-a/qemu-server/100.conf' does not exist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hayır, açılmadı; ama hatanın kendisi beklediğimden daha ince bir şey söylüyor. Hata "pve-b'ye ulaşamıyorum" demiyor; "&lt;code&gt;pve-a&lt;/code&gt;'nın kendi node dizininde böyle bir config dosyası yok" diyor. Sebep şu: pmxcfs, &lt;code&gt;nodes/pve-b/qemu-server/100.conf&lt;/code&gt; dosyasını cluster genelinde senkronize ediyor, &lt;code&gt;pve-a&lt;/code&gt; bile bu dosyayı görebiliyor (&lt;code&gt;pvesh get /cluster/resources&lt;/code&gt; bunu az önce gösterdi). Ama &lt;code&gt;qm start&lt;/code&gt;, sadece komutu çalıştırdığın node'un &lt;strong&gt;kendi&lt;/strong&gt; dizinindeki config'lere bakıyor. VM 100'ün config'i &lt;code&gt;pve-b&lt;/code&gt;'nin dizininde olduğu için, &lt;code&gt;pve-a&lt;/code&gt; üzerinde &lt;code&gt;qm start 100&lt;/code&gt; demek, "burada böyle bir VM yok" demekle aynı şey.&lt;/p&gt;

&lt;p&gt;Bu ayrım önemli: pmxcfs veriyi her yere kopyalıyor, ama "bu VM'i sahiplenip başka bir node'da başlat" kararını kimse otomatik almıyor. Veri paylaşılıyor, karar paylaşılmıyor. Bunu yapacak mekanizma (HA Manager, fencing, watchdog) henüz devrede değil.&lt;/p&gt;

&lt;h3&gt;
  
  
  Toparlamak ve Doğrulamak
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pve-b&lt;/code&gt;'yi geri başlattım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;virsh start pve-b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Birkaç dakika sonra &lt;code&gt;pve-a&lt;/code&gt;'dan tekrar baktım:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pvecm status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nodes:            3
Quorate:          Yes
Expected votes:   3
Total votes:      3
Quorum:           2
Membership information
----------------------
    Nodeid      Votes Name
0x00000001          1 10.10.10.11 (local)
0x00000002          1 10.10.10.12
0x00000003          1 10.10.10.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Üç node da geri döndü, cluster kendiliğinden toparlandı; hiçbir manuel müdahale gerekmedi. &lt;code&gt;pve-b&lt;/code&gt; geri geldikten sonra &lt;code&gt;qm start 100&lt;/code&gt;'ü &lt;code&gt;pve-a&lt;/code&gt;'dan tekrar denedim, aynı hatayı aldım; bu da tutarlıydı, çünkü hatanın sebebi "node kapalı" değil "config burada değil" olduğu için, &lt;code&gt;pve-b&lt;/code&gt; geri gelmesi bu durumu değiştirmiyor. VM'i başlatmanın doğru yolu, &lt;code&gt;pve-b&lt;/code&gt;'ye SSH ile bağlanıp oradan &lt;code&gt;qm start 100&lt;/code&gt; demekti; ki VM zaten orada duruyordu, hiç kapanmamıştı bile (sadece host çöktüğü için o da düşmüştü).&lt;/p&gt;




&lt;h2&gt;
  
  
  Genel Değerlendirme
&lt;/h2&gt;

&lt;p&gt;Bu modülde, işleri hem GUI'den hem CLI'dan yapmaya çalışınca gerçek bir hatayla karşılaştım: TLS hostname doğrulaması. Bu, Modül 1'in başında yaptığım pmxcfs gözlemiyle (node adının management IP'sine çözümlenmesi) doğrudan bağlantılıydı; o gözlemi yapmamış olsaydım, hatanın kaynağını bulmak daha uzun sürerdi. Bir kavramı önceden anlamış olmak, karşılaştığın hatayı çok daha hızlı okumanı sağlıyor.&lt;/p&gt;

&lt;p&gt;Task viewer'ın donması ise ayrı bir ders: arayüzün "bir şey oluyormuş gibi görünmemesi", arka planda bir şey olmadığı anlamına gelmiyor. Terminal üzerinden çapraz doğrulama yapmak, GUI'ye kör güvenmemek için iyi bir alışkanlık.&lt;/p&gt;

&lt;p&gt;En değerlisi ise sona sakladığım migration testiydi. Cluster'ı kurmuş olmak, ona güvenmek için yeterli değil; gerçekten bir workload'u taşımadan, bir node'u çökertmeden, "çalışıyor" demek bir iddiadan ibaret kalıyor. Test, iki şeyi net gösterdi: birincisi, live migration gerçekten çalışıyor, VM kesintisiz taşınabiliyor. İkincisi, ve daha önemlisi, cluster kurmak otomatik yüksek erişilebilirlik anlamına gelmiyor. Bir node çöktüğünde, üzerindeki VM kendiliğinden başka bir yerde açılmıyor; pmxcfs veriyi her yere kopyalasa da, "bu VM'i devral" kararını hiçbir mekanizma otomatik almıyor. Bunu yapacak parça (HA Manager, fencing, watchdog) ayrı bir modülün konusu.&lt;/p&gt;

&lt;p&gt;Sonuç olarak: cluster çalışıyor, üç node quorate, pmxcfs gerçek zamanlı senkronize oluyor, migration gerçekten işe yarıyor, ve otomatik failover'ın olmadığını da artık varsayım değil, test ederek biliyorum. VMware serisinde her adımı önceden hazırlanmış bir manuel yönlendiriyordu; burada hem TLS hatasının kaynağını hem de HA'nın sınırlarını kendim bulmam gerekti. Daha yorucu, ama muhtemelen daha kalıcı bir öğrenme biçimi.&lt;/p&gt;

&lt;p&gt;Bir sonraki yazıda Modül 2'ye geçiyorum: quorum'u gerçekten kırıp gözlemleyeceğim, &lt;code&gt;pvecm expected&lt;/code&gt;'in riskini test edeceğim, ve QDevice'i devreye alacağım.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bu seri, resmi Proxmox VE dokümantasyonundan (pve.proxmox.com) derlenen bir müfredatı takip ediyor. Buradaki gözlemler bir homelab ortamına dayanıyor; production kararları için resmi dokümantasyon ve deneyimli sistem yöneticileri referans alınmalıdır.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Kendi Laboratuvarını Kurmak: Lab Ortamı Hazırlığı (Modül 0)</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Fri, 07 Aug 2026 06:01:43 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/kendi-laboratuvarini-kurmak-lab-ortami-hazirligi-modul-0-1idj</link>
      <guid>https://dev.to/hakanbaban53/kendi-laboratuvarini-kurmak-lab-ortami-hazirligi-modul-0-1idj</guid>
      <description>&lt;p&gt;&lt;strong&gt;Seri:&lt;/strong&gt; Proxmox VE Cluster ve Corosync | Hazırlık&lt;/p&gt;




&lt;p&gt;vSAN serisinde VMware'in hazırladığı bir HOL ortamı üzerinden ilerlemiştim; ekran adımları, önceden kurulmuş cluster'lar, tek yapmam gereken doğru yere tıklamaktı. Bu seri farklı bir yerden başlıyor: Proxmox VE için böyle hazır bir lab yok. Yani cluster'ı, ağı, hatta sanal makineleri bile sıfırdan ben kuruyorum.&lt;/p&gt;

&lt;p&gt;Bunun bir bedeli var: her adım biraz daha uzun sürüyor, hata yapma ihtimalim daha yüksek. Ama bir avantajı da var: bir HOL asla veremeyeceği kadar derin bir öğrenme sağlıyor, çünkü her kararı (kaç node, hangi ağ mimarisi, ne kadar kaynak) kendim veriyorum ve neden öyle verdiğimi de anlamak zorundayım.&lt;/p&gt;

&lt;p&gt;Bu ilk yazı bir "modül" değil, bir hazırlık; asıl Modül 1 (Cluster, Corosync, pmxcfs kavramları) bir sonraki yazıda geliyor. Ama o kavramları anlamlandırmadan önce, üzerinde çalışacağım zemini kurmam gerekiyordu; bu yazı o zemini nasıl hazırladığımı anlatıyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ortam Hakkında Bir Not
&lt;/h2&gt;

&lt;p&gt;Bu lab kendi dizüstü bilgisayarımda çalışıyor: CachyOS (Arch tabanlı), 11. nesil Intel Core i7-11800H (8 çekirdek/16 iş parçacığı), 32 GB RAM, NVMe depolama. Production bir ortam değil; nested virtualization ile tek bir fiziksel makinede dört sanal makineyi birbirine "cluster" olarak bağlıyorum. Gerçek sahada birden fazla fiziksel sunucu, ayrı switch'ler, gerçek ağ gecikmeleri olurdu; burada hepsi tek bir kutunun içinde simüle ediliyor.&lt;/p&gt;

&lt;p&gt;Bunu baştan söylüyorum çünkü Corosync'in gerçek gereksinimlerinden biri düşük gecikmeli bir ağ (5 milisaniyenin altı); nested bir ortamda bu neredeyse otomatik olarak sağlanıyor (sanal switch üzerinden iletişim gerçek bir fiziksel ağdan çok daha hızlı), bu yüzden bazı gerçek dünya sorunlarını (ağ gecikmesi, switch arızası) burada birebir yaşayamayacağım. Ama cluster mantığını, quorum'u, fencing'i anlamak için bu ortam fazlasıyla yeterli.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lab Mimarisi: Neden Nested Virtualization, Neden libvirt
&lt;/h2&gt;

&lt;p&gt;Üç ayrı fiziksel sunucum olmadığı için, tek makinede nested virtualization kullanıyorum: fiziksel makinede QEMU/KVM çalışıyor, bunun üzerinde dört sanal makine var, bu sanal makinelerin üçü Proxmox VE (cluster node'ları), biri de QDevice için minimal bir Debian.&lt;/p&gt;

&lt;p&gt;Bunu iki şekilde yapabilirdim: Proxmox VE'yi fiziksel makineye kurup içine nested Proxmox VM'leri koymak (bu, topluluk kaynaklarında sık görülen bir yöntem), ya da doğrudan Linux masaüstümde çalışan libvirt/QEMU'yu kullanmak. İkincisini seçtim, çünkü zaten kurulu ve çalışır durumdaydı; fazladan bir sanallaştırma katmanı eklemek (host'ta Proxmox, onun içinde nested Proxmox'lar) gereksiz karmaşıklık olurdu. Masaüstümü de kullanmaya devam edebiliyorum, bu da pratik bir avantaj.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nested Virtualization Kontrolü
&lt;/h3&gt;

&lt;p&gt;Bunu kurmadan önce doğrulamam gereken bir şey vardı: fiziksel CPU'nun nested virtualization'ı destekleyip desteklemediği. Bu, "VM içindeki VM'in de donanım hızlandırmalı çalışması" anlamına geliyor; olmazsa Proxmox node'larının içine koyacağım test VM'leri (ileride HA testleri için) çok yavaş çalışır.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/module/kvm_intel/parameters/nested
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fklg0ch5rxbu8oglrx7mx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fklg0ch5rxbu8oglrx7mx.png" alt="Terminal görüntüsü: nested virtualization kontrolü, çıktı Y" width="469" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Çıktı &lt;code&gt;Y&lt;/code&gt; geldi. Bu, i7-11800H'nin (Tiger Lake mimarisi) nested virtualization'ı sorunsuz desteklediğini doğruladı.&lt;/p&gt;




&lt;h2&gt;
  
  
  Kaynak Planlaması
&lt;/h2&gt;

&lt;p&gt;Donanımı (8 çekirdek/16 iş parçacığı, 32 GB RAM, NVMe üzerinde ~419 GB boş alan) dört VM'e nasıl paylaştıracağıma karar vermem gerekiyordu. Amaç, host masaüstünün (GNOME) rahatça çalışmaya devam etmesi, aynı zamanda cluster node'larının gerçekçi bir şekilde test edilebilmesiydi.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;VM&lt;/th&gt;
&lt;th&gt;vCPU&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;th&gt;Disk (qcow2, thin)&lt;/th&gt;
&lt;th&gt;Rol&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;pve-a&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;6 GB&lt;/td&gt;
&lt;td&gt;40 GB&lt;/td&gt;
&lt;td&gt;Cluster node 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pve-b&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;6 GB&lt;/td&gt;
&lt;td&gt;40 GB&lt;/td&gt;
&lt;td&gt;Cluster node 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pve-c&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;6 GB&lt;/td&gt;
&lt;td&gt;40 GB&lt;/td&gt;
&lt;td&gt;Cluster node 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qdevice-1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;8 GB&lt;/td&gt;
&lt;td&gt;QDevice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Toplam&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;19 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;128 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Bu paylaşım, 16 iş parçacığından 7'sini, 32 GB RAM'in 19 GB'ını kullanıyor; host için 9 iş parçacığı ve ~13 GB rahatça kalıyor. Disk thin-provisioned (qcow2) olduğu için 128 GB tahsis etsem bile gerçek kullanım başlangıçta çok daha az.&lt;/p&gt;

&lt;p&gt;Üç cluster node'una eşit kaynak verdim (2 vCPU / 6 GB); QDevice'e ise çok daha az (1 vCPU / 1 GB), çünkü QDevice'in işi ağır değil, sadece bir oy sağlamak.&lt;/p&gt;




&lt;h2&gt;
  
  
  ISO'ları İndirmek ve Doğrulamak
&lt;/h2&gt;

&lt;p&gt;Proxmox VE 9.2 ve QDevice için minimal bir Debian 13 (Trixie) ISO'su indirdim. Burada bir adımı atlamamak önemli: checksum doğrulaması.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"65273beed27b2df543b68b65630ba525cfbad8df2b12035732b2dff87d6664e7  debian-13.6.0-amd64-netinst.iso"&lt;/span&gt; | &lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"4e88fe416df9b527624a175f24c9aa07c714d3332afb1ee3dbf3879573ef2c6c  proxmox-ve_9.2-1.iso"&lt;/span&gt; | &lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft2v5wwmsxf31wf3sfzds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft2v5wwmsxf31wf3sfzds.png" alt="Checksum doğrulaması" width="728" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bu adımı bir lab ortamında bile atlamamaya çalışıyorum; alışkanlık, üretim ortamında da aynı disiplini otomatik hale getiriyor. Bozuk bir indirme, kurulumun ortasında garip hatalar olarak geri dönüp saatler kaybettirebiliyor; iki saniyelik bir kontrol bunun önüne geçiyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ağ Mimarisi: NAT ve İzole Corosync Ağı
&lt;/h2&gt;

&lt;p&gt;Gerçek bir Proxmox cluster kurulumunda Corosync trafiğinin ayrı, adanmış bir ağda olması öneriliyor; VM trafiği veya storage trafiğiyle paylaşılan bir ağda gecikme dalgalanmaları Corosync'i etkileyebiliyor. Bunu nested lab'ımda da simüle etmek istedim, bu yüzden iki ayrı sanal ağ kurdum:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;default&lt;/code&gt; ağı:&lt;/strong&gt; libvirt'ün standart NAT ağı (&lt;code&gt;192.168.122.0/24&lt;/code&gt;). İnternete çıkış (apt update, paket indirme) ve Proxmox web arayüzüne host'tan erişim için kullanılıyor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;corosync-net&lt;/code&gt; ağı:&lt;/strong&gt; Kendi tanımladığım, izole bir ağ (&lt;code&gt;10.10.10.0/24&lt;/code&gt;). Dışarıya çıkışı yok; sadece bu ağa bağlı VM'ler birbirini görebiliyor. Bunu, gerçek sahadaki "Corosync için adanmış NIC ve switch" pratiğinin bir simülasyonu olarak düşünebilirsin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;network&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;corosync-net&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;bridge&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'virbr-coro'&lt;/span&gt; &lt;span class="na"&gt;stp=&lt;/span&gt;&lt;span class="s"&gt;'on'&lt;/span&gt; &lt;span class="na"&gt;delay=&lt;/span&gt;&lt;span class="s"&gt;'0'&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ip&lt;/span&gt; &lt;span class="na"&gt;address=&lt;/span&gt;&lt;span class="s"&gt;'10.10.10.1'&lt;/span&gt; &lt;span class="na"&gt;netmask=&lt;/span&gt;&lt;span class="s"&gt;'255.255.255.0'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dhcp&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;range&lt;/span&gt; &lt;span class="na"&gt;start=&lt;/span&gt;&lt;span class="s"&gt;'10.10.10.10'&lt;/span&gt; &lt;span class="na"&gt;end=&lt;/span&gt;&lt;span class="s"&gt;'10.10.10.100'&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dhcp&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ip&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/network&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Her VM'e iki ağ arayüzü atadım: biri &lt;code&gt;default&lt;/code&gt;'a, biri &lt;code&gt;corosync-net&lt;/code&gt;'e. Proxmox kurulum sihirbazı sırasında sadece birinci arayüzü (default/NAT) yapılandıracağım; ikinci arayüzü (corosync-net) kurulumdan sonra Proxmox web arayüzünden elle ayarlayacağım. Bu ayrım bilinçli: yönetim/internet trafiği ile cluster'ın kendi iç iletişimini daha kurulum aşamasında birbirinden ayırmış oluyorum.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;corosync-net&lt;/code&gt;'i bilinçli olarak XML dosyası yazıp &lt;code&gt;virsh net-define&lt;/code&gt; ile tanımladım; virt-manager'ın "Create Virtual Network" sihirbazı da aynı işi yapardı, ama XML'i elle yazmak beni bridge adı, IP aralığı ve DHCP range gibi alanların her birini tek tek düşünmeye zorladı. Sihirbazda "ileri, ileri, bitir" derken çoğu ayarı görmeden geçebiliyorsun; XML'de her satırı kendin yazdığın için ne yaptığını daha net biliyorsun.&lt;/p&gt;

&lt;p&gt;Burada küçük ama can sıkıcı bir ayrıntıyla karşılaştım: &lt;code&gt;virsh&lt;/code&gt;'i normal kullanıcı olarak çalıştırdığımda, varsayılan olarak &lt;code&gt;qemu:///session&lt;/code&gt;'a bağlanıyor; bu, kullanıcıya özel, izole bir libvirt oturumu ve &lt;code&gt;default&lt;/code&gt; ağı dahil hiçbir sistem-geneli kaynağı görmüyor. Oysa virt-manager'ın oluşturduğu VM'ler ve &lt;code&gt;default&lt;/code&gt; NAT ağı &lt;code&gt;qemu:///system&lt;/code&gt; altında yaşıyor. &lt;code&gt;virsh net-define&lt;/code&gt;'ı &lt;code&gt;sudo&lt;/code&gt; olmadan çalıştırınca ağı yanlış tarafa (session) tanımlamış oldum; sonuç olarak &lt;code&gt;corosync-net&lt;/code&gt;, VM'lerin ağ listesinde hiç görünmedi. Çözüm basitti: komutu &lt;code&gt;sudo virsh net-define ...&lt;/code&gt; ile, yani sistem bağlantısı üzerinden çalıştırmak. Aynı ayrım &lt;code&gt;pool-define-as&lt;/code&gt; ve diğer &lt;code&gt;virsh&lt;/code&gt; komutları için de geçerli; hepsini &lt;code&gt;sudo&lt;/code&gt; ile ya da &lt;code&gt;-c qemu:///system&lt;/code&gt; ile çalıştırmak gerekiyor, aksi halde sessizce yanlış (görünmez) bir yere yazıyorsunuz.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxwst5e7xns6djjlmt9nf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxwst5e7xns6djjlmt9nf.png" alt="virsh net-list çıktısı - default ve corosync-net ağları" width="526" height="117"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Storage Pool ve VM'ler: Geri Kalanı Arayüzden
&lt;/h2&gt;

&lt;p&gt;Ağı XML ile tanımladıktan sonra, storage pool'u ve dört VM'i virt-manager'ın grafik arayüzünden oluşturdum. "Create a new virtual machine" sihirbazında sırasıyla: ISO dosyasını seçtim, bellek/vCPU değerlerini yukarıdaki tabloya göre girdim, disk boyutunu ayarladım (qcow2, thin-provisioned), ve ağ sekmesinde iki arayüzü de (&lt;code&gt;default&lt;/code&gt; ve &lt;code&gt;corosync-net&lt;/code&gt;) ekledim. CPU modeli için "Copy host CPU configuration" seçeneğini işaretledim; bu, host-passthrough ile aynı işi görüyor, fiziksel CPU'nun VMX özelliğini VM'e taşıyor ve nested virtualization zincirinin ikinci halkasını tamamlıyor.&lt;/p&gt;

&lt;p&gt;Aynı sihirbazı &lt;code&gt;pve-a&lt;/code&gt;, &lt;code&gt;pve-b&lt;/code&gt;, &lt;code&gt;pve-c&lt;/code&gt; için üç kez, &lt;code&gt;qdevice-1&lt;/code&gt; için çok daha hafif kaynaklarla bir kez daha çalıştırdım.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fetnk3gzgnvriesxwqi25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fetnk3gzgnvriesxwqi25.png" alt="virt-manager - dört VM'in listesi, kurulum bekliyor durumunda" width="755" height="391"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0s8num7u6u8myb1pw40a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0s8num7u6u8myb1pw40a.png" alt="VM oluşturma sihirbazı - CPU sekmesi, Copy host CPU configuration seçili" width="800" height="767"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Kurulumu Tamamlamak: Proxmox VE ve Debian
&lt;/h2&gt;

&lt;p&gt;VM'leri oluşturup ISO dosyalarını bağladıktan sonra kurulumları doğrudan &lt;strong&gt;virt-manager&lt;/strong&gt; konsolundan tamamladım. İlk aşamada her sanal makineye yalnızca &lt;strong&gt;Management (NAT)&lt;/strong&gt; ağı bağlıydı. Böylece Proxmox VE ve Debian kurulumları herhangi bir ek ağ yapılandırmasına ihtiyaç duymadan tamamlanabildi.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pve-a&lt;/code&gt;, &lt;code&gt;pve-b&lt;/code&gt; ve &lt;code&gt;pve-c&lt;/code&gt; için Proxmox VE kurulumu standart sihirbaz üzerinden ilerledi. Hedef disk seçimi (tek bir VirtIO disk), ülke ve saat dilimi, root parolası ve son olarak &lt;strong&gt;Management Network Configuration&lt;/strong&gt; ekranında ilk ağ arayüzüne yönetim IP adreslerini atadım.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;qdevice-1&lt;/code&gt; için ise masaüstü ortamı kurmadan, yalnızca &lt;strong&gt;SSH server&lt;/strong&gt; ve &lt;strong&gt;standard system utilities&lt;/strong&gt; paketlerini seçerek minimal bir Debian 13 kurulumu yaptım.&lt;/p&gt;

&lt;p&gt;Kurulum tamamlandıktan sonra artık virt-manager konsoluna ihtiyaç kalmadı. Bundan sonraki tüm yapılandırmayı kendi terminalim üzerinden &lt;strong&gt;SSH&lt;/strong&gt; ile yaptım. Bunun iki önemli avantajı vardı:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Komutları doğrudan kopyala-yapıştır ile çalıştırabilmek,&lt;/li&gt;
&lt;li&gt;Yapılan değişiklikleri daha rahat takip edip gerektiğinde tekrar edebilmek.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Proxmox VE'de root kullanıcısıyla SSH erişimi varsayılan olarak açık geldiği için ek bir ayar yapmam gerekmedi. Debian tarafında ise güvenlik nedeniyle root ile SSH oturumu varsayılan olarak kapalıydı. Bu nedenle &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; dosyasında aşağıdaki satırı düzenledim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PermitRootLogin yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp99xribhku7ys71ya6i3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp99xribhku7ys71ya6i3.png" alt=" " width="799" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ardından SSH servisini yeniden başlatarak root kullanıcısıyla uzaktan bağlantıyı etkinleştirdim.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl restart sshd.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;İlk kurulum tamamlandıktan sonra ikinci ağ arayüzünü (&lt;strong&gt;corosync-net&lt;/strong&gt;) yapılandırdım. Proxmox node'larında bunu &lt;code&gt;/etc/network/interfaces&lt;/code&gt; dosyası üzerinden yaptım; Debian tarafında da aynı yöntemle Corosync ağı için statik IP adresini tanımladım.&lt;/p&gt;

&lt;p&gt;Son durumda lab ortamının ağ planı şu şekilde oluştu:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Makine&lt;/th&gt;
&lt;th&gt;Management (NAT)&lt;/th&gt;
&lt;th&gt;Corosync&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;pve-a&lt;/td&gt;
&lt;td&gt;192.168.122.11&lt;/td&gt;
&lt;td&gt;10.10.10.11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pve-b&lt;/td&gt;
&lt;td&gt;192.168.122.12&lt;/td&gt;
&lt;td&gt;10.10.10.12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pve-c&lt;/td&gt;
&lt;td&gt;192.168.122.13&lt;/td&gt;
&lt;td&gt;10.10.10.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qdevice-1&lt;/td&gt;
&lt;td&gt;192.168.122.20&lt;/td&gt;
&lt;td&gt;10.10.10.20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Kurulum sırasında hostnameleri de son hâlleriyle belirledim. Bu lab'da &lt;strong&gt;&lt;code&gt;.home.arpa&lt;/code&gt;&lt;/strong&gt; alan adını kullanıyorum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pve-a.home.arpa
pve-b.home.arpa
pve-c.home.arpa
qdevice-1.home.arpa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bunun iki önemli sebebi var. Birincisi, &lt;strong&gt;&lt;code&gt;.home.arpa&lt;/code&gt;&lt;/strong&gt; RFC 8375 ile yalnızca ev ve yerel ağlar için ayrılmış özel bir alan adıdır. Gerçek internette hiçbir zaman çözümlenmez; dolayısıyla ileride gerçek bir alan adıyla çakışma riski bulunmaz. İkincisi ise sıkça kullanılan &lt;strong&gt;&lt;code&gt;.local&lt;/code&gt;&lt;/strong&gt; uzantısının mDNS (Multicast DNS) tarafından rezerve edilmiş olmasıdır. Linux sistemlerde Avahi, macOS'ta Bonjour ve diğer mDNS uygulamaları &lt;code&gt;.local&lt;/code&gt; alanını kullandığından, özellikle cluster ortamlarında isim çözümleme problemlerine yol açabilir. Bu nedenle Proxmox'un da önerdiği yaklaşımlardan biri olan &lt;strong&gt;&lt;code&gt;.home.arpa&lt;/code&gt;&lt;/strong&gt; daha doğru ve geleceğe dönük bir tercih oluyor.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Not:&lt;/strong&gt; Bu aşamadan sonra dört makine de birbirine Management ağı üzerinden SSH ile erişebilir, Corosync ağı ise tamamen hazır durumda bekliyor. Artık cluster oluşturma aşamasına geçebiliriz.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Bağlantıyı Doğrulamak
&lt;/h3&gt;

&lt;p&gt;Dört node'un birbirini gerçekten görüp görmediğini, &lt;code&gt;pve-a&lt;/code&gt;'dan her iki ağ üzerinden ping atarak test ettim:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpyg895fywj055z09n329.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpyg895fywj055z09n329.png" alt="NAT ağı üzerinden ping" width="658" height="571"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqg1s584y13lskp2fcedc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqg1s584y13lskp2fcedc.png" alt="Corosync ağı üzerinden ping" width="658" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sıfır paket kaybı, gecikmeler milisaniyenin çok altında (Corosync'in gerektirdiği 5ms sınırının çok altında, beklendiği gibi; nested bir ortamda ağ gecikmesi neredeyse ölçülemeyecek kadar düşük). Aynı testi &lt;code&gt;pve-b&lt;/code&gt; ve &lt;code&gt;pve-c&lt;/code&gt;'den de tekrarladım, sonuçlar aynı; hepsi birbirini her iki ağdan da görüyor.&lt;/p&gt;

&lt;p&gt;Bu noktada lab hazır: dört makine kurulu, isimlendirilmiş, iki ayrı ağdan birbirine ulaşabiliyor. Bir sonraki yazıda artık kuruluma değil, doğrudan kavramlara geçebiliyorum.&lt;/p&gt;

&lt;p&gt;Küçük bir not: &lt;code&gt;qdevice-1&lt;/code&gt; üzerine &lt;code&gt;corosync-qnetd&lt;/code&gt; paketini de bu aşamada kurup servisi etkinleştirdim; ileride QDevice'i devreye alacağım modülde hazır beklesin diye. Asıl QDevice kurulumu ve testleri (&lt;code&gt;pvecm qdevice setup&lt;/code&gt;, 2-node senaryosu, quorum matematiği) kendi modülünde ayrıca işlenecek; burada sadece "ortam hazır" demek istedim.&lt;/p&gt;




&lt;h2&gt;
  
  
  Genel Değerlendirme
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa5m0bw5qb24iau5pvmt1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa5m0bw5qb24iau5pvmt1.png" alt="Genel Topoloji" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bu modülde henüz tek bir Proxmox komutu çalıştırmadım; her şey "cluster'ı kuracağım ortamı hazırlamak" üzerineydi. Ama bu hazırlık aşaması, VMware serisindeki gibi hazır bir HOL kullanırken hiç düşünmek zorunda kalmadığım kararları öne çıkardı.&lt;/p&gt;

&lt;p&gt;En çok üzerinde durduğum nokta ağ mimarisiydi. VMware HOL'da Corosync'e denk bir kavram yoktu, ama vSAN serisinin Modül 4'ünde (Stretched Cluster) gördüğüm "witness trafiğini ayrı tutma" mantığıyla burada kurduğum NAT/corosync-net ayrımı aslında aynı prensibi paylaşıyor: kritik, düşük gecikmeli bir iletişim kanalını, genel amaçlı trafikten izole tutmak. Farklı ürünler, aynı temel disiplin.&lt;/p&gt;

&lt;p&gt;İkinci nokta, checksum doğrulamasını bir lab ortamında bile atlamamaktı. Küçük bir şey ama bunun bir HOL'da hiç düşünmem gereken bir adım olduğunu fark ettim; VMware zaten doğrulanmış bir ortam sunuyordu. Kendi lab'ını kurunca, bu tür "sıradan" disiplinlerin aslında ne kadar bilinçli bir seçim olduğunu görüyorsun.&lt;/p&gt;

&lt;p&gt;Üçüncü nokta, ağı XML ile tanımlarken fark ettiğim şey. Storage pool ve VM'leri virt-manager sihirbazıyla oluşturmak hızlı ve rahattı, ama tam olarak neyi neden yaptığımı hatırlamıyorum diyebilirim; sihirbaz benim adıma çoğu kararı verdi. Ağı XML ile yazarken ise her alanı (bridge adı, IP aralığı, DHCP range) kendim belirlemek zorunda kaldım; bu yüzden şu an o ağın nasıl çalıştığını çok daha net açıklayabiliyorum. Bu, aslında GUI'nin kötü olduğu anlamına gelmiyor, sadece "hızlı yapmak" ile "anlayarak yapmak" arasında küçük bir ödünleşim olduğunu gösteriyor.&lt;/p&gt;

&lt;p&gt;Bir sonraki yazıda Modül 1'e geçiyorum: lab artık tamamen hazır ve doğrulanmış durumda, sırada Cluster, Corosync ve pmxcfs kavramları, ve cluster'ı kurmadan hemen önce pmxcfs'in tek-node haldeki davranışını gözlemlemek var.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bu seri, resmi Proxmox VE dokümantasyonundan (pve.proxmox.com) derlenen bir müfredatı takip ediyor; hazır bir lab ortamı olmadığı için her adımı kendim kuruyor ve test ediyorum. Buradaki gözlemler bir homelab ortamına dayanıyor; production kararları için resmi dokümantasyon ve deneyimli sistem yöneticileri referans alınmalıdır.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>proxmox</category>
      <category>corosync</category>
    </item>
    <item>
      <title>vSAN Data Protection: Snapshot, Protection Group ve Immutability (Modül 7)</title>
      <dc:creator>Hakan İSMAİL</dc:creator>
      <pubDate>Sun, 26 Jul 2026 14:08:18 +0000</pubDate>
      <link>https://dev.to/hakanbaban53/vsan-data-protection-snapshot-protection-group-ve-immutability-modul-7-190a</link>
      <guid>https://dev.to/hakanbaban53/vsan-data-protection-snapshot-protection-group-ve-immutability-modul-7-190a</guid>
      <description>&lt;p&gt;Altı haftadır SPBM'den File Services'e kadar vSAN'ın farklı katmanlarını gezdim. Modül 7, serinin son modülü ve konusu net: Data Protection. Bu modülde snapshot almayı, VM'leri "protection group" adı verilen politikalarla korumayı, silinmiş bir VM'i geri getirmeyi ve ransomware senaryolarına karşı değiştirilemez (immutable) koruma gruplarını görüyoruz.&lt;/p&gt;

&lt;p&gt;Bu modül önceki altısına göre daha "hikaye anlatan" bir yapıya sahip. Sadece bir özelliği açıp kapatmıyoruz; bir VM oluşturup koruyoruz, siliyoruz, geri getiriyoruz, klonluyoruz. Bu akış, konuyu soyut bir özellik listesinden çıkarıp gerçek bir operasyonel senaryoya dönüştürüyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  vSAN Data Protection Nedir?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8q4gyj9dejq0ll9osdyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8q4gyj9dejq0ll9osdyh.png" alt="vSAN Data Protection" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;vSAN Data Protection (DP), vSAN 8.0 Update 3'te tanıtılan bir özellik. Amaç, VM'leri operasyonel hatalardan veya ransomware saldırılarından hızlıca kurtarabilmek; bunu vSAN cluster üzerinde yerel olarak saklanan native snapshot'larla yapıyor.&lt;/p&gt;

&lt;p&gt;Mimari olarak birkaç önemli nokta var:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Policy tabanlı "protection group" kavramıyla çalışıyor; hangi VM'lerin korunacağını, snapshot sıklığını ve saklama (retention) süresini bu gruplar üzerinden yönetiyorsunuz.&lt;/li&gt;
&lt;li&gt;Yanlışlıkla silinen VM'leri kurtarmak veya operasyonel çeviklik için VM'leri hızlıca klonlamak için kullanılabiliyor.&lt;/li&gt;
&lt;li&gt;VMware Live Cyber Recovery çözümüyle entegre olarak ransomware senaryolarından kurtarmayı da destekliyor (bu ayrı bir lisans gerektiriyor).&lt;/li&gt;
&lt;li&gt;Sadece vSAN ESA (Express Storage Architecture) ile çalışan HCI cluster'larda destekleniyor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mimari tarihçesi de ilginç: vSAN 8.0u3'te bu servis ayrı indirilebilir bir "vSAN Snapmanager Appliance" olarak geliyordu. vSAN 9.0 ile birlikte vSAN Data Protection, vSphere Replication ve VMware Live Recovery tek bir appliance'da birleşti. HOL'da bu appliance zaten kurulu ve vCenter'a kayıtlı; biz doğrudan vSphere Client üzerinden Data Protection'ı kullanıyoruz.&lt;/p&gt;

&lt;p&gt;Bir not daha: vSAN 9'da protection group'lar, vCenter'lar arası vSAN-to-vSAN replikasyon içeren VMware Live Recovery ile entegre olabiliyor. Bu modül sadece yerel snapshot'ları kapsıyor; replikasyonun aksiyonda görülmesi için ayrı bir lab (HOL-2634-03-VCF-L) öneriliyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  vSAN ESA Snapshot'ları: Temel Mekanik
&lt;/h2&gt;

&lt;p&gt;ESA, VM'in durumunu ve verisini snapshot alındığı andaki haliyle koruyan entegre, yüksek performanslı bir snapshot mekanizması sunuyor. Birkaç teknik detay:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Snapshot alındığında VM'in o anki çalışan durumu yakalanıyor; snapshot'lar "quiesced" (uygulama seviyesinde tutarlılık için duraklatılmış) değil.&lt;/li&gt;
&lt;li&gt;Snapshot'lar bireysel VM'ler üzerinde çalışıyor; her VM ayrı bir snapshot gerektiriyor.&lt;/li&gt;
&lt;li&gt;Her vSAN snapshot'ı, VM'in namespace nesnesinin ve virtual disk nesnelerinin durumunu içeriyor.&lt;/li&gt;
&lt;li&gt;Protection group'lardaki VM'lerin snapshot'ları zamanlanmış aralıklarla alınıyor ve yerel olarak vSAN datastore'da saklanıyor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bu, geleneksel vSphere snapshot mekanizmasından farklı bir zihniyetle çalışıyor; snapshot almak burada manuel bir "şimdi al" işlemi değil, bir politikanın parçası olarak zamanlanmış bir süreç.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data Protection Arayüzüne Giriş
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;cluster-esa-01a &amp;gt; Configure &amp;gt; vSAN &amp;gt; Data Protection&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Genel Bakış (Overview)
&lt;/h3&gt;

&lt;p&gt;Overview bölümünde, cluster'daki 7 VM'den 4'ünün korunduğu görülüyor. Grafik bu korumanın türünü (yerel, replikasyon veya korumasız) gösteriyor.&lt;/p&gt;

&lt;p&gt;"vSAN Snapshot Space Usage" bölümünde snapshot'ların kullandığı kapasitenin dökümü var. Burada dikkat çeken bir uyarı kutusu var: datastore kullanımı %70'i aştığında zamanlanmış snapshot'lar alınmıyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffeux7au184zz69vha2vg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffeux7au184zz69vha2vg.png" alt="Data Protection Overview - korunan VM sayısı ve Snapshot Space Usage" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bu %70 eşiği bana Modül 1'deki Reserved Capacity konseptini hatırlattı; orada da benzer bir "belirli bir doluluk oranından sonra sistem kendini korumaya alır" mantığı vardı. Burada aynı mantık, snapshot alma sürecine uygulanmış.&lt;/p&gt;

&lt;h3&gt;
  
  
  Protection Groups
&lt;/h3&gt;

&lt;p&gt;HOL ortamında iki protection group önceden tanımlı:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core PG:&lt;/strong&gt; Sadece yerel snapshot alıyor, bir VM'i aktif olarak koruyor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote Acct PG:&lt;/strong&gt; Site B'ye vSAN replikasyonu yapıyor, üç VM'i koruyor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Her ikisi de "Active" durumda, yani zamanlanmış snapshot'lar konfigüre edildiği gibi alınıyor. Core PG'nin iki yerel snapshot'ı var; en son snapshot belirli bir tarihte alınmış.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1d4nlpfxp7fmmge33h4v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1d4nlpfxp7fmmge33h4v.png" alt="Protection Groups listesi - Core PG ve Remote Acct PG detayları" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Existing VMs, Removed VMs, Incoming VMs
&lt;/h3&gt;

&lt;p&gt;VMs sekmesi, cluster'daki VM'leri üç kategoride gruplandırıyor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Existing VMs:&lt;/strong&gt; Şu anda vSAN'da aktif olarak bulunan VM'ler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Removed VMs:&lt;/strong&gt; Silinmiş ama hâlâ mevcut bir snapshot tarafından korunan VM'ler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incoming VMs:&lt;/strong&gt; Bu cluster'a replike edilmekte olan VM'ler.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Her VM için protection group'u, yerel koruma durumu, replikasyon durumu ve (varsa) hangi peer cluster'a replike edildiği gösteriliyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0up6j8j1z71zk388i9e0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0up6j8j1z71zk388i9e0.png" alt="VMs sekmesi - Existing/Removed/Incoming VM kategorileri" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Removed VMs sekmesinde &lt;code&gt;core-c&lt;/code&gt; adlı bir VM görülüyor; bu VM silinmiş ama snapshot'ı hâlâ mevcut, yani geri getirilebilir durumda.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7dqtbcglntuutb90x0wn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7dqtbcglntuutb90x0wn.png" alt="Removed VMs - core-c VM'inin görünümü" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Replication Pairs
&lt;/h3&gt;

&lt;p&gt;REPLICATION sekmesinde vSAN replikasyon sunucu çiftleri görülüyor. Site A cluster'ı (&lt;code&gt;cluster-esa-01a&lt;/code&gt;), Site B cluster'ına (&lt;code&gt;vsan-client-cluster-01b&lt;/code&gt;) eşleştirilmiş ve Remote Acct PG protection group'undaki üç VM'i replike ediyor. Bu özelliğin VMware Live Recovery lisansı gerektirdiği belirtiliyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy0m91xc52h1vzvba58hg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy0m91xc52h1vzvba58hg.png" alt="Replication Pairs - Site A'dan Site B'ye replikasyon" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bu ekran, Modül 5'te gördüğümüz Storage Cluster/Compute Cluster ayrımının farklı bir versiyonunu hatırlattı bana; orada da farklı cluster'lar birbirine bir hizmet sağlıyordu, burada ise koruma amaçlı bir replikasyon ilişkisi var.&lt;/p&gt;




&lt;h2&gt;
  
  
  Yeni Bir Protection Group Oluşturmak
&lt;/h2&gt;

&lt;p&gt;Modülün pratik kısmı burada başlıyor. Amacımız, "sales-" ön ekiyle başlayan satış uygulaması VM'lerini koruyan bir protection group oluşturmak.&lt;/p&gt;

&lt;h3&gt;
  
  
  Genel Ayarlar
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Protection Groups &amp;gt; Create Protection Group&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;İsim: &lt;code&gt;Sales Apps PG&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Protection type: üç seçenek var:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local protection and replication:&lt;/strong&gt; Yerel snapshot alıp başka bir vSAN cluster'a da replike ediyor (VMware Live Recovery lisansı gerekiyor).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local protection:&lt;/strong&gt; Sadece yerel snapshot alıyor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replication:&lt;/strong&gt; Sadece başka bir cluster'a replike ediyor (VMware Live Recovery gerekiyor).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bu HOL'da &lt;strong&gt;Local protection&lt;/strong&gt; seçiliyor.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Immutability mode kutusu şimdilik işaretsiz bırakılıyor (bu konuya modülün sonunda dönülüyor).&lt;/li&gt;
&lt;li&gt;VM seçim yöntemi olarak Dynamic VM name patterns seçiliyor.&lt;/li&gt;
&lt;li&gt;NEXT&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkkwy6lstcbprgshglfmx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkkwy6lstcbprgshglfmx.png" alt="Create Protection Group - General sayfası" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  VM İsim Deseni Eklemek
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;sales*&lt;/code&gt; deseni giriliyor (tüm satış uygulaması VM'leri &lt;code&gt;sales-xxxx-y&lt;/code&gt; adlandırma kuralını kullanıyor). Add'e tıklandığında, eşleşen &lt;code&gt;sales-app-01&lt;/code&gt;, &lt;code&gt;sales-db-01&lt;/code&gt; ve &lt;code&gt;sales-web-01&lt;/code&gt; VM'leri listede görünüyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhdv6d9rba5ltsyl92asu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhdv6d9rba5ltsyl92asu.png" alt="VM name pattern ekranı - sales* deseni ve eşleşen VM'ler" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bu dinamik isimlendirme yaklaşımı bence modülün en pratik fikri. Sabit bir VM listesi tutmak yerine bir isim deseni tanımlamak, gelecekte oluşturulacak yeni VM'lerin de otomatik olarak korunmasını sağlıyor; bunu birazdan test edeceğiz.&lt;/p&gt;

&lt;h3&gt;
  
  
  Snapshot Zamanlaması
&lt;/h3&gt;

&lt;p&gt;Burada önemli bir teknik sınır var: vSAN ESA snapshot'ları, nesne başına en fazla 200 kopyaya kadar ölçekleniyor. Snapshot sıklığı ve saklama süresi kombinasyonu bu sınırı aşarsa, protection group oluşturulmadan önce düzenlenmesi gerekiyor.&lt;/p&gt;

&lt;p&gt;Bu HOL'da:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Snapshot sıklığı: her 8 saatte bir&lt;/li&gt;
&lt;li&gt;Saklama süresi: 2 ay&lt;/li&gt;
&lt;li&gt;NEXT&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bir protection group için birden fazla snapshot zamanlaması tanımlamak da mümkün, ama bu lab'da tek bir zamanlama kullanılıyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcfb2k9xklpb33fk09mqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcfb2k9xklpb33fk09mqk.png" alt="Snapshot schedule ekranı - 8 saat sıklık, 2 ay saklama" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;200 kopya sınırını akılda tutarak hızlıca bir hesap yaptım: 8 saatte bir snapshot, günde 3 kopya demek. 2 aylık saklama süresinde bu yaklaşık 180 kopyaya denk geliyor, sınırın altında ama yakınında. Bu tür bir hesabı protection group tasarlarken önceden yapmak, ileride "limit aşıldı" hatasıyla karşılaşmamak için gerekli.&lt;/p&gt;

&lt;h3&gt;
  
  
  Review ve Oluşturma
&lt;/h3&gt;

&lt;p&gt;Review ekranında dikkat çeken bir not var: standart vSphere Client "snapshot" arayüzünün aksine, vSAN Data Protection snapshot'ları hemen oluşturmuyor, bunun yerine zamanlıyor. Ama istenirse manuel bir snapshot da alınabileceği hatırlatılıyor. CREATE ile protection group oluşturuluyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhgdb245mcadu2j5hq2pr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhgdb245mcadu2j5hq2pr.png" alt="Review sayfası - protection group özeti" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Manuel Snapshot Almak
&lt;/h2&gt;

&lt;p&gt;Protection group oluşturulduğunda henüz hiçbir snapshot yok; bu yüzden bir uyarı üçgeni ile işaretleniyor. 8 saat beklemek yerine manuel snapshot alınabiliyor.&lt;/p&gt;

&lt;p&gt;Sales Apps PG'nin üç nokta menüsünden "Take local snapshot" seçiliyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl3uidc9auu2ggtfgzyk7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl3uidc9auu2ggtfgzyk7.png" alt="Protection Groups listesi - uyarı üçgeni ve Take local snapshot seçeneği" width="799" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Snapshot ismi varsayılan olarak protection group adı, oluşturma yöntemi ve zaman damgasını içeren açıklayıcı bir isim alıyor. Saklama süresi olarak "1 gün" seçiliyor, TAKE SNAPSHOT tıklanıyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftzklu8e9zchem84fal1t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftzklu8e9zchem84fal1t.png" alt="Take Snapshot ekranı - isim ve retention seçimi" width="547" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recent Tasks panelinden işlem takip ediliyor; tamamlandığında uyarı üçgeni kayboluyor ve üç Sales VM'i için snapshot'lar görünüyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dinamik İsimlendirmeyi Test Etmek: Yeni Bir VM Oluşturmak
&lt;/h2&gt;

&lt;p&gt;Bu bölüm, dinamik isim deseninin gerçekten çalıştığını kanıtlıyor. &lt;code&gt;sales-&lt;/code&gt; ön ekiyle yeni bir VM oluşturursak, protection group'a manuel müdahale etmeden otomatik olarak dahil olması bekleniyor.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cluster-esa-01a&lt;/code&gt; üzerine sağ tık → New Virtual Machine → Create a new virtual machine. Sihirbaz adımları:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;İsim: &lt;code&gt;sales-lab&lt;/code&gt;, Sales klasörü seçili&lt;/li&gt;
&lt;li&gt;Compute resource: &lt;code&gt;cluster-esa-01a&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Storage: &lt;code&gt;vsan-esa-01a_Datastore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Compatibility: varsayılan (ESX 9.0 ve üzeri)&lt;/li&gt;
&lt;li&gt;Guest OS: Linux, VMware Photon OS (64-bit)&lt;/li&gt;
&lt;li&gt;Hardware: varsayılan ayarlar, FINISH&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fftt2tkn2goi70gy6mg3f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fftt2tkn2goi70gy6mg3f.png" alt="New Virtual Machine wizard - sales-lab VM'i oluşturma" width="800" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VM oluşturulduktan sonra vCenter inventory'de kayıtlı olduğu doğrulanıyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk6shhcnwgskzlotod45x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk6shhcnwgskzlotod45x.png" alt="cluster-esa-01a VM listesi - sales-lab VM'i" width="376" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Data Protection'a dönülüp Sales Apps PG için tekrar manuel snapshot alınıyor (yeni VM'in zamanlanmış aralığı beklemesine gerek kalmadan test etmek için). İşlem tamamlandığında dört VM'in korunduğu görülüyor: orijinal üç sales VM'i ve yeni oluşturulan &lt;code&gt;sales-lab&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Burada altı çizilen önemli nokta şu: yeni VM eklendiğinde protection group'un yeniden yapılandırılmasına gerek kalmadı, çünkü dinamik isimlendirme seçeneği kullanılmıştı. Bu, production ortamında sürekli yeni VM oluşturulan bir ortamda (örneğin bir CI/CD pipeline veya self-service VM provisioning sürecinde) manuel takip yükünü ortadan kaldıran bir tasarım.&lt;/p&gt;




&lt;h2&gt;
  
  
  VM'leri Geri Yüklemek
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mevcut Bir VM'i Önceki Bir Ana Geri Döndürmek
&lt;/h3&gt;

&lt;p&gt;Bu senaryoda &lt;code&gt;core-a&lt;/code&gt; VM'i, mevcut bir snapshot noktasına geri döndürülüyor. Önce &lt;code&gt;core-a&lt;/code&gt;'nın açık (powered on) olduğu doğrulanıyor.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;VMs &amp;gt; core-a seçili &amp;gt; Restore VM&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Data Protection burada bir uyarı veriyor: VM kapatılacak ve geri yükleme öncesinde, ileride "ileri sarma" (mevcut duruma dönme) seçeneği olabilmesi için yeni bir snapshot otomatik olarak alınacak. Mevcut snapshot'lardan biri seçilip RESTORE'a tıklanıyor.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;[Resim: Restore VM ekranı - core-a için snapshot seçimi ve uyarı]&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyo0pbxwyqhtevzkvjyft.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyo0pbxwyqhtevzkvjyft.png" alt=" " width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;İşlem tamamlandığında &lt;code&gt;core-a&lt;/code&gt;'nın kapalı (powered off) olduğu görülüyor, tıpkı uyarıldığı gibi. Snapshot sütununda snapshot sayısının 2'den 3'e çıktığı da fark ediliyor; bu, restore işleminin kendisinin de bir snapshot bıraktığını gösteriyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhsx4utznvka4x924nzy4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhsx4utznvka4x924nzy4.png" alt="Restored VM durumu - powered off ve artan snapshot sayısı" width="800" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bu davranış bana mantıklı geldi: bir restore işlemi, geri dönülemez bir işlem değil. Restore öncesi durumun da bir snapshot olarak saklanması, "yanlışlıkla yanlış snapshot'a döndüm" senaryosunda ikinci bir güvenlik ağı sağlıyor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Silinmiş Bir VM'i Geri Getirmek
&lt;/h3&gt;

&lt;p&gt;Bu, modülün en dramatik anı: tamamen silinmiş bir VM'in snapshot'tan yeniden oluşturulması.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;VMs &amp;gt; Removed VMs &amp;gt; core-c seçili &amp;gt; Restore VM&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Sihirbaz adımları:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bir snapshot seçiliyor, NEXT&lt;/li&gt;
&lt;li&gt;İsim ve klasör: varsayılan olarak orijinal isim &lt;code&gt;core-c&lt;/code&gt; korunuyor, &lt;code&gt;dc-a&lt;/code&gt; kök klasöründe kalıyor, NEXT&lt;/li&gt;
&lt;li&gt;Compute resource: varsayılan &lt;code&gt;cluster-esa-01a&lt;/code&gt;, NEXT&lt;/li&gt;
&lt;li&gt;RESTORE&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgcw659mr27nwzsy2d6fx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgcw659mr27nwzsy2d6fx.png" alt=" " width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;İşlem tamamlandığında &lt;code&gt;core-c&lt;/code&gt;, vCenter inventory'sinde tekrar görünüyor, açılmaya hazır durumda. VM'in adı "core" ile başladığı için, cluster'da kaldığı sürece otomatik olarak zaten var olan Core PG protection group'u tarafından korunmaya devam ediyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7xo3tfmowm0y99nmz8l1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7xo3tfmowm0y99nmz8l1.png" alt="vCenter inventory - geri getirilen core-c VM'i" width="384" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bu son detay dikkatimi çekti: dinamik isimlendirme kullanan bir protection group, sadece yeni oluşturulan VM'leri değil, geri getirilen VM'leri de otomatik olarak kapsıyor. İsim deseni eşleştiği sürece, VM'in "geçmişi" (silinmiş olması) önemli değil.&lt;/p&gt;




&lt;h2&gt;
  
  
  VM Klonlamak
&lt;/h2&gt;

&lt;p&gt;vSAN Data Protection, protection group'a atanmış herhangi bir VM'den hızlıca klon oluşturma imkanı da sunuyor. Bu klonlar ESA snapshot'larına dayanıyor ve linked-clone mimarisi kullanıyor; hızlı oluşturma ve yüksek performans sağlıyor, ama bunlar kalıcı VM'ler değil, araştırma veya VM içindeki belirli verileri kurtarma amaçlı geçici kopyalar olarak düşünülmeli.&lt;/p&gt;

&lt;p&gt;Önemli bir ayrım: Data Protection'ın ürettiği klonlar, vCenter'ın geleneksel "clone" işleminden farklı ve bu klonlar vSAN Data Protection tarafından korunmaya &lt;strong&gt;aday değil&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Klonlama Adımları
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;VMs &amp;gt; Existing VMs &amp;gt; core-a seçili &amp;gt; Clone VM&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bir snapshot seçiliyor, NEXT&lt;/li&gt;
&lt;li&gt;İsim: varsayılan &lt;code&gt;core-a-clone&lt;/code&gt;, &lt;code&gt;dc-a&lt;/code&gt; kök klasörü, NEXT&lt;/li&gt;
&lt;li&gt;Compute resource: varsayılan &lt;code&gt;cluster-esa-01a&lt;/code&gt;, NEXT&lt;/li&gt;
&lt;li&gt;CLONE&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcbl9tlppgbtxiarou0hu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcbl9tlppgbtxiarou0hu.png" alt="Clone VM sihirbazı - snapshot ve hedef seçimi" width="799" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Klon tamamlandığında &lt;code&gt;core-a-clone&lt;/code&gt; vCenter inventory'de görünüyor. VMs panelinde bu klonun durumu "Not protected" olarak işaretleniyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyz5nhhmey2i5y38r9x5i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyz5nhhmey2i5y38r9x5i.png" alt="VMs listesi - core-a-clone'un Not protected durumu" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Klonların Neden Korunmadığını Doğrulamak
&lt;/h3&gt;

&lt;p&gt;Bunu somut olarak görmek için Core PG protection group'u Edit ile açılıyor, "Individual VM selection" seçiliyor. Bu ekranda vSAN Data Protection açıkça uyarıyor: linked-clone VM'ler protection group'larda desteklenmiyor. &lt;code&gt;core-a-clone&lt;/code&gt;, aday VM listesinden zaten filtrelenmiş durumda.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foj1hlnt5m1lne3iq0fhq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foj1hlnt5m1lne3iq0fhq.png" alt="Edit Protection Group - core-a-clone'un aday listesinden filtrelenmesi" width="799" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bu kısıtlama mantıklı: bir linked-clone, zaten bir snapshot'a bağımlı bir yapı. Onu tekrar bir protection group'a dahil edip snapshot almaya çalışmak, muhtemelen mimari olarak dairesel bir bağımlılık yaratırdı. Data Protection bu senaryoyu en baştan engelliyor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Immutable Protection Groups: Ransomware'e Karşı Bir Kilit
&lt;/h2&gt;

&lt;p&gt;Modülün son ve bence en önemli konsepti burada: değiştirilemez (immutable) protection group'lar.&lt;/p&gt;

&lt;p&gt;Bir protection group immutable olarak oluşturulduğunda, oluşturulduktan sonra değiştirilemiyor veya iptal edilemiyor. Korunan VM kriterleri (dinamik isimlendirme, bireysel VM seçimi veya ikisinin kombinasyonu), snapshot zamanlaması ve saklama süresi ilk konfigürasyondan sonra sabitleniyor. Protection group da silinemiyor.&lt;/p&gt;

&lt;p&gt;Bu tasarımın amacı açık: kötü niyetli aktörlerin (bad actors) koruma planlarına müdahale etmesini veya onları silmesini engellemek. Bu, ransomware saldırısı senaryosunda özellikle anlamlı; bir saldırgan sisteme erişim sağlasa bile, snapshot'ları ve koruma politikasını silip kurtarma imkanını ortadan kaldıramıyor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Immutable Protection Group Oluşturmak
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Protection Groups &amp;gt; Create Protection Group&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;İsim: &lt;code&gt;Immutable Core Apps&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Protection type: Local protection&lt;/li&gt;
&lt;li&gt;Immutability mode kutusu işaretleniyor; bu grup oluşturulduktan sonra düzenlenemeyeceğine dair bir uyarı beliriyor&lt;/li&gt;
&lt;li&gt;VM seçimi: Dynamic VM name patterns&lt;/li&gt;
&lt;li&gt;NEXT&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi1ritgw5qukqiom89yb7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi1ritgw5qukqiom89yb7.png" alt="Create Protection Group - Immutability mode etkinleştirme ve uyarı" width="799" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VM isim deseni olarak &lt;code&gt;core*&lt;/code&gt; giriliyor. Bu desenle üç VM eşleşiyor: &lt;code&gt;core-a&lt;/code&gt;, &lt;code&gt;core-a-clone&lt;/code&gt; ve &lt;code&gt;core-c&lt;/code&gt;.&lt;br&gt;
Immutable Core Apps&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiujboefye8xy27fonl4r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiujboefye8xy27fonl4r.png" alt="VM name pattern - core* deseni ve eşleşen VM'ler" width="799" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not: Manuel burada ilginç bir detay veriyor; &lt;code&gt;core-a-clone&lt;/code&gt; isim deseni ile eşleşse de (çünkü "core" ile başlıyor), daha önce gördüğümüz gibi linked-clone VM'ler zaten protection group'lardan otomatik filtreleniyor. Yani isim eşleşmesi, filtrelemenin önüne geçmiyor.&lt;/p&gt;

&lt;p&gt;Snapshot zamanlaması olarak her 1 günde bir snapshot, 3 aylık saklama süresi belirleniyor. NEXT, sonra CREATE.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fch90slbhbdti0u63f4w6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fch90slbhbdti0u63f4w6.png" alt="Snapshot schedule - günlük snapshot, 3 aylık saklama" width="799" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Immutability'nin Etkisini Doğrulamak
&lt;/h3&gt;

&lt;p&gt;Yeni oluşturulan protection group'un üç nokta menüsüne bakıldığında, Edit, Pause ve Delete seçeneklerinin gri renkte (devre dışı) olduğu görülüyor. Bu yetenekler immutable protection group'lar için izin verilmiyor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8mp3bez4fhcsxhygtzqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8mp3bez4fhcsxhygtzqp.png" alt="Protection Groups listesi - Immutable Core Apps için devre dışı Edit/Pause/Delete" width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dinamik isimlendirme kullanıldığı için, "core" ile başlayan yeni VM'ler vSAN datastore'lara eklendiğinde otomatik olarak korunacak ve snapshot'ları, VM'ler daha sonra silinse bile 3 ay boyunca saklanacak.&lt;/p&gt;




&lt;h2&gt;
  
  
  Genel Değerlendirme
&lt;/h2&gt;

&lt;p&gt;Modül 7, serinin kapanışı için iyi bir seçim olmuş; çünkü burada gördüğümüz her şey, önceki altı modülde öğrendiğimiz kavramların üzerine inşa ediliyor. Protection group'lar SPBM'in mantığını (politika tanımla, sistem uygula) hatırlatıyor. Replication pairs, Modül 5'teki cluster-arası ilişkileri andırıyor. %70 doluluk eşiği, Modül 1'deki Reserved Capacity mantığıyla aynı prensibi paylaşıyor.&lt;/p&gt;

&lt;p&gt;Benim için en dikkat çekici üç nokta şunlardı:&lt;/p&gt;

&lt;p&gt;Birincisi, dinamik isim deseninin gücü. Bir VM isim kuralı tanımlamak, gelecekteki tüm uyumlu VM'lerin otomatik korunmasını sağlıyor; bu, "her yeni VM için manuel yapılandırma" yükünü ortadan kaldıran zarif bir tasarım. Aynı zamanda bunun disiplinli bir isimlendirme kuralı gerektirdiğini de görmek gerekiyor; "sales-" ile başlamayan bir satış VM'i sessizce korumasız kalabilir.&lt;/p&gt;

&lt;p&gt;İkincisi, restore işleminin kendisinin bir snapshot bırakması. Bu, "geri dönülemez işlem" korkusunu azaltan küçük ama önemli bir güvenlik katmanı.&lt;/p&gt;

&lt;p&gt;Üçüncüsü, Immutable Protection Groups. Bu özellik, teknik bir yetenekten çok bir tehdit modeline cevap veriyor: eğer bir saldırgan (ya da yanlışlıkla yetkili bir kullanıcı) sisteme erişim kazanırsa, koruma planının kendisi saldırı yüzeyi olmamalı. Bu, "backup'ınız varsa güvendesiniz" varsayımının yeterli olmadığı, backup'ın kendisinin de korunması gerektiği fikrinin somut bir uygulaması.&lt;/p&gt;




&lt;h2&gt;
  
  
  Seri Üzerine Kapanış Notu
&lt;/h2&gt;

&lt;p&gt;Yedi modül boyunca vSAN'ı SPBM'den başlayıp izleme, şifreleme, stretched cluster, storage cluster ayrımı, file services ve şimdi data protection'a kadar bir yolculuk olarak takip ettim. HOL formatının en büyük avantajı, hiçbir şeyi bozma korkusu olmadan bu kadar geniş bir yüzeyi tek bir ortamda gezebilmekti.&lt;/p&gt;

&lt;p&gt;Ama başta da söylediğim gibi, bunun sınırlarını da net tutmak istiyorum: gerçek bir production ortamında bu kararların her biri, burada göremediğimiz baskılarla (bütçe, mevcut donanım, ekip deneyimi, değişim yönetimi süreçleri) birlikte alınıyor. Bu seri, o kararları alacak kişi olmaya değil, o kararların neye dayandığını anlayacak bir zemine sahip olmaya yardımcı olmayı amaçladı.&lt;/p&gt;

&lt;p&gt;Serinin geri kalanını okuyanlara teşekkürler. Bir sonraki öğrenme serisinde görüşmek üzere.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bu seri VMware HOL ortamı (HOL-2634-01-VCF-L) üzerinden yürütülmektedir. Buradaki gözlemler lab bağlamında değerlendirilmeli, production ortamı kararları için mutlaka resmi VMware dokümantasyonu ve deneyimli mühendisler referans alınmalıdır.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
