Layanan dapat dirancang dengan semua jenis keandalan ketahanan yang terpasang, tetapi untuk membuatnya andal dalam praktiknya, layanan tersebut juga harus mengatasi kegagalan yang dapat diprediksi saat benar-benar terjadi. Di Amazon, kami membangun layanan agar dapat diskalakan secara horizontal dan berlebihan karena perangkat keras pada akhirnya dirancang untuk gagal. Semua hard drive memiliki masa pakai maksimum yang diharapkan, dan semua bagian perangkat lunak rentan terhadap crash pada titik tertentu. Tampaknya kondisi sebuah server biner: server bisa berfungsi baik, tidak berfungsi sama sekali, dan keluar jalur. Sayangnya, bukan ini masalahnya. Kami menemukan bahwa bukan hanya mati, server yang gagal dapat menyebabkan kerusakan sistem yang tidak dapat diprediksi dan terkadang tidak proporsional. Pemeriksaan kondisi dapat mendeteksi dan merespons jenis masalah ini secara otomatis.

Artikel ini menjelaskan bagaimana pemeriksaan kondisi mendeteksi dan mengatasi kegagalan server tunggal, hal-hal yang terjadi ketika pemeriksaan kondisi tidak digunakan, dan bagaimana sistem yang bereaksi berlebihan terhadap kegagalan pemeriksaan kondisi dapat mengubah masalah kecil menjadi pemadaman total. Kami juga memberikan wawasan dari pengalaman di Amazon tentang menyeimbangkan tradeoff di antara berbagai jenis penerapan pemeriksaan kondisi.

Kegagalan kecil dengan dampak besar

Saat saya menjadi pengembang perangkat lunak baru di Amazon, saya mengerjakan situs web yang merender armada di balik Amazon.com. Saat mengerjakan perubahan untuk menambah beberapa instrumentasi dan mendapatkan visibilitas tentang seberapa baik perangkat lunak berjalan, sayangnya saya menulis bug. Bug jarang dipicu, tetapi ketika itu terjadi, hal ini menyebabkan server web merender halaman kesalahan kosong di setiap permintaan. Hanya memulai ulang proses server web memperbaiki masalah. Kami mendeteksi bug dan segera mengurungkan perubahan, menambahkan beberapa tes, dan meningkatkan proses untuk mendapatkan kondisi seperti ini di masa mendatang. Tetapi saat bug dalam produksi, beberapa server di dalam armada yang besar berakhir di kondisi rusak ini.
 
Satu hal yang membuat bug sedikit rumit adalah mengetahui bahwa server ini tidak menyadari bahwa sedang dalam kondisi tidak sehat. Selain itu, server kehilangan kemampuan untuk melaporkan kondisinya ke sistem pemantauan, sehingga server tidak dikeluarkan dari layanan secara otomatis dan tidak memicu alarm reguler. Memperburuk keadaan, server menjadi sangat cepat dan mulai menghasilkan halaman kesalahan kosong lebih cepat dari “server sehat” peer yang merender halaman web yang sehat. Teknologi penyeimbang muatan yang kita gunakan lebih memilih server cepat dibandingkan yang lambat, sehingga teknologi ini mengarahkan jumlah lalu lintas yang tidak seimbang ke server yang tidak sehat, yang semakin meningkatkan dampaknya.

Alarm lainnya dipicu, karena pemantauan melibatkan pengukuran nilai kesalahan dan latensi dari beberapa titik di dalam sistem. Sementara jenis sistem pemantauan dan proses operasional ini dapat berfungsi sebagai penghalang untuk mengatasi masalah, pemeriksaan kondisi yang tepat dapat meminimalkan dampak kelas kesalahan secara signifikan dengan mendeteksi dan menindaklanjuti kegagalan dengan cepat.

Tradeoff pemeriksaan kondisi

Pemeriksaan kondisi merupakan cara meminta layanan pada server tertentu, terlepas dari server tersebut dapat berhasil melakukan pekerjaannya atau tidak. Penyeimbang muatan menanyakan pertanyaan ini ke setiap server secara berkala untuk menentukan server mana yang aman untuk mengarahkan lalu lintas. Layanan yang mengumpulkan pesan dari antrean mungkin bertanya kepada dirinya sendiri apakah layanan tersebut cukup sehat sebelum memutuskan untuk mengumpulkan lebih banyak pekerjaan dari antrean tersebut. Agen pemantauan—berjalan di setiap server atau di armada pemantauan eksternal—dapat bertanya ke server apakah server tersebut sehat sehingga mereka dapat menyalakan alarm atau secara otomatis mengatasi server yang gagal.

