xenvman: Lingkungan pengujian microservice fleksibel (dan banyak lagi)

Halo semuanya!


Saya ingin berbicara sedikit tentang proyek yang telah saya kerjakan selama enam bulan terakhir. Saya mengerjakan proyek di waktu senggang saya, tetapi motivasi untuk penciptaannya berasal dari pengamatan yang dilakukan pada pekerjaan utama.


Pada proyek yang sedang berjalan, kami menggunakan arsitektur layanan microsoft, dan salah satu masalah utama yang memanifestasikan dirinya dari waktu ke waktu dan peningkatan jumlah layanan ini adalah pengujian. Ketika suatu layanan bergantung pada lima hingga tujuh layanan lainnya, ditambah beberapa database lain (atau bahkan beberapa) untuk mem-boot, maka mengujinya dalam bentuk "langsung", bisa dikatakan, sangat merepotkan. Anda harus mengenakan mokas di semua sisi dengan sangat erat sehingga Anda bahkan tidak bisa melihat adonan itu sendiri. Baik, atau entah bagaimana mengatur lingkungan pengujian di mana semua dependensi benar-benar dapat diluncurkan.


Sebenarnya, untuk memfasilitasi opsi kedua, saya hanya duduk untuk menulis xenvman . Singkatnya, ini adalah sesuatu seperti docker-compose dan test container hybrid, hanya tanpa mengikat ke Java (atau bahasa lainnya) dan dengan kemampuan untuk secara dinamis membuat dan mengkonfigurasi lingkungan melalui API HTTP.


xenvman ditulis dalam Go dan diimplementasikan sebagai server HTTP sederhana, yang memungkinkan Anda untuk menggunakan semua fungsi yang tersedia dari bahasa apa pun yang dapat berbicara protokol ini.


Hal utama yang dapat dilakukan xenvman adalah:


  • Uraikan konten lingkungan secara fleksibel dengan skrip JavaScript sederhana
  • Buat gambar dengan cepat
  • Buat jumlah kontainer yang tepat dan gabungkan menjadi satu jaringan yang terisolasi
  • Meneruskan port internal lingkungan di luar, sehingga pengujian dapat menjangkau layanan yang diperlukan bahkan dari host lain
  • Secara dinamis mengubah komposisi lingkungan (berhenti, mulai dan tambahkan wadah baru) saat bepergian, tanpa menghentikan lingkungan kerja.

Lingkungan


Karakter utama dalam xenvman adalah lingkungan. Ini adalah gelembung terisolasi, di mana semua dependensi yang diperlukan (dikemas dalam wadah Docker) layanan Anda diluncurkan.



Gambar di atas menunjukkan server xenvman dan lingkungan aktif tempat berbagai layanan dan basis data berjalan. Setiap lingkungan dibuat langsung dari kode uji integrasi, dan akan dihapus setelah selesai.


Pola


Apa yang secara langsung merupakan bagian dari lingkungan ditentukan oleh template, yang merupakan skrip kecil di JS. xenvman memiliki juru bahasa bawaan untuk bahasa ini, dan ketika menerima permintaan untuk membuat lingkungan baru, ia hanya mengeksekusi templat yang ditentukan, masing-masing menambahkan satu atau lebih kontainer ke daftar untuk dieksekusi.


JavaScript dipilih untuk memungkinkan perubahan / penambahan templat secara dinamis tanpa harus membangun kembali server. Selain itu, template biasanya hanya menggunakan fitur dasar dan tipe data dari bahasa (ES5 tua yang baik, tidak ada DOM, React dan sihir lainnya), jadi bekerja dengan template seharusnya tidak menimbulkan kesulitan khusus bahkan bagi mereka yang tahu JS sama sekali.


Templat dapat parameterable, yaitu, kita dapat sepenuhnya mengontrol logika templat dengan melewatkan parameter tertentu dalam permintaan HTTP kami.


Buat gambar dengan cepat


Salah satu fitur xenvman yang paling nyaman, menurut pendapat saya, adalah pembuatan gambar Docker tepat dalam proses mengkonfigurasi lingkungan. Mengapa ini perlu?
Sebagai contoh, pada proyek kami, untuk mendapatkan gambar layanan, Anda perlu melakukan perubahan pada cabang terpisah, mulai dan tunggu sampai Gitlab CI mengumpulkan dan mengisi gambar.
Jika hanya satu layanan yang berubah, maka itu bisa memakan waktu 3-5 menit.


