VS Code Dev Container IO Yüksekliği: executeInWSL Yapılandırma Detayları ve Kök Neden Analizi
Windows’ta VS Code’un Dev Container uzantısını kullanarak konteyner tabanlı geliştirme yaparken, bazı kullanıcılar sistemin belirgin bir şekilde takılmasına neden olan bir durumla karşılaşabilir. Görev Yöneticisi’nde Extension Host sürecinin CPU ve disk okuma IO’sunun sürekli yüksek olduğu, hatta aktif bir işlem yapılmasa bile düşmediği gözlemlenebilir. Bu makale, sorunun belirtilerinden başlayarak kök nedeni adım adım tespit edip temel çözüm yolunu bulmaya kadar olan tam inceleme sürecini kaydeder.
Sorun Belirtileri
Sistem takılması, Dev Container uzantısı ile konteynere bağlandıktan sonra gerçekleşir. Görev Yöneticisi’nde Extension Host sürecinin disk okuma IO’su ve CPU kullanımının sürekli yüksek olduğu, kullanıcı hiçbir düzenleme veya terminal işlemi yapmasa bile bu metriklerin boşta seviyelere inmediği görülür. Aşırı durumlarda, tüm Windows masaüstünün yanıt hızı etkilenir, fare imlecinde aralıklı takılmalar meydana gelir.
İnceleme Süreci
Process Monitor ile IO Kaynağını Belirleme
Sysinternals Process Monitor, bu tür sorunları araştırmanın ilk adımıdır. Procmon’u başlattıktan sonra filtre koşulunu Process Name is Code.exe veya Process Name is Extension Host olarak ayarlayın; böylece tüm ReadFile/WriteFile işlemlerinin yolları ve sıklığı gerçek zamanlı olarak izlenebilir. Filtre sonuçlarında, yolu \\pipe\ ile başlayan named pipe işlemlerinin sıklığının anormal derecede yüksek olduğu, saniyede onlarca kez gerçekleştiği görülür. Bu named pipe işlemleri, Docker CLI ile Docker daemon arasındaki iletişimi temsil eder ve Extension Host’un Docker CLI’yi sıkça çağırdığını gösterir.
VS Code Yerleşik Araçlarıyla Doğrulama
Help > Toggle Developer Tools aracılığıyla Chromium DevTools’u açın, Performance panelinde bir CPU profili kaydedin; Extension Host’un büyük bir kısmının alt süreçlerin spawn edilmesi ve stdout boru okumasına harcandığını görebilirsiniz. Output panelindeki “Dev Containers” günlük seviyesini “trace” olarak ayarlayın; tam komut çağrı dizisini görebilirsiniz: docker inspect --type container, docker version --format, docker exec, docker ps vb. Komutların tekil çağrı maliyetini issue #9194 günlük verileriyle ölçebilirsiniz: docker inspect --type container yaklaşık 1800 ms, docker version yaklaşık 620 ms sürer.
Windows Performance Analyzer ile Sistem Düzeyi IO Analizi
Daha derin bir analiz için wpr.exe -start GeneralProfile -filemode ile ETW kaydı başlatın, sorunu yeniden üretin ve wpr.exe -stop capture.etl ile kaydı durdurun. Sonuçları Windows Performance Analyzer’da açın. Disk I/O görünümü, Extension Host’un disk okuma IO’sunun ana katkıcısı olduğunu doğrular; Process Life Cycle görünümü, kısa ömürlü alt süreçlerin sıkça oluşturulup yok edildiğini gösterir; bu alt süreçler Docker CLI çağrı örnekleridir.
Kök Neden Analizi
Dev Container’ın Süreç İletişim Mimarisi
İnceleme sonuçları, Dev Container’ın çoklu süreç iletişim mimarisine işaret eder. Kullanıcı Windows’ta Dev Container uzantısı ile bir konteyner çalışma alanı açtığında, Windows ve Linux sınırını aşan çoklu süreç iletişim zinciri başlatılır.
flowchart LR
subgraph Windows["Windows Ana Makine"]
A["VS Code İstemci<br/>(Electron)"]
B["Extension Host<br/>(Node.js)"]
C["Docker CLI<br/>(Alt Süreç)"]
end
subgraph WSL2["WSL2 / Docker Desktop"]
D["Docker Daemon<br/>(dockerd)"]
end
subgraph Container["Konteyner İçinde"]
E["VS Code Sunucusu"]
F["Uzak Extension Host"]
G["Port Forwarding Süreci<br/>(Node.js)"]
end
A e1@--> B
B e2@--> C
C e3@--> D
B e4@--> E
E e5@--> F
F e6@--> G
classDef win fill:#E3F2FD,stroke:#1565C0,stroke-width:1px,color:#0D47A1;
classDef wsl fill:#FFF8E1,stroke:#EF6C00,stroke-width:1px,color:#E65100;
classDef ctr fill:#E8F5E9,stroke:#2E7D32,stroke-width:1px,color:#1B5E20;
classDef animate stroke:#EF6C00,stroke-width:2px,stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
class A,B,C win;
class D wsl;
class E,F,G ctr;
class e1,e2,e3,e4,e5,e6 animate;Yerel VS Code istemcisi (Electron), yerel Extension Host (Node.js) ile IPC üzerinden iletişim kurar. Extension Host, konteyner içindeki VS Code Sunucusu ile bağlantı kurmak zorundadır ve bu bağlantı Docker CLI aracılığıyla gerçekleşir. Konteyner içindeki VS Code Sunucusu başlatıldıktan sonra, Remote Extension Host uzantı yürütmesini devralır; port forwarding süreci ise yerel tarayıcının konteyner hizmetine erişmesi için bir TCP tüneli sağlar. Zincirdeki her bir adım, IO şişkinliğine yol açabilecek bir nokta olabilir.
Docker CLI Döngü Mekanizması
Dev Containers uzantısı, konteyner durumunu elde etmek ve sürdürmek için Docker CLI komutlarını sıkça çalıştırır. Konteyner başlatma aşamasında, uzantı sırasıyla docker inspect --type container (konteyner meta verisi alır), docker version --format (Docker daemon kullanılabilirliğini kontrol eder), docker exec (konteyner içinde ortam keşfi yapar) ve docker ps (çalışan konteynerleri listeler) komutlarını yürütür. Bu komutlar yalnızca başlangıçta bir kez çalışmaz; konteynerin yaşam döngüsü boyunca belirli bir sıklıkta tekrar tekrar çağrılır.
Her Docker CLI çağrısı yeni bir alt süreç başlatır; süreç oluşturma maliyeti, stdout boru okuması, JSON sonuç ayrıştırması gibi bir dizi IO işlemini tetikler. Varsayılan modda bu veriler Windows/WSL sınırını aşar ve WSL2’nin 9P dosya paylaşım protokolü, çok sayıda küçük dosya IO ve yüksek frekanslı kısa bağlantılarda düşük performans gösterir. Microsoft resmi belgeleri sınır ötesi dosya sistemleri işlemlerinin mümkün olduğunca kaçınılması gerektiğini önerir, ancak Dev Container mimarisi bu yükü tamamen ortadan kaldırmayı zorlaştırır.
sequenceDiagram
participant EH as Extension Host
participant CLI as Docker CLI
participant DD as Docker Daemon
EH->>CLI: spawn docker inspect
CLI->>DD: named pipe isteği
DD-->>CLI: JSON yanıtı
CLI-->>EH: stdout boru çıktısı
EH->>CLI: spawn docker version
CLI->>DD: named pipe isteği
DD-->>CLI: sürüm bilgisi
CLI-->>EH: stdout boru çıktısı
EH->>CLI: spawn docker exec
CLI->>DD: named pipe isteği
DD-->>CLI: yürütme sonucu
CLI-->>EH: stdout boru çıktısıPort Forwarding Bağlantı Sızıntısı
VS Code’un port forwarding mekanizması, her yönlendirme portu için ayrı bir Node.js alt süreci oluşturur. Bu süreçler net.createConnection ile konteyner içindeki hedef porta bağlanır ve yerel port ile konteyner portu arasında çift yönlü veri aktarır. Sorun, tarayıcı veya başka bir istemci yönlendirme portuna bağlandıktan sonra bağlantıyı kestiğinde, temizlik mantığı zamanında çalışmazsa bu yönlendirme süreçlerinin normal şekilde sonlanmaması ve hâlâ çalışmaya devam etmesidir.
microsoft/vscode-remote-release#5767 analizine göre, her sızan port forwarding süreci yaklaşık 26 MiB bellek tüketir. Birden fazla yönlendirme portu yapılandırılmış ve sık sık erişilen bir geliştirme ortamında, süreç sayısı kısa sürede normal 2’den onlara kadar çıkabilir.
const net = require('net');
process.stdin.pause();
const client = net.createConnection({ port: 36187 }, () => {
client.pipe(process.stdout);
process.stdin.pipe(client);
});
client.on('close', function (hadError) {
console.error(hadError ? 'Remote close with error' : 'Remote close');
process.exit(hadError ? 1 : 0);
});
client.on('error', function (err) {
process.stderr.write(err && (err.stack || err.message) || String(err));
});
Docker CLI’nin docker exec süreci anormal şekilde sonlandığında ve konteyner içindeki Node.js süreci hâlâ çalışıyorsa, bu yetim süreçler normal şekilde toplanamaz ve bellek artışı devam eder. Bu sorun VS Code 1.62 sürümünden itibaren kısmen düzeltilmiştir, ancak belirli ağ koşullarında hâlâ tekrar edilebilir. Dikkat edilmesi gereken nokta, port forwarding sızıntısının executeInWSL ile doğrudan bir ilişkisi olmadığı; bu, VS Code port forwarding mekanizmasının kendi yazılım hatasıdır.
Extension Host Yeniden Bağlantı Döngüsü
microsoft/vscode-remote-release#6178 kaydına göre, uzak konteyner bağlantısı ağ kesintisi veya başka bir sebeple kaybolduğunda, Extension Host’taki yeniden bağlanma mantığında bir bug vardır: bir async fonksiyon catch bloğunda kendisini yineleyerek çağırır, bu da CPU’nun boş döngüde takılmasına yol açar. Çağrı yığını, fonksiyonun processTicksAndRejections içinde sürekli döndüğünü, çıkış koşulu olmadığını gösterir.
flowchart TD
A["Bağlantı Kesildi"] e1@--> B["async Yeniden Bağlanma Fonksiyonu"]
B e2@--> C{"Bağlantı Başarılı mı?"}
C e3@-->|Evet| D["Normal Duruma Dönüş"]
C e4@-->|Hayır| E["catch Bloğu"]
E e5@--> B
classDef start fill:#FFEBEE,stroke:#C62828,stroke-width:1px,color:#B71C1C;
classDef work fill:#E8F5E9,stroke:#2E7D32,stroke-width:1px,color:#1B5E20;
classDef check fill:#FFF8E1,stroke:#EF6C00,stroke-width:1px,color:#E65100;
classDef animate stroke:#EF6C00,stroke-width:2px,stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
class A start;
class B,D work;
class C,E check;
class e1,e2,e3,e4,e5 animate;Aynı zamanda, Extension Host’un belleği dakikada yaklaşık 1 MB hızında artar; çünkü her yineleme, serbest bırakılmayan bağlamları çağrı yığınına ekler. Bu sorun Remote-Containers 0.221.0‑pre‑release sürümünde düzeltilmiştir, ancak uzantıyı zamanında güncellemeyen kullanıcılar bir ağ kesintisi (örneğin Wi‑Fi kapatmak) simüle ederek sorunu tetikleyebilir; kesinti diyalog penceresi hiç gösterilmez. Bu bağımsız bir yazılım hatasıdır, executeInWSL yapılandırmasıyla doğrudan ilişkili değildir, ancak sistem takılmasını daha da şiddetlendirir.
WSL2 Dosya Sistemi IO Büyütmesi
Docker Desktop’un WSL2 arka ucunu Windows’ta kullandığınızda, yerleşik bir dosya sistemi IO performans sorunu vardır. Windows tarafındaki VS Code uzantısı, pipe aracılığıyla WSL2 içindeki Docker daemon ile iletişim kurar; konteyner içindeki dosya sistemi işlemleri /mnt/c yolunu içeriyorsa (Windows diskine erişim), 9P dosya paylaşım protokolünün dönüşümünden geçmek zorunda kalır. 9P, çok sayıda küçük dosya IO senaryolarında yerel dosya sistemine göre belirgin şekilde yavaştır; tek bir işlem gecikmesi yerel yolun 3‑5 katına kadar çıkabilir.
Ayrıca, microsoft/vscode-remote-release#9372 raporunda, ARM Mac’te Rosetta ile x86 konteyner çalıştırıldığında VS Code Server sürecinin CPU affinity maskesinin yalnızca tek bir çekirdeği kullanacak şekilde hatalı ayarlandığı, bu yüzden nproc çıktısının gerçek fiziksel çekirdek sayısı yerine 1 döndürdüğü belirtiliyor. Bu sorun esas olarak macOS’ta görülse de, Dev Container’ın platformlar arası süreç zamanlama parametreleri kontrolündeki tutarsızlığı ortaya koyar; benzer bir durum Windows WSL2 ortamında da farklı biçimlerde ortaya çıkabilir. executeInWSL: true, Docker CLI’nin yürütülme konumunu WSL içine taşıyarak Windows/WSL sınırındaki IO sıklığını azaltır, ancak /mnt/c yoluna erişim sırasında 9P maliyetini tamamen ortadan kaldırmaz.
Çözüm Önerileri
Port Forwarding Yapılandırmasını Sadeleştirme
devcontainer.json içinde forwardPorts yapılandırmasını sadeleştirerek yalnızca gerçekten ihtiyaç duyulan portları tutun; bu, port forwarding süreç sayısını önemli ölçüde azaltır ve issue #5767 de açıklanan süreç sızıntısı riskini düşürür. Kullanılmayan Dev Container pencerelerini kapatmak da ilgili Extension Host ve port forwarding kaynaklarını hemen serbest bırakır.
Docker Desktop Optimizasyonu
Docker Desktop’un Settings > Resources panelinde CPU ve bellek tahsislerini uygun şekilde ayarlayın; böylece Docker daemon kaynak yetersizliği nedeniyle sık sık çöp toplama veya takas işlemi yapmaz. En yeni Docker Desktop sürümünü kullandığınızdan emin olun; WSL2 arka ucunun performansı her sürümde iyileştirilir. Performans gereksinimi yüksek senaryolarda, Docker Desktop sanallaştırma katmanını atlayarak WSL2 içinde doğrudan Docker Engine kurmayı düşünebilirsiniz; bu, Windows/WSL sınırındaki ek yükü tamamen ortadan kaldırır.
VS Code Yapılandırma Optimizasyonu
Gereksiz uzantıları devre dışı bırakmak, Extension Host üzerindeki yükü hafifletir; özellikle uzaktan konteyner içinde çalışan ve dosya izleme işlevi olan uzantılar (TypeScript dil hizmeti, ESLint vb.) etkili olur. settings.json içinde files.watcherExclude ile node_modules, .git, dist gibi büyük klasörleri dışlayarak dosya sistemi izleme kaynaklı IO’yu azaltabilirsiniz. extensions.autoUpdate: false ayarı, uzantıların arka planda güncellenmesi nedeniyle oluşabilecek ek ağ ve disk işlemlerini önler.
Alternatif Yaklaşımlar
Yukarıdaki önlemler yeterli gelmezse, VS Code Remote‑SSH uzantısını kullanarak WSL2’ye bağlanabilir ve Docker CLI’yi doğrudan WSL2 içinde yönetebilirsiniz. Bu yöntem, Docker CLI çağrılarını Windows/WSL sınırından WSL2 içindeki yerel iletişime dönüştürür. Bir diğer seçenek, Docker Compose ile konteyner yaşam döngüsünü yönetmek; docker compose up -d ile hizmetleri başlatıp, ardından Remote‑SSH ile konteynere bağlanarak geliştirme yapmak; böylece Dev Container uzantısının döngü mekanizmasından tamamen kaçınılır.
executeInWSL’ı Etkinleştirme (Temel Çözüm)
Yukarıdaki adımlar IO yüksekliğini bir ölçüde hafifletebilir, ancak temel neden olan Docker CLI’nin Windows/WSL sınırını aşan named pipe iletişim maliyetini ortadan kaldırmaz. dev.containers.executeInWSL tam da bu temel sorunu hedefleyen doğrudan bir çözümdür.
VS Code ekibi üyesi chrmarti, microsoft/vscode-remote-release#9194 içinde net bir açıklama yapmıştır: Bu ayar, docker komutunun Windows tarafında mı yoksa WSL içinde mi çalışacağını belirler. true olarak ayarlandığında, tüm Docker CLI çağrıları (docker inspect, docker version, docker exec, docker ps vb.) WSL içinde yürütülür, Unix socket üzerinden doğrudan Docker daemon ile iletişim kurar ve Windows/WSL sınırındaki named pipe ve 9P dönüşüm maliyetlerini atlar.
settings.json içine aşağıdaki yapılandırmayı ekleyerek etkinleştirebilirsiniz:
{
"dev.containers.executeInWSL": true
}
Bu yapılandırmanın IO performansını neden bu kadar iyileştirdiğini anlamak için iki mod arasındaki iletişim yolunu karşılaştırmak gerekir. Varsayılan modda (executeInWSL: false veya ayarsız), VS Code Extension Host Windows’ta çalışır; Docker daemon ile etkileşime geçmek için her seferinde Windows’ta docker.exe alt sürecini spawn eder. Bu alt süreç, \\pipe\ ile başlayan named pipe üzerinden Windows/WSL sınırını aşarak Docker daemon ile iletişim kurar. Bu yol, Windows süreç oluşturma, named pipe sınırötesi IO ve stdout boru geri dönüşü olmak üzere üç aşama içerir; her aşama ek gecikme ve IO maliyeti ekler. executeInWSL: true olduğunda, Extension Host wsl -d <distro> -e docker komutunu WSL içinde çalıştırır; Docker CLI WSL içinde yerel olarak çalışır ve Unix socket (yerel IPC) üzerinden aynı WSL örneğindeki Docker daemon ile iletişim kurar. Bu yol tamamen Linux çekirdek alanında gerçekleşir, named pipe sınırötesi maliyetini ortadan kaldırır.
flowchart TB
subgraph Default["Varsayılan Mod (executeInWSL: false)"]
direction LR
A1["Extension Host<br/>(Windows)"]
A2["docker.exe<br/>(Windows Alt Süreç)"]
A3["named pipe<br/>(\\\\pipe\\\\)"]
A4["Docker Daemon<br/>(WSL2)"]
A1 e1@--> A2
A2 e2@--> A3
A3 e3@--> A4
end
subgraph Optimized["Optimizasyon Modu (executeInWSL: true)"]
direction LR
B1["Extension Host<br/>(Windows)"]
B2["wsl -e docker<br/>(WSL İçinde)"]
B3["Unix socket<br/>(Yerel IPC)"]
B4["Docker Daemon<br/>(WSL2)"]
B1 e4@--> B2
B2 e5@--> B3
B3 e6@--> B4
end
Default ~~~ Optimized
classDef win fill:#E3F2FD,stroke:#1565C0,stroke-width:1px,color:#0D47A1;
classDef wsl fill:#FFF8E1,stroke:#EF6C00,stroke-width:1px,color:#E65100;
classDef fast fill:#E8F5E9,stroke:#2E7D32,stroke-width:1px,color:#1B5E20;
classDef animate stroke:#EF6C00,stroke-width:2px,stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
class A1,A2 win;
class A3,A4 wsl;
class B1 win;
class B2,B3,B4 fast;
class e1,e2,e3,e4,e5,e6 animate;İnceleme aşamasındaki ölçüm verileri bu iyileşmeyi doğrular: Varsayılan modda tek bir docker inspect --type container çağrısı yaklaşık 1800 ms, docker version yaklaşık 620 ms sürer; bu gecikmeler büyük ölçüde Windows/WSL sınırındaki named pipe iletişimi ve süreç oluşturma maliyetlerinden kaynaklanır. executeInWSL: true etkinleştirildiğinde, Docker CLI WSL içinde Unix socket üzerinden daemon ile iletişim kurar; tek bir çağrı gecikmesi milisaniyeye düşer ve birikimli etkisi belirgin şekilde iyileşir.
Bilinen Sorunlar ve Dikkat Edilmesi Gerekenler
dev.containers.executeInWSL IO performansını önemli ölçüde artırsa da, kullanım sırasında aşağıdaki bilinen sorunlara dikkat edilmelidir.
- Docker Desktop otomatik başlatma sorunu: issue #9695 raporunda,
executeInWSL: trueiken Docker Desktop’un otomatik olarak başlatılmadığı belirtilmiştir. Bu sorun Dev Containers 0.353.0‑pre‑release sürümünde düzeltilmiştir; daha yeni sürüm kullananlar artık bu problemi görmemelidir. - WSL hizmet yönlendirmesi: issue #9194 raporunda,
executeInWSLfalseolsa bile uzantının WSL’ye (display/ssh-agent/gpg-agent yönlendirmesi için) bağlanmaya çalıştığı belirtilmiştir. Bu davranış 0.337.0‑pre‑release sürümünde yenidev.containers.wslServiceForwardingayarıyla kontrol altına alınmıştır; kullanıcılar WSL hizmet yönlendirmesini ayrı olarak kapatabilir. - Rancher Desktop uyumluluğu: issue #10722 raporunda, Docker Desktop yerine Rancher Desktop kullanıldığında
executeInWSL: trueWSL1 hata mesajı tetiklediği belirtilmiştir. Bu sorun hâlâ açık durumdadır; Rancher Desktop kullanıcıları geçici olarak bu ayarı devre dışı bırakmak zorunda kalabilir. - Beklenmedik aktivasyon: issue #11005 raporunda,
executeInWSL: trueyerel Windows deposunda beklenmedik bir şekilde Dev Container başlatma sürecini tetiklediği belirtilmiştir. Bu da açık bir sorundur; etkilenen kullanıcılar bu ayarı yalnızca belirli çalışma alanlarına, global konfigürasyona değil, uygulamalı olarak sınırlamayı düşünebilir.
Özet
Windows’ta VS Code Dev Container’ın IO yüksekliği sorununu araştırmak, belirtilerden başlayarak kök nedeni adım adım tespit etmeyi gerektirir. Process Monitor, VS Code yerleşik araçları ve Windows Performance Analyzer, IO’nun ana kaynağının Docker CLI’nin named pipe iletişimi olduğunu doğrular; aynı zamanda port forwarding sızıntısı, Extension Host yeniden bağlanma döngüsü ve WSL2 dosya sistemi IO büyütmesi gibi ek faktörleri ortaya koyar. Kök neden analizi, dört üst üste gelen faktörü gösterir: Docker CLI’nin yüksek frekanslı sınırötesi sorgulaması en büyük performans darboğazıdır; port forwarding sızıntısı ve Extension Host yeniden bağlanma hataları onaylanmış yazılım hatalarıdır; WSL2 dosya sistemi IO büyütmesi platformun doğal sınırlamasıdır.
Çözümde, dev.containers.executeInWSL: true en kritik önlemdir; bu, Docker CLI’nin Windows/WSL sınırını aşan named pipe iletişim maliyetini ortadan kaldırarak IO’yu doğrudan Unix socket üzerinden yerel IPC’ye indirger. Diğer önlemler (port forwarding sadeleştirme, Docker Desktop optimizasyonu, VS Code yapılandırma iyileştirmeleri) yardımcı stratejiler olarak ek fayda sağlar. Bu sorunu yaşayan kullanıcıların, önceki inceleme sürecini izleyerek kök nedeni doğrulamaları, ardından executeInWSL: true‘ı etkinleştirip, ihtiyaç duyulursa ek optimizasyonları uygulamaları önerilir.