Seperti yang kita lihat di contoh bug situs web saya, ketika server tidak sehat tetap melayani, server tersebut dapat mengurangi ketersediaan layanan dengan tidak seimbang secara keseluruhan. Dengan armada sepuluh server, satu server buruk berarti bahwa ketersediaan armada berjumlah 90% atau kurang. Memperburuk keadaan, beberapa algoritme penyeimbang muatan, seperti “permintaan paling sedikit”, memberikan lebih banyak pekerjaan ke server tercepat. Ketika server gagal, server tersebut mulai gagal melakukan permintaan dengan cepat, membuat “lubang hitam” pada armada layanan dengan menarik lebih banyak permintaan dibanding server yang sehat. Dalam beberapa kasus, kami menambahkan perlindungan ekstra untuk mencegah lubang hitam memperlambat permintaan gagal untuk menyesuaikan latensi rata-rata pada permintaan yang berhasil. Namun, terdapat skenario lainnya, seperti menggunakan poller antrean, di mana masalah ini lebih sulit untuk diselesaikan. Misalnya, jika poller antrean mem-polling pesan secepat mungkin, server yang gagal akan menjadi lubang hitam juga. Dengan kumpulan lingkungan yang beragam untuk mendistribusikan pekerjaan, cara kita berpikir tentang melindungi server yang sebagian gagal berbeda dari sistem ke sistem.

Kami menemukan bahwa server gagal secara independen karena berbagai alasan, termasuk disk yang menjadi tidak dapat ditulis dan menyebabkan permintaan langsung gagal, jam yang condong tiba-tiba dan menyebabkan panggilan terhadap ketergantungan gagal melakukan autentikasi, server yang gagal mengambil material kripto yang diperbarui dan menyebabkan dekripsi dan enkripsi gagal, proses dukungan penting yang crash karena bug-nya sendiri, kebocoran memori, dan kebuntuan yang membekukan pemrosesan.

Server juga gagal karena alasan yang berhubungan yang menyebabkan banyak atau semua server di armada gagal bersamaan. Alasan yang berhubungan termasuk pemadaman dependensi bersama dan masalah jaringan berskala besar. Pemeriksaan kondisi ideal akan menguji setiap aspek kondisi server dan aplikasi, bahkan mungkin memverifikasi proses dukungan tidak penting yang berjalan. Tetapi, masalah muncul ketika pemeriksaan kondisi gagal karena alasan yang tidak penting dan ketika kegagalan tersebut berkorelasi di seluruh server. Jika automasi menghapus server dari layanan ketika server masih dapat melakukan pekerjaan bermanfaat, automasi lebih merugikan dibanding memberikan keuntungan.

Kesulitan pada pemeriksaan kondisi adalah pada tensi di antara, di sisi lain, keuntungan pada pemeriksaan kondisi menyeluruh dan memitigasi kegagalan server tunggal dengan cepat, serta di sisi lain, kerugian diakibatkan oleh kegagalan positif palsu di seluruh armada. Oleh karena itu, salah satu tantangan merancang pemeriksaan kondisi yang baik adalah untuk melindungi dari positif palsu secara cermat. Secara umum, ini berarti bahwa automasi di sekeliling pemeriksaan kondisi harus berhenti mengarahkan lalu lintas ke server buruk tunggal, tetapi tetap mengizinkan lalu lintas jika seluruh armada bermasalah.

Cara untuk mengukur kondisi

Terdapat banyak hal yang dapat rusak di sebuah server, dan terdapat banyak tempat di sistem kita ketika mempertimbangkan kondisi server. Beberapa pemeriksaan kondisi dapat secara definitif melaporkan bahwa server tertentu rusak secara independen, sementara yang lainnya lebih kabur dan melaporkan positif palsu dalam hal kegagalan yang berkorelasi. Beberapa pemeriksaan kondisi sulit untuk dilaksanakan. Pemeriksaan lainnya diterapkan saat penyiapan dengan layanan seperti Amazon Elastic Compute Cloud (Amazon EC2) dan Elastic Load Balancing. Setiap jenis pemeriksaan kondisi memiliki kekuatannya sendiri.

Pemeriksaan kehidupan

Pemeriksaan kehidupan menguji konektivitas dasar ke layanan dan keberadaan proses server. Pemeriksaan tersebut sering dilakukan oleh penyeimbang muatan atau agen pemantauan eksternal, dan mereka tidak menyadari detail tentang bagaimana cara kerja aplikasi. Pemeriksaan cenderung disertakan dengan layanan dan tidak memerlukan pembuat aplikasi untuk menerapkan apa pun. Beberapa contoh pemeriksaan kehidupan yang kita gunakan di Amazon termasuk antara lain:

• Tes yang mengonfirmasi bahwa server mendengarkan port yang diharapkan dan menerima koneksi TCP baru.
• Tes yang melakukan permintaan HTTP dasar dan memastikan bahwa server tersebut merespons dengan 200 kode.
• Pemeriksaan Status untuk Amazon EC2 yang menguji hal-hal dasar yang diperlukan untuk semua sistem beroperasi, seperti jangkauan jaringan.

Pemeriksaan kondisi dasar

Pemeriksaan kondisi dasar dilakukan lebih mendalam dibanding pemeriksaan kehidupan untuk memverifikasi bahwa aplikasi tersebut cenderung dapat berfungsi. Pemeriksaan kondisi ini menguji sumber daya yang tidak dibagikan dengan peer server. Oleh karena itu, pemeriksaan ini tidak mungkin gagal pada banyak server secara bersamaan di armada tersebut. Tes pemeriksaan kondisi tersebut adalah sebagai berikut:

