Kasus penggunaan mesh layanan





Approx. terjemahan. : Penulis artikel ini (Luc Perkins) adalah advokat pengembang untuk CNCF, rumah bagi proyek Open Source seperti Linkerd, SMI (Service Mesh Interface) dan Kuma (ngomong-ngomong, apakah Anda juga bertanya-tanya mengapa Istio tidak ada dalam daftar ini?. .). Dalam upaya lain untuk membawa pemahaman yang lebih baik tentang hype modis yang disebut mesh layanan ke komunitas DevOps, ia mengutip 16 kemampuan karakteristik yang disediakan oleh solusi tersebut. Jaring layanan adalah salah satu topik terpanas dalam rekayasa perangkat lunak



saat ini(dan memang demikian!). Saya menemukan teknologi ini sangat menjanjikan dan bermimpi menyaksikan adopsi yang meluas (jika masuk akal, tentu saja). Namun, itu masih dikelilingi oleh lingkaran misteri bagi kebanyakan orang. Apalagi, bahkan mereka yangSangat mengenalnya , seringkali sulit untuk merumuskan kelebihannya dan apa sebenarnya itu (termasuk hamba Anda yang rendah hati). Pada artikel ini, saya akan mencoba untuk memperbaiki situasi dengan membuat daftar berbagai skenario untuk menggunakan "jaringan layanan" *.



* Perkiraan. transl.: di sini dan selanjutnya dalam artikel, ini adalah terjemahan ("mesh layanan") yang akan digunakan untuk istilah layanan mesh yang masih baru.



Tapi pertama-tama, saya ingin memberi beberapa komentar:



  • , . , service mesh Twitter 2015 ( « ») Linkerd, - .
  • — . , , .
  • Pada saat yang sama, tidak setiap implementasi mesh layanan yang ada mendukung semua kasus penggunaan ini. Oleh karena itu, ekspresi saya seperti "layanan mesh dapat ..." harus dibaca sebagai "terpisah, dan mungkin semua implementasi layanan mesh populer dapat ...".
  • Urutan contoh tidak menjadi masalah.


Daftar pendek:



  • penemuan layanan;
  • enkripsi;
  • otentikasi dan otorisasi;
  • penyeimbang beban;
  • pemutus sirkuit;
  • penskalaan otomatis;
  • penyebaran kenari;
  • penyebaran biru-hijau;
  • cek kesehatan;
  • beban penumpahan;
  • mirroring lalu lintas;
  • isolasi;
  • pembatasan tingkat, percobaan ulang dan batas waktu;
  • telemetri;
  • audit;
  • visualisasi.


1. Penemuan Layanan



TL; DR: Sambungkan ke layanan lain di jaringan menggunakan nama sederhana.



Layanan harus dapat secara otomatis "menemukan" satu sama lain melalui nama yang sesuai - misalnya service.api.production,, pets/stagingatau cassandra. Lingkungan cloud bersifat tangguh, dan satu nama dapat menyembunyikan beberapa instance layanan. Jelas bahwa dalam situasi seperti itu, secara fisik tidak mungkin untuk melakukan hardcode semua alamat IP.



Selain itu, ketika satu layanan menemukan yang lain, itu harus dapat mengirim permintaan ke layanan ini tanpa takut bahwa mereka akan berakhir di pintu masuk contoh menganggurnya. Dengan kata lain, mesh layanan harus memantau kesehatan semua instance layanan dan selalu memperbarui daftar host.



Setiap mesh layanan mengimplementasikan mekanisme penemuan layanan secara berbeda. Saat ini, cara yang paling umum adalah mendelegasikan ke proses eksternal seperti DNS Kubernetes. Dulu, di Twitter, kami menggunakan sistem penamaan Finagle untuk tujuan ini . Selain itu, teknologi mesh layanan memungkinkan munculnya mekanisme penamaan kustom (meskipun saya belum menemukan implementasi SM dengan fungsi ini).



2. Enkripsi



TL; DR: Singkirkan lalu lintas tidak terenkripsi antar layanan dan buat prosesnya otomatis dan skalabel.



