Ada
banyak artikel di jala layanan
di Internet, dan ini artikel lain. Hore! Tapi mengapa? Lalu, apa yang ingin saya sampaikan pendapat saya adalah bahwa akan lebih baik untuk memiliki jerat layanan muncul 10 tahun yang lalu, sebelum munculnya platform wadah seperti Docker dan Kubernetes. Saya tidak mengklaim bahwa sudut pandang saya lebih baik atau lebih buruk daripada yang lain, tetapi karena jerat layanan adalah hewan yang cukup kompleks, banyaknya sudut pandang akan membantu untuk lebih memahami mereka.
Saya akan berbicara tentang platform dotCloud, yang dibangun di lebih dari seratus layanan microser dan mendukung ribuan aplikasi dalam wadah. Saya akan menjelaskan masalah yang kami temui selama pengembangan dan peluncurannya, dan bagaimana jerat layanan dapat membantu (atau tidak bisa).
Sejarah dotCloud
Saya sudah menulis tentang sejarah dotCloud dan pilihan arsitektur untuk platform ini, tetapi berbicara sedikit tentang tingkat jaringan. Jika Anda tidak ingin membaca
artikel sebelumnya tentang dotCloud, berikut ringkasan singkatnya: ini adalah platform PaaS-as-a-service yang memungkinkan klien untuk meluncurkan berbagai aplikasi (Java, PHP, Python ...), dengan dukungan untuk berbagai layanan data (MongoDB, MySQL, Redis ...) dan alur kerja seperti Heroku: Anda mengunggah kode Anda ke platform, itu membangun gambar kontainer dan menyebarkannya.
Saya akan memberi tahu Anda bagaimana lalu lintas diarahkan ke platform dotCloud. Bukan karena itu sangat keren (walaupun sistem bekerja dengan baik pada masanya!), Tetapi terutama karena dengan bantuan alat modern, desain seperti itu dapat dengan mudah diimplementasikan dalam waktu singkat oleh tim sederhana jika mereka membutuhkan cara untuk mengarahkan lalu lintas antara sekelompok layanan mikro atau banyak aplikasi. Dengan demikian, Anda dapat membandingkan opsi: apa yang terjadi jika Anda mengembangkan semuanya sendiri atau menggunakan layanan mesh yang ada. Pilihan standar: lakukan sendiri atau beli.
Perutean lalu lintas untuk aplikasi yang dihosting
Aplikasi DotCloud dapat menyediakan HTTP dan titik akhir TCP.
Titik akhir HTTP ditambahkan secara dinamis ke konfigurasi kluster penyeimbang beban
Hipache . Ini mirip dengan apa yang dilakukan sumber daya Kubernetes
Ingress dan penyeimbang beban seperti yang
dilakukan Traefik saat ini .
Klien terhubung ke titik akhir HTTP melalui masing-masing domain, asalkan nama domain menunjuk ke dotCloud memuat penyeimbang. Tidak ada yang istimewa.
Titik akhir TCP dikaitkan dengan nomor port, yang kemudian diteruskan ke semua wadah tumpukan ini melalui variabel lingkungan.
Klien dapat terhubung ke titik akhir TCP menggunakan nama host yang sesuai (sesuatu seperti gateway-X.dotcloud.com) dan nomor port.
Nama host ini diselesaikan ke kluster server "nats" (tidak terkait dengan
NATS ), yang akan merutekan koneksi TCP yang masuk ke wadah yang benar (atau, dalam hal layanan yang seimbang beban, ke wadah yang benar).
Jika Anda terbiasa dengan Kubernetes, ini mungkin akan mengingatkan Anda pada layanan
NodePort .
Tidak ada yang setara dengan layanan
ClusterIP pada
platform dotCloud : untuk kesederhanaan, akses ke layanan itu sama baik dari dalam maupun luar platform.
Semuanya diatur dengan cukup sederhana: implementasi awal jaringan HTTP dan TCP routing, mungkin hanya beberapa ratus baris Python. Algoritma sederhana (saya akan mengatakan, naif) yang diselesaikan dengan pertumbuhan platform dan munculnya persyaratan tambahan.
Refactoring ekstensif dari kode yang ada tidak diperlukan. Secara khusus,
aplikasi 12 faktor dapat langsung menggunakan alamat yang diperoleh melalui variabel lingkungan.
Apa bedanya dengan mesh layanan modern?
Visibilitas terbatas. Kami umumnya tidak memiliki metrik untuk kisi perutean TCP. Adapun perutean HTTP, versi yang lebih baru memiliki metrik HTTP terperinci dengan kode kesalahan dan waktu respons, tetapi jerat layanan modern bahkan melangkah lebih jauh, menyediakan integrasi dengan sistem pengumpulan metrik seperti Prometheus, misalnya.
Visibilitas penting tidak hanya dari sudut pandang operasional (untuk membantu memecahkan masalah), tetapi juga ketika fitur baru dirilis. Ini tentang
penyebaran biru-hijau yang aman dan
penyebaran kenari .
Efisiensi perutean juga terbatas. Di grid routing dotCloud, semua lalu lintas harus melalui sekelompok node routing khusus. Ini berarti potensi melintasi beberapa perbatasan AZ (zona aksesibilitas) dan peningkatan penundaan yang signifikan. Saya ingat bagaimana saya memperbaiki masalah dengan kode yang membuat lebih dari seratus permintaan SQL per halaman dan untuk setiap permintaan membuka koneksi baru ke server SQL. Ketika diluncurkan secara lokal, halaman dimuat secara instan, tetapi dalam dotCloud, memuat membutuhkan beberapa detik, karena dibutuhkan puluhan milidetik untuk setiap koneksi TCP (dan kueri SQL berikutnya). Dalam kasus khusus ini, koneksi persisten memecahkan masalah.
Sambungan layanan modern lebih baik dengan masalah seperti itu. Pertama-tama, mereka memverifikasi bahwa koneksi diarahkan
pada sumbernya . Alur logisnya sama:
→ →
, tetapi sekarang mesh bekerja secara lokal dan bukan pada node yang jauh, sehingga koneksi
→
bersifat lokal dan sangat cepat (mikrodetik, bukan milidetik).
Sambungan layanan modern juga menerapkan algoritma load balancing yang lebih cerdas. Dengan mengontrol kinerja backend, mereka dapat mengirim lebih banyak lalu lintas ke backend yang lebih cepat, yang mengarah pada peningkatan kinerja secara keseluruhan.
Keamanan juga lebih baik. Grid routing dotCloud bekerja sepenuhnya pada EC2 Classic dan tidak mengenkripsi lalu lintas (dengan asumsi bahwa jika seseorang berhasil menempatkan sniffer pada lalu lintas jaringan EC2, Anda sudah memiliki masalah besar). Sambungan layanan modern secara transparan melindungi semua lalu lintas kami, misalnya, dengan otentikasi TLS bersama dan enkripsi selanjutnya.
Routing Traffic untuk Layanan Platform
Oke, kami membahas lalu lintas antar aplikasi, tetapi bagaimana dengan platform dotCloud itu sendiri?
Platform itu sendiri terdiri dari sekitar seratus layanan microser yang bertanggung jawab untuk berbagai fungsi. Beberapa menerima permintaan dari yang lain, dan beberapa adalah pekerja latar belakang yang terhubung ke layanan lain tetapi tidak menerima koneksi. Dalam setiap kasus, setiap layanan harus mengetahui titik akhir dari alamat yang ingin Anda sambungkan.
Banyak layanan tingkat tinggi dapat menggunakan grid routing yang dijelaskan di atas. Bahkan, banyak dari lebih dari ratusan microservices dotCloud telah digunakan sebagai aplikasi reguler pada platform dotCloud itu sendiri. Tetapi sejumlah kecil layanan tingkat rendah (khususnya, yang menerapkan kisi-kisi perutean ini) membutuhkan sesuatu yang lebih sederhana, dengan lebih sedikit ketergantungan (karena mereka tidak dapat bergantung pada diri sendiri untuk bekerja - masalah ayam dan telur tua yang baik).
Layanan tingkat rendah dan penting ini dikerahkan dengan menjalankan kontainer secara langsung pada beberapa node utama. Pada saat yang sama, layanan platform standar tidak terlibat: penghubung, penjadwal, dan pelari. Jika Anda ingin membandingkan dengan platform kontainer modern, itu seperti meluncurkan pesawat kontrol dengan
docker run
langsung di node, alih-alih mendelegasikan tugas Kubernetes. Ini sangat mirip dengan konsep
modul statis (perapian) yang digunakan
kubeadm atau
bootkube saat memuat cluster mandiri.
Layanan ini diekspos dengan cara yang sederhana dan kasar: nama dan alamat mereka tercantum dalam file YAML; dan setiap klien harus mengambil salinan file YAML ini untuk ditempatkan.
Di satu sisi, ini sangat dapat diandalkan karena tidak memerlukan dukungan dari toko kunci / nilai eksternal seperti Zookeeper (jangan lupa, pada saat itu dlld atau Konsul belum ada). Di sisi lain, ini membuatnya sulit untuk memindahkan layanan. Setiap kali ketika bergerak, semua klien harus menerima file YAML yang diperbarui (dan berpotensi reboot). Sangat tidak nyaman!
Selanjutnya, kami mulai memperkenalkan skema baru, di mana setiap klien terhubung ke server proxy lokal. Alih-alih alamat dan port, cukup baginya untuk hanya mengetahui nomor port layanan, dan terhubung melalui
localhost
. Server proxy lokal memproses koneksi ini dan merutekannya ke server yang sebenarnya. Sekarang, ketika memindahkan backend ke mesin lain atau scaling alih-alih memperbarui semua klien, Anda hanya perlu memperbarui semua proxy lokal ini; dan reboot tidak lagi diperlukan.
(Itu juga direncanakan untuk merangkum lalu lintas dalam koneksi TLS dan menempatkan server proxy lain di sisi penerima, serta memeriksa sertifikat TLS tanpa partisipasi dari layanan penerima, yang dikonfigurasi untuk menerima koneksi hanya di
localhost
. Lebih lanjut tentang ini nanti).
Ini sangat mirip dengan
SmartStack Airbnb, tetapi perbedaan yang signifikan adalah bahwa SmartStack diimplementasikan dan digunakan dalam produksi, sementara sistem perutean internal dotCloud dimasukkan ke dalam kotak ketika dotCloud berubah menjadi Docker.
Saya pribadi menganggap SmartStack sebagai salah satu pendahulu sistem seperti Istio, Linkerd dan Consul Connect, karena mereka semua mengikuti pola yang sama:
- Menjalankan proxy pada setiap node.
- Klien terhubung ke proxy.
- Bidang manajemen memperbarui konfigurasi proksi ketika mengubah backend.
- ... Untung!
Implementasi modern dari service mesh
Jika kita perlu menerapkan kisi yang sama hari ini, kita dapat menggunakan prinsip yang sama. Misalnya, konfigurasikan zona DNS internal dengan memetakan nama layanan ke alamat di
127.0.0.0/8
. Kemudian jalankan HAProxy pada setiap node cluster, menerima koneksi ke setiap alamat layanan (
127.0.0.0/8
di subnet ini) dan mengarahkan / menyeimbangkan beban ke backends yang sesuai. Konfigurasi HAProxy dapat dikontrol oleh
confd , memungkinkan Anda untuk menyimpan informasi backend di etcd atau Consul dan secara otomatis mendorong konfigurasi yang diperbarui ke HAProxy bila perlu.
Beginilah cara Istio bekerja! Tetapi dengan beberapa perbedaan:
- Menggunakan Proxy Utusan bukan HAProxy.
- Menyimpan konfigurasi backend melalui API Kubernetes daripada etcd atau Consul.
- Layanan dialokasikan alamat pada subnet internal (alamat Kubernetes ClusterIP) bukan 127.0.0.0/8.
- Ini memiliki komponen opsional (Benteng) untuk menambahkan otentikasi TLS bersama antara klien dan server.
- Mendukung fitur-fitur baru seperti pemutusan sirkuit, pelacakan terdistribusi, penyebaran kenari, dll.
Mari kita lihat beberapa perbedaan.
Utusan proxy
Proxy Enftoy ditulis oleh Lyft. trans.]. Ini sangat mirip dengan proksi lain dalam banyak hal (misalnya, HAProxy, Nginx, Traefik ...), tetapi Lyft menulis sendiri karena mereka memerlukan fungsi yang tidak ada di proksi lain, dan tampaknya lebih masuk akal untuk membuat yang baru daripada memperluas yang sudah ada.
Utusan dapat digunakan sendiri. Jika saya memiliki layanan khusus yang harus terhubung ke layanan lain, saya dapat mengkonfigurasinya untuk terhubung ke Utusan, dan kemudian secara dinamis mengkonfigurasi dan mengkonfigurasi ulang Utusan dengan lokasi layanan lain, sambil menerima banyak fitur tambahan yang sangat baik, misalnya, visibilitas. Alih-alih perpustakaan klien khusus atau menanamkan pelacakan panggilan dalam kode, kami mengarahkan lalu lintas ke Utusan, dan mengumpulkan metrik untuk kami.
Tetapi Utusan juga dapat bekerja sebagai pesawat data untuk layanan mesh. Ini berarti bahwa untuk layanan mesh ini, Utusan sekarang dikonfigurasi
oleh bidang kontrol.
Kontrol pesawat
Dalam bidang manajemen, Istio mengandalkan API Kubernetes.
Ini tidak jauh berbeda dari menggunakan confd , yang bergantung pada etcd atau Consul untuk melihat sekumpulan kunci dalam data warehouse. Istio, melalui API Kubernetes, melihat kumpulan sumber daya Kubernetes.
Antara kasus : Saya pribadi menemukan
deskripsi API Kubernetes ini
berguna , yang berbunyi:
Server API Kubernetes adalah "server bisu" yang menawarkan penyimpanan, versi, validasi, pembaruan, dan semantik sumber daya API.
Istio dirancang untuk bekerja dengan Kubernetes; dan jika Anda ingin menggunakannya di luar Kubernetes, maka Anda perlu menjalankan sebuah instance dari server API Kubernetes (dan layanan tambahan dlld).
Alamat Layanan
Istio mengandalkan alamat ClusterIP yang dialokasikan Kubernetes, sehingga layanan Istio mendapatkan alamat internal (tidak dalam kisaran
127.0.0.0/8
).
Lalu lintas ke alamat ClusterIP untuk layanan tertentu di kluster Kubernetes tanpa Istio dicegat oleh kube-proxy dan dikirim ke bagian server dari proxy ini. Jika Anda tertarik pada detail teknis, maka kube-proxy menetapkan aturan iptables (atau penyeimbang beban IPVS, bergantung pada cara Anda mengonfigurasinya) untuk menulis ulang alamat IP tujuan dari koneksi yang menuju ke alamat ClusterIP.
Setelah menginstal Istio di kluster Kubernetes, tidak ada yang berubah sampai secara eksplisit dihidupkan untuk konsumen yang diberikan atau bahkan seluruh namespace dengan memperkenalkan wadah
sidecar
ke perapian kustom. Kontainer ini akan memulai instance dari Utusan dan menetapkan serangkaian aturan iptables untuk mencegat lalu lintas ke layanan lain dan mengarahkan lalu lintas itu ke Utusan.
Ketika terintegrasi dengan Kubernetes DNS, ini berarti bahwa kode kami dapat terhubung dengan nama layanan, dan semuanya "hanya berfungsi." Dengan kata lain, kode kami mengeluarkan permintaan seperti
http://api/v1/users/4242
, lalu
api
menyelesaikan permintaan ke
10.97.105.48
, aturan iptables mencegat koneksi dari 10.97.105.48 dan mengarahkan mereka ke proxy Utusan lokal, dan proksi lokal ini akan mengarahkan meminta API backend yang sebenarnya. Fuh!
Benda kecil ekstra
Istio juga menyediakan enkripsi dan otentikasi ujung-ke-ujung melalui mTLS (mutual TLS). Komponen yang disebut
Citadel bertanggung jawab untuk ini.
Ada juga komponen
Mixer yang Utusan dapat meminta untuk
setiap permintaan untuk membuat keputusan khusus tentang permintaan ini, tergantung pada berbagai faktor, seperti header, backend loading, dll ... (jangan khawatir: ada banyak cara untuk memastikan bahwa Mixer bekerja, dan bahkan jika crash, Utusan akan terus bekerja secara normal sebagai proxy).
Dan, tentu saja, kami menyebutkan visibilitas: Utusan mengumpulkan sejumlah besar metrik, sambil menyediakan penelusuran terdistribusi. Dalam arsitektur layanan microser, jika satu permintaan API harus melalui layanan microser A, B, C, dan D, maka ketika Anda masuk ke sistem, jejak yang didistribusikan akan menambahkan pengidentifikasi unik ke permintaan dan menyimpan pengidentifikasi ini melalui subqueries ke semua layanan Microsoft ini, memungkinkan Anda untuk merekam semua panggilan terkait, mereka keterlambatan dll.
Kembangkan atau beli
Istio memiliki reputasi sebagai sistem yang kompleks. Sebaliknya, membangun kisi perutean, yang saya jelaskan di awal tulisan ini, relatif sederhana menggunakan alat yang ada. Jadi, apakah masuk akal untuk membuat jala layanan Anda sendiri?
Jika kami memiliki kebutuhan sederhana (Anda tidak perlu visibilitas, pemutus sirkuit, dan kehalusan lainnya), maka muncul pemikiran untuk mengembangkan alat Anda sendiri. Tetapi jika kita menggunakan Kubernetes, itu mungkin bahkan tidak perlu, karena Kubernetes sudah menyediakan alat dasar untuk penemuan layanan dan penyeimbangan muatan.
Tetapi jika kita memiliki persyaratan tingkat lanjut, maka “membeli” layanan tampaknya menjadi pilihan yang jauh lebih baik. (Ini tidak selalu merupakan “pembelian”, karena Istio datang dengan kode sumber terbuka, tetapi kita masih perlu menginvestasikan waktu teknik untuk memahami pekerjaannya, untuk menggunakan dan mengelolanya).
Apa yang harus dipilih: Istio, Linkerd atau Consul Connect?
Sejauh ini kami hanya berbicara tentang Istio, tetapi ini bukan satu-satunya service mesh. Alternatif yang populer adalah
Linkerd , dan ada juga
Consul Connect .
Apa yang harus dipilih?
Sejujurnya, saya tidak tahu. Saat ini, saya tidak menganggap diri saya cukup kompeten untuk menjawab pertanyaan ini. Ada beberapa
artikel menarik yang membandingkan alat-alat ini dan bahkan
tolok ukur .
Salah satu pendekatan yang menjanjikan adalah dengan menggunakan alat seperti
SuperGloo . Ini mengimplementasikan lapisan abstraksi untuk menyederhanakan dan menyatukan API yang disediakan oleh layanan mesh. Alih-alih mempelajari API spesifik (dan, menurut saya, relatif kompleks) dari berbagai layanan, kita dapat menggunakan konstruksi SuperGloo yang lebih sederhana - dan dengan mudah beralih dari satu ke yang lain, seolah-olah kita memiliki format konfigurasi menengah yang menggambarkan antarmuka HTTP dan backends mampu menghasilkan konfigurasi aktual untuk Nginx, HAProxy, Traefik, Apache ...
Saya sedikit memanjakan diri dengan Istio dan SuperGloo, dan di artikel berikutnya saya ingin menunjukkan bagaimana cara menambahkan Istio atau Linkerd ke cluster yang ada menggunakan SuperGloo, dan seberapa banyak yang terakhir akan mengatasi pekerjaannya, yaitu, memungkinkan Anda untuk beralih dari satu service mesh ke yang lain tanpa menulis ulang konfigurasi.