Kubernetes Minimum yang Layak

Terjemahan artikel disiapkan pada malam dimulainya kursus "Praktik dan alat DevOps".

Kubernetes Minimum yang Layak

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:

Kubernetes Minimum yang Layak

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:

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

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.

$ 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 lari saja kubelet?

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

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:

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

Kemudian, di terminal lain/jendela tmux/apa pun, 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

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):

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 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:

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 pods, dan server API akan dimulai. Memeriksa dengan curl menunjukkan bahwa Kubernetes API mendengarkan pada port 8080 dengan akses terbuka sepenuhnya - tidak diperlukan autentikasi!

$ 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 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):

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 prosesnya kubelet dan mulai ulang dengan parameter yang diperlukan:

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

(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:

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 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:

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

(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.

Pelajari lebih lanjut tentang kursus di webinar gratis.

Baca lebih banyak:

Sumber: www.habr.com

Tambah komentar