Sangat menyenangkan mengetahui bahwa penyerang tidak dapat menembus jaringan internal Anda. Firewall melakukan pekerjaan ini dengan sangat baik. Tetapi apa yang terjadi jika seorang peretas masuk? Bisakah dia melakukan apapun yang dia inginkan dengan lalu lintas layanan? Semoga itu tidak terjadi. Untuk mencegah skenario ini, Anda harus menerapkan jaringan tanpa kepercayaan di mana semua lalu lintas antar layanan dienkripsi. Kebanyakan jerat layanan modern mencapai ini melalui TLS timbal balik .(TLS bersama, mTLS). Dalam beberapa kasus, mTLS bekerja di seluruh cloud dan cluster (menurut saya komunikasi antarplanet suatu hari nanti akan diatur dengan cara yang sama).



Tentu saja, untuk mTLS, mesh layanan bersifat opsional . Setiap layanan dapat mengelola TLS-nya sendiri, tetapi ini berarti perlu menemukan cara untuk membuat sertifikat, mendistribusikannya di antara host layanan, menyertakan kode dalam aplikasi yang akan memuat sertifikat ini dari file. Ya, ingat juga untuk memperbarui sertifikat ini secara berkala. Kisi layanan mengotomatiskan mTLS menggunakan sistem seperti SPIFFE , yang pada gilirannya mengotomatiskan proses penerbitan dan rotasi sertifikat.



3. Otentikasi dan otorisasi



TL; DR: Tentukan siapa yang memulai permintaan dan tentukan apa yang diizinkan untuk mereka lakukan sebelum permintaan mencapai layanan.



Layanan sering kali ingin mengetahui siapa yang membuat permintaan (otentikasi) dan, dengan menggunakan informasi ini, memutuskan apa yang boleh dilakukan subjek (otorisasi). Dalam hal ini, kata ganti "who" dapat menyembunyikan:



  1. Layanan lainnya. Ini disebut "otentikasi rekan ". Misalnya, suatu layanan webingin mengakses suatu layanan db. Jerat layanan biasanya menyelesaikan masalah ini dengan mTLS: sertifikat dalam hal ini bertindak sebagai pengenal yang diperlukan.
  2. -. « ». , haxor69 . , , JSON Web Tokens.



    . , users, , permissions .. service mesh , .


Setelah kami menentukan dari siapa permintaan itu berasal, kami perlu menentukan subjek apa yang boleh dilakukan. Beberapa jerat layanan memungkinkan Anda untuk menentukan kebijakan dasar (tentang siapa yang dapat melakukan apa) dalam file YAML atau pada baris perintah, sementara yang lain menawarkan integrasi dengan kerangka kerja seperti Agen Kebijakan Terbuka . Tujuan utamanya adalah untuk memastikan bahwa layanan Anda menerima permintaan apa pun, dengan asumsi aman bahwa permintaan tersebut berasal dari sumber yang dapat diandalkan dan tindakan tersebut diizinkan.



4. Penyeimbangan beban



TL; DR: Mendistribusikan beban ke seluruh contoh layanan sesuai dengan pola tertentu.



Sebuah "layanan" dalam sekte layanan sering kali terdiri dari banyak contoh yang identik. Misalnya, hari ini layanan cacheterdiri dari 5 eksemplar, dan besok jumlahnya bisa bertambah menjadi 11. Permintaan yang diarahkan ke cacheharus didistribusikan sesuai dengan tujuan tertentu. Misalnya, minimalkan latensi atau maksimalkan kemungkinan mendapatkan instance yang sehat. Algoritma round-robin yang paling umum digunakan (Round-robin), tetapi ada banyak lainnya - misalnya, metode permintaan berbobot (berbobot) (Anda dapat memilih target yang disukai), annular (cincin)hashing (menggunakan hashing yang konsisten untuk host upstream) atau metode permintaan paling sedikit (instance dengan jumlah permintaan paling sedikit lebih disukai).



Penyeimbang beban klasik memiliki fitur lain, seperti caching HTTP dan perlindungan DDoS, tetapi ini tidak terlalu relevan untuk lalu lintas timur-barat. mesh). Tentu saja, Anda tidak perlu menggunakan mesh layanan untuk load balancing, tetapi ini memungkinkan Anda untuk menentukan dan mengontrol kebijakan balancing untuk setiap layanan dari lapisan manajemen terpusat, sehingga menghilangkan kebutuhan untuk memulai dan mengonfigurasi load balancer terpisah dalam tumpukan jaringan.



5. memutus sirkuit