Dan jika kita secara aktif melihat fitur-fitur baru dalam layanan kami, atau mencoba memahami mengapa itu tidak berhasil, menambahkan fmt.Printf lama yang baik. fmt.Printf bolak-balik, atau sering mengubah kode, bahkan penundaan 5 menit akan sangat bagus untuk memadamkan kinerja ( kami sebagai penulis kode). Sebagai gantinya, kita bisa menambahkan semua debug yang diperlukan ke kode, kompilasi secara lokal, dan kemudian cukup melampirkan binar yang sudah selesai ke permintaan HTTP.


Setelah menerima persetujuan tersebut, templat akan mengambil binar ini dan membuat gambar sementara saat dalam perjalanan, dari mana kita sudah dapat meluncurkan wadah seolah-olah tidak ada yang terjadi.


Pada proyek kami, dalam templat utama untuk layanan, misalnya, kami memeriksa apakah binar hadir dalam parameter, dan jika demikian, kami mengumpulkan gambar saat bepergian, jika tidak, kami hanya mengunduh versi latest dari cabang dev . Kode lebih lanjut untuk membuat wadah identik untuk kedua opsi.


Contoh kecil


Untuk kejelasan, mari kita lihat contoh mikro.


Katakanlah kita menulis semacam server ajaib (sebut saja wut ), yang membutuhkan database untuk menyimpan semua yang ada di sana. Sebagai basis, kami memilih MongoDB. Oleh karena itu, untuk pengujian penuh, kita memerlukan server Mongo yang berfungsi. Tentu saja, Anda dapat menginstal dan menjalankannya secara lokal, tetapi untuk kesederhanaan dan visibilitas contoh, kami menganggap bahwa untuk beberapa alasan ini sulit dilakukan (dengan konfigurasi lain yang lebih kompleks dalam sistem nyata, ini akan lebih seperti kebenaran).


Jadi kami akan mencoba menggunakan xenvman untuk menciptakan lingkungan dengan Mongo yang berjalan dan server wut kami.


Pertama-tama, kita perlu membuat direktori dasar di mana semua templat akan disimpan:


$ mkdir xenv-templates && cd xenv-templates


Selanjutnya, buat dua templat, satu untuk Mongo, yang lain untuk server kami:


$ touch mongo.tpl.js wut.tpl.js


mongo.tpl.js


Buka mongo.tpl.js dan tulis yang berikut di sana:


 function execute(tpl, params) { var img = tpl.FetchImage(fmt("mongo:%s", params.tag)); var cont = img.NewContainer("mongo"); cont.SetLabel("mongo", "true"); cont.SetPorts(27017); cont.AddReadinessCheck("net", { "protocol": "tcp", "address": '{{.ExternalAddress}}:{{.Self.ExposedPort 27017}}' }); } 

Template harus memiliki fungsi execute () dengan dua parameter.
Yang pertama adalah turunan dari objek tpl di mana lingkungan dikonfigurasi. Argumen kedua (params) hanyalah objek JSON yang dengannya kita akan membuat parameter template kita.


Sejalan


 var img = tpl.FetchImage(fmt("mongo:%s", params.tag)); 

kami meminta xenvman untuk mengunduh gambar docker mongo:<tag> , di mana <tag> adalah versi gambar yang ingin kami gunakan. Pada prinsipnya, baris ini sama dengan perintah docker pull mongo:<tag> , dengan satu-satunya perbedaan adalah bahwa semua fungsi objek tpl pada dasarnya bersifat deklaratif, yaitu, gambar akan diunduh hanya setelah xenvman mengeksekusi semua templat yang ditentukan dalam konfigurasi lingkungan.


Setelah kita memiliki gambar, kita dapat membuat wadah:


 var cont = img.NewContainer("mongo"); 

Sekali lagi, wadah tidak akan langsung dibuat di tempat ini, kami hanya menyatakan niat untuk membuatnya, jadi untuk berbicara.


Selanjutnya, kami menaruh label pada wadah kami:


 cont.SetLabel("mongo", "true"); 

