Jika kamu membaca ini, kamu mungkin pernah mendengar sesuatu tentang Kubernetes (dan jika belum, bagaimana kamu bisa sampai di sini?) Tapi apa sebenarnya Kubernetes itu? Ini “Orkestrasi kontainer kelas industri”? Atau "Sistem Operasi Cloud-Native"? Apa artinya ini?
Sejujurnya, saya tidak 100% yakin. Namun menurut saya menarik untuk menggali lebih dalam dan melihat apa yang sebenarnya terjadi di Kubernetes di bawah banyak lapisan abstraksinya. Jadi sekedar bersenang-senang, mari kita lihat seperti apa sebenarnya “klaster Kubernetes” yang minimalis. (Ini akan jauh lebih mudah daripada Kubernetes dengan Cara yang Sulit.)
Saya berasumsi Anda memiliki pengetahuan dasar tentang Kubernetes, Linux, dan container. Segala sesuatu yang kita bicarakan di sini hanya untuk tujuan penelitian/pembelajaran saja, jangan dimasukkan ke dalam produksi!
Tinjau
Kubernetes berisi banyak komponen. Berdasarkan wikipedia, arsitekturnya terlihat seperti ini:
Setidaknya ada delapan komponen yang ditampilkan di sini, tapi sebagian besar akan kita abaikan. Saya ingin menyatakan bahwa hal minimum yang dapat disebut Kubernetes terdiri dari tiga komponen utama:
kubus
kube-apiserver (yang bergantung pada etcd - database-nya)
runtime kontainer (Docker dalam hal ini)
Mari kita lihat apa yang dikatakan dokumentasi tentang masing-masingnya (rus., Inggris.). Pertama kubus:
Agen berjalan pada setiap node di cluster. Ini memastikan bahwa container berjalan di dalam pod.
Kedengarannya cukup sederhana. Bagaimana dengan runtime kontainer (waktu proses kontainer)?
Runtime container adalah program yang dirancang untuk menjalankan container.
Sangat informatif. Namun jika Anda sudah familiar dengan Docker, Anda pasti sudah memiliki gambaran umum tentang apa yang dilakukannya. (Rincian pemisahan tanggung jawab antara runtime container dan kubelet sebenarnya cukup halus dan saya tidak akan membahasnya di sini.)
И server API?
API Server adalah komponen panel kontrol Kubernetes yang mengekspos API Kubernetes. Server API adalah sisi klien dari panel kontrol Kubernetes
Siapa pun yang pernah melakukan apa pun dengan Kubernetes harus berinteraksi dengan API baik secara langsung atau melalui kubectl. Inilah inti dari apa yang menjadikan Kubernetes Kubernetes - otak yang mengubah pegunungan YAML yang kita semua kenal dan cintai (?) menjadi infrastruktur yang berfungsi. Tampak jelas bahwa API harus ada dalam konfigurasi minimal kita.
Prasyarat
Mesin virtual atau fisik Linux dengan akses root (Saya menggunakan Ubuntu 18.04 pada mesin virtual).
Dan itu semua!
Instalasi yang membosankan
Kita perlu menginstal Docker pada mesin yang akan kita gunakan. (Saya tidak akan menjelaskan secara detail tentang cara kerja Docker dan container; jika Anda tertarik, ada artikel yang luar biasa). Langsung saja kita install dengan apt:
Setelah itu, kita perlu mendapatkan biner Kubernetes. Padahal, untuk peluncuran awal “cluster” kita, kita hanya perlu saja kubelet, karena untuk menjalankan komponen server lain bisa kita gunakan kubelet. Untuk berinteraksi dengan cluster kami setelah berjalan, kami juga akan menggunakan kubectl.
kubelet harus dijalankan sebagai root. Cukup logis, karena dia perlu mengelola seluruh node. Mari kita lihat parameternya:
$ ./kubelet -h
<слишком много строк, чтобы разместить здесь>
$ ./kubelet -h | wc -l
284
Wah, banyak sekali pilihannya! Untungnya, kita hanya membutuhkan beberapa saja. Berikut adalah salah satu parameter yang kami minati:
--pod-manifest-path string
Jalur ke direktori yang berisi file untuk pod statis, atau jalur ke file yang menjelaskan pod statis. File yang dimulai dengan titik akan diabaikan. (DIHAPUSKAN: Opsi ini harus diatur dalam file konfigurasi yang diteruskan ke Kubelet melalui opsi --config. Untuk informasi lebih lanjut, lihat kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)
Opsi ini memungkinkan kita untuk berlari pod statis — pod yang tidak dikelola melalui API Kubernetes. Pod statis jarang digunakan, tetapi sangat berguna untuk membangun cluster dengan cepat, dan inilah yang kita butuhkan. Kita akan mengabaikan peringatan besar ini (sekali lagi, jangan jalankan ini dalam produksi!) dan lihat apakah kita bisa menjalankan podnya.
Pertama kita akan membuat direktori untuk pod statis dan menjalankannya kubelet:
kubelet mulai menulis beberapa peringatan dan sepertinya tidak terjadi apa-apa. Tapi itu tidak benar! Mari kita lihat Docker:
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8c8a35e26663 busybox "echo 'hello world!'" 36 seconds ago Exited (0) 36 seconds ago k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
68f670c3c85f k8s.gcr.io/pause:3.2 "/pause" 2 minutes ago Up 2 minutes k8s_POD_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_0
$ sudo docker logs k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
hello world!
kubelet Saya membaca manifes pod dan memberi Docker perintah untuk meluncurkan beberapa container sesuai dengan spesifikasi kami. (Jika Anda bertanya-tanya tentang wadah "jeda", ini adalah peretasan Kubernetes - lihat blog ini.) Kubelet akan meluncurkan container kita busybox dengan perintah yang ditentukan dan akan memulai ulang tanpa batas waktu hingga pod statis dihapus.
Ucapkan selamat pada diri sendiri. Kami baru saja menemukan salah satu cara paling membingungkan untuk menampilkan teks ke terminal!
Luncurkan dll
Tujuan utama kami adalah menjalankan API Kubernetes, namun untuk melakukan hal tersebut, kami harus menjalankannya terlebih dahulu dll. Mari kita mulai cluster etcd minimal dengan menempatkan pengaturannya di direktori pods (misalnya, pods/etcd.yaml):
Jika Anda pernah bekerja dengan Kubernetes, file YAML ini pasti sudah tidak asing lagi bagi Anda. Hanya ada dua hal yang perlu diperhatikan di sini:
Kami telah memasang folder host /var/lib/etcd di dalam pod sehingga data etcd tetap tersimpan setelah restart (jika hal ini tidak dilakukan, status cluster akan terhapus setiap kali pod di-restart, yang tidak akan baik bahkan untuk instalasi Kubernetes yang minimal).
Kami telah menginstal hostNetwork: true. Pengaturan ini, tidak mengherankan, mengonfigurasikan etcd untuk menggunakan jaringan host dan bukan jaringan internal pod (ini akan memudahkan server API untuk menemukan cluster etcd).
Pemeriksaan sederhana menunjukkan bahwa etcd memang berjalan di localhost dan menyimpan data ke disk:
$ curl localhost:2379/version
{"etcdserver":"3.4.3","etcdcluster":"3.4.0"}
$ sudo tree /var/lib/etcd/
/var/lib/etcd/
└── member
├── snap
│ └── db
└── wal
├── 0.tmp
└── 0000000000000000-0000000000000000.wal
Memulai server API
Menjalankan server API Kubernetes menjadi lebih mudah. Satu-satunya parameter yang perlu dilewati adalah --etcd-servers, melakukan apa yang Anda harapkan:
Tempatkan file YAML ini di direktori pods, dan server API akan dimulai. Memeriksa dengan curl menunjukkan bahwa Kubernetes API mendengarkan pada port 8080 dengan akses terbuka sepenuhnya - tidak diperlukan autentikasi!
(Sekali lagi, jangan jalankan ini dalam produksi! Saya sedikit terkejut karena pengaturan defaultnya sangat tidak aman. Namun menurut saya ini untuk membuat pengembangan dan pengujian lebih mudah.)
Dan, kejutan yang menyenangkan, kubectl langsung berfungsi tanpa pengaturan tambahan apa pun!
$ ./kubectl version
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:47:41Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:39:24Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
$ ./kubectl get pod
No resources found in default namespace.
masalah
Namun jika Anda menggali lebih dalam, sepertinya ada yang tidak beres:
$ ./kubectl get pod -n kube-system
No resources found in kube-system namespace.
Pod statis yang kami buat telah hilang! Faktanya, node kubelet kita tidak ditemukan sama sekali:
$ ./kubectl get nodes
No resources found in default namespace.
Apa masalahnya? Jika kamu ingat beberapa paragraf yang lalu, kita memulai kubelet dengan serangkaian parameter baris perintah yang sangat sederhana, sehingga kubelet tidak tahu cara menghubungi server API dan memberitahukan statusnya. Setelah mempelajari dokumentasi, kami menemukan tanda yang sesuai:
--kubeconfig string
Jalur ke file kubeconfig, yang menentukan cara menyambung ke server API. Ketersediaan --kubeconfig mengaktifkan mode server API, tidak --kubeconfig mengaktifkan mode offline.
Selama ini, tanpa kita sadari, kita menjalankan kubelet dalam “mode offline”. (Jika kita terlalu bertele-tele, kita bisa menganggap kubelet yang berdiri sendiri sebagai "Kubernet minimum yang layak", tapi itu akan sangat membosankan). Agar konfigurasi "sebenarnya" berfungsi, kita perlu meneruskan file kubeconfig ke kubelet agar kubelet mengetahui cara berkomunikasi dengan server API. Untungnya ini cukup sederhana (karena kami tidak memiliki masalah otentikasi atau sertifikat):
(Omong-omong, jika Anda mencoba mengakses API melalui curl ketika kubelet tidak berjalan, Anda akan menemukan bahwa API tersebut masih berjalan! Kubelet bukanlah “induk” dari podnya seperti Docker, ini lebih seperti “kontrol daemon." Kontainer yang dikelola oleh kubelet akan terus berjalan hingga kubelet menghentikannya.)
Dalam beberapa menit kubectl harus menunjukkan kepada kita pod dan node seperti yang kita harapkan:
$ ./kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
default hello-mink8s 0/1 CrashLoopBackOff 261 21h
kube-system etcd-mink8s 1/1 Running 0 21h
kube-system kube-apiserver-mink8s 1/1 Running 0 21h
$ ./kubectl get nodes -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
mink8s Ready <none> 21h v1.18.5 10.70.10.228 <none> Ubuntu 18.04.4 LTS 4.15.0-109-generic docker://19.3.6
Mari kita ucapkan selamat pada diri kita sendiri kali ini (saya tahu saya sudah mengucapkan selamat pada diri kita sendiri) - kita memiliki "cluster" Kubernetes minimal yang berjalan dengan API yang berfungsi penuh!
Kami meluncurkan di bawah
Sekarang mari kita lihat kemampuan API tersebut. Mari kita mulai dengan pod nginx:
Di sini kita mendapatkan kesalahan yang cukup menarik:
$ ./kubectl apply -f nginx.yaml
Error from server (Forbidden): error when creating "nginx.yaml": pods "nginx" is
forbidden: error looking up service account default/default: serviceaccount
"default" not found
$ ./kubectl get serviceaccounts
No resources found in default namespace.
Di sini kita melihat betapa tidak lengkapnya lingkungan Kubernetes kita - kita tidak punya akun untuk layanan. Mari kita coba lagi dengan membuat akun layanan secara manual dan lihat apa yang terjadi:
$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
EOS
serviceaccount/default created
$ ./kubectl apply -f nginx.yaml
Error from server (ServerTimeout): error when creating "nginx.yaml": No API
token found for service account "default", retry after the token is
automatically created and added to the service account
Meskipun kami membuat akun layanan secara manual, token autentikasi tidak dibuat. Saat kita terus bereksperimen dengan "cluster" minimalis kita, kita akan menemukan bahwa sebagian besar hal berguna yang biasanya terjadi secara otomatis akan hilang. Server API Kubernetes cukup minimalis, dengan sebagian besar tugas berat dan konfigurasi otomatis terjadi di berbagai pengontrol dan pekerjaan latar belakang yang belum berjalan.
Kita dapat mengatasi masalah ini dengan mengatur opsi automountServiceAccountToken untuk akun layanan (karena kita tidak perlu menggunakannya lagi):
$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
automountServiceAccountToken: false
EOS
serviceaccount/default configured
$ ./kubectl apply -f nginx.yaml
pod/nginx created
$ ./kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 0/1 Pending 0 13m
Akhirnya podnya muncul! Tapi nyatanya itu tidak akan dimulai karena kita tidak punya perencana (penjadwal) adalah komponen penting lainnya dari Kubernetes. Sekali lagi, kita melihat bahwa API Kubernetes secara mengejutkan "bodoh" - saat Anda membuat sebuah Pod di API, ia akan mendaftarkannya, namun tidak mencoba mencari tahu di node mana ia akan dijalankan.
Faktanya, Anda tidak memerlukan penjadwal untuk menjalankan sebuah pod. Anda dapat menambahkan node secara manual ke manifes di parameter nodeName:
(Mengganti mink8s ke nama node.) Setelah menghapus dan menerapkan, kita melihat bahwa nginx telah dimulai dan mendengarkan alamat IP internal:
$ ./kubectl delete pod nginx
pod "nginx" deleted
$ ./kubectl apply -f nginx.yaml
pod/nginx created
$ ./kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 30s 172.17.0.2 mink8s <none> <none>
$ curl -s 172.17.0.2 | head -4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
Untuk memastikan bahwa jaringan antar pod berfungsi dengan benar, kita dapat menjalankan curl dari pod lain:
$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: curl
spec:
containers:
- image: curlimages/curl
name: curl
command: ["curl", "172.17.0.2"]
nodeName: mink8s
EOS
pod/curl created
$ ./kubectl logs curl | head -6
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
Sangat menarik untuk menggali lingkungan ini dan melihat mana yang berhasil dan mana yang tidak. Saya menemukan bahwa ConfigMap dan Secret berfungsi seperti yang diharapkan, namun Service dan Deployment tidak.
ех!
Posting ini semakin panjang, jadi saya akan menyatakan kemenangan dan mengatakan bahwa ini adalah konfigurasi yang layak yang dapat disebut "Kubernetes". Ringkasnya: empat biner, lima parameter baris perintah dan "hanya" 45 baris YAML (bukan sebanyak itu menurut standar Kubernetes) dan ada beberapa hal yang berfungsi:
Pod dikelola menggunakan API Kubernetes biasa (dengan beberapa peretasan)
Anda dapat mengunggah dan mengelola gambar kontainer publik
Pod tetap hidup dan dimulai ulang secara otomatis
Jaringan antar pod dalam node yang sama berfungsi cukup baik
ConfigMap, Rahasia, dan pemasangan penyimpanan sederhana berfungsi seperti yang diharapkan
Namun masih banyak hal yang membuat Kubernetes benar-benar berguna, seperti:
Penjadwal Pod
Otentikasi/otorisasi
Banyak node
Jaringan layanan
DNS internal berkerumun
Pengontrol untuk akun layanan, penerapan, integrasi dengan penyedia cloud, dan sebagian besar fitur lain yang dihadirkan Kubernetes
Jadi apa yang sebenarnya kita dapatkan? API Kubernetes, yang berjalan sendiri, sebenarnya hanyalah sebuah platform untuk otomatisasi kontainer. Hal ini tidak banyak membantu - ini merupakan pekerjaan bagi berbagai pengontrol dan operator yang menggunakan API - namun hal ini menyediakan lingkungan yang konsisten untuk otomatisasi.