Kontainer, layanan mikro, dan jerat layanan

Di internet banyak artikel о jaring layanan (layanan mesh), dan ini satu lagi. Hore! Tapi kenapa? Kemudian, saya ingin menyampaikan pendapat saya bahwa akan lebih baik jika jerat layanan muncul 10 tahun yang lalu, sebelum munculnya platform container seperti Docker dan Kubernetes. Saya tidak mengatakan bahwa sudut pandang saya lebih baik atau lebih buruk daripada yang lain, tetapi karena jaringan layanan adalah hewan yang cukup kompleks, berbagai sudut pandang akan membantu untuk memahaminya dengan lebih baik.

Saya akan berbicara tentang platform dotCloud, yang dibangun di lebih dari seratus layanan mikro dan mendukung ribuan aplikasi dalam container. Saya akan menjelaskan tantangan yang kami hadapi dalam mengembangkan dan meluncurkannya, dan bagaimana jaringan layanan dapat (atau tidak dapat) membantu.

Sejarah dotCloud

Saya telah menulis tentang sejarah dotCloud dan pilihan arsitektur untuk platform ini, namun saya belum berbicara banyak tentang lapisan jaringan. Jika Anda tidak ingin mendalami membaca artikel terakhir tentang dotCloud, inilah intinya: ini adalah platform-as-a-service PaaS yang memungkinkan pelanggan menjalankan berbagai aplikasi (Java, PHP, Python...), dengan dukungan untuk berbagai macam data layanan (MongoDB, MySQL, Redis...) dan alur kerja seperti Heroku: Anda mengunggah kode Anda ke platform, itu membuat gambar kontainer dan menyebarkannya.

Saya akan memberi tahu Anda bagaimana lalu lintas diarahkan ke platform dotCloud. Bukan karena desainnya sangat keren (walaupun sistemnya bekerja dengan baik pada masanya!), tetapi terutama karena dengan peralatan modern, desain seperti itu dapat dengan mudah diimplementasikan dalam waktu singkat oleh tim sederhana jika mereka memerlukan cara untuk mengarahkan lalu lintas antar kelompok. layanan mikro atau sekumpulan aplikasi. Dengan cara ini, Anda dapat membandingkan pilihannya: apa yang terjadi jika Anda mengembangkan semuanya sendiri atau menggunakan mesh layanan yang ada. Pilihan standarnya adalah membuatnya sendiri atau membelinya.

Perutean lalu lintas untuk aplikasi yang dihosting

Aplikasi di dotCloud dapat mengekspos titik akhir HTTP dan TCP.

Titik akhir HTTP ditambahkan secara dinamis ke konfigurasi kluster penyeimbang beban sakit pinggang. Hal ini mirip dengan apa yang dilakukan sumber daya saat ini Jalan masuk di Kubernetes dan sejenisnya penyeimbang beban Traefik.

Klien terhubung ke titik akhir HTTP melalui domain yang sesuai, asalkan nama domain mengarah ke penyeimbang beban dotCloud. Tidak ada yang spesial.

titik akhir TCP dikaitkan dengan nomor port, yang kemudian diteruskan ke semua kontainer di tumpukan itu melalui variabel lingkungan.

Klien dapat terhubung ke titik akhir TCP menggunakan nama host yang sesuai (seperti gateway-X.dotcloud.com) dan nomor port.

Nama host ini ditetapkan ke cluster server “nats” (tidak terkait dengan NATS), yang akan merutekan koneksi TCP masuk ke kontainer yang benar (atau, dalam kasus layanan dengan beban seimbang, ke kontainer yang benar).

Jika Anda familiar dengan Kubernetes, ini mungkin mengingatkan Anda pada Layanan Pelabuhan Node.

Tidak ada layanan yang setara di platform dotCloud ClusterIP: Untuk mempermudah, layanan diakses dengan cara yang sama baik dari dalam maupun luar platform.

Semuanya diatur dengan cukup sederhana: implementasi awal jaringan perutean HTTP dan TCP masing-masing mungkin hanya beberapa ratus baris Python. Algoritme sederhana (menurut saya naif) yang disempurnakan seiring pertumbuhan platform dan munculnya persyaratan tambahan.

