
Saat membangun proses CI / CD menggunakan Kubernetes, kadang-kadang ada masalah ketidakcocokan persyaratan infrastruktur baru dan aplikasi yang ditransfer ke sana. Secara khusus, pada tahap perakitan aplikasi, penting untuk mendapatkan
satu gambar yang akan digunakan di
semua lingkungan dan kluster proyek. Prinsip ini mendasari manajemen kontainer yang benar menurut pendapat
Google (teknisi kami telah berulang kali
membicarakan hal ini).
Namun, Anda tidak akan mengejutkan siapa pun dengan situasi ketika kerangka kerja yang sudah jadi digunakan dalam kode situs, penggunaan yang memberlakukan pembatasan pada operasi lebih lanjut. Dan jika mudah ditangani di "lingkungan normal," di Kubernetes, perilaku semacam ini bisa menjadi masalah, terutama ketika Anda menjumpainya pertama kali. Meskipun pikiran yang cerdik mampu menawarkan solusi infrastruktur yang tampak jelas dan bahkan cukup baik pada pandangan pertama ... penting untuk diingat bahwa sebagian besar situasi dapat dan harus
diselesaikan secara arsitektur .
Mari kita menganalisis solusi-solusi populer untuk menyimpan file, yang dapat menyebabkan konsekuensi yang tidak menyenangkan selama operasi cluster, dan juga menunjuk ke jalur yang lebih benar.
Penyimpanan Statis
Untuk menggambarkan, pertimbangkan aplikasi web yang menggunakan generator statis untuk memperoleh serangkaian gambar, gaya, dan banyak lagi. Misalnya, kerangka kerja PHP Yii memiliki pengelola aset bawaan yang menghasilkan nama direktori unik. Dengan demikian, outputnya adalah serangkaian jalur non-berpotongan yang sengaja dibuat untuk statika situs (ini dilakukan karena beberapa alasan - misalnya, untuk menghilangkan duplikat saat menggunakan sumber daya yang sama dengan banyak komponen). Jadi, di luar kotak, ketika Anda pertama kali mengakses modul sumber daya web, statika dibentuk dan diletakkan (pada kenyataannya, sering kali symlink, tetapi lebih lanjut tentang itu nanti) dengan direktori root umum yang unik untuk penyebaran ini:
webroot/assets/2072c2df/css/β¦
webroot/assets/2072c2df/images/β¦
webroot/assets/2072c2df/js/β¦
Ada apa dengan kluster ini?
Contoh paling sederhana
Mari kita ambil kasus yang cukup umum ketika PHP menghadapi nginx untuk mendistribusikan statika dan menangani pertanyaan sederhana. Cara termudah adalah
Penempatan dengan dua wadah:
apiVersion: apps/v1 kind: Deployment metadata: name: site spec: selector: matchLabels: component: backend template: metadata: labels: component: backend spec: volumes: - name: nginx-config configMap: name: nginx-configmap containers: - name: php image: own-image-with-php-backend:v1.0 command: ["/usr/local/sbin/php-fpm","-F"] workingDir: /var/www - name: nginx image: nginx:1.16.0 command: ["/usr/sbin/nginx", "-g", "daemon off;"] volumeMounts: - name: nginx-config mountPath: /etc/nginx/conf.d/default.conf subPath: nginx.conf
Dalam bentuk yang disederhanakan, konfigurasi nginx bermuara sebagai berikut:
apiVersion: v1 kind: ConfigMap metadata: name: "nginx-configmap" data: nginx.conf: | server { listen 80; server_name _; charset utf-8; root /var/www; access_log /dev/stdout; error_log /dev/stderr; location / { index index.php; try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; } }
Ketika Anda pertama kali mengakses situs dalam wadah dengan PHP, aset muncul. Tetapi dalam kasus dua kontainer dalam pod yang sama, nginx tidak tahu apa-apa tentang file-file statis ini, yang (sesuai dengan konfigurasi) harus diberikan kepada mereka. Akibatnya, klien akan melihat kesalahan 404 untuk semua permintaan file CSS dan JS. Solusi paling sederhana di sini adalah mengatur direktori umum untuk kontainer. Opsi primitif adalah generik
emptyDir
:
apiVersion: apps/v1 kind: Deployment metadata: name: site spec: selector: matchLabels: component: backend template: metadata: labels: component: backend spec: volumes: - name: assets emptyDir: {} - name: nginx-config configMap: name: nginx-configmap containers: - name: php image: own-image-with-php-backend:v1.0 command: ["/usr/local/sbin/php-fpm","-F"] workingDir: /var/www volumeMounts: - name: assets mountPath: /var/www/assets - name: nginx image: nginx:1.16.0 command: ["/usr/sbin/nginx", "-g", "daemon off;"] volumeMounts: - name: assets mountPath: /var/www/assets - name: nginx-config mountPath: /etc/nginx/conf.d/default.conf subPath: nginx.conf
Sekarang file statis yang dihasilkan dalam wadah diberikan oleh nginx dengan benar. Tetapi izinkan saya mengingatkan Anda bahwa ini adalah solusi primitif, yang berarti itu jauh dari ideal dan memiliki nuansa dan kekurangannya sendiri, yang dibahas di bawah ini.
Penyimpanan lebih canggih
Sekarang bayangkan sebuah situasi ketika seorang pengguna mengunjungi sebuah situs, memuat halaman dengan gaya yang tersedia dalam wadah, dan ketika dia membaca halaman ini, kami menyebarkan kembali wadah tersebut. Direktori aset menjadi kosong dan membutuhkan permintaan ke PHP untuk mulai membuat yang baru. Namun, bahkan setelah ini, tautan ke statika lama akan kedaluwarsa, yang akan menyebabkan kesalahan dalam menampilkan statika.
Selain itu, kemungkinan besar kami memiliki proyek yang dimuat lebih atau kurang, yang berarti bahwa satu salinan aplikasi tidak akan cukup:
- Skala Penempatan ke dua replika.
- Ketika Anda pertama kali mengakses situs dalam satu replika, aset dibuat.
- Pada titik tertentu, ingress memutuskan (untuk menyeimbangkan beban) untuk mengirim permintaan untuk replika kedua, dan aset ini belum ada di sana. Atau mungkin mereka tidak lagi di sana, karena kami menggunakan
RollingUpdate
dan saat ini sedang melakukan penyebaran.
Secara umum, hasilnya adalah kesalahan lagi.
Agar tidak kehilangan aset lama, Anda dapat mengubah
emptyDir
ke
hostPath
, menambahkan statika secara fisik ke simpul cluster. Pendekatan ini buruk karena kita sebenarnya harus
mengikat node cluster tertentu dengan aplikasi kita, karena - dalam kasus pindah ke node lain - direktori tidak akan berisi file yang diperlukan. Atau, beberapa sinkronisasi latar belakang direktori antara node diperlukan.
Apa solusinya?
- Jika perangkat keras dan sumber daya memungkinkan, Anda dapat menggunakan cephfs untuk mengatur direktori yang sama - sama dapat diakses untuk kebutuhan statika. Dokumentasi resmi merekomendasikan SSD, setidaknya replikasi tiga kali lipat, dan koneksi "tebal" yang kuat antara node cluster.
- Opsi yang tidak terlalu menuntut adalah mengatur server NFS. Namun, maka Anda perlu mempertimbangkan kemungkinan peningkatan waktu respons untuk memproses permintaan oleh server web, dan toleransi kesalahan akan meninggalkan banyak yang harus diinginkan. Konsekuensi dari kegagalan adalah bencana: hilangnya gunung menghancurkan cluster sampai mati di bawah serangan beban LA yang bergegas ke langit.
Di antara hal-hal lain, untuk semua opsi untuk menciptakan penyimpanan yang persisten,
pembersihan latar belakang dari set file yang sudah usang yang diakumulasikan selama periode waktu tertentu akan diperlukan. Sebelum wadah dengan PHP, Anda dapat menempatkan
DaemonSet dari caching nginx, yang akan menyimpan salinan aset untuk waktu yang terbatas. Perilaku ini dapat dengan mudah dikonfigurasi menggunakan
proxy_cache
dengan kedalaman penyimpanan dalam beberapa hari atau gigabytes ruang disk.
Menggabungkan metode ini dengan sistem file terdistribusi yang disebutkan di atas menyediakan bidang besar untuk imajinasi, keterbatasan hanya dalam anggaran dan potensi teknis dari mereka yang akan menerapkan dan mendukungnya. Dari pengalaman, kami mengatakan bahwa semakin sederhana sistem, semakin stabil kerjanya. Dengan penambahan lapisan seperti itu, menjadi jauh lebih sulit untuk mempertahankan infrastruktur, dan pada saat yang sama, waktu yang dihabiskan untuk diagnostik dan pemulihan jika terjadi peningkatan kegagalan.
Rekomendasi
Jika implementasi opsi penyimpanan yang diusulkan juga tampaknya tidak dapat dibenarkan bagi Anda (rumit, mahal ...), maka Anda harus melihat situasi dari sisi lain. Yaitu, menggali ke dalam arsitektur proyek dan
memberantas masalah dalam kode dengan menghubungkan ke beberapa struktur data statis dalam gambar, memberikan definisi yang jelas tentang isi atau prosedur "pemanasan" dan / atau mengkompilasi aset pada tahap perakitan gambar. Jadi kita mendapatkan perilaku yang benar-benar dapat diprediksi dan kumpulan file yang sama untuk semua lingkungan dan replika aplikasi yang sedang berjalan.
Jika kita kembali ke contoh spesifik dengan kerangka kerja Yii dan tidak menyelidiki strukturnya (yang bukan merupakan tujuan artikel), itu sudah cukup untuk menunjukkan dua pendekatan populer:
- Ubah proses perakitan gambar sehingga aset ditempatkan di lokasi yang dapat diprediksi. Jadi tawarkan / terapkan dalam ekstensi seperti yii2-static-aset .
- Tetapkan hash spesifik untuk direktori aset, seperti yang dijelaskan, misalnya, dalam presentasi ini (dimulai dengan slide 35). Ngomong-ngomong, penulis laporan akhirnya (dan bukan tanpa alasan!) Menyarankan, setelah merakit aset pada server build, untuk mengunggahnya ke repositori pusat (seperti S3), di depan meletakkan CDN.
File yang Dapat Diunduh
Kasus lain yang pasti akan diaktifkan saat mentransfer aplikasi ke kluster Kubernetes adalah menyimpan file pengguna dalam sistem file. Sebagai contoh, kami sekali lagi memiliki aplikasi PHP yang menerima file melalui formulir unggah, melakukan sesuatu dengan mereka dalam proses, dan mengembalikannya.
Tempat di mana file-file ini harus ditempatkan dalam realitas Kubernetes harus umum untuk semua replika aplikasi. Bergantung pada kerumitan aplikasi dan kebutuhan untuk mengatur kegigihan file-file ini, tempat seperti itu mungkin merupakan opsi untuk perangkat bersama yang disebutkan di atas, tetapi, seperti yang kita lihat, mereka memiliki kelemahan.
Rekomendasi
Salah satu solusinya adalah dengan
menggunakan penyimpanan yang kompatibel S3 (bahkan jika beberapa jenis kategori self-host seperti minio). Transisi untuk bekerja dengan S3 akan memerlukan perubahan
pada level kode , dan kami telah
menulis bagaimana konten akan dikembalikan di frontend.
Sesi khusus
Secara terpisah, perlu dicatat organisasi penyimpanan sesi pengguna. Seringkali ini juga file pada disk, yang, dalam konteks Kubernetes, akan mengarah pada permintaan otorisasi konstan dari pengguna jika permintaannya jatuh ke wadah lain.
Sebagian masalah diselesaikan dengan memasukkan
stickySessions
pada masuknya
(fitur ini didukung di semua pengontrol masuk yang populer - lihat ulasan kami untuk lebih jelasnya) untuk mengikat pengguna ke pod tertentu dengan aplikasi:
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: nginx-test annotations: nginx.ingress.kubernetes.io/affinity: "cookie" nginx.ingress.kubernetes.io/session-cookie-name: "route" nginx.ingress.kubernetes.io/session-cookie-expires: "172800" nginx.ingress.kubernetes.io/session-cookie-max-age: "172800" spec: rules: - host: stickyingress.example.com http: paths: - backend: serviceName: http-svc servicePort: 80 path: /
Tetapi ini tidak akan menyelamatkan Anda dari penyebaran yang berulang.
Rekomendasi
Cara yang lebih benar adalah dengan mentransfer aplikasi ke
sesi penyimpanan dalam memcached, Redis, dan solusi serupa - secara umum, sepenuhnya mengabaikan opsi file.
Kesimpulan
Solusi infrastruktur yang dipertimbangkan dalam teks hanya layak diaplikasikan dalam format βkrukβ sementara (yang terdengar lebih indah dalam bahasa Inggris sebagai solusi). Mereka mungkin relevan pada tahap awal migrasi aplikasi ke Kubernetes, tetapi tidak boleh "di-root".
Cara umum yang disarankan adalah untuk menyingkirkan mereka demi penyempurnaan arsitektural dari aplikasi sesuai dengan
Aplikasi 12-Faktor yang sudah terkenal. Namun, ini - membawa aplikasi ke bentuk tanpa kewarganegaraan - pasti berarti bahwa perubahan dalam kode akan diperlukan, dan penting untuk menemukan keseimbangan antara kemampuan / persyaratan bisnis dan prospek untuk menerapkan dan memelihara jalur yang dipilih.
PS
Baca juga di blog kami: