حداقل Kubernetes قابل دوام

ترجمه مقاله در آستانه شروع دوره آماده شد "روش ها و ابزارهای DevOps".

حداقل Kubernetes قابل دوام

اگر در حال خواندن این مطلب هستید، احتمالاً چیزی در مورد Kubernetes شنیده اید (و اگر نه، چگونه به اینجا رسیدید؟) اما دقیقاً Kubernetes چیست؟ این "ارکستراسیون کانتینرهای صنعتی"? یا "سیستم عامل Cloud-Native"? این اصلا به چه معناست؟

راستش من 100% مطمئن نیستم. اما من فکر می‌کنم جالب است که درونی‌ها را کاوش کنیم و ببینیم واقعاً در Kubernetes تحت لایه‌های متعدد انتزاع آن چه می‌گذرد. بنابراین فقط برای سرگرمی، بیایید نگاهی بیاندازیم به اینکه یک "خوشه Kubernetes" مینیمال در واقع چگونه به نظر می رسد. (این بسیار ساده تر از این خواهد بود Kubernetes راه سخت.)

من فرض می‌کنم شما دانش اولیه Kubernetes، Linux، و کانتینرها را دارید. همه چیزهایی که ما در اینجا در مورد آن صحبت می کنیم فقط برای اهداف تحقیقاتی/آموزشی است، هیچ کدام از آنها را وارد تولید نکنید!

مرور

Kubernetes شامل اجزای بسیاری است. مطابق با ویکیپدیا، معماری به شکل زیر است:

حداقل Kubernetes قابل دوام

حداقل هشت مؤلفه در اینجا نشان داده شده است، اما ما بیشتر آنها را نادیده می گیریم. من می خواهم بگویم که حداقل چیزی که می توان به طور منطقی Kubernetes نامید از سه جزء اصلی تشکیل شده است:

  • کوبلت
  • kube-apiserver (که به etcd بستگی دارد - پایگاه داده آن)
  • زمان اجرا کانتینر (در این مورد Docker)

بیایید ببینیم اسناد در مورد هر یک از آنها چه می گوید (rus., انگلیسی.). در ابتدا کوبلت:

یک عامل در حال اجرا بر روی هر گره در خوشه. این اطمینان حاصل می کند که ظروف در غلاف در حال اجرا هستند.

به اندازه کافی ساده به نظر می رسد. چه در مورد زمان اجرا کانتینر (زمان اجرای کانتینر)؟

Container Runtime برنامه ای است که برای اجرای کانتینرها طراحی شده است.

بسیار آموزنده اما اگر با Docker آشنا هستید، باید یک ایده کلی از کاری که انجام می دهد داشته باشید. (جزئیات تفکیک مسئولیت ها بین زمان اجرای کانتینر و کوبلت در واقع کاملاً ظریف است و من در اینجا به آنها نمی پردازم.)

И سرور API?

سرور API جزء کنترل پنل Kubernetes است که API Kubernetes را در معرض دید قرار می دهد. سرور API سمت کلاینت کنترل پنل Kubernetes است

هرکسی که تا به حال کاری با Kubernetes انجام داده است، باید مستقیماً یا از طریق kubectl با API تعامل داشته باشد. این قلب چیزی است که Kubernetes Kubernetes را می سازد - مغزی که کوه های YAML را که همه ما می شناسیم و دوست داریم (؟) به زیرساخت های کاری تبدیل می کند. بدیهی است که API باید در پیکربندی حداقلی ما وجود داشته باشد.

پیش شرط ها

  • ماشین مجازی یا فیزیکی لینوکس با دسترسی ریشه (من از اوبونتو 18.04 در ماشین مجازی استفاده می کنم).
  • و این همه!

نصب خسته کننده

ما باید Docker را روی دستگاهی که استفاده خواهیم کرد نصب کنیم. (من قصد ندارم به جزئیات در مورد نحوه کار داکر و کانتینرها بپردازم؛ اگر علاقه مند هستید، مقالات فوق العاده). بیایید فقط آن را با آن نصب کنیم apt:

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