Pemfaktoran ulang ekstensif pada kode yang ada tidak diperlukan. Secara khusus, Aplikasi 12 faktor dapat langsung menggunakan alamat yang diperoleh melalui variabel lingkungan.

Apa bedanya dengan jaringan layanan modern?

Terbatas visibilitas. Kami tidak memiliki metrik apa pun untuk mesh perutean TCP sama sekali. Terkait perutean HTTP, versi yang lebih baru memperkenalkan metrik HTTP terperinci dengan kode kesalahan dan waktu respons, namun jaringan layanan modern melangkah lebih jauh lagi, menyediakan integrasi dengan sistem pengumpulan metrik seperti Prometheus, misalnya.

Visibilitas penting tidak hanya dari sudut pandang operasional (untuk membantu memecahkan masalah), namun juga saat merilis fitur baru. Ini tentang aman penyebaran biru-hijau и penyebaran kenari.

Efisiensi perutean juga terbatas. Dalam mesh perutean dotCloud, semua lalu lintas harus melalui sekelompok node perutean khusus. Hal ini berarti berpotensi melintasi beberapa batas AZ (Availability Zone) dan meningkatkan latensi secara signifikan. Saya ingat kode pemecahan masalah yang membuat lebih dari seratus kueri SQL per halaman dan membuka koneksi baru ke server SQL untuk setiap kueri. Saat dijalankan secara lokal, halaman dimuat secara instan, tetapi di dotCloud dibutuhkan beberapa detik untuk memuat karena setiap koneksi TCP (dan kueri SQL berikutnya) memerlukan waktu puluhan milidetik. Dalam kasus khusus ini, koneksi persisten memecahkan masalah tersebut.

Jejaring layanan modern lebih baik dalam menangani masalah seperti itu. Pertama-tama, mereka memeriksa apakah koneksi telah dirutekan dalam sumber. Alur logisnya sama: клиент → меш → сервис, tapi sekarang mesh berfungsi secara lokal dan bukan pada node jarak jauh, jadi koneksinya клиент → меш bersifat lokal dan sangat cepat (mikrodetik, bukan milidetik).

Jejaring layanan modern juga menerapkan algoritme penyeimbangan beban yang lebih cerdas. Dengan memantau kesehatan backend, mereka dapat mengirimkan lebih banyak lalu lintas ke backend yang lebih cepat, sehingga menghasilkan peningkatan kinerja secara keseluruhan.

keamanan lebih baik juga. Jaring perutean dotCloud berjalan sepenuhnya di EC2 Classic dan tidak mengenkripsi lalu lintas (berdasarkan asumsi bahwa jika seseorang berhasil memasang sniffer pada lalu lintas jaringan EC2, Anda sudah berada dalam masalah besar). Jejaring layanan modern secara transparan melindungi semua lalu lintas kami, misalnya, dengan otentikasi TLS bersama dan enkripsi berikutnya.

Merutekan lalu lintas untuk layanan platform

Oke, kita sudah membahas lalu lintas antar aplikasi, tapi bagaimana dengan platform dotCloud itu sendiri?

Platform itu sendiri terdiri dari sekitar seratus layanan mikro yang bertanggung jawab atas berbagai fungsi. Beberapa menerima permintaan dari orang lain, dan beberapa merupakan pekerja latar belakang yang terhubung ke layanan lain namun tidak menerima koneksi itu sendiri. Bagaimanapun, setiap layanan harus mengetahui titik akhir dari alamat yang perlu disambungkan.

Banyak layanan tingkat tinggi mungkin menggunakan routing mesh yang dijelaskan di atas. Faktanya, banyak dari lebih dari seratus layanan mikro dotCloud telah diterapkan sebagai aplikasi reguler pada platform dotCloud itu sendiri. Namun sejumlah kecil layanan tingkat rendah (khususnya yang mengimplementasikan routing mesh ini) memerlukan sesuatu yang lebih sederhana, dengan lebih sedikit ketergantungan (karena mereka tidak dapat bergantung pada dirinya sendiri untuk bekerja - masalah ayam dan telur yang sudah lama ada).

Layanan tingkat rendah yang sangat penting ini disebarkan dengan menjalankan container secara langsung di beberapa node utama. Dalam hal ini, layanan platform standar tidak digunakan: linker, scheduler, dan runner. Jika ingin dibandingkan dengan platform kontainer modern, ini seperti menjalankan pesawat kendali docker run langsung pada node, alih-alih mendelegasikan tugas ke Kubernetes. Konsepnya sangat mirip modul statis (pod), yang digunakannya kubeadm или bootkube saat mem-boot cluster mandiri.

Layanan-layanan ini diekspos dengan cara yang sederhana dan kasar: file YAML mencantumkan nama dan alamatnya; dan setiap klien harus mengambil salinan file YAML ini untuk diterapkan.

Di satu sisi, ini sangat dapat diandalkan karena tidak memerlukan dukungan penyimpanan kunci/nilai eksternal seperti Zookeeper (ingat, dll atau Konsul belum ada pada saat itu). Di sisi lain, hal itu mempersulit perpindahan layanan. Setiap kali perpindahan dilakukan, semua klien akan menerima file YAML yang diperbarui (dan berpotensi melakukan boot ulang). Sangat tidak nyaman!

Selanjutnya, kami mulai menerapkan skema baru, di mana setiap klien terhubung ke server proxy lokal. Alih-alih alamat dan port, ia hanya perlu mengetahui nomor port layanan, dan terhubung melalui localhost. Proksi lokal menangani koneksi ini dan meneruskannya ke server sebenarnya. Sekarang, saat memindahkan backend ke komputer lain atau melakukan penskalaan, alih-alih memperbarui semua klien, Anda hanya perlu memperbarui semua proxy lokal ini; dan reboot tidak lagi diperlukan.

(Direncanakan juga untuk merangkum lalu lintas dalam koneksi TLS dan menempatkan server proxy lain di sisi penerima, serta memverifikasi sertifikat TLS tanpa partisipasi layanan penerima, yang dikonfigurasi untuk menerima koneksi hanya pada localhost. Lebih lanjut tentang ini nanti).

Ini sangat mirip dengan tumpukan pintar dari Airbnb, namun perbedaan signifikannya adalah SmartStack diimplementasikan dan diterapkan ke produksi, sedangkan sistem perutean internal dotCloud disimpan saat dotCloud menjadi Docker.

Saya pribadi menganggap SmartStack sebagai salah satu pendahulu sistem seperti Istio, Linkerd, dan Consul Connect karena semuanya mengikuti pola yang sama:

  • Jalankan proxy di setiap node.
  • Klien terhubung ke proxy.
  • Bidang kontrol memperbarui konfigurasi proksi ketika backend berubah.
  • ... Laba!

Implementasi modern dari jaring layanan

Jika kita perlu menerapkan jaringan serupa saat ini, kita dapat menggunakan prinsip serupa. Misalnya, konfigurasikan zona DNS internal dengan memetakan nama layanan ke alamat di ruang tersebut 127.0.0.0/8. Kemudian jalankan HAProxy pada setiap node di cluster, terima koneksi di setiap alamat layanan (di subnet tersebut 127.0.0.0/8) dan mengarahkan/menyeimbangkan beban ke backend yang sesuai. Konfigurasi HAProxy dapat dikontrol percaya, memungkinkan Anda menyimpan informasi backend di etcd atau Konsul dan secara otomatis memasukkan konfigurasi yang diperbarui ke HAProxy bila diperlukan.

Beginilah cara kerja Istio! Namun dengan beberapa perbedaan:

  • menggunakan Wakil Utusan bukannya HAProxy.
  • Menyimpan konfigurasi backend melalui Kubernetes API, bukan etcd atau Consul.
  • Layanan dialokasikan alamat pada subnet internal (alamat IP Cluster Kubernetes) dan bukan 127.0.0.0/8.
  • Memiliki komponen tambahan (Citadel) untuk menambahkan otentikasi TLS timbal balik antara klien dan server.
  • Mendukung fitur-fitur baru seperti pemutusan sirkuit, penelusuran terdistribusi, penerapan canary, dll.

Mari kita lihat sekilas beberapa perbedaannya.

Wakil Utusan

Envoy Proxy ditulis oleh Lyft [pesaing Uber di pasar taksi - kira-kira. jalur]. Dalam banyak hal, ini mirip dengan proxy lain (misalnya HAProxy, Nginx, Traefik...), tetapi Lyft menulis proxy mereka karena mereka memerlukan fitur yang tidak dimiliki proxy lain, dan tampaknya lebih pintar membuat yang baru daripada memperluas yang sudah ada.

Utusan dapat digunakan sendiri. Jika saya memiliki layanan tertentu yang perlu terhubung ke layanan lain, saya dapat mengkonfigurasinya untuk terhubung ke Envoy, dan kemudian secara dinamis mengkonfigurasi dan mengkonfigurasi ulang Envoy dengan lokasi layanan lain, sekaligus mendapatkan banyak fungsi tambahan yang hebat, seperti visibilitas. Daripada membuat pustaka klien khusus atau memasukkan jejak panggilan ke dalam kode, kami mengirimkan lalu lintas ke Envoy, dan Envoy mengumpulkan metrik untuk kami.

Namun Utusan juga mampu bekerja sebagai pesawat data (bidang data) untuk mesh layanan. Ini berarti Envoy sekarang dikonfigurasi untuk mesh layanan ini pesawat kendali (bidang kontrol).

Pesawat kendali

Untuk bidang kendali, Istio mengandalkan API Kubernetes. Ini tidak jauh berbeda dengan menggunakan confd, yang mengandalkan etcd atau Consul untuk melihat kumpulan kunci di penyimpanan data. Istio menggunakan API Kubernetes untuk melihat sekumpulan sumber daya Kubernetes.

Antara ini dan nanti: Saya pribadi menganggap ini berguna Deskripsi API Kubernetesyang berbunyi:

Server API Kubernetes adalah “server bodoh” yang menawarkan penyimpanan, pembuatan versi, validasi, pembaruan, dan semantik untuk sumber daya API.

Istio dirancang untuk bekerja dengan Kubernetes; dan jika Anda ingin menggunakannya di luar Kubernetes, Anda perlu menjalankan instance server API Kubernetes (dan layanan helper etcd).

Alamat layanan

Istio bergantung pada alamat ClusterIP yang dialokasikan Kubernetes, sehingga layanan Istio menerima alamat internal (bukan dalam kisaran 127.0.0.0/8).

Lalu lintas ke alamat ClusterIP untuk layanan tertentu di cluster Kubernetes tanpa Istio dicegat oleh kube-proxy dan dikirim ke backend proxy tersebut. Jika kamu tertarik dengan detail teknisnya, kube-proxy menyiapkan aturan iptables (atau penyeimbang beban IPVS, bergantung pada cara konfigurasinya) untuk menulis ulang alamat IP tujuan dari koneksi yang menuju ke alamat ClusterIP.

Setelah Istio diinstal pada cluster Kubernetes, tidak ada perubahan sampai Istio diaktifkan secara eksplisit untuk konsumen tertentu, atau bahkan seluruh namespace, dengan memperkenalkan sebuah container sidecar ke dalam pod khusus. Kontainer ini akan menjalankan instance Envoy dan menyiapkan seperangkat aturan iptables untuk mencegat lalu lintas yang menuju ke layanan lain dan mengalihkan lalu lintas tersebut ke Envoy.

Ketika terintegrasi dengan DNS Kubernetes, ini berarti kode kami dapat terhubung berdasarkan nama layanan dan semuanya “berfungsi”. Dengan kata lain, kode kita mengeluarkan pertanyaan seperti http://api/v1/users/4242lalu api menyelesaikan permintaan untuk 10.97.105.48, aturan iptables akan mencegat koneksi dari 10.97.105.48 dan meneruskannya ke proksi Utusan lokal, dan proksi lokal tersebut akan meneruskan permintaan ke API backend sebenarnya. Fiuh!

