الحد الأدنى Kubernetes قابلة للتطبيق

تم إعداد ترجمة المقالة مسبقًا قبل بدء دورة "ممارسات وأدوات DevOps" .










إذا كنت تقرأ هذا ، فمن المحتمل أنك سمعت شيئًا عن Kubernetes (وإذا لم يكن كذلك ، فكيف وصلت إلى هنا؟) ولكن ما هو Kubernetes بالضبط؟ هل هذا "تنظيم الحاويات الصناعية" ؟ أو "نظام التشغيل السحابي الأصلي" ؟ ماذا يعني هذا على أي حال؟



لأكون صادقًا ، لست متأكدًا بنسبة 100٪. لكني أعتقد أنه من المثير للاهتمام البحث في الدواخل ومعرفة ما يحدث بالفعل في Kubernetes تحت طبقات التجريد العديدة. لذا من أجل المتعة فقط ، دعنا نرى ما تبدو عليه "مجموعة Kubernetes" الصغيرة. (سيكون هذا أسهل بكثير من Kubernetes The Hard Way .)



أفترض أن لديك معرفة أساسية بـ Kubernetes و Linux والحاويات. كل ما سنتحدث عنه هنا هو للبحث / الدراسة فقط ، لا تقم بتشغيل أي من هذا في الإنتاج!



نظرة عامة



يحتوي Kubernetes على العديد من المكونات. وفقًا لويكيبيديا ، تبدو الهندسة كما يلي:







هناك ثمانية مكونات على الأقل معروضة هنا ، لكننا سنتجاهل معظمها. أريد أن أوضح أن أصغر شيء يمكن تسميته بشكل معقول Kubernetes يحتوي على ثلاثة مكونات رئيسية:



  • kubelet
  • kube-apiserver (الذي يعتمد على etcd - قاعدة بياناته)
  • وقت تشغيل الحاوية (في هذه الحالة Docker)


دعونا نرى ما تقول وثائق حول كل واحد منهم ( الروسية ، الانجليزية ). أولاً الكوبيليت :



عامل يعمل على كل عقدة في الكتلة. يتأكد من أن الحاويات تعمل في الكبسولة.



تبدو بسيطة بما فيه الكفاية. ماذا عن حاويات وقت التشغيل (وقت تشغيل الحاوية)؟



وقت تشغيل الحاوية هو برنامج مصمم لتشغيل الحاويات.



مليء بالمعلومات. ولكن إذا كنت معتادًا على Docker ، فيجب أن يكون لديك فهم أساسي لما يفعله. (تفاصيل فصل الاهتمامات بين وقت تشغيل الحاوية و kubelet هي في الواقع دقيقة للغاية ولن أتطرق إليها هنا.)



وماذا عن الخادم API ؟



خادم API - مكون لوحة معلومات Kubernetes يمثل واجهة برمجة تطبيقات Kubernetes. خادم API هو الواجهة الأمامية للوحة معلومات Kubernetes. يجب على



أي شخص فعل أي شيء باستخدام Kubernetes التفاعل مع واجهة برمجة التطبيقات إما مباشرة أو عبر kubectl. هذا هو جوهر ما يجعل Kubernetes Kubernetes - العقل الذي يحول جبال YAML التي نعرفها جميعًا ونحبها (؟) إلى بنية تحتية عاملة. يبدو واضحًا أن API يجب أن يكون موجودًا في الحد الأدنى من التكوين لدينا.



الشروط المسبقة



  • جهاز Linux الظاهري أو المادي المتجذر (أنا أستخدم Ubuntu 18.04 في جهاز افتراضي).
  • وهذا كل شيء!


تركيب مملة



يجب تثبيت Docker على الجهاز الذي سنستخدمه. (لن أخوض في التفاصيل حول كيفية عمل 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 .)



تتيح لنا هذه المعلمة تشغيل ثابت السنفات - Pods التي لا تتم إدارتها من خلال Kubernetes API. نادرًا ما يتم استخدام القرون الثابتة ، لكنها مريحة جدًا لرفع الكتلة بسرعة ، وهذا بالضبط ما نحتاجه. سنتجاهل هذا التحذير الصاخب (مرة أخرى ، لا تقم بتشغيل هذا في الإنتاج!) ومعرفة ما إذا كان بإمكاننا الركض.



أولاً ، سننشئ دليلًا للقرون الثابتة ونشغل kubelet:



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


ثم في نافذة Terminal / 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قراءة بيان الكبسولة وإصدار تعليمات إلى Docker بتشغيل بضع حاويات وفقًا لمواصفاتنا. (إذا كنت مهتمًا بحاوية "الإيقاف المؤقت" ، فهذه هي اختراق Kubernetes - راجع هذه المدونة للحصول على التفاصيل .) سيقوم Kubelet بتشغيل الحاوية الخاصة بنا busyboxباستخدام الأمر المحدد وإعادة تشغيلها إلى أجل غير مسمى حتى تتم إزالة الحاوية الثابتة.



هنئ نفسك. لقد توصلنا للتو إلى واحدة من أصعب الطرق لإخراج النص إلى المحطة!



تشغيل إلخ



هدفنا النهائي هو تشغيل Kubernetes API ، ولكن من أجل ذلك نحتاج أولاً إلى تشغيل etcd . لنبدأ مجموعة صغيرة etcd عن طريق وضع إعداداتها في دليل pods (على سبيل المثال 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بتثبيت المجلد المضيف في pod بحيث يتم حفظ بيانات etcd بعد إعادة التشغيل (إذا لم يتم ذلك ، فسيتم مسح حالة الكتلة في كل مرة يتم فيها إعادة تشغيل pod ، وهو ما سيكون سيئًا حتى مع الحد الأدنى من تثبيت Kubernetes).



لقد قمنا بتثبيت hostNetwork: true. هذا الخيار ، بشكل غير مفاجئ ، يقوم بتكوين إلخ لاستخدام الشبكة المضيفة بدلاً من الشبكة الداخلية للحجرة (وهذا سيجعل من السهل على خادم API العثور على مجموعة etcd).



يظهر فحص بسيط أن etcd يعمل بالفعل على localhost ويقوم بحفظ البيانات على القرص:



$ 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



يعد بدء تشغيل خادم Kubernetes API أسهل. المعلمة الوحيدة التي يجب تمريرها --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يُظهر التحقق بمساعدة المساعدة أن Kubernetes API تستمع على المنفذ 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 قبل بضع فقرات بمجموعة بسيطة للغاية من معلمات سطر الأوامر ، لذلك لا يعرف kubelet كيفية الاتصال بخادم API وإخطاره بحالته. بعد فحص الوثائق ، نجد العلامة المقابلة:



--kubeconfig string



المسار إلى الملف kubeconfig، والذي يشير إلى كيفية الاتصال بخادم API. --kubeconfigيتيح التواجد وضع خادم واجهة برمجة التطبيقات ، ويؤدي الغياب إلى --kubeconfigتمكين وضع عدم الاتصال.



طوال هذا الوقت ، دون معرفة ذلك ، كنا نشغل kubelet في "وضع غير متصل بالشبكة". (إذا كنا متحذلقين ، فيمكننا اعتبار وضع kubelet المستقل على أنه "الحد الأدنى من Kubernetes القابلة للحياة" ، لكن هذا سيكون مملًا جدًا). لكي يعمل التكوين "الحقيقي" ، نحتاج إلى تمرير ملف 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 عندما لا يعمل kubelet ، فستجد أنه لا يزال يعمل! Kubelet ليس "والد" كبسولاته ، مثل Docker ، إنه أشبه بـ "عنصر تحكم خفي". ستعمل الحاويات التي تديرها kubelet حتى توقفها kubelet.)



بعد بضع دقائق ، 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 القيام به. لنبدأ بجراب nginx:



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 غبية بشكل مدهش - عندما تنشئ جرابًا في واجهة برمجة التطبيقات ، فإنه يسجله ، لكنه لا يحاول معرفة العقدة التي سيتم تشغيله عليها.



لا تحتاج في الواقع إلى جدولة لتشغيل الكبسولة. يمكنك إضافة العقدة يدويًا إلى البيان في المعلمة 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 يعملان كما هو متوقع ، لكن الخدمة والنشر لا يعملان.



نجاح!



أصبح هذا المنشور كبيرًا ، لذلك سأعلن النصر وأعلن أن هذا تكوين قابل للتطبيق لاستدعاء "Kubernetes". للتلخيص: أربعة ثنائيات وخمس معلمات لسطر الأوامر و 45 سطرًا "فقط" من YAML (ليس كثيرًا بالمعايير Kubernetes) ولدينا الكثير من الأشياء التي تعمل:



  • تتم إدارة البودات باستخدام واجهة برمجة تطبيقات Kubernetes العادية (مع بعض الاختراقات)
  • يمكنك تحميل وإدارة صور الحاويات العامة
  • تبقى البودات على قيد الحياة وإعادة التشغيل تلقائيًا
  • يعمل الاتصال بين البودات داخل عقدة واحدة بشكل جيد
  • يعمل ConfigMap والسري وأبسط تركيب للمستودعات كما هو متوقع


لكن معظم الأشياء التي تجعل Kubernetes مفيدة حقًا ما زالت مفقودة ، على سبيل المثال:



  • مخطط جراب
  • المصادقة / التفويض
  • عقد متعددة
  • خدمات الانترنت
  • DNS الداخلية المجمعة
  • أدوات التحكم في حسابات الخدمة وعمليات النشر وتكامل موفري السحابة ومعظم الأشياء الجيدة الأخرى التي يجلبها Kubernetes


إذن ما الذي حصلنا عليه بالفعل؟ تعد Kubernetes API ، التي تعمل من تلقاء نفسها ، مجرد منصة لأتمتة الحاويات . إنه لا يفعل الكثير - إنه يعمل مع مختلف وحدات التحكم والمشغلين الذين يستخدمون واجهة برمجة التطبيقات - ولكنه يوفر إطارًا ثابتًا للأتمتة.



تعرف على المزيد حول الدورة التدريبية في ندوة مجانية عبر الإنترنت.






اقرأ أكثر:






All Articles