Kubernetes yang Layak Minimum

Terjemahan artikel disiapkan untuk mengantisipasi dimulainya kursus "Praktik dan Alat DevOps" .










Jika Anda membaca ini, Anda mungkin pernah mendengar sesuatu tentang Kubernetes (dan jika tidak, bagaimana Anda bisa sampai di sini?) Tapi apa sebenarnya Kubernetes itu? Apakah ini "Orkestrasi Kontainer Kelas Industri" ? Atau "Sistem Operasi Cloud-Native" ? Apa artinya ini?



Sejujurnya, saya tidak 100% yakin. Tapi menurut saya menarik untuk menggali internal dan melihat apa yang sebenarnya terjadi di Kubernetes di bawah banyak lapisan abstraksinya. Jadi hanya untuk bersenang-senang, mari kita lihat seperti apa sebenarnya β€œcluster Kubernetes” minimal. (Ini akan jauh lebih mudah daripada Kubernetes The Hard Way .)



Saya berasumsi bahwa Anda memiliki pengetahuan dasar tentang Kubernetes, Linux, dan container. Semua yang akan kita bicarakan di sini hanya untuk penelitian / studi, jangan menjalankan semua ini dalam produksi!



Gambaran



Kubernetes mengandung banyak komponen. Menurut wikipedia , arsitekturnya terlihat seperti ini:







Setidaknya ada delapan komponen yang ditampilkan di sini, tetapi kami akan mengabaikan sebagian besar dari mereka. Saya ingin menyatakan bahwa hal terkecil yang bisa disebut Kubernetes memiliki tiga komponen utama:



  • kubelet
  • kube-apiserver (yang bergantung pada etcd - database-nya)
  • runtime kontainer (dalam hal ini Docker)


Mari kita lihat apa yang dikatakan dokumentasi tentang masing-masing ( Rusia , Inggris ). Pertama kubelet :



Agen yang berjalan pada setiap node di cluster. Dia memastikan bahwa kontainernya berjalan di dalam pod.



Kedengarannya cukup sederhana. Bagaimana dengan runtime container (runtime container)?



Runtime kontainer adalah program yang dirancang untuk menjalankan kontainer.



Sangat informatif. Tetapi jika Anda terbiasa dengan Docker, maka Anda harus memiliki pemahaman dasar tentang apa yang dilakukannya. (Rincian pemisahan kekhawatiran antara runtime container dan kubelet sebenarnya cukup halus dan saya tidak akan membahasnya di sini.)



Dan API server ?



Server API - Komponen dasbor Kubernetes yang mewakili API Kubernetes. Server API adalah ujung depan dasbor Kubernetes.



Siapapun yang pernah melakukan sesuatu dengan Kubernetes harus berinteraksi dengan API baik secara langsung atau melalui kubectl. Inilah inti dari apa yang membuat Kubernetes Kubernetes - otak yang mengubah pegunungan YAML yang kita semua kenal dan cintai (?) Menjadi infrastruktur yang berfungsi. Tampaknya jelas bahwa API harus ada dalam konfigurasi minimal kami.



Prasyarat



  • Mesin virtual atau fisik Linux yang di-root (Saya menggunakan Ubuntu 18.04 di mesin virtual).
  • Dan itu semua!


Instalasi yang membosankan



Docker perlu diinstal pada mesin yang akan kita gunakan. (Saya tidak akan menjelaskan secara rinci tentang cara kerja Docker dan container; ada artikel bagus di luar sana jika Anda tertarik ). Mari kita instal dengan apt:



$ sudo apt install docker.io
$ sudo systemctl start docker


Setelah itu, kita perlu mendapatkan binari Kubernetes. Sebenarnya, untuk peluncuran awal "cluster" kami, kami hanya perlu kubelet, karena kami dapat menggunakan untuk meluncurkan komponen server lainnya kubelet. Untuk berinteraksi dengan cluster kami setelah itu aktif dan berjalan, kami juga akan menggunakan kubectl.