• Ketidakmampuan menulis atau membaca dari disk—Mungkin lebih mudah untuk dipercaya bahwa layanan tanpa status tidak memerlukan disk yang dapat ditulis. Namun di Amazon, layanan kami cenderung menggunakan disk untuk pekerjaan seperti pemantauan, pengelogan, dan penerbitan data pengukuran asinkron.
•Proses kritis macet atau rusak — Beberapa layanan menerima permintaan menggunakan proxy di server (mirip dengan NGINX) dan menjalankan logika bisnis mereka dalam proses server lain. Pemeriksaan kehidupan mungkin hanya menguji apakah proses proxy berjalan. Proses pemeriksaan kondisi dasar mungkin melewati dari proxi ke aplikasi untuk memeriksa apakah keduanya berjalan dan menjawab permintaan dengan benar. Menariknya, di dalam contoh situs web dari awal artikel, pemeriksaan kondisi yang ada cukup dalam untuk memastikan bahwa proses perenderan berjalan dan merespons, tetapi tidak cukup dalam untuk memastikan apakah proses tersebut merespons dengan benar.
• Kehilangan proses dukungan—Host yang kehilangan daemon pemantauannya akan membiarkan operator "terbang buta" dan tidak menyadari kondisi layanannya. Proses dukungan lainnya mendorong data penggunaan pengukuran dan penghitungan atau menerima pembaruan kredensial. Server dengan proses dukungan yang rusak membahayakan fungsi dengan cara yang rumit dan sulit dideteksi.

Pemeriksaan kondisi dependensi

Pemeriksaan kondisi dependensi merupakan pemeriksaan menyeluruh pada kemampuan aplikasi untuk berinteraksi dengan sistem yang berdekatan. Pemeriksaan ini secara ideal menangkap masalah lokal ke server, seperti kredensial kedaluwarsa, yang mencegahnya berinteraksi dengan dependensi. Tetapi, pemeriksaan tersebut juga bisa memiliki positif palsu ketika terdapat masalah dengan dependensi itu sendiri. Karena positif palsu tersebut, kita harus berhati-hati pada cara kita beraksi terhadap kegagalak pemeriksaan kondisi dependensi. Tes pemeriksaan kondisi dependensi antara lain:

• Konfigurasi buruk atau metadata usang—Jika proses secara asinkron mencari pembaruan metadata atau konfigurasi tetapi mekanisme pembaruannya rusak di server, server tersebut dapat menjadi tidak sinkron dengan peer-nya dan melakukan hal yang buruk dengan cara yagn tidak dapat diprediksi dan tidak teruji. Namun, ketika server tidak melihat pembaruan selama beberapa waktu, server tersebut tidak mengetahui apakah mekanisme pembaruan rusak atau sistem pembaruan pusat berhenti menerbitkan pembaruan ke semua server.
• Ketidakmampuan untuk berkomunikasi dengan server peer atau dependensi—Perilaku jaringan yang tidak biasa diketahui memengaruhi kemampuan subset server dalam armada untuk bicara ke dependensi tanpa memengaruhi kemampuan lalu lintas yang dikirimkan ke server tersebut. Masalah perangkat lunak, seperti kebuntuan atau bug dalam kumpulan koneksi, juga dapat menghambat komunikasi jaringan.
• Bug perangkat lunak tidak biasa lainnya yang memerlukan proses pantul—Kebuntuan, kebocoran memori, atau bug kerusakan status dapat membuat server menghasilkan kesalahan. 

Deteksi anomali

Deteksi anomali mencari ke seluruh server di dalam armada untuk menentukan apakah terdapat server yang berperilaku tidak wajar dibandingkan peer-nya. Dengan mengagregasi data pemantauan per server, kita dapat membandingkan nilai kesalahaan, data latensi, atau atribut lainnya secara berkelanjutan untuk menemukan server yang ganjil dan menghapusnya dari layanan secara otomatis. Deteksi anomali dapat menemukan perbedaan di dalam armada yang tidak dapat dideteksi server itu sendiri, contohnya:

• Jam condong—Terutama ketika server sedang bermuatan tinggi, jamnya akan condong tiba-tiba dan secara drastis. Pengukuran keamanan, seperti yang digunakan untuk mengevaluasi permintaan yang ditandatangani ke AWS, mengharuskan waktu pada jam klien lima menit dari waktu sesungguhnya. Jika tidak, permintaan ke Layanan AWS akan gagal.
• Kode lama—Jika server diputus dari jaringan atau dimatikan dalam waktu yang lama lalu kembali online, server dapat menjalankan kode usang berbahaya yang tidak kompatibel dengan armada lainnya.
• Mode kegagalan apa pun yang tidak diantisipasi — Terkadang server gagal sedemikian rupa sehingga mereka mengembalikan kesalahan sehingga mereka mengidentifikasi kesalahan sebagai milik klien dan bukannya milik mereka (HTTP 400, bukannya 500). Server mungkin melambat dan bukan gagal, atau mereka mungkin merespons lebih cepat dari peer mereka, yang merupakan tanda bahwa mereka mengembalikan respons palsu ke pemanggil mereka. Deteksi anomali merupakan catchall luar biasa untuk mode kegagalan yang tidak diantisipasi.

Ada beberapa hal yang harus berlaku agar pendeteksian anomali bekerja dalam praktik:

• Server harus melakukan hal yang hampir sama—Dalam kasus ketika kita merutekan jenis lalu lintas yang berbeda ke jenis server yang berbeda, perilaku server mungkin tidak cukup sama untuk mendeteksi titik luar. Namun, dalam beberapa kasus saat kita menggunakan penyeimbang muatan untuk mengarahkan lalu lintas ke server, tampaknya server merespons dengan cara yang hampir sama.
• Armada harus relatif sejenis—Di armada yang menyertakan jenis instans yang berbeda, beberapa instans mungkin lebih lambat dari yang lainnya, yang dapat memicu deteksi server buruk yang pasif secara salah. Untuk menyelasikan skenario ini, kami menyatukan metrik berdasarkan jenis instans.
• Kesalahan atau perbedaan dalam perilaku harus dilaporkan—karena kita bergantung di server itu sendiri untuk melaporkan kesalahan, apa yang terjadi ketika mereka memantau sistem yang juga rusak? Beruntungnya, klien layanan adalah tempat yang hebat untuk menambahkan instrumentasi. Penyeimbang muatan seperi Application Load Balancer memublikasikan log akses yang menampilkan server backend mana yang dihubungi di setiap permintaan, waktu respons, dan apakah permintaan tersebut berhasil atau gagal. 

Bereaksi dengan aman terhadap kegagalan pemeriksaan kondisi

Ketika server menentukan apakah kondisinya kurang baik, terdapat dua tindakan yang diambil. Pada kasus-kasus yang paling ekstrem, hal ini dapat ditentukan secara lokal bahwa kondisi ini tidak memerlukan tindak lanjut atau layanan akan berhenti dengan sendirinya dengan menggagalkan pemeriksaan kondisi penyeimbang muatan atau dengan menghentikan polling antrean. Server dapat bereaksi dengan cara lain seperti menginformasikan otoritas pusat yang memiliki masalah dan membiarkan sistem pusat memutuskan cara menangani masalah. Sistem pusat dapat mengatasi masalah dengan aman tanpa membiarkan automasi mematikan seluruh armada.

Terdapat beberapa cara untuk menerapkan dan merespons pemeriksaan kondisi. Bagian ini menjelaskan beberapa pola yang kita gunakan di Amazon.

Gagal membuka

Beberapa penyeimbang muatan dapat bertindak sebagai otoritas pusat cerdas. Ketika server individual gagal melakukan pemeriksaan kondisi, penyeimbang muatan akan berhenti mengirimkan lalu lintas. Tetapi ketika semua server gagal melakukan pemeriksaan kondisi di saat yang sama, penyeimbang muatan gagal membuka, memungkinkan lalu lintas ke semua server. Kami dapat menggunakan penyeimbang muatan untuk mendukung penerapan yang aman pada pemeriksaan kondisi dependensi, kemungkinan termasuk salah satu yang membuat kueri ke database dan memeriksa untuk memastikan proses dukungan yang tidak penting sedang berjalan.

Misalnya, AWS Network Load Balancer gagal membuka jika tidak ada server yang melaporkan kondisi baik. Server juga gagal keluar dari Availability Zone tidak sehat jika semua server di Availability Zone melaporkan tidak sehat. (Untuk informasi selengkapnya mengenai penggunaan Network Load Balancers untuk pemeriksaan kondisi, lihat dokumentasi Elastic Load Balancing.) Application Load Balancer kami juga mendukung gagal membuka, sama halnya dengan Amazon Route 53. (Untuk informasi selengkapnya mengenai cara mengonfigurasi pemeriksaan kondisi menggunakan Route 53, lihat dokumentasi Route 53.)

Ketika kami bergantung pada perilaku gagal membuka, kami memastikan untuk menguji mode gagal pada pemeriksaan kondisi dependensi. Misalnya, pertimbangkan layanan tempat server terhubung ke penyimpanan data bersama. Jika penyimpanan data menjadi lambat atau merespons dengan angka kesalahan rendah, server tersebut mungkin sesekali menggagalkan pemeriksaan kondisi dependensi. Kondisi ini menyebabkan server masuk dan keluar layanan tetapi tidak memicu ambang batas gagal membuka. Memikirkan dan menguji kegagalan parsial dependensi dengan pemeriksaan kondisi ini penting untuk menghindari situasi di mana kegagalan dapat menyebabkan pemeriksaan kondisi mendalam memperburuk keadaan.

Meskipun gagal membuka adalah perilaku yang membantu, di Amazon kami cenderung skeptis terhadap hal-hal yang tidak dapat sepenuhnya kami pikirkan atau uji dalam semua situasi. Kami belum menemukan bukti umum bahwa gagal membuka akan memicu seperti yang kami harapkan pada semua jenis kelebihan muatan, kegagalan sebagian, atau kegagalan abu-abu dalam suatu sistem atau dalam dependensi sistem itu. Karena keterbatasan ini, tim-tim di Amazon cenderung membatasi pemeriksaan kondisi penyeimbang muatan mereka yang bertindak cepat untuk pemeriksaan kondisi dasar dan mengandalkan sistem terpusat untuk secara hati-hati bereaksi terhadap pemeriksaan kondisi dependensi yang lebih dalam. Hal ini bukan berarti kami tidak menggunakan perilaku gagal membuka atau membuktikan bahwa pemeriksaan ini berfungsi dalam kasus-kasus tertentu. Tetapi ketika logika dapat bertindak pada sejumlah besar server dengan cepat, kami sangat berhati-hati pada logika tersebut.