Pintasan digunakan sehingga wadah dapat menemukan satu sama lain di lingkungan, misalnya, untuk memasukkan alamat IP atau nama host dalam file konfigurasi.


Sekarang kita perlu menggantung port Mongo internal (27017). Ini mudah dilakukan seperti ini:


  cont.SetPorts(27017); 

Sebelum xenvman melaporkan keberhasilan penciptaan lingkungan, alangkah baiknya memastikan bahwa semua layanan tidak hanya berjalan, tetapi siap menerima permintaan. Xenvman telah memeriksa kesiapan untuk ini.
Tambahkan satu untuk wadah mongo kami:


  cont.AddReadinessCheck("net", { "protocol": "tcp", "address": '{{.ExternalAddress}}:{{.Self.ExposedPort 27017}}' }); 

Seperti yang bisa kita lihat, di sini di bilah alamat ada bertopik di mana nilai-nilai yang diperlukan akan diganti secara dinamis tepat sebelum peluncuran wadah.


Alih-alih {{.ExternalAddress}} alamat eksternal dari host yang menjalankan xenvman akan diganti, dan alih-alih {{.Self.ExposedPort 27017}} port eksternal yang diteruskan ke internal 27017 akan diganti.


Baca lebih lanjut tentang interpolasi di sini .


Sebagai hasil dari semua ini, kita dapat terhubung ke Mongo yang berjalan di lingkungan, tepat di luar, misalnya, dari host di mana kita menjalankan pengujian kita.


wut.tpl.js