Hiasan tambahan

Istio juga menyediakan enkripsi dan otentikasi ujung ke ujung melalui mTLS (mutual TLS). Sebuah komponen disebut Benteng.

Ada juga komponennya Pengaduk, yang dapat diminta oleh Utusan dari masing-masing permintaan untuk membuat keputusan khusus tentang permintaan itu tergantung pada berbagai faktor seperti header, beban backend, dll... (jangan khawatir: ada banyak cara untuk menjaga Mixer tetap berjalan, dan bahkan jika crash, Envoy akan terus bekerja baik sebagai proxy).

Dan, tentu saja, kami menyebutkan visibilitas: Envoy mengumpulkan sejumlah besar metrik sambil menyediakan penelusuran terdistribusi. Dalam arsitektur layanan mikro, jika satu permintaan API harus melewati layanan mikro A, B, C, dan D, maka saat masuk, penelusuran terdistribusi akan menambahkan pengidentifikasi unik ke permintaan dan menyimpan pengidentifikasi ini melalui subpermintaan ke semua layanan mikro ini, sehingga memungkinkan semua panggilan terkait yang akan ditangkap, penundaan, dll.

Kembangkan atau beli

Istio memiliki reputasi sebagai orang yang kompleks. Sebaliknya, membangun routing mesh yang saya jelaskan di awal postingan ini relatif sederhana menggunakan alat yang ada. Jadi, apakah masuk akal untuk membuat mesh layanan Anda sendiri?

Jika kita memiliki kebutuhan yang sederhana (kita tidak memerlukan visibilitas, pemutus sirkuit, dan hal-hal halus lainnya), maka muncullah pemikiran untuk mengembangkan alat kita sendiri. Namun jika kita menggunakan Kubernetes, hal tersebut mungkin tidak diperlukan lagi karena Kubernetes sudah menyediakan tools dasar untuk service Discovery dan Load Balancing.

Namun jika kita memiliki persyaratan tingkat lanjut, maka “membeli” jaringan layanan tampaknya merupakan pilihan yang jauh lebih baik. (Ini tidak selalu berarti "beli" karena Istio bersifat open source, namun kita masih perlu menginvestasikan waktu teknis untuk memahami, menerapkan, dan mengelolanya.)

Haruskah saya memilih Istio, Linkerd atau Consul Connect?

Sejauh ini kita hanya membicarakan Istio, tapi ini bukan satu-satunya jaringan layanan. Alternatif populer - Linkerd, dan masih ada lagi Konsul Hubungkan.

Apa yang harus dipilih?

Sejujurnya, saya tidak tahu. Saat ini saya merasa diri saya tidak cukup kompeten untuk menjawab pertanyaan ini. Ada beberapa menarik artikel dengan perbandingan alat-alat ini dan bahkan tolak ukur.

Salah satu pendekatan yang menjanjikan adalah dengan menggunakan alat seperti SuperGloo. Ini mengimplementasikan lapisan abstraksi untuk menyederhanakan dan menyatukan API yang diekspos oleh jerat layanan. Alih-alih mempelajari API spesifik (dan, menurut pendapat saya, relatif kompleks) dari jerat layanan yang berbeda, kita dapat menggunakan konstruksi SuperGloo yang lebih sederhana - dan dengan mudah beralih dari satu ke yang lain, seolah-olah kita memiliki format konfigurasi perantara yang menjelaskan antarmuka HTTP dan kemampuan backend untuk menghasilkan konfigurasi aktual untuk Nginx, HAProxy, Traefik, Apache...

Saya telah mencoba-coba sedikit dengan Istio dan SuperGloo, dan di artikel berikutnya saya ingin menunjukkan cara menambahkan Istio atau Linkerd ke cluster yang ada menggunakan SuperGloo, dan bagaimana SuperGloo menyelesaikan pekerjaannya, yaitu memungkinkan Anda untuk beralih dari satu layanan mesh ke layanan lainnya tanpa menimpa konfigurasi.

Sumber: www.habr.com

Tambah komentar