Pemeriksaan kondisi tanpa sakelar pemutus tenaga

Mengizinkan server bereaksi terhadap masalahnya sendiri mungkin tampak seperti jalan tercepat dan termudah untuk pemulihan. Namun, ini juga merupakan jalur paling berisiko jika server salah tentang kondisinya atau tidak melihat seluruh gambaran tentang apa yang terjadi di seluruh armada. Ketika semua server di seluruh armada membuat keputusan salah yang sama secara bersamaan, hal ini dapat menyebabkan kegagalan berjenjang di seluruh layanan yang berdekatan. Risiko memberikan trade-off kepada kita. Jika ada celah dalam pemeriksaan dan pemantauan kondisi, server dapat mengurangi ketersediaan layanan hingga masalah terdeteksi. Namun, skenario ini menghindari pemadaman layanan total karena perilaku pemeriksaan kondisi yang tidak terduga di seluruh armada.

Ini adalah praktik terbaik yang kami ikuti untuk menerapkan pemeriksaan kondisi ketika kami tidak memiliki sakelar pemutus tenaga bawaan:

• Konfigurasikan produsen pekerjaan (penyeimbang muatan, thread polling antrean) untuk melakukan pemeriksaan kehidupan dan kondisi dasar. Server dikeluarkan dari layanan secara otomatis oleh penyeimbang muatan hanya jika mereka memiliki beberapa masalah yang bersifat lokal untuk server itu, seperti disk yang buruk.
• Konfigurasikan sistem pemantauan eksternal lainnya untuk melakukan pemeriksaan kondisi dependensi dan deteksi anomali. Sistem-sistem ini dapat berupaya untuk menghentikan instans secara otomatis atau alarm atau melibatkan operator.

Ketika kita membangun sistem untuk bereaksi secara otomatis terhadap kegagalan pemeriksaan kondisi dependensi, kita harus membangun dalam jumlah ambang batas yang tepat untuk mencegah sistem otomatis mengambil tindakan drastis secara tak terduga. Tim di Amazon yang mengoperasikan server stateful seperti Amazon DynamoDB, Amazon S3, dan Amazon Relational Database Service (Amazon RDS) memiliki persyaratan daya tahan yang penting pada penggantian server. Mereka juga telah membangun pembatasan tingkat dengan cermat dan mengontrol putaran umpan balik sehingga automasi berhenti dan melibatkan manusia ketika ambang batas dilewati. Ketika kita membangun automasi semacam itu, kita harus yakin bahwa kita melihat server mengagalkan pemeriksaan kondisi dependensi. Untuk beberapa metrik, kami mengandalkan server untuk melaporkan sendiri status masing-masing ke sistem pemantauan pusat. Untuk memberikan kompensasi pada kasus ketika server rusak sehingga tidak bisa melapokan kondisinya, kami juga secara aktif menjangkaunya untuk memeriksa kondisinya. 

Prioritaskan kondisi Anda

Khususnya dalam kondisi kelebihan muatan, penting bagi server untuk memprioritaskan pemeriksaan kondisinya dibanding pekerjaan reguler. Dalam situasi ini, gagal atau lambat merespons dapat mengakibatkan situasi brown out yang buruk semakin parah. 

Ketika server menggagalkan pemeriksaan kondisi penyeimbang muatan, server akan meminta penyeimbang muatan untuk keluar dari layanan segera dan untuk jumlah waktu yang tidak sedikit. Ketika satu server gagal, itu bukan masalah, tetapi dalam lonjakan lalu lintas ke layanan, hal terakhir yang kita inginkan adalah mengecilkan ukuran layanan. Mengambil server dari layanan selama kelebihan beban dapat menyebabkan spiral ke bawah. Memaksa server yang tersisa menggunakan lebih banyak lalu lintas yang membuat mereka lebih cenderung menjadi kelebihan beban, juga gagal dalam pemeriksaan kondisi, dan menyusutkan armada lebih banyak.

Masalahnya bukan bahwa server kelebihan beban mengembalikan kesalahan ketika mereka kelebihan beban. Hal ini karena server tidak merespons permintaan ping penyeimbang muatan tepat waktu. Bagaimanapun, pemeriksaan kondisi penyeimbang muatan dikonfigurasikan dengan timeout, sama seperti panggilan layanan jarak jauh lainnya. Server yang dibatasi dayanya lambat merespons karena beberapa alasan, termasuk konflik CPU yang tinggi, siklus pengumpul sampah lama, atau hanya kehabisan thread pekerja. Layanan perlu dikonfigurasi untuk menyisihkan sumber daya guna merespons pemeriksaan kondisi pada waktu yang tepat sebagai ganti menerima terlalu banyak permintaan tambahan.

Untungnya, ada beberapa praktik terbaik konfigurasi langsung yang kami ikuti untuk membantu mencegah spiral ke bawah ini. Alat seperti iptables, dan bahkan beberapa penyeimbang muatan, mendukung gagasan "koneksi maksimum". Dalam hal ini, OS (atau penyeimbang muatan) membatasi jumlah koneksi ke server sehingga proses server tidak dibanjiri dengan permintaan bersamaan yang akan memperlambatnya.