$ curl -L https://dl.k8s.io/v1.18.5/kubernetes-server-linux-amd64.tar.gz > server.tar.gz
$ tar xzvf server.tar.gz
$ cp kubernetes/server/bin/kubelet .
$ cp kubernetes/server/bin/kubectl .
$ ./kubelet --version
Kubernetes v1.18.5


Apa yang terjadi jika kita baru saja meluncurkannya kubelet?



$ ./kubelet
F0609 04:03:29.105194    4583 server.go:254] mkdir /var/lib/kubelet: permission denied


kubeletharus dijalankan sebagai root. Ini cukup logis, karena dia perlu mengelola seluruh node. Mari kita lihat parameternya:



$ ./kubelet -h
<  ,   >
$ ./kubelet -h | wc -l
284


Wah, banyak sekali pilihannya! Untungnya, kami 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 mendeskripsikan pod statis. File yang dimulai dengan titik akan diabaikan. (BERLAKU LAGI:. Parameter ini harus ditetapkan dalam file konfigurasi diteruskan ke Kubelet melalui opsi --config Untuk informasi lebih lanjut lihat kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)



Parameter ini memungkinkan kita untuk menjalankan statis Pod - Pod yang tidak dikelola melalui Kubernetes API. Pod statis jarang digunakan, tetapi sangat nyaman untuk meningkatkan cluster dengan cepat, dan inilah yang kami butuhkan. Kami akan mengabaikan peringatan keras ini (sekali lagi, jangan menjalankan ini dalam produksi!) Dan lihat apakah kami dapat berjalan di bawah.



Pertama, kita akan membuat direktori untuk pod statis dan menjalankan kubelet:



$ mkdir pods
$ sudo ./kubelet --pod-manifest-path=pods


Kemudian di jendela terminal / tmux lain / di tempat lain, kita akan membuat manifes pod:



$ cat <<EOF > pods/hello.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello
spec:
  containers:
  - image: busybox
    name: hello
    command: ["echo", "hello world!"]
EOF


kubeletmulai menulis beberapa peringatan dan sepertinya tidak ada yang terjadi. Tapi bukan ini masalahnya! 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!


kubeletmembaca manifes pod dan menginstruksikan Docker untuk menjalankan beberapa container sesuai spesifikasi kami. (Jika Anda penasaran dengan container "pause", ini adalah peretasan Kubernetes - lihat blog ini untuk detailnya .) Kubelet akan meluncurkan container kami busyboxdengan perintah yang ditentukan dan memulai ulang tanpa batas hingga pod statis dihapus.



Beri selamat pada diri sendiri. Kami baru saja menemukan salah satu cara paling rumit untuk menampilkan teks ke terminal!



Jalankan etcd



Tujuan utama kami adalah menjalankan Kubernetes API, tetapi untuk itu kami harus menjalankan etcd terlebih dahulu . Mari kita mulai cluster etcd minimal dengan menempatkan pengaturannya di direktori pods (misalnya pods/etcd.yaml):



apiVersion: v1
kind: Pod
metadata:
  name: etcd
  namespace: kube-system
spec:
  containers:
  - name: etcd
    command:
    - etcd
    - --data-dir=/var/lib/etcd
    image: k8s.gcr.io/etcd:3.4.3-0
    volumeMounts:
    - mountPath: /var/lib/etcd
      name: etcd-data
  hostNetwork: true
  volumes:
  - hostPath:
      path: /var/lib/etcd
      type: DirectoryOrCreate
    name: etcd-data


Jika Anda pernah bekerja dengan Kubernetes, file-file YAML ini sudah tidak asing lagi bagi Anda. Hanya ada dua hal yang perlu diperhatikan di sini:



Kami memasang folder host /var/lib/etcddi pod sehingga data etcd disimpan setelah restart (jika ini tidak dilakukan, maka status cluster akan dihapus setiap kali pod di-restart, yang akan menjadi buruk bahkan untuk instalasi Kubernetes minimal).



Kami telah menginstal hostNetwork: true. Opsi ini, tidak mengherankan, mengonfigurasi etcd untuk menggunakan jaringan host daripada jaringan internal pod (ini akan mempermudah server API untuk menemukan kluster 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


Meluncurkan Server API



Memulai Server API Kubernetes jauh lebih mudah. Satu-satunya parameter yang perlu diteruskan --etcd-servers, melakukan apa yang Anda harapkan:



apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - name: kube-apiserver
    command:
    - kube-apiserver
    - --etcd-servers=http://127.0.0.1:2379
    image: k8s.gcr.io/kube-apiserver:v1.18.5
  hostNetwork: true


Tempatkan file YAML ini di direktori podsdan server API akan mulai. Verifikasi dengan bantuan curlmenunjukkan bahwa Kubernetes API mendengarkan pada port 8080 dengan akses terbuka penuh - tidak diperlukan otentikasi!



$ curl localhost:8080/healthz
ok
$ curl localhost:8080/api/v1/pods
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "selfLink": "/api/v1/pods",
    "resourceVersion": "59"
  },
  "items": []
}


(Sekali lagi, jangan jalankan ini dalam produksi! Saya sedikit terkejut bahwa pengaturan default sangat tidak aman. Tapi saya rasa ini untuk kemudahan pengembangan dan pengujian.)



Dan, untungnya, kubectl bekerja di luar kotak tanpa tambahan apapun. pengaturan!



$ ./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



Tetapi 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 muncul sama sekali:



$ ./kubectl get nodes
No resources found in default namespace.


Apa masalahnya? Jika Anda ingat, beberapa paragraf yang lalu kami memulai kubelet dengan seperangkat parameter baris perintah yang sangat sederhana, jadi kubelet tidak tahu bagaimana menghubungi server API dan memberitahukan statusnya. Setelah memeriksa dokumentasi, kami menemukan tanda yang sesuai:



--kubeconfig string



Jalur ke file kubeconfig, yang menunjukkan cara menyambung ke server API. Kehadiran --kubeconfigmengaktifkan mode server API, ketidakhadiran --kubeconfigmengaktifkan mode offline.



Selama ini, tanpa disadari, kami menjalankan kubelet dalam "mode offline". (Jika kita bertele-tele, kita bisa menganggap mode kubelet standalone sebagai β€œKubernetes minimum yang layak,” tapi itu akan sangat membosankan). Agar konfigurasi "nyata" berfungsi, kita perlu meneruskan file kubeconfig ke kubelet sehingga ia tahu bagaimana berkomunikasi dengan server API. Untungnya, ini cukup mudah (karena kami tidak memiliki masalah dengan otentikasi atau sertifikat):



apiVersion: v1
kind: Config
clusters:
- cluster:
    server: http://127.0.0.1:8080
  name: mink8s
contexts:
- context:
    cluster: mink8s
  name: mink8s
current-context: mink8s


Simpan ini sebagai kubeconfig.yaml, matikan proses kubeletdan mulai ulang dengan parameter yang diperlukan:



$ sudo ./kubelet --pod-manifest-path=pods --kubeconfig=kubeconfig.yaml


(Ngomong-ngomong, jika Anda mencoba mengakses API dengan curl ketika kubelet tidak berfungsi, Anda akan menemukan bahwa itu masih berfungsi! Kubelet bukanlah "induk" dari pod-nya, seperti Docker, ini lebih seperti "daemon kontrol". Kontainer yang dikelola oleh kubelet akan berjalan sampai kubelet menghentikannya.)



Setelah beberapa menit, kubectlakan ditunjukkan 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 benar-benar memberi selamat pada diri kita sendiri kali ini (saya tahu saya sudah memberi selamat) - kami memiliki "cluster" Kubernetes minimal yang bekerja dengan API yang berfungsi penuh!



