Bagaimana wadah sespan ini sampai di sini [di Kubernetes]?

Catatan perev. : Dengan artikel ini, yang ditulis oleh Scott Rahner, seorang insinyur di Dow Jones, kami melanjutkan serangkaian banyak bahan yang menjelaskan bagaimana Kubernetes bekerja, bagaimana komponen dasarnya bekerja, saling terhubung dan digunakan. Kali ini ini adalah catatan praktis dengan kode sampel untuk membuat kait di Kubernetes, yang ditunjukkan oleh penulis "dengan dalih" untuk secara otomatis membuat wadah sespan.


(Foto oleh Gordon A. Maxwell, ditemukan di Internet.)

Ketika saya mulai mempelajari wadah sespan dan service mesh, saya perlu memahami bagaimana mekanisme kunci bekerja - penyisipan otomatis wadah sespan. Memang, dalam kasus menggunakan sistem seperti Istio atau Konsul, ketika wadah dengan aplikasi dikerahkan, wadah Utusan yang sudah dikonfigurasi tiba-tiba muncul di podnya (situasi serupa terjadi dengan Conduit, yang kami tulis di awal tahun - kira-kira Terjemahkan.) . Apa? Bagaimana? Jadi penelitian saya dimulai ...

Bagi mereka yang tidak tahu, wadah sespan adalah wadah yang ditempatkan di sebelah wadah aplikasi untuk "membantu" aplikasi ini dengan cara tertentu. Contoh penggunaan tersebut adalah proksi untuk mengelola lalu lintas dan mengakhiri sesi TLS, wadah untuk streaming log dan metrik, wadah untuk memindai masalah keamanan ... Idenya adalah untuk mengisolasi berbagai aspek dari seluruh aplikasi dari logika bisnis dengan menggunakan wadah terpisah untuk masing-masing fungsi.

Sebelum melanjutkan, saya akan menjabarkan harapan saya. Tujuan artikel ini bukan untuk menjelaskan seluk-beluk dan skenario penggunaan Docker, Kubernetes, service mesh, dll., Tetapi untuk menunjukkan satu pendekatan yang kuat untuk memperluas kemampuan teknologi ini. Artikel ini ditujukan bagi mereka yang sudah terbiasa dengan penggunaan teknologi ini atau, setidaknya, telah membaca banyak tentang mereka. Untuk mencoba bagian praktis dalam aksi, Anda akan membutuhkan mesin dengan Docker dan Kubernetes yang sudah dikonfigurasi. Cara termudah untuk melakukan ini adalah https://docs.docker.com/docker-for-windows/kubernetes/ (manual Windows yang bekerja dengan Docker untuk Mac). (Catatan perev .: Sebagai alternatif bagi pengguna Linux dan * nix-sistem, kami dapat menawarkan Minikube .)

Gambaran keseluruhan


Untuk memulai, mari kita lihat sedikit Kubernetes:


Kube Arch dilisensikan di bawah CC BY 4.0

Ketika Anda bermaksud untuk menyebarkan sesuatu ke Kubernetes, Anda harus mengirim objek ke kube-apiserver. Ini paling sering dilakukan dengan memberikan argumen atau file YAML ke kubectl. Dalam hal ini, server API melewati beberapa tahap sebelum langsung menempatkan data dalam etcd dan menjadwalkan tugas yang sesuai:



Urutan ini penting untuk memahami cara kerja penyisipan wadah sespan. Secara khusus, Anda perlu memperhatikan Kontrol Penerimaan , di mana Kubernetes memvalidasi dan, jika perlu, memodifikasi objek sebelum menyimpannya (untuk rincian lebih lanjut tentang langkah ini, lihat bab "Kontrol akses" di artikel ini - sekitar Terjemahan.) . Kubernetes juga memungkinkan Anda untuk mendaftar webhook yang dapat melakukan validasi dan mutasi yang ditentukan pengguna.

Namun, proses membuat dan mendaftarkan kait Anda tidak begitu sederhana dan didokumentasikan dengan baik. Saya harus menghabiskan beberapa hari membaca dan membaca kembali dokumentasi, serta menganalisis kode Istio dan Konsul. Dan ketika sampai pada kode untuk beberapa tanggapan API, saya menghabiskan setidaknya setengah hari melakukan percobaan dan kesalahan acak.

Setelah hasilnya tercapai, saya pikir tidak adil untuk tidak membagikannya kepada Anda semua. Ini sederhana dan sekaligus efektif.

Kode


Nama webhook berbicara untuk dirinya sendiri - itu adalah titik akhir HTTP yang mengimplementasikan API yang didefinisikan dalam Kubernetes. Anda membuat server API yang dapat dipanggil Kubernetes sebelum berurusan dengan Penyebaran. Saya harus berurusan dengan kesulitan di sini, karena hanya beberapa contoh yang tersedia, beberapa di antaranya hanya unit test Kubernetes, yang lain tersembunyi di tengah-tengah basis kode besar ... dan semua ditulis dalam Go. Tapi saya memilih opsi yang lebih terjangkau - Node.js:

const app = express(); app.use(bodyParser.json()); app.post('/mutate', (req, res) => { console.log(req.body) console.log(req.body.request.object) let adminResp = {response:{ allowed: true, patch: Buffer.from("[{ \"op\": \"add\", \"path\": \"/metadata/labels/foo\", \"value\": \"bar\" }]").toString('base64'), patchType: "JSONPatch", }} console.log(adminResp) res.send(adminResp) }) const server = https.createServer(options, app); 

( index.js )

Jalur ke API - dalam hal ini /mutate - dapat berubah-ubah (hanya sesuai dengan YAML yang diteruskan ke Kubernetes di masa mendatang). Penting baginya untuk melihat dan memahami JSON yang diterima dari server API. Dalam hal ini, kami tidak menarik apa pun dari JSON, tetapi mungkin berguna dalam skenario lain. Dalam kode di atas, kami memperbarui JSON. Dua hal diperlukan untuk ini:

  1. Pelajari dan pahami JSON Patch .
  2. Konversi ekspresi Patch JSON dengan benar menjadi array byte yang disandikan dengan base64.

Setelah ini selesai, yang harus Anda lakukan adalah memberikan respons ke server API dengan objek yang sangat sederhana. Dalam hal ini, kami menambahkan label foo=bar sembarang pod yang datang kepada kami.

Penempatan


Ya, kami memiliki kode yang menerima permintaan dari server API Kubernetes dan meresponsnya, tetapi bagaimana cara menggunakannya? Dan bagaimana cara mendapatkan Kubernetes untuk mengarahkan permintaan ini kepada kami? Anda dapat menggunakan titik akhir seperti itu di mana saja Anda dapat mencapai server API Kubernetes. Cara paling sederhana adalah dengan menyebarkan kode ke kluster Kubernetes itu sendiri, yang akan kita lakukan dalam contoh ini. Saya mencoba membuat contoh sesederhana mungkin, jadi untuk semua tindakan saya hanya menggunakan Docker dan kubectl. Mari kita mulai dengan membuat wadah tempat kode akan dijalankan:

 FROM node:8 USER node WORKDIR /home/node COPY index.js . COPY package.json . RUN npm install #       TLS CMD node index.js 

( Dockerfile )

Ternyata, semuanya sangat sederhana di sini. Ambil gambar komunitas dari node dan masukkan kode ke dalamnya. Sekarang Anda dapat melakukan perakitan sederhana:

 docker build . -t localserver 

Langkah selanjutnya adalah membuat Penempatan di Kubernetes:

 apiVersion: apps/v1 kind: Deployment metadata: name: webhook-server spec: replicas: 1 selector: matchLabels: component: webhook-server template: metadata: labels: component: webhook-server spec: containers: - name: webhook-server imagePullPolicy: Never image: localserver 

( deployment.yaml )

Perhatikan bagaimana kita menyinggung gambar yang baru saja kita buat? Bisa juga berupa pod, dan hal lain yang bisa kita hubungkan dengan layanan di Kubernetes. Sekarang tentukan Layanan ini:

 apiVersion: v1 kind: Service metadata: name: webhook-service spec: ports: - port: 443 targetPort: 8443 selector: component: webhook-server 

Jadi di Kubernetes, titik akhir muncul dengan nama internal yang menunjuk ke wadah kami. Langkah terakhir adalah memberi tahu Kubernetes bahwa kami ingin server API memanggil layanan ini ketika siap membuat mutasi :

 apiVersion: admissionregistration.k8s.io/v1beta1 kind: MutatingWebhookConfiguration metadata: name: webhook webhooks: - name: webhook-service.default.svc failurePolicy: Fail clientConfig: service: name: webhook-service namespace: default path: "/mutate" #    base64-  rootCA.crt #    `cat rootCA.crt | base64 | tr -d '\n'` #    .  caBundle: "==" rules: - operations: [ "CREATE" ] apiGroups: [""] apiVersions: ["v1"] resources: ["pods"] 
( hook.yaml )

Nama dan jalur di sini dapat berupa apa saja, tetapi saya mencoba menjadikannya bermakna mungkin. Mengubah jalur berarti perlunya memodifikasi kode yang sesuai dalam JavaScript. Webhook failurePolicy juga failurePolicy - menentukan apakah objek harus disimpan jika kail mengembalikan kesalahan atau gagal. Dalam hal ini, kami memberi tahu Kubernetes untuk tidak melanjutkan pemrosesan. Akhirnya, aturan: mereka akan berubah tergantung pada API panggilan mana yang Anda harapkan dari Kubernetes. Dalam hal ini, karena kami mencoba untuk meniru penyisipan wadah sespan, kami perlu mencegat permintaan untuk membuat pod.

Itu saja! Sangat sederhana ... tapi bagaimana dengan keamanan? RBAC adalah salah satu aspek yang tidak tercakup dalam artikel. Saya berasumsi bahwa Anda menjalankan contoh di Minikube atau di Kubernetes, yang dilengkapi dengan Docker untuk Windows / Mac. Namun, saya akan memberi tahu Anda tentang elemen lain yang diperlukan. Server API Kubernetes hanya mengakses titik akhir dengan HTTPS, sehingga aplikasi memerlukan sertifikat SSL. Anda juga perlu memberi tahu Kubernetes siapa otoritas sertifikasi dari sertifikat root.

TLS


Untuk tujuan demonstrasi saja (!!!), saya menambahkan beberapa kode ke Dockerfile untuk membuat root CA dan menggunakannya untuk menandatangani sertifikat:

 RUN openssl genrsa -out rootCA.key 4096 RUN openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt \ -subj "/C=US/ST=New Jersey/L=Princeton /O=Dow Jones/OU=PIB/CN=*.default.svc/emailAddress=scott.rahner@dowjones.com" RUN openssl genrsa -out webhook.key 4096 RUN openssl req -new -key webhook.key -out webhook.csr \ -subj "/C=US/ST=New Jersey/L=Princeton /O=Dow Jones/OU=PIB/CN=webhook-service.default.svc/emailAddress=scott.rahner@dowjones.com" RUN openssl x509 -req -in webhook.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out webhook.crt -days 1024 -sha256 RUN cat rootCA.crt | base64 | tr -d '\n' 

( Dockerfile )

Harap dicatat: langkah terakhir adalah menampilkan satu baris dengan root CA yang disandikan di base64. Ini adalah persis apa yang diperlukan untuk konfigurasi hook, jadi dalam pengujian Anda selanjutnya, pastikan untuk menyalin baris ini ke bidang caBundle dari file caBundle . Dockerfile melempar sertifikat langsung ke WORKDIR , jadi JavaScript hanya mengambilnya dari sana dan menggunakannya untuk server:

 const privateKey = fs.readFileSync('webhook.key').toString(); const certificate = fs.readFileSync('webhook.crt').toString(); //… const options = {key: privateKey, cert: certificate}; const server = https.createServer(options, app); 

Sekarang kode mendukung peluncuran HTTPS, dan juga memberi tahu Kubernetes di mana menemukan kami dan pusat kepercayaan mana yang harus dipercaya. Tetap hanya untuk menanamkan semuanya ke dalam kluster:

 kubectl create -f deployment.yaml kubectl create -f service.yaml kubectl create -f hook.yaml 

Ringkaslah


  • Deployment.yaml meluncurkan wadah yang melayani API hook melalui HTTPS dan mengembalikan Patch JSON untuk memodifikasi objek.
  • Service.yaml menyediakan titik akhir untuk wadah - webhook-service.default.svc .
  • Hook.yaml memberi tahu server API tempat untuk menemukan kami: https://webhook-service.default.svc/mutate .

Mari kita coba dalam bisnis!


Semuanya dikerahkan dalam sebuah cluster - saatnya untuk mencoba kode dalam tindakan, yang akan kita lakukan dengan menambahkan pod / Penempatan baru. Jika semuanya berfungsi dengan benar, hook harus menambahkan label foo tambahan:

 apiVersion: apps/v1 kind: Deployment metadata: name: test spec: replicas: 1 selector: matchLabels: component: test template: metadata: labels: component: test spec: containers: - name: test image: node:8 command: [ "/bin/sh", "-c", "--" ] args: [ "while true; do sleep 30; done;" ] 

( test.yaml )

 kubectl create -f test.yaml 

Oke, kami melihat deployment.apps test created ... tetapi apakah berhasil?

 kubectl describe pods test Name: test-6f79f9f8bd-r7tbd Namespace: default Node: docker-for-desktop/192.168.65.3 Start Time: Sat, 10 Nov 2018 16:08:47 -0500 Labels: component=test foo=bar 

Hebat! Meskipun test.yaml diberi label tunggal ( component ), pod yang dihasilkan menerima dua: component dan foo .

PR


Tapi tunggu! Apakah kita akan menggunakan kode ini untuk membuat wadah sespan? Saya memperingatkan bahwa saya akan menunjukkan cara menambahkan sespan ... Dan sekarang, dengan pengetahuan dan kode: https://github.com/dowjones/k8s-webhook - jangan ragu untuk bereksperimen dan mencari cara untuk membuat sespan yang dimasukkan secara otomatis. Ini cukup sederhana: Anda hanya perlu menyiapkan JSON Patch yang benar, yang akan menambahkan wadah tambahan dalam penyebaran Penerapan. Selamat orkestra!

PS dari penerjemah


Baca juga di blog kami:

Source: https://habr.com/ru/post/id431252/


All Articles