Ketika sebuah layanan digawangi oleh proxy atau penyeimbang muatan yang mendukung koneksi maksimum, tampaknya logis untuk membuat jumlah thread pekerja pada server HTTP cocok dengan koneksi maksimum dalam proxy. Namun, konfigurasi ini akan mengatur layanan untuk spiral ke bawah selama pembatasan daya. Pemeriksaan kondisi proxy juga memerlukan koneksi, dan karenanya penting untuk membuat kumpulan pekerja server cukup besar untuk mengakomodasi permintaan pemeriksaan kondisi tambahan. Pekerja menganggur itu murah, jadi kami cenderung mengonfigurasi yang ekstra: di mana saja dari segelintir pekerja tambahan untuk menggandakan koneksi maksimum proxy yang dikonfigurasi.

Strategi lain yang kami gunakan untuk memprioritaskan pemeriksaan kondisi adalah server mengimplementasikan penerapan permintaan serentak maksimum mereka sendiri. Dalam hal ini, pemeriksaan kondisi penyeimbang muatan selalu diizinkan, tetapi permintaan normal ditolak jika server sudah bekerja pada ambang tertentu. Implementasi seputar Amazon berkisar dari semafor sederhana di Java hingga analisis tren penggunaan CPU yang lebih kompleks.

Cara lain untuk membantu memastikan bahwa layanan merespons permintaan ping pemeriksaan kondisi dengan tepat waktu adalah dengan melakukan logika pemeriksaan kondisi dependensi di thread latar belakang dan memperbarui bendera isHealthy yang diperiksa oleh logika ping. Dalam hal ini, server segera merespons pemeriksaan kondisi, dan pemeriksaan kondisi dependensi menghasilkan muatan yang dapat diprediksi pada sistem eksternal yang berinteraksi dengannya. Ketika tim melakukan ini, mereka ekstra hati-hati dalam mendeteksi kegagalan thread pemeriksaan kondisi. Jika thread latar belakang keluar, server tidak mendeteksi kegagalan server di masa mendatang (atau pemulihan!).

Menyeimbangkan pemeriksaan kondisi dependensi dengan cakupan dampak

Pemeriksaan kondisi dependensi menarik karena mereka bertindak sebagai tes menyeluruh terhadap kondisi server. Sayangnya mereka bisa berbahaya karena dependensi dapat menyebabkan kegagalan berjenjang di seluruh sistem.

Kami dapat menggambarkan beberapa wawasan tentang penanganan dependensi pemeriksaan kondisi dengan melihat arsitektur berorientasi layanan kami di Amazon. Setiap layanan di Amazon dirancang untuk melakukan beberapa hal; tidak ada monolit yang melakukan segalanya. Ada banyak alasan kami ingin membangun layanan dengan cara ini, termasuk inovasi lebih cepat dengan tim kecil dan mengurangi ruang lingkup dampak jika ada masalah dengan satu layanan. Desain arsitektur ini dapat diterapkan ke pemeriksaan kondisi juga.

Ketika satu layanan memanggil layanan lainnya, layanan ini mengambil dependensi pada layanan tersebut. Jika suatu layanan hanya memanggil dependensi beberapa kali, kami mungkin menganggap dependensi sebagai "ketergantungan lunak," karena layanan masih dapat melakukan beberapa jenis pekerjaan bahkan jika ia tidak dapat berbicara dengan dependensi. Tanpa perlindungan gagal membuka, menerapkan pemeriksaan kondisi yang menguji ketergantungan mengubah ketergantungan itu menjadi "ketergantungan keras". Jika dependensi menurun, layanan juga turun, menciptakan kegagalan berjenjang dengan peningkatan cakupan dampak.

Meskipun kami memisahkan fungsionalitas menjadi layanan yang berbeda, setiap layanan kemungkinan melayani banyak API. Terkadang, API pada layanan memiliki dependensi mereka sendiri. Jika satu API terpengaruh, kami lebih memilih layanan untuk terus melayani API lainnya. Misalnya, suatu layanan dapat berupa bidang kontrol (seperti API CRUD yang terkadang disebut pada sumber daya yang berumur panjang) dan bidang data (API bisnis super penting throughput tinggi). Kami ingin API bidang data terus beroperasi meskipun API bidang kontrol mengalami kesulitan berbicara dengan dependensi mereka.

Demikian pula, bahkan satu API pun dapat berperilaku berbeda tergantung pada input atau keadaan data. Pola umum adalah Baca API yang membuat kueri ke database tetapi cache merespons secara lokal untuk beberapa waktu. Jika database turun, layanan masih dapat melayani pembacaan di cache hingga database kembali online. Gagal memeriksa kondisi jika hanya satu jalur kode yang tidak sehat meningkatkan cakupan dampak masalah ketika membicarakan dependensi.

Diskusi tentang dependensi terhadap pemeriksaan kondisi ini menimbulkan pertanyaan menarik tentang trade-off antara layanan mikro dan layanan yang relatif monolitik. Jarang ada aturan yang jelas untuk berapa banyak unit yang dapat digunakan atau titik akhir untuk memecah layanan, tetapi pertanyaan tentang "dependensi mana yang harus diperiksa kondisinya" dan "apakah kegagalan kemudian meningkatkan cakupan dampak" adalah lensa yang menarik untuk digunakan dalam menentukan cara mikro atau makro untuk membuat layanan. 