TL; DR: Hentikan lalu lintas ke layanan bermasalah dan kendalikan kerusakan dalam skenario terburuk.



Jika, karena alasan apa pun, layanan tidak dapat menangani lalu lintas, mesh layanan menyediakan beberapa opsi untuk memecahkan masalah ini (yang lain akan dibahas di bagian terkait). Pemutusan sirkuit adalah cara paling parah untuk memutuskan layanan dari lalu lintas. Namun, itu sendiri tidak masuk akal - Anda memerlukan rencana cadangan. Dapat memberikan tekanan balik ( backpressure ) pada layanan yang mengkueri (jangan lupa untuk menyiapkan jaring layanan Anda untuk itu!), Atau, misalnya, pewarnaan status halaman dalam warna merah dan mengarahkan pengguna ke versi halaman lain dengan "paus jatuh» («Twitter sedang down ").



Jaringan layanan melakukan lebih dari sekadar menentukan kapan dan apa yang akan terjadi selanjutnya. Dalam kasus ini, "saat" dapat menyertakan kombinasi apa pun dari parameter yang ditentukan: jumlah total permintaan untuk periode tertentu, jumlah koneksi paralel, permintaan yang menunggu keputusan, percobaan ulang aktif, dll.



Anda mungkin tidak ingin menyalahgunakan pemutus sirkuit, tetapi senang mengetahui bahwa ada rencana darurat untuk keadaan darurat.



6. Skala otomatis



TL; DR: Menambah atau mengurangi jumlah instance layanan berdasarkan kriteria yang ditentukan.



Jerat layanan tidak penjadwal, sehingga mereka tidak skala pada mereka sendiri. Namun, mereka dapat memberikan informasi atas dasar perencana yang akan mengambil keputusan. Karena jerat layanan memiliki akses ke semua lalu lintas antar layanan, mereka memiliki banyak informasi tentang apa yang terjadi: layanan mana yang mengalami masalah, yang dimuat dengan sangat lemah (daya yang dialokasikan untuk mereka terbuang percuma), dll.



Misalnya, Kubernetes menskalakan layanan bergantung pada CPU dan penggunaan memori pod (lihat pembicaraan kami " Penskalaan Otomatis dan manajemen sumber daya di Kubernetes " - kira-kira. Terjemahan), tetapi jika Anda memutuskan untuk menskalakan berdasarkan metrik lain (dalam kasus kami, terkait dengan lalu lintas), Anda akan memerlukan metrik khusus. Sebuah tutorial seperti ini menunjukkan Anda bagaimana untuk melakukan hal ini dengan Utusan , Istio, dan Prometheus , tetapi proses itu sendiri cukup kompleks. Kami ingin mesh layanan menyederhanakannya hanya dengan mengizinkan kondisi seperti "tingkatkan jumlah layanan authjika jumlah permintaan yang menunggu keputusan melebihi ambang batas selama satu menit".



7. penyebaran Canary



TL; DR: Coba fitur atau versi layanan baru pada sebagian pengguna.



Katakanlah Anda sedang mengembangkan produk SaaS dan berniat untuk meluncurkan versi baru yang keren. Anda mengujinya dalam pementasan dan itu berhasil dengan baik. Tapi tetap saja, ada kekhawatiran tertentu tentang perilakunya dalam kondisi nyata. Dengan kata lain, diperlukan untuk menguji versi baru pada tugas nyata, tanpa mempertaruhkan kepercayaan pengguna. Penerapan Canary sangat bagus untuk ini. Mereka memungkinkan Anda untuk mendemonstrasikan fitur baru kepada sekelompok pengguna. Subkumpulan ini mungkin pengguna paling setia, atau mereka yang menggunakan versi gratis produk, atau mereka yang telah menyatakan keinginan untuk menjadi kelinci percobaan.



Mesh layanan melakukan ini dengan memungkinkan Anda menentukan kriteria yang menentukan siapa yang melihat versi aplikasi Anda dan merutekan lalu lintas yang sesuai. Pada saat yang sama, tidak ada yang berubah untuk layanan itu sendiri. Versi 1.0 layanan percaya bahwa semua permintaan datang dari pengguna yang harus melihatnya, dan versi 1.1 mengasumsikan hal yang sama untuk penggunanya. Sementara itu, Anda dapat mengubah persentase lalu lintas antara versi lama dan versi baru, mengalihkan semakin banyak pengguna ke versi baru, jika berfungsi dengan stabil dan "eksperimental" Anda memberikan lampu hijau.



8. Penerapan biru-hijau



TL; DR: Luncurkan fitur baru yang keren, tetapi bersiaplah untuk segera memasangnya kembali.



Tujuan penerapan biru-hijau adalah meluncurkan layanan biru baru dengan menjalankannya secara paralel dengan layanan hijau lama. Jika semuanya berjalan lancar dan layanan baru terbukti dengan baik, layanan lama dapat dimatikan secara bertahap. (Sayangnya, suatu hari nanti layanan "biru" baru ini akan mengulangi nasib layanan "hijau" dan menghilang ...) Penerapan biru-hijau berbeda dari penerapan canary karena fungsi baru mencakup semua pengguna sekaligus (bukan hanya sebagian); intinya di sini adalah untuk menyiapkan "pelabuhan cadangan" jika terjadi kesalahan.



Jerat layanan menawarkan cara yang sangat nyaman untuk menguji layanan biru dan langsung beralih ke hijau berfungsi jika terjadi masalah. Belum lagi fakta bahwa sepanjang jalan mereka memberikan banyak informasi (lihat item "Telemetri" di bawah) tentang pekerjaan "biru", yang membantu untuk memahami apakah dia siap untuk eksploitasi penuh.



Approx. terjemahan. : Baca selengkapnya tentang berbagai strategi penerapan Kubernetes (termasuk canary yang disebutkan, biru / hijau, dan lainnya) di artikel ini .



9. Pemeriksaan kesehatan



TL; DR: Melacak instance layanan mana yang aktif dan bereaksi terhadap yang tidak lagi demikian.



Sebuah pemeriksaan kesehatan membantu Anda memutuskan apakah kasus layanan siap untuk menerima dan lalu lintas proses. Misalnya, dalam kasus layanan HTTP, health check mungkin terlihat seperti permintaan GET ke titik akhir /health. Jawabannya 200 OKakan berarti bahwa instance tersebut sehat, yang lain - tidak siap untuk menerima lalu lintas. Mesh layanan memungkinkan Anda menentukan cara pemeriksaan kesehatan dan frekuensi pemeriksaan ini akan dilakukan. Informasi ini kemudian dapat digunakan untuk tujuan lain, seperti penyeimbangan beban dan pemutusan sirkuit.



Jadi, pemeriksaan kesehatan bukanlah kasus penggunaan independen, tetapi biasanya digunakan untuk mencapai tujuan lain. Selain itu, bergantung pada hasil health check, tindakan eksternal (terkait dengan target lain dari jaringan layanan) mungkin diperlukan: misalnya, menyegarkan halaman status, membuat masalah di GitHub, atau mengisi tiket JIRA. Dan mesh layanan menawarkan mekanisme praktis untuk mengotomatiskan semua ini.



10. Penumpahan beban



TL; DR: Mengalihkan lalu lintas sebagai tanggapan atas lonjakan penggunaan sementara.



Jika layanan tertentu kelebihan beban dengan lalu lintas, Anda sementara dapat mengarahkan beberapa lalu lintas ini ke lokasi lain (yaitu, "Dump", " gudang" di sana). Misalnya, ke layanan backup atau pusat data, atau ke topik Pulsar permanen . Akibatnya, layanan akan terus memproses sebagian permintaan alih-alih mogok dan berhenti memproses semuanya. Membuang beban lebih disukai daripada memutus sirkuit, tetapi masih tidak diinginkan untuk menggunakannya secara berlebihan. Ini membantu mencegah kegagalan berjenjang yang menyebabkan layanan hilir macet.



11. paralelisasi lalu lintas / mirroring



TL; DR: Mengirim satu permintaan ke beberapa lokasi sekaligus.



Terkadang perlu mengirim permintaan (atau beberapa contoh permintaan) ke beberapa layanan sekaligus. Contoh umumnya adalah mengirimkan sebagian lalu lintas produksi ke layanan pementasan. Server web produksi utama mengirimkan permintaan ke products.productiondan hanya ke layanan hilir . Dan mesh layanan dengan cerdas menyalin permintaan ini dan mengirimkannya ke products.staging, yang bahkan tidak disadari oleh server web.



