VS Code Dev Container IO Tinggi: Analisis Konfigurasi executeInWSL dan Akar Penyebab
Di Windows, saat menggunakan ekstensi Dev Container VS Code untuk pengembangan berbasis container, sebagian pengguna mengalami sistem yang menjadi tidak responsif. Di Task Manager, Anda dapat mengamati bahwa CPU dan disk read IO dari proses Extension Host tetap tinggi, dan tidak turun kembali ke level idle bahkan tanpa operasi aktif. Dokumen ini mencatat proses troubleshooting lengkap, dimulai dari gejala masalah, mengidentifikasi akar penyebab, hingga menemukan solusi inti.
Gejala Masalah
Kelambatan sistem terjadi setelah menghubungkan ke container menggunakan ekstensi Dev Container. Melalui Task Manager, Anda dapat mengamati bahwa disk read IO dan penggunaan CPU dari proses Extension Host tetap tinggi, dan tidak turun kembali ke level idle bahkan saat pengguna tidak melakukan editing atau operasi terminal. Dalam keadaan ekstrem, kecepatan respons desktop Windows keseluruhan terpengaruh, dan kursor mouse mengalami lag sesekali.
Proses Troubleshooting
Menggunakan Process Monitor untuk Mengidentifikasi Sumber IO
Sysinternals Process Monitor adalah alat langkah pertama untuk troubleshooting masalah seperti ini. Setelah memulai procmon, atur filter menjadi Process Name is Code.exe atau Process Name is Extension Host, Anda dapat secara real-time mengamati semua operasi ReadFile/WriteFile beserta path dan frekuensinya. Di hasil filter, operasi named pipe yang dimulai dengan \\pipe\ muncul dengan frekuensi异常 tinggi, mencapai puluhan kali per detik. Operasi named pipe ini sesuai dengan komunikasi antara Docker CLI dan Docker daemon, yang menunjukkan bahwa Extension Host sering memanggil Docker CLI.
Menggunaan Alat Bawaan VS Code untuk Konfirmasi
Buka Chromium DevTools melalui Help > Toggle Developer Tools, rekam CPU profile di panel Performance, Anda dapat melihat bahwa sebagian besar waktu Extension Host dihabiskan untuk spawn proses anak dan membaca stdout pipe. Setel level log “Dev Containers” di panel Output menjadi “trace”, Anda dapat melihat urutan panggilan perintah yang lengkap: docker inspect --type container, docker version --format, docker exec, docker ps dan perintah lainnya dieksekusi berulang-ulang. Dari data log pada issue #9194, Anda dapat menghitung overhead dari setiap panggilan: docker inspect --type container memakan waktu sekitar 1800ms, docker version memakan waktu sekitar 620ms.
Menggunaan Windows Performance Analyzer untuk Analisis IO Sistem Level
Untuk analisis yang lebih dalam, gunakan wpr.exe -start GeneralProfile -filemode untuk memulai rekaman ETW, reproduksi masalah, lalu gunakan wpr.exe -stop capture.etl untuk berhenti merekam, dan muat hasilnya di Windows Performance Analyzer. Tampilan Disk I/O mengonfirmasi bahwa Extension Host adalah kontributor utama dari disk read IO, dan tampilan Process Life Cycle menunjukkan bahwa sejumlah besar proses anak dengan masa hidup pendek dibuat dan dihancurkan berulang-ulang, proses anak ini adalah instansi pemanggilan Docker CLI.
Analisis Akar Penyebab
Arsitektur Komunikasi Proses Dev Container
Hasil troubleshooting menunjuk pada arsitektur komunikasi proses multi-proses dari Dev Container. Saat pengguna membuka workspace container melalui ekstensi Dev Container di Windows, sebenarnya sebuah jalur komunikasi multi-proses yang melintasi batas Windows dan Linux dibuat.
flowchart LR
subgraph Windows["Windows Host"]
A["VS Code Client<br/>(Electron)"]
B["Extension Host<br/>(Node.js)"]
C["Docker CLI<br/>(Subprocess)"]
end
subgraph WSL2["WSL2 / Docker Desktop"]
D["Docker Daemon<br/>(dockerd)"]
end
subgraph Container["Inside Container"]
E["VS Code Server"]
F["Remote Extension Host"]
G["Port Forwarding Process<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;Klien VS Code lokal (Electron) berkomunikasi melalui IPC dengan Extension Host lokal (Node.js). Extension Host perlu membangun koneksi dengan VS Code Server di dalam container, dan koneksi ini bergantung pada Docker CLI sebagai perantara. Setelah VS Code Server di container diaktifkan, Remote Extension Host mengambil alih eksekusi ekstensi, dan proses port forwarding memberikan TCP tunnel untuk akses browser lokal ke layanan container. Setiap tautan dalam rantai ini dapat menjadi sumber amplifikasi IO.
Mekanisme Polling Docker CLI
Ekstensi Dev Containers memanggil perintah Docker CLI secara berkala untuk mendapatkan dan mempertahankan status container. Pada fase startup container, ekstensi secara berurutan mengeksekusi docker inspect --type container untuk mendapatkan metadata container, docker version --format untuk memeriksa ketersediaan Docker daemon, docker exec untuk mengeksekusi perintah deteksi lingkungan di dalam container, dan docker ps untuk daftar container yang berjalan. Perintah ini tidak hanya dijalankan sekali saat startup, tetapi dipanggil secara berkala sepanjang masa hidup container.
Setiap pemanggilan Docker CLI memulai一个新的 proses anak, yang melibatkan overhead penciptaan proses, pembacaan stdout pipe, dan parsing JSON,以及一系列 operasi IO. Dalam mode default, data dari operasi ini harus melintasi batas Windows/WSL, dan protokol berbagi file 9P WSL2 kinerjanya buruk dalam menangani banyak kecil file IO dan k高频 koneksi pendek. Berdasarkan rekomendasi dokumen Microsoft, operasi lintas sistem file harus dihindari, tetapi arsitektur Dev Container membuatnya sulit untuk sepenuhnya menghindari overhead ini.
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 request
DD-->>CLI: JSON response
CLI-->>EH: stdout pipe output
EH->>CLI: spawn docker version
CLI->>DD: named pipe request
DD-->>CLI: version info
CLI-->>EH: stdout pipe output
EH->>CLI: spawn docker exec
CLI->>DD: named pipe request
DD-->>CLI: execution result
CLI-->>EH: stdout pipe outputKebocoran Konektivitas Port Forwarding
Mekanisme port forwarding VS Code membuat proses Node.js independen untuk setiap port forwarding. Proses ini terhubung ke port tujuan di container melalui net.createConnection, dan meneruskan data dua arah antara port lokal dan port container. Masalahnya, ketika browser atau klien lain mengakses port forwarding dan kemudian putus, jika logika pembersihan tidak tepat waktu, proses forwarding akan terus berjalan daripada keluar dengan normal.
Berdasarkan analisis microsoft/vscode-remote-release#5767, setiap proses forwarding yang bocor menghabiskan sekitar 26 MiB memori. Dalam lingkungan pengembangan dengan banyak port forwarding dan akses频繁, jumlah proses dapat meningkat dari normal 2 menjadi puluhan dalam waktu singkat. Cuplikan code berikut menunjukkan pola inti dari proses port forwarding, di mana penanganan event client.on('close') adalah kunci apakah proses dapat keluar dengan normal.
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));
});
Ketika proses docker exec Docker CLI mati secara abnormal sedangkan proses Node.js di dalam container masih berjalan, proses anak yatim ini tidak dapat dipulihkan, menyebabkan memori terus bertumbuh. Masalah ini sebagian diperbaiki sejak VS Code versi 1.62, tetapi dalam kondisi jaringan tertentu masih dapat direproduksi. Nilai tambah, kebocoran port forwarding tidak memiliki hubungan langsung dengan executeInWSL, ini adalah cacak perangkat lunak dari mekanisme port forwarding VS Code.
Loop Reconnection Extension Host
Berdasarkan catatan microsoft/vscode-remote-release#6178, ketika koneksi dengan container remote hilang karena gangguan jaringan atau alasan lain, terdapat bug pada logika reconnection Extension Host: sebuah fungsi async memanggil dirinya sendiri di dalam block catch, menyebabkan CPU berputar tanpa henti. Stack trace menunjukkan fungsi ini berulang di processTicksAndRejections, tanpa kondisi keluar.
flowchart TD
A["Connection Lost"] e1@--> B["async Reconnect Function"]
B e2@--> C{"Connection Successful?"}
C e3@-->|Yes| D["Restored"]
C e4@-->|No| E["catch Block"]
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;Sebagai tambahan, memori Extension Host tumbuh dengan kecepatan sekitar 1 MB/menit, karena setiap panggilan rekursif menumpuk konteks yang tidak dilepaskan di stack. Masalah ini diperbaiki pada versi Remote-Containers 0.221.0-pre-release, tetapi untuk pengguna yang tidak memperbarui ekstensi tepat waktu, cukup dengan mensimulasikan pemutusan jaringan (misalnya matikan WiFi) untuk memicu masalah ini, dan dialog peringatan putus k Connection tidak pernah ditampilkan. Ini adalah bug perangkat lunak independen, tidak terkait dengan konfigurasi executeInWSL, tetapi dapat memperburuk kelambatan sistem yang dirasakan pengguna.
Amplifikasi IO Lintas Sistem File WSL2
Di Windows saat menggunakan backend WSL2 Docker Desktop, terdapat masalah kinerja IO lintas sistem file yang inheren. Ekstensi VS Code di Windows berkomunikasi dengan Docker daemon di WSL2 melalui pipe, dan operasi sistem file di dalam container jika melibatkan path /mnt/c (akses ke disk Windows), perlu melalui konversi protokol berbagi file 9P. Protokol 9P WSL2 kinerjanya signifikan lebih rendah daripada sistem file asli dalam menangani banyak kecil file IO, dan latency per operasi dapat mencapai 3-5 kali dari path asli.
Selain itu, berdasarkan laporan microsoft/vscode-remote-release#9372, di ARM Mac melalui Rosetta menjalankan x86 container, affinity mask CPU dari proses VS Code Server diatur secara abnormal hanya menggunakan satu inti, menyebabkan nproc mengembalikan 1 daripada jumlah inti fisik yang sebenarnya. Meskipun masalah ini terutama muncul di platform macOS, ini mengungkapkan inkonsistensi Dev Container dalam mengontrol parameter scheduling proses di lintas platform, dan masalah serupa dapat muncul di lingkungan Windows WSL2 dalam bentuk berbeda. executeInWSL: true dengan memindahkan lokasi eksekusi Docker CLI ke dalam WSL, mengurangi frekuensi operasi IO lintas sistem file, tetapi tidak dapat sepenuhnya menghilangkan overhead 9P saat container mengakses path /mnt/c.
Solusi
Konfigurasi Port Forwarding yang Disempurnakan
Di devcontainer.json, ringkas konfigurasi forwardPorts, hanya pertahankan port yang benar-benar dibutuhkan, dapat secara signifikan mengurangi jumlah proses port forwarding, dan menurunkan risiko kebocoran proses yang dijelaskan di issue #5767. Menutup jendela Dev Container yang tidak digunakan juga dapat langsung melepaskan sumber daya Extension Host dan port forwarding.
Optimasi Docker Desktop
Di panel Settings > Resources Docker Desktop, sesuaikan alokasi CPU dan memori dengan tepat, hindari Docker daemon sering melakukan garbage回收 atau operasi swap karena kekurangan sumber pastikan menggunakan versi terbaru Docker Desktop, karena kinerja backend WSL2 meningkat di setiap versi. Untuk skenario yang menuntut kinerja tinggi, pertimbangkan untuk menginstal Docker Engine secara langsung di dalam WSL, mengelilingi lapisan virtualisasi Docker Desktop, sehingga dapat menghilangkan overhead tambahan dari batas Windows/WSL.
Optimasi Konfigurasi VS Code
Nonaktifkan ekstensi yang tidak diperlukan dapat mengurangi beban Extension Host, khususnya ekstensi yang berjalan di remote container dan memiliki fitur file watcher (seperti TypeScript language service, ESLint, dsb). Di settings.json, atur files.watcherExclude untuk mengexclude direktori besar seperti node_modules, .git, dist, dapat mengurangi IO yang dihasilkan dari pemantauan sistem file. Atur extensions.autoUpdate: false dapat menghindari operasi jaringan dan disk tambahan yang dipicu oleh pembaruan ekstensi di lingkungan container.
Alternatif Solusi
Jika langkah-langkah di atas masih tidak memenuhi kebutuhan kinerja, Anda dapat mempertimbangkan menggunakan ekstensi VS Code Remote-SSH untuk terhubung ke WSL2, dan menggunakan Docker CLI secara langsung di dalam WSL2 untuk mengelola container. Pendekatan ini mengubah panggilan Docker CLI dari komunikasi lintas batas Windows/WSL menjadi komunikasi lokal di dalam WSL2. Cara lain adalah menggunakan Docker Compose untuk mengelola siklus hidup container, dengan menghidupkan service melalui docker compose up -d, kemudian hanya menggunakan Remote-SSH untuk menghubungkan container dan mengembangkan, seluruhnya mengelilingi mekanisme polling dari ekstensi Dev Container.
Mengaktifkan executeInWSL (Solusi Inti)
Langkah-langkah di atas dapat meredam masalah IO tinggi dalam berbagai tingkatan, tetapi mereka hanya mengurangi frekuensi IO (meminimalkan port forwarding), atau hanya mengoptimalkan alokasi sumber daya (optimasi Docker Desktop), dan tidak menyentuh akar penyebat masalah: overhead komunikasi named pipe yang dihasilkan dari panggilan Docker CLI melintasi batas Windows/WSL. dev.containers.executeInWSL adalah solusi langsung yang ditujukan untuk akar penyebab ini.
Berdasarkan penjelasan jelas dari anggota tim VS Code chrmarti di microsoft/vscode-remote-release#9194, pengaturan ini menentukan apakah perintah docker dijalankan di sisi Windows atau di dalam WSL. Dengan mengaturnya menjadi true, semua panggilan Docker CLI (termasuk docker inspect, docker version, docker exec, docker ps, dsb) akan dijalankan di dalam WSL, berkomunikasi langsung dengan Docker daemon melalui Unix socket, sehingga menghindari overhead named pipe dan konversi protokol 9P di batas Windows/WSL.
Tambahkan konfigurasi berikut di settings.json untuk mengaktifkannya:
{
"dev.contains.executeInWSL": true
}
Untuk memahami mengapa pengaturan ini dapat meningkatkan kinerja IO secara signifikan, perlu dibandingkan perbedaan jalur komunikasi pada dua mode. Dalam mode default (executeInWSL: false atau tidak diatur), VS Code Extension Host berjalan di Windows, dan setiap kali perlu berinteraksi dengan Docker daemon, akan melalui spawn untuk memulai proses anak docker.exe di Windows. Proses anak ini berkomunikasi dengan Docker daemon melalui named pipe (path dimulai dengan \\pipe\) yang melintasi batas Windows/WSL. Jalur ini melibatkan tiga tahap: penciptaan proses Windows, IO named pipe lintas batas, dan pengembalian stdout pipe, dan setiap tahap menghadirkan latency dan overhead IO tambahan. Saat executeInWSL: true, Extension Host beralih ke wsl -d <distro> -e docker untuk mengeksekusi Docker CLI di dalam WSL. Docker CLI berjalan asli di dalam WSL, dan berkomunikasi dengan Docker daemon dalam instansi WSL yang sama melalui Unix socket (IPC lokal). Jalur ini sebagian besar diselesaikan di dalam kernel Linux, menghindari overhead named pipe lintas batas.
flowchart TB
subgraph Default["Default Mode (executeInWSL: false)"]
direction LR
A1["Extension Host<br/>(Windows)"]
A2["docker.exe<br/>(Windows Subprocess)"]
A3["named pipe<br/>(\\\\pipe\\\\)"]
A4["Docker Daemon<br/>(WSL2)"]
A1 e1@--> A2
A2 e2@--> A3
A3 e3@--> A4
end
subgraph Optimized["Optimized Mode (executeInWSL: true)"]
direction LR
B1["Extension Host<br/>(Windows)"]
B2["wsl -e docker<br/>(Inside WSL)"]
B3["Unix socket<br/>(Local 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;Dari data kuantitatif pada fase troubleshooting, kita dapat memverifikasi peningkatan ini: dalam mode default, satu panggilan docker inspect --type container memakan waktu sekitar 1800ms, dan docker version memakan waktu sekitar 620ms, latency ini terutama berasal dari overhead komunikasi named pipe dan penciptaan proses di batas Windows/WSL. Setelah mengaktifkan executeInWSL: true, Docker CLI berjalan di dalam WSL dan berkomunikasi dengan daemon melalui Unix socket, latency per panggilan dapat turun ke level milidetik, dan efek akumulatif dari peningkatan ini sangat signifikan.
Masalah Diketahui dan Catatan
dev.containers.executeInWSL dapat secara efektif meningkatkan kinerja IO, tetapi perlu memperhatikan masalah yang diketahui saat penggunaannya.
Masalah auto-start Docker Desktop: issue #9695 melaporkan bahwa saat executeInWSL: true, Docker Desktop tidak akan secara otomatis start. Masalah ini telah diperbaiki di versi Dev Containers 0.353.0-pre-release, pengguna dengan versi terbaru tidak lagi mengalami masalah ini.
Forwarding layanan WSL: issue #9194 melaporkan bahwa bahkan dengan executeInWSL diatur menjadi false, ekstensi masih akan mencoba menghubungkan ke WSL (untuk forwarding display/ssh-agent/gpg-agent). Perilaku ini telah dikendalikan sejak versi 0.337.0-pre-release melalui pengaturan baru dev.containers.wslServiceForwarding, pengguna dapat secara independen menonaktifkan forwarding layanan WSL.
Kompatibilitas Rancher Desktop: issue #10722 melaporkan bahwa saat menggunakan Rancher Desktop sebagai pengganti Docker Desktop, executeInWSL: true akan memicu kesalahan WSL1. Masalah ini masih dalam keadaan terbuka, pengguna Rancher Desktop mungkin perlu menonaktifkan pengaturan ini sementara.
Aktivasi tidak sengaja: issue #11005 melaporkan bahwa executeInWSL: true akan secara tidak sengaja memicu proses inisialisasi Dev Container di repository Windows lokal. Masalah ini juga masih terbuka, pengguna yang terdampak dapat mempertimbangkan untuk membatasi pengaturan ini pada workspace tertentu daripada konfigurasi global.
Kesimpulan
Untuk troubleshooting masalah IO tinggi dari VS Code Dev Container di Windows, perlu dimulai dari gejala dan secara bertahap mengidentifikasi akar penyebab. Dengan Process Monitor, Anda dapat mengonfirmasi bahwa sumber utama IO adalah komunikasi named pipe dari Docker CLI, dan dengan alat bawaan VS Code serta Windows Performance Analyzer, Anda dapat lebih menghitung frekuensi panggilan dan latency. Analisis akar penyebab mengungkapkan empat faktor yang tumpang tindih: polling Docker CLI lintas batas高频 adalah bottleneck kinerja utama, kebocoran proses port forwarding dan loop reconnection Extension Host adalah bug perangkat lunak yang telah dikonfirmasi, dan amplifikasi IO lintas sistem file WSL2 adalah batasan inheren di level platform.
Di antara solusi, dev.containers.executeInWSL: true adalah tindakan inti, yang langsung menghilangkan overhead komunikasi named pipe dari panggilan Docker CLI melintasi batas Windows/WSL, dengan memindahkan lokasi eksekusi panggilan CLI高频 dari Windows ke dalam WSL, dan menyelesaikan IPC lokal melalui Unix socket. Solusi lainnya (meminimalkan port forwarding, optimasi Docker Desktop, optimasi konfigurasi VS Code) sebagai tindakan pendukung, meredam dampak dari faktor lain dalam berbagai tingkatan. Untuk pengguna yang terganggu oleh masalah ini, disarankan untuk mengikuti proses troubleshooting dalam dokumen ini untuk mengonfirmasi akar penyebab terlebih dahulu, kemudian memberikan prioritas pada mengaktifkan executeInWSL: true, dan kemudian memilih strategi optimasi pendukung sesuai dengan skenario nyata.
</end markdown>