Hal-hal nyata yang salah dengan pemeriksaan kondisi

Semua ini mungkin masuk akal secara teori, tetapi apa yang terjadi pada sistem dalam praktiknya ketika mereka tidak mendapatkan pemeriksaan kondisi dengan benar? Kami mencari pola dalam cerita dari pelanggan AWS dan dari sekitar Amazon untuk membantu mengilustrasikan gambaran yang lebih besar. Kami juga melihat faktor kompensasi - jenis "metode" yang diterapkan tim untuk mencegah kelemahan dalam pemeriksaan kondisi dari menyebabkan masalah yang meluas.

Penerapan

Satu pola masalah pemeriksaan kondisi melibatkan penerapan. Sistem penerapan seperti AWS CodeDeploy mendorong kode baru ke satu bagian armada pada satu waktu, menunggu satu gelombang penerapan untuk menyelesaikan sebelum pindah ke yang berikutnya. Proses ini bergantung pada server yang melaporkan kembali ke sistem penerapan setelah mereka menindaklanjuti dan menjalankan kode baru. Jika mereka tidak melaporkan kembali, sistem penerapan melihat ada yang salah dengan kode baru dan memutar kembali penerapan.

Skrip penerapan startup layanan paling dasar hanya akan memotong proses server dan segera menanggapi "penerapan yang dilakukan" ke sistem penerapan. Namun hal ini berbahaya karena begitu banyak kemungkinan terjadi kesalahan dengan kode baru: kode baru bisa macet tepat setelah diluncurkan, menggantung dan gagal untuk mulai mendengarkan pada soket server, gagal memuat konfigurasi yang diperlukan untuk memproses permintaan dengan sukses, atau menemukan bug. Ketika suatu sistem penerapan tidak dikonfigurasikan untuk menguji pemeriksaan kondisi dependensi, sistem tidak menyadari bahwa sedang mendorong penerapan yang buruk. Sistem berjalan sembari merusak satu server dan server lainnya.

Untungnya, dalam praktiknya tim Amazon menerapkan beberapa sistem mitigasi untuk mencegah skenario ini mengambil seluruh armada mereka. Salah satu mitigasi tersebut adalah mengonfigurasi alarm yang memicu kapan pun ukuran armada keseluruhan terlalu kecil atau berjalan pada muatan tinggi, atau ketika ada latensi tinggi atau tingkat kesalahan. Jika salah satu dari alarm ini memicu, sistem penerapan menghentikan penerapan dan memutar kembali.

Jenis mitigasi lainnya menggunakan penerapan bertahap. Sebagai ganti mengerahkan seluruh armada dalam satu penempatan, layanan ini dapat dikonfigurasikan untuk menggunakan subset, mungkin Availability Zone, sebelum menjeda dan menjalankan serangkaian uji integrasi penuh terhadap zona itu. Penyelarasan penerapan per Availability Zone ini mudah karena layanan sudah dirancang untuk dapat tetap beroperasi jika ada masalah dengan satu Availability Zone.

Dan tentu saja sebelum melakukan produksi, tim Amazon mendorong perubahan itu melalui lingkungan pengujian dan menjalankan tes integrasi otomatis yang akan menangkap kegagalan jenis ini. Namun, perbedaan rumit dan tak terhindarkan antara produksi dan lingkungan pengujian mungkin ada, jadi penting untuk menggabungkan banyak lapisan keselamatan penerapan untuk menangkap semua jenis masalah sebelum menyebabkan dampak dalam produksi. Meskipun pemeriksaan kondisi penting untuk melindungi layanan terhadap penerapan yang buruk, kami memastikan untuk tidak berhenti di situ. Kami berpikir tentang pendekatan “metode” yang berfungsi sebagai pendukung untuk melindungi armada dari kesalahan ini dan lainnya.

Prosesor asinkron

Pola kegagalan lainnya adalah sekitar pemrosesan pesan yang tidak sinkron, seperti layanan yang bekerja dengan polling SQS Queue atau Amazon Kinesis Stream. Tidak seperti dalam sistem yang menerima permintaan dari penyeimbang muatan, tidak ada hal yang secara otomatis melakukan pemeriksaan kondisi untuk menghapus server dari layanan.

Ketika layanan tidak memiliki pemeriksaan kondisi yang cukup dalam, server pekerja antrean dapat mengalami kegagalan seperti disk yang penuh atau kehabisan file deskriptor. Masalah ini tidak akan menghentikan server untuk menarik antrean kerja, tetapi akan menghentikan server agar tidak dapat memproses pesan dengan sukses. Masalah ini mengakibatkan pemrosesan pesan tertunda, di mana server yang buruk menarik pekerjaan dari antrean dengan cepat dan gagal mengatasinya.

Dalam situasi seperti ini, seringkali ada beberapa faktor kompensasi untuk membantu mengendalikan dampaknya. Misalnya, jika server gagal memproses pesan yang menarik SQS, SQS akan mengembalikan pesan itu ke server lain setelah batas waktu visibilitas pesan yang dikonfigurasi. Latensi end-to-end meningkat, tetapi pesan tidak dijatuhkan. Faktor kompensasi lainnya adalah alarm yang berbunyi ketika ada terlalu banyak kesalahan dalam memproses pesan, memperingatkan operator untuk menyelidiki.