Kasus penggunaan jaringan layanan terkait lainnya yang dapat diterapkan di atas paralelisasi lalu lintas adalah pengujian regresi... Ini melibatkan pengiriman permintaan yang sama ke versi layanan yang berbeda dan memeriksa apakah semua versi berperilaku dengan cara yang sama. Saya belum menemukan implementasi mesh layanan dengan sistem pengujian regresi terintegrasi seperti Diffy , tetapi idenya sendiri tampaknya menjanjikan.



12. Isolasi



TL; DR: Bagi mesh layanan Anda menjadi mesh mini.



Juga dikenal sebagai segmentasi , isolasi adalah seni membagi kisi layanan menjadi segmen yang berbeda secara logis yang tidak mengetahui satu sama lain. Isolasi mirip seperti membuat jaringan pribadi virtual. Perbedaan mendasarnya adalah Anda masih dapat memanfaatkan sepenuhnya mesh layanan (seperti penemuan layanan), tetapi dengan keamanan tambahan. Misalnya, jika penyerang berhasil menembus layanan di salah satu subnet, dia tidak akan dapat melihat layanan mana yang berjalan di subnet lain atau mencegat lalu lintas mereka.



Selain itu, manfaatnya bisa bersifat organisasi. Anda mungkin ingin membuat subnet layanan Anda berdasarkan struktur organisasi Anda dan membebaskan developer dari beban kognitif karena memikirkan seluruh mesh layanan.



13. Pembatasan tingkat, percobaan ulang dan batas waktu



TL; DR: Tidak perlu lagi menyertakan tugas mendesak untuk mengelola permintaan dalam basis kode.



Semua hal ini dapat dilihat sebagai kasus penggunaan terpisah, tetapi saya memutuskan untuk menggabungkannya karena satu kesamaan: mereka mengambil alih tugas manajemen siklus hidup permintaan yang biasanya ditangani oleh pustaka aplikasi. Jika Anda mengembangkan server web Ruby on Rails (tidak terintegrasi dengan mesh layanan) yang membuat permintaan ke layanan backend melalui gRPC, aplikasi harus memutuskan apa yang harus dilakukan jika permintaan N gagal. Anda juga harus mencari tahu berapa banyak lalu lintas layanan ini akan dapat menangani dan hardcode parameter ini menggunakan perpustakaan khusus. Selain itu, aplikasi harus memutuskan kapan harus menyerah dan membiarkan permintaan menjadi buruk (dengan batas waktu). Dan untuk mengubah salah satu parameter di atas, server web harus dihentikan, dikonfigurasi ulang, dan dimulai ulang.



Mentransfer tugas-tugas ini ke jaringan layanan tidak hanya berarti bahwa pengembang layanan tidak perlu memikirkannya, tetapi juga dapat dilihat dengan cara yang lebih global. Jika rantai layanan yang kompleks digunakan, katakanlah A -> B -> C -> D -> E, seluruh siklus hidup permintaan harus dipertimbangkan. Jika tugasnya adalah untuk memperpanjang waktu tunggu di layanan C, logis untuk melakukannya sekaligus, dan bukan di bagian: dengan memperbarui kode layanan dan menunggu permintaan tarik diterima dan sistem CI untuk menyebarkan layanan yang diperbarui.



14. Telemetri



TL; DR: Kumpulkan semua informasi yang diperlukan (dan tidak cukup) dari layanan.



Telemetri adalah istilah umum yang mencakup metrik, pelacakan terdistribusi, dan logging. Jaringan layanan menawarkan mekanisme untuk mengumpulkan dan memproses ketiga jenis data. Di sinilah segalanya menjadi sedikit kabur karena ada begitu banyak pilihan yang tersedia. Untuk mengumpulkan metrik, ada Prometheus dan alat lainnya, untuk mengumpulkan log Anda dapat menggunakan fluentd , Loki , Vector , dll. (Misalnya, ClickHouse dengan loghouse kami untuk K8s - kira-kira .transl.) , Untuk penelusuran terdistribusi ada Jaegerdll. Setiap mesh layanan mungkin mendukung beberapa alat dan bukan yang lain. Akan sangat penasaran untuk melihat apakah proyek Telemetri Terbuka dapat menyediakan beberapa konvergensi.



