File lokal saat memigrasikan aplikasi ke Kubernetes

File lokal saat memigrasikan aplikasi ke Kubernetes

Saat membangun proses CI/CD menggunakan Kubernetes, terkadang muncul masalah ketidakcocokan antara persyaratan infrastruktur baru dan aplikasi yang ditransfer ke dalamnya. Khususnya, pada tahap pembuatan aplikasi, hal ini penting untuk didapatkan satu gambar yang akan digunakan Semua lingkungan proyek dan cluster. Prinsip ini mendasari kebenarannya menurut Google manajemen kontainer (lebih dari sekali tentang ini berbicara dan departemen teknis kami).

Namun, Anda tidak akan melihat siapa pun dalam situasi di mana kode situs menggunakan kerangka kerja yang sudah jadi, yang penggunaannya membatasi penggunaan lebih lanjut. Meskipun dalam β€œlingkungan normal” hal ini mudah untuk diatasi, di Kubernetes perilaku ini bisa menjadi masalah, terutama saat Anda menghadapinya untuk pertama kali. Meskipun orang yang berpikiran inventif dapat menghasilkan solusi infrastruktur yang tampak jelas atau bahkan bagus pada pandangan pertama... penting untuk diingat bahwa sebagian besar situasi dapat dan harus dilakukan. diselesaikan secara arsitektural.

Mari kita lihat solusi solusi populer untuk menyimpan file yang dapat menyebabkan konsekuensi tidak menyenangkan saat mengoperasikan cluster, dan juga menunjukkan jalur yang lebih benar.

Penyimpanan statis

Sebagai ilustrasi, pertimbangkan aplikasi web yang menggunakan semacam generator statis untuk mendapatkan sekumpulan gambar, gaya, dan hal lainnya. Misalnya, framework Yii PHP memiliki manajer aset bawaan yang menghasilkan nama direktori unik. Oleh karena itu, keluarannya adalah sekumpulan jalur untuk situs statis yang jelas-jelas tidak bersinggungan satu sama lain (hal ini dilakukan karena beberapa alasan - misalnya, untuk menghilangkan duplikat ketika beberapa komponen menggunakan sumber daya yang sama). Jadi, pertama kali Anda mengakses modul sumber daya web, file statis (pada kenyataannya, sering kali berupa symlink, tetapi akan dibahas lebih lanjut nanti) dibentuk dan ditata dengan direktori root umum yang unik untuk penerapan ini:

  • webroot/assets/2072c2df/css/…
  • webroot/assets/2072c2df/images/…
  • webroot/assets/2072c2df/js/…

Apa artinya ini dalam cluster?

Contoh paling sederhana

Mari kita ambil kasus yang cukup umum, ketika PHP didahului oleh nginx untuk mendistribusikan data statis dan memproses permintaan sederhana. Cara termudah - Penyebaran 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 adalah 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;
        }
    }

Saat Anda pertama kali mengakses situs ini, aset muncul di wadah PHP. Namun dalam kasus dua container dalam satu pod, nginx tidak mengetahui apa pun tentang file statis ini, yang (menurut konfigurasi) harus diberikan kepada mereka. Akibatnya, klien akan melihat kesalahan 404 untuk semua permintaan ke file CSS dan JS. Solusi paling sederhana di sini adalah dengan mengatur direktori umum untuk container. Opsi primitif bersifat umum 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 disajikan oleh nginx dengan benar. Namun izinkan saya mengingatkan Anda bahwa ini adalah solusi primitif, yang berarti jauh dari ideal dan memiliki nuansa serta kekurangan tersendiri, yang akan dibahas di bawah ini.

Penyimpanan lebih canggih

Sekarang bayangkan situasi ketika pengguna mengunjungi situs, memuat halaman dengan gaya yang tersedia di penampung, dan saat dia membaca halaman ini, kami menerapkan kembali penampung tersebut. Katalog aset menjadi kosong dan permintaan ke PHP diperlukan untuk mulai membuat yang baru. Namun, bahkan setelah ini, tautan ke statika lama tidak akan relevan, yang akan menyebabkan kesalahan dalam menampilkan statika.

Selain itu, kemungkinan besar kami memiliki proyek yang lebih banyak atau lebih sedikit dimuat, yang berarti satu salinan aplikasi tidak akan cukup:

  • Mari kita tingkatkan Penyebaran hingga dua replika.
  • Saat situs pertama kali diakses, aset dibuat dalam satu replika.
  • Pada titik tertentu, ingress memutuskan (untuk tujuan penyeimbangan beban) untuk mengirim permintaan ke replika kedua, dan aset tersebut belum ada. Atau mungkin sudah tidak ada lagi karena kita pakai RollingUpdate dan saat ini kami sedang melakukan penerapan.

Secara umum, hasilnya lagi-lagi kesalahan.

Untuk menghindari kehilangan aset lama, Anda bisa mengubahnya emptyDir pada hostPath, menambahkan statis secara fisik ke node cluster. Pendekatan ini buruk karena sebenarnya kita harus melakukannya mengikat ke node cluster tertentu aplikasi Anda, karena - jika dipindahkan ke node lain - direktori tidak akan berisi file yang diperlukan. Atau diperlukan semacam sinkronisasi direktori latar belakang antar node.

Apa solusinya?

  1. Jika perangkat keras dan sumber daya memungkinkan, Anda dapat menggunakannya cephfs untuk mengatur direktori yang sama-sama dapat diakses untuk kebutuhan statis. Dokumentasi resmi merekomendasikan drive SSD, setidaknya replikasi tiga kali lipat dan koneksi β€œtebal” yang stabil antar node cluster.
  2. Pilihan yang tidak terlalu menuntut adalah mengatur server NFS. Namun, Anda perlu memperhitungkan kemungkinan peningkatan waktu respons untuk memproses permintaan oleh server web, dan toleransi kesalahan akan meninggalkan banyak hal yang diinginkan. Konsekuensi dari kegagalan tersebut sangatlah besar: hilangnya tunggangan tersebut menyebabkan cluster tersebut mati di bawah tekanan beban LA yang melesat ke langit.