پس از آن، باید باینری های Kubernetes را بدست آوریم. در واقع، برای راه اندازی اولیه "خوشه" ما فقط نیاز داریم kubelet، از آنجایی که برای اجرای سایر اجزای سرور می توانیم استفاده کنیم kubelet. برای تعامل با خوشه خود پس از اجرا، از آن نیز استفاده خواهیم کرد 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

اگر فقط فرار کنیم چه اتفاقی می افتد kubelet?

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

kubelet باید به صورت روت اجرا شود. کاملاً منطقی است، زیرا او باید کل گره را مدیریت کند. بیایید به پارامترهای آن نگاه کنیم:

$ ./kubelet -h
<слишком много строк, чтобы разместить здесь>
$ ./kubelet -h | wc -l
284

وای، گزینه های زیادی! خوشبختانه ما فقط به چند تا از آنها نیاز داریم. در اینجا یکی از پارامترهایی است که ما به آن علاقه داریم:

--pod-manifest-path string

مسیری به دایرکتوری حاوی فایل‌هایی برای پادهای ثابت، یا مسیری به فایلی که غلاف‌های استاتیک را توصیف می‌کند. فایل هایی که با نقطه شروع می شوند نادیده گرفته می شوند. (منسوخ شده: این گزینه باید در فایل پیکربندی ارسال شده به Kubelet از طریق گزینه --config تنظیم شود. برای اطلاعات بیشتر، رجوع کنید به kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

این گزینه به ما امکان اجرا را می دهد غلاف های ساکن - پادهایی که از طریق Kubernetes API مدیریت نمی شوند. غلاف های استاتیک به ندرت استفاده می شوند، اما برای بالا بردن سریع یک خوشه بسیار راحت هستند و این دقیقا همان چیزی است که ما به آن نیاز داریم. ما این هشدار بزرگ را نادیده می گیریم (باز هم، این را در تولید اجرا نکنید!) و ببینیم که آیا می توانیم پاد را راه اندازی کنیم.

ابتدا یک دایرکتوری برای پادهای ثابت ایجاد می کنیم و اجرا می کنیم kubelet:

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

سپس، در پنجره ترمینال/tmux دیگر/هر چیزی، یک مانیفست 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 شروع به نوشتن چند هشدار می کند و به نظر می رسد که هیچ اتفاقی نمی افتد. اما این درست نیست! بیایید به 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 من مانیفست پاد را خواندم و به داکر دستور دادم تا طبق مشخصات ما چند کانتینر راه اندازی کند. (اگر در مورد ظرف "مکث" تعجب می کنید، این یک هک Kubernetes است - ببینید این وبلاگ.) Kubelet کانتینر ما را راه اندازی خواهد کرد busybox با دستور مشخص شده و به طور نامحدود آن را مجدداً راه اندازی می کند تا زمانی که پاد استاتیک حذف شود.

به خودت تبریک بگو ما به تازگی به یکی از گیج کننده ترین راه ها برای خروجی متن به ترمینال رسیده ایم!

etcd را راه اندازی کنید

هدف نهایی ما اجرای Kubernetes API است، اما برای انجام این کار ابتدا باید اجرا کنیم etcd. بیایید با قرار دادن تنظیمات آن در دایرکتوری pods یک خوشه etcd حداقلی را شروع کنیم (به عنوان مثال، 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

اگر تا به حال با Kubernetes کار کرده اید، این فایل های YAML باید برای شما آشنا باشند. در اینجا تنها دو نکته قابل توجه است:

ما پوشه میزبان را نصب کرده ایم /var/lib/etcd در پاد به طوری که داده‌های etcd پس از راه‌اندازی مجدد حفظ شوند (اگر این کار انجام نشود، هر بار که غلاف راه‌اندازی مجدد می‌شود، وضعیت کلاستر پاک می‌شود، که حتی برای نصب حداقل Kubernetes خوب نخواهد بود).

نصب کرده ایم hostNetwork: true. این تنظیم، بدون تعجب، etcd را برای استفاده از شبکه میزبان به جای شبکه داخلی pod پیکربندی می‌کند (این کار باعث می‌شود که سرور API بتواند خوشه etcd را پیدا کند).

یک بررسی ساده نشان می دهد که etcd واقعاً روی لوکال هاست اجرا می شود و داده ها را روی دیسک ذخیره می کند:

$ 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

راه اندازی سرور API

اجرای یک سرور API Kubernetes حتی ساده تر است. تنها پارامتری که باید پاس شود این است --etcd-servers، آنچه را که انتظار دارید انجام می دهد:

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

این فایل YAML را در دایرکتوری قرار دهید podsو سرور API شروع به کار خواهد کرد. چک کردن با curl نشان می دهد که API Kubernetes در پورت 8080 با دسترسی کاملاً باز گوش می دهد - بدون نیاز به احراز هویت!

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

(باز هم، این را در مرحله تولید اجرا نکنید! من کمی تعجب کردم که تنظیمات پیش فرض آنقدر ناامن است. اما حدس می زنم این کار برای آسان کردن توسعه و آزمایش باشد.)

و، شگفتی دلپذیر، kubectl بدون هیچ تنظیمات اضافی کار می کند!

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

مشکل

اما اگر کمی عمیق‌تر شوید، به نظر می‌رسد چیزی اشتباه پیش می‌رود:

$ ./kubectl get pod -n kube-system
No resources found in kube-system namespace.

غلاف های ثابتی که ایجاد کردیم از بین رفته اند! در واقع، گره kubelet ما اصلاً کشف نشده است:

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

موضوع چیه؟ اگر چند پاراگراف قبل را به خاطر دارید، ما کوبلت را با مجموعه ای بسیار ساده از پارامترهای خط فرمان شروع کردیم، بنابراین kubelet نمی داند چگونه با سرور API تماس بگیرد و وضعیت خود را به آن اطلاع دهد. پس از مطالعه اسناد، پرچم مربوطه را پیدا می کنیم:

--kubeconfig string

مسیر فایل kubeconfig، که نحوه اتصال به سرور API را مشخص می کند. دسترسی --kubeconfig حالت سرور API را فعال می کند، خیر --kubeconfig حالت آفلاین را فعال می کند.

در تمام این مدت، بدون اینکه بدانیم، کوبلت را در حالت آفلاین اجرا می کردیم. (اگر ما بچه‌دار بودیم، می‌توانستیم یک کوبلت مستقل را به‌عنوان «حداقل کوبرنت‌های قابل دوام» در نظر بگیریم، اما این بسیار کسل‌کننده خواهد بود). برای اینکه پیکربندی "واقعی" کار کند، باید فایل kubeconfig را به kubelet منتقل کنیم تا بداند چگونه با سرور API صحبت کند. خوشبختانه این بسیار ساده است (زیرا ما هیچ مشکلی با احراز هویت یا گواهینامه نداریم):

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

این را به عنوان ذخیره کنید kubeconfig.yaml، روند را بکشید kubelet و با پارامترهای لازم راه اندازی مجدد کنید:

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

(به هر حال، اگر سعی کنید از طریق curl به API دسترسی پیدا کنید، زمانی که کوبلت در حال اجرا نیست، متوجه خواهید شد که هنوز در حال اجرا است! Kubelet مانند Docker یک "والد" پادهای خود نیست، بلکه بیشتر شبیه یک "کنترل" است. ظروف تحت مدیریت یک کوبلت تا زمانی که کوبلت آنها را متوقف کند به کار خود ادامه خواهند داد.)

در چند دقیقه kubectl باید غلاف ها و گره ها را همانطور که انتظار داریم به ما نشان دهد:

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

بیایید این بار واقعاً به خودمان تبریک بگوییم (می دانم که قبلاً به خودمان تبریک گفته ام) - ما یک "خوشه" کوچک Kubernetes داریم که با یک API کاملاً کاربردی اجرا می شود!

زیر راه اندازی می کنیم

حال بیایید ببینیم که API چه توانایی هایی دارد. بیایید با nginx pod شروع کنیم:

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

در اینجا با یک خطای نسبتاً جالب مواجه می شویم:

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

در اینجا می بینیم که محیط Kubernetes ما چقدر ناقص است - ما هیچ حساب کاربری برای خدمات نداریم. بیایید با ایجاد دستی یک حساب سرویس دوباره تلاش کنیم و ببینیم چه اتفاقی می‌افتد:

$ 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

حتی زمانی که حساب سرویس را به صورت دستی ایجاد کردیم، رمز احراز هویت ایجاد نمی‌شود. همانطور که ما به آزمایش با "خوشه" حداقلی خود ادامه می دهیم، متوجه خواهیم شد که بسیاری از چیزهای مفیدی که معمولاً به طور خودکار اتفاق می افتد از دست خواهند رفت. سرور Kubernetes API کاملاً مینیمالیستی است، با بیشتر پیکربندی‌های سنگین و خودکار در کنترلرهای مختلف و کارهای پس‌زمینه که هنوز اجرا نشده‌اند.

با تنظیم گزینه می توانیم این مشکل را حل کنیم automountServiceAccountToken برای حساب سرویس (زیرا به هر حال مجبور نیستیم از آن استفاده کنیم):

$ 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

بالاخره غلاف ظاهر شد! اما در واقع شروع نمی شود زیرا ما نداریم برنامه ریز (زمانبندی) یکی دیگر از اجزای مهم Kubernetes است. باز هم می بینیم که Kubernetes API به طور شگفت انگیزی "گنگ" است - وقتی یک Pod را در API ایجاد می کنید، آن را ثبت می کند، اما سعی نمی کند بفهمد آن را روی چه گره ای اجرا کند.

در واقع برای اجرای پاد نیازی به زمانبندی ندارید. می توانید به صورت دستی یک گره به مانیفست در پارامتر اضافه کنید nodeName:

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

(جایگزین کردن mink8s به نام گره.) پس از حذف و اعمال، می بینیم که nginx شروع شده است و به آدرس IP داخلی گوش می دهد:

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

برای اطمینان از اینکه شبکه بین پادها به درستی کار می کند، می توانیم curl را از یک پاد دیگر اجرا کنیم:

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

بسیار جالب است که در این محیط کاوش کنید و ببینید چه چیزی کار می کند و چه چیزی نه. متوجه شدم که ConfigMap و Secret همانطور که انتظار می رود کار می کنند، اما Service و Deployment اینطور نیست.

!

این پست در حال طولانی شدن است، بنابراین من می خواهم پیروزی را اعلام کنم و بگویم که این یک پیکربندی قابل اجرا است که می توان آن را "Kubernetes" نامید. به طور خلاصه: چهار باینری، پنج پارامتر خط فرمان و "فقط" 45 خط YAML (نه تا حد زیادی طبق استانداردهای Kubernetes) و ما چندین چیز داریم که کار می کنند:

  • پادها با استفاده از API معمولی Kubernetes مدیریت می شوند (با چند هک)
  • می توانید تصاویر کانتینر عمومی را آپلود و مدیریت کنید
  • پادها زنده می مانند و به طور خودکار راه اندازی مجدد می شوند
  • شبکه سازی بین پادها در همان گره بسیار خوب عمل می کند
  • ConfigMap، Secret و نصب ذخیره سازی ساده همانطور که انتظار می رود کار می کنند

اما بسیاری از چیزهایی که Kubernetes را واقعاً مفید می‌سازد هنوز گم شده‌اند، مانند:

  • زمانبندی پاد
  • احراز هویت / مجوز
  • گره های متعدد
  • شبکه خدمات
  • DNS داخلی خوشه ای
  • کنترل‌کننده‌های حساب‌های سرویس، استقرار، ادغام با ارائه‌دهندگان ابری و بسیاری از چیزهای دیگری که Kubernetes به ارمغان می‌آورد.

پس در واقع چه چیزی به دست آوردیم؟ Kubernetes API که به تنهایی اجرا می شود، در واقع فقط یک پلتفرم برای آن است اتوماسیون کانتینری. کار زیادی انجام نمی دهد - این کار برای کنترلرها و اپراتورهای مختلف با استفاده از API است - اما یک محیط سازگار برای اتوماسیون فراهم می کند.

در وبینار رایگان درباره دوره بیشتر بدانید.

بیشتر بخوانید:

منبع: www.habr.com

اضافه کردن نظر