Jadi, c, setelah berurusan dengan monga, kami akan menulis template lain untuk server wut kami.
Karena kami ingin mengumpulkan gambar saat bepergian, template akan sedikit berbeda:


 function execute(tpl, params) { var img = tpl.BuildImage("wut-image"); img.CopyDataToWorkspace("Dockerfile"); // Extract server binary var bin = type.FromBase64("binary", params.binary); img.AddFileToWorkspace("wut", bin, 0755); // Create container var cont = img.NewContainer("wut"); cont.MountData("config.toml", "/config.toml", {"interpolate": true}); cont.SetPorts(params.port); cont.AddReadinessCheck("http", { "url": fmt('http://{{.ExternalAddress}}:{{.Self.ExposedPort %v}}/', params.port), "codes": [200] }); } 

Karena kami sedang BuildImage() gambar di sini, kami menggunakan BuildImage() sebagai ganti FetchImage() :


  var img = tpl.BuildImage("wut-image"); 

Untuk merakit gambar, kita perlu beberapa file:
Dockerfile - sebenarnya petunjuk tentang cara merakit gambar
config.toml - file konfigurasi untuk server wut kami


Menggunakan metode img.CopyDataToWorkspace("Dockerfile"); kami menyalin Dockerfile dari direktori data templat ke direktori kerja sementara .


Direktori data adalah direktori tempat kita dapat menyimpan semua file yang diperlukan agar templat kita berfungsi.


Di direktori kerja sementara, kami menyalin file (menggunakan img.CopyDataToWorkspace ()) yang masuk ke dalam gambar.


Berikut ini:


  // Extract server binary var bin = type.FromBase64("binary", params.binary); img.AddFileToWorkspace("wut", bin, 0755); 

Kami melewati binar server kami secara langsung dalam parameter, dalam bentuk yang dikodekan (base64). Dan di dalam templat, kita cukup mendekodekannya, dan menyimpan string yang dihasilkan di direktori kerja sebagai file dengan nama wut .


Kemudian buat wadah dan pasang file konfigurasi ke dalamnya:


  var cont = img.NewContainer("wut"); cont.MountData("config.toml", "/config.toml", {"interpolate": true}); 

Panggilan ke MountData() berarti bahwa file config.toml dari direktori data akan dipasang di dalam wadah dengan nama /config.toml . Bendera interpolate memberitahu xenvman server bahwa semua bertopik di sana harus diganti sebelum pemasangan di file.


Seperti apa konfigurasi tersebut:


 {{with .ContainerWithLabel "mongo" "" -}} mongo = "{{.Hostname}}/wut" {{- end}} 

Di sini kita mencari wadah dengan label mongo , dan mengganti nama inangnya, apa pun itu di lingkungan ini.


Setelah substitusi, file mungkin terlihat seperti:


 mongo = โ€œmongo.0.mongo.xenv/wutโ€ 

Selanjutnya, kami kembali memposting port dan memulai pemeriksaan kesiapan, kali ini HTTP:


 cont.SetPorts(params.port); cont.AddReadinessCheck("http", { "url": fmt('http://{{.ExternalAddress}}:{{.Self.ExposedPort %v}}/', params.port), "codes": [200] }); 

Template kami siap untuk ini, dan kami dapat menggunakannya dalam kode uji integrasi:


 import "github.com/syhpoon/xenvman/pkg/client" import "github.com/syhpoon/xenvman/pkg/def" //  xenvman  cl := client.New(client.Params{}) //      env := cl.MustCreateEnv(&def.InputEnv{ Name: "wut-test", Description: "Testing Wut", // ,      Templates: []*def.Tpl{ { Tpl: "wut", Parameters: def.TplParams{ "binary": client.FileToBase64("wut"), "port": 5555, }, }, { Tpl: "mongo", Parameters: def.TplParams{"tag": โ€œlatestโ€}, }, }, }) //      defer env.Terminate() //     wut  wutCont, err := env.GetContainer("wut", 0, "wut") require.Nil(t, err) //      mongoCont, err := env.GetContainer("mongo", 0, "mongo") require.Nil(t, err) //    wutUrl := fmt.Sprintf("http://%s:%d/v1/wut/", env.ExternalAddress, wutCont.Ports[โ€œ5555โ€]) mongoUrl := fmt.Sprintf("%s:%d/wut", env.ExternalAddress, mongoCont.Ports["27017"]) // !      ,            ,   

Tampaknya menulis templat akan memakan waktu terlalu lama.
Namun, dengan desain yang tepat, ini adalah tugas satu kali, dan kemudian templat yang sama dapat digunakan kembali lebih dan lebih (dan bahkan untuk bahasa yang berbeda!) Dengan hanya menyelaraskannya dengan melewati parameter tertentu. Seperti yang Anda lihat pada contoh di atas, kode tes itu sendiri sangat sederhana, karena kami menempatkan semua sekam pada pengaturan lingkungan ke dalam templat.


Dalam contoh kecil ini, jauh dari semua fitur xenvman ditampilkan, panduan langkah demi langkah yang lebih rinci tersedia di sini.


Pelanggan


Saat ini ada klien untuk dua bahasa:


Pergi
Python


Tetapi menambahkan yang baru tidaklah sulit, karena API yang disediakan sangat, sangat sederhana.


Antarmuka web


Dalam versi 2.0.0, antarmuka web sederhana ditambahkan dengan mana Anda dapat mengelola lingkungan Anda dan melihat templat yang tersedia.





Apa perbedaan xenvman dengan komposisi buruh pelabuhan?


Tentu saja, ada banyak kesamaan, tetapi xenvman menurut saya pendekatan yang sedikit lebih fleksibel dan dinamis, dibandingkan dengan konfigurasi statis dalam file.
Berikut adalah fitur-fitur pembeda utama, menurut saya:


  • Tentu saja semua kontrol dilakukan melalui API HTTP, oleh karena itu kami dapat membuat lingkungan dari kode bahasa apa pun yang memahami HTTP
  • Karena xenvman dapat dijalankan pada host yang berbeda, kita dapat menggunakan semua fitur-fiturnya bahkan dari sebuah host di mana buruh pelabuhan tidak diinstal.
  • Kemampuan untuk secara dinamis membuat gambar dengan cepat
  • Kemampuan untuk mengubah komposisi lingkungan (menambah / menghentikan wadah) selama operasinya
  • Mengurangi kode boilerplate, meningkatkan komposisi dan kemampuan untuk menggunakan kembali kode konfigurasi melalui penggunaan templat yang dapat parameter

Referensi


Halaman proyek Github
Contoh langkah demi langkah terperinci, dalam bahasa Inggris.


Kesimpulan


Itu saja. Dalam waktu dekat saya berencana menambah peluang
panggil templat dari templat dan karenanya memungkinkan Anda untuk menggabungkannya dengan efisiensi yang lebih besar.


Saya akan mencoba menjawab pertanyaan apa pun, dan saya akan senang jika ada orang lain yang menganggap proyek ini bermanfaat.

Source: https://habr.com/ru/post/id439236/


All Articles