Antara lain, semua opsi untuk membuat penyimpanan persisten akan diperlukan pembersihan latar belakang kumpulan file usang yang terakumulasi selama periode waktu tertentu. Di depan wadah dengan PHP Anda dapat meletakkannya Set Daemon dari caching nginx, yang akan menyimpan salinan aset untuk waktu terbatas. Perilaku ini mudah dikonfigurasi menggunakan proxy_cache dengan kedalaman penyimpanan dalam hitungan hari atau gigabyte ruang disk.

Menggabungkan metode ini dengan sistem file terdistribusi yang disebutkan di atas memberikan ruang imajinasi yang luas, hanya dibatasi oleh anggaran dan potensi teknis dari mereka yang akan menerapkan dan mendukungnya. Dari pengalaman, kita dapat mengatakan bahwa semakin sederhana sistemnya, semakin stabil kerjanya. Ketika lapisan-lapisan tersebut ditambahkan, pemeliharaan infrastruktur menjadi jauh lebih sulit, dan pada saat yang sama waktu yang dihabiskan untuk mendiagnosis dan memulihkan kegagalan pun meningkat.

Rekomendasi

Jika penerapan opsi penyimpanan yang diusulkan juga tampaknya tidak dapat dibenarkan bagi Anda (rumit, mahal...), maka ada baiknya melihat situasi dari sisi lain. Yaitu, untuk menggali arsitektur proyek dan memperbaiki masalah dalam kode, terkait dengan beberapa struktur data statis dalam gambar, definisi yang jelas tentang konten atau prosedur untuk β€œpemanasan” dan/atau kompilasi awal aset pada tahap perakitan gambar. Dengan cara ini 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 framework Yii dan tidak mempelajari strukturnya (yang bukan merupakan tujuan artikel), cukup dengan menunjukkan dua pendekatan populer:

  1. Ubah proses pembuatan gambar untuk menempatkan aset di lokasi yang dapat diprediksi. Ini disarankan/diimplementasikan dalam ekstensi seperti yii2-aset statis.
  2. Tentukan hash spesifik untuk direktori aset, seperti yang dibahas di mis. presentasi ini (mulai dari slide no.35). Omong-omong, penulis laporan pada akhirnya (dan bukan tanpa alasan!) menyarankan bahwa setelah merakit aset di server build, unggah aset tersebut ke penyimpanan pusat (seperti S3), yang di depannya terdapat CDN.

Unduhan

Kasus lain yang pasti akan berperan ketika memigrasikan aplikasi ke cluster Kubernetes adalah menyimpan file pengguna di sistem file. Misalnya, kita kembali memiliki aplikasi PHP yang menerima file melalui formulir unggah, melakukan sesuatu dengannya selama pengoperasian, dan mengirimkannya kembali.

Di Kubernetes, lokasi penempatan file-file ini harus umum untuk semua replika aplikasi. Bergantung pada kompleksitas aplikasi dan kebutuhan untuk mengatur persistensi file-file ini, opsi perangkat bersama yang disebutkan di atas mungkin merupakan tempatnya, tetapi, seperti yang bisa kita lihat, opsi tersebut memiliki kekurangannya.

Rekomendasi

Salah satu solusinya adalah menggunakan penyimpanan yang kompatibel dengan S3 (walaupun itu semacam kategori yang dihosting sendiri seperti minio). Beralih ke S3 memerlukan perubahan pada tingkat kode, dan bagaimana konten akan dikirimkan di front-end, kami sudah melakukannya писали.

Sesi pengguna

Secara terpisah, perlu diperhatikan organisasi penyimpanan sesi pengguna. Seringkali ini juga merupakan file di disk, yang dalam konteks Kubernetes akan menyebabkan permintaan otorisasi terus-menerus dari pengguna jika permintaannya berakhir di container lain.

Masalahnya sebagian terpecahkan dengan menyalakannya stickySessions saat masuk (fitur ini didukung di semua pengontrol ingress yang populer - untuk lebih jelasnya, lihat ulasan kami)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: /

Namun hal ini tidak akan menghilangkan masalah dengan penerapan yang berulang-ulang.

Rekomendasi

Cara yang lebih tepat adalah dengan mentransfer aplikasi ke menyimpan sesi di memcached, Redis dan solusi serupa - secara umum, abaikan sepenuhnya opsi file.

Kesimpulan

Solusi infrastruktur yang dibahas dalam teks hanya layak digunakan dalam format β€œkruk” sementara (yang terdengar lebih indah dalam bahasa Inggris sebagai solusinya). Mereka mungkin relevan pada tahap pertama migrasi aplikasi ke Kubernetes, tetapi tidak akan mengakar.

Jalur umum yang direkomendasikan adalah membuangnya demi modifikasi arsitektur aplikasi sesuai dengan apa yang sudah diketahui banyak orang Aplikasi 12 Faktor. Namun, hal ini - membawa aplikasi ke bentuk tanpa kewarganegaraan - pasti berarti bahwa perubahan dalam kode akan diperlukan, dan di sini penting untuk menemukan keseimbangan antara kemampuan/persyaratan bisnis dan prospek penerapan dan pemeliharaan jalur yang dipilih. .

PS

Baca juga di blog kami:

Sumber: www.habr.com

Tambah komentar