Jalankan di bawah



Sekarang mari kita lihat kemampuan API. Mari kita mulai dengan pod nginx:



apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: 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 memiliki akun layanan. Mari 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, tidak ada token autentikasi yang dibuat. Saat kami terus bereksperimen dengan "cluster" minimalis kami, kami akan menemukan bahwa sebagian besar hal berguna yang biasanya terjadi secara otomatis akan hilang. Server API Kubernetes cukup minimalis, dengan sebagian besar perubahan otomatis yang berat terjadi di berbagai pengontrol dan pekerjaan latar belakang yang belum berjalan.



Kami dapat mengatasi masalah ini dengan menetapkan opsi automountServiceAccountTokenuntuk akun layanan (karena kami tidak perlu menggunakannya):



$ 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, under muncul! Namun nyatanya, ini tidak akan dimulai, karena kita tidak memiliki scheduler (penjadwal) - komponen penting lainnya Kubernetes. Sekali lagi, kita dapat melihat bahwa Kubernetes API ternyata sangat bodoh - ketika Anda membuat sebuah pod di API, ia mendaftarkannya, tetapi tidak mencoba untuk mencari tahu di node mana untuk menjalankannya.



Anda sebenarnya tidak membutuhkan penjadwal untuk menjalankan pod. Anda dapat menambahkan node secara manual ke manifes di parameter nodeName:



apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
  nodeName: mink8s


(Ganti mink8sdengan nama host.) Setelah menghapus dan menerapkan, kita melihat bahwa nginx telah dimulai dan sedang mendengarkan di 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 memverifikasi 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>


Cukup menyenangkan untuk menggali lingkungan ini dan melihat apa yang berhasil dan yang tidak. Saya menemukan bahwa ConfigMap dan Secret berfungsi seperti yang diharapkan, tetapi Service dan Deployment tidak.



Keberhasilan!



Posting ini semakin besar, jadi saya akan mengumumkan kemenangan dan menyatakan bahwa ini adalah konfigurasi yang layak untuk memanggil "Kubernetes". Singkatnya: empat biner, lima parameter baris perintah, dan "hanya" 45 baris YAML (tidak banyak menurut standar Kubernetes) dan kami memiliki banyak hal yang berfungsi:



  • Pod dikelola menggunakan Kubernetes API biasa (dengan beberapa peretasan)
  • Anda dapat mengunggah dan mengelola gambar kontainer publik
  • Pod tetap hidup dan dimulai ulang secara otomatis
  • Jaringan antar pod dalam satu node bekerja dengan cukup baik
  • ConfigMap, Rahasia, dan pemasangan repositori yang paling sederhana berfungsi seperti yang diharapkan


Tetapi sebagian besar hal yang membuat Kubernetes sangat berguna masih belum ada, misalnya:



  • Perencana Pod
  • Otentikasi / Otorisasi
  • Beberapa node
  • Jaringan layanan
  • DNS Internal Berkelompok
  • Pengontrol untuk akun layanan, penerapan, integrasi penyedia cloud, dan sebagian besar barang lain yang dibawa Kubernetes


Jadi, apa yang sebenarnya kita dapatkan? Kubernetes API, yang bekerja sendiri, sebenarnya hanyalah platform otomatisasi container . Itu tidak berbuat banyak - ini berfungsi untuk berbagai pengontrol dan operator yang menggunakan API - tetapi menyediakan kerangka kerja yang konsisten untuk otomatisasi.



Pelajari lebih lanjut tentang kursus ini di webinar gratis.






Baca lebih banyak:



  • Mengapa sysadmin, pengembang, dan penguji mempelajari praktik DevOps?
  • Thanos β€” Prometheus
  • QA- GitLab GitLab Performance Tool
  • Loki β€” , Prometheus
  • DevOps



All Articles