Dalam hal ini, keuntungan dari teknologi mesh layanan adalah bahwa kontainer sespan pada prinsipnya dapat mengumpulkan semua data di atas dari layanan mereka. Dengan kata lain, Anda mendapatkan sistem pengumpulan telemetri tunggal yang Anda inginkan, dan mesh layanan dapat memproses semua informasi ini dengan cara yang berbeda. Misalnya:



  • tail 'logs dari layanan tertentu di CLI;
  • melacak volume permintaan dari dasbor jala layanan;
  • mengumpulkan jejak terdistribusi dan mengarahkannya ke sistem seperti Jaeger.


Perhatian, penilaian subjektif: Secara umum, telemetri adalah area di mana gangguan mesh layanan yang kuat tidak diinginkan. Mengumpulkan informasi dasar dan melacak dengan cepat beberapa "metrik emas" seperti tingkat keberhasilan dan latensi tidak masalah, tapi semoga kita tidak menyaksikan tumpukan Frankenstein yang mencoba menggantikan sistem khusus, beberapa di antaranya telah terbukti sendiri dan dipelajari dengan baik.



15. Audit



TL; DR: Mereka yang melupakan pelajaran sejarah ditakdirkan untuk mengulanginya.



Auditing adalah seni mengamati peristiwa penting dalam sistem. Dalam kasus mesh layanan, ini bisa berarti melacak siapa yang membuat permintaan ke titik akhir tertentu dari layanan tertentu, atau berapa kali peristiwa keamanan terjadi dalam sebulan terakhir.



Jelas bahwa audit sangat erat kaitannya dengan telemetri. Perbedaannya adalah bahwa telemetri biasanya dikaitkan dengan hal-hal seperti kinerja dan ketepatan teknis, sedangkan audit dapat dikaitkan dengan masalah hukum dan lain yang melampaui bidang teknis yang ketat (misalnya, kepatuhan dengan persyaratan GDPR - Peraturan Umum Uni Eropa untuk perlindungan data).



16. Visualisasi



TL; DR: Hidup React.js - sumber antarmuka mewah yang tidak ada habisnya.



Mungkin ada istilah yang lebih baik, tapi saya tidak mengetahuinya. Yang saya maksud adalah representasi grafis dari mesh layanan atau beberapa komponennya. Visualisasi ini dapat mencakup indikator seperti latensi rata-rata, informasi konfigurasi sidecar, hasil health check, dan peringatan.



Bekerja di lingkungan yang berorientasi pada layanan membawa beban kognitif yang jauh lebih tinggi daripada Yang Mulia Monolith. Oleh karena itu, tekanan kognitif harus dikurangi dengan segala cara. Antarmuka grafis klise untuk mesh layanan dengan kemampuan untuk mengklik tombol dan mendapatkan hasil yang diinginkan dapat menjadi hal yang penting untuk pertumbuhan teknologi ini.



Tidak termasuk dalam daftar



Saya awalnya bermaksud untuk memasukkan beberapa kasus penggunaan lagi ke dalam daftar, tetapi kemudian memutuskan untuk tidak melakukannya. Ini dia, beserta alasan keputusan saya:



  • Pusat multi-data . Menurut pendapat saya, ini bukan kasus penggunaan sebagai area aplikasi grid layanan yang sempit dan spesifik atau beberapa set fungsi seperti penemuan layanan.
  • Masuk dan keluar . Ini adalah area terkait, tetapi saya telah membatasi diri (mungkin secara artifisial) pada skenario lalu lintas timur-barat. Ingress dan egress berhak mendapatkan artikel terpisah.


Kesimpulan



Itu saja untuk saat ini! Sekali lagi, daftar ini sangat tentatif dan kemungkinan besar tidak lengkap. Jika Anda merasa saya melewatkan sesuatu atau salah tentang sesuatu, silakan hubungi saya di Twitter ( @lucperkins ). Harap perhatikan aturan kesopanan.



PS dari penerjemah



Sebagai dasar ilustrasi judul artikel, gambar dari artikel “ Apa itu Jaring Layanan (dan kapan harus menggunakannya)? "(Oleh Gregory MacKinnon). Ini menunjukkan bagaimana beberapa fungsionalitas dari aplikasi (berwarna hijau) telah dipindahkan ke mesh layanan, yang menyediakan interkoneksi di antara mereka (berwarna biru).



Baca juga di blog kami:






All Articles