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