Disk terisi

Kelas kegagalan lain yang kita lihat adalah ketika disk di server terisi, menyebabkan pemrosesan dan pengelogan gagal. Kegagalan ini menyebabkan celah dalam visibilitas pemantauan, karena server mungkin tidak dapat melaporkan kegagalannya ke sistem pemantauan.

Sekali lagi, beberapa kontrol mitigasi menjaga layanan dari “terbang buta” dan mengurangi dampak dengan cepat. Sistem yang digawangi oleh proxy seperti Application Load Balancer atau API Gateway akan memiliki tingkat kesalahan dan metrik latensi yang dihasilkan oleh proxy itu. Dalam hal ini, alarm akan menyala meskipun server tidak melaporkannya. Untuk sistem berbasis antrean, layanan seperti Amazon Simple Queue Service (Amazon SQS) melaporkan metrik yang menunjukkan bahwa pemrosesan tertunda untuk beberapa pesan.

Kesamaan dari solusi ini adalah bahwa ada beberapa lapisan pemantauan. Server sendiri melaporkan kesalahan, tetapi demikian pula sistem eksternal. Prinsip yang sama penting dengan pemeriksaan kondisi. Sistem eksternal dapat menguji kondisi sistem yang diberikan lebih akurat daripada yang dapat menguji sendiri. Inilah sebabnya mengapa dengan Auto Scaling AWS, tim mengonfigurasi penyeimbang muatan untuk melakukan pemeriksaan kondisi ping eksternal.

Tim juga menulis sistem pemeriksaan kondisi kustom mereka sendiri untuk secara berkala menanyakan setiap server apakah kondisnya baik dan melaporkan ke Auto Scaling AWS ketika server tidak sehat. Salah satu implementasi umum dari sistem ini melibatkan fungsi Lambda yang berjalan setiap menit, menguji kondisi setiap server. Pemeriksaan kondisi ini bahkan dapat menyelamatkan status mereka di antara setiap proses dalam DynamoDB sehingga mereka tidak secara tidak sengaja menandai terlalu banyak kondisi server tidak baik sekaligus.

Zombi

Pola masalah lainnya termasuk server zombi. Server dapat terputus dari jaringan untuk periode waktu tertentu tetapi tetap berjalan, atau mereka dapat mati untuk waktu yang lama dan kemudian dinyalakan kembali.

Ketika server zombi hidup kembali, mereka dapat menjadi tidak sinkron secara signifikan bersama armada lainnya, yang dapat menyebabkan masalah serius. Misalnya, jika server zombi menjalankan versi perangkat lunak yang jauh lebih lama dan tidak kompatibel, hal ini dapat menyebabkan kegagalan ketika mencoba berinteraksi dengan database dengan skema yang berbeda atau dapat menggunakan konfigurasi yang salah.

Untuk menghadapi zombi, sistem sering membalas pemeriksaan kondisi dengan versi perangkat lunak yang saat ini berjalan. Kemudian agen pemantau pusat kemudian membandingkan respons di seluruh armada untuk mencari apa pun yang menjalankan versi kedaluwarsa yang tak terduga dan mencegah server-server ini bergerak kembali ke layanan.

Penutup

Server, dan perangkat lunak yang berjalan di dalamnya, gagal karena berbagai jenis alasan aneh. Perangkat keras pada akhirnya rusak secara fisik. Sebagai pengembang perangkat lunak, kami akhirnya menulis beberapa bug seperti yang saya jelaskan di atas yang membuat perangkat lunak dalam keadaan rusak. Beberapa lapisan pemeriksaan, dari pemeriksaan kehidupan ringan hingga pemantauan pasif metrik per server, diperlukan untuk menangkap semua jenis mode kegagalan yang tidak terduga.

Ketika kegagalan ini terjadi, penting untuk mendeteksi mereka dan mengambil server yang terkena dampak dengan cepat. Namun, seperti halnya automasi armada, kami menambahkan pembatas laju, ambang, dan pemutus sirkuit tenaga yang mematikan automasi dan melibatkan manusia dalam situasi ketidakpastian atau dalam situasi ekstrem. Gagal membuka dan membangun aktor terpusat adalah strategi untuk menuai manfaat pemeriksaan kondisi mendalam dengan keamanan automasi tingkat terbatas.

Laboratorium praktik langsung

Coba beberapa prinsip yang telah Anda pelajari di sini dengan laboratorium praktik langsung.


Tentang penulis

David Yanacek adalah Senior Principal Engineer yang bekerja di AWS Lambda. David telah menjadi pengembang perangkat lunak di Amazon sejak tahun 2006, sebelumnya bekerja di Amazon DynamoDB dan AWS IoT, juga kerangka kerja layanan web internal serta sistem automasi pengoperasian armada. Salah satu kegiatan favorit David di kantor adalah melakukan analisis log dan menyelidiki metrik pengoperasian guna menemukan cara untuk membuat sistem berjalan semakin mulus dari waktu ke waktu.

Batas waktu, percobaan ulang, dan kemunduran dengan gangguan